updates.
[silc.git] / lib / silcclient / client_notify.c
1 /*
2
3   client_notify.c
4
5   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   Copyright (C) 1997 - 2001 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; either version 2 of the License, or
12   (at your option) any later version.
13   
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19 */
20 /* $Id$ */
21 /* This file includes the Notify packet handling. Notify packets are
22    important packets sent by the server. They tell different things to the
23    client such as nick changes, mode changes etc. */
24
25 #include "clientlibincludes.h"
26 #include "client_internal.h"
27
28 /* Called when notify is received and some async operation (such as command)
29    is required before processing the notify message. This calls again the
30    silc_client_notify_by_server and reprocesses the original notify packet. */
31
32 static void silc_client_notify_by_server_pending(void *context)
33 {
34   SilcPacketContext *p = (SilcPacketContext *)context;
35   silc_client_notify_by_server(p->context, p->sock, p);
36 }
37
38 /* Destructor for the pending command callback */
39
40 static void silc_client_notify_by_server_destructor(void *context)
41 {
42   silc_packet_context_free((SilcPacketContext *)context);
43 }
44
45 /* Resolve client information from server by Client ID. */
46
47 static void silc_client_notify_by_server_resolve(SilcClient client,
48                                                  SilcClientConnection conn,
49                                                  SilcPacketContext *packet,
50                                                  SilcClientID *client_id)
51 {
52   SilcPacketContext *p = silc_packet_context_dup(packet);
53   SilcBuffer idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
54
55   p->context = (void *)client;
56   p->sock = conn->sock;
57
58   silc_client_send_command(client, conn, SILC_COMMAND_WHOIS, ++conn->cmd_ident,
59                            1, 3, idp->data, idp->len);
60   silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
61                               silc_client_notify_by_server_destructor,
62                               silc_client_notify_by_server_pending, p);
63   silc_buffer_free(idp);
64 }
65
66 /* Received notify message from server */
67
68 void silc_client_notify_by_server(SilcClient client,
69                                   SilcSocketConnection sock,
70                                   SilcPacketContext *packet)
71 {
72   SilcBuffer buffer = packet->buffer;
73   SilcClientConnection conn = (SilcClientConnection)sock->user_data;
74   SilcNotifyPayload payload;
75   SilcNotifyType type;
76   SilcArgumentPayload args;
77
78   SilcClientID *client_id = NULL;
79   SilcChannelID *channel_id = NULL;
80   SilcClientEntry client_entry;
81   SilcClientEntry client_entry2;
82   SilcChannelEntry channel;
83   SilcChannelUser chu;
84   SilcIDCacheEntry id_cache = NULL;
85   unsigned char *tmp;
86   unsigned int tmp_len, mode;
87
88   payload = silc_notify_payload_parse(buffer);
89   if (!payload)
90     goto out;
91
92   type = silc_notify_get_type(payload);
93   args = silc_notify_get_args(payload);
94   if (!args)
95     goto out;
96
97   switch(type) {
98   case SILC_NOTIFY_TYPE_NONE:
99     /* Notify application */
100     client->ops->notify(client, conn, type, 
101                         silc_argument_get_arg_type(args, 1, NULL));
102     break;
103
104   case SILC_NOTIFY_TYPE_INVITE:
105     /* 
106      * Someone invited me to a channel. Find Client and Channel entries
107      * for the application.
108      */
109     
110     /* Get Channel ID */
111     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
112     if (!tmp)
113       goto out;
114
115     channel_id = silc_id_payload_parse_id(tmp, tmp_len);
116     if (!channel_id)
117       goto out;
118
119     /* Get the channel entry */
120     channel = NULL;
121     if (silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
122                                      SILC_ID_CHANNEL, &id_cache))
123       channel = (SilcChannelEntry)id_cache->context;
124
125     /* Get sender Client ID */
126     tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
127     if (!tmp)
128       goto out;
129
130     client_id = silc_id_payload_parse_id(tmp, tmp_len);
131     if (!client_id)
132       goto out;
133
134     /* Find Client entry and if not found query it */
135     client_entry = silc_client_get_client_by_id(client, conn, client_id);
136     if (!client_entry) {
137       silc_client_notify_by_server_resolve(client, conn, packet, client_id);
138       goto out;
139     }
140
141     /* Get the channel name */
142     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
143     if (!tmp)
144       goto out;
145
146     /* Notify application */
147     client->ops->notify(client, conn, type, channel, tmp, client_entry);
148     break;
149
150   case SILC_NOTIFY_TYPE_JOIN:
151     /*
152      * Someone has joined to a channel. Get their ID and nickname and
153      * cache them for later use.
154      */
155
156     /* Get Client ID */
157     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
158     if (!tmp)
159       goto out;
160
161     client_id = silc_id_payload_parse_id(tmp, tmp_len);
162     if (!client_id)
163       goto out;
164
165     /* Find Client entry and if not found query it */
166     client_entry = silc_client_get_client_by_id(client, conn, client_id);
167     if (!client_entry) {
168       silc_client_notify_by_server_resolve(client, conn, packet, client_id);
169       goto out;
170     }
171
172     /* If nickname or username hasn't been resolved, do so */
173     if (!client_entry->nickname || !client_entry->username) {
174       silc_client_notify_by_server_resolve(client, conn, packet, client_id);
175       goto out;
176     }
177
178     /* Get Channel ID */
179     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
180     if (!tmp)
181       goto out;
182
183     channel_id = silc_id_payload_parse_id(tmp, tmp_len);
184     if (!channel_id)
185       goto out;
186
187     /* Get channel entry */
188     if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
189                                      SILC_ID_CHANNEL, &id_cache))
190       break;
191
192     channel = (SilcChannelEntry)id_cache->context;
193
194     /* Add client to channel */
195     if (client_entry != conn->local_entry) {
196       chu = silc_calloc(1, sizeof(*chu));
197       chu->client = client_entry;
198       silc_list_add(channel->clients, chu);
199     }
200
201     /* XXX add support for multiple same nicks on same channel. Check
202        for them here */
203
204     /* Notify application. The channel entry is sent last as this notify
205        is for channel but application don't know it from the arguments
206        sent by server. */
207     client->ops->notify(client, conn, type, client_entry, channel);
208     break;
209
210   case SILC_NOTIFY_TYPE_LEAVE:
211     /*
212      * Someone has left a channel. We will remove it from the channel but
213      * we'll keep it in the cache in case we'll need it later.
214      */
215     
216     /* Get Client ID */
217     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
218     if (!tmp)
219       goto out;
220
221     client_id = silc_id_payload_parse_id(tmp, tmp_len);
222     if (!client_id)
223       goto out;
224
225     /* Find Client entry */
226     client_entry = 
227       silc_client_get_client_by_id(client, conn, client_id);
228     if (!client_entry)
229       goto out;
230
231     /* Get channel entry */
232     channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
233                                 SILC_ID_CHANNEL);
234     if (!channel_id)
235       goto out;
236     if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
237                                      SILC_ID_CHANNEL, &id_cache))
238       break;
239
240     channel = (SilcChannelEntry)id_cache->context;
241
242     /* Remove client from channel */
243     silc_list_start(channel->clients);
244     while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
245       if (chu->client == client_entry) {
246         silc_list_del(channel->clients, chu);
247         silc_free(chu);
248         break;
249       }
250     }
251
252     /* Notify application. The channel entry is sent last as this notify
253        is for channel but application don't know it from the arguments
254        sent by server. */
255     client->ops->notify(client, conn, type, client_entry, channel);
256     break;
257
258   case SILC_NOTIFY_TYPE_SIGNOFF:
259     /*
260      * Someone left SILC. We'll remove it from all channels and from cache.
261      */
262
263     /* Get Client ID */
264     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
265     if (!tmp)
266       goto out;
267
268     client_id = silc_id_payload_parse_id(tmp, tmp_len);
269     if (!client_id)
270       goto out;
271
272     /* Find Client entry */
273     client_entry = 
274       silc_client_get_client_by_id(client, conn, client_id);
275     if (!client_entry)
276       goto out;
277
278     /* Remove from all channels */
279     silc_client_remove_from_channels(client, conn, client_entry);
280
281     /* Remove from cache */
282     silc_idcache_del_by_id(conn->client_cache, SILC_ID_CLIENT, 
283                            client_entry->id);
284
285     /* Get signoff message */
286     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
287     if (tmp_len > 128)
288       tmp = NULL;
289
290     /* Notify application */
291     client->ops->notify(client, conn, type, client_entry, tmp);
292
293     /* Free data */
294     if (client_entry->nickname)
295       silc_free(client_entry->nickname);
296     if (client_entry->server)
297       silc_free(client_entry->server);
298     if (client_entry->id)
299       silc_free(client_entry->id);
300     if (client_entry->send_key)
301       silc_cipher_free(client_entry->send_key);
302     if (client_entry->receive_key)
303       silc_cipher_free(client_entry->receive_key);
304     break;
305
306   case SILC_NOTIFY_TYPE_TOPIC_SET:
307     /*
308      * Someone set the topic on a channel.
309      */
310
311     /* Get Client ID */
312     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
313     if (!tmp)
314       goto out;
315
316     client_id = silc_id_payload_parse_id(tmp, tmp_len);
317     if (!client_id)
318       goto out;
319
320     /* Find Client entry */
321     client_entry = 
322       silc_client_get_client_by_id(client, conn, client_id);
323     if (!client_entry)
324       goto out;
325
326     /* Get topic */
327     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
328     if (!tmp)
329       goto out;
330
331     /* Get channel entry */
332     channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
333                                 SILC_ID_CHANNEL);
334     if (!channel_id)
335       goto out;
336     if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
337                                      SILC_ID_CHANNEL, &id_cache))
338       break;
339
340     channel = (SilcChannelEntry)id_cache->context;
341
342     /* Notify application. The channel entry is sent last as this notify
343        is for channel but application don't know it from the arguments
344        sent by server. */
345     client->ops->notify(client, conn, type, client_entry, tmp, channel);
346     break;
347
348   case SILC_NOTIFY_TYPE_NICK_CHANGE:
349     /*
350      * Someone changed their nickname. If we don't have entry for the new
351      * ID we will query it and return here after it's done. After we've
352      * returned we fetch the old entry and free it and notify the 
353      * application.
354      */
355
356     /* Get old Client ID */
357     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
358     if (!tmp)
359       goto out;
360
361     client_id = silc_id_payload_parse_id(tmp, tmp_len);
362     if (!client_id)
363       goto out;
364
365     /* Ignore my ID */
366     if (!SILC_ID_CLIENT_COMPARE(client_id, conn->local_id))
367       break;
368
369     /* Find old Client entry */
370     client_entry = silc_client_get_client_by_id(client, conn, client_id);
371     if (!client_entry)
372       goto out;
373     silc_free(client_id);
374
375     /* Get new Client ID */
376     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
377     if (!tmp)
378       goto out;
379
380     client_id = silc_id_payload_parse_id(tmp, tmp_len);
381     if (!client_id)
382       goto out;
383
384     /* Find Client entry and if not found resolve it */
385     client_entry2 = silc_client_get_client_by_id(client, conn, client_id);
386     if (!client_entry2) {
387       silc_client_notify_by_server_resolve(client, conn, packet, client_id);
388       goto out;
389     }
390
391     /* Remove the old from cache */
392     silc_idcache_del_by_id(conn->client_cache, SILC_ID_CLIENT, 
393                            client_entry->id);
394
395     /* Replace old ID entry with new one on all channels. */
396     silc_client_replace_from_channels(client, conn, client_entry,
397                                       client_entry2);
398
399     /* Notify application */
400     client->ops->notify(client, conn, type, client_entry, client_entry2);
401
402     /* Free data */
403     if (client_entry->nickname)
404       silc_free(client_entry->nickname);
405     if (client_entry->server)
406       silc_free(client_entry->server);
407     if (client_entry->id)
408       silc_free(client_entry->id);
409     if (client_entry->send_key)
410       silc_cipher_free(client_entry->send_key);
411     if (client_entry->receive_key)
412       silc_cipher_free(client_entry->receive_key);
413     silc_free(client_entry);
414     break;
415
416   case SILC_NOTIFY_TYPE_CMODE_CHANGE:
417     /*
418      * Someone changed a channel mode
419      */
420
421     /* Get Client ID */
422     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
423     if (!tmp)
424       goto out;
425
426     client_id = silc_id_payload_parse_id(tmp, tmp_len);
427     if (!client_id)
428       goto out;
429
430     /* Find Client entry */
431     client_entry = 
432       silc_client_get_client_by_id(client, conn, client_id);
433     if (!client_entry)
434       goto out;
435
436     /* Get the mode */
437     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
438     if (!tmp)
439       goto out;
440
441     SILC_GET32_MSB(mode, tmp);
442
443     /* Get channel entry */
444     channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
445                                 SILC_ID_CHANNEL);
446     if (!channel_id)
447       goto out;
448     if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
449                                      SILC_ID_CHANNEL, &id_cache))
450       break;
451
452     channel = (SilcChannelEntry)id_cache->context;
453
454     /* Save the new mode */
455     channel->mode = mode;
456
457     /* Get the hmac */
458     tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
459     if (tmp) {
460       unsigned char hash[32];
461
462       if (channel->hmac)
463         silc_hmac_free(channel->hmac);
464       if (!silc_hmac_alloc(tmp, NULL, &channel->hmac))
465         goto out;
466
467       silc_hash_make(channel->hmac->hash, channel->key, channel->key_len / 8,
468                      hash);
469       silc_hmac_set_key(channel->hmac, hash, 
470                         silc_hash_len(channel->hmac->hash));
471       memset(hash, 0, sizeof(hash));
472     }
473
474     /* Notify application. The channel entry is sent last as this notify
475        is for channel but application don't know it from the arguments
476        sent by server. */
477     client->ops->notify(client, conn, type, client_entry, mode, NULL, 
478                         tmp, channel);
479     break;
480
481   case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
482     /*
483      * Someone changed user's mode on a channel
484      */
485
486     /* Get Client ID */
487     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
488     if (!tmp)
489       goto out;
490
491     client_id = silc_id_payload_parse_id(tmp, tmp_len);
492     if (!client_id)
493       goto out;
494
495     /* Find Client entry */
496     client_entry = 
497       silc_client_get_client_by_id(client, conn, client_id);
498     if (!client_entry)
499       goto out;
500
501     /* Get the mode */
502     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
503     if (!tmp)
504       goto out;
505
506     SILC_GET32_MSB(mode, tmp);
507
508     /* Get target Client ID */
509     tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
510     if (!tmp)
511       goto out;
512
513     silc_free(client_id);
514     client_id = silc_id_payload_parse_id(tmp, tmp_len);
515     if (!client_id)
516       goto out;
517
518     /* Find target Client entry */
519     client_entry2 = 
520       silc_client_get_client_by_id(client, conn, client_id);
521     if (!client_entry2)
522       goto out;
523
524     /* Get channel entry */
525     channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
526                                 SILC_ID_CHANNEL);
527     if (!channel_id)
528       goto out;
529     if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
530                                      SILC_ID_CHANNEL, &id_cache))
531       break;
532
533     channel = (SilcChannelEntry)id_cache->context;
534
535     /* Save the mode */
536     silc_list_start(channel->clients);
537     while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
538       if (chu->client == client_entry) {
539         chu->mode = mode;
540         break;
541       }
542     }
543
544     /* Notify application. The channel entry is sent last as this notify
545        is for channel but application don't know it from the arguments
546        sent by server. */
547     client->ops->notify(client, conn, type, client_entry, mode, 
548                         client_entry2, channel);
549     break;
550
551   case SILC_NOTIFY_TYPE_MOTD:
552     /*
553      * Received Message of the day
554      */
555
556     /* Get motd */
557     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
558     if (!tmp)
559       goto out;
560     
561     /* Notify application */
562     client->ops->notify(client, conn, type, tmp);
563     break;
564
565   case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
566     /*
567      * Router has enforced a new ID to a channel. Let's change the old
568      * ID to the one provided here.
569      */
570
571     /* Get the old ID */
572     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
573     if (!tmp)
574       goto out;
575     channel_id = silc_id_payload_parse_id(tmp, tmp_len);
576     if (!channel_id)
577       goto out;
578     
579     /* Get the channel entry */
580     if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
581                                      SILC_ID_CHANNEL, &id_cache))
582       break;
583
584     channel = (SilcChannelEntry)id_cache->context;
585
586     /* Free the old ID */
587     silc_free(channel_id);
588     silc_free(channel->id);
589
590     /* Get the new ID */
591     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
592     if (!tmp)
593       goto out;
594     channel->id = silc_id_payload_parse_id(tmp, tmp_len);
595     if (!channel->id)
596       goto out;
597
598     id_cache->id = (void *)channel->id;
599
600     /* Notify application */
601     client->ops->notify(client, conn, type, channel, channel);
602     break;
603
604   case SILC_NOTIFY_TYPE_KICKED:
605     /*
606      * A client (maybe me) was kicked from a channel
607      */
608
609     /* Get Client ID */
610     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
611     if (!tmp)
612       goto out;
613
614     client_id = silc_id_payload_parse_id(tmp, tmp_len);
615     if (!client_id)
616       goto out;
617
618     /* Find Client entry */
619     client_entry = silc_client_get_client_by_id(client, conn, client_id);
620     if (!client_entry)
621       goto out;
622
623     /* Get channel entry */
624     channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
625                                 SILC_ID_CHANNEL);
626     if (!channel_id)
627       goto out;
628     if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
629                                      SILC_ID_CHANNEL, &id_cache))
630       break;
631
632     channel = (SilcChannelEntry)id_cache->context;
633
634     /* Get comment */
635     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
636
637     /* Notify application. The channel entry is sent last as this notify
638        is for channel but application don't know it from the arguments
639        sent by server. */
640     client->ops->notify(client, conn, type, client_entry, tmp, channel);
641
642     /* If I was kicked from channel, remove the channel */
643     if (client_entry == conn->local_entry) {
644       if (conn->current_channel == channel)
645         conn->current_channel = NULL;
646       silc_idcache_del_by_id(conn->channel_cache, 
647                              SILC_ID_CHANNEL, channel->id);
648       silc_free(channel->channel_name);
649       silc_free(channel->id);
650       silc_free(channel->key);
651       silc_cipher_free(channel->channel_key);
652       silc_free(channel);
653     }
654     break;
655
656   case SILC_NOTIFY_TYPE_KILLED:
657     /*
658      * A client (maybe me) was killed from the network.
659      */
660
661     /* Get Client ID */
662     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
663     if (!tmp)
664       goto out;
665
666     client_id = silc_id_payload_parse_id(tmp, tmp_len);
667     if (!client_id)
668       goto out;
669
670     /* Find Client entry */
671     client_entry = silc_client_get_client_by_id(client, conn, client_id);
672     if (!client_entry)
673       goto out;
674
675     /* Get comment */
676     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
677
678     /* Notify application. The channel entry is sent last as this notify
679        is for channel but application don't know it from the arguments
680        sent by server. */
681     client->ops->notify(client, conn, type, client_entry, tmp);
682
683     if (client_entry != conn->local_entry) {
684       /* Remove client from all channels */
685       silc_client_remove_from_channels(client, conn, client_entry);
686       silc_idcache_del_by_id(conn->client_cache, SILC_ID_CLIENT, 
687                              client_entry->id);
688       if (client_entry->nickname)
689         silc_free(client_entry->nickname);
690       if (client_entry->server)
691         silc_free(client_entry->server);
692       if (client_entry->id)
693         silc_free(client_entry->id);
694       if (client_entry->send_key)
695         silc_cipher_free(client_entry->send_key);
696       if (client_entry->receive_key)
697         silc_cipher_free(client_entry->receive_key);
698       silc_free(client_entry);
699     }
700
701     break;
702     
703   default:
704     break;
705   }
706
707  out:
708   silc_notify_payload_free(payload);
709   if (client_id)
710     silc_free(client_id);
711   if (channel_id)
712     silc_free(channel_id);
713 }