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 "silcincludes.h"
25 #include "silcclient.h"
26 #include "client_internal.h"
28 /* Context used for resolving client, channel and server info. */
32 SilcSocketConnection sock;
33 } *SilcClientNotifyResolve;
35 SILC_TASK_CALLBACK(silc_client_notify_check_client)
37 SilcClientNotifyResolve res = (SilcClientNotifyResolve)context;
38 SilcClientConnection conn = res->context;
39 SilcClient client = conn->client;
40 SilcClientID *client_id = res->packet;
41 silc_client_get_client_by_id_resolve(client, conn, client_id, NULL, NULL);
46 /* Called when notify is received and some async operation (such as command)
47 is required before processing the notify message. This calls again the
48 silc_client_notify_by_server and reprocesses the original notify packet. */
50 static void silc_client_notify_by_server_pending(void *context, void *context2)
52 SilcClientNotifyResolve res = (SilcClientNotifyResolve)context;
53 SilcClientCommandReplyContext reply =
54 (SilcClientCommandReplyContext)context2;
56 SILC_LOG_DEBUG(("Start"));
59 SilcCommandStatus status = silc_command_get_status(reply->payload);
60 if (status != SILC_STATUS_OK)
64 silc_client_notify_by_server(res->context, res->sock, res->packet);
67 silc_socket_free(res->sock);
68 silc_packet_context_free(res->packet);
72 /* Resolve client, channel or server information. */
74 static void silc_client_notify_by_server_resolve(SilcClient client,
75 SilcClientConnection conn,
76 SilcPacketContext *packet,
80 SilcClientNotifyResolve res = silc_calloc(1, sizeof(*res));
81 SilcBuffer idp = silc_id_payload_encode(id, id_type);
83 res->packet = silc_packet_context_dup(packet);
84 res->context = client;
85 res->sock = silc_socket_dup(conn->sock);
87 /* For client resolving use WHOIS, and otherwise use IDENTIFY */
88 if (id_type == SILC_ID_CLIENT) {
89 silc_client_command_register(client, SILC_COMMAND_WHOIS, NULL, NULL,
90 silc_client_command_reply_whois_i, 0,
92 silc_client_command_send(client, conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
93 1, 3, idp->data, idp->len);
94 silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
95 silc_client_notify_by_server_pending, res);
97 silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
98 silc_client_command_reply_identify_i, 0,
100 silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
101 conn->cmd_ident, 1, 5, idp->data, idp->len);
102 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
103 silc_client_notify_by_server_pending, res);
105 silc_buffer_free(idp);
108 /* Received notify message from server */
110 void silc_client_notify_by_server(SilcClient client,
111 SilcSocketConnection sock,
112 SilcPacketContext *packet)
114 SilcBuffer buffer = packet->buffer;
115 SilcClientConnection conn = (SilcClientConnection)sock->user_data;
116 SilcNotifyPayload payload;
118 SilcArgumentPayload args;
122 SilcClientID *client_id = NULL;
123 SilcChannelID *channel_id = NULL;
124 SilcServerID *server_id = NULL;
125 SilcClientEntry client_entry = NULL;
126 SilcClientEntry client_entry2 = NULL;
127 SilcChannelEntry channel;
129 SilcServerEntry server;
131 SilcUInt32 tmp_len, mode;
133 SILC_LOG_DEBUG(("Start"));
135 payload = silc_notify_payload_parse(buffer->data, buffer->len);
139 type = silc_notify_get_type(payload);
140 args = silc_notify_get_args(payload);
145 case SILC_NOTIFY_TYPE_NONE:
146 /* Notify application */
147 client->internal->ops->notify(client, conn, type,
148 silc_argument_get_arg_type(args, 1, NULL));
151 case SILC_NOTIFY_TYPE_INVITE:
153 * Someone invited me to a channel. Find Client and Channel entries
154 * for the application.
157 SILC_LOG_DEBUG(("Notify: INVITE"));
160 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
164 channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
168 /* Get the channel entry */
169 channel = silc_client_get_channel_by_id(client, conn, channel_id);
171 /* Get sender Client ID */
172 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
176 client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
180 /* Find Client entry and if not found query it */
181 client_entry = silc_client_get_client_by_id(client, conn, client_id);
183 silc_client_notify_by_server_resolve(client, conn, packet,
184 SILC_ID_CLIENT, client_id);
188 /* Get the channel name */
189 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
193 /* Notify application */
194 client->internal->ops->notify(client, conn, type, channel, tmp,
198 case SILC_NOTIFY_TYPE_JOIN:
200 * Someone has joined to a channel. Get their ID and nickname and
201 * cache them for later use.
204 SILC_LOG_DEBUG(("Notify: JOIN"));
207 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
211 client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
215 /* Find Client entry and if not found query it */
216 client_entry = silc_client_get_client_by_id(client, conn, client_id);
218 silc_client_notify_by_server_resolve(client, conn, packet,
219 SILC_ID_CLIENT, client_id);
223 /* If nickname or username hasn't been resolved, do so */
224 if (!client_entry->nickname || !client_entry->username) {
225 if (client_entry->status & SILC_CLIENT_STATUS_RESOLVING) {
226 client_entry->status &= ~SILC_CLIENT_STATUS_RESOLVING;
229 client_entry->status |= SILC_CLIENT_STATUS_RESOLVING;
230 silc_client_notify_by_server_resolve(client, conn, packet,
231 SILC_ID_CLIENT, client_id);
234 if (client_entry != conn->local_entry)
235 silc_client_nickname_format(client, conn, client_entry);
239 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
243 channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
247 /* Get channel entry */
248 channel = silc_client_get_channel_by_id(client, conn, channel_id);
252 /* Join the client to channel */
253 if (!silc_client_on_channel(channel, client_entry)) {
254 chu = silc_calloc(1, sizeof(*chu));
255 chu->client = client_entry;
256 chu->channel = channel;
257 silc_hash_table_add(channel->user_list, client_entry, chu);
258 silc_hash_table_add(client_entry->channels, channel, chu);
261 /* Notify application. The channel entry is sent last as this notify
262 is for channel but application don't know it from the arguments
264 client->internal->ops->notify(client, conn, type, client_entry, channel);
267 case SILC_NOTIFY_TYPE_LEAVE:
269 * Someone has left a channel. We will remove it from the channel but
270 * we'll keep it in the cache in case we'll need it later.
273 SILC_LOG_DEBUG(("Notify: LEAVE"));
276 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
280 client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
284 /* Find Client entry */
286 silc_client_get_client_by_id(client, conn, client_id);
290 /* Get channel entry */
291 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
295 channel = silc_client_get_channel_by_id(client, conn, channel_id);
299 /* Remove client from channel */
300 chu = silc_client_on_channel(channel, client_entry);
302 silc_hash_table_del(client_entry->channels, channel);
303 silc_hash_table_del(channel->user_list, client_entry);
307 /* Some client implementations actually quit network by first doing
308 LEAVE and then immediately SIGNOFF. We'll check for this by doing
309 check for the client after 5 - 14 seconds. If it is not valid after
310 that we'll remove the client from cache. */
311 if (!silc_hash_table_count(client_entry->channels)) {
312 SilcClientNotifyResolve res = silc_calloc(1, sizeof(*res));
314 res->packet = silc_id_dup(client_id, SILC_ID_CLIENT);
315 silc_schedule_task_add(client->schedule, 0,
316 silc_client_notify_check_client, conn,
317 (5 + (silc_rng_get_rn16(client->rng) % 9)),
318 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
321 /* Notify application. The channel entry is sent last as this notify
322 is for channel but application don't know it from the arguments
324 client->internal->ops->notify(client, conn, type, client_entry, channel);
327 case SILC_NOTIFY_TYPE_SIGNOFF:
329 * Someone left SILC. We'll remove it from all channels and from cache.
332 SILC_LOG_DEBUG(("Notify: SIGNOFF"));
335 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
339 client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
343 /* Find Client entry */
345 silc_client_get_client_by_id(client, conn, client_id);
349 /* Remove from all channels */
350 silc_client_remove_from_channels(client, conn, client_entry);
352 /* Remove from cache */
353 silc_idcache_del_by_context(conn->client_cache, client_entry);
355 /* Get signoff message */
356 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
360 /* Notify application */
361 client->internal->ops->notify(client, conn, type, client_entry, tmp);
364 silc_client_del_client_entry(client, conn, client_entry);
367 case SILC_NOTIFY_TYPE_TOPIC_SET:
369 * Someone set the topic on a channel.
372 SILC_LOG_DEBUG(("Notify: TOPIC_SET"));
375 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
378 id = silc_id_payload_parse_id(tmp, tmp_len, &id_type);
382 /* Find Client entry */
383 if (id_type == SILC_ID_CLIENT) {
384 /* Find Client entry */
386 client_entry = silc_client_get_client_by_id(client, conn, client_id);
388 silc_client_notify_by_server_resolve(client, conn, packet,
389 SILC_ID_CLIENT, client_id);
392 } else if (id_type == SILC_ID_SERVER) {
393 /* Find Server entry */
395 server = silc_client_get_server_by_id(client, conn, server_id);
397 silc_client_notify_by_server_resolve(client, conn, packet,
398 SILC_ID_SERVER, server_id);
402 /* Save the pointer to the client_entry pointer */
403 client_entry = (SilcClientEntry)server;
405 /* Find Channel entry */
407 channel = silc_client_get_channel_by_id(client, conn, channel_id);
409 silc_client_notify_by_server_resolve(client, conn, packet,
410 SILC_ID_CHANNEL, channel_id);
414 /* Save the pointer to the client_entry pointer */
415 client_entry = (SilcClientEntry)channel;
416 silc_free(channel_id);
421 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
425 /* Get channel entry */
426 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
430 channel = silc_client_get_channel_by_id(client, conn, channel_id);
434 /* Notify application. The channel entry is sent last as this notify
435 is for channel but application don't know it from the arguments
437 client->internal->ops->notify(client, conn, type, id_type,
438 client_entry, tmp, channel);
442 case SILC_NOTIFY_TYPE_NICK_CHANGE:
444 * Someone changed their nickname. If we don't have entry for the new
445 * ID we will query it and return here after it's done. After we've
446 * returned we fetch the old entry and free it and notify the
450 SILC_LOG_DEBUG(("Notify: NICK_CHANGE"));
452 /* Get old Client ID */
453 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
457 client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
462 if (SILC_ID_CLIENT_COMPARE(client_id, conn->local_id))
465 /* Find old Client entry */
466 client_entry = silc_client_get_client_by_id(client, conn, client_id);
469 silc_free(client_id);
471 client_entry->valid = FALSE;
473 /* Get new Client ID */
474 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
478 client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
482 /* From protocol version 1.1 we get the new nickname in notify as well,
483 so we don't have to resolve it. Do it the hard way if server doesn't
485 tmp = silc_argument_get_arg_type(args, 3, NULL);
487 /* Protocol version 1.1 */
489 /* Create new client entry, and save all old information with the
490 new nickname and client ID */
491 client_entry2 = silc_client_add_client(client, conn, NULL, NULL,
492 client_entry->realname,
493 silc_id_dup(client_id,
498 if (client_entry->server)
499 client_entry2->server = strdup(client_entry->server);
500 if (client_entry->username)
501 client_entry2->username = strdup(client_entry->username);
502 if (client_entry->hostname)
503 client_entry2->hostname = strdup(client_entry->hostname);
504 silc_client_update_client(client, conn, client_entry2, tmp, NULL, NULL,
507 /* Protocol version 1.0 */
509 /* Find client entry and if not found resolve it */
510 client_entry2 = silc_client_get_client_by_id(client, conn, client_id);
511 if (!client_entry2) {
512 /* Resolve the entry information */
513 silc_client_notify_by_server_resolve(client, conn, packet,
514 SILC_ID_CLIENT, client_id);
516 /* Add the new entry even though we resolved it. This is because we
517 want to replace the old entry with the new entry here right now. */
519 silc_client_add_client(client, conn, NULL, NULL, NULL,
520 silc_id_dup(client_id, SILC_ID_CLIENT),
523 /* Replace old ID entry with new one on all channels. */
524 silc_client_replace_from_channels(client, conn, client_entry,
529 if (client_entry2 != conn->local_entry)
530 silc_client_nickname_format(client, conn, client_entry2);
533 /* Remove the old from cache */
534 silc_idcache_del_by_context(conn->client_cache, client_entry);
536 /* Replace old ID entry with new one on all channels. */
537 silc_client_replace_from_channels(client, conn, client_entry,
540 /* Notify application */
541 client->internal->ops->notify(client, conn, type,
542 client_entry, client_entry2);
544 /* Free old client entry */
545 silc_client_del_client_entry(client, conn, client_entry);
549 case SILC_NOTIFY_TYPE_CMODE_CHANGE:
551 * Someone changed a channel mode
554 SILC_LOG_DEBUG(("Notify: CMODE_CHANGE"));
557 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
560 id = silc_id_payload_parse_id(tmp, tmp_len, &id_type);
564 /* Find Client entry */
565 if (id_type == SILC_ID_CLIENT) {
566 /* Find Client entry */
568 client_entry = silc_client_get_client_by_id(client, conn, client_id);
570 silc_client_notify_by_server_resolve(client, conn, packet,
571 SILC_ID_CLIENT, client_id);
574 } else if (id_type == SILC_ID_SERVER) {
575 /* Find Server entry */
577 server = silc_client_get_server_by_id(client, conn, server_id);
579 silc_client_notify_by_server_resolve(client, conn, packet,
580 SILC_ID_SERVER, server_id);
584 /* Save the pointer to the client_entry pointer */
585 client_entry = (SilcClientEntry)server;
587 /* Find Channel entry */
589 channel = silc_client_get_channel_by_id(client, conn, channel_id);
591 silc_client_notify_by_server_resolve(client, conn, packet,
592 SILC_ID_CHANNEL, channel_id);
596 /* Save the pointer to the client_entry pointer */
597 client_entry = (SilcClientEntry)channel;
598 silc_free(channel_id);
603 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
607 SILC_GET32_MSB(mode, tmp);
609 /* Get channel entry */
610 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
614 channel = silc_client_get_channel_by_id(client, conn, channel_id);
618 /* Save the new mode */
619 channel->mode = mode;
622 tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
624 unsigned char hash[32];
627 silc_hmac_free(channel->hmac);
628 if (!silc_hmac_alloc(tmp, NULL, &channel->hmac))
631 silc_hash_make(silc_hmac_get_hash(channel->hmac),
632 channel->key, channel->key_len / 8,
634 silc_hmac_set_key(channel->hmac, hash,
635 silc_hash_len(silc_hmac_get_hash(channel->hmac)));
636 memset(hash, 0, sizeof(hash));
639 /* Notify application. The channel entry is sent last as this notify
640 is for channel but application don't know it from the arguments
642 client->internal->ops->notify(client, conn, type, id_type,
643 client_entry, mode, NULL, tmp, channel);
646 case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
648 * Someone changed user's mode on a channel
651 SILC_LOG_DEBUG(("Notify: CUMODE_CHANGE"));
654 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
657 id = silc_id_payload_parse_id(tmp, tmp_len, &id_type);
661 /* Find Client entry */
662 if (id_type == SILC_ID_CLIENT) {
663 /* Find Client entry */
665 client_entry = silc_client_get_client_by_id(client, conn, client_id);
667 silc_client_notify_by_server_resolve(client, conn, packet,
668 SILC_ID_CLIENT, client_id);
671 } else if (id_type == SILC_ID_SERVER) {
672 /* Find Server entry */
674 server = silc_client_get_server_by_id(client, conn, server_id);
676 silc_client_notify_by_server_resolve(client, conn, packet,
677 SILC_ID_SERVER, server_id);
681 /* Save the pointer to the client_entry pointer */
682 client_entry = (SilcClientEntry)server;
684 /* Find Channel entry */
686 channel = silc_client_get_channel_by_id(client, conn, channel_id);
688 silc_client_notify_by_server_resolve(client, conn, packet,
689 SILC_ID_CHANNEL, channel_id);
693 /* Save the pointer to the client_entry pointer */
694 client_entry = (SilcClientEntry)channel;
695 silc_free(channel_id);
700 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
704 SILC_GET32_MSB(mode, tmp);
706 /* Get target Client ID */
707 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
711 silc_free(client_id);
712 client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
716 /* Find target Client entry */
718 silc_client_get_client_by_id(client, conn, client_id);
722 /* Get channel entry */
723 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
727 channel = silc_client_get_channel_by_id(client, conn, channel_id);
732 chu = silc_client_on_channel(channel, client_entry2);
736 /* Notify application. The channel entry is sent last as this notify
737 is for channel but application don't know it from the arguments
739 client->internal->ops->notify(client, conn, type,
740 id_type, client_entry, mode,
741 client_entry2, channel);
744 case SILC_NOTIFY_TYPE_MOTD:
746 * Received Message of the day
749 SILC_LOG_DEBUG(("Notify: MOTD"));
752 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
756 /* Notify application */
757 client->internal->ops->notify(client, conn, type, tmp);
760 case SILC_NOTIFY_TYPE_CHANNEL_CHANGE:
762 * Router has enforced a new ID to a channel. Let's change the old
763 * ID to the one provided here.
766 SILC_LOG_DEBUG(("Notify: CHANNEL_CHANGE"));
769 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
772 channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
776 /* Get the channel entry */
777 channel = silc_client_get_channel_by_id(client, conn, channel_id);
781 silc_free(channel_id);
784 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
787 channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
791 /* Replace the Channel ID */
792 silc_client_replace_channel_id(client, conn, channel, channel_id);
794 /* Notify application */
795 client->internal->ops->notify(client, conn, type, channel, channel);
798 case SILC_NOTIFY_TYPE_KICKED:
800 * A client (maybe me) was kicked from a channel
803 SILC_LOG_DEBUG(("Notify: KICKED"));
806 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
810 client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
814 /* Find Client entry */
815 client_entry = silc_client_get_client_by_id(client, conn, client_id);
819 /* Get channel entry */
820 channel_id = silc_id_str2id(packet->dst_id, packet->dst_id_len,
824 channel = silc_client_get_channel_by_id(client, conn, channel_id);
828 /* From protocol version 1.1 we get the kicker's client ID as well */
829 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
831 silc_free(client_id);
832 client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
836 /* Find kicker's client entry and if not found resolve it */
837 client_entry2 = silc_client_get_client_by_id(client, conn, client_id);
838 if (!client_entry2) {
839 silc_client_notify_by_server_resolve(client, conn, packet,
840 SILC_ID_CLIENT, client_id);
843 if (client_entry2 != conn->local_entry)
844 silc_client_nickname_format(client, conn, client_entry2);
849 tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
851 /* Notify application. The channel entry is sent last as this notify
852 is for channel but application don't know it from the arguments
854 client->internal->ops->notify(client, conn, type, client_entry, tmp,
855 client_entry2, channel);
857 /* Remove kicked client from channel */
858 if (client_entry == conn->local_entry) {
859 /* If I was kicked from channel, remove the channel */
860 if (conn->current_channel == channel)
861 conn->current_channel = NULL;
862 silc_client_del_channel(client, conn, channel);
864 chu = silc_client_on_channel(channel, client_entry);
866 silc_hash_table_del(client_entry->channels, channel);
867 silc_hash_table_del(channel->user_list, client_entry);
873 case SILC_NOTIFY_TYPE_KILLED:
876 * A client (maybe me) was killed from the network.
879 SilcUInt32 comment_len;
881 SILC_LOG_DEBUG(("Notify: KILLED"));
884 tmp = silc_argument_get_arg_type(args, 1, &tmp_len);
888 client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
892 /* Find Client entry */
893 client_entry = silc_client_get_client_by_id(client, conn, client_id);
898 comment = silc_argument_get_arg_type(args, 2, &comment_len);
900 /* From protocol version 1.1 we get killer's client ID as well */
901 tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
903 silc_free(client_id);
904 client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
908 /* Find killer's client entry and if not found resolve it */
909 client_entry2 = silc_client_get_client_by_id(client, conn, client_id);
910 if (!client_entry2) {
911 silc_client_notify_by_server_resolve(client, conn, packet,
912 SILC_ID_CLIENT, client_id);
915 if (client_entry2 != conn->local_entry)
916 silc_client_nickname_format(client, conn, client_entry2);
920 /* Notify application. */
921 client->internal->ops->notify(client, conn, type, client_entry,
922 comment, client_entry2);
924 if (client_entry != conn->local_entry)
925 /* Remove the client from all channels and free it */
926 silc_client_del_client(client, conn, client_entry);
930 case SILC_NOTIFY_TYPE_SERVER_SIGNOFF:
933 * A server quit the SILC network and some clients must be removed
934 * from channels as they quit as well.
936 SilcClientEntry *clients = NULL;
937 SilcUInt32 clients_count = 0;
940 SILC_LOG_DEBUG(("Notify: SIGNOFF"));
942 for (i = 1; i < silc_argument_get_arg_num(args); i++) {
944 tmp = silc_argument_get_arg_type(args, i + 1, &tmp_len);
946 client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
950 /* Get the client entry */
951 client_entry = silc_client_get_client_by_id(client, conn, client_id);
953 clients = silc_realloc(clients, sizeof(*clients) *
954 (clients_count + 1));
955 clients[clients_count] = client_entry;
958 silc_free(client_id);
963 /* Notify application. We don't keep server entries so the server
964 entry is returned as NULL. The client's are returned as array
965 of SilcClientEntry pointers. */
966 client->internal->ops->notify(client, conn, type, NULL,
967 clients, clients_count);
969 for (i = 0; i < clients_count; i++) {
970 /* Remove client from all channels */
971 client_entry = clients[i];
972 if (client_entry == conn->local_entry)
975 /* Remove the client from all channels and free it */
976 silc_client_del_client(client, conn, client_entry);
988 silc_notify_payload_free(payload);
989 silc_free(client_id);
990 silc_free(channel_id);
991 silc_free(server_id);