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);
38 /* Destructor for the pending command callback */
40 static void silc_client_notify_by_server_destructor(void *context)
42 silc_packet_context_free((SilcPacketContext *)context);
45 /* Resolve client information from server by Client ID. */
47 static void silc_client_notify_by_server_resolve(SilcClient client,
48 SilcClientConnection conn,
49 SilcPacketContext *packet,
50 SilcClientID *client_id)
52 SilcPacketContext *p = silc_packet_context_dup(packet);
53 SilcBuffer idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
55 p->context = (void *)client;
58 silc_client_send_command(client, conn, SILC_COMMAND_WHOIS, ++conn->cmd_ident,
59 1, 3, idp->data, idp->len);
60 silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
61 silc_client_notify_by_server_destructor,
62 silc_client_notify_by_server_pending, p);
63 silc_buffer_free(idp);
66 /* Received notify message from server */
68 void silc_client_notify_by_server(SilcClient client,
69 SilcSocketConnection sock,
70 SilcPacketContext *packet)
72 SilcBuffer buffer = packet->buffer;
73 SilcClientConnection conn = (SilcClientConnection)sock->user_data;
74 SilcNotifyPayload payload;
76 SilcArgumentPayload args;
79 SilcClientID *client_id = NULL;
80 SilcChannelID *channel_id = NULL;
81 SilcClientEntry client_entry;
82 SilcClientEntry client_entry2;
83 SilcChannelEntry channel;
85 SilcIDCacheEntry id_cache = NULL;
87 unsigned int tmp_len, mode;
89 payload = silc_notify_payload_parse(buffer);
93 type = silc_notify_get_type(payload);
94 args = silc_notify_get_args(payload);
99 case SILC_NOTIFY_TYPE_NONE:
100 /* Notify application */
101 client->ops->notify(client, conn, type,
102 silc_argument_get_arg_type(args, 1, NULL));
105 case SILC_NOTIFY_TYPE_INVITE:
107 * Someone invited me to a channel. Find Client and Channel entries
108 * for the application.
112 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
116 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
120 /* Get the channel entry */
122 if (silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
123 SILC_ID_CHANNEL, &id_cache))
124 channel = (SilcChannelEntry)id_cache->context;
126 /* Get sender Client ID */
127 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
131 client_id = silc_id_payload_parse_id(tmp, tmp_len);
135 /* Find Client entry and if not found query it */
136 client_entry = silc_client_get_client_by_id(client, conn, client_id);
138 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
142 /* Get the channel name */
143 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
147 /* Notify application */
148 client->ops->notify(client, conn, type, channel, tmp, client_entry);
151 case SILC_NOTIFY_TYPE_JOIN:
153 * Someone has joined to a channel. Get their ID and nickname and
154 * cache them for later use.
158 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
162 client_id = silc_id_payload_parse_id(tmp, tmp_len);
166 /* Find Client entry and if not found query it */
167 client_entry = silc_client_get_client_by_id(client, conn, client_id);
169 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
173 /* If nickname or username hasn't been resolved, do so */
174 if (!client_entry->nickname || !client_entry->username) {
175 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
180 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
184 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
188 /* Get channel entry */
189 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
190 SILC_ID_CHANNEL, &id_cache))
193 channel = (SilcChannelEntry)id_cache->context;
195 /* Add client to channel */
196 if (client_entry != conn->local_entry) {
197 chu = silc_calloc(1, sizeof(*chu));
198 chu->client = client_entry;
199 silc_list_add(channel->clients, chu);
202 /* XXX add support for multiple same nicks on same channel. Check
205 /* Notify application. The channel entry is sent last as this notify
206 is for channel but application don't know it from the arguments
208 client->ops->notify(client, conn, type, client_entry, channel);
211 case SILC_NOTIFY_TYPE_LEAVE:
213 * Someone has left a channel. We will remove it from the channel but
214 * we'll keep it in the cache in case we'll need it later.
218 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
222 client_id = silc_id_payload_parse_id(tmp, tmp_len);
226 /* Find Client entry */
228 silc_client_get_client_by_id(client, conn, client_id);
232 /* Get channel entry */
233 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
237 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
238 SILC_ID_CHANNEL, &id_cache))
241 channel = (SilcChannelEntry)id_cache->context;
243 /* Remove client from channel */
244 silc_list_start(channel->clients);
245 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
246 if (chu->client == client_entry) {
247 silc_list_del(channel->clients, chu);
253 /* Notify application. The channel entry is sent last as this notify
254 is for channel but application don't know it from the arguments
256 client->ops->notify(client, conn, type, client_entry, channel);
259 case SILC_NOTIFY_TYPE_SIGNOFF:
261 * Someone left SILC. We'll remove it from all channels and from cache.
265 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
269 client_id = silc_id_payload_parse_id(tmp, tmp_len);
273 /* Find Client entry */
275 silc_client_get_client_by_id(client, conn, client_id);
279 /* Remove from all channels */
280 silc_client_remove_from_channels(client, conn, client_entry);
282 /* Remove from cache */
283 silc_idcache_del_by_id(conn->client_cache, SILC_ID_CLIENT,
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,
338 SILC_ID_CHANNEL, &id_cache))
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_id(conn->client_cache, SILC_ID_CLIENT,
396 /* Replace old ID entry with new one on all channels. */
397 silc_client_replace_from_channels(client, conn, client_entry,
400 /* Notify application */
401 client->ops->notify(client, conn, type, client_entry, client_entry2);
404 if (client_entry->nickname)
405 silc_free(client_entry->nickname);
406 if (client_entry->server)
407 silc_free(client_entry->server);
408 if (client_entry->id)
409 silc_free(client_entry->id);
410 if (client_entry->send_key)
411 silc_cipher_free(client_entry->send_key);
412 if (client_entry->receive_key)
413 silc_cipher_free(client_entry->receive_key);
414 silc_free(client_entry);
417 case SILC_NOTIFY_TYPE_CMODE_CHANGE:
419 * Someone changed a channel mode
423 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
427 idp = silc_id_payload_parse_data(tmp, tmp_len);
431 /* Find Client entry */
432 if (silc_id_payload_get_type(idp) == SILC_ID_CLIENT) {
433 client_id = silc_id_payload_parse_id(tmp, tmp_len);
435 silc_id_payload_free(idp);
439 client_entry = silc_client_get_client_by_id(client, conn, client_id);
441 silc_id_payload_free(idp);
448 silc_id_payload_free(idp);
451 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
455 SILC_GET32_MSB(mode, tmp);
457 /* Get channel entry */
458 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
462 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
463 SILC_ID_CHANNEL, &id_cache))
466 channel = (SilcChannelEntry)id_cache->context;
468 /* Save the new mode */
469 channel->mode = mode;
472 tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
474 unsigned char hash[32];
477 silc_hmac_free(channel->hmac);
478 if (!silc_hmac_alloc(tmp, NULL, &channel->hmac))
481 silc_hash_make(channel->hmac->hash, channel->key, channel->key_len / 8,
483 silc_hmac_set_key(channel->hmac, hash,
484 silc_hash_len(channel->hmac->hash));
485 memset(hash, 0, sizeof(hash));
488 /* Notify application. The channel entry is sent last as this notify
489 is for channel but application don't know it from the arguments
491 client->ops->notify(client, conn, type, client_entry, mode, NULL,
495 case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
497 * Someone changed user's mode on a channel
501 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
505 client_id = silc_id_payload_parse_id(tmp, tmp_len);
509 /* Find Client entry */
511 silc_client_get_client_by_id(client, conn, 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,
544 SILC_ID_CHANNEL, &id_cache))
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,
595 SILC_ID_CHANNEL, &id_cache))
598 channel = (SilcChannelEntry)id_cache->context;
600 /* Free the old ID */
601 silc_free(channel->id);
604 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
607 channel->id = silc_id_payload_parse_id(tmp, tmp_len);
611 id_cache->id = (void *)channel->id;
613 /* Notify application */
614 client->ops->notify(client, conn, type, channel, channel);
617 case SILC_NOTIFY_TYPE_KICKED:
619 * A client (maybe me) was kicked from a channel
623 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
627 client_id = silc_id_payload_parse_id(tmp, tmp_len);
631 /* Find Client entry */
632 client_entry = silc_client_get_client_by_id(client, conn, client_id);
636 /* Get channel entry */
637 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
641 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
642 SILC_ID_CHANNEL, &id_cache))
645 channel = (SilcChannelEntry)id_cache->context;
648 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
650 /* Notify application. The channel entry is sent last as this notify
651 is for channel but application don't know it from the arguments
653 client->ops->notify(client, conn, type, client_entry, tmp, channel);
655 /* If I was kicked from channel, remove the channel */
656 if (client_entry == conn->local_entry) {
657 if (conn->current_channel == channel)
658 conn->current_channel = NULL;
659 silc_idcache_del_by_id(conn->channel_cache,
660 SILC_ID_CHANNEL, channel->id);
661 silc_free(channel->channel_name);
662 silc_free(channel->id);
663 silc_free(channel->key);
664 silc_cipher_free(channel->channel_key);
669 case SILC_NOTIFY_TYPE_KILLED:
671 * A client (maybe me) was killed from the network.
675 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
679 client_id = silc_id_payload_parse_id(tmp, tmp_len);
683 /* Find Client entry */
684 client_entry = silc_client_get_client_by_id(client, conn, client_id);
689 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
691 /* Notify application. */
692 client->ops->notify(client, conn, type, client_entry, tmp);
694 if (client_entry != conn->local_entry) {
695 /* Remove client from all channels */
696 silc_client_remove_from_channels(client, conn, client_entry);
697 silc_idcache_del_by_id(conn->client_cache, SILC_ID_CLIENT,
699 if (client_entry->nickname)
700 silc_free(client_entry->nickname);
701 if (client_entry->server)
702 silc_free(client_entry->server);
703 if (client_entry->id)
704 silc_free(client_entry->id);
705 if (client_entry->send_key)
706 silc_cipher_free(client_entry->send_key);
707 if (client_entry->receive_key)
708 silc_cipher_free(client_entry->receive_key);
709 silc_free(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 unsigned int 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_idcache_del_by_id(conn->client_cache, SILC_ID_CLIENT,
759 if (client_entry->nickname)
760 silc_free(client_entry->nickname);
761 if (client_entry->server)
762 silc_free(client_entry->server);
763 if (client_entry->id)
764 silc_free(client_entry->id);
765 if (client_entry->send_key)
766 silc_cipher_free(client_entry->send_key);
767 if (client_entry->receive_key)
768 silc_cipher_free(client_entry->receive_key);
769 silc_free(client_entry);
781 silc_notify_payload_free(payload);
783 silc_free(client_id);
785 silc_free(channel_id);