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)
53 silc_client_notify_by_server(res->context, res->sock, res->packet);
56 silc_socket_free(res->sock);
57 silc_packet_context_free(res->packet);
61 /* Resolve client information from server by Client ID. */
63 static void silc_client_notify_by_server_resolve(SilcClient client,
64 SilcClientConnection conn,
65 SilcPacketContext *packet,
66 SilcClientID *client_id)
68 SilcClientNotifyResolve res = silc_calloc(1, sizeof(*res));
69 SilcBuffer idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
71 res->packet = silc_packet_context_dup(packet);
72 res->context = client;
73 res->sock = silc_socket_dup(conn->sock);
75 silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
76 silc_client_command_reply_whois_i, 0,
78 silc_client_command_send(client, conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
79 1, 3, idp->data, idp->len);
80 silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
81 silc_client_notify_by_server_pending, res);
82 silc_buffer_free(idp);
85 /* Received notify message from server */
87 void silc_client_notify_by_server(SilcClient client,
88 SilcSocketConnection sock,
89 SilcPacketContext *packet)
91 SilcBuffer buffer = packet->buffer;
92 SilcClientConnection conn = (SilcClientConnection)sock->user_data;
93 SilcNotifyPayload payload;
95 SilcArgumentPayload args;
98 SilcClientID *client_id = NULL;
99 SilcChannelID *channel_id = NULL;
100 SilcServerID *server_id = NULL;
101 SilcClientEntry client_entry;
102 SilcClientEntry client_entry2;
103 SilcChannelEntry channel;
105 SilcServerEntry server;
107 uint32 tmp_len, mode;
109 SILC_LOG_DEBUG(("Start"));
111 payload = silc_notify_payload_parse(buffer->data, buffer->len);
115 type = silc_notify_get_type(payload);
116 args = silc_notify_get_args(payload);
121 case SILC_NOTIFY_TYPE_NONE:
122 /* Notify application */
123 client->internal->ops->notify(client, conn, type,
124 silc_argument_get_arg_type(args, 1, NULL));
127 case SILC_NOTIFY_TYPE_INVITE:
129 * Someone invited me to a channel. Find Client and Channel entries
130 * for the application.
133 SILC_LOG_DEBUG(("Notify: INVITE"));
136 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
140 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
144 /* Get the channel entry */
145 channel = silc_client_get_channel_by_id(client, conn, channel_id);
147 /* Get sender Client ID */
148 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
152 client_id = silc_id_payload_parse_id(tmp, tmp_len);
156 /* Find Client entry and if not found query it */
157 client_entry = silc_client_get_client_by_id(client, conn, client_id);
159 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
163 /* Get the channel name */
164 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
168 /* Notify application */
169 client->internal->ops->notify(client, conn, type, channel, tmp,
173 case SILC_NOTIFY_TYPE_JOIN:
175 * Someone has joined to a channel. Get their ID and nickname and
176 * cache them for later use.
179 SILC_LOG_DEBUG(("Notify: JOIN"));
182 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
186 client_id = silc_id_payload_parse_id(tmp, tmp_len);
190 /* Find Client entry and if not found query it */
191 client_entry = silc_client_get_client_by_id(client, conn, client_id);
193 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
197 /* If nickname or username hasn't been resolved, do so */
198 if (!client_entry->nickname || !client_entry->username) {
199 if (client_entry->status & SILC_CLIENT_STATUS_RESOLVING) {
200 client_entry->status &= ~SILC_CLIENT_STATUS_RESOLVING;
203 client_entry->status |= SILC_CLIENT_STATUS_RESOLVING;
204 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
207 if (client_entry != conn->local_entry)
208 silc_client_nickname_format(client, conn, client_entry);
212 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
216 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
220 /* Get channel entry */
221 channel = silc_client_get_channel_by_id(client, conn, channel_id);
225 /* Join the client to channel */
226 if (!silc_client_on_channel(channel, client_entry)) {
227 chu = silc_calloc(1, sizeof(*chu));
228 chu->client = client_entry;
229 chu->channel = channel;
230 silc_hash_table_add(channel->user_list, client_entry, chu);
231 silc_hash_table_add(client_entry->channels, channel, chu);
234 /* Notify application. The channel entry is sent last as this notify
235 is for channel but application don't know it from the arguments
237 client->internal->ops->notify(client, conn, type, client_entry, channel);
240 case SILC_NOTIFY_TYPE_LEAVE:
242 * Someone has left a channel. We will remove it from the channel but
243 * we'll keep it in the cache in case we'll need it later.
246 SILC_LOG_DEBUG(("Notify: LEAVE"));
249 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
253 client_id = silc_id_payload_parse_id(tmp, tmp_len);
257 /* Find Client entry */
259 silc_client_get_client_by_id(client, conn, client_id);
263 /* Get channel entry */
264 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
268 channel = silc_client_get_channel_by_id(client, conn, channel_id);
272 /* Remove client from channel */
273 chu = silc_client_on_channel(channel, client_entry);
275 silc_hash_table_del(client_entry->channels, channel);
276 silc_hash_table_del(channel->user_list, client_entry);
280 /* Notify application. The channel entry is sent last as this notify
281 is for channel but application don't know it from the arguments
283 client->internal->ops->notify(client, conn, type, client_entry, channel);
286 case SILC_NOTIFY_TYPE_SIGNOFF:
288 * Someone left SILC. We'll remove it from all channels and from cache.
291 SILC_LOG_DEBUG(("Notify: SIGNOFF"));
294 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
298 client_id = silc_id_payload_parse_id(tmp, tmp_len);
302 /* Find Client entry */
304 silc_client_get_client_by_id(client, conn, client_id);
308 /* Remove from all channels */
309 silc_client_remove_from_channels(client, conn, client_entry);
311 /* Remove from cache */
312 silc_idcache_del_by_context(conn->client_cache, client_entry);
314 /* Get signoff message */
315 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
319 /* Notify application */
320 client->internal->ops->notify(client, conn, type, client_entry, tmp);
323 silc_client_del_client_entry(client, conn, client_entry);
326 case SILC_NOTIFY_TYPE_TOPIC_SET:
328 * Someone set the topic on a channel.
331 SILC_LOG_DEBUG(("Notify: TOPIC_SET"));
334 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
338 idp = silc_id_payload_parse(tmp, tmp_len);
342 /* Find Client entry */
343 if (silc_id_payload_get_type(idp) == SILC_ID_CLIENT) {
344 client_id = silc_id_payload_parse_id(tmp, tmp_len);
346 silc_id_payload_free(idp);
350 /* Find Client entry */
352 silc_client_get_client_by_id(client, conn, client_id);
355 } else if (silc_id_payload_get_type(idp) == SILC_ID_SERVER) {
356 server_id = silc_id_payload_parse_id(tmp, tmp_len);
358 silc_id_payload_free(idp);
362 server = silc_client_get_server_by_id(client, conn, server_id);
364 silc_id_payload_free(idp);
365 silc_free(server_id);
369 /* Save the pointer to the client_entry pointer */
370 client_entry = (SilcClientEntry)server;
371 silc_free(server_id);
373 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
375 silc_id_payload_free(idp);
379 channel = silc_client_get_channel_by_id(client, conn, channel_id);
381 silc_id_payload_free(idp);
382 silc_free(channel_id);
386 /* Save the pointer to the client_entry pointer */
387 client_entry = (SilcClientEntry)channel;
388 silc_free(channel_id);
392 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
396 /* Get channel entry */
397 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
401 channel = silc_client_get_channel_by_id(client, conn, channel_id);
405 /* Notify application. The channel entry is sent last as this notify
406 is for channel but application don't know it from the arguments
408 client->internal->ops->notify(client, conn, type,
409 silc_id_payload_get_type(idp),
410 client_entry, tmp, channel);
412 silc_id_payload_free(idp);
415 case SILC_NOTIFY_TYPE_NICK_CHANGE:
417 * Someone changed their nickname. If we don't have entry for the new
418 * ID we will query it and return here after it's done. After we've
419 * returned we fetch the old entry and free it and notify the
423 SILC_LOG_DEBUG(("Notify: NICK_CHANGE"));
425 /* Get old Client ID */
426 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
430 client_id = silc_id_payload_parse_id(tmp, tmp_len);
435 if (SILC_ID_CLIENT_COMPARE(client_id, conn->local_id))
438 /* Find old Client entry */
439 client_entry = silc_client_get_client_by_id(client, conn, client_id);
442 silc_free(client_id);
444 client_entry->valid = FALSE;
446 /* Get new Client ID */
447 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
451 client_id = silc_id_payload_parse_id(tmp, tmp_len);
455 /* Find Client entry and if not found resolve it */
456 client_entry2 = silc_client_get_client_by_id(client, conn, client_id);
457 if (!client_entry2) {
458 /* Resolve the entry information */
459 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
461 /* Add the new entry even though we resolved it. This is because we
462 want to replace the old entry with the new entry here right now. */
464 silc_client_add_client(client, conn, NULL, NULL, NULL,
465 silc_id_dup(client_id, SILC_ID_CLIENT),
468 /* Replace old ID entry with new one on all channels. */
469 silc_client_replace_from_channels(client, conn, client_entry,
472 if (client_entry2 != conn->local_entry)
473 silc_client_nickname_format(client, conn, client_entry2);
475 /* Remove the old from cache */
476 silc_idcache_del_by_context(conn->client_cache, client_entry);
478 /* Replace old ID entry with new one on all channels. */
479 silc_client_replace_from_channels(client, conn, client_entry,
482 /* Notify application */
483 client->internal->ops->notify(client, conn, type,
484 client_entry, client_entry2);
487 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 channel = silc_client_get_channel_by_id(client, conn, channel_id);
557 silc_id_payload_free(idp);
561 /* Save the new mode */
562 channel->mode = mode;
565 tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
567 unsigned char hash[32];
570 silc_hmac_free(channel->hmac);
571 if (!silc_hmac_alloc(tmp, NULL, &channel->hmac))
574 silc_hash_make(silc_hmac_get_hash(channel->hmac),
575 channel->key, channel->key_len / 8,
577 silc_hmac_set_key(channel->hmac, hash,
578 silc_hash_len(silc_hmac_get_hash(channel->hmac)));
579 memset(hash, 0, sizeof(hash));
582 /* Notify application. The channel entry is sent last as this notify
583 is for channel but application don't know it from the arguments
585 client->internal->ops->notify(client, conn, type,
586 silc_id_payload_get_type(idp),
587 client_entry, mode, NULL, tmp, channel);
589 silc_id_payload_free(idp);
592 case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
594 * Someone changed user's mode on a channel
597 SILC_LOG_DEBUG(("Notify: CUMODE_CHANGE"));
600 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
604 client_id = silc_id_payload_parse_id(tmp, tmp_len);
608 /* 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, client_id);
616 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
620 SILC_GET32_MSB(mode, tmp);
622 /* Get target Client ID */
623 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
627 silc_free(client_id);
628 client_id = silc_id_payload_parse_id(tmp, tmp_len);
632 /* Find target Client entry */
634 silc_client_get_client_by_id(client, conn, client_id);
638 /* Get channel entry */
639 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
643 channel = silc_client_get_channel_by_id(client, conn, channel_id);
648 chu = silc_client_on_channel(channel, client_entry2);
652 /* Notify application. The channel entry is sent last as this notify
653 is for channel but application don't know it from the arguments
655 client->internal->ops->notify(client, conn, type,
657 client_entry2, channel);
660 case SILC_NOTIFY_TYPE_MOTD:
662 * Received Message of the day
665 SILC_LOG_DEBUG(("Notify: MOTD"));
668 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
672 /* Notify application */
673 client->internal->ops->notify(client, conn, type, tmp);
676 case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
678 * Router has enforced a new ID to a channel. Let's change the old
679 * ID to the one provided here.
682 SILC_LOG_DEBUG(("Notify: CHANNEL_CHANGE"));
685 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
688 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
692 /* Get the channel entry */
693 channel = silc_client_get_channel_by_id(client, conn, channel_id);
697 silc_free(channel_id);
700 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
703 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
707 /* Replace the Channel ID */
708 silc_client_replace_channel_id(client, conn, channel, channel_id);
710 /* Notify application */
711 client->internal->ops->notify(client, conn, type, channel, channel);
714 case SILC_NOTIFY_TYPE_KICKED:
716 * A client (maybe me) was kicked from a channel
719 SILC_LOG_DEBUG(("Notify: KICKED"));
722 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
726 client_id = silc_id_payload_parse_id(tmp, tmp_len);
730 /* Find Client entry */
731 client_entry = silc_client_get_client_by_id(client, conn, client_id);
735 /* Get channel entry */
736 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
740 channel = silc_client_get_channel_by_id(client, conn, channel_id);
745 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
749 client_id = silc_id_payload_parse_id(tmp, tmp_len);
753 /* Find kicker's client entry and if not found resolve it */
754 client_entry2 = silc_client_get_client_by_id(client, conn, client_id);
755 if (!client_entry2) {
756 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
759 if (client_entry2 != conn->local_entry)
760 silc_client_nickname_format(client, conn, client_entry2);
764 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
766 /* Notify application. The channel entry is sent last as this notify
767 is for channel but application don't know it from the arguments
769 client->internal->ops->notify(client, conn, type, client_entry, tmp,
770 client_entry2, channel);
772 /* If I was kicked from channel, remove the channel */
773 if (client_entry == conn->local_entry) {
774 if (conn->current_channel == channel)
775 conn->current_channel = NULL;
776 silc_idcache_del_by_id(conn->channel_cache, channel->id);
777 silc_free(channel->channel_name);
778 silc_free(channel->id);
779 silc_free(channel->key);
780 silc_cipher_free(channel->channel_key);
785 case SILC_NOTIFY_TYPE_KILLED:
787 * A client (maybe me) was killed from the network.
790 SILC_LOG_DEBUG(("Notify: KILLED"));
793 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
797 client_id = silc_id_payload_parse_id(tmp, tmp_len);
801 /* Find Client entry */
802 client_entry = silc_client_get_client_by_id(client, conn, client_id);
807 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
809 /* Notify application. */
810 client->internal->ops->notify(client, conn, type, client_entry, tmp);
812 if (client_entry != conn->local_entry)
813 /* Remove the client from all channels and free it */
814 silc_client_del_client(client, conn, client_entry);
818 case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
821 * A server quit the SILC network and some clients must be removed
822 * from channels as they quit as well.
824 SilcClientEntry *clients = NULL;
825 uint32 clients_count = 0;
828 SILC_LOG_DEBUG(("Notify: SIGNOFF"));
830 for (i = 1; i < silc_argument_get_arg_num(args); i++) {
832 tmp = silc_argument_get_arg_type(args, i + 1, &tmp_len);
834 client_id = silc_id_payload_parse_id(tmp, tmp_len);
838 /* Get the client entry */
839 client_entry = silc_client_get_client_by_id(client, conn, client_id);
841 clients = silc_realloc(clients, sizeof(*clients) *
842 (clients_count + 1));
843 clients[clients_count] = client_entry;
846 silc_free(client_id);
851 /* Notify application. We don't keep server entries so the server
852 entry is returned as NULL. The client's are returned as array
853 of SilcClientEntry pointers. */
854 client->internal->ops->notify(client, conn, type, NULL,
855 clients, clients_count);
857 for (i = 0; i < clients_count; i++) {
858 /* Remove client from all channels */
859 client_entry = clients[i];
860 if (client_entry == conn->local_entry)
863 /* Remove the client from all channels and free it */
864 silc_client_del_client(client, conn, client_entry);
876 silc_notify_payload_free(payload);
877 silc_free(client_id);
878 silc_free(channel_id);