Added KILL command.
[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 Client ID */
111     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
112     if (!tmp)
113       goto out;
114
115     client_id = silc_id_payload_parse_id(tmp, tmp_len);
116     if (!client_id)
117       goto out;
118
119     /* Find Client entry and if not found query it */
120     client_entry = silc_client_get_client_by_id(client, conn, client_id);
121     if (!client_entry) {
122       silc_client_notify_by_server_resolve(client, conn, packet, client_id);
123       goto out;
124     }
125
126     /* Get Channel ID */
127     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
128     if (!tmp)
129       goto out;
130
131     channel_id = silc_id_payload_parse_id(tmp, tmp_len);
132     if (!channel_id)
133       goto out;
134
135     /* XXX Will ALWAYS fail because currently we don't have way to resolve
136        channel information for channel that we're not joined to. */
137     /* XXX ways to fix: use (extended) LIST command, or define the channel
138        name to the notfy type when name resolving is not mandatory. */
139     /* Find channel entry */
140     if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
141                                      SILC_ID_CHANNEL, &id_cache))
142       goto out;
143
144     channel = (SilcChannelEntry)id_cache->context;
145
146     /* Notify application */
147     client->ops->notify(client, conn, type, client_entry, channel);
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     /* Notify application. The channel entry is sent last as this notify
458        is for channel but application don't know it from the arguments
459        sent by server. */
460     client->ops->notify(client, conn, type, client_entry, mode, channel);
461     break;
462
463   case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
464     /*
465      * Someone changed user's mode on a channel
466      */
467
468     /* Get Client ID */
469     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
470     if (!tmp)
471       goto out;
472
473     client_id = silc_id_payload_parse_id(tmp, tmp_len);
474     if (!client_id)
475       goto out;
476
477     /* Find Client entry */
478     client_entry = 
479       silc_client_get_client_by_id(client, conn, client_id);
480     if (!client_entry)
481       goto out;
482
483     /* Get the mode */
484     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
485     if (!tmp)
486       goto out;
487
488     SILC_GET32_MSB(mode, tmp);
489
490     /* Get target Client ID */
491     tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
492     if (!tmp)
493       goto out;
494
495     silc_free(client_id);
496     client_id = silc_id_payload_parse_id(tmp, tmp_len);
497     if (!client_id)
498       goto out;
499
500     /* Find target Client entry */
501     client_entry2 = 
502       silc_client_get_client_by_id(client, conn, client_id);
503     if (!client_entry2)
504       goto out;
505
506     /* Get channel entry */
507     channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
508                                 SILC_ID_CHANNEL);
509     if (!channel_id)
510       goto out;
511     if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
512                                      SILC_ID_CHANNEL, &id_cache))
513       break;
514
515     channel = (SilcChannelEntry)id_cache->context;
516
517     /* Save the mode */
518     silc_list_start(channel->clients);
519     while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
520       if (chu->client == client_entry) {
521         chu->mode = mode;
522         break;
523       }
524     }
525
526     /* Notify application. The channel entry is sent last as this notify
527        is for channel but application don't know it from the arguments
528        sent by server. */
529     client->ops->notify(client, conn, type, client_entry, mode, 
530                         client_entry2, channel);
531     break;
532
533   case SILC_NOTIFY_TYPE_MOTD:
534     /*
535      * Received Message of the day
536      */
537
538     /* Get motd */
539     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
540     if (!tmp)
541       goto out;
542     
543     /* Notify application */
544     client->ops->notify(client, conn, type, tmp);
545     break;
546
547   case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
548     /*
549      * Router has enforced a new ID to a channel. Let's change the old
550      * ID to the one provided here.
551      */
552
553     /* Get the old ID */
554     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
555     if (!tmp)
556       goto out;
557     channel_id = silc_id_payload_parse_id(tmp, tmp_len);
558     if (!channel_id)
559       goto out;
560     
561     /* Get the channel entry */
562     if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
563                                      SILC_ID_CHANNEL, &id_cache))
564       break;
565
566     channel = (SilcChannelEntry)id_cache->context;
567
568     /* Free the old ID */
569     silc_free(channel_id);
570     silc_free(channel->id);
571
572     /* Get the new ID */
573     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
574     if (!tmp)
575       goto out;
576     channel->id = silc_id_payload_parse_id(tmp, tmp_len);
577     if (!channel->id)
578       goto out;
579
580     id_cache->id = (void *)channel->id;
581
582     /* Notify application */
583     client->ops->notify(client, conn, type, channel, channel);
584     break;
585
586   case SILC_NOTIFY_TYPE_KICKED:
587     /*
588      * A client (maybe me) was kicked from a channel
589      */
590
591     /* Get Client ID */
592     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
593     if (!tmp)
594       goto out;
595
596     client_id = silc_id_payload_parse_id(tmp, tmp_len);
597     if (!client_id)
598       goto out;
599
600     /* Find Client entry */
601     client_entry = silc_client_get_client_by_id(client, conn, client_id);
602     if (!client_entry)
603       goto out;
604
605     /* Get channel entry */
606     channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
607                                 SILC_ID_CHANNEL);
608     if (!channel_id)
609       goto out;
610     if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
611                                      SILC_ID_CHANNEL, &id_cache))
612       break;
613
614     channel = (SilcChannelEntry)id_cache->context;
615
616     /* Get comment */
617     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
618
619     /* Notify application. The channel entry is sent last as this notify
620        is for channel but application don't know it from the arguments
621        sent by server. */
622     client->ops->notify(client, conn, type, client_entry, tmp, channel);
623
624     /* If I was kicked from channel, remove the channel */
625     if (client_entry == conn->local_entry) {
626       if (conn->current_channel == channel)
627         conn->current_channel = NULL;
628       silc_idcache_del_by_id(conn->channel_cache, 
629                              SILC_ID_CHANNEL, channel->id);
630       silc_free(channel->channel_name);
631       silc_free(channel->id);
632       silc_free(channel->key);
633       silc_cipher_free(channel->channel_key);
634       silc_free(channel);
635     }
636     break;
637
638   case SILC_NOTIFY_TYPE_KILLED:
639     /*
640      * A client (maybe me) was killed from the network.
641      */
642
643     /* Get Client ID */
644     tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
645     if (!tmp)
646       goto out;
647
648     client_id = silc_id_payload_parse_id(tmp, tmp_len);
649     if (!client_id)
650       goto out;
651
652     /* Find Client entry */
653     client_entry = silc_client_get_client_by_id(client, conn, client_id);
654     if (!client_entry)
655       goto out;
656
657     /* Get comment */
658     tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
659
660     /* Notify application. The channel entry is sent last as this notify
661        is for channel but application don't know it from the arguments
662        sent by server. */
663     client->ops->notify(client, conn, type, client_entry, tmp);
664
665     if (client_entry != conn->local_entry) {
666       silc_idcache_del_by_id(conn->client_cache, SILC_ID_CLIENT, 
667                              client_entry->id);
668       if (client_entry->nickname)
669         silc_free(client_entry->nickname);
670       if (client_entry->server)
671         silc_free(client_entry->server);
672       if (client_entry->id)
673         silc_free(client_entry->id);
674       if (client_entry->send_key)
675         silc_cipher_free(client_entry->send_key);
676       if (client_entry->receive_key)
677         silc_cipher_free(client_entry->receive_key);
678       silc_free(client_entry);
679     }
680
681     break;
682     
683   default:
684     break;
685   }
686
687  out:
688   silc_notify_payload_free(payload);
689   if (client_id)
690     silc_free(client_id);
691   if (channel_id)
692     silc_free(channel_id);
693 }