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_send_command(client, conn, SILC_COMMAND_WHOIS, ++conn->cmd_ident,
82 1, 3, idp->data, idp->len);
83 silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
84 silc_client_notify_by_server_destructor,
85 silc_client_notify_by_server_pending, res);
86 silc_buffer_free(idp);
89 /* Received notify message from server */
91 void silc_client_notify_by_server(SilcClient client,
92 SilcSocketConnection sock,
93 SilcPacketContext *packet)
95 SilcBuffer buffer = packet->buffer;
96 SilcClientConnection conn = (SilcClientConnection)sock->user_data;
97 SilcNotifyPayload payload;
99 SilcArgumentPayload args;
102 SilcClientID *client_id = NULL;
103 SilcChannelID *channel_id = NULL;
104 SilcServerID *server_id = NULL;
105 SilcClientEntry client_entry;
106 SilcClientEntry client_entry2;
107 SilcChannelEntry channel;
109 SilcServerEntry server;
110 SilcIDCacheEntry id_cache = NULL;
112 uint32 tmp_len, mode;
114 SILC_LOG_DEBUG(("Start"));
116 payload = silc_notify_payload_parse(buffer->data, buffer->len);
120 type = silc_notify_get_type(payload);
121 args = silc_notify_get_args(payload);
126 case SILC_NOTIFY_TYPE_NONE:
127 /* Notify application */
128 client->ops->notify(client, conn, type,
129 silc_argument_get_arg_type(args, 1, NULL));
132 case SILC_NOTIFY_TYPE_INVITE:
134 * Someone invited me to a channel. Find Client and Channel entries
135 * for the application.
138 SILC_LOG_DEBUG(("Notify: INVITE"));
141 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
145 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
149 /* Get the channel entry */
151 if (silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
153 channel = (SilcChannelEntry)id_cache->context;
155 /* Get sender Client ID */
156 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
160 client_id = silc_id_payload_parse_id(tmp, tmp_len);
164 /* Find Client entry and if not found query it */
165 client_entry = silc_client_get_client_by_id(client, conn, client_id);
167 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
171 /* Get the channel name */
172 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
176 /* Notify application */
177 client->ops->notify(client, conn, type, channel, tmp, client_entry);
180 case SILC_NOTIFY_TYPE_JOIN:
182 * Someone has joined to a channel. Get their ID and nickname and
183 * cache them for later use.
186 SILC_LOG_DEBUG(("Notify: JOIN"));
189 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
193 client_id = silc_id_payload_parse_id(tmp, tmp_len);
197 /* Find Client entry and if not found query it */
198 client_entry = silc_client_get_client_by_id(client, conn, client_id);
200 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
204 /* If nickname or username hasn't been resolved, do so */
205 if (!client_entry->nickname || !client_entry->username) {
206 if (client_entry->status & SILC_CLIENT_STATUS_RESOLVING) {
207 client_entry->status &= ~SILC_CLIENT_STATUS_RESOLVING;
210 client_entry->status |= SILC_CLIENT_STATUS_RESOLVING;
211 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
214 if (client_entry != conn->local_entry)
215 silc_client_nickname_format(client, conn, client_entry);
219 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
223 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
227 /* Get channel entry */
228 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
232 channel = (SilcChannelEntry)id_cache->context;
234 /* Add client to channel */
235 if (client_entry != conn->local_entry) {
236 chu = silc_calloc(1, sizeof(*chu));
237 chu->client = client_entry;
238 silc_list_add(channel->clients, chu);
241 /* Notify application. The channel entry is sent last as this notify
242 is for channel but application don't know it from the arguments
244 client->ops->notify(client, conn, type, client_entry, channel);
247 case SILC_NOTIFY_TYPE_LEAVE:
249 * Someone has left a channel. We will remove it from the channel but
250 * we'll keep it in the cache in case we'll need it later.
253 SILC_LOG_DEBUG(("Notify: LEAVE"));
256 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
260 client_id = silc_id_payload_parse_id(tmp, tmp_len);
264 /* Find Client entry */
266 silc_client_get_client_by_id(client, conn, client_id);
270 /* Get channel entry */
271 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
275 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
279 channel = (SilcChannelEntry)id_cache->context;
281 /* Remove client from channel */
282 silc_list_start(channel->clients);
283 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
284 if (chu->client == client_entry) {
285 silc_list_del(channel->clients, chu);
291 /* Notify application. The channel entry is sent last as this notify
292 is for channel but application don't know it from the arguments
294 client->ops->notify(client, conn, type, client_entry, channel);
297 case SILC_NOTIFY_TYPE_SIGNOFF:
299 * Someone left SILC. We'll remove it from all channels and from cache.
302 SILC_LOG_DEBUG(("Notify: SIGNOFF"));
305 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
309 client_id = silc_id_payload_parse_id(tmp, tmp_len);
313 /* Find Client entry */
315 silc_client_get_client_by_id(client, conn, client_id);
319 /* Remove from all channels */
320 silc_client_remove_from_channels(client, conn, client_entry);
322 /* Remove from cache */
323 silc_idcache_del_by_context(conn->client_cache, client_entry);
325 /* Get signoff message */
326 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
330 /* Notify application */
331 client->ops->notify(client, conn, type, client_entry, tmp);
334 silc_client_del_client_entry(client, conn, client_entry);
337 case SILC_NOTIFY_TYPE_TOPIC_SET:
339 * Someone set the topic on a channel.
342 SILC_LOG_DEBUG(("Notify: TOPIC_SET"));
345 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
349 idp = silc_id_payload_parse(tmp, tmp_len);
353 /* Find Client entry */
354 if (silc_id_payload_get_type(idp) == SILC_ID_CLIENT) {
355 client_id = silc_id_payload_parse_id(tmp, tmp_len);
357 silc_id_payload_free(idp);
361 /* Find Client entry */
363 silc_client_get_client_by_id(client, conn, client_id);
366 } else if (silc_id_payload_get_type(idp) == SILC_ID_SERVER) {
367 server_id = silc_id_payload_parse_id(tmp, tmp_len);
369 silc_id_payload_free(idp);
373 server = silc_client_get_server_by_id(client, conn, server_id);
375 silc_id_payload_free(idp);
376 silc_free(server_id);
380 /* Save the pointer to the client_entry pointer */
381 client_entry = (SilcClientEntry)server;
382 silc_free(server_id);
384 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
386 silc_id_payload_free(idp);
390 channel = silc_client_get_channel_by_id(client, conn, channel_id);
392 silc_id_payload_free(idp);
393 silc_free(channel_id);
397 /* Save the pointer to the client_entry pointer */
398 client_entry = (SilcClientEntry)channel;
399 silc_free(channel_id);
403 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
407 /* Get channel entry */
408 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
412 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
416 channel = (SilcChannelEntry)id_cache->context;
418 /* Notify application. The channel entry is sent last as this notify
419 is for channel but application don't know it from the arguments
421 client->ops->notify(client, conn, type, silc_id_payload_get_type(idp),
422 client_entry, tmp, channel);
424 silc_id_payload_free(idp);
427 case SILC_NOTIFY_TYPE_NICK_CHANGE:
429 * Someone changed their nickname. If we don't have entry for the new
430 * ID we will query it and return here after it's done. After we've
431 * returned we fetch the old entry and free it and notify the
435 SILC_LOG_DEBUG(("Notify: NICK_CHANGE"));
437 /* Get old Client ID */
438 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
442 client_id = silc_id_payload_parse_id(tmp, tmp_len);
447 if (SILC_ID_CLIENT_COMPARE(client_id, conn->local_id))
450 /* Find old Client entry */
451 client_entry = silc_client_get_client_by_id(client, conn, client_id);
454 silc_free(client_id);
456 client_entry->valid = FALSE;
458 /* Get new Client ID */
459 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
463 client_id = silc_id_payload_parse_id(tmp, tmp_len);
467 /* Find Client entry and if not found resolve it */
468 client_entry2 = silc_client_get_client_by_id(client, conn, client_id);
469 if (!client_entry2) {
470 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
473 if (client_entry2 != conn->local_entry)
474 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->ops->notify(client, conn, type, client_entry, client_entry2);
488 silc_client_del_client_entry(client, conn, client_entry);
491 case SILC_NOTIFY_TYPE_CMODE_CHANGE:
493 * Someone changed a channel mode
496 SILC_LOG_DEBUG(("Notify: CMODE_CHANGE"));
499 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
503 idp = silc_id_payload_parse(tmp, tmp_len);
507 /* Find Client entry */
508 if (silc_id_payload_get_type(idp) == SILC_ID_CLIENT) {
509 client_id = silc_id_payload_parse_id(tmp, tmp_len);
511 silc_id_payload_free(idp);
515 client_entry = silc_client_get_client_by_id(client, conn, client_id);
517 silc_id_payload_free(idp);
521 server_id = silc_id_payload_parse_id(tmp, tmp_len);
523 silc_id_payload_free(idp);
527 server = silc_client_get_server_by_id(client, conn, server_id);
529 silc_id_payload_free(idp);
530 silc_free(server_id);
534 /* Save the pointer to the client_entry pointer */
535 client_entry = (SilcClientEntry)server;
536 silc_free(server_id);
540 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
542 silc_id_payload_free(idp);
546 SILC_GET32_MSB(mode, tmp);
548 /* Get channel entry */
549 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
552 silc_id_payload_free(idp);
555 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
557 silc_id_payload_free(idp);
561 channel = (SilcChannelEntry)id_cache->context;
563 /* Save the new mode */
564 channel->mode = mode;
567 tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
569 unsigned char hash[32];
572 silc_hmac_free(channel->hmac);
573 if (!silc_hmac_alloc(tmp, NULL, &channel->hmac))
576 silc_hash_make(silc_hmac_get_hash(channel->hmac),
577 channel->key, channel->key_len / 8,
579 silc_hmac_set_key(channel->hmac, hash,
580 silc_hash_len(silc_hmac_get_hash(channel->hmac)));
581 memset(hash, 0, sizeof(hash));
584 /* Notify application. The channel entry is sent last as this notify
585 is for channel but application don't know it from the arguments
587 client->ops->notify(client, conn, type, silc_id_payload_get_type(idp),
588 client_entry, mode, NULL, tmp, channel);
590 silc_id_payload_free(idp);
593 case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
595 * Someone changed user's mode on a channel
598 SILC_LOG_DEBUG(("Notify: CUMODE_CHANGE"));
601 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
605 client_id = silc_id_payload_parse_id(tmp, tmp_len);
609 /* Find Client entry */
610 client_entry = silc_client_get_client_by_id(client, conn, client_id);
612 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
617 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
621 SILC_GET32_MSB(mode, tmp);
623 /* Get target Client ID */
624 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
628 silc_free(client_id);
629 client_id = silc_id_payload_parse_id(tmp, tmp_len);
633 /* Find target Client entry */
635 silc_client_get_client_by_id(client, conn, client_id);
639 /* Get channel entry */
640 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
644 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
648 channel = (SilcChannelEntry)id_cache->context;
651 silc_list_start(channel->clients);
652 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
653 if (chu->client == client_entry) {
659 /* Notify application. The channel entry is sent last as this notify
660 is for channel but application don't know it from the arguments
662 client->ops->notify(client, conn, type, client_entry, mode,
663 client_entry2, channel);
666 case SILC_NOTIFY_TYPE_MOTD:
668 * Received Message of the day
671 SILC_LOG_DEBUG(("Notify: MOTD"));
674 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
678 /* Notify application */
679 client->ops->notify(client, conn, type, tmp);
682 case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
684 * Router has enforced a new ID to a channel. Let's change the old
685 * ID to the one provided here.
688 SILC_LOG_DEBUG(("Notify: CHANNEL_CHANGE"));
691 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
694 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
698 /* Get the channel entry */
699 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
703 channel = (SilcChannelEntry)id_cache->context;
705 SILC_LOG_DEBUG(("Old Channel ID id(%s)",
706 silc_id_render(channel->id, SILC_ID_CHANNEL)));
708 /* Remove the old channel entry */
709 silc_idcache_del_by_context(conn->channel_cache, channel);
711 /* Free the old ID */
712 silc_free(channel->id);
715 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
718 channel->id = silc_id_payload_parse_id(tmp, tmp_len);
722 SILC_LOG_DEBUG(("New Channel ID id(%s)",
723 silc_id_render(channel->id, SILC_ID_CHANNEL)));
725 /* Add the channel entry again to ID cache */
726 silc_idcache_add(conn->channel_cache, channel->channel_name,
727 channel->id, channel, 0, NULL);
729 /* Notify application */
730 client->ops->notify(client, conn, type, channel, channel);
733 case SILC_NOTIFY_TYPE_KICKED:
735 * A client (maybe me) was kicked from a channel
738 SILC_LOG_DEBUG(("Notify: KICKED"));
741 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
745 client_id = silc_id_payload_parse_id(tmp, tmp_len);
749 /* Find Client entry */
750 client_entry = silc_client_get_client_by_id(client, conn, client_id);
754 /* Get channel entry */
755 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
759 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
763 channel = (SilcChannelEntry)id_cache->context;
766 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
770 client_id = silc_id_payload_parse_id(tmp, tmp_len);
774 /* Find kicker's client entry and if not found resolve it */
775 client_entry2 = silc_client_get_client_by_id(client, conn, client_id);
776 if (!client_entry2) {
777 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
780 if (client_entry2 != conn->local_entry)
781 silc_client_nickname_format(client, conn, client_entry2);
785 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
787 /* Notify application. The channel entry is sent last as this notify
788 is for channel but application don't know it from the arguments
790 client->ops->notify(client, conn, type, client_entry, tmp,
791 client_entry2, channel);
793 /* If I was kicked from channel, remove the channel */
794 if (client_entry == conn->local_entry) {
795 if (conn->current_channel == channel)
796 conn->current_channel = NULL;
797 silc_idcache_del_by_id(conn->channel_cache, channel->id);
798 silc_free(channel->channel_name);
799 silc_free(channel->id);
800 silc_free(channel->key);
801 silc_cipher_free(channel->channel_key);
806 case SILC_NOTIFY_TYPE_KILLED:
808 * A client (maybe me) was killed from the network.
811 SILC_LOG_DEBUG(("Notify: KILLED"));
814 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
818 client_id = silc_id_payload_parse_id(tmp, tmp_len);
822 /* Find Client entry */
823 client_entry = silc_client_get_client_by_id(client, conn, client_id);
828 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
830 /* Notify application. */
831 client->ops->notify(client, conn, type, client_entry, tmp);
833 if (client_entry != conn->local_entry) {
834 /* Remove client from all channels */
835 silc_client_remove_from_channels(client, conn, client_entry);
836 silc_client_del_client(client, conn, client_entry);
841 case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
844 * A server quit the SILC network and some clients must be removed
845 * from channels as they quit as well.
847 SilcClientEntry *clients = NULL;
848 uint32 clients_count = 0;
851 SILC_LOG_DEBUG(("Notify: SIGNOFF"));
853 for (i = 1; i < silc_argument_get_arg_num(args); i++) {
855 tmp = silc_argument_get_arg_type(args, i + 1, &tmp_len);
857 client_id = silc_id_payload_parse_id(tmp, tmp_len);
861 /* Get the client entry */
862 client_entry = silc_client_get_client_by_id(client, conn, client_id);
864 clients = silc_realloc(clients, sizeof(*clients) *
865 (clients_count + 1));
866 clients[clients_count] = client_entry;
869 silc_free(client_id);
874 /* Notify application. We don't keep server entries so the server
875 entry is returned as NULL. The client's are returned as array
876 of SilcClientEntry pointers. */
877 client->ops->notify(client, conn, type, NULL, clients, clients_count);
879 for (i = 0; i < clients_count; i++) {
880 /* Remove client from all channels */
881 client_entry = clients[i];
882 if (client_entry == conn->local_entry)
885 silc_client_remove_from_channels(client, conn, client_entry);
886 silc_client_del_client(client, conn, client_entry);
898 silc_notify_payload_free(payload);
899 silc_free(client_id);
900 silc_free(channel_id);