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)
189 client_entry->status |= SILC_CLIENT_STATUS_RESOLVING;
190 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
195 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
199 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
203 /* Get channel entry */
204 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
208 channel = (SilcChannelEntry)id_cache->context;
210 /* Add client to channel */
211 if (client_entry != conn->local_entry) {
212 chu = silc_calloc(1, sizeof(*chu));
213 chu->client = client_entry;
214 silc_list_add(channel->clients, chu);
217 /* Notify application. The channel entry is sent last as this notify
218 is for channel but application don't know it from the arguments
220 client->ops->notify(client, conn, type, client_entry, channel);
223 case SILC_NOTIFY_TYPE_LEAVE:
225 * Someone has left a channel. We will remove it from the channel but
226 * we'll keep it in the cache in case we'll need it later.
230 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
234 client_id = silc_id_payload_parse_id(tmp, tmp_len);
238 /* Find Client entry */
240 silc_client_get_client_by_id(client, conn, client_id);
244 /* Get channel entry */
245 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
249 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
253 channel = (SilcChannelEntry)id_cache->context;
255 /* Remove client from channel */
256 silc_list_start(channel->clients);
257 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
258 if (chu->client == client_entry) {
259 silc_list_del(channel->clients, chu);
265 /* Notify application. The channel entry is sent last as this notify
266 is for channel but application don't know it from the arguments
268 client->ops->notify(client, conn, type, client_entry, channel);
271 case SILC_NOTIFY_TYPE_SIGNOFF:
273 * Someone left SILC. We'll remove it from all channels and from cache.
277 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
281 client_id = silc_id_payload_parse_id(tmp, tmp_len);
285 /* Find Client entry */
287 silc_client_get_client_by_id(client, conn, client_id);
291 /* Remove from all channels */
292 silc_client_remove_from_channels(client, conn, client_entry);
294 /* Remove from cache */
295 silc_idcache_del_by_context(conn->client_cache, client_entry);
297 /* Get signoff message */
298 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
302 /* Notify application */
303 client->ops->notify(client, conn, type, client_entry, tmp);
306 silc_client_del_client_entry(client, client_entry);
309 case SILC_NOTIFY_TYPE_TOPIC_SET:
311 * Someone set the topic on a channel.
315 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
319 client_id = silc_id_payload_parse_id(tmp, tmp_len);
323 /* Find Client entry */
325 silc_client_get_client_by_id(client, conn, client_id);
330 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
334 /* Get channel entry */
335 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
339 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
343 channel = (SilcChannelEntry)id_cache->context;
345 /* Notify application. The channel entry is sent last as this notify
346 is for channel but application don't know it from the arguments
348 client->ops->notify(client, conn, type, client_entry, tmp, channel);
351 case SILC_NOTIFY_TYPE_NICK_CHANGE:
353 * Someone changed their nickname. If we don't have entry for the new
354 * ID we will query it and return here after it's done. After we've
355 * returned we fetch the old entry and free it and notify the
359 /* Get old Client ID */
360 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
364 client_id = silc_id_payload_parse_id(tmp, tmp_len);
369 if (SILC_ID_CLIENT_COMPARE(client_id, conn->local_id))
372 /* Find old Client entry */
373 client_entry = silc_client_get_client_by_id(client, conn, client_id);
376 silc_free(client_id);
378 client_entry->valid = FALSE;
380 /* Get new Client ID */
381 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
385 client_id = silc_id_payload_parse_id(tmp, tmp_len);
389 /* Find Client entry and if not found resolve it */
390 client_entry2 = silc_client_get_client_by_id(client, conn, client_id);
391 if (!client_entry2) {
392 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
396 /* Remove the old from cache */
397 silc_idcache_del_by_context(conn->client_cache, client_entry);
399 /* Replace old ID entry with new one on all channels. */
400 silc_client_replace_from_channels(client, conn, client_entry,
403 /* Notify application */
404 client->ops->notify(client, conn, type, client_entry, client_entry2);
407 silc_client_del_client_entry(client, client_entry);
410 case SILC_NOTIFY_TYPE_CMODE_CHANGE:
412 * Someone changed a channel mode
416 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
420 idp = silc_id_payload_parse_data(tmp, tmp_len);
424 /* Find Client entry */
425 if (silc_id_payload_get_type(idp) == SILC_ID_CLIENT) {
426 client_id = silc_id_payload_parse_id(tmp, tmp_len);
428 silc_id_payload_free(idp);
432 client_entry = silc_client_get_client_by_id(client, conn, client_id);
434 silc_id_payload_free(idp);
438 server_id = silc_id_payload_parse_id(tmp, tmp_len);
440 silc_id_payload_free(idp);
444 server = silc_client_get_server_by_id(client, conn, server_id);
446 silc_id_payload_free(idp);
447 silc_free(server_id);
451 /* Save the pointer to the client_entry pointer */
452 client_entry = (SilcClientEntry)server;
453 silc_free(server_id);
457 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
459 silc_id_payload_free(idp);
463 SILC_GET32_MSB(mode, tmp);
465 /* Get channel entry */
466 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
469 silc_id_payload_free(idp);
472 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
474 silc_id_payload_free(idp);
478 channel = (SilcChannelEntry)id_cache->context;
480 /* Save the new mode */
481 channel->mode = mode;
484 tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
486 unsigned char hash[32];
489 silc_hmac_free(channel->hmac);
490 if (!silc_hmac_alloc(tmp, NULL, &channel->hmac))
493 silc_hash_make(silc_hmac_get_hash(channel->hmac),
494 channel->key, channel->key_len / 8,
496 silc_hmac_set_key(channel->hmac, hash,
497 silc_hash_len(silc_hmac_get_hash(channel->hmac)));
498 memset(hash, 0, sizeof(hash));
501 /* Notify application. The channel entry is sent last as this notify
502 is for channel but application don't know it from the arguments
504 client->ops->notify(client, conn, type, silc_id_payload_get_type(idp),
505 client_entry, mode, NULL, tmp, channel);
507 silc_id_payload_free(idp);
510 case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
512 * Someone changed user's mode on a channel
516 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
520 client_id = silc_id_payload_parse_id(tmp, tmp_len);
524 /* Find Client entry */
525 client_entry = silc_client_get_client_by_id(client, conn, client_id);
527 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
532 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
536 SILC_GET32_MSB(mode, tmp);
538 /* Get target Client ID */
539 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
543 silc_free(client_id);
544 client_id = silc_id_payload_parse_id(tmp, tmp_len);
548 /* Find target Client entry */
550 silc_client_get_client_by_id(client, conn, client_id);
554 /* Get channel entry */
555 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
559 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
563 channel = (SilcChannelEntry)id_cache->context;
566 silc_list_start(channel->clients);
567 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
568 if (chu->client == client_entry) {
574 /* Notify application. The channel entry is sent last as this notify
575 is for channel but application don't know it from the arguments
577 client->ops->notify(client, conn, type, client_entry, mode,
578 client_entry2, channel);
581 case SILC_NOTIFY_TYPE_MOTD:
583 * Received Message of the day
587 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
591 /* Notify application */
592 client->ops->notify(client, conn, type, tmp);
595 case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
597 * Router has enforced a new ID to a channel. Let's change the old
598 * ID to the one provided here.
602 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
605 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
609 /* Get the channel entry */
610 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
614 channel = (SilcChannelEntry)id_cache->context;
616 SILC_LOG_DEBUG(("Old Channel ID id(%s)",
617 silc_id_render(channel->id, SILC_ID_CHANNEL)));
619 /* Free the old ID */
620 silc_free(channel->id);
623 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
626 channel->id = silc_id_payload_parse_id(tmp, tmp_len);
630 SILC_LOG_DEBUG(("New Channel ID id(%s)",
631 silc_id_render(channel->id, SILC_ID_CHANNEL)));
633 /* Remove the old cache entry and create a new one */
634 silc_idcache_del_by_context(conn->channel_cache, channel);
635 silc_idcache_add(conn->channel_cache, channel->channel_name,
636 channel->id, channel, FALSE);
638 /* Notify application */
639 client->ops->notify(client, conn, type, channel, channel);
642 case SILC_NOTIFY_TYPE_KICKED:
644 * A client (maybe me) was kicked from a channel
648 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
652 client_id = silc_id_payload_parse_id(tmp, tmp_len);
656 /* Find Client entry */
657 client_entry = silc_client_get_client_by_id(client, conn, client_id);
661 /* Get channel entry */
662 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
666 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
670 channel = (SilcChannelEntry)id_cache->context;
673 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
675 /* Notify application. The channel entry is sent last as this notify
676 is for channel but application don't know it from the arguments
678 client->ops->notify(client, conn, type, client_entry, tmp, channel);
680 /* If I was kicked from channel, remove the channel */
681 if (client_entry == conn->local_entry) {
682 if (conn->current_channel == channel)
683 conn->current_channel = NULL;
684 silc_idcache_del_by_id(conn->channel_cache, channel->id);
685 silc_free(channel->channel_name);
686 silc_free(channel->id);
687 silc_free(channel->key);
688 silc_cipher_free(channel->channel_key);
693 case SILC_NOTIFY_TYPE_KILLED:
695 * A client (maybe me) was killed from the network.
699 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
703 client_id = silc_id_payload_parse_id(tmp, tmp_len);
707 /* Find Client entry */
708 client_entry = silc_client_get_client_by_id(client, conn, client_id);
713 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
715 /* Notify application. */
716 client->ops->notify(client, conn, type, client_entry, tmp);
718 if (client_entry != conn->local_entry) {
719 /* Remove client from all channels */
720 silc_client_remove_from_channels(client, conn, client_entry);
721 silc_client_del_client(client, conn, client_entry);
726 case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
729 * A server quit the SILC network and some clients must be removed
730 * from channels as they quit as well.
732 SilcClientEntry *clients = NULL;
733 uint32 clients_count = 0;
736 for (i = 1; i < silc_argument_get_arg_num(args); i++) {
738 tmp = silc_argument_get_arg_type(args, i + 1, &tmp_len);
740 client_id = silc_id_payload_parse_id(tmp, tmp_len);
744 /* Get the client entry */
745 client_entry = silc_client_get_client_by_id(client, conn, client_id);
747 clients = silc_realloc(clients, sizeof(*clients) *
748 (clients_count + 1));
749 clients[clients_count] = client_entry;
752 silc_free(client_id);
757 /* Notify application. We don't keep server entries so the server
758 entry is returned as NULL. The client's are returned as array
759 of SilcClientEntry pointers. */
760 client->ops->notify(client, conn, type, NULL, clients, clients_count);
762 for (i = 0; i < clients_count; i++) {
763 /* Remove client from all channels */
764 client_entry = clients[i];
765 if (client_entry == conn->local_entry)
768 silc_client_remove_from_channels(client, conn, client_entry);
769 silc_client_del_client(client, conn, client_entry);
781 silc_notify_payload_free(payload);
782 silc_free(client_id);
783 silc_free(channel_id);