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   SilcClient client = res->context;
39   SilcClientConnection conn = res->sock->user_data;
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_socket_free(res->sock);
44   silc_free(res);
45 }
46
47 /* Called when notify is received and some async operation (such as command)
48    is required before processing the notify message. This calls again the
49    silc_client_notify_by_server and reprocesses the original notify packet. */
50
51 static void silc_client_notify_by_server_pending(void *context, void *context2)
52 {
53   SilcClientNotifyResolve res = (SilcClientNotifyResolve)context;
54   SilcClientCommandReplyContext reply = 
55     (SilcClientCommandReplyContext)context2;
56
57   SILC_LOG_DEBUG(("Start"));
58
59   if (reply && !silc_command_get_status(reply->payload, NULL, NULL))
60     goto out;
61
62   silc_client_notify_by_server(res->context, res->sock, res->packet);
63
64  out:
65   silc_socket_free(res->sock);
66   silc_packet_context_free(res->packet);
67   silc_free(res);
68 }
69
70 /* Resolve client, channel or server information. */
71
72 static void silc_client_notify_by_server_resolve(SilcClient client,
73                                                  SilcClientConnection conn,
74                                                  SilcPacketContext *packet,
75                                                  SilcIdType id_type,
76                                                  void *id)
77 {
78   SilcClientNotifyResolve res = silc_calloc(1, sizeof(*res));
79   SilcBuffer idp = silc_id_payload_encode(id, id_type);
80
81   res->packet = silc_packet_context_dup(packet);
82   res->context = client;
83   res->sock = silc_socket_dup(conn->sock);
84
85   /* For client resolving use WHOIS, and otherwise use IDENTIFY */
86   if (id_type == SILC_ID_CLIENT) {
87     silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
88                                  silc_client_command_reply_whois_i, 0,
89                                  ++conn->cmd_ident);
90     silc_client_command_send(client, conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
91                              1, 4, idp->data, idp->len);
92     silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
93                                 silc_client_notify_by_server_pending, res);
94   } else {
95     silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
96                                  silc_client_command_reply_identify_i, 0,
97                                  ++conn->cmd_ident);
98     silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY, 
99                              conn->cmd_ident, 1, 5, idp->data, idp->len);
100     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
101                                 silc_client_notify_by_server_pending, res);
102   }
103   silc_buffer_free(idp);
104 }
105
106 /* Received notify message from server */
107
108 void silc_client_notify_by_server(SilcClient client,
109                                   SilcSocketConnection sock,
110                                   SilcPacketContext *packet)
111 {
112   SilcBuffer buffer = packet->buffer;
113   SilcClientConnection conn = (SilcClientConnection)sock->user_data;
114   SilcNotifyPayload payload;
115   SilcNotifyType type;
116   SilcArgumentPayload args;
117
118   void *id;
119   SilcIdType id_type;
120   SilcClientID *client_id = NULL;
121   SilcChannelID *channel_id = NULL;
122   SilcServerID *server_id = NULL;
123   SilcClientEntry client_entry = NULL;
124   SilcClientEntry client_entry2 = NULL;
125   SilcChannelEntry channel;
126   SilcChannelUser chu;
127   SilcServerEntry server;
128   unsigned char *tmp;
129   SilcUInt32 tmp_len, mode;
130
131   SILC_LOG_DEBUG(("Start"));
132
133   payload = silc_notify_payload_parse(buffer->data, buffer->len);
134   if (!payload)
135     goto out;
136
137   type = silc_notify_get_type(payload);
138   args = silc_notify_get_args(payload);
139   if (!args)
140     goto out;
141
142   switch(type) {
143   case SILC_NOTIFY_TYPE_NONE:
144     /* Notify application */
145     client->internal->ops->notify(client, conn, type, 
146                                   silc_argument_get_arg_type(args, 1, NULL));
147     break;
148
149   case SILC_NOTIFY_TYPE_INVITE:
150     /* 
151      * Someone invited me to a channel. Find Client and Channel entries
152      * for the application.
153      */
154     
155     SILC_LOG_DEBUG(("Notify: INVITE"));
156
157     /* Get Channel ID */
158     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
159     if (!tmp)
160       goto out;
161
162     channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
163     if (!channel_id)
164       goto out;
165
166     /* Get the channel entry */
167     channel = silc_client_get_channel_by_id(client, conn, channel_id);
168
169     /* Get sender Client ID */
170     tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
171     if (!tmp)
172       goto out;
173
174     client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
175     if (!client_id)
176       goto out;
177
178     /* Find Client entry and if not found query it */
179     client_entry = silc_client_get_client_by_id(client, conn, client_id);
180     if (!client_entry) {
181       silc_client_notify_by_server_resolve(client, conn, packet, 
182                                            SILC_ID_CLIENT, client_id);
183       goto out;
184     }
185
186     /* Get the channel name */
187     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
188     if (!tmp)
189       goto out;
190
191     /* Notify application */
192     client->internal->ops->notify(client, conn, type, channel, tmp, 
193                                   client_entry);
194     break;
195
196   case SILC_NOTIFY_TYPE_JOIN:
197     /*
198      * Someone has joined to a channel. Get their ID and nickname and
199      * cache them for later use.
200      */
201
202     SILC_LOG_DEBUG(("Notify: JOIN"));
203
204     /* Get Client ID */
205     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
206     if (!tmp)
207       goto out;
208
209     client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
210     if (!client_id)
211       goto out;
212
213     /* Find Client entry and if not found query it */
214     client_entry = silc_client_get_client_by_id(client, conn, client_id);
215     if (!client_entry) {
216       silc_client_notify_by_server_resolve(client, conn, packet, 
217                                            SILC_ID_CLIENT, client_id);
218       goto out;
219     }
220
221     /* If nickname or username hasn't been resolved, do so */
222     if (!client_entry->nickname || !client_entry->username) {
223       if (client_entry->status & SILC_CLIENT_STATUS_RESOLVING) {
224         client_entry->status &= ~SILC_CLIENT_STATUS_RESOLVING;
225         goto out;
226       }
227       silc_client_notify_by_server_resolve(client, conn, packet, 
228                                            SILC_ID_CLIENT, client_id);
229       client_entry->status |= SILC_CLIENT_STATUS_RESOLVING;
230       client_entry->resolve_cmd_ident = conn->cmd_ident;
231       goto out;
232     } else {
233       if (client_entry != conn->local_entry)
234         silc_client_nickname_format(client, conn, client_entry);
235     }
236
237     /* Get Channel ID */
238     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
239     if (!tmp)
240       goto out;
241
242     channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
243     if (!channel_id)
244       goto out;
245
246     /* Get channel entry */
247     channel = silc_client_get_channel_by_id(client, conn, channel_id);
248     if (!channel)
249       break;
250
251     /* Join the client to channel */
252     if (!silc_client_on_channel(channel, client_entry)) {
253       chu = silc_calloc(1, sizeof(*chu));
254       chu->client = client_entry;
255       chu->channel = channel;
256       silc_hash_table_add(channel->user_list, client_entry, chu);
257       silc_hash_table_add(client_entry->channels, channel, chu);
258     }
259
260     /* Notify application. The channel entry is sent last as this notify
261        is for channel but application don't know it from the arguments
262        sent by server. */
263     client->internal->ops->notify(client, conn, type, client_entry, channel);
264     break;
265
266   case SILC_NOTIFY_TYPE_LEAVE:
267     /*
268      * Someone has left a channel. We will remove it from the channel but
269      * we'll keep it in the cache in case we'll need it later.
270      */
271     
272     SILC_LOG_DEBUG(("Notify: LEAVE"));
273
274     /* Get Client ID */
275     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
276     if (!tmp)
277       goto out;
278
279     client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
280     if (!client_id)
281       goto out;
282
283     /* Find Client entry */
284     client_entry = 
285       silc_client_get_client_by_id(client, conn, client_id);
286     if (!client_entry)
287       goto out;
288
289     /* Get channel entry */
290     channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
291                                 SILC_ID_CHANNEL);
292     if (!channel_id)
293       goto out;
294     channel = silc_client_get_channel_by_id(client, conn, channel_id);
295     if (!channel)
296       break;
297
298     /* Remove client from channel */
299     chu = silc_client_on_channel(channel, client_entry);
300     if (chu) {
301       silc_hash_table_del(client_entry->channels, channel);
302       silc_hash_table_del(channel->user_list, client_entry);
303       silc_free(chu);
304     }
305
306     /* Some client implementations actually quit network by first doing
307        LEAVE and then immediately SIGNOFF.  We'll check for this by doing 
308        check for the client after 5 - 34 seconds.  If it is not valid after
309        that we'll remove the client from cache. */
310     if (!silc_hash_table_count(client_entry->channels)) {
311       SilcClientNotifyResolve res = silc_calloc(1, sizeof(*res));
312       res->context = client;
313       res->sock = silc_socket_dup(conn->sock);
314       res->packet = silc_id_dup(client_id, SILC_ID_CLIENT);
315       silc_schedule_task_add(client->schedule, conn->sock->sock,
316                              silc_client_notify_check_client, res,
317                              (5 + (silc_rng_get_rn16(client->rng) % 29)),
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 (conn->local_id && 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       char *tmp_nick = NULL;
489
490       /* Check whether nickname changed at all.  It is possible that nick
491          change notify is received but nickname didn't changed, only the
492          ID changes. */
493       if (client->internal->params->nickname_parse)
494         client->internal->params->nickname_parse(client_entry->nickname,
495                                                  &tmp_nick);
496       else
497         tmp_nick = strdup(tmp);
498
499       if (tmp_nick && !strcmp(tmp, tmp_nick)) {
500         /* Nickname didn't change. Update only the ID */
501         silc_idcache_del_by_context(conn->client_cache, client_entry);
502         silc_free(client_entry->id);
503         client_entry->id = silc_id_dup(client_id, SILC_ID_CLIENT);
504         silc_idcache_add(conn->client_cache, strdup(tmp),
505                          client_entry->id, client_entry, 0, NULL);
506
507         /* Notify application */
508         client->internal->ops->notify(client, conn, type, 
509                                       client_entry, client_entry);
510         break;
511       }
512       silc_free(tmp_nick);
513
514       /* Create new client entry, and save all old information with the
515          new nickname and client ID */
516       client_entry2 = silc_client_add_client(client, conn, NULL, NULL, 
517                                              client_entry->realname,
518                                              silc_id_dup(client_id, 
519                                                          SILC_ID_CLIENT), 0);
520       if (!client_entry2)
521         goto out;
522
523       if (client_entry->server)
524         client_entry2->server = strdup(client_entry->server);
525       if (client_entry->username)
526         client_entry2->username = strdup(client_entry->username);
527       if (client_entry->hostname)
528         client_entry2->hostname = strdup(client_entry->hostname);
529       silc_client_update_client(client, conn, client_entry2, tmp, NULL, NULL,
530                                 client_entry->mode);
531     } else {
532       /* Protocol version 1.0 */
533
534       /* Find client entry and if not found resolve it */
535       client_entry2 = silc_client_get_client_by_id(client, conn, client_id);
536       if (!client_entry2) {
537         /* Resolve the entry information */
538         silc_client_notify_by_server_resolve(client, conn, packet, 
539                                              SILC_ID_CLIENT, client_id);
540
541         /* Add the new entry even though we resolved it. This is because we
542            want to replace the old entry with the new entry here right now. */
543         client_entry2 = 
544           silc_client_add_client(client, conn, NULL, NULL, NULL, 
545                                  silc_id_dup(client_id, SILC_ID_CLIENT), 
546                                  client_entry->mode);
547
548         /* Replace old ID entry with new one on all channels. */
549         silc_client_replace_from_channels(client, conn, client_entry,
550                                           client_entry2);
551         break;
552       }
553
554       if (client_entry2 != conn->local_entry)
555         silc_client_nickname_format(client, conn, client_entry2);
556     }
557
558     /* Remove the old from cache */
559     silc_idcache_del_by_context(conn->client_cache, client_entry);
560     
561     /* Replace old ID entry with new one on all channels. */
562     silc_client_replace_from_channels(client, conn, client_entry,
563                                       client_entry2);
564
565     /* Notify application */
566     client->internal->ops->notify(client, conn, type, 
567                                   client_entry, client_entry2);
568     
569     /* Free old client entry */
570     silc_client_del_client_entry(client, conn, client_entry);
571
572     break;
573
574   case SILC_NOTIFY_TYPE_CMODE_CHANGE:
575     /*
576      * Someone changed a channel mode
577      */
578
579     SILC_LOG_DEBUG(("Notify: CMODE_CHANGE"));
580
581     /* Get ID */
582     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
583     if (!tmp)
584       goto out;
585     id = silc_id_payload_parse_id(tmp, tmp_len, &id_type);
586     if (!id)
587       goto out;
588
589     /* Find Client entry */
590     if (id_type == SILC_ID_CLIENT) {
591       /* Find Client entry */
592       client_id = id;
593       client_entry = silc_client_get_client_by_id(client, conn, client_id);
594       if (!client_entry) {
595         silc_client_notify_by_server_resolve(client, conn, packet, 
596                                              SILC_ID_CLIENT, client_id);
597         goto out;
598       }
599     } else if (id_type == SILC_ID_SERVER) {
600       /* Find Server entry */
601       server_id = id;
602       server = silc_client_get_server_by_id(client, conn, server_id);
603       if (!server) {
604         silc_client_notify_by_server_resolve(client, conn, packet, 
605                                              SILC_ID_SERVER, server_id);
606         goto out;
607       }
608
609       /* Save the pointer to the client_entry pointer */
610       client_entry = (SilcClientEntry)server;
611     } else {
612       /* Find Channel entry */
613       channel_id = id;
614       channel = silc_client_get_channel_by_id(client, conn, channel_id);
615       if (!channel) {
616         silc_client_notify_by_server_resolve(client, conn, packet, 
617                                              SILC_ID_CHANNEL, channel_id);
618         goto out;
619       }
620       
621       /* Save the pointer to the client_entry pointer */
622       client_entry = (SilcClientEntry)channel;
623       silc_free(channel_id);
624       channel_id = NULL;
625     }
626
627     /* Get the mode */
628     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
629     if (!tmp)
630       goto out;
631
632     SILC_GET32_MSB(mode, tmp);
633
634     /* Get channel entry */
635     channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
636                                 SILC_ID_CHANNEL);
637     if (!channel_id)
638       goto out;
639     channel = silc_client_get_channel_by_id(client, conn, channel_id);
640     if (!channel)
641       goto out;
642
643     /* Save the new mode */
644     channel->mode = mode;
645
646     /* Get the hmac */
647     tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
648     if (tmp) {
649       unsigned char hash[32];
650
651       if (channel->hmac)
652         silc_hmac_free(channel->hmac);
653       if (!silc_hmac_alloc(tmp, NULL, &channel->hmac))
654         goto out;
655
656       silc_hash_make(silc_hmac_get_hash(channel->hmac), 
657                      channel->key, channel->key_len / 8,
658                      hash);
659       silc_hmac_set_key(channel->hmac, hash, 
660                         silc_hash_len(silc_hmac_get_hash(channel->hmac)));
661       memset(hash, 0, sizeof(hash));
662     }
663
664     /* Notify application. The channel entry is sent last as this notify
665        is for channel but application don't know it from the arguments
666        sent by server. */
667     client->internal->ops->notify(client, conn, type, id_type,
668                                   client_entry, mode, NULL, tmp, channel);
669     break;
670
671   case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
672     /*
673      * Someone changed user's mode on a channel
674      */
675
676     SILC_LOG_DEBUG(("Notify: CUMODE_CHANGE"));
677
678     /* Get ID */
679     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
680     if (!tmp)
681       goto out;
682     id = silc_id_payload_parse_id(tmp, tmp_len, &id_type);
683     if (!id)
684       goto out;
685
686     /* Find Client entry */
687     if (id_type == SILC_ID_CLIENT) {
688       /* Find Client entry */
689       client_id = id;
690       client_entry = silc_client_get_client_by_id(client, conn, client_id);
691       if (!client_entry) {
692         silc_client_notify_by_server_resolve(client, conn, packet, 
693                                              SILC_ID_CLIENT, client_id);
694         goto out;
695       }
696     } else if (id_type == SILC_ID_SERVER) {
697       /* Find Server entry */
698       server_id = id;
699       server = silc_client_get_server_by_id(client, conn, server_id);
700       if (!server) {
701         silc_client_notify_by_server_resolve(client, conn, packet, 
702                                              SILC_ID_SERVER, server_id);
703         goto out;
704       }
705
706       /* Save the pointer to the client_entry pointer */
707       client_entry = (SilcClientEntry)server;
708     } else {
709       /* Find Channel entry */
710       channel_id = id;
711       channel = silc_client_get_channel_by_id(client, conn, channel_id);
712       if (!channel) {
713         silc_client_notify_by_server_resolve(client, conn, packet, 
714                                              SILC_ID_CHANNEL, channel_id);
715         goto out;
716       }
717       
718       /* Save the pointer to the client_entry pointer */
719       client_entry = (SilcClientEntry)channel;
720       silc_free(channel_id);
721       channel_id = NULL;
722     }
723
724     /* Get the mode */
725     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
726     if (!tmp)
727       goto out;
728
729     SILC_GET32_MSB(mode, tmp);
730
731     /* Get target Client ID */
732     tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
733     if (!tmp)
734       goto out;
735
736     silc_free(client_id);
737     client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
738     if (!client_id)
739       goto out;
740
741     /* Find target Client entry */
742     client_entry2 = 
743       silc_client_get_client_by_id(client, conn, client_id);
744     if (!client_entry2)
745       goto out;
746
747     /* Get channel entry */
748     channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
749                                 SILC_ID_CHANNEL);
750     if (!channel_id)
751       goto out;
752     channel = silc_client_get_channel_by_id(client, conn, channel_id);
753     if (!channel)
754       break;
755
756     /* Save the mode */
757     chu = silc_client_on_channel(channel, client_entry2);
758     if (chu)
759       chu->mode = mode;
760
761     /* Notify application. The channel entry is sent last as this notify
762        is for channel but application don't know it from the arguments
763        sent by server. */
764     client->internal->ops->notify(client, conn, type,
765                                   id_type, client_entry, mode, 
766                                   client_entry2, channel);
767     break;
768
769   case SILC_NOTIFY_TYPE_MOTD:
770     /*
771      * Received Message of the day
772      */
773
774     SILC_LOG_DEBUG(("Notify: MOTD"));
775
776     /* Get motd */
777     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
778     if (!tmp)
779       goto out;
780     
781     /* Notify application */
782     client->internal->ops->notify(client, conn, type, tmp);
783     break;
784
785   case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
786     /*
787      * Router has enforced a new ID to a channel. Let's change the old
788      * ID to the one provided here.
789      */
790
791     SILC_LOG_DEBUG(("Notify: CHANNEL_CHANGE"));
792
793     /* Get the old ID */
794     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
795     if (!tmp)
796       goto out;
797     channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
798     if (!channel_id)
799       goto out;
800
801     /* Get the channel entry */
802     channel = silc_client_get_channel_by_id(client, conn, channel_id);
803     if (!channel)
804       goto out;
805
806     silc_free(channel_id);
807
808     /* Get the new ID */
809     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
810     if (!tmp)
811       goto out;
812     channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
813     if (!channel_id)
814       goto out;
815
816     /* Replace the Channel ID */
817     silc_client_replace_channel_id(client, conn, channel, channel_id);
818
819     /* Notify application */
820     client->internal->ops->notify(client, conn, type, channel, channel);
821     break;
822
823   case SILC_NOTIFY_TYPE_KICKED:
824     /*
825      * A client (maybe me) was kicked from a channel
826      */
827
828     SILC_LOG_DEBUG(("Notify: KICKED"));
829
830     /* Get Client ID */
831     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
832     if (!tmp)
833       goto out;
834
835     client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
836     if (!client_id)
837       goto out;
838
839     /* Find Client entry */
840     client_entry = silc_client_get_client_by_id(client, conn, client_id);
841     if (!client_entry)
842       goto out;
843
844     /* Get channel entry */
845     channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
846                                 SILC_ID_CHANNEL);
847     if (!channel_id)
848       goto out;
849     channel = silc_client_get_channel_by_id(client, conn, channel_id);
850     if (!channel)
851       break;
852
853     /* From protocol version 1.1 we get the kicker's client ID as well */
854     tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
855     if (tmp) {
856       silc_free(client_id);
857       client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
858       if (!client_id)
859         goto out;
860
861       /* Find kicker's client entry and if not found resolve it */
862       client_entry2 = silc_client_get_client_by_id(client, conn, client_id);
863       if (!client_entry2) {
864         silc_client_notify_by_server_resolve(client, conn, packet, 
865                                              SILC_ID_CLIENT, client_id);
866         goto out;
867       } else {
868         if (client_entry2 != conn->local_entry)
869           silc_client_nickname_format(client, conn, client_entry2);
870       }
871     }
872
873     /* Get comment */
874     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
875
876     /* Notify application. The channel entry is sent last as this notify
877        is for channel but application don't know it from the arguments
878        sent by server. */
879     client->internal->ops->notify(client, conn, type, client_entry, tmp, 
880                                   client_entry2, channel);
881
882     /* Remove kicked client from channel */
883     if (client_entry == conn->local_entry) {
884       /* If I was kicked from channel, remove the channel */
885       if (conn->current_channel == channel)
886         conn->current_channel = NULL;
887       silc_client_del_channel(client, conn, channel);
888     } else {
889       chu = silc_client_on_channel(channel, client_entry);
890       if (chu) {
891         silc_hash_table_del(client_entry->channels, channel);
892         silc_hash_table_del(channel->user_list, client_entry);
893         silc_free(chu);
894       }
895
896       if (!silc_hash_table_count(client_entry->channels)) {
897         SilcClientNotifyResolve res = silc_calloc(1, sizeof(*res));
898         res->context = client;
899         res->sock = silc_socket_dup(conn->sock);
900         res->packet = silc_id_dup(client_entry->id, SILC_ID_CLIENT);
901         silc_schedule_task_add(client->schedule, conn->sock->sock,
902                                silc_client_notify_check_client, res,
903                                (5 + (silc_rng_get_rn16(client->rng) % 529)),
904                                0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
905       }
906     }
907     break;
908
909   case SILC_NOTIFY_TYPE_KILLED:
910     {
911       /*
912        * A client (maybe me) was killed from the network.
913        */
914       char *comment;
915       SilcUInt32 comment_len;
916
917       SILC_LOG_DEBUG(("Notify: KILLED"));
918
919       /* Get Client ID */
920       tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
921       if (!tmp)
922         goto out;
923
924       client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
925       if (!client_id)
926         goto out;
927
928       /* Find Client entry */
929       client_entry = silc_client_get_client_by_id(client, conn, client_id);
930       if (!client_entry)
931         goto out;
932
933       /* Get comment */
934       comment = silc_argument_get_arg_type(args, 2, &comment_len);
935
936       /* From protocol version 1.1 we get killer's client ID as well */
937       tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
938       if (tmp) {
939         silc_free(client_id);
940         id = silc_id_payload_parse_id(tmp, tmp_len, &id_type);
941         if (!id)
942           goto out;
943
944         /* Find Client entry */
945         if (id_type == SILC_ID_CLIENT) {
946           /* Find Client entry */
947           client_id = id;
948           client_entry2 = silc_client_get_client_by_id(client, conn, 
949                                                        client_id);
950           if (!client_entry) {
951             silc_client_notify_by_server_resolve(client, conn, packet, 
952                                                  SILC_ID_CLIENT, client_id);
953             goto out;
954           }
955         } else if (id_type == SILC_ID_SERVER) {
956           /* Find Server entry */
957           server_id = id;
958           server = silc_client_get_server_by_id(client, conn, server_id);
959           if (!server) {
960             silc_client_notify_by_server_resolve(client, conn, packet, 
961                                                  SILC_ID_SERVER, server_id);
962             goto out;
963           }
964       
965           /* Save the pointer to the client_entry pointer */
966           client_entry2 = (SilcClientEntry)server;
967         } else {
968           /* Find Channel entry */
969           channel_id = id;
970           channel = silc_client_get_channel_by_id(client, conn, channel_id);
971           if (!channel) {
972             silc_client_notify_by_server_resolve(client, conn, packet, 
973                                                  SILC_ID_CHANNEL, channel_id);
974             goto out;
975           }
976           
977           /* Save the pointer to the client_entry pointer */
978           client_entry2 = (SilcClientEntry)channel;
979           silc_free(channel_id);
980           channel_id = NULL;
981         }
982       }
983
984       /* Notify application. */
985       client->internal->ops->notify(client, conn, type, client_entry, 
986                                     comment, id_type, client_entry2);
987
988       if (client_entry != conn->local_entry)
989         /* Remove the client from all channels and free it */
990         silc_client_del_client(client, conn, client_entry);
991     }
992     break;
993     
994   case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
995     {
996       /*
997        * A server quit the SILC network and some clients must be removed
998        * from channels as they quit as well.
999        */
1000       SilcClientEntry *clients = NULL;
1001       SilcUInt32 clients_count = 0;
1002       int i;
1003
1004       SILC_LOG_DEBUG(("Notify: SIGNOFF"));
1005
1006       for (i = 1; i < silc_argument_get_arg_num(args); i++) {
1007         /* Get Client ID */
1008         tmp = silc_argument_get_arg_type(args, i + 1, &tmp_len);
1009         if (tmp) {
1010           client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
1011           if (!client_id)
1012             goto out;
1013           
1014           /* Get the client entry */
1015           client_entry = silc_client_get_client_by_id(client, conn, client_id);
1016           if (client_entry) {
1017             clients = silc_realloc(clients, sizeof(*clients) * 
1018                                    (clients_count + 1));
1019             clients[clients_count] = client_entry;
1020             clients_count++;
1021           }
1022           silc_free(client_id);
1023         }
1024       }
1025       client_id = NULL;
1026
1027       /* Notify application. We don't keep server entries so the server
1028          entry is returned as NULL. The client's are returned as array
1029          of SilcClientEntry pointers. */
1030       client->internal->ops->notify(client, conn, type, NULL, 
1031                                     clients, clients_count);
1032
1033       for (i = 0; i < clients_count; i++) {
1034         /* Remove client from all channels */
1035         client_entry = clients[i];
1036         if (client_entry == conn->local_entry)
1037           continue;
1038
1039         /* Remove the client from all channels and free it */
1040         silc_client_del_client(client, conn, client_entry);
1041       }
1042       silc_free(clients);
1043
1044     }
1045     break;
1046
1047   case SILC_NOTIFY_TYPE_ERROR:
1048     {
1049       /*
1050        * Some has occurred and server is notifying us about it.
1051        */
1052       SilcStatus error;
1053
1054       tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
1055       if (!tmp && tmp_len != 1)
1056         goto out;
1057       error = (SilcStatus)tmp[0];
1058
1059       SILC_LOG_DEBUG(("Notify: ERROR (%d)", error));
1060
1061       if (error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1062         tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
1063         if (tmp) {
1064           client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
1065           if (!client_id)
1066             goto out;
1067           client_entry = silc_client_get_client_by_id(client, conn,
1068                                                       client_id);
1069           if (client_entry)
1070             silc_client_del_client(client, conn, client_entry);
1071         }
1072       }
1073
1074       /* Notify application. */
1075       client->internal->ops->notify(client, conn, type, error);
1076     }
1077     break;
1078
1079   case SILC_NOTIFY_TYPE_WATCH:
1080     {
1081       /*
1082        * Received notify about some client we are watching
1083        */
1084       SilcNotifyType notify = 0;
1085
1086       SILC_LOG_DEBUG(("Notify: WATCH"));
1087
1088       /* Get sender Client ID */
1089       tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
1090       if (!tmp)
1091         goto out;
1092       client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
1093       if (!client_id)
1094         goto out;
1095
1096       /* Find Client entry and if not found query it */
1097       client_entry = silc_client_get_client_by_id(client, conn, client_id);
1098       if (!client_entry) {
1099         silc_client_notify_by_server_resolve(client, conn, packet, 
1100                                              SILC_ID_CLIENT, client_id);
1101         goto out;
1102       }
1103
1104       /* Get user mode */
1105       tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
1106       if (!tmp || tmp_len != 4)
1107         goto out;
1108       SILC_GET32_MSB(mode, tmp);
1109
1110       /* Get notify type */
1111       tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
1112       if (tmp && tmp_len != 2)
1113         goto out;
1114       if (tmp)
1115         SILC_GET16_MSB(notify, tmp);
1116
1117       /* Get nickname */
1118       tmp = silc_argument_get_arg_type(args, 2, NULL);
1119       if (tmp) {
1120         char *tmp_nick = NULL;
1121
1122         if (client->internal->params->nickname_parse)
1123           client->internal->params->nickname_parse(client_entry->nickname,
1124                                                    &tmp_nick);
1125         else
1126           tmp_nick = strdup(tmp);
1127
1128         /* If same nick, the client was new to us and has become "present"
1129            to network.  Send NULL as nick to application. */
1130         if (!strcmp(tmp, tmp_nick))
1131           tmp = NULL;
1132
1133         silc_free(tmp_nick);
1134       }
1135
1136       /* Notify application. */
1137       client->internal->ops->notify(client, conn, type, client_entry,
1138                                     tmp, mode, notify);
1139
1140       client_entry->mode = mode;
1141
1142       /* If nickname was changed, remove the client entry unless the
1143          client is on some channel */
1144       if (tmp && notify == SILC_NOTIFY_TYPE_NICK_CHANGE &&
1145           !silc_hash_table_count(client_entry->channels))
1146         silc_client_del_client(client, conn, client_entry);
1147       else if (notify == SILC_NOTIFY_TYPE_SIGNOFF ||
1148                notify == SILC_NOTIFY_TYPE_SERVER_SIGNOFF ||
1149                notify == SILC_NOTIFY_TYPE_KILLED)
1150         silc_client_del_client(client, conn, client_entry);
1151     }
1152     break;
1153
1154   default:
1155     break;
1156   }
1157
1158  out:
1159   silc_notify_payload_free(payload);
1160   silc_free(client_id);
1161   silc_free(channel_id);
1162   silc_free(server_id);
1163 }