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 SilcClientCommandReplyContext reply =
42 (SilcClientCommandReplyContext)context2;
45 SilcCommandStatus status;
46 unsigned char *tmp = silc_argument_get_arg_type(reply->args, 1, NULL);
47 SILC_GET16_MSB(status, tmp);
48 if (status != SILC_STATUS_OK) {
49 silc_socket_free(res->sock);
54 silc_client_notify_by_server(res->context, res->sock, res->packet);
55 silc_socket_free(res->sock);
58 /* Destructor for the pending command callback */
60 static void silc_client_notify_by_server_destructor(void *context)
62 SilcClientNotifyResolve res = (SilcClientNotifyResolve)context;
63 silc_packet_context_free(res->packet);
67 /* Resolve client information from server by Client ID. */
69 static void silc_client_notify_by_server_resolve(SilcClient client,
70 SilcClientConnection conn,
71 SilcPacketContext *packet,
72 SilcClientID *client_id)
74 SilcClientNotifyResolve res = silc_calloc(1, sizeof(*res));
75 SilcBuffer idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
77 res->packet = silc_packet_context_dup(packet);
78 res->context = client;
79 res->sock = silc_socket_dup(conn->sock);
81 silc_client_send_command(client, conn, SILC_COMMAND_WHOIS, ++conn->cmd_ident,
82 1, 3, idp->data, idp->len);
83 silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
84 silc_client_notify_by_server_destructor,
85 silc_client_notify_by_server_pending, res);
86 silc_buffer_free(idp);
89 /* Received notify message from server */
91 void silc_client_notify_by_server(SilcClient client,
92 SilcSocketConnection sock,
93 SilcPacketContext *packet)
95 SilcBuffer buffer = packet->buffer;
96 SilcClientConnection conn = (SilcClientConnection)sock->user_data;
97 SilcNotifyPayload payload;
99 SilcArgumentPayload args;
102 SilcClientID *client_id = NULL;
103 SilcChannelID *channel_id = NULL;
104 SilcServerID *server_id = NULL;
105 SilcClientEntry client_entry;
106 SilcClientEntry client_entry2;
107 SilcChannelEntry channel;
109 SilcServerEntry server;
110 SilcIDCacheEntry id_cache = NULL;
112 uint32 tmp_len, mode;
114 payload = silc_notify_payload_parse(buffer);
118 type = silc_notify_get_type(payload);
119 args = silc_notify_get_args(payload);
124 case SILC_NOTIFY_TYPE_NONE:
125 /* Notify application */
126 client->ops->notify(client, conn, type,
127 silc_argument_get_arg_type(args, 1, NULL));
130 case SILC_NOTIFY_TYPE_INVITE:
132 * Someone invited me to a channel. Find Client and Channel entries
133 * for the application.
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 */
147 if (silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
149 channel = (SilcChannelEntry)id_cache->context;
151 /* Get sender Client ID */
152 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
156 client_id = silc_id_payload_parse_id(tmp, tmp_len);
160 /* Find Client entry and if not found query it */
161 client_entry = silc_client_get_client_by_id(client, conn, client_id);
163 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
167 /* Get the channel name */
168 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
172 /* Notify application */
173 client->ops->notify(client, conn, type, channel, tmp, client_entry);
176 case SILC_NOTIFY_TYPE_JOIN:
178 * Someone has joined to a channel. Get their ID and nickname and
179 * cache them for later use.
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);
210 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
214 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
218 /* Get channel entry */
219 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
223 channel = (SilcChannelEntry)id_cache->context;
225 /* Add client to channel */
226 if (client_entry != conn->local_entry) {
227 chu = silc_calloc(1, sizeof(*chu));
228 chu->client = client_entry;
229 silc_list_add(channel->clients, chu);
232 /* Notify application. The channel entry is sent last as this notify
233 is for channel but application don't know it from the arguments
235 client->ops->notify(client, conn, type, client_entry, channel);
238 case SILC_NOTIFY_TYPE_LEAVE:
240 * Someone has left a channel. We will remove it from the channel but
241 * we'll keep it in the cache in case we'll need it later.
245 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
249 client_id = silc_id_payload_parse_id(tmp, tmp_len);
253 /* Find Client entry */
255 silc_client_get_client_by_id(client, conn, client_id);
259 /* Get channel entry */
260 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
264 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
268 channel = (SilcChannelEntry)id_cache->context;
270 /* Remove client from channel */
271 silc_list_start(channel->clients);
272 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
273 if (chu->client == client_entry) {
274 silc_list_del(channel->clients, chu);
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->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.
292 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
296 client_id = silc_id_payload_parse_id(tmp, tmp_len);
300 /* Find Client entry */
302 silc_client_get_client_by_id(client, conn, client_id);
306 /* Remove from all channels */
307 silc_client_remove_from_channels(client, conn, client_entry);
309 /* Remove from cache */
310 silc_idcache_del_by_context(conn->client_cache, client_entry);
312 /* Get signoff message */
313 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
317 /* Notify application */
318 client->ops->notify(client, conn, type, client_entry, tmp);
321 silc_client_del_client_entry(client, conn, client_entry);
324 case SILC_NOTIFY_TYPE_TOPIC_SET:
326 * Someone set the topic on a channel.
330 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
334 client_id = silc_id_payload_parse_id(tmp, tmp_len);
338 /* Find Client entry */
340 silc_client_get_client_by_id(client, conn, client_id);
345 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
349 /* Get channel entry */
350 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
354 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
358 channel = (SilcChannelEntry)id_cache->context;
360 /* Notify application. The channel entry is sent last as this notify
361 is for channel but application don't know it from the arguments
363 client->ops->notify(client, conn, type, client_entry, tmp, channel);
366 case SILC_NOTIFY_TYPE_NICK_CHANGE:
368 * Someone changed their nickname. If we don't have entry for the new
369 * ID we will query it and return here after it's done. After we've
370 * returned we fetch the old entry and free it and notify the
374 /* Get old Client ID */
375 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
379 client_id = silc_id_payload_parse_id(tmp, tmp_len);
384 if (SILC_ID_CLIENT_COMPARE(client_id, conn->local_id))
387 /* Find old Client entry */
388 client_entry = silc_client_get_client_by_id(client, conn, client_id);
391 silc_free(client_id);
393 client_entry->valid = FALSE;
395 /* Get new Client ID */
396 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
400 client_id = silc_id_payload_parse_id(tmp, tmp_len);
404 /* Find Client entry and if not found resolve it */
405 client_entry2 = silc_client_get_client_by_id(client, conn, client_id);
406 if (!client_entry2) {
407 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
411 /* Remove the old from cache */
412 silc_idcache_del_by_context(conn->client_cache, client_entry);
414 /* Replace old ID entry with new one on all channels. */
415 silc_client_replace_from_channels(client, conn, client_entry,
418 /* Notify application */
419 client->ops->notify(client, conn, type, client_entry, client_entry2);
422 silc_client_del_client_entry(client, conn, client_entry);
425 case SILC_NOTIFY_TYPE_CMODE_CHANGE:
427 * Someone changed a channel mode
431 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
435 idp = silc_id_payload_parse_data(tmp, tmp_len);
439 /* Find Client entry */
440 if (silc_id_payload_get_type(idp) == SILC_ID_CLIENT) {
441 client_id = silc_id_payload_parse_id(tmp, tmp_len);
443 silc_id_payload_free(idp);
447 client_entry = silc_client_get_client_by_id(client, conn, client_id);
449 silc_id_payload_free(idp);
453 server_id = silc_id_payload_parse_id(tmp, tmp_len);
455 silc_id_payload_free(idp);
459 server = silc_client_get_server_by_id(client, conn, server_id);
461 silc_id_payload_free(idp);
462 silc_free(server_id);
466 /* Save the pointer to the client_entry pointer */
467 client_entry = (SilcClientEntry)server;
468 silc_free(server_id);
472 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
474 silc_id_payload_free(idp);
478 SILC_GET32_MSB(mode, tmp);
480 /* Get channel entry */
481 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
484 silc_id_payload_free(idp);
487 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
489 silc_id_payload_free(idp);
493 channel = (SilcChannelEntry)id_cache->context;
495 /* Save the new mode */
496 channel->mode = mode;
499 tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
501 unsigned char hash[32];
504 silc_hmac_free(channel->hmac);
505 if (!silc_hmac_alloc(tmp, NULL, &channel->hmac))
508 silc_hash_make(silc_hmac_get_hash(channel->hmac),
509 channel->key, channel->key_len / 8,
511 silc_hmac_set_key(channel->hmac, hash,
512 silc_hash_len(silc_hmac_get_hash(channel->hmac)));
513 memset(hash, 0, sizeof(hash));
516 /* Notify application. The channel entry is sent last as this notify
517 is for channel but application don't know it from the arguments
519 client->ops->notify(client, conn, type, silc_id_payload_get_type(idp),
520 client_entry, mode, NULL, tmp, channel);
522 silc_id_payload_free(idp);
525 case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
527 * Someone changed user's mode on a channel
531 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
535 client_id = silc_id_payload_parse_id(tmp, tmp_len);
539 /* Find Client entry */
540 client_entry = silc_client_get_client_by_id(client, conn, client_id);
542 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
547 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
551 SILC_GET32_MSB(mode, tmp);
553 /* Get target Client ID */
554 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
558 silc_free(client_id);
559 client_id = silc_id_payload_parse_id(tmp, tmp_len);
563 /* Find target Client entry */
565 silc_client_get_client_by_id(client, conn, client_id);
569 /* Get channel entry */
570 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
574 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
578 channel = (SilcChannelEntry)id_cache->context;
581 silc_list_start(channel->clients);
582 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
583 if (chu->client == client_entry) {
589 /* Notify application. The channel entry is sent last as this notify
590 is for channel but application don't know it from the arguments
592 client->ops->notify(client, conn, type, client_entry, mode,
593 client_entry2, channel);
596 case SILC_NOTIFY_TYPE_MOTD:
598 * Received Message of the day
602 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
606 /* Notify application */
607 client->ops->notify(client, conn, type, tmp);
610 case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
612 * Router has enforced a new ID to a channel. Let's change the old
613 * ID to the one provided here.
617 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
620 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
624 /* Get the channel entry */
625 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
629 channel = (SilcChannelEntry)id_cache->context;
631 SILC_LOG_DEBUG(("Old Channel ID id(%s)",
632 silc_id_render(channel->id, SILC_ID_CHANNEL)));
634 /* Free the old ID */
635 silc_free(channel->id);
638 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
641 channel->id = silc_id_payload_parse_id(tmp, tmp_len);
645 SILC_LOG_DEBUG(("New Channel ID id(%s)",
646 silc_id_render(channel->id, SILC_ID_CHANNEL)));
648 /* Remove the old cache entry and create a new one */
649 silc_idcache_del_by_context(conn->channel_cache, channel);
650 silc_idcache_add(conn->channel_cache, channel->channel_name,
651 channel->id, channel, FALSE);
653 /* Notify application */
654 client->ops->notify(client, conn, type, channel, channel);
657 case SILC_NOTIFY_TYPE_KICKED:
659 * A client (maybe me) was kicked from a channel
663 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
667 client_id = silc_id_payload_parse_id(tmp, tmp_len);
671 /* Find Client entry */
672 client_entry = silc_client_get_client_by_id(client, conn, client_id);
676 /* Get channel entry */
677 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
681 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
685 channel = (SilcChannelEntry)id_cache->context;
688 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
690 /* Notify application. The channel entry is sent last as this notify
691 is for channel but application don't know it from the arguments
693 client->ops->notify(client, conn, type, client_entry, tmp, channel);
695 /* If I was kicked from channel, remove the channel */
696 if (client_entry == conn->local_entry) {
697 if (conn->current_channel == channel)
698 conn->current_channel = NULL;
699 silc_idcache_del_by_id(conn->channel_cache, channel->id);
700 silc_free(channel->channel_name);
701 silc_free(channel->id);
702 silc_free(channel->key);
703 silc_cipher_free(channel->channel_key);
708 case SILC_NOTIFY_TYPE_KILLED:
710 * A client (maybe me) was killed from the network.
714 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
718 client_id = silc_id_payload_parse_id(tmp, tmp_len);
722 /* Find Client entry */
723 client_entry = silc_client_get_client_by_id(client, conn, client_id);
728 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
730 /* Notify application. */
731 client->ops->notify(client, conn, type, client_entry, tmp);
733 if (client_entry != conn->local_entry) {
734 /* Remove client from all channels */
735 silc_client_remove_from_channels(client, conn, client_entry);
736 silc_client_del_client(client, conn, client_entry);
741 case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
744 * A server quit the SILC network and some clients must be removed
745 * from channels as they quit as well.
747 SilcClientEntry *clients = NULL;
748 uint32 clients_count = 0;
751 for (i = 1; i < silc_argument_get_arg_num(args); i++) {
753 tmp = silc_argument_get_arg_type(args, i + 1, &tmp_len);
755 client_id = silc_id_payload_parse_id(tmp, tmp_len);
759 /* Get the client entry */
760 client_entry = silc_client_get_client_by_id(client, conn, client_id);
762 clients = silc_realloc(clients, sizeof(*clients) *
763 (clients_count + 1));
764 clients[clients_count] = client_entry;
767 silc_free(client_id);
772 /* Notify application. We don't keep server entries so the server
773 entry is returned as NULL. The client's are returned as array
774 of SilcClientEntry pointers. */
775 client->ops->notify(client, conn, type, NULL, clients, clients_count);
777 for (i = 0; i < clients_count; i++) {
778 /* Remove client from all channels */
779 client_entry = clients[i];
780 if (client_entry == conn->local_entry)
783 silc_client_remove_from_channels(client, conn, client_entry);
784 silc_client_del_client(client, conn, client_entry);
796 silc_notify_payload_free(payload);
797 silc_free(client_id);
798 silc_free(channel_id);