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 "clientlibincludes.h"
25 #include "client_internal.h"
28 SilcPacketContext *packet;
30 SilcSocketConnection sock;
31 } *SilcClientNotifyResolve;
33 /* Called when notify is received and some async operation (such as command)
34 is required before processing the notify message. This calls again the
35 silc_client_notify_by_server and reprocesses the original notify packet. */
37 static void silc_client_notify_by_server_pending(void *context, void *context2)
39 SilcClientNotifyResolve res = (SilcClientNotifyResolve)context;
40 SilcClientCommandReplyContext reply =
41 (SilcClientCommandReplyContext)context2;
43 SILC_LOG_DEBUG(("Start"));
46 SilcCommandStatus status;
47 unsigned char *tmp = silc_argument_get_arg_type(reply->args, 1, NULL);
48 SILC_GET16_MSB(status, tmp);
49 if (status != SILC_STATUS_OK) {
50 silc_socket_free(res->sock);
55 silc_client_notify_by_server(res->context, res->sock, res->packet);
56 silc_socket_free(res->sock);
59 /* Destructor for the pending command callback */
61 static void silc_client_notify_by_server_destructor(void *context)
63 SilcClientNotifyResolve res = (SilcClientNotifyResolve)context;
64 silc_packet_context_free(res->packet);
68 /* Resolve client information from server by Client ID. */
70 static void silc_client_notify_by_server_resolve(SilcClient client,
71 SilcClientConnection conn,
72 SilcPacketContext *packet,
73 SilcClientID *client_id)
75 SilcClientNotifyResolve res = silc_calloc(1, sizeof(*res));
76 SilcBuffer idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
78 res->packet = silc_packet_context_dup(packet);
79 res->context = client;
80 res->sock = silc_socket_dup(conn->sock);
82 silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
83 silc_client_command_reply_whois_i, 0,
85 silc_client_command_send(client, conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
86 1, 3, idp->data, idp->len);
87 silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
88 silc_client_notify_by_server_destructor,
89 silc_client_notify_by_server_pending, res);
90 silc_buffer_free(idp);
93 /* Received notify message from server */
95 void silc_client_notify_by_server(SilcClient client,
96 SilcSocketConnection sock,
97 SilcPacketContext *packet)
99 SilcBuffer buffer = packet->buffer;
100 SilcClientConnection conn = (SilcClientConnection)sock->user_data;
101 SilcNotifyPayload payload;
103 SilcArgumentPayload args;
106 SilcClientID *client_id = NULL;
107 SilcChannelID *channel_id = NULL;
108 SilcServerID *server_id = NULL;
109 SilcClientEntry client_entry;
110 SilcClientEntry client_entry2;
111 SilcChannelEntry channel;
113 SilcServerEntry server;
114 SilcIDCacheEntry id_cache = NULL;
116 uint32 tmp_len, mode;
118 SILC_LOG_DEBUG(("Start"));
120 payload = silc_notify_payload_parse(buffer->data, buffer->len);
124 type = silc_notify_get_type(payload);
125 args = silc_notify_get_args(payload);
130 case SILC_NOTIFY_TYPE_NONE:
131 /* Notify application */
132 client->internal->ops->notify(client, conn, type,
133 silc_argument_get_arg_type(args, 1, NULL));
136 case SILC_NOTIFY_TYPE_INVITE:
138 * Someone invited me to a channel. Find Client and Channel entries
139 * for the application.
142 SILC_LOG_DEBUG(("Notify: INVITE"));
145 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
149 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
153 /* Get the channel entry */
155 if (silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
157 channel = (SilcChannelEntry)id_cache->context;
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);
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, client_id);
175 /* Get the channel name */
176 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
180 /* Notify application */
181 client->internal->ops->notify(client, conn, type, channel, tmp,
185 case SILC_NOTIFY_TYPE_JOIN:
187 * Someone has joined to a channel. Get their ID and nickname and
188 * cache them for later use.
191 SILC_LOG_DEBUG(("Notify: JOIN"));
194 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
198 client_id = silc_id_payload_parse_id(tmp, tmp_len);
202 /* Find Client entry and if not found query it */
203 client_entry = silc_client_get_client_by_id(client, conn, client_id);
205 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
209 /* If nickname or username hasn't been resolved, do so */
210 if (!client_entry->nickname || !client_entry->username) {
211 if (client_entry->status & SILC_CLIENT_STATUS_RESOLVING) {
212 client_entry->status &= ~SILC_CLIENT_STATUS_RESOLVING;
215 client_entry->status |= SILC_CLIENT_STATUS_RESOLVING;
216 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
219 if (client_entry != conn->local_entry)
220 silc_client_nickname_format(client, conn, client_entry);
224 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
228 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
232 /* Get channel entry */
233 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
237 channel = (SilcChannelEntry)id_cache->context;
239 /* Add client to channel */
240 if (client_entry != conn->local_entry) {
241 chu = silc_calloc(1, sizeof(*chu));
242 chu->client = client_entry;
243 silc_list_add(channel->clients, chu);
246 /* Notify application. The channel entry is sent last as this notify
247 is for channel but application don't know it from the arguments
249 client->internal->ops->notify(client, conn, type, client_entry, channel);
252 case SILC_NOTIFY_TYPE_LEAVE:
254 * Someone has left a channel. We will remove it from the channel but
255 * we'll keep it in the cache in case we'll need it later.
258 SILC_LOG_DEBUG(("Notify: LEAVE"));
261 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
265 client_id = silc_id_payload_parse_id(tmp, tmp_len);
269 /* Find Client entry */
271 silc_client_get_client_by_id(client, conn, client_id);
275 /* Get channel entry */
276 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
280 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
284 channel = (SilcChannelEntry)id_cache->context;
286 /* Remove client from channel */
287 silc_list_start(channel->clients);
288 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
289 if (chu->client == client_entry) {
290 silc_list_del(channel->clients, chu);
296 /* Notify application. The channel entry is sent last as this notify
297 is for channel but application don't know it from the arguments
299 client->internal->ops->notify(client, conn, type, client_entry, channel);
302 case SILC_NOTIFY_TYPE_SIGNOFF:
304 * Someone left SILC. We'll remove it from all channels and from cache.
307 SILC_LOG_DEBUG(("Notify: SIGNOFF"));
310 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
314 client_id = silc_id_payload_parse_id(tmp, tmp_len);
318 /* Find Client entry */
320 silc_client_get_client_by_id(client, conn, client_id);
324 /* Remove from all channels */
325 silc_client_remove_from_channels(client, conn, client_entry);
327 /* Remove from cache */
328 silc_idcache_del_by_context(conn->client_cache, client_entry);
330 /* Get signoff message */
331 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
335 /* Notify application */
336 client->internal->ops->notify(client, conn, type, client_entry, tmp);
339 silc_client_del_client_entry(client, conn, client_entry);
342 case SILC_NOTIFY_TYPE_TOPIC_SET:
344 * Someone set the topic on a channel.
347 SILC_LOG_DEBUG(("Notify: TOPIC_SET"));
350 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
354 idp = silc_id_payload_parse(tmp, tmp_len);
358 /* Find Client entry */
359 if (silc_id_payload_get_type(idp) == SILC_ID_CLIENT) {
360 client_id = silc_id_payload_parse_id(tmp, tmp_len);
362 silc_id_payload_free(idp);
366 /* Find Client entry */
368 silc_client_get_client_by_id(client, conn, client_id);
371 } else if (silc_id_payload_get_type(idp) == SILC_ID_SERVER) {
372 server_id = silc_id_payload_parse_id(tmp, tmp_len);
374 silc_id_payload_free(idp);
378 server = silc_client_get_server_by_id(client, conn, server_id);
380 silc_id_payload_free(idp);
381 silc_free(server_id);
385 /* Save the pointer to the client_entry pointer */
386 client_entry = (SilcClientEntry)server;
387 silc_free(server_id);
389 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
391 silc_id_payload_free(idp);
395 channel = silc_client_get_channel_by_id(client, conn, channel_id);
397 silc_id_payload_free(idp);
398 silc_free(channel_id);
402 /* Save the pointer to the client_entry pointer */
403 client_entry = (SilcClientEntry)channel;
404 silc_free(channel_id);
408 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
412 /* Get channel entry */
413 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
417 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
421 channel = (SilcChannelEntry)id_cache->context;
423 /* Notify application. The channel entry is sent last as this notify
424 is for channel but application don't know it from the arguments
426 client->internal->ops->notify(client, conn, type,
427 silc_id_payload_get_type(idp),
428 client_entry, tmp, channel);
430 silc_id_payload_free(idp);
433 case SILC_NOTIFY_TYPE_NICK_CHANGE:
435 * Someone changed their nickname. If we don't have entry for the new
436 * ID we will query it and return here after it's done. After we've
437 * returned we fetch the old entry and free it and notify the
441 SILC_LOG_DEBUG(("Notify: NICK_CHANGE"));
443 /* Get old Client ID */
444 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
448 client_id = silc_id_payload_parse_id(tmp, tmp_len);
453 if (SILC_ID_CLIENT_COMPARE(client_id, conn->local_id))
456 /* Find old Client entry */
457 client_entry = silc_client_get_client_by_id(client, conn, client_id);
460 silc_free(client_id);
462 client_entry->valid = FALSE;
464 /* Get new Client ID */
465 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
469 client_id = silc_id_payload_parse_id(tmp, tmp_len);
473 /* Find Client entry and if not found resolve it */
474 client_entry2 = silc_client_get_client_by_id(client, conn, client_id);
475 if (!client_entry2) {
476 /* Resolve the entry information */
477 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
479 /* Add the new entry even though we resolved it. This is because we
480 want to replace the old entry with the new entry here right now. */
482 silc_client_add_client(client, conn, NULL, NULL, NULL,
483 silc_id_dup(client_id, SILC_ID_CLIENT),
486 /* Replace old ID entry with new one on all channels. */
487 silc_client_replace_from_channels(client, conn, client_entry,
490 if (client_entry2 != conn->local_entry)
491 silc_client_nickname_format(client, conn, client_entry2);
493 /* Remove the old from cache */
494 silc_idcache_del_by_context(conn->client_cache, client_entry);
496 /* Replace old ID entry with new one on all channels. */
497 silc_client_replace_from_channels(client, conn, client_entry,
500 /* Notify application */
501 client->internal->ops->notify(client, conn, type,
502 client_entry, client_entry2);
505 silc_client_del_client_entry(client, conn, client_entry);
509 case SILC_NOTIFY_TYPE_CMODE_CHANGE:
511 * Someone changed a channel mode
514 SILC_LOG_DEBUG(("Notify: CMODE_CHANGE"));
517 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
521 idp = silc_id_payload_parse(tmp, tmp_len);
525 /* Find Client entry */
526 if (silc_id_payload_get_type(idp) == SILC_ID_CLIENT) {
527 client_id = silc_id_payload_parse_id(tmp, tmp_len);
529 silc_id_payload_free(idp);
533 client_entry = silc_client_get_client_by_id(client, conn, client_id);
535 silc_id_payload_free(idp);
539 server_id = silc_id_payload_parse_id(tmp, tmp_len);
541 silc_id_payload_free(idp);
545 server = silc_client_get_server_by_id(client, conn, server_id);
547 silc_id_payload_free(idp);
548 silc_free(server_id);
552 /* Save the pointer to the client_entry pointer */
553 client_entry = (SilcClientEntry)server;
554 silc_free(server_id);
558 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
560 silc_id_payload_free(idp);
564 SILC_GET32_MSB(mode, tmp);
566 /* Get channel entry */
567 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
570 silc_id_payload_free(idp);
573 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
575 silc_id_payload_free(idp);
579 channel = (SilcChannelEntry)id_cache->context;
581 /* Save the new mode */
582 channel->mode = mode;
585 tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
587 unsigned char hash[32];
590 silc_hmac_free(channel->hmac);
591 if (!silc_hmac_alloc(tmp, NULL, &channel->hmac))
594 silc_hash_make(silc_hmac_get_hash(channel->hmac),
595 channel->key, channel->key_len / 8,
597 silc_hmac_set_key(channel->hmac, hash,
598 silc_hash_len(silc_hmac_get_hash(channel->hmac)));
599 memset(hash, 0, sizeof(hash));
602 /* Notify application. The channel entry is sent last as this notify
603 is for channel but application don't know it from the arguments
605 client->internal->ops->notify(client, conn, type,
606 silc_id_payload_get_type(idp),
607 client_entry, mode, NULL, tmp, channel);
609 silc_id_payload_free(idp);
612 case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
614 * Someone changed user's mode on a channel
617 SILC_LOG_DEBUG(("Notify: CUMODE_CHANGE"));
620 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
624 client_id = silc_id_payload_parse_id(tmp, tmp_len);
628 /* Find Client entry */
629 client_entry = silc_client_get_client_by_id(client, conn, client_id);
631 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
636 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
640 SILC_GET32_MSB(mode, tmp);
642 /* Get target Client ID */
643 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
647 silc_free(client_id);
648 client_id = silc_id_payload_parse_id(tmp, tmp_len);
652 /* Find target Client entry */
654 silc_client_get_client_by_id(client, conn, client_id);
658 /* Get channel entry */
659 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
663 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
667 channel = (SilcChannelEntry)id_cache->context;
670 silc_list_start(channel->clients);
671 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
672 if (chu->client == client_entry) {
678 /* Notify application. The channel entry is sent last as this notify
679 is for channel but application don't know it from the arguments
681 client->internal->ops->notify(client, conn, type,
683 client_entry2, channel);
686 case SILC_NOTIFY_TYPE_MOTD:
688 * Received Message of the day
691 SILC_LOG_DEBUG(("Notify: MOTD"));
694 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
698 /* Notify application */
699 client->internal->ops->notify(client, conn, type, tmp);
702 case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
704 * Router has enforced a new ID to a channel. Let's change the old
705 * ID to the one provided here.
708 SILC_LOG_DEBUG(("Notify: CHANNEL_CHANGE"));
711 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
714 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
718 /* Get the channel entry */
719 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
723 channel = (SilcChannelEntry)id_cache->context;
725 SILC_LOG_DEBUG(("Old Channel ID id(%s)",
726 silc_id_render(channel->id, SILC_ID_CHANNEL)));
728 /* Remove the old channel entry */
729 silc_idcache_del_by_context(conn->channel_cache, channel);
731 /* Free the old ID */
732 silc_free(channel->id);
735 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
738 channel->id = silc_id_payload_parse_id(tmp, tmp_len);
742 SILC_LOG_DEBUG(("New Channel ID id(%s)",
743 silc_id_render(channel->id, SILC_ID_CHANNEL)));
745 /* Add the channel entry again to ID cache */
746 silc_idcache_add(conn->channel_cache, channel->channel_name,
747 channel->id, channel, 0, NULL);
749 /* Notify application */
750 client->internal->ops->notify(client, conn, type, channel, channel);
753 case SILC_NOTIFY_TYPE_KICKED:
755 * A client (maybe me) was kicked from a channel
758 SILC_LOG_DEBUG(("Notify: KICKED"));
761 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
765 client_id = silc_id_payload_parse_id(tmp, tmp_len);
769 /* Find Client entry */
770 client_entry = silc_client_get_client_by_id(client, conn, client_id);
774 /* Get channel entry */
775 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
779 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
783 channel = (SilcChannelEntry)id_cache->context;
786 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
790 client_id = silc_id_payload_parse_id(tmp, tmp_len);
794 /* Find kicker's client entry and if not found resolve it */
795 client_entry2 = silc_client_get_client_by_id(client, conn, client_id);
796 if (!client_entry2) {
797 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
800 if (client_entry2 != conn->local_entry)
801 silc_client_nickname_format(client, conn, client_entry2);
805 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
807 /* Notify application. The channel entry is sent last as this notify
808 is for channel but application don't know it from the arguments
810 client->internal->ops->notify(client, conn, type, client_entry, tmp,
811 client_entry2, channel);
813 /* If I was kicked from channel, remove the channel */
814 if (client_entry == conn->local_entry) {
815 if (conn->current_channel == channel)
816 conn->current_channel = NULL;
817 silc_idcache_del_by_id(conn->channel_cache, channel->id);
818 silc_free(channel->channel_name);
819 silc_free(channel->id);
820 silc_free(channel->key);
821 silc_cipher_free(channel->channel_key);
826 case SILC_NOTIFY_TYPE_KILLED:
828 * A client (maybe me) was killed from the network.
831 SILC_LOG_DEBUG(("Notify: KILLED"));
834 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
838 client_id = silc_id_payload_parse_id(tmp, tmp_len);
842 /* Find Client entry */
843 client_entry = silc_client_get_client_by_id(client, conn, client_id);
848 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
850 /* Notify application. */
851 client->internal->ops->notify(client, conn, type, client_entry, tmp);
853 if (client_entry != conn->local_entry)
854 /* Remove the client from all channels and free it */
855 silc_client_del_client(client, conn, client_entry);
859 case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
862 * A server quit the SILC network and some clients must be removed
863 * from channels as they quit as well.
865 SilcClientEntry *clients = NULL;
866 uint32 clients_count = 0;
869 SILC_LOG_DEBUG(("Notify: SIGNOFF"));
871 for (i = 1; i < silc_argument_get_arg_num(args); i++) {
873 tmp = silc_argument_get_arg_type(args, i + 1, &tmp_len);
875 client_id = silc_id_payload_parse_id(tmp, tmp_len);
879 /* Get the client entry */
880 client_entry = silc_client_get_client_by_id(client, conn, client_id);
882 clients = silc_realloc(clients, sizeof(*clients) *
883 (clients_count + 1));
884 clients[clients_count] = client_entry;
887 silc_free(client_id);
892 /* Notify application. We don't keep server entries so the server
893 entry is returned as NULL. The client's are returned as array
894 of SilcClientEntry pointers. */
895 client->internal->ops->notify(client, conn, type, NULL,
896 clients, clients_count);
898 for (i = 0; i < clients_count; i++) {
899 /* Remove client from all channels */
900 client_entry = clients[i];
901 if (client_entry == conn->local_entry)
904 /* Remove the client from all channels and free it */
905 silc_client_del_client(client, conn, client_entry);
917 silc_notify_payload_free(payload);
918 silc_free(client_id);
919 silc_free(channel_id);