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);
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 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
226 channel = (SilcChannelEntry)id_cache->context;
228 /* Add client to channel */
229 if (client_entry != conn->local_entry) {
230 chu = silc_calloc(1, sizeof(*chu));
231 chu->client = client_entry;
232 silc_list_add(channel->clients, 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->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.
248 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
252 client_id = silc_id_payload_parse_id(tmp, tmp_len);
256 /* Find Client entry */
258 silc_client_get_client_by_id(client, conn, client_id);
262 /* Get channel entry */
263 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
267 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
271 channel = (SilcChannelEntry)id_cache->context;
273 /* Remove client from channel */
274 silc_list_start(channel->clients);
275 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
276 if (chu->client == client_entry) {
277 silc_list_del(channel->clients, chu);
283 /* Notify application. The channel entry is sent last as this notify
284 is for channel but application don't know it from the arguments
286 client->ops->notify(client, conn, type, client_entry, channel);
289 case SILC_NOTIFY_TYPE_SIGNOFF:
291 * Someone left SILC. We'll remove it from all channels and from cache.
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->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.
333 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
337 client_id = silc_id_payload_parse_id(tmp, tmp_len);
341 /* Find Client entry */
343 silc_client_get_client_by_id(client, conn, client_id);
348 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
352 /* Get channel entry */
353 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
357 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
361 channel = (SilcChannelEntry)id_cache->context;
363 /* Notify application. The channel entry is sent last as this notify
364 is for channel but application don't know it from the arguments
366 client->ops->notify(client, conn, type, client_entry, tmp, channel);
369 case SILC_NOTIFY_TYPE_NICK_CHANGE:
371 * Someone changed their nickname. If we don't have entry for the new
372 * ID we will query it and return here after it's done. After we've
373 * returned we fetch the old entry and free it and notify the
377 /* Get old Client ID */
378 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
382 client_id = silc_id_payload_parse_id(tmp, tmp_len);
387 if (SILC_ID_CLIENT_COMPARE(client_id, conn->local_id))
390 /* Find old Client entry */
391 client_entry = silc_client_get_client_by_id(client, conn, client_id);
394 silc_free(client_id);
396 client_entry->valid = FALSE;
398 /* Get new Client ID */
399 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
403 client_id = silc_id_payload_parse_id(tmp, tmp_len);
407 /* Find Client entry and if not found resolve it */
408 client_entry2 = silc_client_get_client_by_id(client, conn, client_id);
409 if (!client_entry2) {
410 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
413 if (client_entry2 != conn->local_entry)
414 silc_client_nickname_format(client, conn, client_entry2);
417 /* Remove the old from cache */
418 silc_idcache_del_by_context(conn->client_cache, client_entry);
420 /* Replace old ID entry with new one on all channels. */
421 silc_client_replace_from_channels(client, conn, client_entry,
424 /* Notify application */
425 client->ops->notify(client, conn, type, client_entry, client_entry2);
428 silc_client_del_client_entry(client, conn, client_entry);
431 case SILC_NOTIFY_TYPE_CMODE_CHANGE:
433 * Someone changed a channel mode
437 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
441 idp = silc_id_payload_parse_data(tmp, tmp_len);
445 /* Find Client entry */
446 if (silc_id_payload_get_type(idp) == SILC_ID_CLIENT) {
447 client_id = silc_id_payload_parse_id(tmp, tmp_len);
449 silc_id_payload_free(idp);
453 client_entry = silc_client_get_client_by_id(client, conn, client_id);
455 silc_id_payload_free(idp);
459 server_id = silc_id_payload_parse_id(tmp, tmp_len);
461 silc_id_payload_free(idp);
465 server = silc_client_get_server_by_id(client, conn, server_id);
467 silc_id_payload_free(idp);
468 silc_free(server_id);
472 /* Save the pointer to the client_entry pointer */
473 client_entry = (SilcClientEntry)server;
474 silc_free(server_id);
478 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
480 silc_id_payload_free(idp);
484 SILC_GET32_MSB(mode, tmp);
486 /* Get channel entry */
487 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
490 silc_id_payload_free(idp);
493 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
495 silc_id_payload_free(idp);
499 channel = (SilcChannelEntry)id_cache->context;
501 /* Save the new mode */
502 channel->mode = mode;
505 tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
507 unsigned char hash[32];
510 silc_hmac_free(channel->hmac);
511 if (!silc_hmac_alloc(tmp, NULL, &channel->hmac))
514 silc_hash_make(silc_hmac_get_hash(channel->hmac),
515 channel->key, channel->key_len / 8,
517 silc_hmac_set_key(channel->hmac, hash,
518 silc_hash_len(silc_hmac_get_hash(channel->hmac)));
519 memset(hash, 0, sizeof(hash));
522 /* Notify application. The channel entry is sent last as this notify
523 is for channel but application don't know it from the arguments
525 client->ops->notify(client, conn, type, silc_id_payload_get_type(idp),
526 client_entry, mode, NULL, tmp, channel);
528 silc_id_payload_free(idp);
531 case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
533 * Someone changed user's mode on a channel
537 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
541 client_id = silc_id_payload_parse_id(tmp, tmp_len);
545 /* Find Client entry */
546 client_entry = silc_client_get_client_by_id(client, conn, client_id);
548 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
553 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
557 SILC_GET32_MSB(mode, tmp);
559 /* Get target Client ID */
560 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
564 silc_free(client_id);
565 client_id = silc_id_payload_parse_id(tmp, tmp_len);
569 /* Find target Client entry */
571 silc_client_get_client_by_id(client, conn, client_id);
575 /* Get channel entry */
576 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
580 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
584 channel = (SilcChannelEntry)id_cache->context;
587 silc_list_start(channel->clients);
588 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
589 if (chu->client == client_entry) {
595 /* Notify application. The channel entry is sent last as this notify
596 is for channel but application don't know it from the arguments
598 client->ops->notify(client, conn, type, client_entry, mode,
599 client_entry2, channel);
602 case SILC_NOTIFY_TYPE_MOTD:
604 * Received Message of the day
608 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
612 /* Notify application */
613 client->ops->notify(client, conn, type, tmp);
616 case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
618 * Router has enforced a new ID to a channel. Let's change the old
619 * ID to the one provided here.
623 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
626 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
630 /* Get the channel entry */
631 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
635 channel = (SilcChannelEntry)id_cache->context;
637 SILC_LOG_DEBUG(("Old Channel ID id(%s)",
638 silc_id_render(channel->id, SILC_ID_CHANNEL)));
640 /* Free the old ID */
641 silc_free(channel->id);
644 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
647 channel->id = silc_id_payload_parse_id(tmp, tmp_len);
651 SILC_LOG_DEBUG(("New Channel ID id(%s)",
652 silc_id_render(channel->id, SILC_ID_CHANNEL)));
654 /* Remove the old cache entry and create a new one */
655 silc_idcache_del_by_context(conn->channel_cache, channel);
656 silc_idcache_add(conn->channel_cache, channel->channel_name,
657 channel->id, channel, FALSE);
659 /* Notify application */
660 client->ops->notify(client, conn, type, channel, channel);
663 case SILC_NOTIFY_TYPE_KICKED:
665 * A client (maybe me) was kicked from a channel
669 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
673 client_id = silc_id_payload_parse_id(tmp, tmp_len);
677 /* Find Client entry */
678 client_entry = silc_client_get_client_by_id(client, conn, client_id);
682 /* Get channel entry */
683 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
687 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
691 channel = (SilcChannelEntry)id_cache->context;
694 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
696 /* Notify application. The channel entry is sent last as this notify
697 is for channel but application don't know it from the arguments
699 client->ops->notify(client, conn, type, client_entry, tmp, channel);
701 /* If I was kicked from channel, remove the channel */
702 if (client_entry == conn->local_entry) {
703 if (conn->current_channel == channel)
704 conn->current_channel = NULL;
705 silc_idcache_del_by_id(conn->channel_cache, channel->id);
706 silc_free(channel->channel_name);
707 silc_free(channel->id);
708 silc_free(channel->key);
709 silc_cipher_free(channel->channel_key);
714 case SILC_NOTIFY_TYPE_KILLED:
716 * A client (maybe me) was killed from the network.
720 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
724 client_id = silc_id_payload_parse_id(tmp, tmp_len);
728 /* Find Client entry */
729 client_entry = silc_client_get_client_by_id(client, conn, client_id);
734 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
736 /* Notify application. */
737 client->ops->notify(client, conn, type, client_entry, tmp);
739 if (client_entry != conn->local_entry) {
740 /* Remove client from all channels */
741 silc_client_remove_from_channels(client, conn, client_entry);
742 silc_client_del_client(client, conn, client_entry);
747 case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
750 * A server quit the SILC network and some clients must be removed
751 * from channels as they quit as well.
753 SilcClientEntry *clients = NULL;
754 uint32 clients_count = 0;
757 for (i = 1; i < silc_argument_get_arg_num(args); i++) {
759 tmp = silc_argument_get_arg_type(args, i + 1, &tmp_len);
761 client_id = silc_id_payload_parse_id(tmp, tmp_len);
765 /* Get the client entry */
766 client_entry = silc_client_get_client_by_id(client, conn, client_id);
768 clients = silc_realloc(clients, sizeof(*clients) *
769 (clients_count + 1));
770 clients[clients_count] = client_entry;
773 silc_free(client_id);
778 /* Notify application. We don't keep server entries so the server
779 entry is returned as NULL. The client's are returned as array
780 of SilcClientEntry pointers. */
781 client->ops->notify(client, conn, type, NULL, clients, clients_count);
783 for (i = 0; i < clients_count; i++) {
784 /* Remove client from all channels */
785 client_entry = clients[i];
786 if (client_entry == conn->local_entry)
789 silc_client_remove_from_channels(client, conn, client_entry);
790 silc_client_del_client(client, conn, client_entry);
802 silc_notify_payload_free(payload);
803 silc_free(client_id);
804 silc_free(channel_id);