5 Author: Pekka Riikonen <priikone@silcnet.org>
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"
29 SilcPacketContext *packet;
31 SilcSocketConnection sock;
32 } *SilcClientNotifyResolve;
34 /* Called when notify is received and some async operation (such as command)
35 is required before processing the notify message. This calls again the
36 silc_client_notify_by_server and reprocesses the original notify packet. */
38 static void silc_client_notify_by_server_pending(void *context, void *context2)
40 SilcClientNotifyResolve res = (SilcClientNotifyResolve)context;
41 SilcClientCommandReplyContext reply =
42 (SilcClientCommandReplyContext)context2;
44 SILC_LOG_DEBUG(("Start"));
47 SilcCommandStatus status;
48 unsigned char *tmp = silc_argument_get_arg_type(reply->args, 1, NULL);
49 SILC_GET16_MSB(status, tmp);
50 if (status != SILC_STATUS_OK) {
51 silc_socket_free(res->sock);
56 silc_client_notify_by_server(res->context, res->sock, res->packet);
57 silc_socket_free(res->sock);
60 /* Destructor for the pending command callback */
62 static void silc_client_notify_by_server_destructor(void *context)
64 SilcClientNotifyResolve res = (SilcClientNotifyResolve)context;
65 silc_packet_context_free(res->packet);
69 /* Resolve client information from server by Client ID. */
71 static void silc_client_notify_by_server_resolve(SilcClient client,
72 SilcClientConnection conn,
73 SilcPacketContext *packet,
74 SilcClientID *client_id)
76 SilcClientNotifyResolve res = silc_calloc(1, sizeof(*res));
77 SilcBuffer idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
79 res->packet = silc_packet_context_dup(packet);
80 res->context = client;
81 res->sock = silc_socket_dup(conn->sock);
83 silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
84 silc_client_command_reply_whois_i, 0,
86 silc_client_command_send(client, conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
87 1, 3, idp->data, idp->len);
88 silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
89 silc_client_notify_by_server_destructor,
90 silc_client_notify_by_server_pending, res);
91 silc_buffer_free(idp);
94 /* Received notify message from server */
96 void silc_client_notify_by_server(SilcClient client,
97 SilcSocketConnection sock,
98 SilcPacketContext *packet)
100 SilcBuffer buffer = packet->buffer;
101 SilcClientConnection conn = (SilcClientConnection)sock->user_data;
102 SilcNotifyPayload payload;
104 SilcArgumentPayload args;
107 SilcClientID *client_id = NULL;
108 SilcChannelID *channel_id = NULL;
109 SilcServerID *server_id = NULL;
110 SilcClientEntry client_entry;
111 SilcClientEntry client_entry2;
112 SilcChannelEntry channel;
114 SilcServerEntry server;
115 SilcIDCacheEntry id_cache = NULL;
117 uint32 tmp_len, mode;
119 SILC_LOG_DEBUG(("Start"));
121 payload = silc_notify_payload_parse(buffer->data, buffer->len);
125 type = silc_notify_get_type(payload);
126 args = silc_notify_get_args(payload);
131 case SILC_NOTIFY_TYPE_NONE:
132 /* Notify application */
133 client->internal->ops->notify(client, conn, type,
134 silc_argument_get_arg_type(args, 1, NULL));
137 case SILC_NOTIFY_TYPE_INVITE:
139 * Someone invited me to a channel. Find Client and Channel entries
140 * for the application.
143 SILC_LOG_DEBUG(("Notify: INVITE"));
146 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
150 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
154 /* Get the channel entry */
156 if (silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
158 channel = (SilcChannelEntry)id_cache->context;
160 /* Get sender Client ID */
161 tmp = silc_argument_get_arg_type(args, 3, &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 /* Get the channel name */
177 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
181 /* Notify application */
182 client->internal->ops->notify(client, conn, type, channel, tmp,
186 case SILC_NOTIFY_TYPE_JOIN:
188 * Someone has joined to a channel. Get their ID and nickname and
189 * cache them for later use.
192 SILC_LOG_DEBUG(("Notify: JOIN"));
195 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
199 client_id = silc_id_payload_parse_id(tmp, tmp_len);
203 /* Find Client entry and if not found query it */
204 client_entry = silc_client_get_client_by_id(client, conn, client_id);
206 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
210 /* If nickname or username hasn't been resolved, do so */
211 if (!client_entry->nickname || !client_entry->username) {
212 if (client_entry->status & SILC_CLIENT_STATUS_RESOLVING) {
213 client_entry->status &= ~SILC_CLIENT_STATUS_RESOLVING;
216 client_entry->status |= SILC_CLIENT_STATUS_RESOLVING;
217 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
220 if (client_entry != conn->local_entry)
221 silc_client_nickname_format(client, conn, client_entry);
225 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
229 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
233 /* Get channel entry */
234 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
238 channel = (SilcChannelEntry)id_cache->context;
240 /* Add client to channel */
241 if (client_entry != conn->local_entry) {
242 chu = silc_calloc(1, sizeof(*chu));
243 chu->client = client_entry;
244 silc_list_add(channel->clients, chu);
247 /* Notify application. The channel entry is sent last as this notify
248 is for channel but application don't know it from the arguments
250 client->internal->ops->notify(client, conn, type, client_entry, channel);
253 case SILC_NOTIFY_TYPE_LEAVE:
255 * Someone has left a channel. We will remove it from the channel but
256 * we'll keep it in the cache in case we'll need it later.
259 SILC_LOG_DEBUG(("Notify: LEAVE"));
262 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
266 client_id = silc_id_payload_parse_id(tmp, tmp_len);
270 /* Find Client entry */
272 silc_client_get_client_by_id(client, conn, client_id);
276 /* Get channel entry */
277 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
281 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
285 channel = (SilcChannelEntry)id_cache->context;
287 /* Remove client from channel */
288 silc_list_start(channel->clients);
289 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
290 if (chu->client == client_entry) {
291 silc_list_del(channel->clients, chu);
297 /* Notify application. The channel entry is sent last as this notify
298 is for channel but application don't know it from the arguments
300 client->internal->ops->notify(client, conn, type, client_entry, channel);
303 case SILC_NOTIFY_TYPE_SIGNOFF:
305 * Someone left SILC. We'll remove it from all channels and from cache.
308 SILC_LOG_DEBUG(("Notify: SIGNOFF"));
311 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
315 client_id = silc_id_payload_parse_id(tmp, tmp_len);
319 /* Find Client entry */
321 silc_client_get_client_by_id(client, conn, client_id);
325 /* Remove from all channels */
326 silc_client_remove_from_channels(client, conn, client_entry);
328 /* Remove from cache */
329 silc_idcache_del_by_context(conn->client_cache, client_entry);
331 /* Get signoff message */
332 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
336 /* Notify application */
337 client->internal->ops->notify(client, conn, type, client_entry, tmp);
340 silc_client_del_client_entry(client, conn, client_entry);
343 case SILC_NOTIFY_TYPE_TOPIC_SET:
345 * Someone set the topic on a channel.
348 SILC_LOG_DEBUG(("Notify: TOPIC_SET"));
351 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
355 idp = silc_id_payload_parse(tmp, tmp_len);
359 /* Find Client entry */
360 if (silc_id_payload_get_type(idp) == SILC_ID_CLIENT) {
361 client_id = silc_id_payload_parse_id(tmp, tmp_len);
363 silc_id_payload_free(idp);
367 /* Find Client entry */
369 silc_client_get_client_by_id(client, conn, client_id);
372 } else if (silc_id_payload_get_type(idp) == SILC_ID_SERVER) {
373 server_id = silc_id_payload_parse_id(tmp, tmp_len);
375 silc_id_payload_free(idp);
379 server = silc_client_get_server_by_id(client, conn, server_id);
381 silc_id_payload_free(idp);
382 silc_free(server_id);
386 /* Save the pointer to the client_entry pointer */
387 client_entry = (SilcClientEntry)server;
388 silc_free(server_id);
390 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
392 silc_id_payload_free(idp);
396 channel = silc_client_get_channel_by_id(client, conn, channel_id);
398 silc_id_payload_free(idp);
399 silc_free(channel_id);
403 /* Save the pointer to the client_entry pointer */
404 client_entry = (SilcClientEntry)channel;
405 silc_free(channel_id);
409 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
413 /* Get channel entry */
414 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
418 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
422 channel = (SilcChannelEntry)id_cache->context;
424 /* Notify application. The channel entry is sent last as this notify
425 is for channel but application don't know it from the arguments
427 client->internal->ops->notify(client, conn, type,
428 silc_id_payload_get_type(idp),
429 client_entry, tmp, channel);
431 silc_id_payload_free(idp);
434 case SILC_NOTIFY_TYPE_NICK_CHANGE:
436 * Someone changed their nickname. If we don't have entry for the new
437 * ID we will query it and return here after it's done. After we've
438 * returned we fetch the old entry and free it and notify the
442 SILC_LOG_DEBUG(("Notify: NICK_CHANGE"));
444 /* Get old Client ID */
445 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
449 client_id = silc_id_payload_parse_id(tmp, tmp_len);
454 if (SILC_ID_CLIENT_COMPARE(client_id, conn->local_id))
457 /* Find old Client entry */
458 client_entry = silc_client_get_client_by_id(client, conn, client_id);
461 silc_free(client_id);
463 client_entry->valid = FALSE;
465 /* Get new Client ID */
466 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
470 client_id = silc_id_payload_parse_id(tmp, tmp_len);
474 /* Find Client entry and if not found resolve it */
475 client_entry2 = silc_client_get_client_by_id(client, conn, client_id);
476 if (!client_entry2) {
477 /* Resolve the entry information */
478 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
480 /* Add the new entry even though we resolved it. This is because we
481 want to replace the old entry with the new entry here right now. */
483 silc_client_add_client(client, conn, NULL, NULL, NULL,
484 silc_id_dup(client_id, SILC_ID_CLIENT),
487 /* Replace old ID entry with new one on all channels. */
488 silc_client_replace_from_channels(client, conn, client_entry,
491 if (client_entry2 != conn->local_entry)
492 silc_client_nickname_format(client, conn, client_entry2);
494 /* Remove the old from cache */
495 silc_idcache_del_by_context(conn->client_cache, client_entry);
497 /* Replace old ID entry with new one on all channels. */
498 silc_client_replace_from_channels(client, conn, client_entry,
501 /* Notify application */
502 client->internal->ops->notify(client, conn, type,
503 client_entry, client_entry2);
506 silc_client_del_client_entry(client, conn, client_entry);
510 case SILC_NOTIFY_TYPE_CMODE_CHANGE:
512 * Someone changed a channel mode
515 SILC_LOG_DEBUG(("Notify: CMODE_CHANGE"));
518 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
522 idp = silc_id_payload_parse(tmp, tmp_len);
526 /* Find Client entry */
527 if (silc_id_payload_get_type(idp) == SILC_ID_CLIENT) {
528 client_id = silc_id_payload_parse_id(tmp, tmp_len);
530 silc_id_payload_free(idp);
534 client_entry = silc_client_get_client_by_id(client, conn, client_id);
536 silc_id_payload_free(idp);
540 server_id = silc_id_payload_parse_id(tmp, tmp_len);
542 silc_id_payload_free(idp);
546 server = silc_client_get_server_by_id(client, conn, server_id);
548 silc_id_payload_free(idp);
549 silc_free(server_id);
553 /* Save the pointer to the client_entry pointer */
554 client_entry = (SilcClientEntry)server;
555 silc_free(server_id);
559 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
561 silc_id_payload_free(idp);
565 SILC_GET32_MSB(mode, tmp);
567 /* Get channel entry */
568 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
571 silc_id_payload_free(idp);
574 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
576 silc_id_payload_free(idp);
580 channel = (SilcChannelEntry)id_cache->context;
582 /* Save the new mode */
583 channel->mode = mode;
586 tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
588 unsigned char hash[32];
591 silc_hmac_free(channel->hmac);
592 if (!silc_hmac_alloc(tmp, NULL, &channel->hmac))
595 silc_hash_make(silc_hmac_get_hash(channel->hmac),
596 channel->key, channel->key_len / 8,
598 silc_hmac_set_key(channel->hmac, hash,
599 silc_hash_len(silc_hmac_get_hash(channel->hmac)));
600 memset(hash, 0, sizeof(hash));
603 /* Notify application. The channel entry is sent last as this notify
604 is for channel but application don't know it from the arguments
606 client->internal->ops->notify(client, conn, type,
607 silc_id_payload_get_type(idp),
608 client_entry, mode, NULL, tmp, channel);
610 silc_id_payload_free(idp);
613 case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
615 * Someone changed user's mode on a channel
618 SILC_LOG_DEBUG(("Notify: CUMODE_CHANGE"));
621 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
625 client_id = silc_id_payload_parse_id(tmp, tmp_len);
629 /* Find Client entry */
630 client_entry = silc_client_get_client_by_id(client, conn, client_id);
632 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
637 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
641 SILC_GET32_MSB(mode, tmp);
643 /* Get target Client ID */
644 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
648 silc_free(client_id);
649 client_id = silc_id_payload_parse_id(tmp, tmp_len);
653 /* Find target Client entry */
655 silc_client_get_client_by_id(client, conn, client_id);
659 /* Get channel entry */
660 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
664 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
668 channel = (SilcChannelEntry)id_cache->context;
671 silc_list_start(channel->clients);
672 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
673 if (chu->client == client_entry) {
679 /* Notify application. The channel entry is sent last as this notify
680 is for channel but application don't know it from the arguments
682 client->internal->ops->notify(client, conn, type,
684 client_entry2, channel);
687 case SILC_NOTIFY_TYPE_MOTD:
689 * Received Message of the day
692 SILC_LOG_DEBUG(("Notify: MOTD"));
695 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
699 /* Notify application */
700 client->internal->ops->notify(client, conn, type, tmp);
703 case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
705 * Router has enforced a new ID to a channel. Let's change the old
706 * ID to the one provided here.
709 SILC_LOG_DEBUG(("Notify: CHANNEL_CHANGE"));
712 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
715 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
719 /* Get the channel entry */
720 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
724 channel = (SilcChannelEntry)id_cache->context;
726 SILC_LOG_DEBUG(("Old Channel ID id(%s)",
727 silc_id_render(channel->id, SILC_ID_CHANNEL)));
729 /* Remove the old channel entry */
730 silc_idcache_del_by_context(conn->channel_cache, channel);
732 /* Free the old ID */
733 silc_free(channel->id);
736 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
739 channel->id = silc_id_payload_parse_id(tmp, tmp_len);
743 SILC_LOG_DEBUG(("New Channel ID id(%s)",
744 silc_id_render(channel->id, SILC_ID_CHANNEL)));
746 /* Add the channel entry again to ID cache */
747 silc_idcache_add(conn->channel_cache, channel->channel_name,
748 channel->id, channel, 0, NULL);
750 /* Notify application */
751 client->internal->ops->notify(client, conn, type, channel, channel);
754 case SILC_NOTIFY_TYPE_KICKED:
756 * A client (maybe me) was kicked from a channel
759 SILC_LOG_DEBUG(("Notify: KICKED"));
762 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
766 client_id = silc_id_payload_parse_id(tmp, tmp_len);
770 /* Find Client entry */
771 client_entry = silc_client_get_client_by_id(client, conn, client_id);
775 /* Get channel entry */
776 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
780 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
784 channel = (SilcChannelEntry)id_cache->context;
787 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
791 client_id = silc_id_payload_parse_id(tmp, tmp_len);
795 /* Find kicker's client entry and if not found resolve it */
796 client_entry2 = silc_client_get_client_by_id(client, conn, client_id);
797 if (!client_entry2) {
798 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
801 if (client_entry2 != conn->local_entry)
802 silc_client_nickname_format(client, conn, client_entry2);
806 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
808 /* Notify application. The channel entry is sent last as this notify
809 is for channel but application don't know it from the arguments
811 client->internal->ops->notify(client, conn, type, client_entry, tmp,
812 client_entry2, channel);
814 /* If I was kicked from channel, remove the channel */
815 if (client_entry == conn->local_entry) {
816 if (conn->current_channel == channel)
817 conn->current_channel = NULL;
818 silc_idcache_del_by_id(conn->channel_cache, channel->id);
819 silc_free(channel->channel_name);
820 silc_free(channel->id);
821 silc_free(channel->key);
822 silc_cipher_free(channel->channel_key);
827 case SILC_NOTIFY_TYPE_KILLED:
829 * A client (maybe me) was killed from the network.
832 SILC_LOG_DEBUG(("Notify: KILLED"));
835 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
839 client_id = silc_id_payload_parse_id(tmp, tmp_len);
843 /* Find Client entry */
844 client_entry = silc_client_get_client_by_id(client, conn, client_id);
849 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
851 /* Notify application. */
852 client->internal->ops->notify(client, conn, type, client_entry, tmp);
854 if (client_entry != conn->local_entry) {
855 /* Remove client from all channels */
856 silc_client_remove_from_channels(client, conn, client_entry);
857 silc_client_del_client(client, conn, client_entry);
862 case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
865 * A server quit the SILC network and some clients must be removed
866 * from channels as they quit as well.
868 SilcClientEntry *clients = NULL;
869 uint32 clients_count = 0;
872 SILC_LOG_DEBUG(("Notify: SIGNOFF"));
874 for (i = 1; i < silc_argument_get_arg_num(args); i++) {
876 tmp = silc_argument_get_arg_type(args, i + 1, &tmp_len);
878 client_id = silc_id_payload_parse_id(tmp, tmp_len);
882 /* Get the client entry */
883 client_entry = silc_client_get_client_by_id(client, conn, client_id);
885 clients = silc_realloc(clients, sizeof(*clients) *
886 (clients_count + 1));
887 clients[clients_count] = client_entry;
890 silc_free(client_id);
895 /* Notify application. We don't keep server entries so the server
896 entry is returned as NULL. The client's are returned as array
897 of SilcClientEntry pointers. */
898 client->internal->ops->notify(client, conn, type, NULL,
899 clients, clients_count);
901 for (i = 0; i < clients_count; i++) {
902 /* Remove client from all channels */
903 client_entry = clients[i];
904 if (client_entry == conn->local_entry)
907 silc_client_remove_from_channels(client, conn, client_entry);
908 silc_client_del_client(client, conn, client_entry);
920 silc_notify_payload_free(payload);
921 silc_free(client_id);
922 silc_free(channel_id);