10c36a324f8fa5bae0fd151cbc1dc5789f061133
[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;
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   /* Notify application */
528   if (client_entry->internal.valid)
529     NOTIFY(client, conn, type, client_entry, tmp);
530
531   /* Remove from channel */
532   if (packet->dst_id_type == SILC_ID_CHANNEL) {
533     if (silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CHANNEL,
534                        &id.u.channel_id, sizeof(id.u.channel_id))) {
535       channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
536       if (channel) {
537         silc_client_remove_from_channel(client, conn, channel, client_entry);
538         silc_client_unref_channel(client, conn, channel);
539       }
540     }
541   }
542
543   /* Delete client */
544   client_entry->internal.valid = FALSE;
545   silc_client_del_client(client, conn, client_entry);
546   silc_client_unref_client(client, conn, client_entry);
547
548  out:
549   /** Notify processed */
550   silc_fsm_next(fsm, silc_client_notify_processed);
551   return SILC_FSM_CONTINUE;
552 }
553
554 /******************************** TOPIC_SET *********************************/
555
556 /* Someone set topic on a channel */
557
558 SILC_FSM_STATE(silc_client_notify_topic_set)
559 {
560   SilcClientConnection conn = fsm_context;
561   SilcClient client = conn->client;
562   SilcClientNotify notify = state_context;
563   SilcNotifyPayload payload = notify->payload;
564   SilcPacket packet = notify->packet;
565   SilcNotifyType type = silc_notify_get_type(payload);
566   SilcArgumentPayload args = silc_notify_get_args(payload);
567   SilcClientEntry client_entry = NULL;
568   SilcChannelEntry channel = NULL, channel_entry = NULL;
569   SilcServerEntry server = NULL;
570   void *entry;
571   unsigned char *tmp;
572   SilcUInt32 tmp_len;
573   SilcID id;
574
575   SILC_LOG_DEBUG(("Notify: TOPIC_SET"));
576
577   /* Get channel entry */
578   if (!silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CHANNEL,
579                       &id.u.channel_id, sizeof(id.u.channel_id)))
580     goto out;
581   channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
582   if (!channel)
583     goto out;
584
585   /* If channel is being resolved handle notify after resolving */
586   if (channel->internal.resolve_cmd_ident) {
587     silc_client_unref_channel(client, conn, channel);
588     SILC_FSM_CALL(silc_client_command_pending(
589                                       conn, SILC_COMMAND_NONE,
590                                       channel->internal.resolve_cmd_ident,
591                                       silc_client_notify_wait_continue,
592                                       notify));
593     /* NOT REACHED */
594   }
595
596   /* Get ID of topic changer */
597   if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
598     goto out;
599
600   /* Get topic */
601   tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
602   if (!tmp)
603     goto out;
604
605   if (id.type == SILC_ID_CLIENT) {
606     /* Find Client entry */
607     client_entry = notify->client_entry;
608     if (!client_entry) {
609       client_entry = silc_client_get_client(client, conn, &id.u.client_id);
610       if (!client_entry || !client_entry->internal.valid) {
611         /** Resolve client */
612         notify->channel = channel;
613         notify->client_entry = client_entry;
614         SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
615                       silc_client_get_client_by_id_resolve(
616                                            client, conn, &id.u.client_id, NULL,
617                                            silc_client_notify_resolved,
618                                            notify));
619         /* NOT REACHED */
620       }
621     }
622
623     /* If client is not on channel, ignore this notify */
624     if (!silc_client_on_channel(channel, client_entry))
625       goto out;
626
627     entry = client_entry;
628   } else if (id.type == SILC_ID_SERVER) {
629     /* Find Server entry */
630     server = silc_client_get_server_by_id(client, conn, &id.u.server_id);
631     if (!server) {
632       /** Resolve server */
633       notify->channel = channel;
634       SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
635                     silc_client_get_server_by_id_resolve(
636                                            client, conn, &id.u.server_id,
637                                            silc_client_notify_resolved,
638                                            notify));
639       /* NOT REACHED */
640     }
641     entry = server;
642   } else {
643     /* Find Channel entry */
644     channel_entry = silc_client_get_channel_by_id(client, conn,
645                                                   &id.u.channel_id);
646     if (!channel_entry) {
647       /** Resolve channel */
648       notify->channel = channel;
649       SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
650                     silc_client_get_channel_by_id_resolve(
651                                     client, conn, &id.u.channel_id,
652                                     silc_client_notify_resolved,
653                                     notify));
654       /* NOT REACHED */
655     }
656     entry = channel_entry;
657   }
658
659   silc_rwlock_wrlock(channel->internal.lock);
660   silc_free(channel->topic);
661   channel->topic = silc_memdup(tmp, strlen(tmp));
662   silc_rwlock_unlock(channel->internal.lock);
663
664   /* Notify application. */
665   NOTIFY(client, conn, type, id.type, entry, channel->topic, channel);
666
667   if (client_entry)
668     silc_client_unref_client(client, conn, client_entry);
669   if (server)
670     silc_client_unref_server(client, conn, server);
671   if (channel_entry)
672     silc_client_unref_channel(client, conn, channel_entry);
673
674  out:
675   /** Notify processed */
676   silc_client_unref_channel(client, conn, channel);
677   silc_fsm_next(fsm, silc_client_notify_processed);
678   return SILC_FSM_CONTINUE;
679 }
680
681 /****************************** NICK_CHANGE *********************************/
682
683 /* Someone changed their nickname on a channel */
684
685 SILC_FSM_STATE(silc_client_notify_nick_change)
686 {
687   SilcClientConnection conn = fsm_context;
688   SilcClient client = conn->client;
689   SilcClientNotify notify = state_context;
690   SilcNotifyPayload payload = notify->payload;
691   SilcNotifyType type = silc_notify_get_type(payload);
692   SilcArgumentPayload args = silc_notify_get_args(payload);
693   SilcClientEntry client_entry = NULL;
694   unsigned char *tmp, oldnick[128 + 1];
695   SilcUInt32 tmp_len;
696   SilcID id, id2;
697   SilcBool valid;
698
699   SILC_LOG_DEBUG(("Notify: NICK_CHANGE"));
700
701   /* Get ID */
702   if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
703     goto out;
704
705   /* Ignore my ID */
706   if (conn->local_id &&
707       SILC_ID_CLIENT_COMPARE(&id.u.client_id, conn->local_id))
708     goto out;
709
710   /* Get new Client ID */
711   if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id2, NULL))
712     goto out;
713
714   /* Ignore my ID */
715   if (conn->local_id &&
716       SILC_ID_CLIENT_COMPARE(&id2.u.client_id, conn->local_id))
717     goto out;
718
719   /* Find old client entry.  If we don't have the entry, we ignore this
720      notify. */
721   client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
722   if (!client_entry)
723     goto out;
724   valid = client_entry->internal.valid;
725
726   /* Take the new nickname */
727   tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
728   if (!tmp)
729     goto out;
730
731   silc_rwlock_wrlock(client_entry->internal.lock);
732
733   /* Check whether nickname changed at all.  It is possible that nick
734      change notify is received but nickname didn't change, only the
735      ID changes.  If Client ID hash match, nickname didn't change. */
736   if (SILC_ID_COMPARE_HASH(&client_entry->id, &id2.u.client_id) &&
737       silc_utf8_strcasecmp(tmp, client_entry->nickname)) {
738     /* Nickname didn't change.  Update only Client ID.  We don't notify
739        application because nickname didn't change. */
740     silc_mutex_lock(conn->internal->lock);
741     silc_idcache_update_by_context(conn->internal->client_cache, client_entry,
742                                    &id2.u.client_id, NULL, FALSE);
743     silc_mutex_unlock(conn->internal->lock);
744     silc_rwlock_unlock(client_entry->internal.lock);
745     goto out;
746   }
747
748   /* Change the nickname */
749   memcpy(oldnick, client_entry->nickname, sizeof(client_entry->nickname));
750   if (!silc_client_change_nickname(client, conn, client_entry, tmp,
751                                    &id2.u.client_id, NULL, 0)) {
752     silc_rwlock_unlock(client_entry->internal.lock);
753     goto out;
754   }
755
756   silc_rwlock_unlock(client_entry->internal.lock);
757
758   /* Notify application, if client entry is valid.  We do not send nick change
759      notify for entries that were invalid (application doesn't know them). */
760   if (valid)
761     NOTIFY(client, conn, type, client_entry, oldnick, client_entry->nickname);
762
763  out:
764   /** Notify processed */
765   silc_client_unref_client(client, conn, client_entry);
766   silc_fsm_next(fsm, silc_client_notify_processed);
767   return SILC_FSM_CONTINUE;
768 }
769
770 /****************************** CMODE_CHANGE ********************************/
771
772 /* Someone changed channel mode */
773
774 SILC_FSM_STATE(silc_client_notify_cmode_change)
775 {
776   SilcClientConnection conn = fsm_context;
777   SilcClient client = conn->client;
778   SilcClientNotify notify = state_context;
779   SilcNotifyPayload payload = notify->payload;
780   SilcPacket packet = notify->packet;
781   SilcNotifyType type = silc_notify_get_type(payload);
782   SilcArgumentPayload args = silc_notify_get_args(payload);
783   SilcClientEntry client_entry = NULL;
784   SilcChannelEntry channel = NULL, channel_entry = NULL;
785   SilcServerEntry server = NULL;
786   void *entry;
787   unsigned char *tmp;
788   SilcUInt32 tmp_len, mode;
789   SilcID id;
790   char *passphrase, *cipher, *hmac;
791   SilcPublicKey founder_key = NULL;
792   SilcDList chpks = NULL;
793
794   SILC_LOG_DEBUG(("Notify: CMODE_CHANGE"));
795
796   /* Get channel entry */
797   if (!silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CHANNEL,
798                       &id.u.channel_id, sizeof(id.u.channel_id)))
799     goto out;
800   channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
801   if (!channel)
802     goto out;
803
804   /* If channel is being resolved handle notify after resolving */
805   if (channel->internal.resolve_cmd_ident) {
806     silc_client_unref_channel(client, conn, channel);
807     SILC_FSM_CALL(silc_client_command_pending(
808                                       conn, SILC_COMMAND_NONE,
809                                       channel->internal.resolve_cmd_ident,
810                                       silc_client_notify_wait_continue,
811                                       notify));
812     /* NOT REACHED */
813   }
814
815   /* Get the mode */
816   tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
817   if (!tmp)
818     goto out;
819   SILC_GET32_MSB(mode, tmp);
820
821   /* Get ID of who changed the mode */
822   if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
823     goto out;
824
825   if (id.type == SILC_ID_CLIENT) {
826     /* Find Client entry */
827     client_entry = notify->client_entry;
828     if (!client_entry) {
829       client_entry = silc_client_get_client(client, conn, &id.u.client_id);
830       if (!client_entry || !client_entry->internal.valid) {
831         /** Resolve client */
832         notify->channel = channel;
833         notify->client_entry = client_entry;
834         SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
835                       silc_client_get_client_by_id_resolve(
836                                            client, conn, &id.u.client_id, NULL,
837                                            silc_client_notify_resolved,
838                                            notify));
839         /* NOT REACHED */
840       }
841     }
842
843     /* If client is not on channel, ignore this notify */
844     if (!silc_client_on_channel(channel, client_entry))
845       goto out;
846
847     entry = client_entry;
848   } else if (id.type == SILC_ID_SERVER) {
849     /* Find Server entry */
850     server = silc_client_get_server_by_id(client, conn, &id.u.server_id);
851     if (!server) {
852       /** Resolve server */
853       notify->channel = channel;
854       SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
855                     silc_client_get_server_by_id_resolve(
856                                            client, conn, &id.u.server_id,
857                                            silc_client_notify_resolved,
858                                            notify));
859       /* NOT REACHED */
860     }
861     entry = server;
862   } else {
863     /* Find Channel entry */
864     channel_entry = silc_client_get_channel_by_id(client, conn,
865                                                   &id.u.channel_id);
866     if (!channel_entry) {
867       /** Resolve channel */
868       notify->channel = channel;
869       SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
870                     silc_client_get_channel_by_id_resolve(
871                                     client, conn, &id.u.channel_id,
872                                     silc_client_notify_resolved,
873                                     notify));
874       /* NOT REACHED */
875     }
876     entry = channel_entry;
877   }
878
879   silc_rwlock_wrlock(channel->internal.lock);
880
881   /* Get the channel founder key if it was set */
882   tmp = silc_argument_get_arg_type(args, 6, &tmp_len);
883   if (tmp) {
884     if (!silc_public_key_payload_decode(tmp, tmp_len, &founder_key)) {
885       silc_rwlock_unlock(channel->internal.lock);
886       goto out;
887     }
888     if (!channel->founder_key) {
889       channel->founder_key = founder_key;
890       founder_key = NULL;
891     }
892   }
893
894   /* Get the cipher */
895   cipher = silc_argument_get_arg_type(args, 3, &tmp_len);
896
897   /* Get the hmac */
898   hmac = silc_argument_get_arg_type(args, 4, &tmp_len);
899   if (hmac) {
900     unsigned char hash[SILC_HASH_MAXLEN];
901     SilcHmac newhmac;
902
903     if (!silc_hmac_alloc(hmac, NULL, &newhmac)) {
904       silc_rwlock_unlock(channel->internal.lock);
905       goto out;
906     }
907
908     /* Get HMAC key from the old HMAC context, and update it to the new one */
909     tmp = (unsigned char *)silc_hmac_get_key(channel->internal.hmac, &tmp_len);
910     if (tmp) {
911       silc_hash_make(silc_hmac_get_hash(newhmac), tmp, tmp_len, hash);
912       silc_hmac_set_key(newhmac, hash,
913                         silc_hash_len(silc_hmac_get_hash(newhmac)));
914       if (channel->internal.hmac)
915         silc_hmac_free(channel->internal.hmac);
916       channel->internal.hmac = newhmac;
917       memset(hash, 0, sizeof(hash));
918     }
919   }
920
921   /* Get the passphrase if it was set */
922   passphrase = silc_argument_get_arg_type(args, 5, &tmp_len);
923
924   /* Get user limit */
925   tmp = silc_argument_get_arg_type(args, 8, &tmp_len);
926   if (tmp && tmp_len == 4)
927     SILC_GET32_MSB(channel->user_limit, tmp);
928   if (!(channel->mode & SILC_CHANNEL_MODE_ULIMIT))
929     channel->user_limit = 0;
930
931   /* Get the channel public key that was added or removed */
932   tmp = silc_argument_get_arg_type(args, 7, &tmp_len);
933   if (tmp)
934     silc_client_channel_save_public_keys(channel, tmp, tmp_len, FALSE);
935   else if (channel->mode & SILC_CHANNEL_MODE_CHANNEL_AUTH)
936     silc_client_channel_save_public_keys(channel, NULL, 0, TRUE);
937
938   /* Save the new mode */
939   channel->mode = mode;
940
941   silc_rwlock_unlock(channel->internal.lock);
942
943   /* Notify application. */
944   NOTIFY(client, conn, type, id.type, entry, mode, cipher, hmac,
945          passphrase, channel->founder_key, chpks, channel);
946
947  out:
948   if (founder_key)
949     silc_pkcs_public_key_free(founder_key);
950   if (chpks)
951     silc_argument_list_free(chpks, SILC_ARGUMENT_PUBLIC_KEY);
952   if (client_entry)
953     silc_client_unref_client(client, conn, client_entry);
954   if (server)
955     silc_client_unref_server(client, conn, server);
956   if (channel_entry)
957     silc_client_unref_channel(client, conn, channel_entry);
958   silc_client_unref_channel(client, conn, channel);
959
960   /** Notify processed */
961   silc_fsm_next(fsm, silc_client_notify_processed);
962   return SILC_FSM_CONTINUE;
963 }
964
965 /***************************** CUMODE_CHANGE ********************************/
966
967 /* Someone changed a user's mode on a channel */
968
969 SILC_FSM_STATE(silc_client_notify_cumode_change)
970 {
971   SilcClientConnection conn = fsm_context;
972   SilcClient client = conn->client;
973   SilcClientNotify notify = state_context;
974   SilcNotifyPayload payload = notify->payload;
975   SilcPacket packet = notify->packet;
976   SilcNotifyType type = silc_notify_get_type(payload);
977   SilcArgumentPayload args = silc_notify_get_args(payload);
978   SilcClientEntry client_entry = NULL, client_entry2 = NULL;
979   SilcChannelEntry channel = NULL, channel_entry = NULL;
980   SilcServerEntry server = NULL;
981   SilcChannelUser chu;
982   void *entry;
983   unsigned char *tmp;
984   SilcUInt32 tmp_len, mode;
985   SilcID id, id2;
986
987   SILC_LOG_DEBUG(("Notify: CUMODE_CHANGE"));
988
989   /* Get channel entry */
990   if (!silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CHANNEL,
991                       &id.u.channel_id, sizeof(id.u.channel_id)))
992     goto out;
993   channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
994   if (!channel)
995     goto out;
996
997   /* If channel is being resolved handle notify after resolving */
998   if (channel->internal.resolve_cmd_ident) {
999     silc_client_unref_channel(client, conn, channel);
1000     SILC_FSM_CALL(silc_client_command_pending(
1001                                       conn, SILC_COMMAND_NONE,
1002                                       channel->internal.resolve_cmd_ident,
1003                                       silc_client_notify_wait_continue,
1004                                       notify));
1005     /* NOT REACHED */
1006   }
1007
1008   /* Get target Client ID */
1009   if (!silc_argument_get_decoded(args, 3, SILC_ARGUMENT_ID, &id2, NULL))
1010     goto out;
1011
1012   /* Find target Client entry */
1013   client_entry2 = silc_client_get_client_by_id(client, conn, &id2.u.client_id);
1014   if (!client_entry2 || !client_entry2->internal.valid) {
1015     /** Resolve client */
1016     silc_client_unref_client(client, conn, client_entry2);
1017     SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
1018                                          client, conn, &id2.u.client_id, NULL,
1019                                          silc_client_notify_resolved,
1020                                          notify));
1021     /* NOT REACHED */
1022   }
1023
1024   /* If target client is not on channel, ignore this notify */
1025   if (!silc_client_on_channel(channel, client_entry2))
1026     goto out;
1027
1028   /* Get the mode */
1029   tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
1030   if (!tmp)
1031     goto out;
1032   SILC_GET32_MSB(mode, tmp);
1033
1034   /* Get ID of mode changer */
1035   if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
1036     goto out;
1037
1038   if (id.type == SILC_ID_CLIENT) {
1039     /* Find Client entry */
1040     client_entry = notify->client_entry;
1041     if (!client_entry) {
1042       client_entry = silc_client_get_client(client, conn, &id.u.client_id);
1043       if (!client_entry || !client_entry->internal.valid) {
1044         /** Resolve client */
1045         notify->channel = channel;
1046         notify->client_entry = client_entry;
1047         SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
1048                       silc_client_get_client_by_id_resolve(
1049                                            client, conn, &id.u.client_id, NULL,
1050                                            silc_client_notify_resolved,
1051                                            notify));
1052         /* NOT REACHED */
1053       }
1054     }
1055
1056     /* If client is not on channel, ignore this notify */
1057     if (!silc_client_on_channel(channel, client_entry))
1058       goto out;
1059
1060     entry = client_entry;
1061   } else if (id.type == SILC_ID_SERVER) {
1062     /* Find Server entry */
1063     server = silc_client_get_server_by_id(client, conn, &id.u.server_id);
1064     if (!server) {
1065       /** Resolve server */
1066       notify->channel = channel;
1067       SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
1068                     silc_client_get_server_by_id_resolve(
1069                                            client, conn, &id.u.server_id,
1070                                            silc_client_notify_resolved,
1071                                            notify));
1072       /* NOT REACHED */
1073     }
1074     entry = server;
1075   } else {
1076     /* Find Channel entry */
1077     channel_entry = silc_client_get_channel_by_id(client, conn,
1078                                                   &id.u.channel_id);
1079     if (!channel_entry) {
1080       /** Resolve channel */
1081       notify->channel = channel;
1082       SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
1083                     silc_client_get_channel_by_id_resolve(
1084                                     client, conn, &id.u.channel_id,
1085                                     silc_client_notify_resolved,
1086                                     notify));
1087       /* NOT REACHED */
1088     }
1089     entry = channel_entry;
1090   }
1091
1092   /* Save the mode */
1093   silc_rwlock_wrlock(channel->internal.lock);
1094   chu = silc_client_on_channel(channel, client_entry2);
1095   if (chu)
1096     chu->mode = mode;
1097   silc_rwlock_unlock(channel->internal.lock);
1098
1099   /* Notify application. */
1100   NOTIFY(client, conn, type, id.type, entry, mode, client_entry2, channel);
1101
1102  out:
1103   silc_client_unref_client(client, conn, client_entry2);
1104   if (client_entry)
1105     silc_client_unref_client(client, conn, client_entry);
1106   if (server)
1107     silc_client_unref_server(client, conn, server);
1108   if (channel_entry)
1109     silc_client_unref_channel(client, conn, channel_entry);
1110   silc_client_unref_channel(client, conn, channel);
1111
1112   /** Notify processed */
1113   silc_fsm_next(fsm, silc_client_notify_processed);
1114   return SILC_FSM_CONTINUE;
1115 }
1116
1117 /********************************* MOTD *************************************/
1118
1119 /* Received Message of the day */
1120
1121 SILC_FSM_STATE(silc_client_notify_motd)
1122 {
1123   SilcClientConnection conn = fsm_context;
1124   SilcClient client = conn->client;
1125   SilcClientNotify notify = state_context;
1126   SilcNotifyPayload payload = notify->payload;
1127   SilcNotifyType type = silc_notify_get_type(payload);
1128   SilcArgumentPayload args = silc_notify_get_args(payload);
1129   unsigned char *tmp;
1130   SilcUInt32 tmp_len;
1131
1132   SILC_LOG_DEBUG(("Notify: MOTD"));
1133
1134   /* Get motd */
1135   tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
1136   if (!tmp)
1137     goto out;
1138
1139   /* Notify application */
1140   NOTIFY(client, conn, type, tmp);
1141
1142  out:
1143   /** Notify processed */
1144   silc_fsm_next(fsm, silc_client_notify_processed);
1145   return SILC_FSM_CONTINUE;
1146 }
1147
1148 /**************************** CHANNEL CHANGE ********************************/
1149
1150 /* Router has enforced a new ID to a channel, change it */
1151
1152 SILC_FSM_STATE(silc_client_notify_channel_change)
1153 {
1154   SilcClientConnection conn = fsm_context;
1155   SilcClient client = conn->client;
1156   SilcClientNotify notify = state_context;
1157   SilcNotifyPayload payload = notify->payload;
1158   SilcNotifyType type = silc_notify_get_type(payload);
1159   SilcArgumentPayload args = silc_notify_get_args(payload);
1160   SilcChannelEntry channel = NULL;
1161   SilcID id;
1162
1163   SILC_LOG_DEBUG(("Notify: CHANNEL_CHANGE"));
1164
1165   /* Get the old ID */
1166   if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
1167     goto out;
1168
1169   /* Get the channel entry */
1170   channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
1171   if (!channel)
1172     goto out;
1173
1174   /* If channel is being resolved handle notify after resolving */
1175   if (channel->internal.resolve_cmd_ident) {
1176     silc_client_unref_channel(client, conn, channel);
1177     SILC_FSM_CALL(silc_client_command_pending(
1178                                       conn, SILC_COMMAND_NONE,
1179                                       channel->internal.resolve_cmd_ident,
1180                                       silc_client_notify_wait_continue,
1181                                       notify));
1182     /* NOT REACHED */
1183   }
1184
1185   /* Get the new ID */
1186   if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL))
1187     goto out;
1188
1189   /* Replace the Channel ID */
1190   if (!silc_client_replace_channel_id(client, conn, channel, &id.u.channel_id))
1191     goto out;
1192
1193   /* Notify application */
1194   NOTIFY(client, conn, type, channel, channel);
1195
1196  out:
1197   /** Notify processed */
1198   silc_client_unref_channel(client, conn, channel);
1199   silc_fsm_next(fsm, silc_client_notify_processed);
1200   return SILC_FSM_CONTINUE;
1201 }
1202
1203 /******************************** KICKED ************************************/
1204
1205 /* Some client was kicked from a channel */
1206
1207 SILC_FSM_STATE(silc_client_notify_kicked)
1208 {
1209   SilcClientConnection conn = fsm_context;
1210   SilcClient client = conn->client;
1211   SilcClientNotify notify = state_context;
1212   SilcNotifyPayload payload = notify->payload;
1213   SilcPacket packet = notify->packet;
1214   SilcNotifyType type = silc_notify_get_type(payload);
1215   SilcArgumentPayload args = silc_notify_get_args(payload);
1216   SilcClientEntry client_entry, client_entry2;
1217   SilcChannelEntry channel = NULL;
1218   unsigned char *tmp;
1219   SilcUInt32 tmp_len;
1220   SilcID id;
1221
1222   SILC_LOG_DEBUG(("Notify: KICKED"));
1223
1224   /* Get channel entry */
1225   if (!silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CHANNEL,
1226                       &id.u.channel_id, sizeof(id.u.channel_id)))
1227     goto out;
1228   channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
1229   if (!channel)
1230     goto out;
1231
1232   /* If channel is being resolved handle notify after resolving */
1233   if (channel->internal.resolve_cmd_ident) {
1234     silc_client_unref_channel(client, conn, channel);
1235     SILC_FSM_CALL(silc_client_command_pending(
1236                                       conn, SILC_COMMAND_NONE,
1237                                       channel->internal.resolve_cmd_ident,
1238                                       silc_client_notify_wait_continue,
1239                                       notify));
1240     /* NOT REACHED */
1241   }
1242
1243   /* Get the kicked Client ID */
1244   if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
1245     goto out;
1246
1247   /* Find client entry */
1248   client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
1249   if (!client_entry)
1250     goto out;
1251
1252   /* Get kicker's Client ID */
1253   if (!silc_argument_get_decoded(args, 3, SILC_ARGUMENT_ID, &id, NULL))
1254     goto out;
1255
1256   /* Find kicker's client entry and if not found resolve it */
1257   client_entry2 = silc_client_get_client_by_id(client, conn, &id.u.client_id);
1258   if (!client_entry2 || !client_entry2->internal.valid) {
1259     /** Resolve client */
1260     silc_client_unref_client(client, conn, client_entry);
1261     silc_client_unref_client(client, conn, client_entry2);
1262     notify->channel = channel;
1263     SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
1264                   silc_client_get_client_by_id_resolve(
1265                                          client, conn, &id.u.client_id, NULL,
1266                                          silc_client_notify_resolved,
1267                                          notify));
1268     /* NOT REACHED */
1269   }
1270
1271   /* Get comment */
1272   tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
1273
1274   /* Remove kicked client from channel */
1275   if (client_entry != conn->local_entry) {
1276     if (!silc_client_remove_from_channel(client, conn, channel, client_entry))
1277       goto out;
1278   }
1279
1280   /* Notify application. */
1281   NOTIFY(client, conn, type, client_entry, tmp, client_entry2, channel);
1282
1283   /* If I was kicked from channel, remove the channel */
1284   if (client_entry == conn->local_entry) {
1285     if (conn->current_channel == channel)
1286       conn->current_channel = NULL;
1287     silc_client_empty_channel(client, conn, channel);
1288     silc_client_del_channel(client, conn, channel);
1289   }
1290
1291   silc_client_unref_client(client, conn, client_entry);
1292   silc_client_unref_client(client, conn, client_entry2);
1293
1294  out:
1295   /** Notify processed */
1296   silc_client_unref_channel(client, conn, channel);
1297   silc_fsm_next(fsm, silc_client_notify_processed);
1298   return SILC_FSM_CONTINUE;
1299 }
1300
1301 /******************************** KILLED ************************************/
1302
1303 /* Some client was killed from the network */
1304
1305 SILC_FSM_STATE(silc_client_notify_killed)
1306 {
1307   SilcClientConnection conn = fsm_context;
1308   SilcClient client = conn->client;
1309   SilcClientNotify notify = state_context;
1310   SilcNotifyPayload payload = notify->payload;
1311   SilcNotifyType type = silc_notify_get_type(payload);
1312   SilcArgumentPayload args = silc_notify_get_args(payload);
1313   SilcClientEntry client_entry = NULL, client_entry2 = NULL;
1314   SilcChannelEntry channel_entry = NULL;
1315   SilcServerEntry server = NULL;
1316   void *entry;
1317   char *comment;
1318   SilcUInt32 comment_len;
1319   SilcID id;
1320
1321   SILC_LOG_DEBUG(("Notify: KILLED"));
1322
1323   /* Get Client ID */
1324   if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
1325     goto out;
1326
1327   /* Find Client entry */
1328   client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
1329   if (!client_entry)
1330     goto out;
1331
1332   /* Get comment */
1333   comment = silc_argument_get_arg_type(args, 2, &comment_len);
1334
1335   /* Get killer's ID */
1336   if (!silc_argument_get_decoded(args, 3, SILC_ARGUMENT_ID, &id, NULL))
1337     goto out;
1338
1339   if (id.type == SILC_ID_CLIENT) {
1340     /* Find Client entry */
1341     client_entry2 = silc_client_get_client_by_id(client, conn,
1342                                                  &id.u.client_id);
1343     if (!client_entry2 || !client_entry2->internal.valid) {
1344       /** Resolve client */
1345       silc_client_unref_client(client, conn, client_entry);
1346       silc_client_unref_client(client, conn, client_entry2);
1347       SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
1348                                            client, conn, &id.u.client_id, NULL,
1349                                            silc_client_notify_resolved,
1350                                            notify));
1351       /* NOT REACHED */
1352     }
1353     entry = client_entry2;
1354   } else if (id.type == SILC_ID_SERVER) {
1355     /* Find Server entry */
1356     server = silc_client_get_server_by_id(client, conn, &id.u.server_id);
1357     if (!server) {
1358       /** Resolve server */
1359       SILC_FSM_CALL(silc_client_get_server_by_id_resolve(
1360                                            client, conn, &id.u.server_id,
1361                                            silc_client_notify_resolved,
1362                                            notify));
1363       /* NOT REACHED */
1364     }
1365     entry = server;
1366   } else {
1367     /* Find Channel entry */
1368     channel_entry = silc_client_get_channel_by_id(client, conn,
1369                                                   &id.u.channel_id);
1370     if (!channel_entry) {
1371       /** Resolve channel */
1372       SILC_FSM_CALL(silc_client_get_channel_by_id_resolve(
1373                                     client, conn, &id.u.channel_id,
1374                                     silc_client_notify_resolved,
1375                                     notify));
1376       /* NOT REACHED */
1377     }
1378     entry = channel_entry;
1379   }
1380
1381   /* Notify application. */
1382   NOTIFY(client, conn, type, client_entry, comment, id.type, entry);
1383
1384   /* Delete the killed client */
1385   if (client_entry != conn->local_entry) {
1386     silc_client_remove_from_channels(client, conn, client_entry);
1387     client_entry->internal.valid = FALSE;
1388     silc_client_del_client(client, conn, client_entry);
1389   }
1390
1391  out:
1392   silc_client_unref_client(client, conn, client_entry);
1393   if (client_entry2)
1394     silc_client_unref_client(client, conn, client_entry2);
1395   if (server)
1396     silc_client_unref_server(client, conn, server);
1397   if (channel_entry)
1398     silc_client_unref_channel(client, conn, channel_entry);
1399
1400   /** Notify processed */
1401   silc_fsm_next(fsm, silc_client_notify_processed);
1402   return SILC_FSM_CONTINUE;
1403 }
1404
1405 /**************************** SERVER SIGNOFF ********************************/
1406
1407 /* Some server quit SILC network.  Remove its clients from channels. */
1408
1409 SILC_FSM_STATE(silc_client_notify_server_signoff)
1410 {
1411   SilcClientConnection conn = fsm_context;
1412   SilcClient client = conn->client;
1413   SilcClientNotify notify = state_context;
1414   SilcNotifyPayload payload = notify->payload;
1415   SilcNotifyType type = silc_notify_get_type(payload);
1416   SilcArgumentPayload args = silc_notify_get_args(payload);
1417   SilcClientEntry client_entry;
1418   SilcServerEntry server_entry = NULL;
1419   SilcDList clients;
1420   SilcID id;
1421   int i;
1422
1423   SILC_LOG_DEBUG(("Notify: SERVER_SIGNOFF"));
1424
1425   clients = silc_dlist_init();
1426   if (!clients)
1427     goto out;
1428
1429   /* Get server ID */
1430   if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
1431     goto out;
1432
1433   /* Get server, in case we have it cached */
1434   server_entry = silc_client_get_server_by_id(client, conn, &id.u.server_id);
1435
1436   for (i = 1; i < silc_argument_get_arg_num(args); i++) {
1437     /* Get Client ID */
1438     if (!silc_argument_get_decoded(args, i + 1, SILC_ARGUMENT_ID, &id, NULL))
1439       goto out;
1440
1441     /* Get the client entry */
1442     client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
1443     if (client_entry && client_entry->internal.valid)
1444       silc_dlist_add(clients, client_entry);
1445   }
1446
1447   /* Notify application. */
1448   NOTIFY(client, conn, type, server_entry, clients);
1449
1450   /* Delete the clients */
1451   silc_dlist_start(clients);
1452   while ((client_entry = silc_dlist_get(clients))) {
1453     silc_client_remove_from_channels(client, conn, client_entry);
1454     client_entry->internal.valid = FALSE;
1455     silc_client_del_client(client, conn, client_entry);
1456   }
1457
1458  out:
1459   /** Notify processed */
1460   silc_client_unref_server(client, conn, server_entry);
1461   silc_client_list_free(client, conn, clients);
1462   silc_fsm_next(fsm, silc_client_notify_processed);
1463   return SILC_FSM_CONTINUE;
1464 }
1465
1466 /******************************** ERROR *************************************/
1467
1468 /* Some error occurred */
1469
1470 SILC_FSM_STATE(silc_client_notify_error)
1471 {
1472   SilcClientConnection conn = fsm_context;
1473   SilcClient client = conn->client;
1474   SilcClientNotify notify = state_context;
1475   SilcNotifyPayload payload = notify->payload;
1476   SilcNotifyType type = silc_notify_get_type(payload);
1477   SilcArgumentPayload args = silc_notify_get_args(payload);
1478   SilcClientEntry client_entry;
1479   unsigned char *tmp;
1480   SilcUInt32 tmp_len;
1481   SilcID id;
1482   SilcStatus error;
1483
1484   /* Get error */
1485   tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
1486   if (!tmp && tmp_len != 1)
1487     goto out;
1488   error = (SilcStatus)tmp[0];
1489
1490   SILC_LOG_DEBUG(("Notify: ERROR (%d)", error));
1491
1492   /* Handle the error */
1493   if (error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1494     if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL))
1495       goto out;
1496     client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
1497     if (client_entry && client_entry != conn->local_entry) {
1498       silc_client_remove_from_channels(client, conn, client_entry);
1499       silc_client_del_client(client, conn, client_entry);
1500       silc_client_unref_client(client, conn, client_entry);
1501     }
1502   }
1503
1504   /* Notify application. */
1505   NOTIFY(client, conn, type, error);
1506
1507  out:
1508   /** Notify processed */
1509   silc_fsm_next(fsm, silc_client_notify_processed);
1510   return SILC_FSM_CONTINUE;
1511 }
1512
1513 /******************************** WATCH *************************************/
1514
1515 /* Received notify about some client we are watching */
1516
1517 SILC_FSM_STATE(silc_client_notify_watch)
1518 {
1519   SilcClientConnection conn = fsm_context;
1520   SilcClient client = conn->client;
1521   SilcClientNotify notify = state_context;
1522   SilcNotifyPayload payload = notify->payload;
1523   SilcNotifyType type = silc_notify_get_type(payload);
1524   SilcArgumentPayload args = silc_notify_get_args(payload);
1525   SilcClientEntry client_entry = NULL;
1526   SilcNotifyType ntype = 0;
1527   unsigned char *pk, *tmp;
1528   SilcUInt32 mode, pk_len, tmp_len;
1529   SilcPublicKey public_key = NULL;
1530   SilcID id;
1531
1532   SILC_LOG_DEBUG(("Notify: WATCH"));
1533
1534   /* Get sender Client ID */
1535   if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
1536     goto out;
1537
1538   /* Find client entry and if not found resolve it */
1539   client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
1540   if (!client_entry || !client_entry->internal.valid) {
1541     /** Resolve client */
1542     silc_client_unref_client(client, conn, client_entry);
1543     SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
1544                                          client, conn, &id.u.client_id, NULL,
1545                                          silc_client_notify_resolved,
1546                                          notify));
1547     /* NOT REACHED */
1548   }
1549
1550   /* Get user mode */
1551   tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
1552   if (!tmp || tmp_len != 4)
1553     goto out;
1554   SILC_GET32_MSB(mode, tmp);
1555
1556   /* Get notify type */
1557   tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
1558   if (tmp && tmp_len != 2)
1559     goto out;
1560   if (tmp)
1561     SILC_GET16_MSB(ntype, tmp);
1562
1563   /* Get nickname */
1564   tmp = silc_argument_get_arg_type(args, 2, NULL);
1565   if (tmp) {
1566     char *tmp_nick = NULL;
1567
1568     silc_client_nickname_parse(client, conn, client_entry->nickname,
1569                                &tmp_nick);
1570
1571     /* If same nick, the client was new to us and has become "present"
1572        to network.  Send NULL as nick to application. */
1573     if (tmp_nick && silc_utf8_strcasecmp(tmp, tmp_nick))
1574       tmp = NULL;
1575
1576     silc_free(tmp_nick);
1577   }
1578
1579   /* Get public key, if present */
1580   pk = silc_argument_get_arg_type(args, 5, &pk_len);
1581   if (pk && !client_entry->public_key) {
1582     if (silc_public_key_payload_decode(pk, pk_len, &public_key)) {
1583       client_entry->public_key = public_key;
1584       public_key = NULL;
1585     }
1586   }
1587
1588   /* Notify application. */
1589   NOTIFY(client, conn, type, client_entry, tmp, mode, ntype,
1590          client_entry->public_key);
1591
1592   client_entry->mode = mode;
1593
1594   /* Remove client that left the network. */
1595   if (ntype == SILC_NOTIFY_TYPE_SIGNOFF ||
1596       ntype == SILC_NOTIFY_TYPE_SERVER_SIGNOFF ||
1597       ntype == SILC_NOTIFY_TYPE_KILLED) {
1598     silc_client_remove_from_channels(client, conn, client_entry);
1599     client_entry->internal.valid = FALSE;
1600     silc_client_del_client(client, conn, client_entry);
1601   }
1602
1603   if (public_key)
1604     silc_pkcs_public_key_free(public_key);
1605
1606  out:
1607   /** Notify processed */
1608   silc_client_unref_client(client, conn, client_entry);
1609   silc_fsm_next(fsm, silc_client_notify_processed);
1610   return SILC_FSM_CONTINUE;
1611 }