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