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