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 client_id = silc_id_payload_parse_id(tmp, tmp_len);
353 /* Find Client entry */
355 silc_client_get_client_by_id(client, conn, client_id);
360 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
364 /* Get channel entry */
365 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
369 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
373 channel = (SilcChannelEntry)id_cache->context;
375 /* Notify application. The channel entry is sent last as this notify
376 is for channel but application don't know it from the arguments
378 client->ops->notify(client, conn, type, client_entry, tmp, channel);
381 case SILC_NOTIFY_TYPE_NICK_CHANGE:
383 * Someone changed their nickname. If we don't have entry for the new
384 * ID we will query it and return here after it's done. After we've
385 * returned we fetch the old entry and free it and notify the
389 SILC_LOG_DEBUG(("Notify: NICK_CHANGE"));
391 /* Get old Client ID */
392 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
396 client_id = silc_id_payload_parse_id(tmp, tmp_len);
401 if (SILC_ID_CLIENT_COMPARE(client_id, conn->local_id))
404 /* Find old Client entry */
405 client_entry = silc_client_get_client_by_id(client, conn, client_id);
408 silc_free(client_id);
410 client_entry->valid = FALSE;
412 /* Get new Client ID */
413 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
417 client_id = silc_id_payload_parse_id(tmp, tmp_len);
421 /* Find Client entry and if not found resolve it */
422 client_entry2 = silc_client_get_client_by_id(client, conn, client_id);
423 if (!client_entry2) {
424 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
427 if (client_entry2 != conn->local_entry)
428 silc_client_nickname_format(client, conn, client_entry2);
431 /* Remove the old from cache */
432 silc_idcache_del_by_context(conn->client_cache, client_entry);
434 /* Replace old ID entry with new one on all channels. */
435 silc_client_replace_from_channels(client, conn, client_entry,
438 /* Notify application */
439 client->ops->notify(client, conn, type, client_entry, client_entry2);
442 silc_client_del_client_entry(client, conn, client_entry);
445 case SILC_NOTIFY_TYPE_CMODE_CHANGE:
447 * Someone changed a channel mode
450 SILC_LOG_DEBUG(("Notify: CMODE_CHANGE"));
453 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
457 idp = silc_id_payload_parse(tmp, tmp_len);
461 /* Find Client entry */
462 if (silc_id_payload_get_type(idp) == SILC_ID_CLIENT) {
463 client_id = silc_id_payload_parse_id(tmp, tmp_len);
465 silc_id_payload_free(idp);
469 client_entry = silc_client_get_client_by_id(client, conn, client_id);
471 silc_id_payload_free(idp);
475 server_id = silc_id_payload_parse_id(tmp, tmp_len);
477 silc_id_payload_free(idp);
481 server = silc_client_get_server_by_id(client, conn, server_id);
483 silc_id_payload_free(idp);
484 silc_free(server_id);
488 /* Save the pointer to the client_entry pointer */
489 client_entry = (SilcClientEntry)server;
490 silc_free(server_id);
494 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
496 silc_id_payload_free(idp);
500 SILC_GET32_MSB(mode, tmp);
502 /* Get channel entry */
503 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
506 silc_id_payload_free(idp);
509 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
511 silc_id_payload_free(idp);
515 channel = (SilcChannelEntry)id_cache->context;
517 /* Save the new mode */
518 channel->mode = mode;
521 tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
523 unsigned char hash[32];
526 silc_hmac_free(channel->hmac);
527 if (!silc_hmac_alloc(tmp, NULL, &channel->hmac))
530 silc_hash_make(silc_hmac_get_hash(channel->hmac),
531 channel->key, channel->key_len / 8,
533 silc_hmac_set_key(channel->hmac, hash,
534 silc_hash_len(silc_hmac_get_hash(channel->hmac)));
535 memset(hash, 0, sizeof(hash));
538 /* Notify application. The channel entry is sent last as this notify
539 is for channel but application don't know it from the arguments
541 client->ops->notify(client, conn, type, silc_id_payload_get_type(idp),
542 client_entry, mode, NULL, tmp, channel);
544 silc_id_payload_free(idp);
547 case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
549 * Someone changed user's mode on a channel
552 SILC_LOG_DEBUG(("Notify: CUMODE_CHANGE"));
555 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
559 client_id = silc_id_payload_parse_id(tmp, tmp_len);
563 /* Find Client entry */
564 client_entry = silc_client_get_client_by_id(client, conn, client_id);
566 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
571 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
575 SILC_GET32_MSB(mode, tmp);
577 /* Get target Client ID */
578 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
582 silc_free(client_id);
583 client_id = silc_id_payload_parse_id(tmp, tmp_len);
587 /* Find target Client entry */
589 silc_client_get_client_by_id(client, conn, client_id);
593 /* Get channel entry */
594 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
598 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
602 channel = (SilcChannelEntry)id_cache->context;
605 silc_list_start(channel->clients);
606 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
607 if (chu->client == client_entry) {
613 /* Notify application. The channel entry is sent last as this notify
614 is for channel but application don't know it from the arguments
616 client->ops->notify(client, conn, type, client_entry, mode,
617 client_entry2, channel);
620 case SILC_NOTIFY_TYPE_MOTD:
622 * Received Message of the day
625 SILC_LOG_DEBUG(("Notify: MOTD"));
628 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
632 /* Notify application */
633 client->ops->notify(client, conn, type, tmp);
636 case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
638 * Router has enforced a new ID to a channel. Let's change the old
639 * ID to the one provided here.
642 SILC_LOG_DEBUG(("Notify: CHANNEL_CHANGE"));
645 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
648 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
652 /* Get the channel entry */
653 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
657 channel = (SilcChannelEntry)id_cache->context;
659 SILC_LOG_DEBUG(("Old Channel ID id(%s)",
660 silc_id_render(channel->id, SILC_ID_CHANNEL)));
662 /* Free the old ID */
663 silc_free(channel->id);
666 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
669 channel->id = silc_id_payload_parse_id(tmp, tmp_len);
673 SILC_LOG_DEBUG(("New Channel ID id(%s)",
674 silc_id_render(channel->id, SILC_ID_CHANNEL)));
676 /* Remove the old cache entry and create a new one */
677 silc_idcache_del_by_context(conn->channel_cache, channel);
678 silc_idcache_add(conn->channel_cache, channel->channel_name,
679 channel->id, channel, FALSE);
681 /* Notify application */
682 client->ops->notify(client, conn, type, channel, channel);
685 case SILC_NOTIFY_TYPE_KICKED:
687 * A client (maybe me) was kicked from a channel
690 SILC_LOG_DEBUG(("Notify: KICKED"));
693 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
697 client_id = silc_id_payload_parse_id(tmp, tmp_len);
701 /* Find Client entry */
702 client_entry = silc_client_get_client_by_id(client, conn, client_id);
706 /* Get channel entry */
707 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
711 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
715 channel = (SilcChannelEntry)id_cache->context;
718 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
722 client_id = silc_id_payload_parse_id(tmp, tmp_len);
726 /* Find kicker's client entry and if not found resolve it */
727 client_entry2 = silc_client_get_client_by_id(client, conn, client_id);
728 if (!client_entry2) {
729 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
732 if (client_entry2 != conn->local_entry)
733 silc_client_nickname_format(client, conn, client_entry2);
737 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
739 /* Notify application. The channel entry is sent last as this notify
740 is for channel but application don't know it from the arguments
742 client->ops->notify(client, conn, type, client_entry, tmp,
743 client_entry2, channel);
745 /* If I was kicked from channel, remove the channel */
746 if (client_entry == conn->local_entry) {
747 if (conn->current_channel == channel)
748 conn->current_channel = NULL;
749 silc_idcache_del_by_id(conn->channel_cache, channel->id);
750 silc_free(channel->channel_name);
751 silc_free(channel->id);
752 silc_free(channel->key);
753 silc_cipher_free(channel->channel_key);
758 case SILC_NOTIFY_TYPE_KILLED:
760 * A client (maybe me) was killed from the network.
763 SILC_LOG_DEBUG(("Notify: KILLED"));
766 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
770 client_id = silc_id_payload_parse_id(tmp, tmp_len);
774 /* Find Client entry */
775 client_entry = silc_client_get_client_by_id(client, conn, client_id);
780 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
782 /* Notify application. */
783 client->ops->notify(client, conn, type, client_entry, tmp);
785 if (client_entry != conn->local_entry) {
786 /* Remove client from all channels */
787 silc_client_remove_from_channels(client, conn, client_entry);
788 silc_client_del_client(client, conn, client_entry);
793 case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
796 * A server quit the SILC network and some clients must be removed
797 * from channels as they quit as well.
799 SilcClientEntry *clients = NULL;
800 uint32 clients_count = 0;
803 SILC_LOG_DEBUG(("Notify: SIGNOFF"));
805 for (i = 1; i < silc_argument_get_arg_num(args); i++) {
807 tmp = silc_argument_get_arg_type(args, i + 1, &tmp_len);
809 client_id = silc_id_payload_parse_id(tmp, tmp_len);
813 /* Get the client entry */
814 client_entry = silc_client_get_client_by_id(client, conn, client_id);
816 clients = silc_realloc(clients, sizeof(*clients) *
817 (clients_count + 1));
818 clients[clients_count] = client_entry;
821 silc_free(client_id);
826 /* Notify application. We don't keep server entries so the server
827 entry is returned as NULL. The client's are returned as array
828 of SilcClientEntry pointers. */
829 client->ops->notify(client, conn, type, NULL, clients, clients_count);
831 for (i = 0; i < clients_count; i++) {
832 /* Remove client from all channels */
833 client_entry = clients[i];
834 if (client_entry == conn->local_entry)
837 silc_client_remove_from_channels(client, conn, client_entry);
838 silc_client_del_client(client, conn, client_entry);
850 silc_notify_payload_free(payload);
851 silc_free(client_id);
852 silc_free(channel_id);