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, 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 /* XXX add support for multiple same nicks on same channel. Check
208 /* Notify application. The channel entry is sent last as this notify
209 is for channel but application don't know it from the arguments
211 client->ops->notify(client, conn, type, client_entry, channel);
214 case SILC_NOTIFY_TYPE_LEAVE:
216 * Someone has left a channel. We will remove it from the channel but
217 * we'll keep it in the cache in case we'll need it later.
221 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
225 client_id = silc_id_payload_parse_id(tmp, tmp_len);
229 /* Find Client entry */
231 silc_client_get_client_by_id(client, conn, client_id);
235 /* Get channel entry */
236 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
240 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
244 channel = (SilcChannelEntry)id_cache->context;
246 /* Remove client from channel */
247 silc_list_start(channel->clients);
248 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
249 if (chu->client == client_entry) {
250 silc_list_del(channel->clients, chu);
256 /* Notify application. The channel entry is sent last as this notify
257 is for channel but application don't know it from the arguments
259 client->ops->notify(client, conn, type, client_entry, channel);
262 case SILC_NOTIFY_TYPE_SIGNOFF:
264 * Someone left SILC. We'll remove it from all channels and from cache.
268 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
272 client_id = silc_id_payload_parse_id(tmp, tmp_len);
276 /* Find Client entry */
278 silc_client_get_client_by_id(client, conn, client_id);
282 /* Remove from all channels */
283 silc_client_remove_from_channels(client, conn, client_entry);
285 /* Remove from cache */
286 silc_idcache_del_by_context(conn->client_cache, client_entry);
288 /* Get signoff message */
289 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
293 /* Notify application */
294 client->ops->notify(client, conn, type, client_entry, tmp);
297 silc_client_del_client_entry(client, client_entry);
300 case SILC_NOTIFY_TYPE_TOPIC_SET:
302 * Someone set the topic on a channel.
306 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
310 client_id = silc_id_payload_parse_id(tmp, tmp_len);
314 /* Find Client entry */
316 silc_client_get_client_by_id(client, conn, client_id);
321 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
325 /* Get channel entry */
326 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
330 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
334 channel = (SilcChannelEntry)id_cache->context;
336 /* Notify application. The channel entry is sent last as this notify
337 is for channel but application don't know it from the arguments
339 client->ops->notify(client, conn, type, client_entry, tmp, channel);
342 case SILC_NOTIFY_TYPE_NICK_CHANGE:
344 * Someone changed their nickname. If we don't have entry for the new
345 * ID we will query it and return here after it's done. After we've
346 * returned we fetch the old entry and free it and notify the
350 /* Get old Client ID */
351 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
355 client_id = silc_id_payload_parse_id(tmp, tmp_len);
360 if (SILC_ID_CLIENT_COMPARE(client_id, conn->local_id))
363 /* Find old Client entry */
364 client_entry = silc_client_get_client_by_id(client, conn, client_id);
367 silc_free(client_id);
369 /* Get new Client ID */
370 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
374 client_id = silc_id_payload_parse_id(tmp, tmp_len);
378 /* Find Client entry and if not found resolve it */
379 client_entry2 = silc_client_get_client_by_id(client, conn, client_id);
380 if (!client_entry2) {
381 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
385 /* Remove the old from cache */
386 silc_idcache_del_by_context(conn->client_cache, client_entry);
388 /* Replace old ID entry with new one on all channels. */
389 silc_client_replace_from_channels(client, conn, client_entry,
392 /* Notify application */
393 client->ops->notify(client, conn, type, client_entry, client_entry2);
396 silc_client_del_client_entry(client, client_entry);
399 case SILC_NOTIFY_TYPE_CMODE_CHANGE:
401 * Someone changed a channel mode
405 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
409 idp = silc_id_payload_parse_data(tmp, tmp_len);
413 /* Find Client entry */
414 if (silc_id_payload_get_type(idp) == SILC_ID_CLIENT) {
415 client_id = silc_id_payload_parse_id(tmp, tmp_len);
417 silc_id_payload_free(idp);
421 client_entry = silc_client_get_client_by_id(client, conn, client_id);
423 silc_id_payload_free(idp);
427 server_id = silc_id_payload_parse_id(tmp, tmp_len);
429 silc_id_payload_free(idp);
433 server = silc_client_get_server_by_id(client, conn, server_id);
435 silc_id_payload_free(idp);
436 silc_free(server_id);
440 /* Save the pointer to the client_entry pointer */
441 client_entry = (SilcClientEntry)server;
442 silc_free(server_id);
446 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
448 silc_id_payload_free(idp);
452 SILC_GET32_MSB(mode, tmp);
454 /* Get channel entry */
455 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
458 silc_id_payload_free(idp);
461 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
463 silc_id_payload_free(idp);
467 channel = (SilcChannelEntry)id_cache->context;
469 /* Save the new mode */
470 channel->mode = mode;
473 tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
475 unsigned char hash[32];
478 silc_hmac_free(channel->hmac);
479 if (!silc_hmac_alloc(tmp, NULL, &channel->hmac))
482 silc_hash_make(channel->hmac->hash, channel->key, channel->key_len / 8,
484 silc_hmac_set_key(channel->hmac, hash,
485 silc_hash_len(channel->hmac->hash));
486 memset(hash, 0, sizeof(hash));
489 /* Notify application. The channel entry is sent last as this notify
490 is for channel but application don't know it from the arguments
492 client->ops->notify(client, conn, type, silc_id_payload_get_type(idp),
493 client_entry, mode, NULL, tmp, channel);
495 silc_id_payload_free(idp);
498 case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
500 * Someone changed user's mode on a channel
504 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
508 client_id = silc_id_payload_parse_id(tmp, tmp_len);
512 /* Find Client entry */
513 client_entry = silc_client_get_client_by_id(client, conn, client_id);
515 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
520 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
524 SILC_GET32_MSB(mode, tmp);
526 /* Get target Client ID */
527 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
531 silc_free(client_id);
532 client_id = silc_id_payload_parse_id(tmp, tmp_len);
536 /* Find target Client entry */
538 silc_client_get_client_by_id(client, conn, client_id);
542 /* Get channel entry */
543 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
547 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
551 channel = (SilcChannelEntry)id_cache->context;
554 silc_list_start(channel->clients);
555 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
556 if (chu->client == client_entry) {
562 /* Notify application. The channel entry is sent last as this notify
563 is for channel but application don't know it from the arguments
565 client->ops->notify(client, conn, type, client_entry, mode,
566 client_entry2, channel);
569 case SILC_NOTIFY_TYPE_MOTD:
571 * Received Message of the day
575 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
579 /* Notify application */
580 client->ops->notify(client, conn, type, tmp);
583 case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
585 * Router has enforced a new ID to a channel. Let's change the old
586 * ID to the one provided here.
590 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
593 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
597 /* Get the channel entry */
598 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
602 channel = (SilcChannelEntry)id_cache->context;
604 SILC_LOG_DEBUG(("Old Channel ID id(%s)",
605 silc_id_render(channel->id, SILC_ID_CHANNEL)));
607 /* Free the old ID */
608 silc_free(channel->id);
611 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
614 channel->id = silc_id_payload_parse_id(tmp, tmp_len);
618 SILC_LOG_DEBUG(("New Channel ID id(%s)",
619 silc_id_render(channel->id, SILC_ID_CHANNEL)));
621 /* Remove the old cache entry and create a new one */
622 silc_idcache_del_by_context(conn->channel_cache, channel);
623 silc_idcache_add(conn->channel_cache, channel->channel_name,
624 channel->id, channel, FALSE);
626 /* Notify application */
627 client->ops->notify(client, conn, type, channel, channel);
630 case SILC_NOTIFY_TYPE_KICKED:
632 * A client (maybe me) was kicked from a channel
636 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
640 client_id = silc_id_payload_parse_id(tmp, tmp_len);
644 /* Find Client entry */
645 client_entry = silc_client_get_client_by_id(client, conn, client_id);
649 /* Get channel entry */
650 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
654 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
658 channel = (SilcChannelEntry)id_cache->context;
661 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
663 /* Notify application. The channel entry is sent last as this notify
664 is for channel but application don't know it from the arguments
666 client->ops->notify(client, conn, type, client_entry, tmp, channel);
668 /* If I was kicked from channel, remove the channel */
669 if (client_entry == conn->local_entry) {
670 if (conn->current_channel == channel)
671 conn->current_channel = NULL;
672 silc_idcache_del_by_id(conn->channel_cache, channel->id);
673 silc_free(channel->channel_name);
674 silc_free(channel->id);
675 silc_free(channel->key);
676 silc_cipher_free(channel->channel_key);
681 case SILC_NOTIFY_TYPE_KILLED:
683 * A client (maybe me) was killed from the network.
687 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
691 client_id = silc_id_payload_parse_id(tmp, tmp_len);
695 /* Find Client entry */
696 client_entry = silc_client_get_client_by_id(client, conn, client_id);
701 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
703 /* Notify application. */
704 client->ops->notify(client, conn, type, client_entry, tmp);
706 if (client_entry != conn->local_entry) {
707 /* Remove client from all channels */
708 silc_client_remove_from_channels(client, conn, client_entry);
709 silc_client_del_client(client, conn, client_entry);
714 case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
717 * A server quit the SILC network and some clients must be removed
718 * from channels as they quit as well.
720 SilcClientEntry *clients = NULL;
721 uint32 clients_count = 0;
724 for (i = 1; i < silc_argument_get_arg_num(args); i++) {
726 tmp = silc_argument_get_arg_type(args, i + 1, &tmp_len);
728 client_id = silc_id_payload_parse_id(tmp, tmp_len);
732 /* Get the client entry */
733 client_entry = silc_client_get_client_by_id(client, conn, client_id);
735 clients = silc_realloc(clients, sizeof(*clients) *
736 (clients_count + 1));
737 clients[clients_count] = client_entry;
740 silc_free(client_id);
745 /* Notify application. We don't keep server entries so the server
746 entry is returned as NULL. The client's are returned as array
747 of SilcClientEntry pointers. */
748 client->ops->notify(client, conn, type, NULL, clients, clients_count);
750 for (i = 0; i < clients_count; i++) {
751 /* Remove client from all channels */
752 client_entry = clients[i];
753 if (client_entry == conn->local_entry)
756 silc_client_remove_from_channels(client, conn, client_entry);
757 silc_client_del_client(client, conn, client_entry);
769 silc_notify_payload_free(payload);
771 silc_free(client_id);
773 silc_free(channel_id);