5 Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
7 Copyright (C) 1997 - 2001 Pekka Riikonen
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.
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.
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. */
25 #include "clientlibincludes.h"
26 #include "client_internal.h"
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. */
32 static void silc_client_notify_by_server_pending(void *context)
34 SilcPacketContext *p = (SilcPacketContext *)context;
35 silc_client_notify_by_server(p->context, p->sock, p);
38 /* Destructor for the pending command callback */
40 static void silc_client_notify_by_server_destructor(void *context)
42 silc_packet_context_free((SilcPacketContext *)context);
45 /* Resolve client information from server by Client ID. */
47 static void silc_client_notify_by_server_resolve(SilcClient client,
48 SilcClientConnection conn,
49 SilcPacketContext *packet,
50 SilcClientID *client_id)
52 SilcPacketContext *p = silc_packet_context_dup(packet);
53 SilcBuffer idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
55 p->context = (void *)client;
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);
66 /* Received notify message from server */
68 void silc_client_notify_by_server(SilcClient client,
69 SilcSocketConnection sock,
70 SilcPacketContext *packet)
72 SilcBuffer buffer = packet->buffer;
73 SilcClientConnection conn = (SilcClientConnection)sock->user_data;
74 SilcNotifyPayload payload;
76 SilcArgumentPayload args;
78 SilcClientID *client_id = NULL;
79 SilcChannelID *channel_id = NULL;
80 SilcClientEntry client_entry;
81 SilcClientEntry client_entry2;
82 SilcChannelEntry channel;
84 SilcIDCacheEntry id_cache = NULL;
86 unsigned int tmp_len, mode;
88 payload = silc_notify_payload_parse(buffer);
92 type = silc_notify_get_type(payload);
93 args = silc_notify_get_args(payload);
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));
104 case SILC_NOTIFY_TYPE_INVITE:
106 * Someone invited me to a channel. Find Client and Channel entries
107 * for the application.
111 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
115 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
119 /* Get the channel entry */
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;
125 /* Get sender Client ID */
126 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
130 client_id = silc_id_payload_parse_id(tmp, tmp_len);
134 /* Find Client entry and if not found query it */
135 client_entry = silc_client_get_client_by_id(client, conn, client_id);
137 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
141 /* Get the channel name */
142 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
146 /* Notify application */
147 client->ops->notify(client, conn, type, channel, tmp, client_entry);
150 case SILC_NOTIFY_TYPE_JOIN:
152 * Someone has joined to a channel. Get their ID and nickname and
153 * cache them for later use.
157 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
161 client_id = silc_id_payload_parse_id(tmp, tmp_len);
165 /* Find Client entry and if not found query it */
166 client_entry = silc_client_get_client_by_id(client, conn, client_id);
168 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
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);
179 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
183 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
187 /* Get channel entry */
188 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
189 SILC_ID_CHANNEL, &id_cache))
192 channel = (SilcChannelEntry)id_cache->context;
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);
201 /* XXX add support for multiple same nicks on same channel. Check
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
207 client->ops->notify(client, conn, type, client_entry, channel);
210 case SILC_NOTIFY_TYPE_LEAVE:
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.
217 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
221 client_id = silc_id_payload_parse_id(tmp, tmp_len);
225 /* Find Client entry */
227 silc_client_get_client_by_id(client, conn, client_id);
231 /* Get channel entry */
232 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
236 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
237 SILC_ID_CHANNEL, &id_cache))
240 channel = (SilcChannelEntry)id_cache->context;
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);
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
255 client->ops->notify(client, conn, type, client_entry, channel);
258 case SILC_NOTIFY_TYPE_SIGNOFF:
260 * Someone left SILC. We'll remove it from all channels and from cache.
264 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
268 client_id = silc_id_payload_parse_id(tmp, tmp_len);
272 /* Find Client entry */
274 silc_client_get_client_by_id(client, conn, client_id);
278 /* Remove from all channels */
279 silc_client_remove_from_channels(client, conn, client_entry);
281 /* Remove from cache */
282 silc_idcache_del_by_id(conn->client_cache, SILC_ID_CLIENT,
285 /* Get signoff message */
286 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
290 /* Notify application */
291 client->ops->notify(client, conn, type, client_entry, tmp);
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);
306 case SILC_NOTIFY_TYPE_TOPIC_SET:
308 * Someone set the topic on a channel.
312 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
316 client_id = silc_id_payload_parse_id(tmp, tmp_len);
320 /* Find Client entry */
322 silc_client_get_client_by_id(client, conn, client_id);
327 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
331 /* Get channel entry */
332 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
336 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
337 SILC_ID_CHANNEL, &id_cache))
340 channel = (SilcChannelEntry)id_cache->context;
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
345 client->ops->notify(client, conn, type, client_entry, tmp, channel);
348 case SILC_NOTIFY_TYPE_NICK_CHANGE:
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
356 /* Get old Client ID */
357 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
361 client_id = silc_id_payload_parse_id(tmp, tmp_len);
366 if (!SILC_ID_CLIENT_COMPARE(client_id, conn->local_id))
369 /* Find old Client entry */
370 client_entry = silc_client_get_client_by_id(client, conn, client_id);
373 silc_free(client_id);
375 /* Get new Client ID */
376 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
380 client_id = silc_id_payload_parse_id(tmp, tmp_len);
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);
391 /* Remove the old from cache */
392 silc_idcache_del_by_id(conn->client_cache, SILC_ID_CLIENT,
395 /* Replace old ID entry with new one on all channels. */
396 silc_client_replace_from_channels(client, conn, client_entry,
399 /* Notify application */
400 client->ops->notify(client, conn, type, client_entry, client_entry2);
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);
416 case SILC_NOTIFY_TYPE_CMODE_CHANGE:
418 * Someone changed a channel mode
422 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
426 client_id = silc_id_payload_parse_id(tmp, tmp_len);
430 /* Find Client entry */
432 silc_client_get_client_by_id(client, conn, client_id);
437 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
441 SILC_GET32_MSB(mode, tmp);
443 /* Get channel entry */
444 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
448 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
449 SILC_ID_CHANNEL, &id_cache))
452 channel = (SilcChannelEntry)id_cache->context;
454 /* Save the new mode */
455 channel->mode = mode;
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
460 client->ops->notify(client, conn, type, client_entry, mode, channel);
463 case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
465 * Someone changed user's mode on a channel
469 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
473 client_id = silc_id_payload_parse_id(tmp, tmp_len);
477 /* Find Client entry */
479 silc_client_get_client_by_id(client, conn, client_id);
484 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
488 SILC_GET32_MSB(mode, tmp);
490 /* Get target Client ID */
491 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
495 silc_free(client_id);
496 client_id = silc_id_payload_parse_id(tmp, tmp_len);
500 /* Find target Client entry */
502 silc_client_get_client_by_id(client, conn, client_id);
506 /* Get channel entry */
507 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
511 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
512 SILC_ID_CHANNEL, &id_cache))
515 channel = (SilcChannelEntry)id_cache->context;
518 silc_list_start(channel->clients);
519 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
520 if (chu->client == client_entry) {
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
529 client->ops->notify(client, conn, type, client_entry, mode,
530 client_entry2, channel);
533 case SILC_NOTIFY_TYPE_MOTD:
535 * Received Message of the day
539 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
543 /* Notify application */
544 client->ops->notify(client, conn, type, tmp);
547 case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
549 * Router has enforced a new ID to a channel. Let's change the old
550 * ID to the one provided here.
554 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
557 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
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))
566 channel = (SilcChannelEntry)id_cache->context;
568 /* Free the old ID */
569 silc_free(channel_id);
570 silc_free(channel->id);
573 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
576 channel->id = silc_id_payload_parse_id(tmp, tmp_len);
580 id_cache->id = (void *)channel->id;
582 /* Notify application */
583 client->ops->notify(client, conn, type, channel, channel);
586 case SILC_NOTIFY_TYPE_KICKED:
588 * A client (maybe me) was kicked from a channel
592 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
596 client_id = silc_id_payload_parse_id(tmp, tmp_len);
600 /* Find Client entry */
601 client_entry = silc_client_get_client_by_id(client, conn, client_id);
605 /* Get channel entry */
606 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
610 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
611 SILC_ID_CHANNEL, &id_cache))
614 channel = (SilcChannelEntry)id_cache->context;
617 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
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
622 client->ops->notify(client, conn, type, client_entry, tmp, channel);
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);
638 case SILC_NOTIFY_TYPE_KILLED:
640 * A client (maybe me) was killed from the network.
644 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
648 client_id = silc_id_payload_parse_id(tmp, tmp_len);
652 /* Find Client entry */
653 client_entry = silc_client_get_client_by_id(client, conn, client_id);
658 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
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
663 client->ops->notify(client, conn, type, client_entry, tmp);
665 if (client_entry != conn->local_entry) {
666 /* Remove client from all channels */
667 silc_client_remove_from_channels(client, conn, client_entry);
668 silc_idcache_del_by_id(conn->client_cache, SILC_ID_CLIENT,
670 if (client_entry->nickname)
671 silc_free(client_entry->nickname);
672 if (client_entry->server)
673 silc_free(client_entry->server);
674 if (client_entry->id)
675 silc_free(client_entry->id);
676 if (client_entry->send_key)
677 silc_cipher_free(client_entry->send_key);
678 if (client_entry->receive_key)
679 silc_cipher_free(client_entry->receive_key);
680 silc_free(client_entry);
690 silc_notify_payload_free(payload);
692 silc_free(client_id);
694 silc_free(channel_id);