a137ce47eef996e515066f43a779dd395cdc06d5
[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 SILC_TASK_CALLBACK(silc_client_notify_del_client_cb)
48 {
49   SilcClientNotifyResolve res = (SilcClientNotifyResolve)context;
50   SilcClient client = res->context;
51   SilcClientConnection conn = res->sock->user_data;
52   SilcClientID *client_id = res->packet;
53   SilcClientEntry client_entry;
54   client_entry = silc_client_get_client_by_id(client, conn, client_id);
55   if (client_entry)
56     silc_client_del_client(client, conn, client_entry);
57   silc_free(client_id);
58   silc_socket_free(res->sock);
59   silc_free(res);
60 }
61
62 /* Called when notify is received and some async operation (such as command)
63    is required before processing the notify message. This calls again the
64    silc_client_notify_by_server and reprocesses the original notify packet. */
65
66 static void silc_client_notify_by_server_pending(void *context, void *context2)
67 {
68   SilcClientNotifyResolve res = (SilcClientNotifyResolve)context;
69   SilcClientCommandReplyContext reply = 
70     (SilcClientCommandReplyContext)context2;
71
72   SILC_LOG_DEBUG(("Start"));
73
74   if (reply && !silc_command_get_status(reply->payload, NULL, NULL))
75     goto out;
76
77   silc_client_notify_by_server(res->context, res->sock, res->packet);
78
79  out:
80   silc_socket_free(res->sock);
81   silc_packet_context_free(res->packet);
82   silc_free(res);
83 }
84
85 /* Resets the channel entry's resolve_cmd_ident after whatever-thing
86    was resolved is completed. */
87
88 static void silc_client_channel_cond(void *context, void *context2)
89 {
90   SilcClientNotifyResolve res = (SilcClientNotifyResolve)context;
91   SilcClient client = res->context;
92   SilcClientConnection conn = res->sock->user_data;
93   SilcChannelID *channel_id = res->packet;
94   SilcChannelEntry channel;
95   channel = silc_client_get_channel_by_id(client, conn, channel_id);
96   if (channel)
97     channel->resolve_cmd_ident = 0;
98   silc_free(channel_id);
99   silc_socket_free(res->sock);
100   silc_free(res);
101 }
102
103 /* Function that starts waiting for the `cmd_ident' to arrive and
104    marks the channel info being resolved.  */
105
106 static void silc_client_channel_set_wait(SilcClient client,
107                                          SilcClientConnection conn,
108                                          SilcChannelEntry channel,
109                                          SilcUInt16 cmd_ident)
110 {
111   SilcClientNotifyResolve res;
112
113   if (!channel->resolve_cmd_ident) {
114     res = silc_calloc(1, sizeof(*res));
115     res->context = client;
116     res->sock = silc_socket_dup(conn->sock);
117     res->packet = silc_id_dup(channel->id, SILC_ID_CHANNEL);
118     silc_client_command_pending(conn, SILC_COMMAND_NONE, cmd_ident,
119                                 silc_client_channel_cond, res);
120     channel->resolve_cmd_ident = cmd_ident;
121   }
122 }
123
124 /* Attaches to the channel's resolving cmd ident and calls the 
125    notify handling with `packet' after it's received. */
126
127 static void silc_client_channel_wait(SilcClient client,
128                                      SilcClientConnection conn,
129                                      SilcChannelEntry channel,
130                                      SilcPacketContext *packet)
131 {
132   SilcClientNotifyResolve res;
133
134   if (!channel->resolve_cmd_ident)
135     return;
136
137   res = silc_calloc(1, sizeof(*res));
138   res->packet = silc_packet_context_dup(packet);
139   res->context = client;
140   res->sock = silc_socket_dup(conn->sock);
141
142   silc_client_command_pending(conn, SILC_COMMAND_NONE,
143                               channel->resolve_cmd_ident,
144                               silc_client_notify_by_server_pending, res);
145 }
146
147 /* Resolve client, channel or server information. */
148
149 static void silc_client_notify_by_server_resolve(SilcClient client,
150                                                  SilcClientConnection conn,
151                                                  SilcPacketContext *packet,
152                                                  SilcIdType id_type,
153                                                  void *id)
154 {
155   SilcClientNotifyResolve res = silc_calloc(1, sizeof(*res));
156   SilcBuffer idp = silc_id_payload_encode(id, id_type);
157
158   res->packet = silc_packet_context_dup(packet);
159   res->context = client;
160   res->sock = silc_socket_dup(conn->sock);
161
162   /* For client resolving use WHOIS, and otherwise use IDENTIFY */
163   if (id_type == SILC_ID_CLIENT) {
164     silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
165                                  silc_client_command_reply_whois_i, 0,
166                                  ++conn->cmd_ident);
167     silc_client_command_send(client, conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
168                              1, 4, idp->data, idp->len);
169     silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
170                                 silc_client_notify_by_server_pending, res);
171   } else {
172     silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
173                                  silc_client_command_reply_identify_i, 0,
174                                  ++conn->cmd_ident);
175     silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY, 
176                              conn->cmd_ident, 1, 5, idp->data, idp->len);
177     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
178                                 silc_client_notify_by_server_pending, res);
179   }
180   silc_buffer_free(idp);
181 }
182
183 /* Received notify message from server */
184
185 void silc_client_notify_by_server(SilcClient client,
186                                   SilcSocketConnection sock,
187                                   SilcPacketContext *packet)
188 {
189   SilcBuffer buffer = packet->buffer;
190   SilcClientConnection conn = (SilcClientConnection)sock->user_data;
191   SilcNotifyPayload payload;
192   SilcNotifyType type;
193   SilcArgumentPayload args;
194
195   void *id;
196   SilcIdType id_type;
197   SilcClientID *client_id = NULL;
198   SilcChannelID *channel_id = NULL;
199   SilcServerID *server_id = NULL;
200   SilcClientEntry client_entry = NULL;
201   SilcClientEntry client_entry2 = NULL;
202   SilcChannelEntry channel;
203   SilcChannelUser chu;
204   SilcServerEntry server;
205   unsigned char *tmp;
206   SilcUInt32 tmp_len, mode;
207
208   SILC_LOG_DEBUG(("Start"));
209
210   payload = silc_notify_payload_parse(buffer->data, buffer->len);
211   if (!payload)
212     goto out;
213
214   type = silc_notify_get_type(payload);
215   args = silc_notify_get_args(payload);
216   if (!args)
217     goto out;
218
219   switch(type) {
220   case SILC_NOTIFY_TYPE_NONE:
221     /* Notify application */
222     client->internal->ops->notify(client, conn, type, 
223                                   silc_argument_get_arg_type(args, 1, NULL));
224     break;
225
226   case SILC_NOTIFY_TYPE_INVITE:
227     /* 
228      * Someone invited me to a channel. Find Client and Channel entries
229      * for the application.
230      */
231     
232     SILC_LOG_DEBUG(("Notify: INVITE"));
233
234     /* Get Channel ID */
235     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
236     if (!tmp)
237       goto out;
238
239     channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
240     if (!channel_id)
241       goto out;
242
243     /* Get the channel entry */
244     channel = silc_client_get_channel_by_id(client, conn, channel_id);
245
246     /* Get sender Client ID */
247     tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
248     if (!tmp)
249       goto out;
250
251     client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
252     if (!client_id)
253       goto out;
254
255     /* Find Client entry and if not found query it */
256     client_entry = silc_client_get_client_by_id(client, conn, client_id);
257     if (!client_entry) {
258       silc_client_notify_by_server_resolve(client, conn, packet, 
259                                            SILC_ID_CLIENT, client_id);
260       goto out;
261     }
262
263     /* Get the channel name */
264     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
265     if (!tmp)
266       goto out;
267
268     /* Notify application */
269     client->internal->ops->notify(client, conn, type, channel, tmp, 
270                                   client_entry);
271     break;
272
273   case SILC_NOTIFY_TYPE_JOIN:
274     /*
275      * Someone has joined to a channel. Get their ID and nickname and
276      * cache them for later use.
277      */
278
279     SILC_LOG_DEBUG(("Notify: JOIN"));
280
281     /* Get Client ID */
282     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
283     if (!tmp)
284       goto out;
285
286     client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
287     if (!client_id)
288       goto out;
289
290     /* Find Client entry and if not found query it */
291     client_entry = silc_client_get_client_by_id(client, conn, client_id);
292     if (!client_entry) {
293       silc_client_notify_by_server_resolve(client, conn, packet, 
294                                            SILC_ID_CLIENT, client_id);
295       goto out;
296     }
297
298     /* If nickname or username hasn't been resolved, do so */
299     if (!client_entry->nickname || !client_entry->username) {
300       if (client_entry->status & SILC_CLIENT_STATUS_RESOLVING) {
301         client_entry->status &= ~SILC_CLIENT_STATUS_RESOLVING;
302         goto out;
303       }
304       silc_client_notify_by_server_resolve(client, conn, packet, 
305                                            SILC_ID_CLIENT, client_id);
306       client_entry->status |= SILC_CLIENT_STATUS_RESOLVING;
307       client_entry->resolve_cmd_ident = conn->cmd_ident;
308       goto out;
309     } else {
310       if (client_entry != conn->local_entry)
311         silc_client_nickname_format(client, conn, client_entry);
312     }
313
314     /* Get Channel ID */
315     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
316     if (!tmp)
317       goto out;
318
319     channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
320     if (!channel_id)
321       goto out;
322
323     /* Get channel entry */
324     channel = silc_client_get_channel_by_id(client, conn, channel_id);
325     if (!channel)
326       break;
327
328     /* Join the client to channel */
329     if (!silc_client_on_channel(channel, client_entry)) {
330       chu = silc_calloc(1, sizeof(*chu));
331       chu->client = client_entry;
332       chu->channel = channel;
333       silc_hash_table_add(channel->user_list, client_entry, chu);
334       silc_hash_table_add(client_entry->channels, channel, chu);
335     }
336
337     /* Notify application. The channel entry is sent last as this notify
338        is for channel but application don't know it from the arguments
339        sent by server. */
340     client->internal->ops->notify(client, conn, type, client_entry, channel);
341     break;
342
343   case SILC_NOTIFY_TYPE_LEAVE:
344     /*
345      * Someone has left a channel. We will remove it from the channel but
346      * we'll keep it in the cache in case we'll need it later.
347      */
348     
349     SILC_LOG_DEBUG(("Notify: LEAVE"));
350
351     /* Get Client ID */
352     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
353     if (!tmp)
354       goto out;
355
356     client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
357     if (!client_id)
358       goto out;
359
360     /* Find Client entry */
361     client_entry = 
362       silc_client_get_client_by_id(client, conn, client_id);
363     if (!client_entry)
364       goto out;
365
366     /* Get channel entry */
367     channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
368                                 SILC_ID_CHANNEL);
369     if (!channel_id)
370       goto out;
371     channel = silc_client_get_channel_by_id(client, conn, channel_id);
372     if (!channel)
373       break;
374
375     /* Remove client from channel */
376     chu = silc_client_on_channel(channel, client_entry);
377     if (chu) {
378       silc_hash_table_del(client_entry->channels, channel);
379       silc_hash_table_del(channel->user_list, client_entry);
380       silc_free(chu);
381     }
382
383     /* Some client implementations actually quit network by first doing
384        LEAVE and then immediately SIGNOFF.  We'll check for this by doing 
385        check for the client after 5 - 34 seconds.  If it is not valid after
386        that we'll remove the client from cache. */
387     if (!silc_hash_table_count(client_entry->channels)) {
388       SilcClientNotifyResolve res = silc_calloc(1, sizeof(*res));
389       res->context = client;
390       res->sock = silc_socket_dup(conn->sock);
391       res->packet = silc_id_dup(client_id, SILC_ID_CLIENT);
392       silc_schedule_task_add(client->schedule, conn->sock->sock,
393                              silc_client_notify_check_client, res,
394                              (5 + (silc_rng_get_rn16(client->rng) % 29)),
395                              0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
396     }
397
398     /* Notify application. The channel entry is sent last as this notify
399        is for channel but application don't know it from the arguments
400        sent by server. */
401     client->internal->ops->notify(client, conn, type, client_entry, channel);
402     break;
403
404   case SILC_NOTIFY_TYPE_SIGNOFF:
405     /*
406      * Someone left SILC. We'll remove it from all channels and from cache.
407      */
408
409     SILC_LOG_DEBUG(("Notify: SIGNOFF"));
410
411     /* Get Client ID */
412     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
413     if (!tmp)
414       goto out;
415
416     client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
417     if (!client_id)
418       goto out;
419
420     /* Find Client entry */
421     client_entry = 
422       silc_client_get_client_by_id(client, conn, client_id);
423     if (!client_entry)
424       goto out;
425
426     /* Remove from all channels */
427     silc_client_remove_from_channels(client, conn, client_entry);
428
429     /* Remove from cache */
430     silc_idcache_del_by_context(conn->client_cache, client_entry);
431
432     /* Get signoff message */
433     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
434     if (tmp_len > 128)
435       tmp = NULL;
436
437     /* Notify application */
438     client->internal->ops->notify(client, conn, type, client_entry, tmp);
439
440     /* Free data */
441     silc_client_del_client_entry(client, conn, client_entry);
442     break;
443
444   case SILC_NOTIFY_TYPE_TOPIC_SET:
445     /*
446      * Someone set the topic on a channel.
447      */
448
449     SILC_LOG_DEBUG(("Notify: TOPIC_SET"));
450
451     /* Get channel entry */
452     channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
453                                 SILC_ID_CHANNEL);
454     if (!channel_id)
455       goto out;
456     channel = silc_client_get_channel_by_id(client, conn, channel_id);
457     if (!channel)
458       break;
459
460     /* Get ID */
461     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
462     if (!tmp)
463       goto out;
464     id = silc_id_payload_parse_id(tmp, tmp_len, &id_type);
465     if (!id)
466       goto out;
467
468     /* Find Client entry */
469     if (id_type == SILC_ID_CLIENT) {
470       /* Find Client entry */
471       client_id = id;
472       client_entry = silc_client_get_client_by_id(client, conn, client_id);
473       if (!client_entry) {
474         silc_client_channel_set_wait(client, conn, channel,
475                                      conn->cmd_ident + 1);
476         silc_client_notify_by_server_resolve(client, conn, packet, 
477                                              SILC_ID_CLIENT, client_id);
478         goto out;
479       }
480     } else if (id_type == SILC_ID_SERVER) {
481       /* Find Server entry */
482       server_id = id;
483       server = silc_client_get_server_by_id(client, conn, server_id);
484       if (!server) {
485         silc_client_channel_set_wait(client, conn, channel,
486                                      conn->cmd_ident + 1);
487         silc_client_notify_by_server_resolve(client, conn, packet, 
488                                              SILC_ID_SERVER, server_id);
489         server = silc_client_add_server(client, conn, NULL, NULL, server_id);
490         if (!server)
491           goto out;
492
493         server->resolve_cmd_ident = conn->cmd_ident;
494         server_id = NULL;
495         goto out;
496       }
497
498       /* If entry being resoled, wait for it before processing this notify */
499       if (server->resolve_cmd_ident) {
500         SilcClientNotifyResolve res = silc_calloc(1, sizeof(*res));
501         res->packet = silc_packet_context_dup(packet);
502         res->context = client;
503         res->sock = silc_socket_dup(conn->sock);
504         silc_client_command_pending(conn, SILC_COMMAND_NONE, 
505                                     server->resolve_cmd_ident,
506                                     silc_client_notify_by_server_pending, res);
507         goto out;
508       }
509       
510       /* Save the pointer to the client_entry pointer */
511       client_entry = (SilcClientEntry)server;
512     } else {
513       /* Find Channel entry */
514       silc_free(channel_id);
515       channel_id = id;
516       client_entry = (SilcClientEntry)
517         silc_client_get_channel_by_id(client, conn, channel_id);
518       if (!client_entry) {
519         silc_client_channel_set_wait(client, conn, channel,
520                                      conn->cmd_ident + 1);
521         silc_client_notify_by_server_resolve(client, conn, packet, 
522                                              SILC_ID_CHANNEL, channel_id);
523         goto out;
524       }
525     }
526
527     /* Get topic */
528     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
529     if (!tmp)
530       goto out;
531
532     /* If information is being resolved for this channel, wait for it */
533     if (channel->resolve_cmd_ident) {
534       silc_client_channel_wait(client, conn, channel, packet);
535       goto out;
536     }
537
538     /* Notify application. The channel entry is sent last as this notify
539        is for channel but application don't know it from the arguments
540        sent by server. */
541     client->internal->ops->notify(client, conn, type, id_type,
542                                   client_entry, tmp, channel);
543
544     break;
545
546   case SILC_NOTIFY_TYPE_NICK_CHANGE:
547     /*
548      * Someone changed their nickname. If we don't have entry for the new
549      * ID we will query it and return here after it's done. After we've
550      * returned we fetch the old entry and free it and notify the 
551      * application.
552      */
553
554     SILC_LOG_DEBUG(("Notify: NICK_CHANGE"));
555
556     /* Get old Client ID */
557     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
558     if (!tmp)
559       goto out;
560
561     client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
562     if (!client_id)
563       goto out;
564
565     /* Ignore my ID */
566     if (conn->local_id && SILC_ID_CLIENT_COMPARE(client_id, conn->local_id))
567       break;
568
569     /* Find old Client entry */
570     client_entry = silc_client_get_client_by_id(client, conn, client_id);
571     if (!client_entry)
572       goto out;
573     silc_free(client_id);
574
575     client_entry->valid = FALSE;
576
577     /* Get new Client ID */
578     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
579     if (!tmp)
580       goto out;
581
582     client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
583     if (!client_id)
584       goto out;
585
586     /* From protocol version 1.1 we get the new nickname in notify as well,
587        so we don't have to resolve it.  Do it the hard way if server doesn't
588        send it to us. */
589     tmp = silc_argument_get_arg_type(args, 3, NULL);
590     if (tmp) {
591       /* Protocol version 1.1 */
592       char *tmp_nick = NULL;
593
594       /* Check whether nickname changed at all.  It is possible that nick
595          change notify is received but nickname didn't changed, only the
596          ID changes. */
597       if (client->internal->params->nickname_parse)
598         client->internal->params->nickname_parse(client_entry->nickname,
599                                                  &tmp_nick);
600       else
601         tmp_nick = strdup(tmp);
602
603       if (tmp_nick && !strcmp(tmp, tmp_nick)) {
604         /* Nickname didn't change. Update only the ID */
605         silc_idcache_del_by_context(conn->client_cache, client_entry);
606         silc_free(client_entry->id);
607         client_entry->id = silc_id_dup(client_id, SILC_ID_CLIENT);
608         silc_idcache_add(conn->client_cache, strdup(tmp),
609                          client_entry->id, client_entry, 0, NULL);
610
611         /* Notify application */
612         client->internal->ops->notify(client, conn, type, 
613                                       client_entry, client_entry);
614         break;
615       }
616       silc_free(tmp_nick);
617
618       /* Create new client entry, and save all old information with the
619          new nickname and client ID */
620       client_entry2 = silc_client_add_client(client, conn, NULL, NULL, 
621                                              client_entry->realname,
622                                              silc_id_dup(client_id, 
623                                                          SILC_ID_CLIENT), 0);
624       if (!client_entry2)
625         goto out;
626
627       if (client_entry->server)
628         client_entry2->server = strdup(client_entry->server);
629       if (client_entry->username)
630         client_entry2->username = strdup(client_entry->username);
631       if (client_entry->hostname)
632         client_entry2->hostname = strdup(client_entry->hostname);
633       silc_client_update_client(client, conn, client_entry2, tmp, NULL, NULL,
634                                 client_entry->mode);
635     } else {
636       /* Protocol version 1.0 */
637
638       /* Find client entry and if not found resolve it */
639       client_entry2 = silc_client_get_client_by_id(client, conn, client_id);
640       if (!client_entry2) {
641         /* Resolve the entry information */
642         silc_client_notify_by_server_resolve(client, conn, packet, 
643                                              SILC_ID_CLIENT, client_id);
644
645         /* Add the new entry even though we resolved it. This is because we
646            want to replace the old entry with the new entry here right now. */
647         client_entry2 = 
648           silc_client_add_client(client, conn, NULL, NULL, NULL, 
649                                  silc_id_dup(client_id, SILC_ID_CLIENT), 
650                                  client_entry->mode);
651
652         /* Replace old ID entry with new one on all channels. */
653         silc_client_replace_from_channels(client, conn, client_entry,
654                                           client_entry2);
655         break;
656       }
657
658       if (client_entry2 != conn->local_entry)
659         silc_client_nickname_format(client, conn, client_entry2);
660     }
661
662     /* Remove the old from cache */
663     silc_idcache_del_by_context(conn->client_cache, client_entry);
664     
665     /* Replace old ID entry with new one on all channels. */
666     silc_client_replace_from_channels(client, conn, client_entry,
667                                       client_entry2);
668
669     /* Notify application */
670     client->internal->ops->notify(client, conn, type, 
671                                   client_entry, client_entry2);
672     
673     /* Free old client entry */
674     silc_client_del_client_entry(client, conn, client_entry);
675
676     break;
677
678   case SILC_NOTIFY_TYPE_CMODE_CHANGE:
679     /*
680      * Someone changed a channel mode
681      */
682
683     SILC_LOG_DEBUG(("Notify: CMODE_CHANGE"));
684
685     /* Get channel entry */
686     channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
687                                 SILC_ID_CHANNEL);
688     if (!channel_id)
689       goto out;
690     channel = silc_client_get_channel_by_id(client, conn, channel_id);
691     if (!channel)
692       goto out;
693
694     /* Get ID */
695     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
696     if (!tmp)
697       goto out;
698     id = silc_id_payload_parse_id(tmp, tmp_len, &id_type);
699     if (!id)
700       goto out;
701
702     /* Find Client entry */
703     if (id_type == SILC_ID_CLIENT) {
704       /* Find Client entry */
705       client_id = id;
706       client_entry = silc_client_get_client_by_id(client, conn, client_id);
707       if (!client_entry) {
708         silc_client_channel_set_wait(client, conn, channel,
709                                      conn->cmd_ident + 1);
710         silc_client_notify_by_server_resolve(client, conn, packet, 
711                                              SILC_ID_CLIENT, client_id);
712         goto out;
713       }
714     } else if (id_type == SILC_ID_SERVER) {
715       /* Find Server entry */
716       server_id = id;
717       server = silc_client_get_server_by_id(client, conn, server_id);
718       if (!server) {
719         silc_client_channel_set_wait(client, conn, channel,
720                                      conn->cmd_ident + 1);
721         silc_client_notify_by_server_resolve(client, conn, packet, 
722                                              SILC_ID_SERVER, server_id);
723         server = silc_client_add_server(client, conn, NULL, NULL, server_id);
724         if (!server)
725           goto out;
726
727         server->resolve_cmd_ident = conn->cmd_ident;
728         server_id = NULL;
729         goto out;
730       }
731
732       /* If entry being resoled, wait for it before processing this notify */
733       if (server->resolve_cmd_ident) {
734         SilcClientNotifyResolve res = silc_calloc(1, sizeof(*res));
735         res->packet = silc_packet_context_dup(packet);
736         res->context = client;
737         res->sock = silc_socket_dup(conn->sock);
738         silc_client_command_pending(conn, SILC_COMMAND_NONE, 
739                                     server->resolve_cmd_ident,
740                                     silc_client_notify_by_server_pending, res);
741         goto out;
742       }
743       
744       /* Save the pointer to the client_entry pointer */
745       client_entry = (SilcClientEntry)server;
746     } else {
747       /* Find Channel entry */
748       silc_free(channel_id);
749       channel_id = id;
750       client_entry = (SilcClientEntry)
751         silc_client_get_channel_by_id(client, conn, channel_id);
752       if (!client_entry) {
753         silc_client_channel_set_wait(client, conn, channel,
754                                      conn->cmd_ident + 1);
755         silc_client_notify_by_server_resolve(client, conn, packet, 
756                                              SILC_ID_CHANNEL, channel_id);
757         goto out;
758       }
759     }
760
761     /* Get the mode */
762     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
763     if (!tmp)
764       goto out;
765
766     SILC_GET32_MSB(mode, tmp);
767
768     /* If information is being resolved for this channel, wait for it */
769     if (channel->resolve_cmd_ident) {
770       silc_client_channel_wait(client, conn, channel, packet);
771       goto out;
772     }
773
774     /* Save the new mode */
775     channel->mode = mode;
776
777     /* Get the hmac */
778     tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
779     if (tmp) {
780       unsigned char hash[32];
781
782       if (channel->hmac)
783         silc_hmac_free(channel->hmac);
784       if (!silc_hmac_alloc(tmp, NULL, &channel->hmac))
785         goto out;
786
787       silc_hash_make(silc_hmac_get_hash(channel->hmac), 
788                      channel->key, channel->key_len / 8,
789                      hash);
790       silc_hmac_set_key(channel->hmac, hash, 
791                         silc_hash_len(silc_hmac_get_hash(channel->hmac)));
792       memset(hash, 0, sizeof(hash));
793     }
794
795     /* Notify application. The channel entry is sent last as this notify
796        is for channel but application don't know it from the arguments
797        sent by server. */
798     client->internal->ops->notify(client, conn, type, id_type,
799                                   client_entry, mode, NULL, tmp, channel);
800     break;
801
802   case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
803     /*
804      * Someone changed user's mode on a channel
805      */
806
807     SILC_LOG_DEBUG(("Notify: CUMODE_CHANGE"));
808
809     /* Get channel entry */
810     channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
811                                 SILC_ID_CHANNEL);
812     if (!channel_id)
813       goto out;
814     channel = silc_client_get_channel_by_id(client, conn, channel_id);
815     if (!channel)
816       break;
817
818     /* Get ID */
819     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
820     if (!tmp)
821       goto out;
822     id = silc_id_payload_parse_id(tmp, tmp_len, &id_type);
823     if (!id)
824       goto out;
825
826     /* Find Client entry */
827     if (id_type == SILC_ID_CLIENT) {
828       /* Find Client entry */
829       client_id = id;
830       client_entry = silc_client_get_client_by_id(client, conn, client_id);
831       if (!client_entry) {
832         silc_client_channel_set_wait(client, conn, channel,
833                                      conn->cmd_ident + 1);
834         silc_client_notify_by_server_resolve(client, conn, packet, 
835                                              SILC_ID_CLIENT, client_id);
836         goto out;
837       }
838     } else if (id_type == SILC_ID_SERVER) {
839       /* Find Server entry */
840       server_id = id;
841       server = silc_client_get_server_by_id(client, conn, server_id);
842       if (!server) {
843         silc_client_channel_set_wait(client, conn, channel,
844                                      conn->cmd_ident + 1);
845         silc_client_notify_by_server_resolve(client, conn, packet, 
846                                              SILC_ID_SERVER, server_id);
847         server = silc_client_add_server(client, conn, NULL, NULL, server_id);
848         if (!server)
849           goto out;
850
851         server->resolve_cmd_ident = conn->cmd_ident;
852         server_id = NULL;
853         goto out;
854       }
855
856       /* If entry being resoled, wait for it before processing this notify */
857       if (server->resolve_cmd_ident) {
858         SilcClientNotifyResolve res = silc_calloc(1, sizeof(*res));
859         res->packet = silc_packet_context_dup(packet);
860         res->context = client;
861         res->sock = silc_socket_dup(conn->sock);
862         silc_client_command_pending(conn, SILC_COMMAND_NONE, 
863                                     server->resolve_cmd_ident,
864                                     silc_client_notify_by_server_pending, res);
865         goto out;
866       }
867
868       /* Save the pointer to the client_entry pointer */
869       client_entry = (SilcClientEntry)server;
870     } else {
871       /* Find Channel entry */
872       silc_free(channel_id);
873       channel_id = id;
874       client_entry = (SilcClientEntry)
875         silc_client_get_channel_by_id(client, conn, channel_id);
876       if (!client_entry) {
877         silc_client_channel_set_wait(client, conn, channel,
878                                      conn->cmd_ident + 1);
879         silc_client_notify_by_server_resolve(client, conn, packet, 
880                                              SILC_ID_CHANNEL, channel_id);
881         goto out;
882       }
883     }
884
885     /* Get the mode */
886     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
887     if (!tmp)
888       goto out;
889
890     SILC_GET32_MSB(mode, tmp);
891
892     /* If information is being resolved for this channel, wait for it */
893     if (channel->resolve_cmd_ident) {
894       silc_client_channel_wait(client, conn, channel, packet);
895       goto out;
896     }
897
898     /* Get target Client ID */
899     tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
900     if (!tmp)
901       goto out;
902
903     silc_free(client_id);
904     client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
905     if (!client_id)
906       goto out;
907
908     /* Find target Client entry */
909     client_entry2 = 
910       silc_client_get_client_by_id(client, conn, client_id);
911     if (!client_entry2) {
912       silc_client_notify_by_server_resolve(client, conn, packet, 
913                                            SILC_ID_CLIENT, client_id);
914       goto out;
915     }
916
917     /* Save the mode */
918     chu = silc_client_on_channel(channel, client_entry2);
919     if (chu)
920       chu->mode = mode;
921
922     /* Notify application. The channel entry is sent last as this notify
923        is for channel but application don't know it from the arguments
924        sent by server. */
925     client->internal->ops->notify(client, conn, type,
926                                   id_type, client_entry, mode, 
927                                   client_entry2, channel);
928     break;
929
930   case SILC_NOTIFY_TYPE_MOTD:
931     /*
932      * Received Message of the day
933      */
934
935     SILC_LOG_DEBUG(("Notify: MOTD"));
936
937     /* Get motd */
938     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
939     if (!tmp)
940       goto out;
941     
942     /* Notify application */
943     client->internal->ops->notify(client, conn, type, tmp);
944     break;
945
946   case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
947     /*
948      * Router has enforced a new ID to a channel. Let's change the old
949      * ID to the one provided here.
950      */
951
952     SILC_LOG_DEBUG(("Notify: CHANNEL_CHANGE"));
953
954     /* Get the old ID */
955     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
956     if (!tmp)
957       goto out;
958     channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
959     if (!channel_id)
960       goto out;
961
962     /* Get the channel entry */
963     channel = silc_client_get_channel_by_id(client, conn, channel_id);
964     if (!channel)
965       goto out;
966
967     silc_free(channel_id);
968
969     /* Get the new ID */
970     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
971     if (!tmp)
972       goto out;
973     channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
974     if (!channel_id)
975       goto out;
976
977     /* Replace the Channel ID */
978     if (silc_client_replace_channel_id(client, conn, channel, channel_id))
979       channel_id = NULL;
980
981     /* Notify application */
982     client->internal->ops->notify(client, conn, type, channel, channel);
983     break;
984
985   case SILC_NOTIFY_TYPE_KICKED:
986     /*
987      * A client (maybe me) was kicked from a channel
988      */
989
990     SILC_LOG_DEBUG(("Notify: KICKED"));
991
992     /* Get Client ID */
993     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
994     if (!tmp)
995       goto out;
996
997     client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
998     if (!client_id)
999       goto out;
1000
1001     /* Find Client entry */
1002     client_entry = silc_client_get_client_by_id(client, conn, client_id);
1003     if (!client_entry)
1004       goto out;
1005
1006     /* Get channel entry */
1007     channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
1008                                 SILC_ID_CHANNEL);
1009     if (!channel_id)
1010       goto out;
1011     channel = silc_client_get_channel_by_id(client, conn, channel_id);
1012     if (!channel)
1013       break;
1014
1015     /* From protocol version 1.1 we get the kicker's client ID as well */
1016     tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
1017     if (tmp) {
1018       silc_free(client_id);
1019       client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
1020       if (!client_id)
1021         goto out;
1022
1023       /* Find kicker's client entry and if not found resolve it */
1024       client_entry2 = silc_client_get_client_by_id(client, conn, client_id);
1025       if (!client_entry2) {
1026         silc_client_notify_by_server_resolve(client, conn, packet, 
1027                                              SILC_ID_CLIENT, client_id);
1028         goto out;
1029       } else {
1030         if (client_entry2 != conn->local_entry)
1031           silc_client_nickname_format(client, conn, client_entry2);
1032       }
1033     }
1034
1035     /* Get comment */
1036     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
1037
1038     /* Notify application. The channel entry is sent last as this notify
1039        is for channel but application don't know it from the arguments
1040        sent by server. */
1041     client->internal->ops->notify(client, conn, type, client_entry, tmp, 
1042                                   client_entry2, channel);
1043
1044     /* Remove kicked client from channel */
1045     if (client_entry == conn->local_entry) {
1046       /* If I was kicked from channel, remove the channel */
1047       if (conn->current_channel == channel)
1048         conn->current_channel = NULL;
1049       silc_client_del_channel(client, conn, channel);
1050     } else {
1051       chu = silc_client_on_channel(channel, client_entry);
1052       if (chu) {
1053         silc_hash_table_del(client_entry->channels, channel);
1054         silc_hash_table_del(channel->user_list, client_entry);
1055         silc_free(chu);
1056       }
1057
1058       if (!silc_hash_table_count(client_entry->channels)) {
1059         SilcClientNotifyResolve res = silc_calloc(1, sizeof(*res));
1060         res->context = client;
1061         res->sock = silc_socket_dup(conn->sock);
1062         res->packet = silc_id_dup(client_entry->id, SILC_ID_CLIENT);
1063         silc_schedule_task_add(client->schedule, conn->sock->sock,
1064                                silc_client_notify_check_client, res,
1065                                (5 + (silc_rng_get_rn16(client->rng) % 529)),
1066                                0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
1067       }
1068     }
1069     break;
1070
1071   case SILC_NOTIFY_TYPE_KILLED:
1072     {
1073       /*
1074        * A client (maybe me) was killed from the network.
1075        */
1076       char *comment;
1077       SilcUInt32 comment_len;
1078
1079       SILC_LOG_DEBUG(("Notify: KILLED"));
1080
1081       /* Get Client ID */
1082       tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
1083       if (!tmp)
1084         goto out;
1085
1086       client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
1087       if (!client_id)
1088         goto out;
1089
1090       /* Find Client entry */
1091       client_entry = silc_client_get_client_by_id(client, conn, client_id);
1092       if (!client_entry)
1093         goto out;
1094
1095       /* Get comment */
1096       comment = silc_argument_get_arg_type(args, 2, &comment_len);
1097
1098       /* From protocol version 1.1 we get killer's client ID as well */
1099       tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
1100       if (tmp) {
1101         silc_free(client_id);
1102         id = silc_id_payload_parse_id(tmp, tmp_len, &id_type);
1103         if (!id)
1104           goto out;
1105
1106         /* Find Client entry */
1107         if (id_type == SILC_ID_CLIENT) {
1108           /* Find Client entry */
1109           client_id = id;
1110           client_entry2 = silc_client_get_client_by_id(client, conn, 
1111                                                        client_id);
1112           if (!client_entry) {
1113             silc_client_notify_by_server_resolve(client, conn, packet, 
1114                                                  SILC_ID_CLIENT, client_id);
1115             goto out;
1116           }
1117         } else if (id_type == SILC_ID_SERVER) {
1118           /* Find Server entry */
1119           server_id = id;
1120           server = silc_client_get_server_by_id(client, conn, server_id);
1121           if (!server) {
1122             silc_client_notify_by_server_resolve(client, conn, packet, 
1123                                                  SILC_ID_SERVER, server_id);
1124             server = silc_client_add_server(client, conn, NULL, NULL,
1125                                             server_id);
1126             if (!server)
1127               goto out;
1128
1129             server->resolve_cmd_ident = conn->cmd_ident;
1130             server_id = NULL;
1131             goto out;
1132           }
1133
1134           if (server->resolve_cmd_ident) {
1135             SilcClientNotifyResolve res = silc_calloc(1, sizeof(*res));
1136             res->packet = silc_packet_context_dup(packet);
1137             res->context = client;
1138             res->sock = silc_socket_dup(conn->sock);
1139             silc_client_command_pending(conn, SILC_COMMAND_NONE, 
1140                                         server->resolve_cmd_ident,
1141                                         silc_client_notify_by_server_pending,
1142                                         res);
1143             goto out;
1144           }
1145       
1146           /* Save the pointer to the client_entry pointer */
1147           client_entry2 = (SilcClientEntry)server;
1148         } else {
1149           /* Find Channel entry */
1150           channel_id = id;
1151           channel = silc_client_get_channel_by_id(client, conn, channel_id);
1152           if (!channel) {
1153             silc_client_notify_by_server_resolve(client, conn, packet, 
1154                                                  SILC_ID_CHANNEL, channel_id);
1155             goto out;
1156           }
1157           
1158           /* Save the pointer to the client_entry pointer */
1159           client_entry2 = (SilcClientEntry)channel;
1160           silc_free(channel_id);
1161           channel_id = NULL;
1162         }
1163       }
1164
1165       /* Notify application. */
1166       client->internal->ops->notify(client, conn, type, client_entry, 
1167                                     comment, id_type, client_entry2);
1168
1169       if (client_entry != conn->local_entry)
1170         /* Remove the client from all channels and free it */
1171         silc_client_del_client(client, conn, client_entry);
1172     }
1173     break;
1174     
1175   case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
1176     {
1177       /*
1178        * A server quit the SILC network and some clients must be removed
1179        * from channels as they quit as well.
1180        */
1181       SilcClientEntry *clients = NULL;
1182       SilcUInt32 clients_count = 0;
1183       int i;
1184
1185       SILC_LOG_DEBUG(("Notify: SIGNOFF"));
1186
1187       for (i = 1; i < silc_argument_get_arg_num(args); i++) {
1188         /* Get Client ID */
1189         tmp = silc_argument_get_arg_type(args, i + 1, &tmp_len);
1190         if (tmp) {
1191           client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
1192           if (!client_id)
1193             goto out;
1194           
1195           /* Get the client entry */
1196           client_entry = silc_client_get_client_by_id(client, conn, client_id);
1197           if (client_entry) {
1198             clients = silc_realloc(clients, sizeof(*clients) * 
1199                                    (clients_count + 1));
1200             clients[clients_count] = client_entry;
1201             clients_count++;
1202           }
1203           silc_free(client_id);
1204         }
1205       }
1206       client_id = NULL;
1207
1208       /* Notify application. We don't keep server entries so the server
1209          entry is returned as NULL. The client's are returned as array
1210          of SilcClientEntry pointers. */
1211       client->internal->ops->notify(client, conn, type, NULL, 
1212                                     clients, clients_count);
1213
1214       for (i = 0; i < clients_count; i++) {
1215         /* Remove client from all channels */
1216         client_entry = clients[i];
1217         if (client_entry == conn->local_entry)
1218           continue;
1219
1220         /* Remove the client from all channels and free it */
1221         silc_client_del_client(client, conn, client_entry);
1222       }
1223       silc_free(clients);
1224
1225     }
1226     break;
1227
1228   case SILC_NOTIFY_TYPE_ERROR:
1229     {
1230       /*
1231        * Some has occurred and server is notifying us about it.
1232        */
1233       SilcStatus error;
1234
1235       tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
1236       if (!tmp && tmp_len != 1)
1237         goto out;
1238       error = (SilcStatus)tmp[0];
1239
1240       SILC_LOG_DEBUG(("Notify: ERROR (%d)", error));
1241
1242       if (error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
1243         tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
1244         if (tmp) {
1245           client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
1246           if (!client_id)
1247             goto out;
1248           client_entry = silc_client_get_client_by_id(client, conn,
1249                                                       client_id);
1250           if (client_entry)
1251             silc_client_del_client(client, conn, client_entry);
1252         }
1253       }
1254
1255       /* Notify application. */
1256       client->internal->ops->notify(client, conn, type, error);
1257     }
1258     break;
1259
1260   case SILC_NOTIFY_TYPE_WATCH:
1261     {
1262       /*
1263        * Received notify about some client we are watching
1264        */
1265       SilcNotifyType notify = 0;
1266       bool del_client = FALSE;
1267
1268       SILC_LOG_DEBUG(("Notify: WATCH"));
1269
1270       /* Get sender Client ID */
1271       tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
1272       if (!tmp)
1273         goto out;
1274       client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
1275       if (!client_id)
1276         goto out;
1277
1278       /* Find Client entry and if not found query it */
1279       client_entry = silc_client_get_client_by_id(client, conn, client_id);
1280       if (!client_entry) {
1281         silc_client_notify_by_server_resolve(client, conn, packet, 
1282                                              SILC_ID_CLIENT, client_id);
1283         goto out;
1284       }
1285
1286       /* Get user mode */
1287       tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
1288       if (!tmp || tmp_len != 4)
1289         goto out;
1290       SILC_GET32_MSB(mode, tmp);
1291
1292       /* Get notify type */
1293       tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
1294       if (tmp && tmp_len != 2)
1295         goto out;
1296       if (tmp)
1297         SILC_GET16_MSB(notify, tmp);
1298
1299       /* Get nickname */
1300       tmp = silc_argument_get_arg_type(args, 2, NULL);
1301       if (tmp) {
1302         char *tmp_nick = NULL;
1303
1304         if (client->internal->params->nickname_parse)
1305           client->internal->params->nickname_parse(client_entry->nickname,
1306                                                    &tmp_nick);
1307         else
1308           tmp_nick = strdup(tmp);
1309
1310         /* If same nick, the client was new to us and has become "present"
1311            to network.  Send NULL as nick to application. */
1312         if (tmp_nick && !strcmp(tmp, tmp_nick))
1313           tmp = NULL;
1314
1315         silc_free(tmp_nick);
1316       }
1317
1318       /* Notify application. */
1319       client->internal->ops->notify(client, conn, type, client_entry,
1320                                     tmp, mode, notify);
1321
1322       client_entry->mode = mode;
1323
1324       /* If nickname was changed, remove the client entry unless the
1325          client is on some channel */
1326       if (tmp && notify == SILC_NOTIFY_TYPE_NICK_CHANGE &&
1327           !silc_hash_table_count(client_entry->channels))
1328         del_client = TRUE;
1329       else if (notify == SILC_NOTIFY_TYPE_SIGNOFF ||
1330                notify == SILC_NOTIFY_TYPE_SERVER_SIGNOFF ||
1331                notify == SILC_NOTIFY_TYPE_KILLED)
1332         del_client = TRUE;
1333
1334       if (del_client) {
1335         SilcClientNotifyResolve res = silc_calloc(1, sizeof(*res));
1336         res->context = client;
1337         res->sock = silc_socket_dup(conn->sock);
1338         res->packet = client_id;
1339         client_id = NULL;
1340         silc_schedule_task_add(client->schedule, conn->sock->sock,
1341                                silc_client_notify_del_client_cb, res,
1342                                1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
1343       }
1344     }
1345     break;
1346
1347   default:
1348     break;
1349   }
1350
1351  out:
1352   silc_notify_payload_free(payload);
1353   silc_free(client_id);
1354   silc_free(channel_id);
1355   silc_free(server_id);
1356 }