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 "silcincludes.h"
25 #include "silcclient.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;
44 SILC_LOG_DEBUG(("Start"));
47 SilcCommandStatus status;
48 unsigned char *tmp = silc_argument_get_arg_type(reply->args, 1, NULL);
49 SILC_GET16_MSB(status, tmp);
50 if (status != SILC_STATUS_OK)
54 silc_client_notify_by_server(res->context, res->sock, res->packet);
57 silc_socket_free(res->sock);
58 silc_packet_context_free(res->packet);
62 /* Resolve client information from server by Client ID. */
64 static void silc_client_notify_by_server_resolve(SilcClient client,
65 SilcClientConnection conn,
66 SilcPacketContext *packet,
67 SilcClientID *client_id)
69 SilcClientNotifyResolve res = silc_calloc(1, sizeof(*res));
70 SilcBuffer idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
72 res->packet = silc_packet_context_dup(packet);
73 res->context = client;
74 res->sock = silc_socket_dup(conn->sock);
76 silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
77 silc_client_command_reply_whois_i, 0,
79 silc_client_command_send(client, conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
80 1, 3, idp->data, idp->len);
81 silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
82 silc_client_notify_by_server_pending, res);
83 silc_buffer_free(idp);
86 /* Received notify message from server */
88 void silc_client_notify_by_server(SilcClient client,
89 SilcSocketConnection sock,
90 SilcPacketContext *packet)
92 SilcBuffer buffer = packet->buffer;
93 SilcClientConnection conn = (SilcClientConnection)sock->user_data;
94 SilcNotifyPayload payload;
96 SilcArgumentPayload args;
99 SilcClientID *client_id = NULL;
100 SilcChannelID *channel_id = NULL;
101 SilcServerID *server_id = NULL;
102 SilcClientEntry client_entry = NULL;
103 SilcClientEntry client_entry2 = NULL;
104 SilcChannelEntry channel;
106 SilcServerEntry server;
108 SilcUInt32 tmp_len, mode;
110 SILC_LOG_DEBUG(("Start"));
112 payload = silc_notify_payload_parse(buffer->data, buffer->len);
116 type = silc_notify_get_type(payload);
117 args = silc_notify_get_args(payload);
122 case SILC_NOTIFY_TYPE_NONE:
123 /* Notify application */
124 client->internal->ops->notify(client, conn, type,
125 silc_argument_get_arg_type(args, 1, NULL));
128 case SILC_NOTIFY_TYPE_INVITE:
130 * Someone invited me to a channel. Find Client and Channel entries
131 * for the application.
134 SILC_LOG_DEBUG(("Notify: INVITE"));
137 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
141 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
145 /* Get the channel entry */
146 channel = silc_client_get_channel_by_id(client, conn, channel_id);
148 /* Get sender Client ID */
149 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
153 client_id = silc_id_payload_parse_id(tmp, tmp_len);
157 /* Find Client entry and if not found query it */
158 client_entry = silc_client_get_client_by_id(client, conn, client_id);
160 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
164 /* Get the channel name */
165 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
169 /* Notify application */
170 client->internal->ops->notify(client, conn, type, channel, tmp,
174 case SILC_NOTIFY_TYPE_JOIN:
176 * Someone has joined to a channel. Get their ID and nickname and
177 * cache them for later use.
180 SILC_LOG_DEBUG(("Notify: JOIN"));
183 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
187 client_id = silc_id_payload_parse_id(tmp, tmp_len);
191 /* Find Client entry and if not found query it */
192 client_entry = silc_client_get_client_by_id(client, conn, client_id);
194 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
198 /* If nickname or username hasn't been resolved, do so */
199 if (!client_entry->nickname || !client_entry->username) {
200 if (client_entry->status & SILC_CLIENT_STATUS_RESOLVING) {
201 client_entry->status &= ~SILC_CLIENT_STATUS_RESOLVING;
204 client_entry->status |= SILC_CLIENT_STATUS_RESOLVING;
205 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
208 if (client_entry != conn->local_entry)
209 silc_client_nickname_format(client, conn, client_entry);
213 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
217 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
221 /* Get channel entry */
222 channel = silc_client_get_channel_by_id(client, conn, channel_id);
226 /* Join the client to channel */
227 if (!silc_client_on_channel(channel, client_entry)) {
228 chu = silc_calloc(1, sizeof(*chu));
229 chu->client = client_entry;
230 chu->channel = channel;
231 silc_hash_table_add(channel->user_list, client_entry, chu);
232 silc_hash_table_add(client_entry->channels, channel, chu);
235 /* Notify application. The channel entry is sent last as this notify
236 is for channel but application don't know it from the arguments
238 client->internal->ops->notify(client, conn, type, client_entry, channel);
241 case SILC_NOTIFY_TYPE_LEAVE:
243 * Someone has left a channel. We will remove it from the channel but
244 * we'll keep it in the cache in case we'll need it later.
247 SILC_LOG_DEBUG(("Notify: LEAVE"));
250 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
254 client_id = silc_id_payload_parse_id(tmp, tmp_len);
258 /* Find Client entry */
260 silc_client_get_client_by_id(client, conn, client_id);
264 /* Get channel entry */
265 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
269 channel = silc_client_get_channel_by_id(client, conn, channel_id);
273 /* Remove client from channel */
274 chu = silc_client_on_channel(channel, client_entry);
276 silc_hash_table_del(client_entry->channels, channel);
277 silc_hash_table_del(channel->user_list, client_entry);
281 /* Notify application. The channel entry is sent last as this notify
282 is for channel but application don't know it from the arguments
284 client->internal->ops->notify(client, conn, type, client_entry, channel);
287 case SILC_NOTIFY_TYPE_SIGNOFF:
289 * Someone left SILC. We'll remove it from all channels and from cache.
292 SILC_LOG_DEBUG(("Notify: SIGNOFF"));
295 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
299 client_id = silc_id_payload_parse_id(tmp, tmp_len);
303 /* Find Client entry */
305 silc_client_get_client_by_id(client, conn, client_id);
309 /* Remove from all channels */
310 silc_client_remove_from_channels(client, conn, client_entry);
312 /* Remove from cache */
313 silc_idcache_del_by_context(conn->client_cache, client_entry);
315 /* Get signoff message */
316 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
320 /* Notify application */
321 client->internal->ops->notify(client, conn, type, client_entry, tmp);
324 silc_client_del_client_entry(client, conn, client_entry);
327 case SILC_NOTIFY_TYPE_TOPIC_SET:
329 * Someone set the topic on a channel.
332 SILC_LOG_DEBUG(("Notify: TOPIC_SET"));
335 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
339 idp = silc_id_payload_parse(tmp, tmp_len);
343 /* Find Client entry */
344 if (silc_id_payload_get_type(idp) == SILC_ID_CLIENT) {
345 client_id = silc_id_payload_parse_id(tmp, tmp_len);
347 silc_id_payload_free(idp);
351 /* Find Client entry */
353 silc_client_get_client_by_id(client, conn, client_id);
356 } else if (silc_id_payload_get_type(idp) == SILC_ID_SERVER) {
357 server_id = silc_id_payload_parse_id(tmp, tmp_len);
359 silc_id_payload_free(idp);
363 server = silc_client_get_server_by_id(client, conn, server_id);
365 silc_id_payload_free(idp);
366 silc_free(server_id);
370 /* Save the pointer to the client_entry pointer */
371 client_entry = (SilcClientEntry)server;
372 silc_free(server_id);
374 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
376 silc_id_payload_free(idp);
380 channel = silc_client_get_channel_by_id(client, conn, channel_id);
382 silc_id_payload_free(idp);
383 silc_free(channel_id);
387 /* Save the pointer to the client_entry pointer */
388 client_entry = (SilcClientEntry)channel;
389 silc_free(channel_id);
393 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
397 /* Get channel entry */
398 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
402 channel = silc_client_get_channel_by_id(client, conn, channel_id);
406 /* Notify application. The channel entry is sent last as this notify
407 is for channel but application don't know it from the arguments
409 client->internal->ops->notify(client, conn, type,
410 silc_id_payload_get_type(idp),
411 client_entry, tmp, channel);
413 silc_id_payload_free(idp);
416 case SILC_NOTIFY_TYPE_NICK_CHANGE:
418 * Someone changed their nickname. If we don't have entry for the new
419 * ID we will query it and return here after it's done. After we've
420 * returned we fetch the old entry and free it and notify the
424 SILC_LOG_DEBUG(("Notify: NICK_CHANGE"));
426 /* Get old Client ID */
427 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
431 client_id = silc_id_payload_parse_id(tmp, tmp_len);
436 if (SILC_ID_CLIENT_COMPARE(client_id, conn->local_id))
439 /* Find old Client entry */
440 client_entry = silc_client_get_client_by_id(client, conn, client_id);
443 silc_free(client_id);
445 client_entry->valid = FALSE;
447 /* Get new Client ID */
448 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
452 client_id = silc_id_payload_parse_id(tmp, tmp_len);
456 /* Find Client entry and if not found resolve it */
457 client_entry2 = silc_client_get_client_by_id(client, conn, client_id);
458 if (!client_entry2) {
459 /* Resolve the entry information */
460 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
462 /* Add the new entry even though we resolved it. This is because we
463 want to replace the old entry with the new entry here right now. */
465 silc_client_add_client(client, conn, NULL, NULL, NULL,
466 silc_id_dup(client_id, SILC_ID_CLIENT),
469 /* Replace old ID entry with new one on all channels. */
470 silc_client_replace_from_channels(client, conn, client_entry,
473 if (client_entry2 != conn->local_entry)
474 silc_client_nickname_format(client, conn, client_entry2);
476 /* Remove the old from cache */
477 silc_idcache_del_by_context(conn->client_cache, client_entry);
479 /* Replace old ID entry with new one on all channels. */
480 silc_client_replace_from_channels(client, conn, client_entry,
483 /* Notify application */
484 client->internal->ops->notify(client, conn, type,
485 client_entry, client_entry2);
488 silc_client_del_client_entry(client, conn, client_entry);
492 case SILC_NOTIFY_TYPE_CMODE_CHANGE:
494 * Someone changed a channel mode
497 SILC_LOG_DEBUG(("Notify: CMODE_CHANGE"));
500 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
504 idp = silc_id_payload_parse(tmp, tmp_len);
508 /* Find Client entry */
509 if (silc_id_payload_get_type(idp) == SILC_ID_CLIENT) {
510 client_id = silc_id_payload_parse_id(tmp, tmp_len);
512 silc_id_payload_free(idp);
516 client_entry = silc_client_get_client_by_id(client, conn, client_id);
518 silc_id_payload_free(idp);
522 server_id = silc_id_payload_parse_id(tmp, tmp_len);
524 silc_id_payload_free(idp);
528 server = silc_client_get_server_by_id(client, conn, server_id);
530 silc_id_payload_free(idp);
531 silc_free(server_id);
535 /* Save the pointer to the client_entry pointer */
536 client_entry = (SilcClientEntry)server;
537 silc_free(server_id);
541 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
543 silc_id_payload_free(idp);
547 SILC_GET32_MSB(mode, tmp);
549 /* Get channel entry */
550 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
553 silc_id_payload_free(idp);
556 channel = silc_client_get_channel_by_id(client, conn, channel_id);
558 silc_id_payload_free(idp);
562 /* Save the new mode */
563 channel->mode = mode;
566 tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
568 unsigned char hash[32];
571 silc_hmac_free(channel->hmac);
572 if (!silc_hmac_alloc(tmp, NULL, &channel->hmac))
575 silc_hash_make(silc_hmac_get_hash(channel->hmac),
576 channel->key, channel->key_len / 8,
578 silc_hmac_set_key(channel->hmac, hash,
579 silc_hash_len(silc_hmac_get_hash(channel->hmac)));
580 memset(hash, 0, sizeof(hash));
583 /* Notify application. The channel entry is sent last as this notify
584 is for channel but application don't know it from the arguments
586 client->internal->ops->notify(client, conn, type,
587 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 channel = silc_client_get_channel_by_id(client, conn, channel_id);
649 chu = silc_client_on_channel(channel, client_entry2);
653 /* Notify application. The channel entry is sent last as this notify
654 is for channel but application don't know it from the arguments
656 client->internal->ops->notify(client, conn, type,
658 client_entry2, channel);
661 case SILC_NOTIFY_TYPE_MOTD:
663 * Received Message of the day
666 SILC_LOG_DEBUG(("Notify: MOTD"));
669 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
673 /* Notify application */
674 client->internal->ops->notify(client, conn, type, tmp);
677 case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
679 * Router has enforced a new ID to a channel. Let's change the old
680 * ID to the one provided here.
683 SILC_LOG_DEBUG(("Notify: CHANNEL_CHANGE"));
686 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
689 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
693 /* Get the channel entry */
694 channel = silc_client_get_channel_by_id(client, conn, channel_id);
698 silc_free(channel_id);
701 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
704 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
708 /* Replace the Channel ID */
709 silc_client_replace_channel_id(client, conn, channel, channel_id);
711 /* Notify application */
712 client->internal->ops->notify(client, conn, type, channel, channel);
715 case SILC_NOTIFY_TYPE_KICKED:
717 * A client (maybe me) was kicked from a channel
720 SILC_LOG_DEBUG(("Notify: KICKED"));
723 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
727 client_id = silc_id_payload_parse_id(tmp, tmp_len);
731 /* Find Client entry */
732 client_entry = silc_client_get_client_by_id(client, conn, client_id);
736 /* Get channel entry */
737 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
741 channel = silc_client_get_channel_by_id(client, conn, channel_id);
746 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
748 client_id = silc_id_payload_parse_id(tmp, tmp_len);
752 /* Find kicker's client entry and if not found resolve it */
753 client_entry2 = silc_client_get_client_by_id(client, conn, client_id);
754 if (!client_entry2) {
755 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
758 if (client_entry2 != conn->local_entry)
759 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 SilcUInt32 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);