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       silc_client_notify_by_server_resolve(client, conn, packet, 
746                                            SILC_ID_CLIENT, client_id);
747       goto out;
748     }
749
750     /* Get channel entry */
751     channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
752                                 SILC_ID_CHANNEL);
753     if (!channel_id)
754       goto out;
755     channel = silc_client_get_channel_by_id(client, conn, channel_id);
756     if (!channel)
757       break;
758
759     /* Save the mode */
760     chu = silc_client_on_channel(channel, client_entry2);
761     if (chu)
762       chu->mode = mode;
763
764     /* Notify application. The channel entry is sent last as this notify
765        is for channel but application don't know it from the arguments
766        sent by server. */
767     client->internal->ops->notify(client, conn, type,
768                                   id_type, client_entry, mode, 
769                                   client_entry2, channel);
770     break;
771
772   case SILC_NOTIFY_TYPE_MOTD:
773     /*
774      * Received Message of the day
775      */
776
777     SILC_LOG_DEBUG(("Notify: MOTD"));
778
779     /* Get motd */
780     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
781     if (!tmp)
782       goto out;
783     
784     /* Notify application */
785     client->internal->ops->notify(client, conn, type, tmp);
786     break;
787
788   case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
789     /*
790      * Router has enforced a new ID to a channel. Let's change the old
791      * ID to the one provided here.
792      */
793
794     SILC_LOG_DEBUG(("Notify: CHANNEL_CHANGE"));
795
796     /* Get the old ID */
797     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
798     if (!tmp)
799       goto out;
800     channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
801     if (!channel_id)
802       goto out;
803
804     /* Get the channel entry */
805     channel = silc_client_get_channel_by_id(client, conn, channel_id);
806     if (!channel)
807       goto out;
808
809     silc_free(channel_id);
810
811     /* Get the new ID */
812     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
813     if (!tmp)
814       goto out;
815     channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
816     if (!channel_id)
817       goto out;
818
819     /* Replace the Channel ID */
820     if (silc_client_replace_channel_id(client, conn, channel, channel_id))
821       channel_id = NULL;
822
823     /* Notify application */
824     client->internal->ops->notify(client, conn, type, channel, channel);
825     break;
826
827   case SILC_NOTIFY_TYPE_KICKED:
828     /*
829      * A client (maybe me) was kicked from a channel
830      */
831
832     SILC_LOG_DEBUG(("Notify: KICKED"));
833
834     /* Get Client ID */
835     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
836     if (!tmp)
837       goto out;
838
839     client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
840     if (!client_id)
841       goto out;
842
843     /* Find Client entry */
844     client_entry = silc_client_get_client_by_id(client, conn, client_id);
845     if (!client_entry)
846       goto out;
847
848     /* Get channel entry */
849     channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
850                                 SILC_ID_CHANNEL);
851     if (!channel_id)
852       goto out;
853     channel = silc_client_get_channel_by_id(client, conn, channel_id);
854     if (!channel)
855       break;
856
857     /* From protocol version 1.1 we get the kicker's client ID as well */
858     tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
859     if (tmp) {
860       silc_free(client_id);
861       client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
862       if (!client_id)
863         goto out;
864
865       /* Find kicker's client entry and if not found resolve it */
866       client_entry2 = silc_client_get_client_by_id(client, conn, client_id);
867       if (!client_entry2) {
868         silc_client_notify_by_server_resolve(client, conn, packet, 
869                                              SILC_ID_CLIENT, client_id);
870         goto out;
871       } else {
872         if (client_entry2 != conn->local_entry)
873           silc_client_nickname_format(client, conn, client_entry2);
874       }
875     }
876
877     /* Get comment */
878     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
879
880     /* Notify application. The channel entry is sent last as this notify
881        is for channel but application don't know it from the arguments
882        sent by server. */
883     client->internal->ops->notify(client, conn, type, client_entry, tmp, 
884                                   client_entry2, channel);
885
886     /* Remove kicked client from channel */
887     if (client_entry == conn->local_entry) {
888       /* If I was kicked from channel, remove the channel */
889       if (conn->current_channel == channel)
890         conn->current_channel = NULL;
891       silc_client_del_channel(client, conn, channel);
892     } else {
893       chu = silc_client_on_channel(channel, client_entry);
894       if (chu) {
895         silc_hash_table_del(client_entry->channels, channel);
896         silc_hash_table_del(channel->user_list, client_entry);
897         silc_free(chu);
898       }
899
900       if (!silc_hash_table_count(client_entry->channels)) {
901         SilcClientNotifyResolve res = silc_calloc(1, sizeof(*res));
902         res->context = client;
903         res->sock = silc_socket_dup(conn->sock);
904         res->packet = silc_id_dup(client_entry->id, SILC_ID_CLIENT);
905         silc_schedule_task_add(client->schedule, conn->sock->sock,
906                                silc_client_notify_check_client, res,
907                                (5 + (silc_rng_get_rn16(client->rng) % 529)),
908                                0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
909       }
910     }
911     break;
912
913   case SILC_NOTIFY_TYPE_KILLED:
914     {
915       /*
916        * A client (maybe me) was killed from the network.
917        */
918       char *comment;
919       SilcUInt32 comment_len;
920
921       SILC_LOG_DEBUG(("Notify: KILLED"));
922
923       /* Get Client ID */
924       tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
925       if (!tmp)
926         goto out;
927
928       client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
929       if (!client_id)
930         goto out;
931
932       /* Find Client entry */
933       client_entry = silc_client_get_client_by_id(client, conn, client_id);
934       if (!client_entry)
935         goto out;
936
937       /* Get comment */
938       comment = silc_argument_get_arg_type(args, 2, &comment_len);
939
940       /* From protocol version 1.1 we get killer's client ID as well */
941       tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
942       if (tmp) {
943         silc_free(client_id);
944         id = silc_id_payload_parse_id(tmp, tmp_len, &id_type);
945         if (!id)
946           goto out;
947
948         /* Find Client entry */
949         if (id_type == SILC_ID_CLIENT) {
950           /* Find Client entry */
951           client_id = id;
952           client_entry2 = silc_client_get_client_by_id(client, conn, 
953                                                        client_id);
954           if (!client_entry) {
955             silc_client_notify_by_server_resolve(client, conn, packet, 
956                                                  SILC_ID_CLIENT, client_id);
957             goto out;
958           }
959         } else if (id_type == SILC_ID_SERVER) {
960           /* Find Server entry */
961           server_id = id;
962           server = silc_client_get_server_by_id(client, conn, server_id);
963           if (!server) {
964             silc_client_notify_by_server_resolve(client, conn, packet, 
965                                                  SILC_ID_SERVER, server_id);
966             goto out;
967           }
968       
969           /* Save the pointer to the client_entry pointer */
970           client_entry2 = (SilcClientEntry)server;
971         } else {
972           /* Find Channel entry */
973           channel_id = id;
974           channel = silc_client_get_channel_by_id(client, conn, channel_id);
975           if (!channel) {
976             silc_client_notify_by_server_resolve(client, conn, packet, 
977                                                  SILC_ID_CHANNEL, channel_id);
978             goto out;
979           }
980           
981           /* Save the pointer to the client_entry pointer */
982           client_entry2 = (SilcClientEntry)channel;
983           silc_free(channel_id);
984           channel_id = NULL;
985         }
986       }
987
988       /* Notify application. */
989       client->internal->ops->notify(client, conn, type, client_entry, 
990                                     comment, id_type, client_entry2);
991
992       if (client_entry != conn->local_entry)
993         /* Remove the client from all channels and free it */
994         silc_client_del_client(client, conn, client_entry);
995     }
996     break;
997     
998   case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
999     {
1000       /*
1001        * A server quit the SILC network and some clients must be removed
1002        * from channels as they quit as well.
1003        */
1004       SilcClientEntry *clients = NULL;
1005       SilcUInt32 clients_count = 0;
1006       int i;
1007
1008       SILC_LOG_DEBUG(("Notify: SIGNOFF"));
1009
1010       for (i = 1; i < silc_argument_get_arg_num(args); i++) {
1011         /* Get Client ID */
1012         tmp = silc_argument_get_arg_type(args, i + 1, &tmp_len);
1013         if (tmp) {
1014           client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
1015           if (!client_id)
1016             goto out;
1017           
1018           /* Get the client entry */
1019           client_entry = silc_client_get_client_by_id(client, conn, client_id);
1020           if (client_entry) {
1021             clients = silc_realloc(clients, sizeof(*clients) * 
1022                                    (clients_count + 1));
1023             clients[clients_count] = client_entry;
1024             clients_count++;
1025           }
1026           silc_free(client_id);
1027         }
1028       }
1029       client_id = NULL;
1030
1031       /* Notify application. We don't keep server entries so the server
1032          entry is returned as NULL. The client's are returned as array
1033          of SilcClientEntry pointers. */
1034       client->internal->ops->notify(client, conn, type, NULL, 
1035                                     clients, clients_count);
1036
1037       for (i = 0; i < clients_count; i++) {
1038         /* Remove client from all channels */
1039         client_entry = clients[i];
1040         if (client_entry == conn->local_entry)
1041           continue;
1042
1043         /* Remove the client from all channels and free it */
1044         silc_client_del_client(client, conn, client_entry);
1045       }
1046       silc_free(clients);
1047
1048     }
1049     break;
1050
1051   case SILC_NOTIFY_TYPE_ERROR:
1052     {
1053       /*
1054        * Some has occurred and server is notifying us about it.
1055        */
1056       SilcStatus error;
1057
1058       tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
1059       if (!tmp && tmp_len != 1)
1060         goto out;
1061       error = (SilcStatus)tmp[0];
1062
1063       SILC_LOG_DEBUG(("Notify: ERROR (%d)", error));
1064
1065       if (error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1066         tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
1067         if (tmp) {
1068           client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
1069           if (!client_id)
1070             goto out;
1071           client_entry = silc_client_get_client_by_id(client, conn,
1072                                                       client_id);
1073           if (client_entry)
1074             silc_client_del_client(client, conn, client_entry);
1075         }
1076       }
1077
1078       /* Notify application. */
1079       client->internal->ops->notify(client, conn, type, error);
1080     }
1081     break;
1082
1083   case SILC_NOTIFY_TYPE_WATCH:
1084     {
1085       /*
1086        * Received notify about some client we are watching
1087        */
1088       SilcNotifyType notify = 0;
1089
1090       SILC_LOG_DEBUG(("Notify: WATCH"));
1091
1092       /* Get sender Client ID */
1093       tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
1094       if (!tmp)
1095         goto out;
1096       client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
1097       if (!client_id)
1098         goto out;
1099
1100       /* Find Client entry and if not found query it */
1101       client_entry = silc_client_get_client_by_id(client, conn, client_id);
1102       if (!client_entry) {
1103         silc_client_notify_by_server_resolve(client, conn, packet, 
1104                                              SILC_ID_CLIENT, client_id);
1105         goto out;
1106       }
1107
1108       /* Get user mode */
1109       tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
1110       if (!tmp || tmp_len != 4)
1111         goto out;
1112       SILC_GET32_MSB(mode, tmp);
1113
1114       /* Get notify type */
1115       tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
1116       if (tmp && tmp_len != 2)
1117         goto out;
1118       if (tmp)
1119         SILC_GET16_MSB(notify, tmp);
1120
1121       /* Get nickname */
1122       tmp = silc_argument_get_arg_type(args, 2, NULL);
1123       if (tmp) {
1124         char *tmp_nick = NULL;
1125
1126         if (client->internal->params->nickname_parse)
1127           client->internal->params->nickname_parse(client_entry->nickname,
1128                                                    &tmp_nick);
1129         else
1130           tmp_nick = strdup(tmp);
1131
1132         /* If same nick, the client was new to us and has become "present"
1133            to network.  Send NULL as nick to application. */
1134         if (!strcmp(tmp, tmp_nick))
1135           tmp = NULL;
1136
1137         silc_free(tmp_nick);
1138       }
1139
1140       /* Notify application. */
1141       client->internal->ops->notify(client, conn, type, client_entry,
1142                                     tmp, mode, notify);
1143
1144       client_entry->mode = mode;
1145
1146       /* If nickname was changed, remove the client entry unless the
1147          client is on some channel */
1148       if (tmp && notify == SILC_NOTIFY_TYPE_NICK_CHANGE &&
1149           !silc_hash_table_count(client_entry->channels))
1150         silc_client_del_client(client, conn, client_entry);
1151       else if (notify == SILC_NOTIFY_TYPE_SIGNOFF ||
1152                notify == SILC_NOTIFY_TYPE_SERVER_SIGNOFF ||
1153                notify == SILC_NOTIFY_TYPE_KILLED)
1154         silc_client_del_client(client, conn, client_entry);
1155     }
1156     break;
1157
1158   default:
1159     break;
1160   }
1161
1162  out:
1163   silc_notify_payload_free(payload);
1164   silc_free(client_id);
1165   silc_free(channel_id);
1166   silc_free(server_id);
1167 }