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"
28 /* Called when notify is received and some async operation (such as command)
29 is required before processing the notify message. This calls again the
30 silc_client_notify_by_server and reprocesses the original notify packet. */
32 static void silc_client_notify_by_server_pending(void *context, void *context2)
34 SilcPacketContext *p = (SilcPacketContext *)context;
35 silc_client_notify_by_server(p->context, p->sock, p);
36 silc_socket_free(p->sock);
39 /* Destructor for the pending command callback */
41 static void silc_client_notify_by_server_destructor(void *context)
43 silc_packet_context_free((SilcPacketContext *)context);
46 /* Resolve client information from server by Client ID. */
48 static void silc_client_notify_by_server_resolve(SilcClient client,
49 SilcClientConnection conn,
50 SilcPacketContext *packet,
51 SilcClientID *client_id)
53 SilcPacketContext *p = silc_packet_context_dup(packet);
54 SilcBuffer idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
56 p->context = (void *)client;
57 p->sock = silc_socket_dup(conn->sock);
59 silc_client_send_command(client, conn, SILC_COMMAND_WHOIS, ++conn->cmd_ident,
60 1, 3, idp->data, idp->len);
61 silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
62 silc_client_notify_by_server_destructor,
63 silc_client_notify_by_server_pending, p);
64 silc_buffer_free(idp);
67 /* Received notify message from server */
69 void silc_client_notify_by_server(SilcClient client,
70 SilcSocketConnection sock,
71 SilcPacketContext *packet)
73 SilcBuffer buffer = packet->buffer;
74 SilcClientConnection conn = (SilcClientConnection)sock->user_data;
75 SilcNotifyPayload payload;
77 SilcArgumentPayload args;
80 SilcClientID *client_id = NULL;
81 SilcChannelID *channel_id = NULL;
82 SilcServerID *server_id = NULL;
83 SilcClientEntry client_entry;
84 SilcClientEntry client_entry2;
85 SilcChannelEntry channel;
87 SilcServerEntry server;
88 SilcIDCacheEntry id_cache = NULL;
92 payload = silc_notify_payload_parse(buffer);
96 type = silc_notify_get_type(payload);
97 args = silc_notify_get_args(payload);
102 case SILC_NOTIFY_TYPE_NONE:
103 /* Notify application */
104 client->ops->notify(client, conn, type,
105 silc_argument_get_arg_type(args, 1, NULL));
108 case SILC_NOTIFY_TYPE_INVITE:
110 * Someone invited me to a channel. Find Client and Channel entries
111 * for the application.
115 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
119 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
123 /* Get the channel entry */
125 if (silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
127 channel = (SilcChannelEntry)id_cache->context;
129 /* Get sender Client ID */
130 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
134 client_id = silc_id_payload_parse_id(tmp, tmp_len);
138 /* Find Client entry and if not found query it */
139 client_entry = silc_client_get_client_by_id(client, conn, client_id);
141 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
145 /* Get the channel name */
146 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
150 /* Notify application */
151 client->ops->notify(client, conn, type, channel, tmp, client_entry);
154 case SILC_NOTIFY_TYPE_JOIN:
156 * Someone has joined to a channel. Get their ID and nickname and
157 * cache them for later use.
161 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
165 client_id = silc_id_payload_parse_id(tmp, tmp_len);
169 /* Find Client entry and if not found query it */
170 client_entry = silc_client_get_client_by_id(client, conn, client_id);
172 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
176 /* If nickname or username hasn't been resolved, do so */
177 if (!client_entry->nickname || !client_entry->username) {
178 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
183 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
187 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
191 /* Get channel entry */
192 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
196 channel = (SilcChannelEntry)id_cache->context;
198 /* Add client to channel */
199 if (client_entry != conn->local_entry) {
200 chu = silc_calloc(1, sizeof(*chu));
201 chu->client = client_entry;
202 silc_list_add(channel->clients, chu);
205 /* Notify application. The channel entry is sent last as this notify
206 is for channel but application don't know it from the arguments
208 client->ops->notify(client, conn, type, client_entry, channel);
211 case SILC_NOTIFY_TYPE_LEAVE:
213 * Someone has left a channel. We will remove it from the channel but
214 * we'll keep it in the cache in case we'll need it later.
218 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
222 client_id = silc_id_payload_parse_id(tmp, tmp_len);
226 /* Find Client entry */
228 silc_client_get_client_by_id(client, conn, client_id);
232 /* Get channel entry */
233 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
237 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
241 channel = (SilcChannelEntry)id_cache->context;
243 /* Remove client from channel */
244 silc_list_start(channel->clients);
245 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
246 if (chu->client == client_entry) {
247 silc_list_del(channel->clients, chu);
253 /* Notify application. The channel entry is sent last as this notify
254 is for channel but application don't know it from the arguments
256 client->ops->notify(client, conn, type, client_entry, channel);
259 case SILC_NOTIFY_TYPE_SIGNOFF:
261 * Someone left SILC. We'll remove it from all channels and from cache.
265 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
269 client_id = silc_id_payload_parse_id(tmp, tmp_len);
273 /* Find Client entry */
275 silc_client_get_client_by_id(client, conn, client_id);
279 /* Remove from all channels */
280 silc_client_remove_from_channels(client, conn, client_entry);
282 /* Remove from cache */
283 silc_idcache_del_by_context(conn->client_cache, client_entry);
285 /* Get signoff message */
286 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
290 /* Notify application */
291 client->ops->notify(client, conn, type, client_entry, tmp);
294 silc_client_del_client_entry(client, client_entry);
297 case SILC_NOTIFY_TYPE_TOPIC_SET:
299 * Someone set the topic on a channel.
303 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
307 client_id = silc_id_payload_parse_id(tmp, tmp_len);
311 /* Find Client entry */
313 silc_client_get_client_by_id(client, conn, client_id);
318 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
322 /* Get channel entry */
323 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
327 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
331 channel = (SilcChannelEntry)id_cache->context;
333 /* Notify application. The channel entry is sent last as this notify
334 is for channel but application don't know it from the arguments
336 client->ops->notify(client, conn, type, client_entry, tmp, channel);
339 case SILC_NOTIFY_TYPE_NICK_CHANGE:
341 * Someone changed their nickname. If we don't have entry for the new
342 * ID we will query it and return here after it's done. After we've
343 * returned we fetch the old entry and free it and notify the
347 /* Get old Client ID */
348 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
352 client_id = silc_id_payload_parse_id(tmp, tmp_len);
357 if (SILC_ID_CLIENT_COMPARE(client_id, conn->local_id))
360 /* Find old Client entry */
361 client_entry = silc_client_get_client_by_id(client, conn, client_id);
364 silc_free(client_id);
366 client_entry->valid = FALSE;
368 /* Get new Client ID */
369 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
373 client_id = silc_id_payload_parse_id(tmp, tmp_len);
377 /* Find Client entry and if not found resolve it */
378 client_entry2 = silc_client_get_client_by_id(client, conn, client_id);
379 if (!client_entry2) {
380 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
384 /* Remove the old from cache */
385 silc_idcache_del_by_context(conn->client_cache, client_entry);
387 /* Replace old ID entry with new one on all channels. */
388 silc_client_replace_from_channels(client, conn, client_entry,
391 /* Notify application */
392 client->ops->notify(client, conn, type, client_entry, client_entry2);
395 silc_client_del_client_entry(client, client_entry);
398 case SILC_NOTIFY_TYPE_CMODE_CHANGE:
400 * Someone changed a channel mode
404 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
408 idp = silc_id_payload_parse_data(tmp, tmp_len);
412 /* Find Client entry */
413 if (silc_id_payload_get_type(idp) == SILC_ID_CLIENT) {
414 client_id = silc_id_payload_parse_id(tmp, tmp_len);
416 silc_id_payload_free(idp);
420 client_entry = silc_client_get_client_by_id(client, conn, client_id);
422 silc_id_payload_free(idp);
426 server_id = silc_id_payload_parse_id(tmp, tmp_len);
428 silc_id_payload_free(idp);
432 server = silc_client_get_server_by_id(client, conn, server_id);
434 silc_id_payload_free(idp);
435 silc_free(server_id);
439 /* Save the pointer to the client_entry pointer */
440 client_entry = (SilcClientEntry)server;
441 silc_free(server_id);
445 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
447 silc_id_payload_free(idp);
451 SILC_GET32_MSB(mode, tmp);
453 /* Get channel entry */
454 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
457 silc_id_payload_free(idp);
460 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
462 silc_id_payload_free(idp);
466 channel = (SilcChannelEntry)id_cache->context;
468 /* Save the new mode */
469 channel->mode = mode;
472 tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
474 unsigned char hash[32];
477 silc_hmac_free(channel->hmac);
478 if (!silc_hmac_alloc(tmp, NULL, &channel->hmac))
481 silc_hash_make(channel->hmac->hash, channel->key, channel->key_len / 8,
483 silc_hmac_set_key(channel->hmac, hash,
484 silc_hash_len(channel->hmac->hash));
485 memset(hash, 0, sizeof(hash));
488 /* Notify application. The channel entry is sent last as this notify
489 is for channel but application don't know it from the arguments
491 client->ops->notify(client, conn, type, silc_id_payload_get_type(idp),
492 client_entry, mode, NULL, tmp, channel);
494 silc_id_payload_free(idp);
497 case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
499 * Someone changed user's mode on a channel
503 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
507 client_id = silc_id_payload_parse_id(tmp, tmp_len);
511 /* Find Client entry */
512 client_entry = silc_client_get_client_by_id(client, conn, client_id);
514 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
519 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
523 SILC_GET32_MSB(mode, tmp);
525 /* Get target Client ID */
526 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
530 silc_free(client_id);
531 client_id = silc_id_payload_parse_id(tmp, tmp_len);
535 /* Find target Client entry */
537 silc_client_get_client_by_id(client, conn, client_id);
541 /* Get channel entry */
542 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
546 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
550 channel = (SilcChannelEntry)id_cache->context;
553 silc_list_start(channel->clients);
554 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
555 if (chu->client == client_entry) {
561 /* Notify application. The channel entry is sent last as this notify
562 is for channel but application don't know it from the arguments
564 client->ops->notify(client, conn, type, client_entry, mode,
565 client_entry2, channel);
568 case SILC_NOTIFY_TYPE_MOTD:
570 * Received Message of the day
574 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
578 /* Notify application */
579 client->ops->notify(client, conn, type, tmp);
582 case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
584 * Router has enforced a new ID to a channel. Let's change the old
585 * ID to the one provided here.
589 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
592 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
596 /* Get the channel entry */
597 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
601 channel = (SilcChannelEntry)id_cache->context;
603 SILC_LOG_DEBUG(("Old Channel ID id(%s)",
604 silc_id_render(channel->id, SILC_ID_CHANNEL)));
606 /* Free the old ID */
607 silc_free(channel->id);
610 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
613 channel->id = silc_id_payload_parse_id(tmp, tmp_len);
617 SILC_LOG_DEBUG(("New Channel ID id(%s)",
618 silc_id_render(channel->id, SILC_ID_CHANNEL)));
620 /* Remove the old cache entry and create a new one */
621 silc_idcache_del_by_context(conn->channel_cache, channel);
622 silc_idcache_add(conn->channel_cache, channel->channel_name,
623 channel->id, channel, FALSE);
625 /* Notify application */
626 client->ops->notify(client, conn, type, channel, channel);
629 case SILC_NOTIFY_TYPE_KICKED:
631 * A client (maybe me) was kicked from a channel
635 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
639 client_id = silc_id_payload_parse_id(tmp, tmp_len);
643 /* Find Client entry */
644 client_entry = silc_client_get_client_by_id(client, conn, client_id);
648 /* Get channel entry */
649 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
653 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
657 channel = (SilcChannelEntry)id_cache->context;
660 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
662 /* Notify application. The channel entry is sent last as this notify
663 is for channel but application don't know it from the arguments
665 client->ops->notify(client, conn, type, client_entry, tmp, channel);
667 /* If I was kicked from channel, remove the channel */
668 if (client_entry == conn->local_entry) {
669 if (conn->current_channel == channel)
670 conn->current_channel = NULL;
671 silc_idcache_del_by_id(conn->channel_cache, channel->id);
672 silc_free(channel->channel_name);
673 silc_free(channel->id);
674 silc_free(channel->key);
675 silc_cipher_free(channel->channel_key);
680 case SILC_NOTIFY_TYPE_KILLED:
682 * A client (maybe me) was killed from the network.
686 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
690 client_id = silc_id_payload_parse_id(tmp, tmp_len);
694 /* Find Client entry */
695 client_entry = silc_client_get_client_by_id(client, conn, client_id);
700 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
702 /* Notify application. */
703 client->ops->notify(client, conn, type, client_entry, tmp);
705 if (client_entry != conn->local_entry) {
706 /* Remove client from all channels */
707 silc_client_remove_from_channels(client, conn, client_entry);
708 silc_client_del_client(client, conn, client_entry);
713 case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
716 * A server quit the SILC network and some clients must be removed
717 * from channels as they quit as well.
719 SilcClientEntry *clients = NULL;
720 uint32 clients_count = 0;
723 for (i = 1; i < silc_argument_get_arg_num(args); i++) {
725 tmp = silc_argument_get_arg_type(args, i + 1, &tmp_len);
727 client_id = silc_id_payload_parse_id(tmp, tmp_len);
731 /* Get the client entry */
732 client_entry = silc_client_get_client_by_id(client, conn, client_id);
734 clients = silc_realloc(clients, sizeof(*clients) *
735 (clients_count + 1));
736 clients[clients_count] = client_entry;
739 silc_free(client_id);
744 /* Notify application. We don't keep server entries so the server
745 entry is returned as NULL. The client's are returned as array
746 of SilcClientEntry pointers. */
747 client->ops->notify(client, conn, type, NULL, clients, clients_count);
749 for (i = 0; i < clients_count; i++) {
750 /* Remove client from all channels */
751 client_entry = clients[i];
752 if (client_entry == conn->local_entry)
755 silc_client_remove_from_channels(client, conn, client_entry);
756 silc_client_del_client(client, conn, client_entry);
768 silc_notify_payload_free(payload);
769 silc_free(client_id);
770 silc_free(channel_id);