5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 1997 - 2002 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; version 2 of the License.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
20 /* This file includes the Notify packet handling. Notify packets are
21 important packets sent by the server. They tell different things to the
22 client such as nick changes, mode changes etc. */
24 #include "silcincludes.h"
25 #include "silcclient.h"
26 #include "client_internal.h"
28 /* Context used for resolving client, channel and server info. */
32 SilcSocketConnection sock;
33 } *SilcClientNotifyResolve;
35 SILC_TASK_CALLBACK(silc_client_notify_check_client)
37 SilcClientNotifyResolve res = (SilcClientNotifyResolve)context;
38 SilcClientConnection conn = res->context;
39 SilcClient client = conn->client;
40 SilcClientID *client_id = res->packet;
41 silc_client_get_client_by_id_resolve(client, conn, client_id, NULL, NULL);
46 /* Called when notify is received and some async operation (such as command)
47 is required before processing the notify message. This calls again the
48 silc_client_notify_by_server and reprocesses the original notify packet. */
50 static void silc_client_notify_by_server_pending(void *context, void *context2)
52 SilcClientNotifyResolve res = (SilcClientNotifyResolve)context;
53 SilcClientCommandReplyContext reply =
54 (SilcClientCommandReplyContext)context2;
56 SILC_LOG_DEBUG(("Start"));
59 SilcCommandStatus status = silc_command_get_status(reply->payload);
60 if (status != SILC_STATUS_OK)
64 silc_client_notify_by_server(res->context, res->sock, res->packet);
67 silc_socket_free(res->sock);
68 silc_packet_context_free(res->packet);
72 /* Resolve client, channel or server information. */
74 static void silc_client_notify_by_server_resolve(SilcClient client,
75 SilcClientConnection conn,
76 SilcPacketContext *packet,
80 SilcClientNotifyResolve res = silc_calloc(1, sizeof(*res));
81 SilcBuffer idp = silc_id_payload_encode(id, id_type);
83 res->packet = silc_packet_context_dup(packet);
84 res->context = client;
85 res->sock = silc_socket_dup(conn->sock);
87 /* For client resolving use WHOIS, and otherwise use IDENTIFY */
88 if (id_type == SILC_ID_CLIENT) {
89 silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
90 silc_client_command_reply_whois_i, 0,
92 silc_client_command_send(client, conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
93 1, 3, idp->data, idp->len);
94 silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
95 silc_client_notify_by_server_pending, res);
97 silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
98 silc_client_command_reply_identify_i, 0,
100 silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
101 conn->cmd_ident, 1, 5, idp->data, idp->len);
102 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
103 silc_client_notify_by_server_pending, res);
105 silc_buffer_free(idp);
108 /* Received notify message from server */
110 void silc_client_notify_by_server(SilcClient client,
111 SilcSocketConnection sock,
112 SilcPacketContext *packet)
114 SilcBuffer buffer = packet->buffer;
115 SilcClientConnection conn = (SilcClientConnection)sock->user_data;
116 SilcNotifyPayload payload;
118 SilcArgumentPayload args;
122 SilcClientID *client_id = NULL;
123 SilcChannelID *channel_id = NULL;
124 SilcServerID *server_id = NULL;
125 SilcClientEntry client_entry = NULL;
126 SilcClientEntry client_entry2 = NULL;
127 SilcChannelEntry channel;
129 SilcServerEntry server;
131 SilcUInt32 tmp_len, mode;
133 SILC_LOG_DEBUG(("Start"));
135 payload = silc_notify_payload_parse(buffer->data, buffer->len);
139 type = silc_notify_get_type(payload);
140 args = silc_notify_get_args(payload);
145 case SILC_NOTIFY_TYPE_NONE:
146 /* Notify application */
147 client->internal->ops->notify(client, conn, type,
148 silc_argument_get_arg_type(args, 1, NULL));
151 case SILC_NOTIFY_TYPE_INVITE:
153 * Someone invited me to a channel. Find Client and Channel entries
154 * for the application.
157 SILC_LOG_DEBUG(("Notify: INVITE"));
160 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
164 channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
168 /* Get the channel entry */
169 channel = silc_client_get_channel_by_id(client, conn, channel_id);
171 /* Get sender Client ID */
172 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
176 client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
180 /* Find Client entry and if not found query it */
181 client_entry = silc_client_get_client_by_id(client, conn, client_id);
183 silc_client_notify_by_server_resolve(client, conn, packet,
184 SILC_ID_CLIENT, client_id);
188 /* Get the channel name */
189 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
193 /* Notify application */
194 client->internal->ops->notify(client, conn, type, channel, tmp,
198 case SILC_NOTIFY_TYPE_JOIN:
200 * Someone has joined to a channel. Get their ID and nickname and
201 * cache them for later use.
204 SILC_LOG_DEBUG(("Notify: JOIN"));
207 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
211 client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
215 /* Find Client entry and if not found query it */
216 client_entry = silc_client_get_client_by_id(client, conn, client_id);
218 silc_client_notify_by_server_resolve(client, conn, packet,
219 SILC_ID_CLIENT, client_id);
223 /* If nickname or username hasn't been resolved, do so */
224 if (!client_entry->nickname || !client_entry->username) {
225 if (client_entry->status & SILC_CLIENT_STATUS_RESOLVING) {
226 client_entry->status &= ~SILC_CLIENT_STATUS_RESOLVING;
229 client_entry->status |= SILC_CLIENT_STATUS_RESOLVING;
230 silc_client_notify_by_server_resolve(client, conn, packet,
231 SILC_ID_CLIENT, client_id);
234 if (client_entry != conn->local_entry)
235 silc_client_nickname_format(client, conn, client_entry);
239 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
243 channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
247 /* Get channel entry */
248 channel = silc_client_get_channel_by_id(client, conn, channel_id);
252 /* Join the client to channel */
253 if (!silc_client_on_channel(channel, client_entry)) {
254 chu = silc_calloc(1, sizeof(*chu));
255 chu->client = client_entry;
256 chu->channel = channel;
257 silc_hash_table_add(channel->user_list, client_entry, chu);
258 silc_hash_table_add(client_entry->channels, channel, chu);
261 /* Notify application. The channel entry is sent last as this notify
262 is for channel but application don't know it from the arguments
264 client->internal->ops->notify(client, conn, type, client_entry, channel);
267 case SILC_NOTIFY_TYPE_LEAVE:
269 * Someone has left a channel. We will remove it from the channel but
270 * we'll keep it in the cache in case we'll need it later.
273 SILC_LOG_DEBUG(("Notify: LEAVE"));
276 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
280 client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
284 /* Find Client entry */
286 silc_client_get_client_by_id(client, conn, client_id);
290 /* Get channel entry */
291 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
295 channel = silc_client_get_channel_by_id(client, conn, channel_id);
299 /* Remove client from channel */
300 chu = silc_client_on_channel(channel, client_entry);
302 silc_hash_table_del(client_entry->channels, channel);
303 silc_hash_table_del(channel->user_list, client_entry);
307 /* Some client implementations actually quit network by first doing
308 LEAVE and then immediately SIGNOFF. We'll check for this by doing
309 check for the client after 15 seconds. If it is not valid after
310 that we'll remove the client from cache. */
311 if (!silc_hash_table_count(client_entry->channels)) {
312 SilcClientNotifyResolve res = silc_calloc(1, sizeof(*res));
314 res->packet = silc_id_dup(client_id, SILC_ID_CLIENT);
315 silc_schedule_task_add(client->schedule, 0,
316 silc_client_notify_check_client, conn,
317 15, 0, SILC_TASK_TIMEOUT,
318 SILC_TASK_PRI_NORMAL);
321 /* Notify application. The channel entry is sent last as this notify
322 is for channel but application don't know it from the arguments
324 client->internal->ops->notify(client, conn, type, client_entry, channel);
327 case SILC_NOTIFY_TYPE_SIGNOFF:
329 * Someone left SILC. We'll remove it from all channels and from cache.
332 SILC_LOG_DEBUG(("Notify: SIGNOFF"));
335 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
339 client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
343 /* Find Client entry */
345 silc_client_get_client_by_id(client, conn, client_id);
349 /* Remove from all channels */
350 silc_client_remove_from_channels(client, conn, client_entry);
352 /* Remove from cache */
353 silc_idcache_del_by_context(conn->client_cache, client_entry);
355 /* Get signoff message */
356 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
360 /* Notify application */
361 client->internal->ops->notify(client, conn, type, client_entry, tmp);
364 silc_client_del_client_entry(client, conn, client_entry);
367 case SILC_NOTIFY_TYPE_TOPIC_SET:
369 * Someone set the topic on a channel.
372 SILC_LOG_DEBUG(("Notify: TOPIC_SET"));
375 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
378 id = silc_id_payload_parse_id(tmp, tmp_len, &id_type);
382 /* Find Client entry */
383 if (id_type == SILC_ID_CLIENT) {
384 /* Find Client entry */
386 client_entry = silc_client_get_client_by_id(client, conn, client_id);
388 silc_client_notify_by_server_resolve(client, conn, packet,
389 SILC_ID_CLIENT, client_id);
392 } else if (id_type == SILC_ID_SERVER) {
393 /* Find Server entry */
395 server = silc_client_get_server_by_id(client, conn, server_id);
397 silc_client_notify_by_server_resolve(client, conn, packet,
398 SILC_ID_SERVER, server_id);
402 /* Save the pointer to the client_entry pointer */
403 client_entry = (SilcClientEntry)server;
405 /* Find Channel entry */
407 channel = silc_client_get_channel_by_id(client, conn, channel_id);
409 silc_client_notify_by_server_resolve(client, conn, packet,
410 SILC_ID_CHANNEL, channel_id);
414 /* Save the pointer to the client_entry pointer */
415 client_entry = (SilcClientEntry)channel;
416 silc_free(channel_id);
421 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
425 /* Get channel entry */
426 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
430 channel = silc_client_get_channel_by_id(client, conn, channel_id);
434 /* Notify application. The channel entry is sent last as this notify
435 is for channel but application don't know it from the arguments
437 client->internal->ops->notify(client, conn, type, id_type,
438 client_entry, tmp, channel);
442 case SILC_NOTIFY_TYPE_NICK_CHANGE:
444 * Someone changed their nickname. If we don't have entry for the new
445 * ID we will query it and return here after it's done. After we've
446 * returned we fetch the old entry and free it and notify the
450 SILC_LOG_DEBUG(("Notify: NICK_CHANGE"));
452 /* Get old Client ID */
453 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
457 client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
462 if (SILC_ID_CLIENT_COMPARE(client_id, conn->local_id))
465 /* Find old Client entry */
466 client_entry = silc_client_get_client_by_id(client, conn, client_id);
469 silc_free(client_id);
471 client_entry->valid = FALSE;
473 /* Get new Client ID */
474 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
478 client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
482 /* Find Client entry and if not found resolve it */
483 client_entry2 = silc_client_get_client_by_id(client, conn, client_id);
484 if (!client_entry2) {
485 /* Resolve the entry information */
486 silc_client_notify_by_server_resolve(client, conn, packet,
487 SILC_ID_CLIENT, client_id);
489 /* Add the new entry even though we resolved it. This is because we
490 want to replace the old entry with the new entry here right now. */
492 silc_client_add_client(client, conn, NULL, NULL, NULL,
493 silc_id_dup(client_id, SILC_ID_CLIENT),
496 /* Replace old ID entry with new one on all channels. */
497 silc_client_replace_from_channels(client, conn, client_entry,
500 if (client_entry2 != conn->local_entry)
501 silc_client_nickname_format(client, conn, client_entry2);
503 /* Remove the old from cache */
504 silc_idcache_del_by_context(conn->client_cache, client_entry);
506 /* Replace old ID entry with new one on all channels. */
507 silc_client_replace_from_channels(client, conn, client_entry,
510 /* Notify application */
511 client->internal->ops->notify(client, conn, type,
512 client_entry, client_entry2);
515 silc_client_del_client_entry(client, conn, client_entry);
519 case SILC_NOTIFY_TYPE_CMODE_CHANGE:
521 * Someone changed a channel mode
524 SILC_LOG_DEBUG(("Notify: CMODE_CHANGE"));
527 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
530 id = silc_id_payload_parse_id(tmp, tmp_len, &id_type);
534 /* Find Client entry */
535 if (id_type == SILC_ID_CLIENT) {
536 /* Find Client entry */
538 client_entry = silc_client_get_client_by_id(client, conn, client_id);
540 silc_client_notify_by_server_resolve(client, conn, packet,
541 SILC_ID_CLIENT, client_id);
544 } else if (id_type == SILC_ID_SERVER) {
545 /* Find Server entry */
547 server = silc_client_get_server_by_id(client, conn, server_id);
549 silc_client_notify_by_server_resolve(client, conn, packet,
550 SILC_ID_SERVER, server_id);
554 /* Save the pointer to the client_entry pointer */
555 client_entry = (SilcClientEntry)server;
557 /* Find Channel entry */
559 channel = silc_client_get_channel_by_id(client, conn, channel_id);
561 silc_client_notify_by_server_resolve(client, conn, packet,
562 SILC_ID_CHANNEL, channel_id);
566 /* Save the pointer to the client_entry pointer */
567 client_entry = (SilcClientEntry)channel;
568 silc_free(channel_id);
573 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
577 SILC_GET32_MSB(mode, tmp);
579 /* Get channel entry */
580 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
584 channel = silc_client_get_channel_by_id(client, conn, channel_id);
588 /* Save the new mode */
589 channel->mode = mode;
592 tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
594 unsigned char hash[32];
597 silc_hmac_free(channel->hmac);
598 if (!silc_hmac_alloc(tmp, NULL, &channel->hmac))
601 silc_hash_make(silc_hmac_get_hash(channel->hmac),
602 channel->key, channel->key_len / 8,
604 silc_hmac_set_key(channel->hmac, hash,
605 silc_hash_len(silc_hmac_get_hash(channel->hmac)));
606 memset(hash, 0, sizeof(hash));
609 /* Notify application. The channel entry is sent last as this notify
610 is for channel but application don't know it from the arguments
612 client->internal->ops->notify(client, conn, type, id_type,
613 client_entry, mode, NULL, tmp, channel);
616 case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
618 * Someone changed user's mode on a channel
621 SILC_LOG_DEBUG(("Notify: CUMODE_CHANGE"));
624 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
627 id = silc_id_payload_parse_id(tmp, tmp_len, &id_type);
631 /* Find Client entry */
632 if (id_type == SILC_ID_CLIENT) {
633 /* Find Client entry */
635 client_entry = silc_client_get_client_by_id(client, conn, client_id);
637 silc_client_notify_by_server_resolve(client, conn, packet,
638 SILC_ID_CLIENT, client_id);
641 } else if (id_type == SILC_ID_SERVER) {
642 /* Find Server entry */
644 server = silc_client_get_server_by_id(client, conn, server_id);
646 silc_client_notify_by_server_resolve(client, conn, packet,
647 SILC_ID_SERVER, server_id);
651 /* Save the pointer to the client_entry pointer */
652 client_entry = (SilcClientEntry)server;
654 /* Find Channel entry */
656 channel = silc_client_get_channel_by_id(client, conn, channel_id);
658 silc_client_notify_by_server_resolve(client, conn, packet,
659 SILC_ID_CHANNEL, channel_id);
663 /* Save the pointer to the client_entry pointer */
664 client_entry = (SilcClientEntry)channel;
665 silc_free(channel_id);
670 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
674 SILC_GET32_MSB(mode, tmp);
676 /* Get target Client ID */
677 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
681 silc_free(client_id);
682 client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
686 /* Find target Client entry */
688 silc_client_get_client_by_id(client, conn, client_id);
692 /* Get channel entry */
693 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
697 channel = silc_client_get_channel_by_id(client, conn, channel_id);
702 chu = silc_client_on_channel(channel, client_entry2);
706 /* Notify application. The channel entry is sent last as this notify
707 is for channel but application don't know it from the arguments
709 client->internal->ops->notify(client, conn, type,
710 id_type, client_entry, mode,
711 client_entry2, channel);
714 case SILC_NOTIFY_TYPE_MOTD:
716 * Received Message of the day
719 SILC_LOG_DEBUG(("Notify: MOTD"));
722 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
726 /* Notify application */
727 client->internal->ops->notify(client, conn, type, tmp);
730 case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
732 * Router has enforced a new ID to a channel. Let's change the old
733 * ID to the one provided here.
736 SILC_LOG_DEBUG(("Notify: CHANNEL_CHANGE"));
739 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
742 channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
746 /* Get the channel entry */
747 channel = silc_client_get_channel_by_id(client, conn, channel_id);
751 silc_free(channel_id);
754 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
757 channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
761 /* Replace the Channel ID */
762 silc_client_replace_channel_id(client, conn, channel, channel_id);
764 /* Notify application */
765 client->internal->ops->notify(client, conn, type, channel, channel);
768 case SILC_NOTIFY_TYPE_KICKED:
770 * A client (maybe me) was kicked from a channel
773 SILC_LOG_DEBUG(("Notify: KICKED"));
776 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
780 client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
784 /* Find Client entry */
785 client_entry = silc_client_get_client_by_id(client, conn, client_id);
789 /* Get channel entry */
790 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
794 channel = silc_client_get_channel_by_id(client, conn, channel_id);
799 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
801 client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
805 /* Find kicker's client entry and if not found resolve it */
806 client_entry2 = silc_client_get_client_by_id(client, conn, client_id);
807 if (!client_entry2) {
808 silc_client_notify_by_server_resolve(client, conn, packet,
809 SILC_ID_CLIENT, client_id);
812 if (client_entry2 != conn->local_entry)
813 silc_client_nickname_format(client, conn, client_entry2);
818 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
820 /* Notify application. The channel entry is sent last as this notify
821 is for channel but application don't know it from the arguments
823 client->internal->ops->notify(client, conn, type, client_entry, tmp,
824 client_entry2, channel);
826 /* Remove kicked client from channel */
827 if (client_entry == conn->local_entry) {
828 /* If I was kicked from channel, remove the channel */
829 if (conn->current_channel == channel)
830 conn->current_channel = NULL;
831 silc_client_del_channel(client, conn, channel);
833 chu = silc_client_on_channel(channel, client_entry);
835 silc_hash_table_del(client_entry->channels, channel);
836 silc_hash_table_del(channel->user_list, client_entry);
842 case SILC_NOTIFY_TYPE_KILLED:
844 * A client (maybe me) was killed from the network.
847 SILC_LOG_DEBUG(("Notify: KILLED"));
850 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
854 client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
858 /* Find Client entry */
859 client_entry = silc_client_get_client_by_id(client, conn, client_id);
864 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
866 /* Notify application. */
867 client->internal->ops->notify(client, conn, type, client_entry, tmp);
869 if (client_entry != conn->local_entry)
870 /* Remove the client from all channels and free it */
871 silc_client_del_client(client, conn, client_entry);
875 case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
878 * A server quit the SILC network and some clients must be removed
879 * from channels as they quit as well.
881 SilcClientEntry *clients = NULL;
882 SilcUInt32 clients_count = 0;
885 SILC_LOG_DEBUG(("Notify: SIGNOFF"));
887 for (i = 1; i < silc_argument_get_arg_num(args); i++) {
889 tmp = silc_argument_get_arg_type(args, i + 1, &tmp_len);
891 client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
895 /* Get the client entry */
896 client_entry = silc_client_get_client_by_id(client, conn, client_id);
898 clients = silc_realloc(clients, sizeof(*clients) *
899 (clients_count + 1));
900 clients[clients_count] = client_entry;
903 silc_free(client_id);
908 /* Notify application. We don't keep server entries so the server
909 entry is returned as NULL. The client's are returned as array
910 of SilcClientEntry pointers. */
911 client->internal->ops->notify(client, conn, type, NULL,
912 clients, clients_count);
914 for (i = 0; i < clients_count; i++) {
915 /* Remove client from all channels */
916 client_entry = clients[i];
917 if (client_entry == conn->local_entry)
920 /* Remove the client from all channels and free it */
921 silc_client_del_client(client, conn, client_entry);
933 silc_notify_payload_free(payload);
934 silc_free(client_id);
935 silc_free(channel_id);
936 silc_free(server_id);