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