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);
36 silc_socket_free(p->sock);
39 /* Destructor for the pending command callback */
41 static void silc_client_notify_by_server_destructor(void *context)
43 silc_packet_context_free((SilcPacketContext *)context);
46 /* Resolve client information from server by Client ID. */
48 static void silc_client_notify_by_server_resolve(SilcClient client,
49 SilcClientConnection conn,
50 SilcPacketContext *packet,
51 SilcClientID *client_id)
53 SilcPacketContext *p = silc_packet_context_dup(packet);
54 SilcBuffer idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
56 p->context = (void *)client;
57 p->sock = silc_socket_dup(conn->sock);
59 silc_client_send_command(client, conn, SILC_COMMAND_WHOIS, ++conn->cmd_ident,
60 1, 3, idp->data, idp->len);
61 silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
62 silc_client_notify_by_server_destructor,
63 silc_client_notify_by_server_pending, p);
64 silc_buffer_free(idp);
67 /* Received notify message from server */
69 void silc_client_notify_by_server(SilcClient client,
70 SilcSocketConnection sock,
71 SilcPacketContext *packet)
73 SilcBuffer buffer = packet->buffer;
74 SilcClientConnection conn = (SilcClientConnection)sock->user_data;
75 SilcNotifyPayload payload;
77 SilcArgumentPayload args;
80 SilcClientID *client_id = NULL;
81 SilcChannelID *channel_id = NULL;
82 SilcClientEntry client_entry;
83 SilcClientEntry client_entry2;
84 SilcChannelEntry channel;
86 SilcIDCacheEntry id_cache = NULL;
90 payload = silc_notify_payload_parse(buffer);
94 type = silc_notify_get_type(payload);
95 args = silc_notify_get_args(payload);
100 case SILC_NOTIFY_TYPE_NONE:
101 /* Notify application */
102 client->ops->notify(client, conn, type,
103 silc_argument_get_arg_type(args, 1, NULL));
106 case SILC_NOTIFY_TYPE_INVITE:
108 * Someone invited me to a channel. Find Client and Channel entries
109 * for the application.
113 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
117 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
121 /* Get the channel entry */
123 if (silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
124 SILC_ID_CHANNEL, &id_cache))
125 channel = (SilcChannelEntry)id_cache->context;
127 /* Get sender Client ID */
128 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
132 client_id = silc_id_payload_parse_id(tmp, tmp_len);
136 /* Find Client entry and if not found query it */
137 client_entry = silc_client_get_client_by_id(client, conn, client_id);
139 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
143 /* Get the channel name */
144 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
148 /* Notify application */
149 client->ops->notify(client, conn, type, channel, tmp, client_entry);
152 case SILC_NOTIFY_TYPE_JOIN:
154 * Someone has joined to a channel. Get their ID and nickname and
155 * cache them for later use.
159 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
163 client_id = silc_id_payload_parse_id(tmp, tmp_len);
167 /* Find Client entry and if not found query it */
168 client_entry = silc_client_get_client_by_id(client, conn, client_id);
170 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
174 /* If nickname or username hasn't been resolved, do so */
175 if (!client_entry->nickname || !client_entry->username) {
176 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
181 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
185 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
189 /* Get channel entry */
190 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
191 SILC_ID_CHANNEL, &id_cache))
194 channel = (SilcChannelEntry)id_cache->context;
196 /* Add client to channel */
197 if (client_entry != conn->local_entry) {
198 chu = silc_calloc(1, sizeof(*chu));
199 chu->client = client_entry;
200 silc_list_add(channel->clients, chu);
203 /* XXX add support for multiple same nicks on same channel. Check
206 /* Notify application. The channel entry is sent last as this notify
207 is for channel but application don't know it from the arguments
209 client->ops->notify(client, conn, type, client_entry, channel);
212 case SILC_NOTIFY_TYPE_LEAVE:
214 * Someone has left a channel. We will remove it from the channel but
215 * we'll keep it in the cache in case we'll need it later.
219 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
223 client_id = silc_id_payload_parse_id(tmp, tmp_len);
227 /* Find Client entry */
229 silc_client_get_client_by_id(client, conn, client_id);
233 /* Get channel entry */
234 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
238 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
239 SILC_ID_CHANNEL, &id_cache))
242 channel = (SilcChannelEntry)id_cache->context;
244 /* Remove client from channel */
245 silc_list_start(channel->clients);
246 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
247 if (chu->client == client_entry) {
248 silc_list_del(channel->clients, chu);
254 /* Notify application. The channel entry is sent last as this notify
255 is for channel but application don't know it from the arguments
257 client->ops->notify(client, conn, type, client_entry, channel);
260 case SILC_NOTIFY_TYPE_SIGNOFF:
262 * Someone left SILC. We'll remove it from all channels and from cache.
266 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
270 client_id = silc_id_payload_parse_id(tmp, tmp_len);
274 /* Find Client entry */
276 silc_client_get_client_by_id(client, conn, client_id);
280 /* Remove from all channels */
281 silc_client_remove_from_channels(client, conn, client_entry);
283 /* Remove from cache */
284 silc_idcache_del_by_id(conn->client_cache, SILC_ID_CLIENT,
287 /* Get signoff message */
288 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
292 /* Notify application */
293 client->ops->notify(client, conn, type, client_entry, tmp);
296 if (client_entry->nickname)
297 silc_free(client_entry->nickname);
298 if (client_entry->server)
299 silc_free(client_entry->server);
300 if (client_entry->id)
301 silc_free(client_entry->id);
302 if (client_entry->send_key)
303 silc_cipher_free(client_entry->send_key);
304 if (client_entry->receive_key)
305 silc_cipher_free(client_entry->receive_key);
308 case SILC_NOTIFY_TYPE_TOPIC_SET:
310 * Someone set the topic on a channel.
314 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
318 client_id = silc_id_payload_parse_id(tmp, tmp_len);
322 /* Find Client entry */
324 silc_client_get_client_by_id(client, conn, client_id);
329 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
333 /* Get channel entry */
334 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
338 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
339 SILC_ID_CHANNEL, &id_cache))
342 channel = (SilcChannelEntry)id_cache->context;
344 /* Notify application. The channel entry is sent last as this notify
345 is for channel but application don't know it from the arguments
347 client->ops->notify(client, conn, type, client_entry, tmp, channel);
350 case SILC_NOTIFY_TYPE_NICK_CHANGE:
352 * Someone changed their nickname. If we don't have entry for the new
353 * ID we will query it and return here after it's done. After we've
354 * returned we fetch the old entry and free it and notify the
358 /* Get old Client ID */
359 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
363 client_id = silc_id_payload_parse_id(tmp, tmp_len);
368 if (!SILC_ID_CLIENT_COMPARE(client_id, conn->local_id))
371 /* Find old Client entry */
372 client_entry = silc_client_get_client_by_id(client, conn, client_id);
375 silc_free(client_id);
377 /* Get new Client ID */
378 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
382 client_id = silc_id_payload_parse_id(tmp, tmp_len);
386 /* Find Client entry and if not found resolve it */
387 client_entry2 = silc_client_get_client_by_id(client, conn, client_id);
388 if (!client_entry2) {
389 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
393 /* Remove the old from cache */
394 silc_idcache_del_by_id(conn->client_cache, SILC_ID_CLIENT,
397 /* Replace old ID entry with new one on all channels. */
398 silc_client_replace_from_channels(client, conn, client_entry,
401 /* Notify application */
402 client->ops->notify(client, conn, type, client_entry, client_entry2);
405 if (client_entry->nickname)
406 silc_free(client_entry->nickname);
407 if (client_entry->server)
408 silc_free(client_entry->server);
409 if (client_entry->id)
410 silc_free(client_entry->id);
411 if (client_entry->send_key)
412 silc_cipher_free(client_entry->send_key);
413 if (client_entry->receive_key)
414 silc_cipher_free(client_entry->receive_key);
415 silc_free(client_entry);
418 case SILC_NOTIFY_TYPE_CMODE_CHANGE:
420 * Someone changed a channel mode
424 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
428 idp = silc_id_payload_parse_data(tmp, tmp_len);
432 /* Find Client entry */
433 if (silc_id_payload_get_type(idp) == SILC_ID_CLIENT) {
434 client_id = silc_id_payload_parse_id(tmp, tmp_len);
436 silc_id_payload_free(idp);
440 client_entry = silc_client_get_client_by_id(client, conn, client_id);
442 silc_id_payload_free(idp);
449 silc_id_payload_free(idp);
452 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
456 SILC_GET32_MSB(mode, tmp);
458 /* Get channel entry */
459 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
463 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
464 SILC_ID_CHANNEL, &id_cache))
467 channel = (SilcChannelEntry)id_cache->context;
469 /* Save the new mode */
470 channel->mode = mode;
473 tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
475 unsigned char hash[32];
478 silc_hmac_free(channel->hmac);
479 if (!silc_hmac_alloc(tmp, NULL, &channel->hmac))
482 silc_hash_make(channel->hmac->hash, channel->key, channel->key_len / 8,
484 silc_hmac_set_key(channel->hmac, hash,
485 silc_hash_len(channel->hmac->hash));
486 memset(hash, 0, sizeof(hash));
489 /* Notify application. The channel entry is sent last as this notify
490 is for channel but application don't know it from the arguments
492 client->ops->notify(client, conn, type, client_entry, mode, NULL,
496 case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
498 * Someone changed user's mode on a channel
502 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
506 client_id = silc_id_payload_parse_id(tmp, tmp_len);
510 /* Find Client entry */
512 silc_client_get_client_by_id(client, conn, client_id);
517 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
521 SILC_GET32_MSB(mode, tmp);
523 /* Get target Client ID */
524 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
528 silc_free(client_id);
529 client_id = silc_id_payload_parse_id(tmp, tmp_len);
533 /* Find target Client entry */
535 silc_client_get_client_by_id(client, conn, client_id);
539 /* Get channel entry */
540 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
544 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
545 SILC_ID_CHANNEL, &id_cache))
548 channel = (SilcChannelEntry)id_cache->context;
551 silc_list_start(channel->clients);
552 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
553 if (chu->client == client_entry) {
559 /* Notify application. The channel entry is sent last as this notify
560 is for channel but application don't know it from the arguments
562 client->ops->notify(client, conn, type, client_entry, mode,
563 client_entry2, channel);
566 case SILC_NOTIFY_TYPE_MOTD:
568 * Received Message of the day
572 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
576 /* Notify application */
577 client->ops->notify(client, conn, type, tmp);
580 case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
582 * Router has enforced a new ID to a channel. Let's change the old
583 * ID to the one provided here.
587 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
590 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
594 /* Get the channel entry */
595 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
596 SILC_ID_CHANNEL, &id_cache))
599 channel = (SilcChannelEntry)id_cache->context;
601 /* Free the old ID */
602 silc_free(channel->id);
605 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
608 channel->id = silc_id_payload_parse_id(tmp, tmp_len);
612 id_cache->id = (void *)channel->id;
614 /* Notify application */
615 client->ops->notify(client, conn, type, channel, channel);
618 case SILC_NOTIFY_TYPE_KICKED:
620 * A client (maybe me) was kicked from a channel
624 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
628 client_id = silc_id_payload_parse_id(tmp, tmp_len);
632 /* Find Client entry */
633 client_entry = silc_client_get_client_by_id(client, conn, client_id);
637 /* Get channel entry */
638 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
642 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
643 SILC_ID_CHANNEL, &id_cache))
646 channel = (SilcChannelEntry)id_cache->context;
649 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
651 /* Notify application. The channel entry is sent last as this notify
652 is for channel but application don't know it from the arguments
654 client->ops->notify(client, conn, type, client_entry, tmp, channel);
656 /* If I was kicked from channel, remove the channel */
657 if (client_entry == conn->local_entry) {
658 if (conn->current_channel == channel)
659 conn->current_channel = NULL;
660 silc_idcache_del_by_id(conn->channel_cache,
661 SILC_ID_CHANNEL, channel->id);
662 silc_free(channel->channel_name);
663 silc_free(channel->id);
664 silc_free(channel->key);
665 silc_cipher_free(channel->channel_key);
670 case SILC_NOTIFY_TYPE_KILLED:
672 * A client (maybe me) was killed from the network.
676 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
680 client_id = silc_id_payload_parse_id(tmp, tmp_len);
684 /* Find Client entry */
685 client_entry = silc_client_get_client_by_id(client, conn, client_id);
690 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
692 /* Notify application. */
693 client->ops->notify(client, conn, type, client_entry, tmp);
695 if (client_entry != conn->local_entry) {
696 /* Remove client from all channels */
697 silc_client_remove_from_channels(client, conn, client_entry);
698 silc_idcache_del_by_id(conn->client_cache, SILC_ID_CLIENT,
700 if (client_entry->nickname)
701 silc_free(client_entry->nickname);
702 if (client_entry->server)
703 silc_free(client_entry->server);
704 if (client_entry->id)
705 silc_free(client_entry->id);
706 if (client_entry->send_key)
707 silc_cipher_free(client_entry->send_key);
708 if (client_entry->receive_key)
709 silc_cipher_free(client_entry->receive_key);
710 silc_free(client_entry);
715 case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
718 * A server quit the SILC network and some clients must be removed
719 * from channels as they quit as well.
721 SilcClientEntry *clients = NULL;
722 uint32 clients_count = 0;
725 for (i = 1; i < silc_argument_get_arg_num(args); i++) {
727 tmp = silc_argument_get_arg_type(args, i + 1, &tmp_len);
729 client_id = silc_id_payload_parse_id(tmp, tmp_len);
733 /* Get the client entry */
734 client_entry = silc_client_get_client_by_id(client, conn, client_id);
736 clients = silc_realloc(clients, sizeof(*clients) *
737 (clients_count + 1));
738 clients[clients_count] = client_entry;
741 silc_free(client_id);
746 /* Notify application. We don't keep server entries so the server
747 entry is returned as NULL. The client's are returned as array
748 of SilcClientEntry pointers. */
749 client->ops->notify(client, conn, type, NULL, clients, clients_count);
751 for (i = 0; i < clients_count; i++) {
752 /* Remove client from all channels */
753 client_entry = clients[i];
754 if (client_entry == conn->local_entry)
757 silc_client_remove_from_channels(client, conn, client_entry);
758 silc_idcache_del_by_id(conn->client_cache, SILC_ID_CLIENT,
760 if (client_entry->nickname)
761 silc_free(client_entry->nickname);
762 if (client_entry->server)
763 silc_free(client_entry->server);
764 if (client_entry->id)
765 silc_free(client_entry->id);
766 if (client_entry->send_key)
767 silc_cipher_free(client_entry->send_key);
768 if (client_entry->receive_key)
769 silc_cipher_free(client_entry->receive_key);
770 silc_free(client_entry);
782 silc_notify_payload_free(payload);
784 silc_free(client_id);
786 silc_free(channel_id);