Send channel entry in SIGNOFF notify to application.
[silc.git] / lib / silcclient / client_notify.c
1 /*
2
3   client_notify.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1997 - 2007 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; version 2 of the License.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18 */
19 /* $Id$ */
20
21 #include "silc.h"
22 #include "silcclient.h"
23 #include "client_internal.h"
24
25 /************************** Types and definitions ***************************/
26
27 #define NOTIFY conn->client->internal->ops->notify
28
29 /* Notify processing context */
30 typedef struct {
31   SilcPacket packet;                  /* Notify packet */
32   SilcNotifyPayload payload;          /* Parsed notify payload */
33   SilcFSMThread fsm;                  /* Notify FSM thread */
34   SilcChannelEntry channel;           /* Channel entry being resolved */
35   SilcClientEntry client_entry;       /* Client entry being resolved */
36   SilcUInt32 resolve_retry;           /* Resolving retry counter */
37 } *SilcClientNotify;
38
39 /************************ Static utility functions **************************/
40
41 /* The client entires in notify processing are resolved if they do not exist,
42    or they are not valid.  We go to this callback after resolving where we
43    check if the client entry has become valid.  If resolving succeeded the
44    entry is valid but remains invalid if resolving failed.  This callback
45    will continue processing the notify.  We use this callback also with other
46    entry resolving. */
47
48 static void silc_client_notify_resolved(SilcClient client,
49                                         SilcClientConnection conn,
50                                         SilcStatus status,
51                                         SilcDList entries,
52                                         void *context)
53 {
54   SilcClientNotify notify = context;
55
56   /* If entry is still invalid, resolving failed.  Finish notify processing. */
57   if (notify->client_entry && !notify->client_entry->internal.valid) {
58     /* If resolving timedout try it again many times. */
59     if (status != SILC_STATUS_ERR_TIMEDOUT || ++notify->resolve_retry > 1000)
60       silc_fsm_next(notify->fsm, silc_client_notify_processed);
61     silc_client_unref_client(client, conn, notify->client_entry);
62   }
63
64   /* If no entries found, just finish the notify processing */
65   if (!entries && !notify->client_entry)
66     silc_fsm_next(notify->fsm, silc_client_notify_processed);
67
68   if (notify->channel) {
69     notify->channel->internal.resolve_cmd_ident = 0;
70     silc_client_unref_channel(client, conn, notify->channel);
71   }
72
73   /* Continue processing the notify */
74   SILC_FSM_CALL_CONTINUE_SYNC(notify->fsm);
75 }
76
77 /* Continue notify processing after it was suspended while waiting for
78    channel information being resolved. */
79
80 static SilcBool silc_client_notify_wait_continue(SilcClient client,
81                                                  SilcClientConnection conn,
82                                                  SilcCommand command,
83                                                  SilcStatus status,
84                                                  SilcStatus error,
85                                                  void *context,
86                                                  va_list ap)
87 {
88   SilcClientNotify notify = context;
89
90   /* Continue after last command reply received */
91   if (SILC_STATUS_IS_ERROR(status) || status == SILC_STATUS_OK ||
92       status == SILC_STATUS_LIST_END)
93     SILC_FSM_CALL_CONTINUE_SYNC(notify->fsm);
94
95   return TRUE;
96 }
97
98 /********************************* Notify ***********************************/
99
100 /* Process received notify packet */
101
102 SILC_FSM_STATE(silc_client_notify)
103 {
104   SilcPacket packet = state_context;
105   SilcClientNotify notify;
106   SilcNotifyPayload payload;
107
108   payload = silc_notify_payload_parse(silc_buffer_data(&packet->buffer),
109                                       silc_buffer_len(&packet->buffer));
110   if (!payload) {
111     SILC_LOG_DEBUG(("Malformed notify payload"));
112     silc_packet_free(packet);
113     return SILC_FSM_FINISH;
114   }
115
116   if (!silc_notify_get_args(payload)) {
117     SILC_LOG_DEBUG(("Malformed notify %d", silc_notify_get_type(payload)));
118     silc_notify_payload_free(payload);
119     silc_packet_free(packet);
120     return SILC_FSM_FINISH;
121   }
122
123   notify = silc_calloc(1, sizeof(*notify));
124   if (!notify) {
125     silc_notify_payload_free(payload);
126     silc_packet_free(packet);
127     return SILC_FSM_FINISH;
128   }
129
130   notify->packet = packet;
131   notify->payload = payload;
132   notify->fsm = fsm;
133   silc_fsm_set_state_context(fsm, notify);
134
135   /* Process the notify */
136   switch (silc_notify_get_type(payload)) {
137
138   case SILC_NOTIFY_TYPE_NONE:
139     /** NONE */
140     silc_fsm_next(fsm, silc_client_notify_none);
141     break;
142
143   case SILC_NOTIFY_TYPE_INVITE:
144     /** INVITE */
145     silc_fsm_next(fsm, silc_client_notify_invite);
146     break;
147
148   case SILC_NOTIFY_TYPE_JOIN:
149     /** JOIN */
150     silc_fsm_next(fsm, silc_client_notify_join);
151     break;
152
153   case SILC_NOTIFY_TYPE_LEAVE:
154     /** LEAVE */
155     silc_fsm_next(fsm, silc_client_notify_leave);
156     break;
157
158   case SILC_NOTIFY_TYPE_SIGNOFF:
159     /** SIGNOFF */
160     silc_fsm_next(fsm, silc_client_notify_signoff);
161     break;
162
163   case SILC_NOTIFY_TYPE_TOPIC_SET:
164     /** TOPIC_SET */
165     silc_fsm_next(fsm, silc_client_notify_topic_set);
166     break;
167
168   case SILC_NOTIFY_TYPE_NICK_CHANGE:
169     /** NICK_CHANGE */
170     silc_fsm_next(fsm, silc_client_notify_nick_change);
171     break;
172
173   case SILC_NOTIFY_TYPE_CMODE_CHANGE:
174     /** CMODE_CHANGE */
175     silc_fsm_next(fsm, silc_client_notify_cmode_change);
176     break;
177
178   case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
179     /** CUMODE_CHANGE */
180     silc_fsm_next(fsm, silc_client_notify_cumode_change);
181     break;
182
183   case SILC_NOTIFY_TYPE_MOTD:
184     /** MOTD */
185     silc_fsm_next(fsm, silc_client_notify_motd);
186     break;
187
188   case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
189     /** CHANNEL_CHANGE */
190     silc_fsm_next(fsm, silc_client_notify_channel_change);
191     break;
192
193   case SILC_NOTIFY_TYPE_KICKED:
194     /** KICKED */
195     silc_fsm_next(fsm, silc_client_notify_kicked);
196     break;
197
198   case SILC_NOTIFY_TYPE_KILLED:
199     /** KILLED */
200     silc_fsm_next(fsm, silc_client_notify_killed);
201     break;
202
203   case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
204     /** SERVER_SIGNOFF */
205     silc_fsm_next(fsm, silc_client_notify_server_signoff);
206     break;
207
208   case SILC_NOTIFY_TYPE_ERROR:
209     /** ERROR */
210     silc_fsm_next(fsm, silc_client_notify_error);
211     break;
212
213   case SILC_NOTIFY_TYPE_WATCH:
214     /** WATCH */
215     silc_fsm_next(fsm, silc_client_notify_watch);
216     break;
217
218   default:
219     /** Unknown notify */
220     silc_notify_payload_free(payload);
221     silc_packet_free(packet);
222     silc_free(notify);
223     return SILC_FSM_FINISH;
224     break;
225   }
226
227   return SILC_FSM_CONTINUE;
228 }
229
230 /* Notify processed, finish the packet processing thread */
231
232 SILC_FSM_STATE(silc_client_notify_processed)
233 {
234   SilcClientNotify notify = state_context;
235   SilcPacket packet = notify->packet;
236   SilcNotifyPayload payload = notify->payload;
237
238   silc_notify_payload_free(payload);
239   silc_packet_free(packet);
240   silc_free(notify);
241   return SILC_FSM_FINISH;
242 }
243
244 /********************************** NONE ************************************/
245
246 SILC_FSM_STATE(silc_client_notify_none)
247 {
248   SilcClientConnection conn = fsm_context;
249   SilcClient client = conn->client;
250   SilcClientNotify notify = state_context;
251   SilcNotifyPayload payload = notify->payload;
252   SilcNotifyType type = silc_notify_get_type(payload);
253   SilcArgumentPayload args = silc_notify_get_args(payload);
254
255   SILC_LOG_DEBUG(("Notify: NONE"));
256
257   /* Notify application */
258   NOTIFY(client, conn, type, silc_argument_get_arg_type(args, 1, NULL));
259
260   /** Notify processed */
261   silc_fsm_next(fsm, silc_client_notify_processed);
262   return SILC_FSM_CONTINUE;
263 }
264
265 /********************************* INVITE ***********************************/
266
267 /* Someone invite me to a channel */
268
269 SILC_FSM_STATE(silc_client_notify_invite)
270 {
271   SilcClientConnection conn = fsm_context;
272   SilcClient client = conn->client;
273   SilcClientNotify notify = state_context;
274   SilcNotifyPayload payload = notify->payload;
275   SilcNotifyType type = silc_notify_get_type(payload);
276   SilcArgumentPayload args = silc_notify_get_args(payload);
277   SilcClientEntry client_entry;
278   SilcChannelEntry channel = NULL;
279   unsigned char *tmp;
280   SilcUInt32 tmp_len;
281   SilcID id;
282
283   SILC_LOG_DEBUG(("Notify: INVITE"));
284
285   /* Get Channel ID */
286   if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
287     goto out;
288
289   /* Get the channel name */
290   tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
291   if (!tmp)
292     goto out;
293
294   /* Get the channel entry */
295   channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
296   if (!channel)
297     goto out;
298
299   /* If channel is being resolved handle notify after resolving */
300   if (channel->internal.resolve_cmd_ident) {
301     silc_client_unref_channel(client, conn, channel);
302     SILC_FSM_CALL(silc_client_command_pending(
303                                       conn, SILC_COMMAND_NONE,
304                                       channel->internal.resolve_cmd_ident,
305                                       silc_client_notify_wait_continue,
306                                       notify));
307     /* NOT REACHED */
308   }
309
310   /* Get sender Client ID */
311   if (!silc_argument_get_decoded(args, 3, SILC_ARGUMENT_ID, &id, NULL))
312     goto out;
313
314   /* Find Client entry and if not found query it */
315   client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
316   if (!client_entry || !client_entry->internal.valid) {
317     /** Resolve client */
318     silc_client_unref_client(client, conn, client_entry);
319     notify->channel = channel;
320     SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
321                   silc_client_get_client_by_id_resolve(
322                                          client, conn, &id.u.client_id, NULL,
323                                          silc_client_notify_resolved,
324                                          notify));
325     /* NOT REACHED */
326   }
327
328   /* Notify application */
329   NOTIFY(client, conn, type, channel, tmp, client_entry);
330
331   silc_client_unref_client(client, conn, client_entry);
332
333  out:
334   /** Notify processed */
335   silc_client_unref_channel(client, conn, channel);
336   silc_fsm_next(fsm, silc_client_notify_processed);
337   return SILC_FSM_CONTINUE;
338 }
339
340 /********************************** JOIN ************************************/
341
342 /* Someone joined a channel */
343
344 SILC_FSM_STATE(silc_client_notify_join)
345 {
346   SilcClientConnection conn = fsm_context;
347   SilcClient client = conn->client;
348   SilcClientNotify notify = state_context;
349   SilcNotifyPayload payload = notify->payload;
350   SilcNotifyType type = silc_notify_get_type(payload);
351   SilcArgumentPayload args = silc_notify_get_args(payload);
352   SilcClientEntry client_entry;
353   SilcChannelEntry channel = NULL;
354   SilcID id;
355
356   SILC_LOG_DEBUG(("Notify: JOIN"));
357
358   /* Get Channel ID */
359   if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL))
360     goto out;
361
362   /* Get channel entry */
363   channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
364   if (!channel)
365     goto out;
366
367   /* If channel is being resolved handle notify after resolving */
368   if (channel->internal.resolve_cmd_ident) {
369     silc_client_unref_channel(client, conn, channel);
370     SILC_FSM_CALL(silc_client_command_pending(
371                                       conn, SILC_COMMAND_NONE,
372                                       channel->internal.resolve_cmd_ident,
373                                       silc_client_notify_wait_continue,
374                                       notify));
375     /* NOT REACHED */
376   }
377
378   /* Get Client ID */
379   if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
380     goto out;
381
382   /* Find client entry and if not found query it.  If we just queried it
383      don't do it again, unless some data (like username) is missing. */
384   client_entry = notify->client_entry;
385   if (!client_entry)
386     client_entry = silc_client_get_client(client, conn, &id.u.client_id);
387   if (!client_entry || !client_entry->internal.valid ||
388       !client_entry->username[0]) {
389     /** Resolve client */
390     notify->channel = channel;
391     notify->client_entry = client_entry;
392     SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
393                   silc_client_get_client_by_id_resolve(
394                                          client, conn, client_entry ?
395                                          &client_entry->id : &id.u.client_id,
396                                          NULL, silc_client_notify_resolved,
397                                          notify));
398     /* NOT REACHED */
399   }
400
401   silc_rwlock_wrlock(client_entry->internal.lock);
402   silc_rwlock_wrlock(channel->internal.lock);
403
404   if (client_entry != conn->local_entry)
405     silc_client_nickname_format(client, conn, client_entry, FALSE);
406
407   /* Join the client to channel */
408   if (!silc_client_add_to_channel(client, conn, channel, client_entry, 0)) {
409     silc_rwlock_unlock(channel->internal.lock);
410     silc_rwlock_unlock(client_entry->internal.lock);
411     goto out;
412   }
413
414   silc_rwlock_unlock(channel->internal.lock);
415   silc_rwlock_unlock(client_entry->internal.lock);
416
417   /* Notify application. */
418   NOTIFY(client, conn, type, client_entry, channel);
419
420   silc_client_unref_client(client, conn, client_entry);
421
422  out:
423   /** Notify processed */
424   silc_client_unref_channel(client, conn, channel);
425   silc_fsm_next(fsm, silc_client_notify_processed);
426   return SILC_FSM_CONTINUE;
427 }
428
429 /********************************** LEAVE ***********************************/
430
431 /* Someone left a channel */
432
433 SILC_FSM_STATE(silc_client_notify_leave)
434 {
435   SilcClientConnection conn = fsm_context;
436   SilcClient client = conn->client;
437   SilcClientNotify notify = state_context;
438   SilcNotifyPayload payload = notify->payload;
439   SilcPacket packet = notify->packet;
440   SilcNotifyType type = silc_notify_get_type(payload);
441   SilcArgumentPayload args = silc_notify_get_args(payload);
442   SilcClientEntry client_entry = NULL;
443   SilcChannelEntry channel = NULL;
444   SilcID id;
445
446   SILC_LOG_DEBUG(("Notify: LEAVE"));
447
448   /* Get channel entry */
449   if (!silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CHANNEL,
450                       &id.u.channel_id, sizeof(id.u.channel_id)))
451     goto out;
452   channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
453   if (!channel)
454     goto out;
455
456   /* If channel is being resolved handle notify after resolving */
457   if (channel->internal.resolve_cmd_ident) {
458     silc_client_unref_channel(client, conn, channel);
459     SILC_FSM_CALL(silc_client_command_pending(
460                                       conn, SILC_COMMAND_NONE,
461                                       channel->internal.resolve_cmd_ident,
462                                       silc_client_notify_wait_continue,
463                                       notify));
464     /* NOT REACHED */
465   }
466
467   /* Get Client ID */
468   if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
469     goto out;
470
471   /* Find Client entry */
472   client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
473   if (!client_entry)
474     goto out;
475
476   /* Remove client from channel */
477   if (!silc_client_remove_from_channel(client, conn, channel, client_entry))
478     goto out;
479
480   /* Notify application. */
481   NOTIFY(client, conn, type, client_entry, channel);
482
483   silc_client_unref_client(client, conn, client_entry);
484
485  out:
486   /** Notify processed */
487   silc_client_unref_channel(client, conn, channel);
488   silc_fsm_next(fsm, silc_client_notify_processed);
489   return SILC_FSM_CONTINUE;
490 }
491
492 /********************************* SIGNOFF **********************************/
493
494 /* Someone quit SILC network */
495
496 SILC_FSM_STATE(silc_client_notify_signoff)
497 {
498   SilcClientConnection conn = fsm_context;
499   SilcClient client = conn->client;
500   SilcClientNotify notify = state_context;
501   SilcNotifyPayload payload = notify->payload;
502   SilcPacket packet = notify->packet;
503   SilcNotifyType type = silc_notify_get_type(payload);
504   SilcArgumentPayload args = silc_notify_get_args(payload);
505   SilcClientEntry client_entry;
506   SilcChannelEntry channel = NULL;
507   unsigned char *tmp;
508   SilcUInt32 tmp_len;
509   SilcID id;
510
511   SILC_LOG_DEBUG(("Notify: SIGNOFF"));
512
513   /* Get Client ID */
514   if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
515     goto out;
516
517   /* Find Client entry */
518   client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
519   if (!client_entry)
520     goto out;
521
522   /* Get signoff message */
523   tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
524   if (tmp && tmp_len > 128)
525     tmp[128] = '\0';
526
527   if (packet->dst_id_type == SILC_ID_CHANNEL) 
528     if (silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CHANNEL,
529                        &id.u.channel_id, sizeof(id.u.channel_id)))
530       channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
531
532   /* Notify application */
533   if (client_entry->internal.valid)
534     NOTIFY(client, conn, type, client_entry, tmp, channel);
535
536   /* Remove from channel */
537   if (channel) {
538     silc_client_remove_from_channel(client, conn, channel, client_entry);
539     silc_client_unref_channel(client, conn, channel);
540   }
541
542   /* Delete client */
543   client_entry->internal.valid = FALSE;
544   silc_client_del_client(client, conn, client_entry);
545   silc_client_unref_client(client, conn, client_entry);
546
547  out:
548   /** Notify processed */
549   silc_fsm_next(fsm, silc_client_notify_processed);
550   return SILC_FSM_CONTINUE;
551 }
552
553 /******************************** TOPIC_SET *********************************/
554
555 /* Someone set topic on a channel */
556
557 SILC_FSM_STATE(silc_client_notify_topic_set)
558 {
559   SilcClientConnection conn = fsm_context;
560   SilcClient client = conn->client;
561   SilcClientNotify notify = state_context;
562   SilcNotifyPayload payload = notify->payload;
563   SilcPacket packet = notify->packet;
564   SilcNotifyType type = silc_notify_get_type(payload);
565   SilcArgumentPayload args = silc_notify_get_args(payload);
566   SilcClientEntry client_entry = NULL;
567   SilcChannelEntry channel = NULL, channel_entry = NULL;
568   SilcServerEntry server = NULL;
569   void *entry;
570   unsigned char *tmp;
571   SilcUInt32 tmp_len;
572   SilcID id;
573
574   SILC_LOG_DEBUG(("Notify: TOPIC_SET"));
575
576   /* Get channel entry */
577   if (!silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CHANNEL,
578                       &id.u.channel_id, sizeof(id.u.channel_id)))
579     goto out;
580   channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
581   if (!channel)
582     goto out;
583
584   /* If channel is being resolved handle notify after resolving */
585   if (channel->internal.resolve_cmd_ident) {
586     silc_client_unref_channel(client, conn, channel);
587     SILC_FSM_CALL(silc_client_command_pending(
588                                       conn, SILC_COMMAND_NONE,
589                                       channel->internal.resolve_cmd_ident,
590                                       silc_client_notify_wait_continue,
591                                       notify));
592     /* NOT REACHED */
593   }
594
595   /* Get ID of topic changer */
596   if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
597     goto out;
598
599   /* Get topic */
600   tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
601   if (!tmp)
602     goto out;
603
604   if (id.type == SILC_ID_CLIENT) {
605     /* Find Client entry */
606     client_entry = notify->client_entry;
607     if (!client_entry) {
608       client_entry = silc_client_get_client(client, conn, &id.u.client_id);
609       if (!client_entry || !client_entry->internal.valid) {
610         /** Resolve client */
611         notify->channel = channel;
612         notify->client_entry = client_entry;
613         SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
614                       silc_client_get_client_by_id_resolve(
615                                            client, conn, &id.u.client_id, NULL,
616                                            silc_client_notify_resolved,
617                                            notify));
618         /* NOT REACHED */
619       }
620     }
621
622     /* If client is not on channel, ignore this notify */
623     if (!silc_client_on_channel(channel, client_entry))
624       goto out;
625
626     entry = client_entry;
627   } else if (id.type == SILC_ID_SERVER) {
628     /* Find Server entry */
629     server = silc_client_get_server_by_id(client, conn, &id.u.server_id);
630     if (!server) {
631       /** Resolve server */
632       notify->channel = channel;
633       SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
634                     silc_client_get_server_by_id_resolve(
635                                            client, conn, &id.u.server_id,
636                                            silc_client_notify_resolved,
637                                            notify));
638       /* NOT REACHED */
639     }
640     entry = server;
641   } else {
642     /* Find Channel entry */
643     channel_entry = silc_client_get_channel_by_id(client, conn,
644                                                   &id.u.channel_id);
645     if (!channel_entry) {
646       /** Resolve channel */
647       notify->channel = channel;
648       SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
649                     silc_client_get_channel_by_id_resolve(
650                                     client, conn, &id.u.channel_id,
651                                     silc_client_notify_resolved,
652                                     notify));
653       /* NOT REACHED */
654     }
655     entry = channel_entry;
656   }
657
658   silc_rwlock_wrlock(channel->internal.lock);
659   silc_free(channel->topic);
660   channel->topic = silc_memdup(tmp, strlen(tmp));
661   silc_rwlock_unlock(channel->internal.lock);
662
663   /* Notify application. */
664   NOTIFY(client, conn, type, id.type, entry, channel->topic, channel);
665
666   if (client_entry)
667     silc_client_unref_client(client, conn, client_entry);
668   if (server)
669     silc_client_unref_server(client, conn, server);
670   if (channel_entry)
671     silc_client_unref_channel(client, conn, channel_entry);
672
673  out:
674   /** Notify processed */
675   silc_client_unref_channel(client, conn, channel);
676   silc_fsm_next(fsm, silc_client_notify_processed);
677   return SILC_FSM_CONTINUE;
678 }
679
680 /****************************** NICK_CHANGE *********************************/
681
682 /* Someone changed their nickname on a channel */
683
684 SILC_FSM_STATE(silc_client_notify_nick_change)
685 {
686   SilcClientConnection conn = fsm_context;
687   SilcClient client = conn->client;
688   SilcClientNotify notify = state_context;
689   SilcNotifyPayload payload = notify->payload;
690   SilcNotifyType type = silc_notify_get_type(payload);
691   SilcArgumentPayload args = silc_notify_get_args(payload);
692   SilcClientEntry client_entry = NULL;
693   unsigned char *tmp, oldnick[128 + 1];
694   SilcUInt32 tmp_len;
695   SilcID id, id2;
696   SilcBool valid;
697
698   SILC_LOG_DEBUG(("Notify: NICK_CHANGE"));
699
700   /* Get ID */
701   if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
702     goto out;
703
704   /* Ignore my ID */
705   if (conn->local_id &&
706       SILC_ID_CLIENT_COMPARE(&id.u.client_id, conn->local_id))
707     goto out;
708
709   /* Get new Client ID */
710   if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id2, NULL))
711     goto out;
712
713   /* Ignore my ID */
714   if (conn->local_id &&
715       SILC_ID_CLIENT_COMPARE(&id2.u.client_id, conn->local_id))
716     goto out;
717
718   /* Find old client entry.  If we don't have the entry, we ignore this
719      notify. */
720   client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
721   if (!client_entry)
722     goto out;
723   valid = client_entry->internal.valid;
724
725   /* Take the new nickname */
726   tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
727   if (!tmp)
728     goto out;
729
730   silc_rwlock_wrlock(client_entry->internal.lock);
731
732   /* Check whether nickname changed at all.  It is possible that nick
733      change notify is received but nickname didn't change, only the
734      ID changes.  If Client ID hash match, nickname didn't change. */
735   if (SILC_ID_COMPARE_HASH(&client_entry->id, &id2.u.client_id) &&
736       silc_utf8_strcasecmp(tmp, client_entry->nickname)) {
737     /* Nickname didn't change.  Update only Client ID.  We don't notify
738        application because nickname didn't change. */
739     silc_mutex_lock(conn->internal->lock);
740     silc_idcache_update_by_context(conn->internal->client_cache, client_entry,
741                                    &id2.u.client_id, NULL, FALSE);
742     silc_mutex_unlock(conn->internal->lock);
743     silc_rwlock_unlock(client_entry->internal.lock);
744     goto out;
745   }
746
747   /* Change the nickname */
748   memcpy(oldnick, client_entry->nickname, sizeof(client_entry->nickname));
749   if (!silc_client_change_nickname(client, conn, client_entry, tmp,
750                                    &id2.u.client_id, NULL, 0)) {
751     silc_rwlock_unlock(client_entry->internal.lock);
752     goto out;
753   }
754
755   silc_rwlock_unlock(client_entry->internal.lock);
756
757   /* Notify application, if client entry is valid.  We do not send nick change
758      notify for entries that were invalid (application doesn't know them). */
759   if (valid)
760     NOTIFY(client, conn, type, client_entry, oldnick, client_entry->nickname);
761
762  out:
763   /** Notify processed */
764   silc_client_unref_client(client, conn, client_entry);
765   silc_fsm_next(fsm, silc_client_notify_processed);
766   return SILC_FSM_CONTINUE;
767 }
768
769 /****************************** CMODE_CHANGE ********************************/
770
771 /* Someone changed channel mode */
772
773 SILC_FSM_STATE(silc_client_notify_cmode_change)
774 {
775   SilcClientConnection conn = fsm_context;
776   SilcClient client = conn->client;
777   SilcClientNotify notify = state_context;
778   SilcNotifyPayload payload = notify->payload;
779   SilcPacket packet = notify->packet;
780   SilcNotifyType type = silc_notify_get_type(payload);
781   SilcArgumentPayload args = silc_notify_get_args(payload);
782   SilcClientEntry client_entry = NULL;
783   SilcChannelEntry channel = NULL, channel_entry = NULL;
784   SilcServerEntry server = NULL;
785   void *entry;
786   unsigned char *tmp;
787   SilcUInt32 tmp_len, mode;
788   SilcID id;
789   char *passphrase, *cipher, *hmac;
790   SilcPublicKey founder_key = NULL;
791   SilcDList chpks = NULL;
792
793   SILC_LOG_DEBUG(("Notify: CMODE_CHANGE"));
794
795   /* Get channel entry */
796   if (!silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CHANNEL,
797                       &id.u.channel_id, sizeof(id.u.channel_id)))
798     goto out;
799   channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
800   if (!channel)
801     goto out;
802
803   /* If channel is being resolved handle notify after resolving */
804   if (channel->internal.resolve_cmd_ident) {
805     silc_client_unref_channel(client, conn, channel);
806     SILC_FSM_CALL(silc_client_command_pending(
807                                       conn, SILC_COMMAND_NONE,
808                                       channel->internal.resolve_cmd_ident,
809                                       silc_client_notify_wait_continue,
810                                       notify));
811     /* NOT REACHED */
812   }
813
814   /* Get the mode */
815   tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
816   if (!tmp)
817     goto out;
818   SILC_GET32_MSB(mode, tmp);
819
820   /* Get ID of who changed the mode */
821   if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
822     goto out;
823
824   if (id.type == SILC_ID_CLIENT) {
825     /* Find Client entry */
826     client_entry = notify->client_entry;
827     if (!client_entry) {
828       client_entry = silc_client_get_client(client, conn, &id.u.client_id);
829       if (!client_entry || !client_entry->internal.valid) {
830         /** Resolve client */
831         notify->channel = channel;
832         notify->client_entry = client_entry;
833         SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
834                       silc_client_get_client_by_id_resolve(
835                                            client, conn, &id.u.client_id, NULL,
836                                            silc_client_notify_resolved,
837                                            notify));
838         /* NOT REACHED */
839       }
840     }
841
842     /* If client is not on channel, ignore this notify */
843     if (!silc_client_on_channel(channel, client_entry))
844       goto out;
845
846     entry = client_entry;
847   } else if (id.type == SILC_ID_SERVER) {
848     /* Find Server entry */
849     server = silc_client_get_server_by_id(client, conn, &id.u.server_id);
850     if (!server) {
851       /** Resolve server */
852       notify->channel = channel;
853       SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
854                     silc_client_get_server_by_id_resolve(
855                                            client, conn, &id.u.server_id,
856                                            silc_client_notify_resolved,
857                                            notify));
858       /* NOT REACHED */
859     }
860     entry = server;
861   } else {
862     /* Find Channel entry */
863     channel_entry = silc_client_get_channel_by_id(client, conn,
864                                                   &id.u.channel_id);
865     if (!channel_entry) {
866       /** Resolve channel */
867       notify->channel = channel;
868       SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
869                     silc_client_get_channel_by_id_resolve(
870                                     client, conn, &id.u.channel_id,
871                                     silc_client_notify_resolved,
872                                     notify));
873       /* NOT REACHED */
874     }
875     entry = channel_entry;
876   }
877
878   silc_rwlock_wrlock(channel->internal.lock);
879
880   /* Get the channel founder key if it was set */
881   tmp = silc_argument_get_arg_type(args, 6, &tmp_len);
882   if (tmp) {
883     if (!silc_public_key_payload_decode(tmp, tmp_len, &founder_key)) {
884       silc_rwlock_unlock(channel->internal.lock);
885       goto out;
886     }
887     if (!channel->founder_key) {
888       channel->founder_key = founder_key;
889       founder_key = NULL;
890     }
891   }
892
893   /* Get the cipher */
894   cipher = silc_argument_get_arg_type(args, 3, &tmp_len);
895
896   /* Get the hmac */
897   hmac = silc_argument_get_arg_type(args, 4, &tmp_len);
898   if (hmac) {
899     unsigned char hash[SILC_HASH_MAXLEN];
900     SilcHmac newhmac;
901
902     if (!silc_hmac_alloc(hmac, NULL, &newhmac)) {
903       silc_rwlock_unlock(channel->internal.lock);
904       goto out;
905     }
906
907     /* Get HMAC key from the old HMAC context, and update it to the new one */
908     tmp = (unsigned char *)silc_hmac_get_key(channel->internal.hmac, &tmp_len);
909     if (tmp) {
910       silc_hash_make(silc_hmac_get_hash(newhmac), tmp, tmp_len, hash);
911       silc_hmac_set_key(newhmac, hash,
912                         silc_hash_len(silc_hmac_get_hash(newhmac)));
913       if (channel->internal.hmac)
914         silc_hmac_free(channel->internal.hmac);
915       channel->internal.hmac = newhmac;
916       memset(hash, 0, sizeof(hash));
917     }
918   }
919
920   /* Get the passphrase if it was set */
921   passphrase = silc_argument_get_arg_type(args, 5, &tmp_len);
922
923   /* Get user limit */
924   tmp = silc_argument_get_arg_type(args, 8, &tmp_len);
925   if (tmp && tmp_len == 4)
926     SILC_GET32_MSB(channel->user_limit, tmp);
927   if (!(channel->mode & SILC_CHANNEL_MODE_ULIMIT))
928     channel->user_limit = 0;
929
930   /* Get the channel public key that was added or removed */
931   tmp = silc_argument_get_arg_type(args, 7, &tmp_len);
932   if (tmp)
933     silc_client_channel_save_public_keys(channel, tmp, tmp_len, FALSE);
934   else if (channel->mode & SILC_CHANNEL_MODE_CHANNEL_AUTH)
935     silc_client_channel_save_public_keys(channel, NULL, 0, TRUE);
936
937   /* Save the new mode */
938   channel->mode = mode;
939
940   silc_rwlock_unlock(channel->internal.lock);
941
942   /* Notify application. */
943   NOTIFY(client, conn, type, id.type, entry, mode, cipher, hmac,
944          passphrase, channel->founder_key, chpks, channel);
945
946  out:
947   if (founder_key)
948     silc_pkcs_public_key_free(founder_key);
949   if (chpks)
950     silc_argument_list_free(chpks, SILC_ARGUMENT_PUBLIC_KEY);
951   if (client_entry)
952     silc_client_unref_client(client, conn, client_entry);
953   if (server)
954     silc_client_unref_server(client, conn, server);
955   if (channel_entry)
956     silc_client_unref_channel(client, conn, channel_entry);
957   silc_client_unref_channel(client, conn, channel);
958
959   /** Notify processed */
960   silc_fsm_next(fsm, silc_client_notify_processed);
961   return SILC_FSM_CONTINUE;
962 }
963
964 /***************************** CUMODE_CHANGE ********************************/
965
966 /* Someone changed a user's mode on a channel */
967
968 SILC_FSM_STATE(silc_client_notify_cumode_change)
969 {
970   SilcClientConnection conn = fsm_context;
971   SilcClient client = conn->client;
972   SilcClientNotify notify = state_context;
973   SilcNotifyPayload payload = notify->payload;
974   SilcPacket packet = notify->packet;
975   SilcNotifyType type = silc_notify_get_type(payload);
976   SilcArgumentPayload args = silc_notify_get_args(payload);
977   SilcClientEntry client_entry = NULL, client_entry2 = NULL;
978   SilcChannelEntry channel = NULL, channel_entry = NULL;
979   SilcServerEntry server = NULL;
980   SilcChannelUser chu;
981   void *entry;
982   unsigned char *tmp;
983   SilcUInt32 tmp_len, mode;
984   SilcID id, id2;
985
986   SILC_LOG_DEBUG(("Notify: CUMODE_CHANGE"));
987
988   /* Get channel entry */
989   if (!silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CHANNEL,
990                       &id.u.channel_id, sizeof(id.u.channel_id)))
991     goto out;
992   channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
993   if (!channel)
994     goto out;
995
996   /* If channel is being resolved handle notify after resolving */
997   if (channel->internal.resolve_cmd_ident) {
998     silc_client_unref_channel(client, conn, channel);
999     SILC_FSM_CALL(silc_client_command_pending(
1000                                       conn, SILC_COMMAND_NONE,
1001                                       channel->internal.resolve_cmd_ident,
1002                                       silc_client_notify_wait_continue,
1003                                       notify));
1004     /* NOT REACHED */
1005   }
1006
1007   /* Get target Client ID */
1008   if (!silc_argument_get_decoded(args, 3, SILC_ARGUMENT_ID, &id2, NULL))
1009     goto out;
1010
1011   /* Find target Client entry */
1012   client_entry2 = silc_client_get_client_by_id(client, conn, &id2.u.client_id);
1013   if (!client_entry2 || !client_entry2->internal.valid) {
1014     /** Resolve client */
1015     silc_client_unref_client(client, conn, client_entry2);
1016     SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
1017                                          client, conn, &id2.u.client_id, NULL,
1018                                          silc_client_notify_resolved,
1019                                          notify));
1020     /* NOT REACHED */
1021   }
1022
1023   /* If target client is not on channel, ignore this notify */
1024   if (!silc_client_on_channel(channel, client_entry2))
1025     goto out;
1026
1027   /* Get the mode */
1028   tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
1029   if (!tmp)
1030     goto out;
1031   SILC_GET32_MSB(mode, tmp);
1032
1033   /* Get ID of mode changer */
1034   if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
1035     goto out;
1036
1037   if (id.type == SILC_ID_CLIENT) {
1038     /* Find Client entry */
1039     client_entry = notify->client_entry;
1040     if (!client_entry) {
1041       client_entry = silc_client_get_client(client, conn, &id.u.client_id);
1042       if (!client_entry || !client_entry->internal.valid) {
1043         /** Resolve client */
1044         notify->channel = channel;
1045         notify->client_entry = client_entry;
1046         SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
1047                       silc_client_get_client_by_id_resolve(
1048                                            client, conn, &id.u.client_id, NULL,
1049                                            silc_client_notify_resolved,
1050                                            notify));
1051         /* NOT REACHED */
1052       }
1053     }
1054
1055     /* If client is not on channel, ignore this notify */
1056     if (!silc_client_on_channel(channel, client_entry))
1057       goto out;
1058
1059     entry = client_entry;
1060   } else if (id.type == SILC_ID_SERVER) {
1061     /* Find Server entry */
1062     server = silc_client_get_server_by_id(client, conn, &id.u.server_id);
1063     if (!server) {
1064       /** Resolve server */
1065       notify->channel = channel;
1066       SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
1067                     silc_client_get_server_by_id_resolve(
1068                                            client, conn, &id.u.server_id,
1069                                            silc_client_notify_resolved,
1070                                            notify));
1071       /* NOT REACHED */
1072     }
1073     entry = server;
1074   } else {
1075     /* Find Channel entry */
1076     channel_entry = silc_client_get_channel_by_id(client, conn,
1077                                                   &id.u.channel_id);
1078     if (!channel_entry) {
1079       /** Resolve channel */
1080       notify->channel = channel;
1081       SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
1082                     silc_client_get_channel_by_id_resolve(
1083                                     client, conn, &id.u.channel_id,
1084                                     silc_client_notify_resolved,
1085                                     notify));
1086       /* NOT REACHED */
1087     }
1088     entry = channel_entry;
1089   }
1090
1091   /* Save the mode */
1092   silc_rwlock_wrlock(channel->internal.lock);
1093   chu = silc_client_on_channel(channel, client_entry2);
1094   if (chu)
1095     chu->mode = mode;
1096   silc_rwlock_unlock(channel->internal.lock);
1097
1098   /* Notify application. */
1099   NOTIFY(client, conn, type, id.type, entry, mode, client_entry2, channel);
1100
1101  out:
1102   silc_client_unref_client(client, conn, client_entry2);
1103   if (client_entry)
1104     silc_client_unref_client(client, conn, client_entry);
1105   if (server)
1106     silc_client_unref_server(client, conn, server);
1107   if (channel_entry)
1108     silc_client_unref_channel(client, conn, channel_entry);
1109   silc_client_unref_channel(client, conn, channel);
1110
1111   /** Notify processed */
1112   silc_fsm_next(fsm, silc_client_notify_processed);
1113   return SILC_FSM_CONTINUE;
1114 }
1115
1116 /********************************* MOTD *************************************/
1117
1118 /* Received Message of the day */
1119
1120 SILC_FSM_STATE(silc_client_notify_motd)
1121 {
1122   SilcClientConnection conn = fsm_context;
1123   SilcClient client = conn->client;
1124   SilcClientNotify notify = state_context;
1125   SilcNotifyPayload payload = notify->payload;
1126   SilcNotifyType type = silc_notify_get_type(payload);
1127   SilcArgumentPayload args = silc_notify_get_args(payload);
1128   unsigned char *tmp;
1129   SilcUInt32 tmp_len;
1130
1131   SILC_LOG_DEBUG(("Notify: MOTD"));
1132
1133   /* Get motd */
1134   tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
1135   if (!tmp)
1136     goto out;
1137
1138   /* Notify application */
1139   NOTIFY(client, conn, type, tmp);
1140
1141  out:
1142   /** Notify processed */
1143   silc_fsm_next(fsm, silc_client_notify_processed);
1144   return SILC_FSM_CONTINUE;
1145 }
1146
1147 /**************************** CHANNEL CHANGE ********************************/
1148
1149 /* Router has enforced a new ID to a channel, change it */
1150
1151 SILC_FSM_STATE(silc_client_notify_channel_change)
1152 {
1153   SilcClientConnection conn = fsm_context;
1154   SilcClient client = conn->client;
1155   SilcClientNotify notify = state_context;
1156   SilcNotifyPayload payload = notify->payload;
1157   SilcNotifyType type = silc_notify_get_type(payload);
1158   SilcArgumentPayload args = silc_notify_get_args(payload);
1159   SilcChannelEntry channel = NULL;
1160   SilcID id;
1161
1162   SILC_LOG_DEBUG(("Notify: CHANNEL_CHANGE"));
1163
1164   /* Get the old ID */
1165   if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
1166     goto out;
1167
1168   /* Get the channel entry */
1169   channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
1170   if (!channel)
1171     goto out;
1172
1173   /* If channel is being resolved handle notify after resolving */
1174   if (channel->internal.resolve_cmd_ident) {
1175     silc_client_unref_channel(client, conn, channel);
1176     SILC_FSM_CALL(silc_client_command_pending(
1177                                       conn, SILC_COMMAND_NONE,
1178                                       channel->internal.resolve_cmd_ident,
1179                                       silc_client_notify_wait_continue,
1180                                       notify));
1181     /* NOT REACHED */
1182   }
1183
1184   /* Get the new ID */
1185   if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL))
1186     goto out;
1187
1188   /* Replace the Channel ID */
1189   if (!silc_client_replace_channel_id(client, conn, channel, &id.u.channel_id))
1190     goto out;
1191
1192   /* Notify application */
1193   NOTIFY(client, conn, type, channel, channel);
1194
1195  out:
1196   /** Notify processed */
1197   silc_client_unref_channel(client, conn, channel);
1198   silc_fsm_next(fsm, silc_client_notify_processed);
1199   return SILC_FSM_CONTINUE;
1200 }
1201
1202 /******************************** KICKED ************************************/
1203
1204 /* Some client was kicked from a channel */
1205
1206 SILC_FSM_STATE(silc_client_notify_kicked)
1207 {
1208   SilcClientConnection conn = fsm_context;
1209   SilcClient client = conn->client;
1210   SilcClientNotify notify = state_context;
1211   SilcNotifyPayload payload = notify->payload;
1212   SilcPacket packet = notify->packet;
1213   SilcNotifyType type = silc_notify_get_type(payload);
1214   SilcArgumentPayload args = silc_notify_get_args(payload);
1215   SilcClientEntry client_entry, client_entry2;
1216   SilcChannelEntry channel = NULL;
1217   unsigned char *tmp;
1218   SilcUInt32 tmp_len;
1219   SilcID id;
1220
1221   SILC_LOG_DEBUG(("Notify: KICKED"));
1222
1223   /* Get channel entry */
1224   if (!silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CHANNEL,
1225                       &id.u.channel_id, sizeof(id.u.channel_id)))
1226     goto out;
1227   channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
1228   if (!channel)
1229     goto out;
1230
1231   /* If channel is being resolved handle notify after resolving */
1232   if (channel->internal.resolve_cmd_ident) {
1233     silc_client_unref_channel(client, conn, channel);
1234     SILC_FSM_CALL(silc_client_command_pending(
1235                                       conn, SILC_COMMAND_NONE,
1236                                       channel->internal.resolve_cmd_ident,
1237                                       silc_client_notify_wait_continue,
1238                                       notify));
1239     /* NOT REACHED */
1240   }
1241
1242   /* Get the kicked Client ID */
1243   if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
1244     goto out;
1245
1246   /* Find client entry */
1247   client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
1248   if (!client_entry)
1249     goto out;
1250
1251   /* Get kicker's Client ID */
1252   if (!silc_argument_get_decoded(args, 3, SILC_ARGUMENT_ID, &id, NULL))
1253     goto out;
1254
1255   /* Find kicker's client entry and if not found resolve it */
1256   client_entry2 = silc_client_get_client_by_id(client, conn, &id.u.client_id);
1257   if (!client_entry2 || !client_entry2->internal.valid) {
1258     /** Resolve client */
1259     silc_client_unref_client(client, conn, client_entry);
1260     silc_client_unref_client(client, conn, client_entry2);
1261     notify->channel = channel;
1262     SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
1263                   silc_client_get_client_by_id_resolve(
1264                                          client, conn, &id.u.client_id, NULL,
1265                                          silc_client_notify_resolved,
1266                                          notify));
1267     /* NOT REACHED */
1268   }
1269
1270   /* Get comment */
1271   tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
1272
1273   /* Remove kicked client from channel */
1274   if (client_entry != conn->local_entry) {
1275     if (!silc_client_remove_from_channel(client, conn, channel, client_entry))
1276       goto out;
1277   }
1278
1279   /* Notify application. */
1280   NOTIFY(client, conn, type, client_entry, tmp, client_entry2, channel);
1281
1282   /* If I was kicked from channel, remove the channel */
1283   if (client_entry == conn->local_entry) {
1284     if (conn->current_channel == channel)
1285       conn->current_channel = NULL;
1286     silc_client_empty_channel(client, conn, channel);
1287     silc_client_del_channel(client, conn, channel);
1288   }
1289
1290   silc_client_unref_client(client, conn, client_entry);
1291   silc_client_unref_client(client, conn, client_entry2);
1292
1293  out:
1294   /** Notify processed */
1295   silc_client_unref_channel(client, conn, channel);
1296   silc_fsm_next(fsm, silc_client_notify_processed);
1297   return SILC_FSM_CONTINUE;
1298 }
1299
1300 /******************************** KILLED ************************************/
1301
1302 /* Some client was killed from the network */
1303
1304 SILC_FSM_STATE(silc_client_notify_killed)
1305 {
1306   SilcClientConnection conn = fsm_context;
1307   SilcClient client = conn->client;
1308   SilcClientNotify notify = state_context;
1309   SilcNotifyPayload payload = notify->payload;
1310   SilcNotifyType type = silc_notify_get_type(payload);
1311   SilcArgumentPayload args = silc_notify_get_args(payload);
1312   SilcClientEntry client_entry = NULL, client_entry2 = NULL;
1313   SilcChannelEntry channel_entry = NULL;
1314   SilcServerEntry server = NULL;
1315   void *entry;
1316   char *comment;
1317   SilcUInt32 comment_len;
1318   SilcID id;
1319
1320   SILC_LOG_DEBUG(("Notify: KILLED"));
1321
1322   /* Get Client ID */
1323   if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
1324     goto out;
1325
1326   /* Find Client entry */
1327   client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
1328   if (!client_entry)
1329     goto out;
1330
1331   /* Get comment */
1332   comment = silc_argument_get_arg_type(args, 2, &comment_len);
1333
1334   /* Get killer's ID */
1335   if (!silc_argument_get_decoded(args, 3, SILC_ARGUMENT_ID, &id, NULL))
1336     goto out;
1337
1338   if (id.type == SILC_ID_CLIENT) {
1339     /* Find Client entry */
1340     client_entry2 = silc_client_get_client_by_id(client, conn,
1341                                                  &id.u.client_id);
1342     if (!client_entry2 || !client_entry2->internal.valid) {
1343       /** Resolve client */
1344       silc_client_unref_client(client, conn, client_entry);
1345       silc_client_unref_client(client, conn, client_entry2);
1346       SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
1347                                            client, conn, &id.u.client_id, NULL,
1348                                            silc_client_notify_resolved,
1349                                            notify));
1350       /* NOT REACHED */
1351     }
1352     entry = client_entry2;
1353   } else if (id.type == SILC_ID_SERVER) {
1354     /* Find Server entry */
1355     server = silc_client_get_server_by_id(client, conn, &id.u.server_id);
1356     if (!server) {
1357       /** Resolve server */
1358       SILC_FSM_CALL(silc_client_get_server_by_id_resolve(
1359                                            client, conn, &id.u.server_id,
1360                                            silc_client_notify_resolved,
1361                                            notify));
1362       /* NOT REACHED */
1363     }
1364     entry = server;
1365   } else {
1366     /* Find Channel entry */
1367     channel_entry = silc_client_get_channel_by_id(client, conn,
1368                                                   &id.u.channel_id);
1369     if (!channel_entry) {
1370       /** Resolve channel */
1371       SILC_FSM_CALL(silc_client_get_channel_by_id_resolve(
1372                                     client, conn, &id.u.channel_id,
1373                                     silc_client_notify_resolved,
1374                                     notify));
1375       /* NOT REACHED */
1376     }
1377     entry = channel_entry;
1378   }
1379
1380   /* Notify application. */
1381   NOTIFY(client, conn, type, client_entry, comment, id.type, entry);
1382
1383   /* Delete the killed client */
1384   if (client_entry != conn->local_entry) {
1385     silc_client_remove_from_channels(client, conn, client_entry);
1386     client_entry->internal.valid = FALSE;
1387     silc_client_del_client(client, conn, client_entry);
1388   }
1389
1390  out:
1391   silc_client_unref_client(client, conn, client_entry);
1392   if (client_entry2)
1393     silc_client_unref_client(client, conn, client_entry2);
1394   if (server)
1395     silc_client_unref_server(client, conn, server);
1396   if (channel_entry)
1397     silc_client_unref_channel(client, conn, channel_entry);
1398
1399   /** Notify processed */
1400   silc_fsm_next(fsm, silc_client_notify_processed);
1401   return SILC_FSM_CONTINUE;
1402 }
1403
1404 /**************************** SERVER SIGNOFF ********************************/
1405
1406 /* Some server quit SILC network.  Remove its clients from channels. */
1407
1408 SILC_FSM_STATE(silc_client_notify_server_signoff)
1409 {
1410   SilcClientConnection conn = fsm_context;
1411   SilcClient client = conn->client;
1412   SilcClientNotify notify = state_context;
1413   SilcNotifyPayload payload = notify->payload;
1414   SilcNotifyType type = silc_notify_get_type(payload);
1415   SilcArgumentPayload args = silc_notify_get_args(payload);
1416   SilcClientEntry client_entry;
1417   SilcServerEntry server_entry = NULL;
1418   SilcDList clients;
1419   SilcID id;
1420   int i;
1421
1422   SILC_LOG_DEBUG(("Notify: SERVER_SIGNOFF"));
1423
1424   clients = silc_dlist_init();
1425   if (!clients)
1426     goto out;
1427
1428   /* Get server ID */
1429   if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
1430     goto out;
1431
1432   /* Get server, in case we have it cached */
1433   server_entry = silc_client_get_server_by_id(client, conn, &id.u.server_id);
1434
1435   for (i = 1; i < silc_argument_get_arg_num(args); i++) {
1436     /* Get Client ID */
1437     if (!silc_argument_get_decoded(args, i + 1, SILC_ARGUMENT_ID, &id, NULL))
1438       goto out;
1439
1440     /* Get the client entry */
1441     client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
1442     if (client_entry && client_entry->internal.valid)
1443       silc_dlist_add(clients, client_entry);
1444   }
1445
1446   /* Notify application. */
1447   NOTIFY(client, conn, type, server_entry, clients);
1448
1449   /* Delete the clients */
1450   silc_dlist_start(clients);
1451   while ((client_entry = silc_dlist_get(clients))) {
1452     silc_client_remove_from_channels(client, conn, client_entry);
1453     client_entry->internal.valid = FALSE;
1454     silc_client_del_client(client, conn, client_entry);
1455   }
1456
1457  out:
1458   /** Notify processed */
1459   silc_client_unref_server(client, conn, server_entry);
1460   silc_client_list_free(client, conn, clients);
1461   silc_fsm_next(fsm, silc_client_notify_processed);
1462   return SILC_FSM_CONTINUE;
1463 }
1464
1465 /******************************** ERROR *************************************/
1466
1467 /* Some error occurred */
1468
1469 SILC_FSM_STATE(silc_client_notify_error)
1470 {
1471   SilcClientConnection conn = fsm_context;
1472   SilcClient client = conn->client;
1473   SilcClientNotify notify = state_context;
1474   SilcNotifyPayload payload = notify->payload;
1475   SilcNotifyType type = silc_notify_get_type(payload);
1476   SilcArgumentPayload args = silc_notify_get_args(payload);
1477   SilcClientEntry client_entry;
1478   unsigned char *tmp;
1479   SilcUInt32 tmp_len;
1480   SilcID id;
1481   SilcStatus error;
1482
1483   /* Get error */
1484   tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
1485   if (!tmp && tmp_len != 1)
1486     goto out;
1487   error = (SilcStatus)tmp[0];
1488
1489   SILC_LOG_DEBUG(("Notify: ERROR (%d)", error));
1490
1491   /* Handle the error */
1492   if (error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1493     if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL))
1494       goto out;
1495     client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
1496     if (client_entry && client_entry != conn->local_entry) {
1497       silc_client_remove_from_channels(client, conn, client_entry);
1498       silc_client_del_client(client, conn, client_entry);
1499       silc_client_unref_client(client, conn, client_entry);
1500     }
1501   }
1502
1503   /* Notify application. */
1504   NOTIFY(client, conn, type, error);
1505
1506  out:
1507   /** Notify processed */
1508   silc_fsm_next(fsm, silc_client_notify_processed);
1509   return SILC_FSM_CONTINUE;
1510 }
1511
1512 /******************************** WATCH *************************************/
1513
1514 /* Received notify about some client we are watching */
1515
1516 SILC_FSM_STATE(silc_client_notify_watch)
1517 {
1518   SilcClientConnection conn = fsm_context;
1519   SilcClient client = conn->client;
1520   SilcClientNotify notify = state_context;
1521   SilcNotifyPayload payload = notify->payload;
1522   SilcNotifyType type = silc_notify_get_type(payload);
1523   SilcArgumentPayload args = silc_notify_get_args(payload);
1524   SilcClientEntry client_entry = NULL;
1525   SilcNotifyType ntype = 0;
1526   unsigned char *pk, *tmp;
1527   SilcUInt32 mode, pk_len, tmp_len;
1528   SilcPublicKey public_key = NULL;
1529   SilcID id;
1530
1531   SILC_LOG_DEBUG(("Notify: WATCH"));
1532
1533   /* Get sender Client ID */
1534   if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
1535     goto out;
1536
1537   /* Find client entry and if not found resolve it */
1538   client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
1539   if (!client_entry || !client_entry->internal.valid) {
1540     /** Resolve client */
1541     silc_client_unref_client(client, conn, client_entry);
1542     SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
1543                                          client, conn, &id.u.client_id, NULL,
1544                                          silc_client_notify_resolved,
1545                                          notify));
1546     /* NOT REACHED */
1547   }
1548
1549   /* Get user mode */
1550   tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
1551   if (!tmp || tmp_len != 4)
1552     goto out;
1553   SILC_GET32_MSB(mode, tmp);
1554
1555   /* Get notify type */
1556   tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
1557   if (tmp && tmp_len != 2)
1558     goto out;
1559   if (tmp)
1560     SILC_GET16_MSB(ntype, tmp);
1561
1562   /* Get nickname */
1563   tmp = silc_argument_get_arg_type(args, 2, NULL);
1564   if (tmp) {
1565     char *tmp_nick = NULL;
1566
1567     silc_client_nickname_parse(client, conn, client_entry->nickname,
1568                                &tmp_nick);
1569
1570     /* If same nick, the client was new to us and has become "present"
1571        to network.  Send NULL as nick to application. */
1572     if (tmp_nick && silc_utf8_strcasecmp(tmp, tmp_nick))
1573       tmp = NULL;
1574
1575     silc_free(tmp_nick);
1576   }
1577
1578   /* Get public key, if present */
1579   pk = silc_argument_get_arg_type(args, 5, &pk_len);
1580   if (pk && !client_entry->public_key) {
1581     if (silc_public_key_payload_decode(pk, pk_len, &public_key)) {
1582       client_entry->public_key = public_key;
1583       public_key = NULL;
1584     }
1585   }
1586
1587   /* Notify application. */
1588   NOTIFY(client, conn, type, client_entry, tmp, mode, ntype,
1589          client_entry->public_key);
1590
1591   client_entry->mode = mode;
1592
1593   /* Remove client that left the network. */
1594   if (ntype == SILC_NOTIFY_TYPE_SIGNOFF ||
1595       ntype == SILC_NOTIFY_TYPE_SERVER_SIGNOFF ||
1596       ntype == SILC_NOTIFY_TYPE_KILLED) {
1597     silc_client_remove_from_channels(client, conn, client_entry);
1598     client_entry->internal.valid = FALSE;
1599     silc_client_del_client(client, conn, client_entry);
1600   }
1601
1602   if (public_key)
1603     silc_pkcs_public_key_free(public_key);
1604
1605  out:
1606   /** Notify processed */
1607   silc_client_unref_client(client, conn, client_entry);
1608   silc_fsm_next(fsm, silc_client_notify_processed);
1609   return SILC_FSM_CONTINUE;
1610 }