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;
78 SilcClientID *client_id = NULL;
79 SilcChannelID *channel_id = NULL;
80 SilcClientEntry client_entry;
81 SilcClientEntry client_entry2;
82 SilcChannelEntry channel;
84 SilcIDCacheEntry id_cache = NULL;
86 unsigned int tmp_len, mode;
88 payload = silc_notify_payload_parse(buffer);
92 type = silc_notify_get_type(payload);
93 args = silc_notify_get_args(payload);
98 case SILC_NOTIFY_TYPE_NONE:
99 /* Notify application */
100 client->ops->notify(client, conn, type,
101 silc_argument_get_arg_type(args, 1, NULL));
104 case SILC_NOTIFY_TYPE_INVITE:
106 * Someone invited me to a channel. Find Client and Channel entries
107 * for the application.
111 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
115 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
119 /* Get the channel entry */
121 if (silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
122 SILC_ID_CHANNEL, &id_cache))
123 channel = (SilcChannelEntry)id_cache->context;
125 /* Get sender Client ID */
126 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
130 client_id = silc_id_payload_parse_id(tmp, tmp_len);
134 /* Find Client entry and if not found query it */
135 client_entry = silc_client_get_client_by_id(client, conn, client_id);
137 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
141 /* Get the channel name */
142 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
146 /* Notify application */
147 client->ops->notify(client, conn, type, channel, tmp, client_entry);
150 case SILC_NOTIFY_TYPE_JOIN:
152 * Someone has joined to a channel. Get their ID and nickname and
153 * cache them for later use.
157 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
161 client_id = silc_id_payload_parse_id(tmp, tmp_len);
165 /* Find Client entry and if not found query it */
166 client_entry = silc_client_get_client_by_id(client, conn, client_id);
168 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
172 /* If nickname or username hasn't been resolved, do so */
173 if (!client_entry->nickname || !client_entry->username) {
174 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
179 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
183 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
187 /* Get channel entry */
188 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
189 SILC_ID_CHANNEL, &id_cache))
192 channel = (SilcChannelEntry)id_cache->context;
194 /* Add client to channel */
195 if (client_entry != conn->local_entry) {
196 chu = silc_calloc(1, sizeof(*chu));
197 chu->client = client_entry;
198 silc_list_add(channel->clients, chu);
201 /* XXX add support for multiple same nicks on same channel. Check
204 /* Notify application. The channel entry is sent last as this notify
205 is for channel but application don't know it from the arguments
207 client->ops->notify(client, conn, type, client_entry, channel);
210 case SILC_NOTIFY_TYPE_LEAVE:
212 * Someone has left a channel. We will remove it from the channel but
213 * we'll keep it in the cache in case we'll need it later.
217 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
221 client_id = silc_id_payload_parse_id(tmp, tmp_len);
225 /* Find Client entry */
227 silc_client_get_client_by_id(client, conn, client_id);
231 /* Get channel entry */
232 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
236 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
237 SILC_ID_CHANNEL, &id_cache))
240 channel = (SilcChannelEntry)id_cache->context;
242 /* Remove client from channel */
243 silc_list_start(channel->clients);
244 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
245 if (chu->client == client_entry) {
246 silc_list_del(channel->clients, chu);
252 /* Notify application. The channel entry is sent last as this notify
253 is for channel but application don't know it from the arguments
255 client->ops->notify(client, conn, type, client_entry, channel);
258 case SILC_NOTIFY_TYPE_SIGNOFF:
260 * Someone left SILC. We'll remove it from all channels and from cache.
264 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
268 client_id = silc_id_payload_parse_id(tmp, tmp_len);
272 /* Find Client entry */
274 silc_client_get_client_by_id(client, conn, client_id);
278 /* Remove from all channels */
279 silc_client_remove_from_channels(client, conn, client_entry);
281 /* Remove from cache */
282 silc_idcache_del_by_id(conn->client_cache, SILC_ID_CLIENT,
285 /* Get signoff message */
286 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
290 /* Notify application */
291 client->ops->notify(client, conn, type, client_entry, tmp);
294 if (client_entry->nickname)
295 silc_free(client_entry->nickname);
296 if (client_entry->server)
297 silc_free(client_entry->server);
298 if (client_entry->id)
299 silc_free(client_entry->id);
300 if (client_entry->send_key)
301 silc_cipher_free(client_entry->send_key);
302 if (client_entry->receive_key)
303 silc_cipher_free(client_entry->receive_key);
306 case SILC_NOTIFY_TYPE_TOPIC_SET:
308 * Someone set the topic on a channel.
312 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
316 client_id = silc_id_payload_parse_id(tmp, tmp_len);
320 /* Find Client entry */
322 silc_client_get_client_by_id(client, conn, client_id);
327 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
331 /* Get channel entry */
332 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
336 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
337 SILC_ID_CHANNEL, &id_cache))
340 channel = (SilcChannelEntry)id_cache->context;
342 /* Notify application. The channel entry is sent last as this notify
343 is for channel but application don't know it from the arguments
345 client->ops->notify(client, conn, type, client_entry, tmp, channel);
348 case SILC_NOTIFY_TYPE_NICK_CHANGE:
350 * Someone changed their nickname. If we don't have entry for the new
351 * ID we will query it and return here after it's done. After we've
352 * returned we fetch the old entry and free it and notify the
356 /* Get old Client ID */
357 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
361 client_id = silc_id_payload_parse_id(tmp, tmp_len);
366 if (!SILC_ID_CLIENT_COMPARE(client_id, conn->local_id))
369 /* Find old Client entry */
370 client_entry = silc_client_get_client_by_id(client, conn, client_id);
373 silc_free(client_id);
375 /* Get new Client ID */
376 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
380 client_id = silc_id_payload_parse_id(tmp, tmp_len);
384 /* Find Client entry and if not found resolve it */
385 client_entry2 = silc_client_get_client_by_id(client, conn, client_id);
386 if (!client_entry2) {
387 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
391 /* Remove the old from cache */
392 silc_idcache_del_by_id(conn->client_cache, SILC_ID_CLIENT,
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 client_id = silc_id_payload_parse_id(tmp, tmp_len);
430 /* Find Client entry */
432 silc_client_get_client_by_id(client, conn, client_id);
437 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
441 SILC_GET32_MSB(mode, tmp);
443 /* Get channel entry */
444 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
448 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
449 SILC_ID_CHANNEL, &id_cache))
452 channel = (SilcChannelEntry)id_cache->context;
454 /* Save the new mode */
455 channel->mode = mode;
458 tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
460 unsigned char hash[32];
463 silc_hmac_free(channel->hmac);
464 if (!silc_hmac_alloc(tmp, NULL, &channel->hmac))
467 silc_hash_make(channel->hmac->hash, channel->key, channel->key_len / 8,
469 silc_hmac_set_key(channel->hmac, hash,
470 silc_hash_len(channel->hmac->hash));
471 memset(hash, 0, sizeof(hash));
474 /* Notify application. The channel entry is sent last as this notify
475 is for channel but application don't know it from the arguments
477 client->ops->notify(client, conn, type, client_entry, mode, NULL,
481 case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
483 * Someone changed user's mode on a channel
487 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
491 client_id = silc_id_payload_parse_id(tmp, tmp_len);
495 /* Find Client entry */
497 silc_client_get_client_by_id(client, conn, client_id);
502 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
506 SILC_GET32_MSB(mode, tmp);
508 /* Get target Client ID */
509 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
513 silc_free(client_id);
514 client_id = silc_id_payload_parse_id(tmp, tmp_len);
518 /* Find target Client entry */
520 silc_client_get_client_by_id(client, conn, client_id);
524 /* Get channel entry */
525 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
529 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
530 SILC_ID_CHANNEL, &id_cache))
533 channel = (SilcChannelEntry)id_cache->context;
536 silc_list_start(channel->clients);
537 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
538 if (chu->client == client_entry) {
544 /* Notify application. The channel entry is sent last as this notify
545 is for channel but application don't know it from the arguments
547 client->ops->notify(client, conn, type, client_entry, mode,
548 client_entry2, channel);
551 case SILC_NOTIFY_TYPE_MOTD:
553 * Received Message of the day
557 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
561 /* Notify application */
562 client->ops->notify(client, conn, type, tmp);
565 case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
567 * Router has enforced a new ID to a channel. Let's change the old
568 * ID to the one provided here.
572 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
575 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
579 /* Get the channel entry */
580 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
581 SILC_ID_CHANNEL, &id_cache))
584 channel = (SilcChannelEntry)id_cache->context;
586 /* Free the old ID */
587 silc_free(channel_id);
588 silc_free(channel->id);
591 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
594 channel->id = silc_id_payload_parse_id(tmp, tmp_len);
598 id_cache->id = (void *)channel->id;
600 /* Notify application */
601 client->ops->notify(client, conn, type, channel, channel);
604 case SILC_NOTIFY_TYPE_KICKED:
606 * A client (maybe me) was kicked from a channel
610 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
614 client_id = silc_id_payload_parse_id(tmp, tmp_len);
618 /* Find Client entry */
619 client_entry = silc_client_get_client_by_id(client, conn, client_id);
623 /* Get channel entry */
624 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
628 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
629 SILC_ID_CHANNEL, &id_cache))
632 channel = (SilcChannelEntry)id_cache->context;
635 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
637 /* Notify application. The channel entry is sent last as this notify
638 is for channel but application don't know it from the arguments
640 client->ops->notify(client, conn, type, client_entry, tmp, channel);
642 /* If I was kicked from channel, remove the channel */
643 if (client_entry == conn->local_entry) {
644 if (conn->current_channel == channel)
645 conn->current_channel = NULL;
646 silc_idcache_del_by_id(conn->channel_cache,
647 SILC_ID_CHANNEL, channel->id);
648 silc_free(channel->channel_name);
649 silc_free(channel->id);
650 silc_free(channel->key);
651 silc_cipher_free(channel->channel_key);
656 case SILC_NOTIFY_TYPE_KILLED:
658 * A client (maybe me) was killed from the network.
662 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
666 client_id = silc_id_payload_parse_id(tmp, tmp_len);
670 /* Find Client entry */
671 client_entry = silc_client_get_client_by_id(client, conn, client_id);
676 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
678 /* Notify application. The channel entry is sent last as this notify
679 is for channel but application don't know it from the arguments
681 client->ops->notify(client, conn, type, client_entry, tmp);
683 if (client_entry != conn->local_entry) {
684 /* Remove client from all channels */
685 silc_client_remove_from_channels(client, conn, client_entry);
686 silc_idcache_del_by_id(conn->client_cache, SILC_ID_CLIENT,
688 if (client_entry->nickname)
689 silc_free(client_entry->nickname);
690 if (client_entry->server)
691 silc_free(client_entry->server);
692 if (client_entry->id)
693 silc_free(client_entry->id);
694 if (client_entry->send_key)
695 silc_cipher_free(client_entry->send_key);
696 if (client_entry->receive_key)
697 silc_cipher_free(client_entry->receive_key);
698 silc_free(client_entry);
708 silc_notify_payload_free(payload);
710 silc_free(client_id);
712 silc_free(channel_id);