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 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);
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);