5 Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
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)
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 SilcClientEntry client_entry;
83 SilcClientEntry client_entry2;
84 SilcChannelEntry channel;
86 SilcIDCacheEntry id_cache = NULL;
90 payload = silc_notify_payload_parse(buffer);
94 type = silc_notify_get_type(payload);
95 args = silc_notify_get_args(payload);
100 case SILC_NOTIFY_TYPE_NONE:
101 /* Notify application */
102 client->ops->notify(client, conn, type,
103 silc_argument_get_arg_type(args, 1, NULL));
106 case SILC_NOTIFY_TYPE_INVITE:
108 * Someone invited me to a channel. Find Client and Channel entries
109 * for the application.
113 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
117 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
121 /* Get the channel entry */
123 if (silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
125 channel = (SilcChannelEntry)id_cache->context;
127 /* Get sender Client ID */
128 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
132 client_id = silc_id_payload_parse_id(tmp, tmp_len);
136 /* Find Client entry and if not found query it */
137 client_entry = silc_client_get_client_by_id(client, conn, client_id);
139 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
143 /* Get the channel name */
144 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
148 /* Notify application */
149 client->ops->notify(client, conn, type, channel, tmp, client_entry);
152 case SILC_NOTIFY_TYPE_JOIN:
154 * Someone has joined to a channel. Get their ID and nickname and
155 * cache them for later use.
159 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
163 client_id = silc_id_payload_parse_id(tmp, tmp_len);
167 /* Find Client entry and if not found query it */
168 client_entry = silc_client_get_client_by_id(client, conn, client_id);
170 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
174 /* If nickname or username hasn't been resolved, do so */
175 if (!client_entry->nickname || !client_entry->username) {
176 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
181 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
185 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
189 /* Get channel entry */
190 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
194 channel = (SilcChannelEntry)id_cache->context;
196 /* Add client to channel */
197 if (client_entry != conn->local_entry) {
198 chu = silc_calloc(1, sizeof(*chu));
199 chu->client = client_entry;
200 silc_list_add(channel->clients, chu);
203 /* XXX add support for multiple same nicks on same channel. Check
206 /* Notify application. The channel entry is sent last as this notify
207 is for channel but application don't know it from the arguments
209 client->ops->notify(client, conn, type, client_entry, channel);
212 case SILC_NOTIFY_TYPE_LEAVE:
214 * Someone has left a channel. We will remove it from the channel but
215 * we'll keep it in the cache in case we'll need it later.
219 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
223 client_id = silc_id_payload_parse_id(tmp, tmp_len);
227 /* Find Client entry */
229 silc_client_get_client_by_id(client, conn, client_id);
233 /* Get channel entry */
234 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
238 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
242 channel = (SilcChannelEntry)id_cache->context;
244 /* Remove client from channel */
245 silc_list_start(channel->clients);
246 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
247 if (chu->client == client_entry) {
248 silc_list_del(channel->clients, chu);
254 /* Notify application. The channel entry is sent last as this notify
255 is for channel but application don't know it from the arguments
257 client->ops->notify(client, conn, type, client_entry, channel);
260 case SILC_NOTIFY_TYPE_SIGNOFF:
262 * Someone left SILC. We'll remove it from all channels and from cache.
266 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
270 client_id = silc_id_payload_parse_id(tmp, tmp_len);
274 /* Find Client entry */
276 silc_client_get_client_by_id(client, conn, client_id);
280 /* Remove from all channels */
281 silc_client_remove_from_channels(client, conn, client_entry);
283 /* Remove from cache */
284 silc_idcache_del_by_context(conn->client_cache, client_entry);
286 /* Get signoff message */
287 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
291 /* Notify application */
292 client->ops->notify(client, conn, type, client_entry, tmp);
295 if (client_entry->nickname)
296 silc_free(client_entry->nickname);
297 if (client_entry->server)
298 silc_free(client_entry->server);
299 if (client_entry->id)
300 silc_free(client_entry->id);
301 if (client_entry->send_key)
302 silc_cipher_free(client_entry->send_key);
303 if (client_entry->receive_key)
304 silc_cipher_free(client_entry->receive_key);
307 case SILC_NOTIFY_TYPE_TOPIC_SET:
309 * Someone set the topic on a channel.
313 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
317 client_id = silc_id_payload_parse_id(tmp, tmp_len);
321 /* Find Client entry */
323 silc_client_get_client_by_id(client, conn, client_id);
328 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
332 /* Get channel entry */
333 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
337 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
341 channel = (SilcChannelEntry)id_cache->context;
343 /* Notify application. The channel entry is sent last as this notify
344 is for channel but application don't know it from the arguments
346 client->ops->notify(client, conn, type, client_entry, tmp, channel);
349 case SILC_NOTIFY_TYPE_NICK_CHANGE:
351 * Someone changed their nickname. If we don't have entry for the new
352 * ID we will query it and return here after it's done. After we've
353 * returned we fetch the old entry and free it and notify the
357 /* Get old Client ID */
358 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
362 client_id = silc_id_payload_parse_id(tmp, tmp_len);
367 if (SILC_ID_CLIENT_COMPARE(client_id, conn->local_id))
370 /* Find old Client entry */
371 client_entry = silc_client_get_client_by_id(client, conn, client_id);
374 silc_free(client_id);
376 /* Get new Client ID */
377 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
381 client_id = silc_id_payload_parse_id(tmp, tmp_len);
385 /* Find Client entry and if not found resolve it */
386 client_entry2 = silc_client_get_client_by_id(client, conn, client_id);
387 if (!client_entry2) {
388 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
392 /* Remove the old from cache */
393 silc_idcache_del_by_context(conn->client_cache, client_entry);
395 /* Replace old ID entry with new one on all channels. */
396 silc_client_replace_from_channels(client, conn, client_entry,
399 /* Notify application */
400 client->ops->notify(client, conn, type, client_entry, client_entry2);
403 if (client_entry->nickname)
404 silc_free(client_entry->nickname);
405 if (client_entry->server)
406 silc_free(client_entry->server);
407 if (client_entry->id)
408 silc_free(client_entry->id);
409 if (client_entry->send_key)
410 silc_cipher_free(client_entry->send_key);
411 if (client_entry->receive_key)
412 silc_cipher_free(client_entry->receive_key);
413 silc_free(client_entry);
416 case SILC_NOTIFY_TYPE_CMODE_CHANGE:
418 * Someone changed a channel mode
422 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
426 idp = silc_id_payload_parse_data(tmp, tmp_len);
430 /* Find Client entry */
431 if (silc_id_payload_get_type(idp) == SILC_ID_CLIENT) {
432 client_id = silc_id_payload_parse_id(tmp, tmp_len);
434 silc_id_payload_free(idp);
438 client_entry = silc_client_get_client_by_id(client, conn, client_id);
440 silc_id_payload_free(idp);
447 silc_id_payload_free(idp);
450 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
454 SILC_GET32_MSB(mode, tmp);
456 /* Get channel entry */
457 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
461 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
465 channel = (SilcChannelEntry)id_cache->context;
467 /* Save the new mode */
468 channel->mode = mode;
471 tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
473 unsigned char hash[32];
476 silc_hmac_free(channel->hmac);
477 if (!silc_hmac_alloc(tmp, NULL, &channel->hmac))
480 silc_hash_make(channel->hmac->hash, channel->key, channel->key_len / 8,
482 silc_hmac_set_key(channel->hmac, hash,
483 silc_hash_len(channel->hmac->hash));
484 memset(hash, 0, sizeof(hash));
487 /* Notify application. The channel entry is sent last as this notify
488 is for channel but application don't know it from the arguments
490 client->ops->notify(client, conn, type, client_entry, mode, NULL,
494 case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
496 * Someone changed user's mode on a channel
500 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
504 client_id = silc_id_payload_parse_id(tmp, tmp_len);
508 /* Find Client entry */
510 silc_client_get_client_by_id(client, conn, client_id);
515 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
519 SILC_GET32_MSB(mode, tmp);
521 /* Get target Client ID */
522 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
526 silc_free(client_id);
527 client_id = silc_id_payload_parse_id(tmp, tmp_len);
531 /* Find target Client entry */
533 silc_client_get_client_by_id(client, conn, client_id);
537 /* Get channel entry */
538 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
542 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
546 channel = (SilcChannelEntry)id_cache->context;
549 silc_list_start(channel->clients);
550 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
551 if (chu->client == client_entry) {
557 /* Notify application. The channel entry is sent last as this notify
558 is for channel but application don't know it from the arguments
560 client->ops->notify(client, conn, type, client_entry, mode,
561 client_entry2, channel);
564 case SILC_NOTIFY_TYPE_MOTD:
566 * Received Message of the day
570 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
574 /* Notify application */
575 client->ops->notify(client, conn, type, tmp);
578 case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
580 * Router has enforced a new ID to a channel. Let's change the old
581 * ID to the one provided here.
585 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
588 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
592 /* Get the channel entry */
593 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
597 channel = (SilcChannelEntry)id_cache->context;
599 /* Free the old ID */
600 silc_free(channel->id);
603 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
606 channel->id = silc_id_payload_parse_id(tmp, tmp_len);
610 id_cache->id = (void *)channel->id;
612 /* Notify application */
613 client->ops->notify(client, conn, type, channel, channel);
616 case SILC_NOTIFY_TYPE_KICKED:
618 * A client (maybe me) was kicked from a channel
622 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
626 client_id = silc_id_payload_parse_id(tmp, tmp_len);
630 /* Find Client entry */
631 client_entry = silc_client_get_client_by_id(client, conn, client_id);
635 /* Get channel entry */
636 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
640 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
644 channel = (SilcChannelEntry)id_cache->context;
647 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
649 /* Notify application. The channel entry is sent last as this notify
650 is for channel but application don't know it from the arguments
652 client->ops->notify(client, conn, type, client_entry, tmp, channel);
654 /* If I was kicked from channel, remove the channel */
655 if (client_entry == conn->local_entry) {
656 if (conn->current_channel == channel)
657 conn->current_channel = NULL;
658 silc_idcache_del_by_id(conn->channel_cache, channel->id);
659 silc_free(channel->channel_name);
660 silc_free(channel->id);
661 silc_free(channel->key);
662 silc_cipher_free(channel->channel_key);
667 case SILC_NOTIFY_TYPE_KILLED:
669 * A client (maybe me) was killed from the network.
673 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
677 client_id = silc_id_payload_parse_id(tmp, tmp_len);
681 /* Find Client entry */
682 client_entry = silc_client_get_client_by_id(client, conn, client_id);
687 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
689 /* Notify application. */
690 client->ops->notify(client, conn, type, client_entry, tmp);
692 if (client_entry != conn->local_entry) {
693 /* Remove client from all channels */
694 silc_client_remove_from_channels(client, conn, client_entry);
695 silc_idcache_del_by_context(conn->client_cache, client_entry);
696 if (client_entry->nickname)
697 silc_free(client_entry->nickname);
698 if (client_entry->server)
699 silc_free(client_entry->server);
700 if (client_entry->id)
701 silc_free(client_entry->id);
702 if (client_entry->send_key)
703 silc_cipher_free(client_entry->send_key);
704 if (client_entry->receive_key)
705 silc_cipher_free(client_entry->receive_key);
706 silc_free(client_entry);
711 case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
714 * A server quit the SILC network and some clients must be removed
715 * from channels as they quit as well.
717 SilcClientEntry *clients = NULL;
718 uint32 clients_count = 0;
721 for (i = 1; i < silc_argument_get_arg_num(args); i++) {
723 tmp = silc_argument_get_arg_type(args, i + 1, &tmp_len);
725 client_id = silc_id_payload_parse_id(tmp, tmp_len);
729 /* Get the client entry */
730 client_entry = silc_client_get_client_by_id(client, conn, client_id);
732 clients = silc_realloc(clients, sizeof(*clients) *
733 (clients_count + 1));
734 clients[clients_count] = client_entry;
737 silc_free(client_id);
742 /* Notify application. We don't keep server entries so the server
743 entry is returned as NULL. The client's are returned as array
744 of SilcClientEntry pointers. */
745 client->ops->notify(client, conn, type, NULL, clients, clients_count);
747 for (i = 0; i < clients_count; i++) {
748 /* Remove client from all channels */
749 client_entry = clients[i];
750 if (client_entry == conn->local_entry)
753 silc_client_remove_from_channels(client, conn, client_entry);
754 silc_idcache_del_by_context(conn->client_cache, client_entry);
755 if (client_entry->nickname)
756 silc_free(client_entry->nickname);
757 if (client_entry->server)
758 silc_free(client_entry->server);
759 if (client_entry->id)
760 silc_free(client_entry->id);
761 if (client_entry->send_key)
762 silc_cipher_free(client_entry->send_key);
763 if (client_entry->receive_key)
764 silc_cipher_free(client_entry->receive_key);
765 silc_free(client_entry);
777 silc_notify_payload_free(payload);
779 silc_free(client_id);
781 silc_free(channel_id);