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"
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 = silc_command_get_status(reply->payload);
48 if (status != SILC_STATUS_OK)
52 silc_client_notify_by_server(res->context, res->sock, res->packet);
55 silc_socket_free(res->sock);
56 silc_packet_context_free(res->packet);
60 /* Resolve client, channel or server information. */
62 static void silc_client_notify_by_server_resolve(SilcClient client,
63 SilcClientConnection conn,
64 SilcPacketContext *packet,
68 SilcClientNotifyResolve res = silc_calloc(1, sizeof(*res));
69 SilcBuffer idp = silc_id_payload_encode(id, id_type);
71 res->packet = silc_packet_context_dup(packet);
72 res->context = client;
73 res->sock = silc_socket_dup(conn->sock);
75 /* For client resolving use WHOIS, and oterhwise use IDENTIFY */
76 if (id_type == SILC_ID_CLIENT) {
77 silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
78 silc_client_command_reply_whois_i, 0,
80 silc_client_command_send(client, conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
81 1, 3, idp->data, idp->len);
82 silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
83 silc_client_notify_by_server_pending, res);
85 silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
86 silc_client_command_reply_identify_i, 0,
88 silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
89 conn->cmd_ident, 1, 5, idp->data, idp->len);
90 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
91 silc_client_notify_by_server_pending, res);
93 silc_buffer_free(idp);
96 /* Received notify message from server */
98 void silc_client_notify_by_server(SilcClient client,
99 SilcSocketConnection sock,
100 SilcPacketContext *packet)
102 SilcBuffer buffer = packet->buffer;
103 SilcClientConnection conn = (SilcClientConnection)sock->user_data;
104 SilcNotifyPayload payload;
106 SilcArgumentPayload args;
110 SilcClientID *client_id = NULL;
111 SilcChannelID *channel_id = NULL;
112 SilcServerID *server_id = NULL;
113 SilcClientEntry client_entry = NULL;
114 SilcClientEntry client_entry2 = NULL;
115 SilcChannelEntry channel;
117 SilcServerEntry server;
119 SilcUInt32 tmp_len, mode;
121 SILC_LOG_DEBUG(("Start"));
123 payload = silc_notify_payload_parse(buffer->data, buffer->len);
127 type = silc_notify_get_type(payload);
128 args = silc_notify_get_args(payload);
133 case SILC_NOTIFY_TYPE_NONE:
134 /* Notify application */
135 client->internal->ops->notify(client, conn, type,
136 silc_argument_get_arg_type(args, 1, NULL));
139 case SILC_NOTIFY_TYPE_INVITE:
141 * Someone invited me to a channel. Find Client and Channel entries
142 * for the application.
145 SILC_LOG_DEBUG(("Notify: INVITE"));
148 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
152 channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
156 /* Get the channel entry */
157 channel = silc_client_get_channel_by_id(client, conn, channel_id);
159 /* Get sender Client ID */
160 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
164 client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
168 /* Find Client entry and if not found query it */
169 client_entry = silc_client_get_client_by_id(client, conn, client_id);
171 silc_client_notify_by_server_resolve(client, conn, packet,
172 SILC_ID_CLIENT, 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, NULL);
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,
207 SILC_ID_CLIENT, client_id);
211 /* If nickname or username hasn't been resolved, do so */
212 if (!client_entry->nickname || !client_entry->username) {
213 if (client_entry->status & SILC_CLIENT_STATUS_RESOLVING) {
214 client_entry->status &= ~SILC_CLIENT_STATUS_RESOLVING;
217 client_entry->status |= SILC_CLIENT_STATUS_RESOLVING;
218 silc_client_notify_by_server_resolve(client, conn, packet,
219 SILC_ID_CLIENT, client_id);
222 if (client_entry != conn->local_entry)
223 silc_client_nickname_format(client, conn, client_entry);
227 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
231 channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
235 /* Get channel entry */
236 channel = silc_client_get_channel_by_id(client, conn, channel_id);
240 /* Join the client to channel */
241 if (!silc_client_on_channel(channel, client_entry)) {
242 chu = silc_calloc(1, sizeof(*chu));
243 chu->client = client_entry;
244 chu->channel = channel;
245 silc_hash_table_add(channel->user_list, client_entry, chu);
246 silc_hash_table_add(client_entry->channels, channel, chu);
249 /* Notify application. The channel entry is sent last as this notify
250 is for channel but application don't know it from the arguments
252 client->internal->ops->notify(client, conn, type, client_entry, channel);
255 case SILC_NOTIFY_TYPE_LEAVE:
257 * Someone has left a channel. We will remove it from the channel but
258 * we'll keep it in the cache in case we'll need it later.
261 SILC_LOG_DEBUG(("Notify: LEAVE"));
264 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
268 client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
272 /* Find Client entry */
274 silc_client_get_client_by_id(client, conn, client_id);
278 /* Get channel entry */
279 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
283 channel = silc_client_get_channel_by_id(client, conn, channel_id);
287 /* Remove client from channel */
288 chu = silc_client_on_channel(channel, client_entry);
290 silc_hash_table_del(client_entry->channels, channel);
291 silc_hash_table_del(channel->user_list, client_entry);
295 /* Notify application. The channel entry is sent last as this notify
296 is for channel but application don't know it from the arguments
298 client->internal->ops->notify(client, conn, type, client_entry, channel);
301 case SILC_NOTIFY_TYPE_SIGNOFF:
303 * Someone left SILC. We'll remove it from all channels and from cache.
306 SILC_LOG_DEBUG(("Notify: SIGNOFF"));
309 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
313 client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
317 /* Find Client entry */
319 silc_client_get_client_by_id(client, conn, client_id);
323 /* Remove from all channels */
324 silc_client_remove_from_channels(client, conn, client_entry);
326 /* Remove from cache */
327 silc_idcache_del_by_context(conn->client_cache, client_entry);
329 /* Get signoff message */
330 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
334 /* Notify application */
335 client->internal->ops->notify(client, conn, type, client_entry, tmp);
338 silc_client_del_client_entry(client, conn, client_entry);
341 case SILC_NOTIFY_TYPE_TOPIC_SET:
343 * Someone set the topic on a channel.
346 SILC_LOG_DEBUG(("Notify: TOPIC_SET"));
349 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
352 id = silc_id_payload_parse_id(tmp, tmp_len, &id_type);
356 /* Find Client entry */
357 if (id_type == SILC_ID_CLIENT) {
358 /* Find Client entry */
360 client_entry = silc_client_get_client_by_id(client, conn, client_id);
362 silc_client_notify_by_server_resolve(client, conn, packet,
363 SILC_ID_CLIENT, client_id);
366 } else if (id_type == SILC_ID_SERVER) {
367 /* Find Server entry */
369 server = silc_client_get_server_by_id(client, conn, server_id);
371 silc_client_notify_by_server_resolve(client, conn, packet,
372 SILC_ID_SERVER, server_id);
376 /* Save the pointer to the client_entry pointer */
377 client_entry = (SilcClientEntry)server;
379 /* Find Channel entry */
381 channel = silc_client_get_channel_by_id(client, conn, channel_id);
383 silc_client_notify_by_server_resolve(client, conn, packet,
384 SILC_ID_CHANNEL, channel_id);
388 /* Save the pointer to the client_entry pointer */
389 client_entry = (SilcClientEntry)channel;
390 silc_free(channel_id);
395 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
399 /* Get channel entry */
400 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
404 channel = silc_client_get_channel_by_id(client, conn, channel_id);
408 /* Notify application. The channel entry is sent last as this notify
409 is for channel but application don't know it from the arguments
411 client->internal->ops->notify(client, conn, type, id_type,
412 client_entry, tmp, channel);
416 case SILC_NOTIFY_TYPE_NICK_CHANGE:
418 * Someone changed their nickname. If we don't have entry for the new
419 * ID we will query it and return here after it's done. After we've
420 * returned we fetch the old entry and free it and notify the
424 SILC_LOG_DEBUG(("Notify: NICK_CHANGE"));
426 /* Get old Client ID */
427 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
431 client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
436 if (SILC_ID_CLIENT_COMPARE(client_id, conn->local_id))
439 /* Find old Client entry */
440 client_entry = silc_client_get_client_by_id(client, conn, client_id);
443 silc_free(client_id);
445 client_entry->valid = FALSE;
447 /* Get new Client ID */
448 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
452 client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
456 /* Find Client entry and if not found resolve it */
457 client_entry2 = silc_client_get_client_by_id(client, conn, client_id);
458 if (!client_entry2) {
459 /* Resolve the entry information */
460 silc_client_notify_by_server_resolve(client, conn, packet,
461 SILC_ID_CLIENT, client_id);
463 /* Add the new entry even though we resolved it. This is because we
464 want to replace the old entry with the new entry here right now. */
466 silc_client_add_client(client, conn, NULL, NULL, NULL,
467 silc_id_dup(client_id, SILC_ID_CLIENT),
470 /* Replace old ID entry with new one on all channels. */
471 silc_client_replace_from_channels(client, conn, client_entry,
474 if (client_entry2 != conn->local_entry)
475 silc_client_nickname_format(client, conn, client_entry2);
477 /* Remove the old from cache */
478 silc_idcache_del_by_context(conn->client_cache, client_entry);
480 /* Replace old ID entry with new one on all channels. */
481 silc_client_replace_from_channels(client, conn, client_entry,
484 /* Notify application */
485 client->internal->ops->notify(client, conn, type,
486 client_entry, client_entry2);
489 silc_client_del_client_entry(client, conn, client_entry);
493 case SILC_NOTIFY_TYPE_CMODE_CHANGE:
495 * Someone changed a channel mode
498 SILC_LOG_DEBUG(("Notify: CMODE_CHANGE"));
501 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
504 id = silc_id_payload_parse_id(tmp, tmp_len, &id_type);
508 /* Find Client entry */
509 if (id_type == SILC_ID_CLIENT) {
510 /* Find Client entry */
512 client_entry = silc_client_get_client_by_id(client, conn, client_id);
514 silc_client_notify_by_server_resolve(client, conn, packet,
515 SILC_ID_CLIENT, client_id);
518 } else if (id_type == SILC_ID_SERVER) {
519 /* Find Server entry */
521 server = silc_client_get_server_by_id(client, conn, server_id);
523 silc_client_notify_by_server_resolve(client, conn, packet,
524 SILC_ID_SERVER, server_id);
528 /* Save the pointer to the client_entry pointer */
529 client_entry = (SilcClientEntry)server;
531 /* Find Channel entry */
533 channel = silc_client_get_channel_by_id(client, conn, channel_id);
535 silc_client_notify_by_server_resolve(client, conn, packet,
536 SILC_ID_CHANNEL, channel_id);
540 /* Save the pointer to the client_entry pointer */
541 client_entry = (SilcClientEntry)channel;
542 silc_free(channel_id);
547 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
551 SILC_GET32_MSB(mode, tmp);
553 /* Get channel entry */
554 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
558 channel = silc_client_get_channel_by_id(client, conn, channel_id);
562 /* Save the new mode */
563 channel->mode = mode;
566 tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
568 unsigned char hash[32];
571 silc_hmac_free(channel->hmac);
572 if (!silc_hmac_alloc(tmp, NULL, &channel->hmac))
575 silc_hash_make(silc_hmac_get_hash(channel->hmac),
576 channel->key, channel->key_len / 8,
578 silc_hmac_set_key(channel->hmac, hash,
579 silc_hash_len(silc_hmac_get_hash(channel->hmac)));
580 memset(hash, 0, sizeof(hash));
583 /* Notify application. The channel entry is sent last as this notify
584 is for channel but application don't know it from the arguments
586 client->internal->ops->notify(client, conn, type, id_type,
587 client_entry, mode, NULL, tmp, channel);
590 case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
592 * Someone changed user's mode on a channel
595 SILC_LOG_DEBUG(("Notify: CUMODE_CHANGE"));
598 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
601 id = silc_id_payload_parse_id(tmp, tmp_len, &id_type);
605 /* Find Client entry */
606 if (id_type == SILC_ID_CLIENT) {
607 /* Find Client entry */
609 client_entry = silc_client_get_client_by_id(client, conn, client_id);
611 silc_client_notify_by_server_resolve(client, conn, packet,
612 SILC_ID_CLIENT, client_id);
615 } else if (id_type == SILC_ID_SERVER) {
616 /* Find Server entry */
618 server = silc_client_get_server_by_id(client, conn, server_id);
620 silc_client_notify_by_server_resolve(client, conn, packet,
621 SILC_ID_SERVER, server_id);
625 /* Save the pointer to the client_entry pointer */
626 client_entry = (SilcClientEntry)server;
628 /* Find Channel entry */
630 channel = silc_client_get_channel_by_id(client, conn, channel_id);
632 silc_client_notify_by_server_resolve(client, conn, packet,
633 SILC_ID_CHANNEL, channel_id);
637 /* Save the pointer to the client_entry pointer */
638 client_entry = (SilcClientEntry)channel;
639 silc_free(channel_id);
644 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
648 SILC_GET32_MSB(mode, tmp);
650 /* Get target Client ID */
651 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
655 silc_free(client_id);
656 client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
660 /* Find target Client entry */
662 silc_client_get_client_by_id(client, conn, client_id);
666 /* Get channel entry */
667 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
671 channel = silc_client_get_channel_by_id(client, conn, channel_id);
676 chu = silc_client_on_channel(channel, client_entry2);
680 /* Notify application. The channel entry is sent last as this notify
681 is for channel but application don't know it from the arguments
683 client->internal->ops->notify(client, conn, type,
684 id_type, client_entry, mode,
685 client_entry2, channel);
688 case SILC_NOTIFY_TYPE_MOTD:
690 * Received Message of the day
693 SILC_LOG_DEBUG(("Notify: MOTD"));
696 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
700 /* Notify application */
701 client->internal->ops->notify(client, conn, type, tmp);
704 case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
706 * Router has enforced a new ID to a channel. Let's change the old
707 * ID to the one provided here.
710 SILC_LOG_DEBUG(("Notify: CHANNEL_CHANGE"));
713 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
716 channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
720 /* Get the channel entry */
721 channel = silc_client_get_channel_by_id(client, conn, channel_id);
725 silc_free(channel_id);
728 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
731 channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
735 /* Replace the Channel ID */
736 silc_client_replace_channel_id(client, conn, channel, channel_id);
738 /* Notify application */
739 client->internal->ops->notify(client, conn, type, channel, channel);
742 case SILC_NOTIFY_TYPE_KICKED:
744 * A client (maybe me) was kicked from a channel
747 SILC_LOG_DEBUG(("Notify: KICKED"));
750 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
754 client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
758 /* Find Client entry */
759 client_entry = silc_client_get_client_by_id(client, conn, client_id);
763 /* Get channel entry */
764 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
768 channel = silc_client_get_channel_by_id(client, conn, channel_id);
773 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
775 client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
779 /* Find kicker's client entry and if not found resolve it */
780 client_entry2 = silc_client_get_client_by_id(client, conn, client_id);
781 if (!client_entry2) {
782 silc_client_notify_by_server_resolve(client, conn, packet,
783 SILC_ID_CLIENT, client_id);
786 if (client_entry2 != conn->local_entry)
787 silc_client_nickname_format(client, conn, client_entry2);
792 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
794 /* Notify application. The channel entry is sent last as this notify
795 is for channel but application don't know it from the arguments
797 client->internal->ops->notify(client, conn, type, client_entry, tmp,
798 client_entry2, channel);
800 /* If I was kicked from channel, remove the channel */
801 if (client_entry == conn->local_entry) {
802 if (conn->current_channel == channel)
803 conn->current_channel = NULL;
804 silc_idcache_del_by_id(conn->channel_cache, channel->id);
805 silc_free(channel->channel_name);
806 silc_free(channel->id);
807 silc_free(channel->key);
808 silc_cipher_free(channel->channel_key);
813 case SILC_NOTIFY_TYPE_KILLED:
815 * A client (maybe me) was killed from the network.
818 SILC_LOG_DEBUG(("Notify: KILLED"));
821 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
825 client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
829 /* Find Client entry */
830 client_entry = silc_client_get_client_by_id(client, conn, client_id);
835 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
837 /* Notify application. */
838 client->internal->ops->notify(client, conn, type, client_entry, tmp);
840 if (client_entry != conn->local_entry)
841 /* Remove the client from all channels and free it */
842 silc_client_del_client(client, conn, client_entry);
846 case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
849 * A server quit the SILC network and some clients must be removed
850 * from channels as they quit as well.
852 SilcClientEntry *clients = NULL;
853 SilcUInt32 clients_count = 0;
856 SILC_LOG_DEBUG(("Notify: SIGNOFF"));
858 for (i = 1; i < silc_argument_get_arg_num(args); i++) {
860 tmp = silc_argument_get_arg_type(args, i + 1, &tmp_len);
862 client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
866 /* Get the client entry */
867 client_entry = silc_client_get_client_by_id(client, conn, client_id);
869 clients = silc_realloc(clients, sizeof(*clients) *
870 (clients_count + 1));
871 clients[clients_count] = client_entry;
874 silc_free(client_id);
879 /* Notify application. We don't keep server entries so the server
880 entry is returned as NULL. The client's are returned as array
881 of SilcClientEntry pointers. */
882 client->internal->ops->notify(client, conn, type, NULL,
883 clients, clients_count);
885 for (i = 0; i < clients_count; i++) {
886 /* Remove client from all channels */
887 client_entry = clients[i];
888 if (client_entry == conn->local_entry)
891 /* Remove the client from all channels and free it */
892 silc_client_del_client(client, conn, client_entry);
904 silc_notify_payload_free(payload);
905 silc_free(client_id);
906 silc_free(channel_id);
907 silc_free(server_id);