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;
45 SilcCommandStatus status;
46 unsigned char *tmp = silc_argument_get_arg_type(reply->args, 1, NULL);
47 SILC_GET16_MSB(status, tmp);
48 if (status != SILC_STATUS_OK) {
49 silc_socket_free(res->sock);
54 silc_client_notify_by_server(res->context, res->sock, res->packet);
55 silc_socket_free(res->sock);
58 /* Destructor for the pending command callback */
60 static void silc_client_notify_by_server_destructor(void *context)
62 SilcClientNotifyResolve res = (SilcClientNotifyResolve)context;
63 silc_packet_context_free(res->packet);
67 /* Resolve client information from server by Client ID. */
69 static void silc_client_notify_by_server_resolve(SilcClient client,
70 SilcClientConnection conn,
71 SilcPacketContext *packet,
72 SilcClientID *client_id)
74 SilcClientNotifyResolve res = silc_calloc(1, sizeof(*res));
75 SilcBuffer idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
77 res->packet = silc_packet_context_dup(packet);
78 res->context = client;
79 res->sock = silc_socket_dup(conn->sock);
81 silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
82 silc_client_command_reply_whois_i, 0,
84 silc_client_command_send(client, conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
85 1, 3, idp->data, idp->len);
86 silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
87 silc_client_notify_by_server_destructor,
88 silc_client_notify_by_server_pending, res);
89 silc_buffer_free(idp);
92 /* Received notify message from server */
94 void silc_client_notify_by_server(SilcClient client,
95 SilcSocketConnection sock,
96 SilcPacketContext *packet)
98 SilcBuffer buffer = packet->buffer;
99 SilcClientConnection conn = (SilcClientConnection)sock->user_data;
100 SilcNotifyPayload payload;
102 SilcArgumentPayload args;
105 SilcClientID *client_id = NULL;
106 SilcChannelID *channel_id = NULL;
107 SilcServerID *server_id = NULL;
108 SilcClientEntry client_entry;
109 SilcClientEntry client_entry2;
110 SilcChannelEntry channel;
112 SilcServerEntry server;
113 SilcIDCacheEntry id_cache = NULL;
115 uint32 tmp_len, mode;
117 SILC_LOG_DEBUG(("Start"));
119 payload = silc_notify_payload_parse(buffer->data, buffer->len);
123 type = silc_notify_get_type(payload);
124 args = silc_notify_get_args(payload);
129 case SILC_NOTIFY_TYPE_NONE:
130 /* Notify application */
131 client->internal->ops->notify(client, conn, type,
132 silc_argument_get_arg_type(args, 1, NULL));
135 case SILC_NOTIFY_TYPE_INVITE:
137 * Someone invited me to a channel. Find Client and Channel entries
138 * for the application.
141 SILC_LOG_DEBUG(("Notify: INVITE"));
144 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
148 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
152 /* Get the channel entry */
154 if (silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
156 channel = (SilcChannelEntry)id_cache->context;
158 /* Get sender Client ID */
159 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
163 client_id = silc_id_payload_parse_id(tmp, tmp_len);
167 /* Find Client entry and if not found query it */
168 client_entry = silc_client_get_client_by_id(client, conn, client_id);
170 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
174 /* Get the channel name */
175 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
179 /* Notify application */
180 client->internal->ops->notify(client, conn, type, channel, tmp,
184 case SILC_NOTIFY_TYPE_JOIN:
186 * Someone has joined to a channel. Get their ID and nickname and
187 * cache them for later use.
190 SILC_LOG_DEBUG(("Notify: JOIN"));
193 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
197 client_id = silc_id_payload_parse_id(tmp, tmp_len);
201 /* Find Client entry and if not found query it */
202 client_entry = silc_client_get_client_by_id(client, conn, client_id);
204 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
208 /* If nickname or username hasn't been resolved, do so */
209 if (!client_entry->nickname || !client_entry->username) {
210 if (client_entry->status & SILC_CLIENT_STATUS_RESOLVING) {
211 client_entry->status &= ~SILC_CLIENT_STATUS_RESOLVING;
214 client_entry->status |= SILC_CLIENT_STATUS_RESOLVING;
215 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
218 if (client_entry != conn->local_entry)
219 silc_client_nickname_format(client, conn, client_entry);
223 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
227 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
231 /* Get channel entry */
232 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
236 channel = (SilcChannelEntry)id_cache->context;
238 /* Add client to channel */
239 if (client_entry != conn->local_entry) {
240 chu = silc_calloc(1, sizeof(*chu));
241 chu->client = client_entry;
242 silc_list_add(channel->clients, chu);
245 /* Notify application. The channel entry is sent last as this notify
246 is for channel but application don't know it from the arguments
248 client->internal->ops->notify(client, conn, type, client_entry, channel);
251 case SILC_NOTIFY_TYPE_LEAVE:
253 * Someone has left a channel. We will remove it from the channel but
254 * we'll keep it in the cache in case we'll need it later.
257 SILC_LOG_DEBUG(("Notify: LEAVE"));
260 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
264 client_id = silc_id_payload_parse_id(tmp, tmp_len);
268 /* Find Client entry */
270 silc_client_get_client_by_id(client, conn, client_id);
274 /* Get channel entry */
275 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
279 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
283 channel = (SilcChannelEntry)id_cache->context;
285 /* Remove client from channel */
286 silc_list_start(channel->clients);
287 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
288 if (chu->client == client_entry) {
289 silc_list_del(channel->clients, chu);
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);
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);
353 idp = silc_id_payload_parse(tmp, tmp_len);
357 /* Find Client entry */
358 if (silc_id_payload_get_type(idp) == SILC_ID_CLIENT) {
359 client_id = silc_id_payload_parse_id(tmp, tmp_len);
361 silc_id_payload_free(idp);
365 /* Find Client entry */
367 silc_client_get_client_by_id(client, conn, client_id);
370 } else if (silc_id_payload_get_type(idp) == SILC_ID_SERVER) {
371 server_id = silc_id_payload_parse_id(tmp, tmp_len);
373 silc_id_payload_free(idp);
377 server = silc_client_get_server_by_id(client, conn, server_id);
379 silc_id_payload_free(idp);
380 silc_free(server_id);
384 /* Save the pointer to the client_entry pointer */
385 client_entry = (SilcClientEntry)server;
386 silc_free(server_id);
388 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
390 silc_id_payload_free(idp);
394 channel = silc_client_get_channel_by_id(client, conn, channel_id);
396 silc_id_payload_free(idp);
397 silc_free(channel_id);
401 /* Save the pointer to the client_entry pointer */
402 client_entry = (SilcClientEntry)channel;
403 silc_free(channel_id);
407 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
411 /* Get channel entry */
412 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
416 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
420 channel = (SilcChannelEntry)id_cache->context;
422 /* Notify application. The channel entry is sent last as this notify
423 is for channel but application don't know it from the arguments
425 client->internal->ops->notify(client, conn, type,
426 silc_id_payload_get_type(idp),
427 client_entry, tmp, channel);
429 silc_id_payload_free(idp);
432 case SILC_NOTIFY_TYPE_NICK_CHANGE:
434 * Someone changed their nickname. If we don't have entry for the new
435 * ID we will query it and return here after it's done. After we've
436 * returned we fetch the old entry and free it and notify the
440 SILC_LOG_DEBUG(("Notify: NICK_CHANGE"));
442 /* Get old Client ID */
443 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
447 client_id = silc_id_payload_parse_id(tmp, tmp_len);
452 if (SILC_ID_CLIENT_COMPARE(client_id, conn->local_id))
455 /* Find old Client entry */
456 client_entry = silc_client_get_client_by_id(client, conn, client_id);
459 silc_free(client_id);
461 client_entry->valid = FALSE;
463 /* Get new Client ID */
464 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
468 client_id = silc_id_payload_parse_id(tmp, tmp_len);
472 /* Find Client entry and if not found resolve it */
473 client_entry2 = silc_client_get_client_by_id(client, conn, client_id);
474 if (!client_entry2) {
475 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
478 if (client_entry2 != conn->local_entry)
479 silc_client_nickname_format(client, conn, client_entry2);
482 /* Remove the old from cache */
483 silc_idcache_del_by_context(conn->client_cache, client_entry);
485 /* Replace old ID entry with new one on all channels. */
486 silc_client_replace_from_channels(client, conn, client_entry,
489 /* Notify application */
490 client->internal->ops->notify(client, conn, type,
491 client_entry, client_entry2);
494 silc_client_del_client_entry(client, conn, client_entry);
497 case SILC_NOTIFY_TYPE_CMODE_CHANGE:
499 * Someone changed a channel mode
502 SILC_LOG_DEBUG(("Notify: CMODE_CHANGE"));
505 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
509 idp = silc_id_payload_parse(tmp, tmp_len);
513 /* Find Client entry */
514 if (silc_id_payload_get_type(idp) == SILC_ID_CLIENT) {
515 client_id = silc_id_payload_parse_id(tmp, tmp_len);
517 silc_id_payload_free(idp);
521 client_entry = silc_client_get_client_by_id(client, conn, client_id);
523 silc_id_payload_free(idp);
527 server_id = silc_id_payload_parse_id(tmp, tmp_len);
529 silc_id_payload_free(idp);
533 server = silc_client_get_server_by_id(client, conn, server_id);
535 silc_id_payload_free(idp);
536 silc_free(server_id);
540 /* Save the pointer to the client_entry pointer */
541 client_entry = (SilcClientEntry)server;
542 silc_free(server_id);
546 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
548 silc_id_payload_free(idp);
552 SILC_GET32_MSB(mode, tmp);
554 /* Get channel entry */
555 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
558 silc_id_payload_free(idp);
561 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
563 silc_id_payload_free(idp);
567 channel = (SilcChannelEntry)id_cache->context;
569 /* Save the new mode */
570 channel->mode = mode;
573 tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
575 unsigned char hash[32];
578 silc_hmac_free(channel->hmac);
579 if (!silc_hmac_alloc(tmp, NULL, &channel->hmac))
582 silc_hash_make(silc_hmac_get_hash(channel->hmac),
583 channel->key, channel->key_len / 8,
585 silc_hmac_set_key(channel->hmac, hash,
586 silc_hash_len(silc_hmac_get_hash(channel->hmac)));
587 memset(hash, 0, sizeof(hash));
590 /* Notify application. The channel entry is sent last as this notify
591 is for channel but application don't know it from the arguments
593 client->internal->ops->notify(client, conn, type,
594 silc_id_payload_get_type(idp),
595 client_entry, mode, NULL, tmp, channel);
597 silc_id_payload_free(idp);
600 case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
602 * Someone changed user's mode on a channel
605 SILC_LOG_DEBUG(("Notify: CUMODE_CHANGE"));
608 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
612 client_id = silc_id_payload_parse_id(tmp, tmp_len);
616 /* Find Client entry */
617 client_entry = silc_client_get_client_by_id(client, conn, client_id);
619 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
624 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
628 SILC_GET32_MSB(mode, tmp);
630 /* Get target Client ID */
631 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
635 silc_free(client_id);
636 client_id = silc_id_payload_parse_id(tmp, tmp_len);
640 /* Find target Client entry */
642 silc_client_get_client_by_id(client, conn, client_id);
646 /* Get channel entry */
647 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
651 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
655 channel = (SilcChannelEntry)id_cache->context;
658 silc_list_start(channel->clients);
659 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
660 if (chu->client == client_entry) {
666 /* Notify application. The channel entry is sent last as this notify
667 is for channel but application don't know it from the arguments
669 client->internal->ops->notify(client, conn, type,
671 client_entry2, channel);
674 case SILC_NOTIFY_TYPE_MOTD:
676 * Received Message of the day
679 SILC_LOG_DEBUG(("Notify: MOTD"));
682 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
686 /* Notify application */
687 client->internal->ops->notify(client, conn, type, tmp);
690 case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
692 * Router has enforced a new ID to a channel. Let's change the old
693 * ID to the one provided here.
696 SILC_LOG_DEBUG(("Notify: CHANNEL_CHANGE"));
699 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
702 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
706 /* Get the channel entry */
707 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
711 channel = (SilcChannelEntry)id_cache->context;
713 SILC_LOG_DEBUG(("Old Channel ID id(%s)",
714 silc_id_render(channel->id, SILC_ID_CHANNEL)));
716 /* Remove the old channel entry */
717 silc_idcache_del_by_context(conn->channel_cache, channel);
719 /* Free the old ID */
720 silc_free(channel->id);
723 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
726 channel->id = silc_id_payload_parse_id(tmp, tmp_len);
730 SILC_LOG_DEBUG(("New Channel ID id(%s)",
731 silc_id_render(channel->id, SILC_ID_CHANNEL)));
733 /* Add the channel entry again to ID cache */
734 silc_idcache_add(conn->channel_cache, channel->channel_name,
735 channel->id, channel, 0, NULL);
737 /* Notify application */
738 client->internal->ops->notify(client, conn, type, channel, channel);
741 case SILC_NOTIFY_TYPE_KICKED:
743 * A client (maybe me) was kicked from a channel
746 SILC_LOG_DEBUG(("Notify: KICKED"));
749 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
753 client_id = silc_id_payload_parse_id(tmp, tmp_len);
757 /* Find Client entry */
758 client_entry = silc_client_get_client_by_id(client, conn, client_id);
762 /* Get channel entry */
763 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
767 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
771 channel = (SilcChannelEntry)id_cache->context;
774 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
778 client_id = silc_id_payload_parse_id(tmp, tmp_len);
782 /* Find kicker's client entry and if not found resolve it */
783 client_entry2 = silc_client_get_client_by_id(client, conn, client_id);
784 if (!client_entry2) {
785 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
788 if (client_entry2 != conn->local_entry)
789 silc_client_nickname_format(client, conn, client_entry2);
793 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
795 /* Notify application. The channel entry is sent last as this notify
796 is for channel but application don't know it from the arguments
798 client->internal->ops->notify(client, conn, type, client_entry, tmp,
799 client_entry2, channel);
801 /* If I was kicked from channel, remove the channel */
802 if (client_entry == conn->local_entry) {
803 if (conn->current_channel == channel)
804 conn->current_channel = NULL;
805 silc_idcache_del_by_id(conn->channel_cache, channel->id);
806 silc_free(channel->channel_name);
807 silc_free(channel->id);
808 silc_free(channel->key);
809 silc_cipher_free(channel->channel_key);
814 case SILC_NOTIFY_TYPE_KILLED:
816 * A client (maybe me) was killed from the network.
819 SILC_LOG_DEBUG(("Notify: KILLED"));
822 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
826 client_id = silc_id_payload_parse_id(tmp, tmp_len);
830 /* Find Client entry */
831 client_entry = silc_client_get_client_by_id(client, conn, client_id);
836 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
838 /* Notify application. */
839 client->internal->ops->notify(client, conn, type, client_entry, tmp);
841 if (client_entry != conn->local_entry) {
842 /* Remove client from all channels */
843 silc_client_remove_from_channels(client, conn, client_entry);
844 silc_client_del_client(client, conn, client_entry);
849 case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
852 * A server quit the SILC network and some clients must be removed
853 * from channels as they quit as well.
855 SilcClientEntry *clients = NULL;
856 uint32 clients_count = 0;
859 SILC_LOG_DEBUG(("Notify: SIGNOFF"));
861 for (i = 1; i < silc_argument_get_arg_num(args); i++) {
863 tmp = silc_argument_get_arg_type(args, i + 1, &tmp_len);
865 client_id = silc_id_payload_parse_id(tmp, tmp_len);
869 /* Get the client entry */
870 client_entry = silc_client_get_client_by_id(client, conn, client_id);
872 clients = silc_realloc(clients, sizeof(*clients) *
873 (clients_count + 1));
874 clients[clients_count] = client_entry;
877 silc_free(client_id);
882 /* Notify application. We don't keep server entries so the server
883 entry is returned as NULL. The client's are returned as array
884 of SilcClientEntry pointers. */
885 client->internal->ops->notify(client, conn, type, NULL,
886 clients, clients_count);
888 for (i = 0; i < clients_count; i++) {
889 /* Remove client from all channels */
890 client_entry = clients[i];
891 if (client_entry == conn->local_entry)
894 silc_client_remove_from_channels(client, conn, client_entry);
895 silc_client_del_client(client, conn, client_entry);
907 silc_notify_payload_free(payload);
908 silc_free(client_id);
909 silc_free(channel_id);