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 client_id = silc_id_payload_parse_id(tmp, tmp_len);
119 /* Find Client entry and if not found query it */
120 client_entry = silc_client_get_client_by_id(client, conn, client_id);
122 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
127 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
131 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
135 /* XXX Will ALWAYS fail because currently we don't have way to resolve
136 channel information for channel that we're not joined to. */
137 /* XXX ways to fix: use (extended) LIST command, or define the channel
138 name to the notfy type when name resolving is not mandatory. */
139 /* Find channel entry */
140 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
141 SILC_ID_CHANNEL, &id_cache))
144 channel = (SilcChannelEntry)id_cache->context;
146 /* Notify application */
147 client->ops->notify(client, conn, type, client_entry, channel);
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 new Client ID */
357 tmp = silc_argument_get_arg_type(args, 2, &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 Client entry and if not found query it */
371 silc_client_get_client_by_id(client, conn, client_id);
372 if (!client_entry2) {
373 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
376 silc_free(client_id);
378 /* Get old Client ID */
379 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
383 client_id = silc_id_payload_parse_id(tmp, tmp_len);
387 /* Find old Client entry */
389 silc_client_get_client_by_id(client, conn, client_id);
393 /* Remove the old from cache */
394 silc_idcache_del_by_id(conn->client_cache, SILC_ID_CLIENT,
397 /* Replace old ID entry with new one on all channels. */
398 silc_client_replace_from_channels(client, conn, client_entry,
401 /* Notify application */
402 client->ops->notify(client, conn, type, client_entry, client_entry2);
405 if (client_entry->nickname)
406 silc_free(client_entry->nickname);
407 if (client_entry->server)
408 silc_free(client_entry->server);
409 if (client_entry->id)
410 silc_free(client_entry->id);
411 if (client_entry->send_key)
412 silc_cipher_free(client_entry->send_key);
413 if (client_entry->receive_key)
414 silc_cipher_free(client_entry->receive_key);
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 client_id = silc_id_payload_parse_id(tmp, tmp_len);
431 /* Find Client entry */
433 silc_client_get_client_by_id(client, conn, client_id);
438 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
442 SILC_GET32_MSB(mode, tmp);
444 /* Get channel entry */
445 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
449 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
450 SILC_ID_CHANNEL, &id_cache))
453 channel = (SilcChannelEntry)id_cache->context;
455 /* Save the new mode */
456 channel->mode = mode;
458 /* Notify application. The channel entry is sent last as this notify
459 is for channel but application don't know it from the arguments
461 client->ops->notify(client, conn, type, client_entry, mode, channel);
464 case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
466 * Someone changed user's mode on a channel
470 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
474 client_id = silc_id_payload_parse_id(tmp, tmp_len);
478 /* Find Client entry */
480 silc_client_get_client_by_id(client, conn, client_id);
485 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
489 SILC_GET32_MSB(mode, tmp);
491 /* Get target Client ID */
492 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
496 silc_free(client_id);
497 client_id = silc_id_payload_parse_id(tmp, tmp_len);
501 /* Find target Client entry */
503 silc_client_get_client_by_id(client, conn, client_id);
507 /* Get channel entry */
508 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
512 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
513 SILC_ID_CHANNEL, &id_cache))
516 channel = (SilcChannelEntry)id_cache->context;
519 silc_list_start(channel->clients);
520 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
521 if (chu->client == client_entry) {
527 /* Notify application. The channel entry is sent last as this notify
528 is for channel but application don't know it from the arguments
530 client->ops->notify(client, conn, type, client_entry, mode,
531 client_entry2, channel);
534 case SILC_NOTIFY_TYPE_MOTD:
536 * Received Message of the day
540 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
544 /* Notify application */
545 client->ops->notify(client, conn, type, tmp);
548 case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
550 * Router has enforced a new ID to a channel. Let's change the old
551 * ID to the one provided here.
555 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
558 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
562 /* Get the channel entry */
563 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
564 SILC_ID_CHANNEL, &id_cache))
567 channel = (SilcChannelEntry)id_cache->context;
569 /* Free the old ID */
570 silc_free(channel_id);
571 silc_free(channel->id);
574 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
577 channel->id = silc_id_payload_parse_id(tmp, tmp_len);
581 id_cache->id = (void *)channel->id;
583 /* Notify application */
584 client->ops->notify(client, conn, type, channel, channel);
587 case SILC_NOTIFY_TYPE_KICKED:
589 * A client (maybe me) was kicked from a channel
593 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
597 client_id = silc_id_payload_parse_id(tmp, tmp_len);
601 /* Find Client entry */
603 silc_client_get_client_by_id(client, conn, client_id);
607 /* Get channel entry */
608 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
612 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
613 SILC_ID_CHANNEL, &id_cache))
616 channel = (SilcChannelEntry)id_cache->context;
619 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
621 /* Notify application. The channel entry is sent last as this notify
622 is for channel but application don't know it from the arguments
624 client->ops->notify(client, conn, type, client_entry, tmp, channel);
626 /* If I was kicked from channel, remove the channel */
627 if (client_entry == conn->local_entry) {
628 if (conn->current_channel == channel)
629 conn->current_channel = NULL;
630 silc_idcache_del_by_id(conn->channel_cache,
631 SILC_ID_CHANNEL, channel->id);
632 silc_free(channel->channel_name);
633 silc_free(channel->id);
634 silc_free(channel->key);
635 silc_cipher_free(channel->channel_key);
645 silc_notify_payload_free(payload);
647 silc_free(client_id);
649 silc_free(channel_id);