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, void *context2)
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 SilcServerID *server_id = NULL;
83 SilcClientEntry client_entry;
84 SilcClientEntry client_entry2;
85 SilcChannelEntry channel;
87 SilcServerEntry server;
88 SilcIDCacheEntry id_cache = NULL;
92 payload = silc_notify_payload_parse(buffer);
96 type = silc_notify_get_type(payload);
97 args = silc_notify_get_args(payload);
102 case SILC_NOTIFY_TYPE_NONE:
103 /* Notify application */
104 client->ops->notify(client, conn, type,
105 silc_argument_get_arg_type(args, 1, NULL));
108 case SILC_NOTIFY_TYPE_INVITE:
110 * Someone invited me to a channel. Find Client and Channel entries
111 * for the application.
115 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
119 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
123 /* Get the channel entry */
125 if (silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
127 channel = (SilcChannelEntry)id_cache->context;
129 /* Get sender Client ID */
130 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
134 client_id = silc_id_payload_parse_id(tmp, tmp_len);
138 /* Find Client entry and if not found query it */
139 client_entry = silc_client_get_client_by_id(client, conn, client_id);
141 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
145 /* Get the channel name */
146 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
150 /* Notify application */
151 client->ops->notify(client, conn, type, channel, tmp, client_entry);
154 case SILC_NOTIFY_TYPE_JOIN:
156 * Someone has joined to a channel. Get their ID and nickname and
157 * cache them for later use.
161 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
165 client_id = silc_id_payload_parse_id(tmp, tmp_len);
169 /* Find Client entry and if not found query it */
170 client_entry = silc_client_get_client_by_id(client, conn, client_id);
172 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
176 /* If nickname or username hasn't been resolved, do so */
177 if (!client_entry->nickname || !client_entry->username) {
178 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
183 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
187 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
191 /* Get channel entry */
192 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
196 channel = (SilcChannelEntry)id_cache->context;
198 /* Add client to channel */
199 if (client_entry != conn->local_entry) {
200 chu = silc_calloc(1, sizeof(*chu));
201 chu->client = client_entry;
202 silc_list_add(channel->clients, chu);
205 /* XXX add support for multiple same nicks on same channel. Check
208 /* Notify application. The channel entry is sent last as this notify
209 is for channel but application don't know it from the arguments
211 client->ops->notify(client, conn, type, client_entry, channel);
214 case SILC_NOTIFY_TYPE_LEAVE:
216 * Someone has left a channel. We will remove it from the channel but
217 * we'll keep it in the cache in case we'll need it later.
221 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
225 client_id = silc_id_payload_parse_id(tmp, tmp_len);
229 /* Find Client entry */
231 silc_client_get_client_by_id(client, conn, client_id);
235 /* Get channel entry */
236 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
240 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
244 channel = (SilcChannelEntry)id_cache->context;
246 /* Remove client from channel */
247 silc_list_start(channel->clients);
248 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
249 if (chu->client == client_entry) {
250 silc_list_del(channel->clients, chu);
256 /* Notify application. The channel entry is sent last as this notify
257 is for channel but application don't know it from the arguments
259 client->ops->notify(client, conn, type, client_entry, channel);
262 case SILC_NOTIFY_TYPE_SIGNOFF:
264 * Someone left SILC. We'll remove it from all channels and from cache.
268 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
272 client_id = silc_id_payload_parse_id(tmp, tmp_len);
276 /* Find Client entry */
278 silc_client_get_client_by_id(client, conn, client_id);
282 /* Remove from all channels */
283 silc_client_remove_from_channels(client, conn, client_entry);
285 /* Remove from cache */
286 silc_idcache_del_by_context(conn->client_cache, client_entry);
288 /* Get signoff message */
289 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
293 /* Notify application */
294 client->ops->notify(client, conn, type, client_entry, tmp);
297 if (client_entry->nickname)
298 silc_free(client_entry->nickname);
299 if (client_entry->server)
300 silc_free(client_entry->server);
301 if (client_entry->id)
302 silc_free(client_entry->id);
303 if (client_entry->send_key)
304 silc_cipher_free(client_entry->send_key);
305 if (client_entry->receive_key)
306 silc_cipher_free(client_entry->receive_key);
309 case SILC_NOTIFY_TYPE_TOPIC_SET:
311 * Someone set the topic on a channel.
315 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
319 client_id = silc_id_payload_parse_id(tmp, tmp_len);
323 /* Find Client entry */
325 silc_client_get_client_by_id(client, conn, client_id);
330 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
334 /* Get channel entry */
335 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
339 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
343 channel = (SilcChannelEntry)id_cache->context;
345 /* Notify application. The channel entry is sent last as this notify
346 is for channel but application don't know it from the arguments
348 client->ops->notify(client, conn, type, client_entry, tmp, channel);
351 case SILC_NOTIFY_TYPE_NICK_CHANGE:
353 * Someone changed their nickname. If we don't have entry for the new
354 * ID we will query it and return here after it's done. After we've
355 * returned we fetch the old entry and free it and notify the
359 /* Get old Client ID */
360 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
364 client_id = silc_id_payload_parse_id(tmp, tmp_len);
369 if (SILC_ID_CLIENT_COMPARE(client_id, conn->local_id))
372 /* Find old Client entry */
373 client_entry = silc_client_get_client_by_id(client, conn, client_id);
376 silc_free(client_id);
378 /* Get new Client ID */
379 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
383 client_id = silc_id_payload_parse_id(tmp, tmp_len);
387 /* Find Client entry and if not found resolve it */
388 client_entry2 = silc_client_get_client_by_id(client, conn, client_id);
389 if (!client_entry2) {
390 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
394 /* Remove the old from cache */
395 silc_idcache_del_by_context(conn->client_cache, client_entry);
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);
446 server_id = silc_id_payload_parse_id(tmp, tmp_len);
448 silc_id_payload_free(idp);
452 server = silc_client_get_server_by_id(client, conn, server_id);
454 silc_id_payload_free(idp);
455 silc_free(server_id);
459 /* Save the pointer to the client_entry pointer */
460 client_entry = (SilcClientEntry)server;
461 silc_free(server_id);
465 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
467 silc_id_payload_free(idp);
471 SILC_GET32_MSB(mode, tmp);
473 /* Get channel entry */
474 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
477 silc_id_payload_free(idp);
480 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
482 silc_id_payload_free(idp);
486 channel = (SilcChannelEntry)id_cache->context;
488 /* Save the new mode */
489 channel->mode = mode;
492 tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
494 unsigned char hash[32];
497 silc_hmac_free(channel->hmac);
498 if (!silc_hmac_alloc(tmp, NULL, &channel->hmac))
501 silc_hash_make(channel->hmac->hash, channel->key, channel->key_len / 8,
503 silc_hmac_set_key(channel->hmac, hash,
504 silc_hash_len(channel->hmac->hash));
505 memset(hash, 0, sizeof(hash));
508 /* Notify application. The channel entry is sent last as this notify
509 is for channel but application don't know it from the arguments
511 client->ops->notify(client, conn, type, silc_id_payload_get_type(idp),
512 client_entry, mode, NULL, tmp, channel);
514 silc_id_payload_free(idp);
517 case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
519 * Someone changed user's mode on a channel
523 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
527 client_id = silc_id_payload_parse_id(tmp, tmp_len);
531 /* Find Client entry */
532 client_entry = silc_client_get_client_by_id(client, conn, client_id);
534 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
539 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
543 SILC_GET32_MSB(mode, tmp);
545 /* Get target Client ID */
546 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
550 silc_free(client_id);
551 client_id = silc_id_payload_parse_id(tmp, tmp_len);
555 /* Find target Client entry */
557 silc_client_get_client_by_id(client, conn, client_id);
561 /* Get channel entry */
562 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
566 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
570 channel = (SilcChannelEntry)id_cache->context;
573 silc_list_start(channel->clients);
574 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
575 if (chu->client == client_entry) {
581 /* Notify application. The channel entry is sent last as this notify
582 is for channel but application don't know it from the arguments
584 client->ops->notify(client, conn, type, client_entry, mode,
585 client_entry2, channel);
588 case SILC_NOTIFY_TYPE_MOTD:
590 * Received Message of the day
594 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
598 /* Notify application */
599 client->ops->notify(client, conn, type, tmp);
602 case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
604 * Router has enforced a new ID to a channel. Let's change the old
605 * ID to the one provided here.
609 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
612 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
616 /* Get the channel entry */
617 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
621 channel = (SilcChannelEntry)id_cache->context;
623 SILC_LOG_DEBUG(("Old Channel ID id(%s)",
624 silc_id_render(channel->id, SILC_ID_CHANNEL)));
626 /* Free the old ID */
627 silc_free(channel->id);
630 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
633 channel->id = silc_id_payload_parse_id(tmp, tmp_len);
637 SILC_LOG_DEBUG(("New Channel ID id(%s)",
638 silc_id_render(channel->id, SILC_ID_CHANNEL)));
640 /* Remove the old cache entry and create a new one */
641 silc_idcache_del_by_context(conn->channel_cache, channel);
642 silc_idcache_add(conn->channel_cache, channel->channel_name,
643 channel->id, channel, FALSE);
645 /* Notify application */
646 client->ops->notify(client, conn, type, channel, channel);
649 case SILC_NOTIFY_TYPE_KICKED:
651 * A client (maybe me) was kicked from a channel
655 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
659 client_id = silc_id_payload_parse_id(tmp, tmp_len);
663 /* Find Client entry */
664 client_entry = silc_client_get_client_by_id(client, conn, client_id);
668 /* Get channel entry */
669 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
673 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
677 channel = (SilcChannelEntry)id_cache->context;
680 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
682 /* Notify application. The channel entry is sent last as this notify
683 is for channel but application don't know it from the arguments
685 client->ops->notify(client, conn, type, client_entry, tmp, channel);
687 /* If I was kicked from channel, remove the channel */
688 if (client_entry == conn->local_entry) {
689 if (conn->current_channel == channel)
690 conn->current_channel = NULL;
691 silc_idcache_del_by_id(conn->channel_cache, channel->id);
692 silc_free(channel->channel_name);
693 silc_free(channel->id);
694 silc_free(channel->key);
695 silc_cipher_free(channel->channel_key);
700 case SILC_NOTIFY_TYPE_KILLED:
702 * A client (maybe me) was killed from the network.
706 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
710 client_id = silc_id_payload_parse_id(tmp, tmp_len);
714 /* Find Client entry */
715 client_entry = silc_client_get_client_by_id(client, conn, client_id);
720 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
722 /* Notify application. */
723 client->ops->notify(client, conn, type, client_entry, tmp);
725 if (client_entry != conn->local_entry) {
726 /* Remove client from all channels */
727 silc_client_remove_from_channels(client, conn, client_entry);
728 silc_idcache_del_by_context(conn->client_cache, client_entry);
729 if (client_entry->nickname)
730 silc_free(client_entry->nickname);
731 if (client_entry->server)
732 silc_free(client_entry->server);
733 if (client_entry->id)
734 silc_free(client_entry->id);
735 if (client_entry->send_key)
736 silc_cipher_free(client_entry->send_key);
737 if (client_entry->receive_key)
738 silc_cipher_free(client_entry->receive_key);
739 silc_free(client_entry);
744 case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
747 * A server quit the SILC network and some clients must be removed
748 * from channels as they quit as well.
750 SilcClientEntry *clients = NULL;
751 uint32 clients_count = 0;
754 for (i = 1; i < silc_argument_get_arg_num(args); i++) {
756 tmp = silc_argument_get_arg_type(args, i + 1, &tmp_len);
758 client_id = silc_id_payload_parse_id(tmp, tmp_len);
762 /* Get the client entry */
763 client_entry = silc_client_get_client_by_id(client, conn, client_id);
765 clients = silc_realloc(clients, sizeof(*clients) *
766 (clients_count + 1));
767 clients[clients_count] = client_entry;
770 silc_free(client_id);
775 /* Notify application. We don't keep server entries so the server
776 entry is returned as NULL. The client's are returned as array
777 of SilcClientEntry pointers. */
778 client->ops->notify(client, conn, type, NULL, clients, clients_count);
780 for (i = 0; i < clients_count; i++) {
781 /* Remove client from all channels */
782 client_entry = clients[i];
783 if (client_entry == conn->local_entry)
786 silc_client_remove_from_channels(client, conn, client_entry);
787 silc_idcache_del_by_context(conn->client_cache, client_entry);
788 if (client_entry->nickname)
789 silc_free(client_entry->nickname);
790 if (client_entry->server)
791 silc_free(client_entry->server);
792 if (client_entry->id)
793 silc_free(client_entry->id);
794 if (client_entry->send_key)
795 silc_cipher_free(client_entry->send_key);
796 if (client_entry->receive_key)
797 silc_cipher_free(client_entry->receive_key);
798 silc_free(client_entry);
810 silc_notify_payload_free(payload);
812 silc_free(client_id);
814 silc_free(channel_id);