b4d4a78e8e166228f6c0b6e4f80f49099f3438cd
[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 - 2001 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; either version 2 of the License, or
12   (at your option) any later version.
13   
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19 */
20 /* $Id$ */
21 /* This file includes the Notify packet handling. Notify packets are
22    important packets sent by the server. They tell different things to the
23    client such as nick changes, mode changes etc. */
24
25 #include "clientlibincludes.h"
26 #include "client_internal.h"
27
28 typedef struct {
29   SilcPacketContext *packet;
30   void *context;
31   SilcSocketConnection sock;
32 } *SilcClientNotifyResolve;
33
34 /* Called when notify is received and some async operation (such as command)
35    is required before processing the notify message. This calls again the
36    silc_client_notify_by_server and reprocesses the original notify packet. */
37
38 static void silc_client_notify_by_server_pending(void *context, void *context2)
39 {
40   SilcClientNotifyResolve res = (SilcClientNotifyResolve)context;
41   SilcClientCommandReplyContext reply = 
42     (SilcClientCommandReplyContext)context2;
43
44   if (reply) {
45     SilcCommandStatus status;
46     unsigned char *tmp = silc_argument_get_arg_type(reply->args, 1, NULL);
47     SILC_GET16_MSB(status, tmp);
48     if (status != SILC_STATUS_OK) {
49       silc_socket_free(res->sock);
50       return;
51     }
52   }
53
54   silc_client_notify_by_server(res->context, res->sock, res->packet);
55   silc_socket_free(res->sock);
56 }
57
58 /* Destructor for the pending command callback */
59
60 static void silc_client_notify_by_server_destructor(void *context)
61 {
62   SilcClientNotifyResolve res = (SilcClientNotifyResolve)context;
63   silc_packet_context_free(res->packet);
64   silc_free(res);
65 }
66
67 /* Resolve client information from server by Client ID. */
68
69 static void silc_client_notify_by_server_resolve(SilcClient client,
70                                                  SilcClientConnection conn,
71                                                  SilcPacketContext *packet,
72                                                  SilcClientID *client_id)
73 {
74   SilcClientNotifyResolve res = silc_calloc(1, sizeof(*res));
75   SilcBuffer idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
76
77   res->packet = silc_packet_context_dup(packet);
78   res->context = client;
79   res->sock = silc_socket_dup(conn->sock);
80
81   silc_client_send_command(client, conn, SILC_COMMAND_WHOIS, ++conn->cmd_ident,
82                            1, 3, idp->data, idp->len);
83   silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
84                               silc_client_notify_by_server_destructor,
85                               silc_client_notify_by_server_pending, res);
86   silc_buffer_free(idp);
87 }
88
89 /* Received notify message from server */
90
91 void silc_client_notify_by_server(SilcClient client,
92                                   SilcSocketConnection sock,
93                                   SilcPacketContext *packet)
94 {
95   SilcBuffer buffer = packet->buffer;
96   SilcClientConnection conn = (SilcClientConnection)sock->user_data;
97   SilcNotifyPayload payload;
98   SilcNotifyType type;
99   SilcArgumentPayload args;
100
101   SilcIDPayload idp;
102   SilcClientID *client_id = NULL;
103   SilcChannelID *channel_id = NULL;
104   SilcServerID *server_id = NULL;
105   SilcClientEntry client_entry;
106   SilcClientEntry client_entry2;
107   SilcChannelEntry channel;
108   SilcChannelUser chu;
109   SilcServerEntry server;
110   SilcIDCacheEntry id_cache = NULL;
111   unsigned char *tmp;
112   uint32 tmp_len, mode;
113
114   SILC_LOG_DEBUG(("Start"));
115
116   payload = silc_notify_payload_parse(buffer->data, buffer->len);
117   if (!payload)
118     goto out;
119
120   type = silc_notify_get_type(payload);
121   args = silc_notify_get_args(payload);
122   if (!args)
123     goto out;
124
125   switch(type) {
126   case SILC_NOTIFY_TYPE_NONE:
127     /* Notify application */
128     client->ops->notify(client, conn, type, 
129                         silc_argument_get_arg_type(args, 1, NULL));
130     break;
131
132   case SILC_NOTIFY_TYPE_INVITE:
133     /* 
134      * Someone invited me to a channel. Find Client and Channel entries
135      * for the application.
136      */
137     
138     SILC_LOG_DEBUG(("Notify: INVITE"));
139
140     /* Get Channel ID */
141     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
142     if (!tmp)
143       goto out;
144
145     channel_id = silc_id_payload_parse_id(tmp, tmp_len);
146     if (!channel_id)
147       goto out;
148
149     /* Get the channel entry */
150     channel = NULL;
151     if (silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
152                                     &id_cache))
153       channel = (SilcChannelEntry)id_cache->context;
154
155     /* Get sender Client ID */
156     tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
157     if (!tmp)
158       goto out;
159
160     client_id = silc_id_payload_parse_id(tmp, tmp_len);
161     if (!client_id)
162       goto out;
163
164     /* Find Client entry and if not found query it */
165     client_entry = silc_client_get_client_by_id(client, conn, client_id);
166     if (!client_entry) {
167       silc_client_notify_by_server_resolve(client, conn, packet, client_id);
168       goto out;
169     }
170
171     /* Get the channel name */
172     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
173     if (!tmp)
174       goto out;
175
176     /* Notify application */
177     client->ops->notify(client, conn, type, channel, tmp, client_entry);
178     break;
179
180   case SILC_NOTIFY_TYPE_JOIN:
181     /*
182      * Someone has joined to a channel. Get their ID and nickname and
183      * cache them for later use.
184      */
185
186     SILC_LOG_DEBUG(("Notify: JOIN"));
187
188     /* Get Client ID */
189     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
190     if (!tmp)
191       goto out;
192
193     client_id = silc_id_payload_parse_id(tmp, tmp_len);
194     if (!client_id)
195       goto out;
196
197     /* Find Client entry and if not found query it */
198     client_entry = silc_client_get_client_by_id(client, conn, client_id);
199     if (!client_entry) {
200       silc_client_notify_by_server_resolve(client, conn, packet, client_id);
201       goto out;
202     }
203
204     /* If nickname or username hasn't been resolved, do so */
205     if (!client_entry->nickname || !client_entry->username) {
206       if (client_entry->status & SILC_CLIENT_STATUS_RESOLVING) {
207         client_entry->status &= ~SILC_CLIENT_STATUS_RESOLVING;
208         goto out;
209       }
210       client_entry->status |= SILC_CLIENT_STATUS_RESOLVING;
211       silc_client_notify_by_server_resolve(client, conn, packet, client_id);
212       goto out;
213     } else {
214       if (client_entry != conn->local_entry)
215         silc_client_nickname_format(client, conn, client_entry);
216     }
217
218     /* Get Channel ID */
219     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
220     if (!tmp)
221       goto out;
222
223     channel_id = silc_id_payload_parse_id(tmp, tmp_len);
224     if (!channel_id)
225       goto out;
226
227     /* Get channel entry */
228     if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
229                                      &id_cache))
230       break;
231
232     channel = (SilcChannelEntry)id_cache->context;
233
234     /* Add client to channel */
235     if (client_entry != conn->local_entry) {
236       chu = silc_calloc(1, sizeof(*chu));
237       chu->client = client_entry;
238       silc_list_add(channel->clients, chu);
239     }
240
241     /* Notify application. The channel entry is sent last as this notify
242        is for channel but application don't know it from the arguments
243        sent by server. */
244     client->ops->notify(client, conn, type, client_entry, channel);
245     break;
246
247   case SILC_NOTIFY_TYPE_LEAVE:
248     /*
249      * Someone has left a channel. We will remove it from the channel but
250      * we'll keep it in the cache in case we'll need it later.
251      */
252     
253     SILC_LOG_DEBUG(("Notify: LEAVE"));
254
255     /* Get Client ID */
256     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
257     if (!tmp)
258       goto out;
259
260     client_id = silc_id_payload_parse_id(tmp, tmp_len);
261     if (!client_id)
262       goto out;
263
264     /* Find Client entry */
265     client_entry = 
266       silc_client_get_client_by_id(client, conn, client_id);
267     if (!client_entry)
268       goto out;
269
270     /* Get channel entry */
271     channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
272                                 SILC_ID_CHANNEL);
273     if (!channel_id)
274       goto out;
275     if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
276                                  &id_cache))
277       break;
278
279     channel = (SilcChannelEntry)id_cache->context;
280
281     /* Remove client from channel */
282     silc_list_start(channel->clients);
283     while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
284       if (chu->client == client_entry) {
285         silc_list_del(channel->clients, chu);
286         silc_free(chu);
287         break;
288       }
289     }
290
291     /* Notify application. The channel entry is sent last as this notify
292        is for channel but application don't know it from the arguments
293        sent by server. */
294     client->ops->notify(client, conn, type, client_entry, channel);
295     break;
296
297   case SILC_NOTIFY_TYPE_SIGNOFF:
298     /*
299      * Someone left SILC. We'll remove it from all channels and from cache.
300      */
301
302     SILC_LOG_DEBUG(("Notify: SIGNOFF"));
303
304     /* Get Client ID */
305     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
306     if (!tmp)
307       goto out;
308
309     client_id = silc_id_payload_parse_id(tmp, tmp_len);
310     if (!client_id)
311       goto out;
312
313     /* Find Client entry */
314     client_entry = 
315       silc_client_get_client_by_id(client, conn, client_id);
316     if (!client_entry)
317       goto out;
318
319     /* Remove from all channels */
320     silc_client_remove_from_channels(client, conn, client_entry);
321
322     /* Remove from cache */
323     silc_idcache_del_by_context(conn->client_cache, client_entry);
324
325     /* Get signoff message */
326     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
327     if (tmp_len > 128)
328       tmp = NULL;
329
330     /* Notify application */
331     client->ops->notify(client, conn, type, client_entry, tmp);
332
333     /* Free data */
334     silc_client_del_client_entry(client, conn, client_entry);
335     break;
336
337   case SILC_NOTIFY_TYPE_TOPIC_SET:
338     /*
339      * Someone set the topic on a channel.
340      */
341
342     SILC_LOG_DEBUG(("Notify: TOPIC_SET"));
343
344     /* Get Client ID */
345     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
346     if (!tmp)
347       goto out;
348
349     idp = silc_id_payload_parse(tmp, tmp_len);
350     if (!idp)
351       goto out;
352
353     /* Find Client entry */
354     if (silc_id_payload_get_type(idp) == SILC_ID_CLIENT) {
355       client_id = silc_id_payload_parse_id(tmp, tmp_len);
356       if (!client_id) {
357         silc_id_payload_free(idp);
358         goto out;
359       }
360
361       /* Find Client entry */
362       client_entry = 
363         silc_client_get_client_by_id(client, conn, client_id);
364       if (!client_entry)
365         goto out;
366     } else if (silc_id_payload_get_type(idp) == SILC_ID_SERVER) {
367       server_id = silc_id_payload_parse_id(tmp, tmp_len);
368       if (!server_id) {
369         silc_id_payload_free(idp);
370         goto out;
371       }
372       
373       server = silc_client_get_server_by_id(client, conn, server_id);
374       if (!server) {
375         silc_id_payload_free(idp);
376         silc_free(server_id);
377         goto out;
378       }
379       
380       /* Save the pointer to the client_entry pointer */
381       client_entry = (SilcClientEntry)server;
382       silc_free(server_id);
383     } else {
384       channel_id = silc_id_payload_parse_id(tmp, tmp_len);
385       if (!channel_id) {
386         silc_id_payload_free(idp);
387         goto out;
388       }
389       
390       channel = silc_client_get_channel_by_id(client, conn, channel_id);
391       if (!channel) {
392         silc_id_payload_free(idp);
393         silc_free(channel_id);
394         goto out;
395       }
396       
397       /* Save the pointer to the client_entry pointer */
398       client_entry = (SilcClientEntry)channel;
399       silc_free(channel_id);
400     }
401
402     /* Get topic */
403     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
404     if (!tmp)
405       goto out;
406
407     /* Get channel entry */
408     channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
409                                 SILC_ID_CHANNEL);
410     if (!channel_id)
411       goto out;
412     if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
413                                  &id_cache))
414       break;
415
416     channel = (SilcChannelEntry)id_cache->context;
417
418     /* Notify application. The channel entry is sent last as this notify
419        is for channel but application don't know it from the arguments
420        sent by server. */
421     client->ops->notify(client, conn, type, silc_id_payload_get_type(idp),
422                         client_entry, tmp, channel);
423
424     silc_id_payload_free(idp);
425     break;
426
427   case SILC_NOTIFY_TYPE_NICK_CHANGE:
428     /*
429      * Someone changed their nickname. If we don't have entry for the new
430      * ID we will query it and return here after it's done. After we've
431      * returned we fetch the old entry and free it and notify the 
432      * application.
433      */
434
435     SILC_LOG_DEBUG(("Notify: NICK_CHANGE"));
436
437     /* Get old Client ID */
438     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
439     if (!tmp)
440       goto out;
441
442     client_id = silc_id_payload_parse_id(tmp, tmp_len);
443     if (!client_id)
444       goto out;
445
446     /* Ignore my ID */
447     if (SILC_ID_CLIENT_COMPARE(client_id, conn->local_id))
448       break;
449
450     /* Find old Client entry */
451     client_entry = silc_client_get_client_by_id(client, conn, client_id);
452     if (!client_entry)
453       goto out;
454     silc_free(client_id);
455
456     client_entry->valid = FALSE;
457
458     /* Get new Client ID */
459     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
460     if (!tmp)
461       goto out;
462
463     client_id = silc_id_payload_parse_id(tmp, tmp_len);
464     if (!client_id)
465       goto out;
466
467     /* Find Client entry and if not found resolve it */
468     client_entry2 = silc_client_get_client_by_id(client, conn, client_id);
469     if (!client_entry2) {
470       silc_client_notify_by_server_resolve(client, conn, packet, client_id);
471       goto out;
472     } else {
473       if (client_entry2 != conn->local_entry)
474         silc_client_nickname_format(client, conn, client_entry2);
475     }
476
477     /* Remove the old from cache */
478     silc_idcache_del_by_context(conn->client_cache, client_entry);
479
480     /* Replace old ID entry with new one on all channels. */
481     silc_client_replace_from_channels(client, conn, client_entry,
482                                       client_entry2);
483
484     /* Notify application */
485     client->ops->notify(client, conn, type, client_entry, client_entry2);
486
487     /* Free data */
488     silc_client_del_client_entry(client, conn, client_entry);
489     break;
490
491   case SILC_NOTIFY_TYPE_CMODE_CHANGE:
492     /*
493      * Someone changed a channel mode
494      */
495
496     SILC_LOG_DEBUG(("Notify: CMODE_CHANGE"));
497
498     /* Get Client ID */
499     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
500     if (!tmp)
501       goto out;
502
503     idp = silc_id_payload_parse(tmp, tmp_len);
504     if (!idp)
505       goto out;
506
507     /* Find Client entry */
508     if (silc_id_payload_get_type(idp) == SILC_ID_CLIENT) {
509       client_id = silc_id_payload_parse_id(tmp, tmp_len);
510       if (!client_id) {
511         silc_id_payload_free(idp);
512         goto out;
513       }
514
515       client_entry = silc_client_get_client_by_id(client, conn, client_id);
516       if (!client_entry) {
517         silc_id_payload_free(idp);
518         goto out;
519       }
520     } else {
521       server_id = silc_id_payload_parse_id(tmp, tmp_len);
522       if (!server_id) {
523         silc_id_payload_free(idp);
524         goto out;
525       }
526       
527       server = silc_client_get_server_by_id(client, conn, server_id);
528       if (!server) {
529         silc_id_payload_free(idp);
530         silc_free(server_id);
531         goto out;
532       }
533       
534       /* Save the pointer to the client_entry pointer */
535       client_entry = (SilcClientEntry)server;
536       silc_free(server_id);
537     }
538
539     /* Get the mode */
540     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
541     if (!tmp) {
542       silc_id_payload_free(idp);
543       goto out;
544     }
545
546     SILC_GET32_MSB(mode, tmp);
547
548     /* Get channel entry */
549     channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
550                                 SILC_ID_CHANNEL);
551     if (!channel_id) {
552       silc_id_payload_free(idp);
553       goto out;
554     }
555     if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
556                                      &id_cache)) {
557       silc_id_payload_free(idp);
558       goto out;
559     }
560
561     channel = (SilcChannelEntry)id_cache->context;
562
563     /* Save the new mode */
564     channel->mode = mode;
565
566     /* Get the hmac */
567     tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
568     if (tmp) {
569       unsigned char hash[32];
570
571       if (channel->hmac)
572         silc_hmac_free(channel->hmac);
573       if (!silc_hmac_alloc(tmp, NULL, &channel->hmac))
574         goto out;
575
576       silc_hash_make(silc_hmac_get_hash(channel->hmac), 
577                      channel->key, channel->key_len / 8,
578                      hash);
579       silc_hmac_set_key(channel->hmac, hash, 
580                         silc_hash_len(silc_hmac_get_hash(channel->hmac)));
581       memset(hash, 0, sizeof(hash));
582     }
583
584     /* Notify application. The channel entry is sent last as this notify
585        is for channel but application don't know it from the arguments
586        sent by server. */
587     client->ops->notify(client, conn, type, silc_id_payload_get_type(idp), 
588                         client_entry, mode, NULL, tmp, channel);
589
590     silc_id_payload_free(idp);
591     break;
592
593   case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
594     /*
595      * Someone changed user's mode on a channel
596      */
597
598     SILC_LOG_DEBUG(("Notify: CUMODE_CHANGE"));
599
600     /* Get Client ID */
601     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
602     if (!tmp)
603       goto out;
604
605     client_id = silc_id_payload_parse_id(tmp, tmp_len);
606     if (!client_id)
607       goto out;
608
609     /* Find Client entry */
610     client_entry = silc_client_get_client_by_id(client, conn, client_id);
611     if (!client_entry) {
612       silc_client_notify_by_server_resolve(client, conn, packet, client_id);
613       goto out;
614     }
615
616     /* Get the mode */
617     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
618     if (!tmp)
619       goto out;
620
621     SILC_GET32_MSB(mode, tmp);
622
623     /* Get target Client ID */
624     tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
625     if (!tmp)
626       goto out;
627
628     silc_free(client_id);
629     client_id = silc_id_payload_parse_id(tmp, tmp_len);
630     if (!client_id)
631       goto out;
632
633     /* Find target Client entry */
634     client_entry2 = 
635       silc_client_get_client_by_id(client, conn, client_id);
636     if (!client_entry2)
637       goto out;
638
639     /* Get channel entry */
640     channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
641                                 SILC_ID_CHANNEL);
642     if (!channel_id)
643       goto out;
644     if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
645                                  &id_cache))
646       break;
647
648     channel = (SilcChannelEntry)id_cache->context;
649
650     /* Save the mode */
651     silc_list_start(channel->clients);
652     while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
653       if (chu->client == client_entry) {
654         chu->mode = mode;
655         break;
656       }
657     }
658
659     /* Notify application. The channel entry is sent last as this notify
660        is for channel but application don't know it from the arguments
661        sent by server. */
662     client->ops->notify(client, conn, type, client_entry, mode, 
663                         client_entry2, channel);
664     break;
665
666   case SILC_NOTIFY_TYPE_MOTD:
667     /*
668      * Received Message of the day
669      */
670
671     SILC_LOG_DEBUG(("Notify: MOTD"));
672
673     /* Get motd */
674     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
675     if (!tmp)
676       goto out;
677     
678     /* Notify application */
679     client->ops->notify(client, conn, type, tmp);
680     break;
681
682   case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
683     /*
684      * Router has enforced a new ID to a channel. Let's change the old
685      * ID to the one provided here.
686      */
687
688     SILC_LOG_DEBUG(("Notify: CHANNEL_CHANGE"));
689
690     /* Get the old ID */
691     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
692     if (!tmp)
693       goto out;
694     channel_id = silc_id_payload_parse_id(tmp, tmp_len);
695     if (!channel_id)
696       goto out;
697     
698     /* Get the channel entry */
699     if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
700                                      &id_cache))
701       break;
702
703     channel = (SilcChannelEntry)id_cache->context;
704
705     SILC_LOG_DEBUG(("Old Channel ID id(%s)", 
706                     silc_id_render(channel->id, SILC_ID_CHANNEL)));
707
708     /* Remove the old channel entry */
709     silc_idcache_del_by_context(conn->channel_cache, channel);
710
711     /* Free the old ID */
712     silc_free(channel->id);
713
714     /* Get the new ID */
715     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
716     if (!tmp)
717       goto out;
718     channel->id = silc_id_payload_parse_id(tmp, tmp_len);
719     if (!channel->id)
720       goto out;
721
722     SILC_LOG_DEBUG(("New Channel ID id(%s)", 
723                     silc_id_render(channel->id, SILC_ID_CHANNEL)));
724
725     /* Add the channel entry again to ID cache */
726     silc_idcache_add(conn->channel_cache, channel->channel_name, 
727                      channel->id, channel, 0, NULL);
728
729     /* Notify application */
730     client->ops->notify(client, conn, type, channel, channel);
731     break;
732
733   case SILC_NOTIFY_TYPE_KICKED:
734     /*
735      * A client (maybe me) was kicked from a channel
736      */
737
738     SILC_LOG_DEBUG(("Notify: KICKED"));
739
740     /* Get Client ID */
741     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
742     if (!tmp)
743       goto out;
744
745     client_id = silc_id_payload_parse_id(tmp, tmp_len);
746     if (!client_id)
747       goto out;
748
749     /* Find Client entry */
750     client_entry = silc_client_get_client_by_id(client, conn, client_id);
751     if (!client_entry)
752       goto out;
753
754     /* Get channel entry */
755     channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
756                                 SILC_ID_CHANNEL);
757     if (!channel_id)
758       goto out;
759     if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
760                                      &id_cache))
761       break;
762
763     channel = (SilcChannelEntry)id_cache->context;
764
765     /* Get the kicker */
766     tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
767     if (!tmp)
768       goto out;
769
770     client_id = silc_id_payload_parse_id(tmp, tmp_len);
771     if (!client_id)
772       goto out;
773
774     /* Find kicker's client entry and if not found resolve it */
775     client_entry2 = silc_client_get_client_by_id(client, conn, client_id);
776     if (!client_entry2) {
777       silc_client_notify_by_server_resolve(client, conn, packet, client_id);
778       goto out;
779     } else {
780       if (client_entry2 != conn->local_entry)
781         silc_client_nickname_format(client, conn, client_entry2);
782     }
783
784     /* Get comment */
785     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
786
787     /* Notify application. The channel entry is sent last as this notify
788        is for channel but application don't know it from the arguments
789        sent by server. */
790     client->ops->notify(client, conn, type, client_entry, tmp, 
791                         client_entry2, channel);
792
793     /* If I was kicked from channel, remove the channel */
794     if (client_entry == conn->local_entry) {
795       if (conn->current_channel == channel)
796         conn->current_channel = NULL;
797       silc_idcache_del_by_id(conn->channel_cache, channel->id);
798       silc_free(channel->channel_name);
799       silc_free(channel->id);
800       silc_free(channel->key);
801       silc_cipher_free(channel->channel_key);
802       silc_free(channel);
803     }
804     break;
805
806   case SILC_NOTIFY_TYPE_KILLED:
807     /*
808      * A client (maybe me) was killed from the network.
809      */
810
811     SILC_LOG_DEBUG(("Notify: KILLED"));
812
813     /* Get Client ID */
814     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
815     if (!tmp)
816       goto out;
817
818     client_id = silc_id_payload_parse_id(tmp, tmp_len);
819     if (!client_id)
820       goto out;
821
822     /* Find Client entry */
823     client_entry = silc_client_get_client_by_id(client, conn, client_id);
824     if (!client_entry)
825       goto out;
826
827     /* Get comment */
828     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
829
830     /* Notify application. */
831     client->ops->notify(client, conn, type, client_entry, tmp);
832
833     if (client_entry != conn->local_entry) {
834       /* Remove client from all channels */
835       silc_client_remove_from_channels(client, conn, client_entry);
836       silc_client_del_client(client, conn, client_entry);
837     }
838
839     break;
840     
841   case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
842     {
843       /*
844        * A server quit the SILC network and some clients must be removed
845        * from channels as they quit as well.
846        */
847       SilcClientEntry *clients = NULL;
848       uint32 clients_count = 0;
849       int i;
850
851       SILC_LOG_DEBUG(("Notify: SIGNOFF"));
852
853       for (i = 1; i < silc_argument_get_arg_num(args); i++) {
854         /* Get Client ID */
855         tmp = silc_argument_get_arg_type(args, i + 1, &tmp_len);
856         if (tmp) {
857           client_id = silc_id_payload_parse_id(tmp, tmp_len);
858           if (!client_id)
859             goto out;
860           
861           /* Get the client entry */
862           client_entry = silc_client_get_client_by_id(client, conn, client_id);
863           if (client_entry) {
864             clients = silc_realloc(clients, sizeof(*clients) * 
865                                    (clients_count + 1));
866             clients[clients_count] = client_entry;
867             clients_count++;
868           }
869           silc_free(client_id);
870         }
871       }
872       client_id = NULL;
873
874       /* Notify application. We don't keep server entries so the server
875          entry is returned as NULL. The client's are returned as array
876          of SilcClientEntry pointers. */
877       client->ops->notify(client, conn, type, NULL, clients, clients_count);
878
879       for (i = 0; i < clients_count; i++) {
880         /* Remove client from all channels */
881         client_entry = clients[i];
882         if (client_entry == conn->local_entry)
883           continue;
884
885         silc_client_remove_from_channels(client, conn, client_entry);
886         silc_client_del_client(client, conn, client_entry);
887       }
888       silc_free(clients);
889
890     }
891     break;
892
893   default:
894     break;
895   }
896
897  out:
898   silc_notify_payload_free(payload);
899   silc_free(client_id);
900   silc_free(channel_id);
901 }