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 */
509 client_entry = silc_client_get_client_by_id(client, conn, client_id);
511 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
516 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
520 SILC_GET32_MSB(mode, tmp);
522 /* Get target Client ID */
523 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
527 silc_free(client_id);
528 client_id = silc_id_payload_parse_id(tmp, tmp_len);
532 /* Find target Client entry */
534 silc_client_get_client_by_id(client, conn, client_id);
538 /* Get channel entry */
539 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
543 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
547 channel = (SilcChannelEntry)id_cache->context;
550 silc_list_start(channel->clients);
551 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
552 if (chu->client == client_entry) {
558 /* Notify application. The channel entry is sent last as this notify
559 is for channel but application don't know it from the arguments
561 client->ops->notify(client, conn, type, client_entry, mode,
562 client_entry2, channel);
565 case SILC_NOTIFY_TYPE_MOTD:
567 * Received Message of the day
571 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
575 /* Notify application */
576 client->ops->notify(client, conn, type, tmp);
579 case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
581 * Router has enforced a new ID to a channel. Let's change the old
582 * ID to the one provided here.
586 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
589 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
593 /* Get the channel entry */
594 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
598 channel = (SilcChannelEntry)id_cache->context;
600 SILC_LOG_DEBUG(("Old Channel ID id(%s)",
601 silc_id_render(channel->id, SILC_ID_CHANNEL)));
603 /* Free the old ID */
604 silc_free(channel->id);
607 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
610 channel->id = silc_id_payload_parse_id(tmp, tmp_len);
614 SILC_LOG_DEBUG(("New Channel ID id(%s)",
615 silc_id_render(channel->id, SILC_ID_CHANNEL)));
617 /* Remove the old cache entry and create a new one */
618 silc_idcache_del_by_context(conn->channel_cache, channel);
619 silc_idcache_add(conn->channel_cache, channel->channel_name,
620 channel->id, channel, FALSE);
622 /* Notify application */
623 client->ops->notify(client, conn, type, channel, channel);
626 case SILC_NOTIFY_TYPE_KICKED:
628 * A client (maybe me) was kicked from a channel
632 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
636 client_id = silc_id_payload_parse_id(tmp, tmp_len);
640 /* Find Client entry */
641 client_entry = silc_client_get_client_by_id(client, conn, client_id);
645 /* Get channel entry */
646 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
650 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
654 channel = (SilcChannelEntry)id_cache->context;
657 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
659 /* Notify application. The channel entry is sent last as this notify
660 is for channel but application don't know it from the arguments
662 client->ops->notify(client, conn, type, client_entry, tmp, channel);
664 /* If I was kicked from channel, remove the channel */
665 if (client_entry == conn->local_entry) {
666 if (conn->current_channel == channel)
667 conn->current_channel = NULL;
668 silc_idcache_del_by_id(conn->channel_cache, channel->id);
669 silc_free(channel->channel_name);
670 silc_free(channel->id);
671 silc_free(channel->key);
672 silc_cipher_free(channel->channel_key);
677 case SILC_NOTIFY_TYPE_KILLED:
679 * A client (maybe me) was killed from the network.
683 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
687 client_id = silc_id_payload_parse_id(tmp, tmp_len);
691 /* Find Client entry */
692 client_entry = silc_client_get_client_by_id(client, conn, client_id);
697 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
699 /* Notify application. */
700 client->ops->notify(client, conn, type, client_entry, tmp);
702 if (client_entry != conn->local_entry) {
703 /* Remove client from all channels */
704 silc_client_remove_from_channels(client, conn, client_entry);
705 silc_idcache_del_by_context(conn->client_cache, client_entry);
706 if (client_entry->nickname)
707 silc_free(client_entry->nickname);
708 if (client_entry->server)
709 silc_free(client_entry->server);
710 if (client_entry->id)
711 silc_free(client_entry->id);
712 if (client_entry->send_key)
713 silc_cipher_free(client_entry->send_key);
714 if (client_entry->receive_key)
715 silc_cipher_free(client_entry->receive_key);
716 silc_free(client_entry);
721 case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
724 * A server quit the SILC network and some clients must be removed
725 * from channels as they quit as well.
727 SilcClientEntry *clients = NULL;
728 uint32 clients_count = 0;
731 for (i = 1; i < silc_argument_get_arg_num(args); i++) {
733 tmp = silc_argument_get_arg_type(args, i + 1, &tmp_len);
735 client_id = silc_id_payload_parse_id(tmp, tmp_len);
739 /* Get the client entry */
740 client_entry = silc_client_get_client_by_id(client, conn, client_id);
742 clients = silc_realloc(clients, sizeof(*clients) *
743 (clients_count + 1));
744 clients[clients_count] = client_entry;
747 silc_free(client_id);
752 /* Notify application. We don't keep server entries so the server
753 entry is returned as NULL. The client's are returned as array
754 of SilcClientEntry pointers. */
755 client->ops->notify(client, conn, type, NULL, clients, clients_count);
757 for (i = 0; i < clients_count; i++) {
758 /* Remove client from all channels */
759 client_entry = clients[i];
760 if (client_entry == conn->local_entry)
763 silc_client_remove_from_channels(client, conn, client_entry);
764 silc_idcache_del_by_context(conn->client_cache, client_entry);
765 if (client_entry->nickname)
766 silc_free(client_entry->nickname);
767 if (client_entry->server)
768 silc_free(client_entry->server);
769 if (client_entry->id)
770 silc_free(client_entry->id);
771 if (client_entry->send_key)
772 silc_cipher_free(client_entry->send_key);
773 if (client_entry->receive_key)
774 silc_cipher_free(client_entry->receive_key);
775 silc_free(client_entry);
787 silc_notify_payload_free(payload);
789 silc_free(client_id);
791 silc_free(channel_id);