5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 1997 - 2002 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; version 2 of the License.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
20 /* This file includes the Notify packet handling. Notify packets are
21 important packets sent by the server. They tell different things to the
22 client such as nick changes, mode changes etc. */
24 #include "clientlibincludes.h"
25 #include "client_internal.h"
28 SilcPacketContext *packet;
30 SilcSocketConnection sock;
31 } *SilcClientNotifyResolve;
33 /* Called when notify is received and some async operation (such as command)
34 is required before processing the notify message. This calls again the
35 silc_client_notify_by_server and reprocesses the original notify packet. */
37 static void silc_client_notify_by_server_pending(void *context, void *context2)
39 SilcClientNotifyResolve res = (SilcClientNotifyResolve)context;
40 SilcClientCommandReplyContext reply =
41 (SilcClientCommandReplyContext)context2;
43 SILC_LOG_DEBUG(("Start"));
46 SilcCommandStatus status;
47 unsigned char *tmp = silc_argument_get_arg_type(reply->args, 1, NULL);
48 SILC_GET16_MSB(status, tmp);
49 if (status != SILC_STATUS_OK)
53 silc_client_notify_by_server(res->context, res->sock, res->packet);
56 silc_socket_free(res->sock);
57 silc_packet_context_free(res->packet);
61 /* Resolve client information from server by Client ID. */
63 static void silc_client_notify_by_server_resolve(SilcClient client,
64 SilcClientConnection conn,
65 SilcPacketContext *packet,
66 SilcClientID *client_id)
68 SilcClientNotifyResolve res = silc_calloc(1, sizeof(*res));
69 SilcBuffer idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
71 res->packet = silc_packet_context_dup(packet);
72 res->context = client;
73 res->sock = silc_socket_dup(conn->sock);
75 silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
76 silc_client_command_reply_whois_i, 0,
78 silc_client_command_send(client, conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
79 1, 3, idp->data, idp->len);
80 silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
81 silc_client_notify_by_server_pending, res);
82 silc_buffer_free(idp);
85 /* Received notify message from server */
87 void silc_client_notify_by_server(SilcClient client,
88 SilcSocketConnection sock,
89 SilcPacketContext *packet)
91 SilcBuffer buffer = packet->buffer;
92 SilcClientConnection conn = (SilcClientConnection)sock->user_data;
93 SilcNotifyPayload payload;
95 SilcArgumentPayload args;
98 SilcClientID *client_id = NULL;
99 SilcChannelID *channel_id = NULL;
100 SilcServerID *server_id = NULL;
101 SilcClientEntry client_entry;
102 SilcClientEntry client_entry2;
103 SilcChannelEntry channel;
105 SilcServerEntry server;
106 SilcIDCacheEntry id_cache = NULL;
108 uint32 tmp_len, mode;
110 SILC_LOG_DEBUG(("Start"));
112 payload = silc_notify_payload_parse(buffer->data, buffer->len);
116 type = silc_notify_get_type(payload);
117 args = silc_notify_get_args(payload);
122 case SILC_NOTIFY_TYPE_NONE:
123 /* Notify application */
124 client->internal->ops->notify(client, conn, type,
125 silc_argument_get_arg_type(args, 1, NULL));
128 case SILC_NOTIFY_TYPE_INVITE:
130 * Someone invited me to a channel. Find Client and Channel entries
131 * for the application.
134 SILC_LOG_DEBUG(("Notify: INVITE"));
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->internal->ops->notify(client, conn, type, channel, tmp,
177 case SILC_NOTIFY_TYPE_JOIN:
179 * Someone has joined to a channel. Get their ID and nickname and
180 * cache them for later use.
183 SILC_LOG_DEBUG(("Notify: JOIN"));
186 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
190 client_id = silc_id_payload_parse_id(tmp, tmp_len);
194 /* Find Client entry and if not found query it */
195 client_entry = silc_client_get_client_by_id(client, conn, client_id);
197 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
201 /* If nickname or username hasn't been resolved, do so */
202 if (!client_entry->nickname || !client_entry->username) {
203 if (client_entry->status & SILC_CLIENT_STATUS_RESOLVING) {
204 client_entry->status &= ~SILC_CLIENT_STATUS_RESOLVING;
207 client_entry->status |= SILC_CLIENT_STATUS_RESOLVING;
208 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
211 if (client_entry != conn->local_entry)
212 silc_client_nickname_format(client, conn, client_entry);
216 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
220 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
224 /* Get channel entry */
225 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
229 channel = (SilcChannelEntry)id_cache->context;
231 /* Add client to channel */
232 if (client_entry != conn->local_entry) {
233 chu = silc_calloc(1, sizeof(*chu));
234 chu->client = client_entry;
235 silc_list_add(channel->clients, chu);
238 /* Notify application. The channel entry is sent last as this notify
239 is for channel but application don't know it from the arguments
241 client->internal->ops->notify(client, conn, type, client_entry, channel);
244 case SILC_NOTIFY_TYPE_LEAVE:
246 * Someone has left a channel. We will remove it from the channel but
247 * we'll keep it in the cache in case we'll need it later.
250 SILC_LOG_DEBUG(("Notify: LEAVE"));
253 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
257 client_id = silc_id_payload_parse_id(tmp, tmp_len);
261 /* Find Client entry */
263 silc_client_get_client_by_id(client, conn, client_id);
267 /* Get channel entry */
268 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
272 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
276 channel = (SilcChannelEntry)id_cache->context;
278 /* Remove client from channel */
279 silc_list_start(channel->clients);
280 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
281 if (chu->client == client_entry) {
282 silc_list_del(channel->clients, chu);
288 /* Notify application. The channel entry is sent last as this notify
289 is for channel but application don't know it from the arguments
291 client->internal->ops->notify(client, conn, type, client_entry, channel);
294 case SILC_NOTIFY_TYPE_SIGNOFF:
296 * Someone left SILC. We'll remove it from all channels and from cache.
299 SILC_LOG_DEBUG(("Notify: SIGNOFF"));
302 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
306 client_id = silc_id_payload_parse_id(tmp, tmp_len);
310 /* Find Client entry */
312 silc_client_get_client_by_id(client, conn, client_id);
316 /* Remove from all channels */
317 silc_client_remove_from_channels(client, conn, client_entry);
319 /* Remove from cache */
320 silc_idcache_del_by_context(conn->client_cache, client_entry);
322 /* Get signoff message */
323 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
327 /* Notify application */
328 client->internal->ops->notify(client, conn, type, client_entry, tmp);
331 silc_client_del_client_entry(client, conn, client_entry);
334 case SILC_NOTIFY_TYPE_TOPIC_SET:
336 * Someone set the topic on a channel.
339 SILC_LOG_DEBUG(("Notify: TOPIC_SET"));
342 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
346 idp = silc_id_payload_parse(tmp, tmp_len);
350 /* Find Client entry */
351 if (silc_id_payload_get_type(idp) == SILC_ID_CLIENT) {
352 client_id = silc_id_payload_parse_id(tmp, tmp_len);
354 silc_id_payload_free(idp);
358 /* Find Client entry */
360 silc_client_get_client_by_id(client, conn, client_id);
363 } else if (silc_id_payload_get_type(idp) == SILC_ID_SERVER) {
364 server_id = silc_id_payload_parse_id(tmp, tmp_len);
366 silc_id_payload_free(idp);
370 server = silc_client_get_server_by_id(client, conn, server_id);
372 silc_id_payload_free(idp);
373 silc_free(server_id);
377 /* Save the pointer to the client_entry pointer */
378 client_entry = (SilcClientEntry)server;
379 silc_free(server_id);
381 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
383 silc_id_payload_free(idp);
387 channel = silc_client_get_channel_by_id(client, conn, channel_id);
389 silc_id_payload_free(idp);
390 silc_free(channel_id);
394 /* Save the pointer to the client_entry pointer */
395 client_entry = (SilcClientEntry)channel;
396 silc_free(channel_id);
400 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
404 /* Get channel entry */
405 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
409 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
413 channel = (SilcChannelEntry)id_cache->context;
415 /* Notify application. The channel entry is sent last as this notify
416 is for channel but application don't know it from the arguments
418 client->internal->ops->notify(client, conn, type,
419 silc_id_payload_get_type(idp),
420 client_entry, tmp, channel);
422 silc_id_payload_free(idp);
425 case SILC_NOTIFY_TYPE_NICK_CHANGE:
427 * Someone changed their nickname. If we don't have entry for the new
428 * ID we will query it and return here after it's done. After we've
429 * returned we fetch the old entry and free it and notify the
433 SILC_LOG_DEBUG(("Notify: NICK_CHANGE"));
435 /* Get old Client ID */
436 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
440 client_id = silc_id_payload_parse_id(tmp, tmp_len);
445 if (SILC_ID_CLIENT_COMPARE(client_id, conn->local_id))
448 /* Find old Client entry */
449 client_entry = silc_client_get_client_by_id(client, conn, client_id);
452 silc_free(client_id);
454 client_entry->valid = FALSE;
456 /* Get new Client ID */
457 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
461 client_id = silc_id_payload_parse_id(tmp, tmp_len);
465 /* Find Client entry and if not found resolve it */
466 client_entry2 = silc_client_get_client_by_id(client, conn, client_id);
467 if (!client_entry2) {
468 /* Resolve the entry information */
469 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
471 /* Add the new entry even though we resolved it. This is because we
472 want to replace the old entry with the new entry here right now. */
474 silc_client_add_client(client, conn, NULL, NULL, NULL,
475 silc_id_dup(client_id, SILC_ID_CLIENT),
478 /* Replace old ID entry with new one on all channels. */
479 silc_client_replace_from_channels(client, conn, client_entry,
482 if (client_entry2 != conn->local_entry)
483 silc_client_nickname_format(client, conn, client_entry2);
485 /* Remove the old from cache */
486 silc_idcache_del_by_context(conn->client_cache, client_entry);
488 /* Replace old ID entry with new one on all channels. */
489 silc_client_replace_from_channels(client, conn, client_entry,
492 /* Notify application */
493 client->internal->ops->notify(client, conn, type,
494 client_entry, client_entry2);
497 silc_client_del_client_entry(client, conn, client_entry);
501 case SILC_NOTIFY_TYPE_CMODE_CHANGE:
503 * Someone changed a channel mode
506 SILC_LOG_DEBUG(("Notify: CMODE_CHANGE"));
509 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
513 idp = silc_id_payload_parse(tmp, tmp_len);
517 /* Find Client entry */
518 if (silc_id_payload_get_type(idp) == SILC_ID_CLIENT) {
519 client_id = silc_id_payload_parse_id(tmp, tmp_len);
521 silc_id_payload_free(idp);
525 client_entry = silc_client_get_client_by_id(client, conn, client_id);
527 silc_id_payload_free(idp);
531 server_id = silc_id_payload_parse_id(tmp, tmp_len);
533 silc_id_payload_free(idp);
537 server = silc_client_get_server_by_id(client, conn, server_id);
539 silc_id_payload_free(idp);
540 silc_free(server_id);
544 /* Save the pointer to the client_entry pointer */
545 client_entry = (SilcClientEntry)server;
546 silc_free(server_id);
550 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
552 silc_id_payload_free(idp);
556 SILC_GET32_MSB(mode, tmp);
558 /* Get channel entry */
559 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
562 silc_id_payload_free(idp);
565 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
567 silc_id_payload_free(idp);
571 channel = (SilcChannelEntry)id_cache->context;
573 /* Save the new mode */
574 channel->mode = mode;
577 tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
579 unsigned char hash[32];
582 silc_hmac_free(channel->hmac);
583 if (!silc_hmac_alloc(tmp, NULL, &channel->hmac))
586 silc_hash_make(silc_hmac_get_hash(channel->hmac),
587 channel->key, channel->key_len / 8,
589 silc_hmac_set_key(channel->hmac, hash,
590 silc_hash_len(silc_hmac_get_hash(channel->hmac)));
591 memset(hash, 0, sizeof(hash));
594 /* Notify application. The channel entry is sent last as this notify
595 is for channel but application don't know it from the arguments
597 client->internal->ops->notify(client, conn, type,
598 silc_id_payload_get_type(idp),
599 client_entry, mode, NULL, tmp, channel);
601 silc_id_payload_free(idp);
604 case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
606 * Someone changed user's mode on a channel
609 SILC_LOG_DEBUG(("Notify: CUMODE_CHANGE"));
612 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
616 client_id = silc_id_payload_parse_id(tmp, tmp_len);
620 /* Find Client entry */
621 client_entry = silc_client_get_client_by_id(client, conn, client_id);
623 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
628 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
632 SILC_GET32_MSB(mode, tmp);
634 /* Get target Client ID */
635 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
639 silc_free(client_id);
640 client_id = silc_id_payload_parse_id(tmp, tmp_len);
644 /* Find target Client entry */
646 silc_client_get_client_by_id(client, conn, client_id);
650 /* Get channel entry */
651 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
655 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
659 channel = (SilcChannelEntry)id_cache->context;
662 silc_list_start(channel->clients);
663 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
664 if (chu->client == client_entry) {
670 /* Notify application. The channel entry is sent last as this notify
671 is for channel but application don't know it from the arguments
673 client->internal->ops->notify(client, conn, type,
675 client_entry2, channel);
678 case SILC_NOTIFY_TYPE_MOTD:
680 * Received Message of the day
683 SILC_LOG_DEBUG(("Notify: MOTD"));
686 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
690 /* Notify application */
691 client->internal->ops->notify(client, conn, type, tmp);
694 case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
696 * Router has enforced a new ID to a channel. Let's change the old
697 * ID to the one provided here.
700 SILC_LOG_DEBUG(("Notify: CHANNEL_CHANGE"));
703 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
706 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
710 /* Get the channel entry */
711 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
715 channel = (SilcChannelEntry)id_cache->context;
717 SILC_LOG_DEBUG(("Old Channel ID id(%s)",
718 silc_id_render(channel->id, SILC_ID_CHANNEL)));
720 /* Remove the old channel entry */
721 silc_idcache_del_by_context(conn->channel_cache, channel);
723 /* Free the old ID */
724 silc_free(channel->id);
727 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
730 channel->id = silc_id_payload_parse_id(tmp, tmp_len);
734 SILC_LOG_DEBUG(("New Channel ID id(%s)",
735 silc_id_render(channel->id, SILC_ID_CHANNEL)));
737 /* Add the channel entry again to ID cache */
738 silc_idcache_add(conn->channel_cache, channel->channel_name,
739 channel->id, channel, 0, NULL);
741 /* Notify application */
742 client->internal->ops->notify(client, conn, type, channel, channel);
745 case SILC_NOTIFY_TYPE_KICKED:
747 * A client (maybe me) was kicked from a channel
750 SILC_LOG_DEBUG(("Notify: KICKED"));
753 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
757 client_id = silc_id_payload_parse_id(tmp, tmp_len);
761 /* Find Client entry */
762 client_entry = silc_client_get_client_by_id(client, conn, client_id);
766 /* Get channel entry */
767 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
771 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
775 channel = (SilcChannelEntry)id_cache->context;
778 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
782 client_id = silc_id_payload_parse_id(tmp, tmp_len);
786 /* Find kicker's client entry and if not found resolve it */
787 client_entry2 = silc_client_get_client_by_id(client, conn, client_id);
788 if (!client_entry2) {
789 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
792 if (client_entry2 != conn->local_entry)
793 silc_client_nickname_format(client, conn, client_entry2);
797 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
799 /* Notify application. The channel entry is sent last as this notify
800 is for channel but application don't know it from the arguments
802 client->internal->ops->notify(client, conn, type, client_entry, tmp,
803 client_entry2, channel);
805 /* If I was kicked from channel, remove the channel */
806 if (client_entry == conn->local_entry) {
807 if (conn->current_channel == channel)
808 conn->current_channel = NULL;
809 silc_idcache_del_by_id(conn->channel_cache, channel->id);
810 silc_free(channel->channel_name);
811 silc_free(channel->id);
812 silc_free(channel->key);
813 silc_cipher_free(channel->channel_key);
818 case SILC_NOTIFY_TYPE_KILLED:
820 * A client (maybe me) was killed from the network.
823 SILC_LOG_DEBUG(("Notify: KILLED"));
826 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
830 client_id = silc_id_payload_parse_id(tmp, tmp_len);
834 /* Find Client entry */
835 client_entry = silc_client_get_client_by_id(client, conn, client_id);
840 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
842 /* Notify application. */
843 client->internal->ops->notify(client, conn, type, client_entry, tmp);
845 if (client_entry != conn->local_entry)
846 /* Remove the client from all channels and free it */
847 silc_client_del_client(client, conn, client_entry);
851 case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
854 * A server quit the SILC network and some clients must be removed
855 * from channels as they quit as well.
857 SilcClientEntry *clients = NULL;
858 uint32 clients_count = 0;
861 SILC_LOG_DEBUG(("Notify: SIGNOFF"));
863 for (i = 1; i < silc_argument_get_arg_num(args); i++) {
865 tmp = silc_argument_get_arg_type(args, i + 1, &tmp_len);
867 client_id = silc_id_payload_parse_id(tmp, tmp_len);
871 /* Get the client entry */
872 client_entry = silc_client_get_client_by_id(client, conn, client_id);
874 clients = silc_realloc(clients, sizeof(*clients) *
875 (clients_count + 1));
876 clients[clients_count] = client_entry;
879 silc_free(client_id);
884 /* Notify application. We don't keep server entries so the server
885 entry is returned as NULL. The client's are returned as array
886 of SilcClientEntry pointers. */
887 client->internal->ops->notify(client, conn, type, NULL,
888 clients, clients_count);
890 for (i = 0; i < clients_count; i++) {
891 /* Remove client from all channels */
892 client_entry = clients[i];
893 if (client_entry == conn->local_entry)
896 /* Remove the client from all channels and free it */
897 silc_client_del_client(client, conn, client_entry);
909 silc_notify_payload_free(payload);
910 silc_free(client_id);
911 silc_free(channel_id);