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 silc_client_notify_by_server(res->context, res->sock, res->packet);
42 silc_socket_free(res->sock);
45 /* Destructor for the pending command callback */
47 static void silc_client_notify_by_server_destructor(void *context)
49 SilcClientNotifyResolve res = (SilcClientNotifyResolve)context;
50 silc_packet_context_free(res->packet);
54 /* Resolve client information from server by Client ID. */
56 static void silc_client_notify_by_server_resolve(SilcClient client,
57 SilcClientConnection conn,
58 SilcPacketContext *packet,
59 SilcClientID *client_id)
61 SilcClientNotifyResolve res = silc_calloc(1, sizeof(*res));
62 SilcBuffer idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
64 res->packet = silc_packet_context_dup(packet);
65 res->context = client;
66 res->sock = silc_socket_dup(conn->sock);
68 silc_client_send_command(client, conn, SILC_COMMAND_WHOIS, ++conn->cmd_ident,
69 1, 3, idp->data, idp->len);
70 silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
71 silc_client_notify_by_server_destructor,
72 silc_client_notify_by_server_pending, res);
73 silc_buffer_free(idp);
76 /* Received notify message from server */
78 void silc_client_notify_by_server(SilcClient client,
79 SilcSocketConnection sock,
80 SilcPacketContext *packet)
82 SilcBuffer buffer = packet->buffer;
83 SilcClientConnection conn = (SilcClientConnection)sock->user_data;
84 SilcNotifyPayload payload;
86 SilcArgumentPayload args;
89 SilcClientID *client_id = NULL;
90 SilcChannelID *channel_id = NULL;
91 SilcServerID *server_id = NULL;
92 SilcClientEntry client_entry;
93 SilcClientEntry client_entry2;
94 SilcChannelEntry channel;
96 SilcServerEntry server;
97 SilcIDCacheEntry id_cache = NULL;
101 payload = silc_notify_payload_parse(buffer);
105 type = silc_notify_get_type(payload);
106 args = silc_notify_get_args(payload);
111 case SILC_NOTIFY_TYPE_NONE:
112 /* Notify application */
113 client->ops->notify(client, conn, type,
114 silc_argument_get_arg_type(args, 1, NULL));
117 case SILC_NOTIFY_TYPE_INVITE:
119 * Someone invited me to a channel. Find Client and Channel entries
120 * for the application.
124 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
128 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
132 /* Get the channel entry */
134 if (silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
136 channel = (SilcChannelEntry)id_cache->context;
138 /* Get sender Client ID */
139 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
143 client_id = silc_id_payload_parse_id(tmp, tmp_len);
147 /* Find Client entry and if not found query it */
148 client_entry = silc_client_get_client_by_id(client, conn, client_id);
150 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
154 /* Get the channel name */
155 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
159 /* Notify application */
160 client->ops->notify(client, conn, type, channel, tmp, client_entry);
163 case SILC_NOTIFY_TYPE_JOIN:
165 * Someone has joined to a channel. Get their ID and nickname and
166 * cache them for later use.
170 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
174 client_id = silc_id_payload_parse_id(tmp, tmp_len);
178 /* Find Client entry and if not found query it */
179 client_entry = silc_client_get_client_by_id(client, conn, client_id);
181 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
185 /* If nickname or username hasn't been resolved, do so */
186 if (!client_entry->nickname || !client_entry->username) {
187 if (client_entry->status & SILC_CLIENT_STATUS_RESOLVING) {
188 client_entry->status &= ~SILC_CLIENT_STATUS_RESOLVING;
191 client_entry->status |= SILC_CLIENT_STATUS_RESOLVING;
192 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
197 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
201 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
205 /* Get channel entry */
206 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
210 channel = (SilcChannelEntry)id_cache->context;
212 /* Add client to channel */
213 if (client_entry != conn->local_entry) {
214 chu = silc_calloc(1, sizeof(*chu));
215 chu->client = client_entry;
216 silc_list_add(channel->clients, chu);
219 /* Notify application. The channel entry is sent last as this notify
220 is for channel but application don't know it from the arguments
222 client->ops->notify(client, conn, type, client_entry, channel);
225 case SILC_NOTIFY_TYPE_LEAVE:
227 * Someone has left a channel. We will remove it from the channel but
228 * we'll keep it in the cache in case we'll need it later.
232 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
236 client_id = silc_id_payload_parse_id(tmp, tmp_len);
240 /* Find Client entry */
242 silc_client_get_client_by_id(client, conn, client_id);
246 /* Get channel entry */
247 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
251 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
255 channel = (SilcChannelEntry)id_cache->context;
257 /* Remove client from channel */
258 silc_list_start(channel->clients);
259 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
260 if (chu->client == client_entry) {
261 silc_list_del(channel->clients, chu);
267 /* Notify application. The channel entry is sent last as this notify
268 is for channel but application don't know it from the arguments
270 client->ops->notify(client, conn, type, client_entry, channel);
273 case SILC_NOTIFY_TYPE_SIGNOFF:
275 * Someone left SILC. We'll remove it from all channels and from cache.
279 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
283 client_id = silc_id_payload_parse_id(tmp, tmp_len);
287 /* Find Client entry */
289 silc_client_get_client_by_id(client, conn, client_id);
293 /* Remove from all channels */
294 silc_client_remove_from_channels(client, conn, client_entry);
296 /* Remove from cache */
297 silc_idcache_del_by_context(conn->client_cache, client_entry);
299 /* Get signoff message */
300 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
304 /* Notify application */
305 client->ops->notify(client, conn, type, client_entry, tmp);
308 silc_client_del_client_entry(client, client_entry);
311 case SILC_NOTIFY_TYPE_TOPIC_SET:
313 * Someone set the topic on a channel.
317 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
321 client_id = silc_id_payload_parse_id(tmp, tmp_len);
325 /* Find Client entry */
327 silc_client_get_client_by_id(client, conn, client_id);
332 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
336 /* Get channel entry */
337 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
341 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
345 channel = (SilcChannelEntry)id_cache->context;
347 /* Notify application. The channel entry is sent last as this notify
348 is for channel but application don't know it from the arguments
350 client->ops->notify(client, conn, type, client_entry, tmp, channel);
353 case SILC_NOTIFY_TYPE_NICK_CHANGE:
355 * Someone changed their nickname. If we don't have entry for the new
356 * ID we will query it and return here after it's done. After we've
357 * returned we fetch the old entry and free it and notify the
361 /* Get old Client ID */
362 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
366 client_id = silc_id_payload_parse_id(tmp, tmp_len);
371 if (SILC_ID_CLIENT_COMPARE(client_id, conn->local_id))
374 /* Find old Client entry */
375 client_entry = silc_client_get_client_by_id(client, conn, client_id);
378 silc_free(client_id);
380 client_entry->valid = FALSE;
382 /* Get new Client ID */
383 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
387 client_id = silc_id_payload_parse_id(tmp, tmp_len);
391 /* Find Client entry and if not found resolve it */
392 client_entry2 = silc_client_get_client_by_id(client, conn, client_id);
393 if (!client_entry2) {
394 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
398 /* Remove the old from cache */
399 silc_idcache_del_by_context(conn->client_cache, client_entry);
401 /* Replace old ID entry with new one on all channels. */
402 silc_client_replace_from_channels(client, conn, client_entry,
405 /* Notify application */
406 client->ops->notify(client, conn, type, client_entry, client_entry2);
409 silc_client_del_client_entry(client, client_entry);
412 case SILC_NOTIFY_TYPE_CMODE_CHANGE:
414 * Someone changed a channel mode
418 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
422 idp = silc_id_payload_parse_data(tmp, tmp_len);
426 /* Find Client entry */
427 if (silc_id_payload_get_type(idp) == SILC_ID_CLIENT) {
428 client_id = silc_id_payload_parse_id(tmp, tmp_len);
430 silc_id_payload_free(idp);
434 client_entry = silc_client_get_client_by_id(client, conn, client_id);
436 silc_id_payload_free(idp);
440 server_id = silc_id_payload_parse_id(tmp, tmp_len);
442 silc_id_payload_free(idp);
446 server = silc_client_get_server_by_id(client, conn, server_id);
448 silc_id_payload_free(idp);
449 silc_free(server_id);
453 /* Save the pointer to the client_entry pointer */
454 client_entry = (SilcClientEntry)server;
455 silc_free(server_id);
459 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
461 silc_id_payload_free(idp);
465 SILC_GET32_MSB(mode, tmp);
467 /* Get channel entry */
468 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
471 silc_id_payload_free(idp);
474 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
476 silc_id_payload_free(idp);
480 channel = (SilcChannelEntry)id_cache->context;
482 /* Save the new mode */
483 channel->mode = mode;
486 tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
488 unsigned char hash[32];
491 silc_hmac_free(channel->hmac);
492 if (!silc_hmac_alloc(tmp, NULL, &channel->hmac))
495 silc_hash_make(silc_hmac_get_hash(channel->hmac),
496 channel->key, channel->key_len / 8,
498 silc_hmac_set_key(channel->hmac, hash,
499 silc_hash_len(silc_hmac_get_hash(channel->hmac)));
500 memset(hash, 0, sizeof(hash));
503 /* Notify application. The channel entry is sent last as this notify
504 is for channel but application don't know it from the arguments
506 client->ops->notify(client, conn, type, silc_id_payload_get_type(idp),
507 client_entry, mode, NULL, tmp, channel);
509 silc_id_payload_free(idp);
512 case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
514 * Someone changed user's mode on a channel
518 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
522 client_id = silc_id_payload_parse_id(tmp, tmp_len);
526 /* Find Client entry */
527 client_entry = silc_client_get_client_by_id(client, conn, client_id);
529 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
534 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
538 SILC_GET32_MSB(mode, tmp);
540 /* Get target Client ID */
541 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
545 silc_free(client_id);
546 client_id = silc_id_payload_parse_id(tmp, tmp_len);
550 /* Find target Client entry */
552 silc_client_get_client_by_id(client, conn, client_id);
556 /* Get channel entry */
557 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
561 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
565 channel = (SilcChannelEntry)id_cache->context;
568 silc_list_start(channel->clients);
569 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
570 if (chu->client == client_entry) {
576 /* Notify application. The channel entry is sent last as this notify
577 is for channel but application don't know it from the arguments
579 client->ops->notify(client, conn, type, client_entry, mode,
580 client_entry2, channel);
583 case SILC_NOTIFY_TYPE_MOTD:
585 * Received Message of the day
589 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
593 /* Notify application */
594 client->ops->notify(client, conn, type, tmp);
597 case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
599 * Router has enforced a new ID to a channel. Let's change the old
600 * ID to the one provided here.
604 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
607 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
611 /* Get the channel entry */
612 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
616 channel = (SilcChannelEntry)id_cache->context;
618 SILC_LOG_DEBUG(("Old Channel ID id(%s)",
619 silc_id_render(channel->id, SILC_ID_CHANNEL)));
621 /* Free the old ID */
622 silc_free(channel->id);
625 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
628 channel->id = silc_id_payload_parse_id(tmp, tmp_len);
632 SILC_LOG_DEBUG(("New Channel ID id(%s)",
633 silc_id_render(channel->id, SILC_ID_CHANNEL)));
635 /* Remove the old cache entry and create a new one */
636 silc_idcache_del_by_context(conn->channel_cache, channel);
637 silc_idcache_add(conn->channel_cache, channel->channel_name,
638 channel->id, channel, FALSE);
640 /* Notify application */
641 client->ops->notify(client, conn, type, channel, channel);
644 case SILC_NOTIFY_TYPE_KICKED:
646 * A client (maybe me) was kicked from a channel
650 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
654 client_id = silc_id_payload_parse_id(tmp, tmp_len);
658 /* Find Client entry */
659 client_entry = silc_client_get_client_by_id(client, conn, client_id);
663 /* Get channel entry */
664 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
668 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
672 channel = (SilcChannelEntry)id_cache->context;
675 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
677 /* Notify application. The channel entry is sent last as this notify
678 is for channel but application don't know it from the arguments
680 client->ops->notify(client, conn, type, client_entry, tmp, channel);
682 /* If I was kicked from channel, remove the channel */
683 if (client_entry == conn->local_entry) {
684 if (conn->current_channel == channel)
685 conn->current_channel = NULL;
686 silc_idcache_del_by_id(conn->channel_cache, channel->id);
687 silc_free(channel->channel_name);
688 silc_free(channel->id);
689 silc_free(channel->key);
690 silc_cipher_free(channel->channel_key);
695 case SILC_NOTIFY_TYPE_KILLED:
697 * A client (maybe me) was killed from the network.
701 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
705 client_id = silc_id_payload_parse_id(tmp, tmp_len);
709 /* Find Client entry */
710 client_entry = silc_client_get_client_by_id(client, conn, client_id);
715 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
717 /* Notify application. */
718 client->ops->notify(client, conn, type, client_entry, tmp);
720 if (client_entry != conn->local_entry) {
721 /* Remove client from all channels */
722 silc_client_remove_from_channels(client, conn, client_entry);
723 silc_client_del_client(client, conn, client_entry);
728 case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
731 * A server quit the SILC network and some clients must be removed
732 * from channels as they quit as well.
734 SilcClientEntry *clients = NULL;
735 uint32 clients_count = 0;
738 for (i = 1; i < silc_argument_get_arg_num(args); i++) {
740 tmp = silc_argument_get_arg_type(args, i + 1, &tmp_len);
742 client_id = silc_id_payload_parse_id(tmp, tmp_len);
746 /* Get the client entry */
747 client_entry = silc_client_get_client_by_id(client, conn, client_id);
749 clients = silc_realloc(clients, sizeof(*clients) *
750 (clients_count + 1));
751 clients[clients_count] = client_entry;
754 silc_free(client_id);
759 /* Notify application. We don't keep server entries so the server
760 entry is returned as NULL. The client's are returned as array
761 of SilcClientEntry pointers. */
762 client->ops->notify(client, conn, type, NULL, clients, clients_count);
764 for (i = 0; i < clients_count; i++) {
765 /* Remove client from all channels */
766 client_entry = clients[i];
767 if (client_entry == conn->local_entry)
770 silc_client_remove_from_channels(client, conn, client_entry);
771 silc_client_del_client(client, conn, client_entry);
783 silc_notify_payload_free(payload);
784 silc_free(client_id);
785 silc_free(channel_id);