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 chu = silc_calloc(1, sizeof(*chu));
196 chu->client = client_entry;
197 silc_list_add(channel->clients, chu);
199 /* XXX add support for multiple same nicks on same channel. Check
202 /* Notify application. The channel entry is sent last as this notify
203 is for channel but application don't know it from the arguments
205 client->ops->notify(client, conn, type, client_entry, channel);
208 case SILC_NOTIFY_TYPE_LEAVE:
210 * Someone has left a channel. We will remove it from the channel but
211 * we'll keep it in the cache in case we'll need it later.
215 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
219 client_id = silc_id_payload_parse_id(tmp, tmp_len);
223 /* Find Client entry */
225 silc_client_get_client_by_id(client, conn, client_id);
229 /* Get channel entry */
230 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
234 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
235 SILC_ID_CHANNEL, &id_cache))
238 channel = (SilcChannelEntry)id_cache->context;
240 /* Remove client from channel */
241 silc_list_start(channel->clients);
242 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
243 if (chu->client == client_entry) {
244 silc_list_del(channel->clients, chu);
250 /* Notify application. The channel entry is sent last as this notify
251 is for channel but application don't know it from the arguments
253 client->ops->notify(client, conn, type, client_entry, channel);
256 case SILC_NOTIFY_TYPE_SIGNOFF:
258 * Someone left SILC. We'll remove it from all channels and from cache.
262 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
266 client_id = silc_id_payload_parse_id(tmp, tmp_len);
270 /* Find Client entry */
272 silc_client_get_client_by_id(client, conn, client_id);
276 /* Remove from all channels */
277 silc_client_remove_from_channels(client, conn, client_entry);
279 /* Remove from cache */
280 silc_idcache_del_by_id(conn->client_cache, SILC_ID_CLIENT,
283 /* Get signoff message */
284 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
288 /* Notify application */
289 client->ops->notify(client, conn, type, client_entry, tmp);
292 if (client_entry->nickname)
293 silc_free(client_entry->nickname);
294 if (client_entry->server)
295 silc_free(client_entry->server);
296 if (client_entry->id)
297 silc_free(client_entry->id);
298 if (client_entry->send_key)
299 silc_cipher_free(client_entry->send_key);
300 if (client_entry->receive_key)
301 silc_cipher_free(client_entry->receive_key);
304 case SILC_NOTIFY_TYPE_TOPIC_SET:
306 * Someone set the topic on a channel.
310 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
314 client_id = silc_id_payload_parse_id(tmp, tmp_len);
318 /* Find Client entry */
320 silc_client_get_client_by_id(client, conn, client_id);
325 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
329 /* Get channel entry */
330 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
334 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
335 SILC_ID_CHANNEL, &id_cache))
338 channel = (SilcChannelEntry)id_cache->context;
340 /* Notify application. The channel entry is sent last as this notify
341 is for channel but application don't know it from the arguments
343 client->ops->notify(client, conn, type, client_entry, tmp, channel);
346 case SILC_NOTIFY_TYPE_NICK_CHANGE:
348 * Someone changed their nickname. If we don't have entry for the new
349 * ID we will query it and return here after it's done. After we've
350 * returned we fetch the old entry and free it and notify the
354 /* Get new Client ID */
355 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
359 client_id = silc_id_payload_parse_id(tmp, tmp_len);
364 if (!SILC_ID_CLIENT_COMPARE(client_id, conn->local_id))
367 /* Find Client entry and if not found query it */
369 silc_client_get_client_by_id(client, conn, client_id);
370 if (!client_entry2) {
371 silc_client_notify_by_server_resolve(client, conn, packet, client_id);
374 silc_free(client_id);
376 /* Get old Client ID */
377 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
381 client_id = silc_id_payload_parse_id(tmp, tmp_len);
385 /* Find old Client entry */
387 silc_client_get_client_by_id(client, conn, 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);
415 case SILC_NOTIFY_TYPE_CMODE_CHANGE:
417 * Someone changed a channel mode
421 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
425 client_id = silc_id_payload_parse_id(tmp, tmp_len);
429 /* Find Client entry */
431 silc_client_get_client_by_id(client, conn, client_id);
436 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
440 SILC_GET32_MSB(mode, tmp);
442 /* Get channel entry */
443 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
447 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
448 SILC_ID_CHANNEL, &id_cache))
451 channel = (SilcChannelEntry)id_cache->context;
453 /* Save the new mode */
454 channel->mode = mode;
456 /* Notify application. The channel entry is sent last as this notify
457 is for channel but application don't know it from the arguments
459 client->ops->notify(client, conn, type, client_entry, mode, channel);
462 case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
464 * Someone changed user's mode on a channel
468 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
472 client_id = silc_id_payload_parse_id(tmp, tmp_len);
476 /* Find Client entry */
478 silc_client_get_client_by_id(client, conn, client_id);
483 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
487 SILC_GET32_MSB(mode, tmp);
489 /* Get target Client ID */
490 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
494 silc_free(client_id);
495 client_id = silc_id_payload_parse_id(tmp, tmp_len);
499 /* Find target Client entry */
501 silc_client_get_client_by_id(client, conn, client_id);
505 /* Get channel entry */
506 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
510 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
511 SILC_ID_CHANNEL, &id_cache))
514 channel = (SilcChannelEntry)id_cache->context;
517 silc_list_start(channel->clients);
518 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
519 if (chu->client == client_entry) {
525 /* Notify application. The channel entry is sent last as this notify
526 is for channel but application don't know it from the arguments
528 client->ops->notify(client, conn, type, client_entry, mode,
529 client_entry2, channel);
532 case SILC_NOTIFY_TYPE_MOTD:
534 * Received Message of the day
538 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
542 /* Notify application */
543 client->ops->notify(client, conn, type, tmp);
546 case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
548 * Router has enforced a new ID to a channel. Let's change the old
549 * ID to the one provided here.
553 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
556 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
560 /* Get the channel entry */
561 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
562 SILC_ID_CHANNEL, &id_cache))
565 channel = (SilcChannelEntry)id_cache->context;
567 /* Free the old ID */
568 silc_free(channel_id);
569 silc_free(channel->id);
572 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
575 channel->id = silc_id_payload_parse_id(tmp, tmp_len);
579 id_cache->id = (void *)channel->id;
581 /* Notify application */
582 client->ops->notify(client, conn, type, channel, channel);
585 case SILC_NOTIFY_TYPE_KICKED:
587 * A client (maybe me) was kicked from a channel
591 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
595 client_id = silc_id_payload_parse_id(tmp, tmp_len);
599 /* Find Client entry */
601 silc_client_get_client_by_id(client, conn, client_id);
605 /* Get channel entry */
606 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
610 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
611 SILC_ID_CHANNEL, &id_cache))
614 channel = (SilcChannelEntry)id_cache->context;
617 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
619 /* Notify application. The channel entry is sent last as this notify
620 is for channel but application don't know it from the arguments
622 client->ops->notify(client, conn, type, client_entry, tmp, channel);
624 /* If I was kicked from channel, remove the channel */
625 if (client_entry == conn->local_entry) {
626 if (conn->current_channel == channel)
627 conn->current_channel = NULL;
628 silc_idcache_del_by_id(conn->channel_cache,
629 SILC_ID_CHANNEL, channel->id);
630 silc_free(channel->channel_name);
631 silc_free(channel->id);
632 silc_free(channel->key);
633 silc_cipher_free(channel->channel_key);
643 silc_notify_payload_free(payload);
645 silc_free(client_id);
647 silc_free(channel_id);