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 (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
486       /* Create new client entry, and save all old information with the
487          new nickname and client ID */
488       client_entry2 = silc_client_add_client(client, conn, NULL, NULL, 
489                                              client_entry->realname,
490                                              silc_id_dup(client_id, 
491                                                          SILC_ID_CLIENT), 0);
492       if (!client_entry2)
493         goto out;
494
495       if (client_entry->server)
496         client_entry2->server = strdup(client_entry->server);
497       if (client_entry->username)
498         client_entry2->username = strdup(client_entry->username);
499       if (client_entry->hostname)
500         client_entry2->hostname = strdup(client_entry->hostname);
501       silc_client_update_client(client, conn, client_entry2, tmp, NULL, NULL,
502                                 client_entry->mode);
503     } else {
504       /* Protocol version 1.0 */
505
506       /* Find client entry and if not found resolve it */
507       client_entry2 = silc_client_get_client_by_id(client, conn, client_id);
508       if (!client_entry2) {
509         /* Resolve the entry information */
510         silc_client_notify_by_server_resolve(client, conn, packet, 
511                                              SILC_ID_CLIENT, client_id);
512
513         /* Add the new entry even though we resolved it. This is because we
514            want to replace the old entry with the new entry here right now. */
515         client_entry2 = 
516           silc_client_add_client(client, conn, NULL, NULL, NULL, 
517                                  silc_id_dup(client_id, SILC_ID_CLIENT), 
518                                  client_entry->mode);
519
520         /* Replace old ID entry with new one on all channels. */
521         silc_client_replace_from_channels(client, conn, client_entry,
522                                           client_entry2);
523         break;
524       }
525
526       if (client_entry2 != conn->local_entry)
527         silc_client_nickname_format(client, conn, client_entry2);
528     }
529
530     /* Remove the old from cache */
531     silc_idcache_del_by_context(conn->client_cache, client_entry);
532     
533     /* Replace old ID entry with new one on all channels. */
534     silc_client_replace_from_channels(client, conn, client_entry,
535                                       client_entry2);
536
537     /* Notify application */
538     client->internal->ops->notify(client, conn, type, 
539                                   client_entry, client_entry2);
540     
541     /* Free old client entry */
542     silc_client_del_client_entry(client, conn, client_entry);
543
544     break;
545
546   case SILC_NOTIFY_TYPE_CMODE_CHANGE:
547     /*
548      * Someone changed a channel mode
549      */
550
551     SILC_LOG_DEBUG(("Notify: CMODE_CHANGE"));
552
553     /* Get ID */
554     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
555     if (!tmp)
556       goto out;
557     id = silc_id_payload_parse_id(tmp, tmp_len, &id_type);
558     if (!id)
559       goto out;
560
561     /* Find Client entry */
562     if (id_type == SILC_ID_CLIENT) {
563       /* Find Client entry */
564       client_id = id;
565       client_entry = silc_client_get_client_by_id(client, conn, client_id);
566       if (!client_entry) {
567         silc_client_notify_by_server_resolve(client, conn, packet, 
568                                              SILC_ID_CLIENT, client_id);
569         goto out;
570       }
571     } else if (id_type == SILC_ID_SERVER) {
572       /* Find Server entry */
573       server_id = id;
574       server = silc_client_get_server_by_id(client, conn, server_id);
575       if (!server) {
576         silc_client_notify_by_server_resolve(client, conn, packet, 
577                                              SILC_ID_SERVER, server_id);
578         goto out;
579       }
580
581       /* Save the pointer to the client_entry pointer */
582       client_entry = (SilcClientEntry)server;
583     } else {
584       /* Find Channel entry */
585       channel_id = id;
586       channel = silc_client_get_channel_by_id(client, conn, channel_id);
587       if (!channel) {
588         silc_client_notify_by_server_resolve(client, conn, packet, 
589                                              SILC_ID_CHANNEL, channel_id);
590         goto out;
591       }
592       
593       /* Save the pointer to the client_entry pointer */
594       client_entry = (SilcClientEntry)channel;
595       silc_free(channel_id);
596       channel_id = NULL;
597     }
598
599     /* Get the mode */
600     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
601     if (!tmp)
602       goto out;
603
604     SILC_GET32_MSB(mode, tmp);
605
606     /* Get channel entry */
607     channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
608                                 SILC_ID_CHANNEL);
609     if (!channel_id)
610       goto out;
611     channel = silc_client_get_channel_by_id(client, conn, channel_id);
612     if (!channel)
613       goto out;
614
615     /* Save the new mode */
616     channel->mode = mode;
617
618     /* Get the hmac */
619     tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
620     if (tmp) {
621       unsigned char hash[32];
622
623       if (channel->hmac)
624         silc_hmac_free(channel->hmac);
625       if (!silc_hmac_alloc(tmp, NULL, &channel->hmac))
626         goto out;
627
628       silc_hash_make(silc_hmac_get_hash(channel->hmac), 
629                      channel->key, channel->key_len / 8,
630                      hash);
631       silc_hmac_set_key(channel->hmac, hash, 
632                         silc_hash_len(silc_hmac_get_hash(channel->hmac)));
633       memset(hash, 0, sizeof(hash));
634     }
635
636     /* Notify application. The channel entry is sent last as this notify
637        is for channel but application don't know it from the arguments
638        sent by server. */
639     client->internal->ops->notify(client, conn, type, id_type,
640                                   client_entry, mode, NULL, tmp, channel);
641     break;
642
643   case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
644     /*
645      * Someone changed user's mode on a channel
646      */
647
648     SILC_LOG_DEBUG(("Notify: CUMODE_CHANGE"));
649
650     /* Get ID */
651     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
652     if (!tmp)
653       goto out;
654     id = silc_id_payload_parse_id(tmp, tmp_len, &id_type);
655     if (!id)
656       goto out;
657
658     /* Find Client entry */
659     if (id_type == SILC_ID_CLIENT) {
660       /* Find Client entry */
661       client_id = id;
662       client_entry = silc_client_get_client_by_id(client, conn, client_id);
663       if (!client_entry) {
664         silc_client_notify_by_server_resolve(client, conn, packet, 
665                                              SILC_ID_CLIENT, client_id);
666         goto out;
667       }
668     } else if (id_type == SILC_ID_SERVER) {
669       /* Find Server entry */
670       server_id = id;
671       server = silc_client_get_server_by_id(client, conn, server_id);
672       if (!server) {
673         silc_client_notify_by_server_resolve(client, conn, packet, 
674                                              SILC_ID_SERVER, server_id);
675         goto out;
676       }
677
678       /* Save the pointer to the client_entry pointer */
679       client_entry = (SilcClientEntry)server;
680     } else {
681       /* Find Channel entry */
682       channel_id = id;
683       channel = silc_client_get_channel_by_id(client, conn, channel_id);
684       if (!channel) {
685         silc_client_notify_by_server_resolve(client, conn, packet, 
686                                              SILC_ID_CHANNEL, channel_id);
687         goto out;
688       }
689       
690       /* Save the pointer to the client_entry pointer */
691       client_entry = (SilcClientEntry)channel;
692       silc_free(channel_id);
693       channel_id = NULL;
694     }
695
696     /* Get the mode */
697     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
698     if (!tmp)
699       goto out;
700
701     SILC_GET32_MSB(mode, tmp);
702
703     /* Get target Client ID */
704     tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
705     if (!tmp)
706       goto out;
707
708     silc_free(client_id);
709     client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
710     if (!client_id)
711       goto out;
712
713     /* Find target Client entry */
714     client_entry2 = 
715       silc_client_get_client_by_id(client, conn, client_id);
716     if (!client_entry2)
717       goto out;
718
719     /* Get channel entry */
720     channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
721                                 SILC_ID_CHANNEL);
722     if (!channel_id)
723       goto out;
724     channel = silc_client_get_channel_by_id(client, conn, channel_id);
725     if (!channel)
726       break;
727
728     /* Save the mode */
729     chu = silc_client_on_channel(channel, client_entry2);
730     if (chu)
731       chu->mode = mode;
732
733     /* Notify application. The channel entry is sent last as this notify
734        is for channel but application don't know it from the arguments
735        sent by server. */
736     client->internal->ops->notify(client, conn, type,
737                                   id_type, client_entry, mode, 
738                                   client_entry2, channel);
739     break;
740
741   case SILC_NOTIFY_TYPE_MOTD:
742     /*
743      * Received Message of the day
744      */
745
746     SILC_LOG_DEBUG(("Notify: MOTD"));
747
748     /* Get motd */
749     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
750     if (!tmp)
751       goto out;
752     
753     /* Notify application */
754     client->internal->ops->notify(client, conn, type, tmp);
755     break;
756
757   case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
758     /*
759      * Router has enforced a new ID to a channel. Let's change the old
760      * ID to the one provided here.
761      */
762
763     SILC_LOG_DEBUG(("Notify: CHANNEL_CHANGE"));
764
765     /* Get the old ID */
766     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
767     if (!tmp)
768       goto out;
769     channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
770     if (!channel_id)
771       goto out;
772
773     /* Get the channel entry */
774     channel = silc_client_get_channel_by_id(client, conn, channel_id);
775     if (!channel)
776       goto out;
777
778     silc_free(channel_id);
779
780     /* Get the new ID */
781     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
782     if (!tmp)
783       goto out;
784     channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
785     if (!channel_id)
786       goto out;
787
788     /* Replace the Channel ID */
789     silc_client_replace_channel_id(client, conn, channel, channel_id);
790
791     /* Notify application */
792     client->internal->ops->notify(client, conn, type, channel, channel);
793     break;
794
795   case SILC_NOTIFY_TYPE_KICKED:
796     /*
797      * A client (maybe me) was kicked from a channel
798      */
799
800     SILC_LOG_DEBUG(("Notify: KICKED"));
801
802     /* Get Client ID */
803     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
804     if (!tmp)
805       goto out;
806
807     client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
808     if (!client_id)
809       goto out;
810
811     /* Find Client entry */
812     client_entry = silc_client_get_client_by_id(client, conn, client_id);
813     if (!client_entry)
814       goto out;
815
816     /* Get channel entry */
817     channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
818                                 SILC_ID_CHANNEL);
819     if (!channel_id)
820       goto out;
821     channel = silc_client_get_channel_by_id(client, conn, channel_id);
822     if (!channel)
823       break;
824
825     /* From protocol version 1.1 we get the kicker's client ID as well */
826     tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
827     if (tmp) {
828       silc_free(client_id);
829       client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
830       if (!client_id)
831         goto out;
832
833       /* Find kicker's client entry and if not found resolve it */
834       client_entry2 = silc_client_get_client_by_id(client, conn, client_id);
835       if (!client_entry2) {
836         silc_client_notify_by_server_resolve(client, conn, packet, 
837                                              SILC_ID_CLIENT, client_id);
838         goto out;
839       } else {
840         if (client_entry2 != conn->local_entry)
841           silc_client_nickname_format(client, conn, client_entry2);
842       }
843     }
844
845     /* Get comment */
846     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
847
848     /* Notify application. The channel entry is sent last as this notify
849        is for channel but application don't know it from the arguments
850        sent by server. */
851     client->internal->ops->notify(client, conn, type, client_entry, tmp, 
852                                   client_entry2, channel);
853
854     /* Remove kicked client from channel */
855     if (client_entry == conn->local_entry) {
856       /* If I was kicked from channel, remove the channel */
857       if (conn->current_channel == channel)
858         conn->current_channel = NULL;
859       silc_client_del_channel(client, conn, channel);
860     } else {
861       chu = silc_client_on_channel(channel, client_entry);
862       if (chu) {
863         silc_hash_table_del(client_entry->channels, channel);
864         silc_hash_table_del(channel->user_list, client_entry);
865         silc_free(chu);
866       }
867     }
868     break;
869
870   case SILC_NOTIFY_TYPE_KILLED:
871     {
872       /*
873        * A client (maybe me) was killed from the network.
874        */
875       char *comment;
876       SilcUInt32 comment_len;
877
878       SILC_LOG_DEBUG(("Notify: KILLED"));
879
880       /* Get Client ID */
881       tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
882       if (!tmp)
883         goto out;
884
885       client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
886       if (!client_id)
887         goto out;
888
889       /* Find Client entry */
890       client_entry = silc_client_get_client_by_id(client, conn, client_id);
891       if (!client_entry)
892         goto out;
893
894       /* Get comment */
895       comment = silc_argument_get_arg_type(args, 2, &comment_len);
896
897       /* From protocol version 1.1 we get killer's client ID as well */
898       tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
899       if (tmp) {
900         silc_free(client_id);
901         id = silc_id_payload_parse_id(tmp, tmp_len, &id_type);
902         if (!id)
903           goto out;
904
905         /* Find Client entry */
906         if (id_type == SILC_ID_CLIENT) {
907           /* Find Client entry */
908           client_id = id;
909           client_entry2 = silc_client_get_client_by_id(client, conn, 
910                                                        client_id);
911           if (!client_entry) {
912             silc_client_notify_by_server_resolve(client, conn, packet, 
913                                                  SILC_ID_CLIENT, client_id);
914             goto out;
915           }
916         } else if (id_type == SILC_ID_SERVER) {
917           /* Find Server entry */
918           server_id = id;
919           server = silc_client_get_server_by_id(client, conn, server_id);
920           if (!server) {
921             silc_client_notify_by_server_resolve(client, conn, packet, 
922                                                  SILC_ID_SERVER, server_id);
923             goto out;
924           }
925       
926           /* Save the pointer to the client_entry pointer */
927           client_entry2 = (SilcClientEntry)server;
928         } else {
929           /* Find Channel entry */
930           channel_id = id;
931           channel = silc_client_get_channel_by_id(client, conn, channel_id);
932           if (!channel) {
933             silc_client_notify_by_server_resolve(client, conn, packet, 
934                                                  SILC_ID_CHANNEL, channel_id);
935             goto out;
936           }
937           
938           /* Save the pointer to the client_entry pointer */
939           client_entry2 = (SilcClientEntry)channel;
940           silc_free(channel_id);
941           channel_id = NULL;
942         }
943       }
944
945       /* Notify application. */
946       client->internal->ops->notify(client, conn, type, client_entry, 
947                                     comment, id_type, client_entry2);
948
949       if (client_entry != conn->local_entry)
950         /* Remove the client from all channels and free it */
951         silc_client_del_client(client, conn, client_entry);
952     }
953     break;
954     
955   case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
956     {
957       /*
958        * A server quit the SILC network and some clients must be removed
959        * from channels as they quit as well.
960        */
961       SilcClientEntry *clients = NULL;
962       SilcUInt32 clients_count = 0;
963       int i;
964
965       SILC_LOG_DEBUG(("Notify: SIGNOFF"));
966
967       for (i = 1; i < silc_argument_get_arg_num(args); i++) {
968         /* Get Client ID */
969         tmp = silc_argument_get_arg_type(args, i + 1, &tmp_len);
970         if (tmp) {
971           client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
972           if (!client_id)
973             goto out;
974           
975           /* Get the client entry */
976           client_entry = silc_client_get_client_by_id(client, conn, client_id);
977           if (client_entry) {
978             clients = silc_realloc(clients, sizeof(*clients) * 
979                                    (clients_count + 1));
980             clients[clients_count] = client_entry;
981             clients_count++;
982           }
983           silc_free(client_id);
984         }
985       }
986       client_id = NULL;
987
988       /* Notify application. We don't keep server entries so the server
989          entry is returned as NULL. The client's are returned as array
990          of SilcClientEntry pointers. */
991       client->internal->ops->notify(client, conn, type, NULL, 
992                                     clients, clients_count);
993
994       for (i = 0; i < clients_count; i++) {
995         /* Remove client from all channels */
996         client_entry = clients[i];
997         if (client_entry == conn->local_entry)
998           continue;
999
1000         /* Remove the client from all channels and free it */
1001         silc_client_del_client(client, conn, client_entry);
1002       }
1003       silc_free(clients);
1004
1005     }
1006     break;
1007
1008   default:
1009     break;
1010   }
1011
1012  out:
1013   silc_notify_payload_free(payload);
1014   silc_free(client_id);
1015   silc_free(channel_id);
1016   silc_free(server_id);
1017 }