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.
22 #include "serverincludes.h"
23 #include "server_internal.h"
24 #include "command_reply.h"
26 /* All functions that call the COMMAND_CHECK_STATUS or the
27 COMMAND_CHECK_STATUS_LIST macros must have out: goto label. */
29 #define COMMAND_CHECK_STATUS \
31 SILC_LOG_DEBUG(("Start")); \
32 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL)); \
33 if (status != SILC_STATUS_OK) \
37 #define COMMAND_CHECK_STATUS_LIST \
39 SILC_LOG_DEBUG(("Start")); \
40 SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL)); \
41 if (status != SILC_STATUS_OK && \
42 status != SILC_STATUS_LIST_START && \
43 status != SILC_STATUS_LIST_ITEM && \
44 status != SILC_STATUS_LIST_END) \
48 /* Server command reply list. Not all commands have reply function as
49 they are never sent by server. More maybe added later if need appears. */
50 SilcServerCommandReply silc_command_reply_list[] =
52 SILC_SERVER_CMD_REPLY(whois, WHOIS),
53 SILC_SERVER_CMD_REPLY(whowas, WHOWAS),
54 SILC_SERVER_CMD_REPLY(identify, IDENTIFY),
55 SILC_SERVER_CMD_REPLY(info, INFO),
56 SILC_SERVER_CMD_REPLY(motd, MOTD),
57 SILC_SERVER_CMD_REPLY(join, JOIN),
58 SILC_SERVER_CMD_REPLY(users, USERS),
63 /* Process received command reply. */
65 void silc_server_command_reply_process(SilcServer server,
66 SilcSocketConnection sock,
69 SilcServerCommandReply *cmd;
70 SilcServerCommandReplyContext ctx;
71 SilcCommandPayload payload;
75 SILC_LOG_DEBUG(("Start"));
77 /* Get command reply payload from packet */
78 payload = silc_command_payload_parse(buffer);
80 /* Silently ignore bad reply packet */
81 SILC_LOG_DEBUG(("Bad command reply packet"));
85 /* Allocate command reply context. This must be free'd by the
86 command reply routine receiving it. */
87 ctx = silc_calloc(1, sizeof(*ctx));
89 ctx->sock = silc_socket_dup(sock);
90 ctx->payload = payload;
91 ctx->args = silc_command_get_args(ctx->payload);
92 ident = silc_command_get_ident(ctx->payload);
94 /* Check for pending commands and mark to be exeucted */
95 silc_server_command_pending_check(server, ctx,
96 silc_command_get(ctx->payload), ident);
98 /* Execute command reply */
99 command = silc_command_get(ctx->payload);
100 for (cmd = silc_command_reply_list; cmd->cb; cmd++)
101 if (cmd->cmd == command)
104 if (cmd == NULL || !cmd->cb) {
105 silc_server_command_reply_free(ctx);
112 /* Free command reply context and its internals. */
114 void silc_server_command_reply_free(SilcServerCommandReplyContext cmd)
117 silc_command_free_payload(cmd->payload);
119 silc_socket_free(cmd->sock); /* Decrease the reference counter */
124 /* Caches the received WHOIS information. */
127 silc_server_command_reply_whois_save(SilcServerCommandReplyContext cmd)
129 SilcServer server = cmd->server;
131 unsigned char *tmp, *id_data;
132 char *nickname, *username, *realname;
133 SilcClientID *client_id;
134 SilcClientEntry client;
135 SilcIDCacheEntry cache = NULL;
138 unsigned int mode = 0;
140 id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
141 nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
142 username = silc_argument_get_arg_type(cmd->args, 4, &len);
143 realname = silc_argument_get_arg_type(cmd->args, 5, &len);
144 if (!id_data || !nickname || !username || !realname) {
145 SILC_LOG_ERROR(("Incomplete WHOIS info: %s %s %s",
146 nickname ? nickname : "",
147 username ? username : "",
148 realname ? realname : ""));
152 tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
154 SILC_GET32_MSB(mode, tmp);
156 client_id = silc_id_payload_parse_id(id_data, id_len);
160 /* Check if we have this client cached already. */
162 client = silc_idlist_find_client_by_id(server->local_list, client_id,
165 client = silc_idlist_find_client_by_id(server->global_list,
171 /* If router did not find such Client ID in its lists then this must
172 be bogus client or some router in the net is buggy. */
173 if (server->server_type == SILC_ROUTER)
176 /* Take hostname out of nick string if it includes it. */
177 if (strchr(nickname, '@')) {
178 int len = strcspn(nickname, "@");
179 nick = silc_calloc(len + 1, sizeof(char));
180 memcpy(nick, nickname, len);
182 nick = strdup(nickname);
185 /* We don't have that client anywhere, add it. The client is added
186 to global list since server didn't have it in the lists so it must be
188 client = silc_idlist_add_client(server->global_list, nick, strlen(nick),
190 strdup(realname), client_id,
191 cmd->sock->user_data, NULL);
195 client->data.registered = TRUE;
198 /* We have the client already, update the data */
200 SILC_LOG_DEBUG(("Updating client data"));
202 /* Take hostname out of nick string if it includes it. */
203 if (strchr(nickname, '@')) {
204 int len = strcspn(nickname, "@");
205 nick = silc_calloc(len + 1, sizeof(char));
206 memcpy(nick, nickname, len);
208 nick = strdup(nickname);
211 if (client->nickname)
212 silc_free(client->nickname);
213 if (client->username)
214 silc_free(client->username);
215 if (client->userinfo)
216 silc_free(client->userinfo);
218 client->nickname = nick;
219 client->username = strdup(username);
220 client->userinfo = strdup(realname);
225 cache->data_len = strlen(nick);
226 silc_idcache_sort_by_data(global ? server->global_list->clients :
227 server->local_list->clients);
230 silc_free(client_id);
236 /* Reiceved reply for WHOIS command. We sent the whois request to our
237 primary router, if we are normal server, and thus has now received reply
238 to the command. We will figure out what client originally sent us the
239 command and will send the reply to it. If we are router we will figure
240 out who server sent us the command and send reply to that one. */
242 SILC_SERVER_CMD_REPLY_FUNC(whois)
244 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
245 SilcCommandStatus status;
247 COMMAND_CHECK_STATUS_LIST;
249 if (!silc_server_command_reply_whois_save(cmd))
252 /* Pending callbacks are not executed if this was an list entry */
253 if (status != SILC_STATUS_OK &&
254 status != SILC_STATUS_LIST_END) {
255 silc_server_command_reply_free(cmd);
260 SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
261 SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_WHOIS);
262 silc_server_command_reply_free(cmd);
265 /* Caches the received WHOWAS information for a short period of time. */
268 silc_server_command_reply_whowas_save(SilcServerCommandReplyContext cmd)
270 SilcServer server = cmd->server;
272 unsigned char *id_data;
273 char *nickname, *username, *realname;
274 SilcClientID *client_id;
275 SilcClientEntry client;
276 SilcIDCacheEntry cache = NULL;
280 id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
281 nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
282 username = silc_argument_get_arg_type(cmd->args, 4, &len);
283 if (!id_data || !nickname || !username)
286 realname = silc_argument_get_arg_type(cmd->args, 5, &len);
288 client_id = silc_id_payload_parse_id(id_data, id_len);
292 /* Check if we have this client cached already. */
294 client = silc_idlist_find_client_by_id(server->local_list, client_id,
297 client = silc_idlist_find_client_by_id(server->global_list,
303 /* If router did not find such Client ID in its lists then this must
304 be bogus client or some router in the net is buggy. */
305 if (server->server_type == SILC_ROUTER)
308 /* Take hostname out of nick string if it includes it. */
309 if (strchr(nickname, '@')) {
310 int len = strcspn(nickname, "@");
311 nick = silc_calloc(len + 1, sizeof(char));
312 memcpy(nick, nickname, len);
314 nick = strdup(nickname);
317 /* We don't have that client anywhere, add it. The client is added
318 to global list since server didn't have it in the lists so it must be
320 client = silc_idlist_add_client(server->global_list, nick, strlen(nick),
321 strdup(username), strdup(realname),
322 silc_id_dup(client_id, SILC_ID_CLIENT),
323 cmd->sock->user_data, NULL);
327 client->data.registered = FALSE;
328 client = silc_idlist_find_client_by_id(server->global_list,
330 cache->expire = SILC_ID_CACHE_EXPIRE_DEF;
332 /* We have the client already, update the data */
334 /* Take hostname out of nick string if it includes it. */
335 if (strchr(nickname, '@')) {
336 int len = strcspn(nickname, "@");
337 nick = silc_calloc(len + 1, sizeof(char));
338 memcpy(nick, nickname, len);
340 nick = strdup(nickname);
343 if (client->nickname)
344 silc_free(client->nickname);
345 if (client->username)
346 silc_free(client->username);
348 client->nickname = nick;
349 client->username = strdup(username);
353 cache->data_len = strlen(nick);
354 silc_idcache_sort_by_data(global ? server->global_list->clients :
355 server->local_list->clients);
359 silc_free(client_id);
364 /* Received reply for WHOWAS command. Cache the client information only for
365 a short period of time. */
367 SILC_SERVER_CMD_REPLY_FUNC(whowas)
369 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
370 SilcCommandStatus status;
372 COMMAND_CHECK_STATUS_LIST;
374 if (!silc_server_command_reply_whowas_save(cmd))
377 /* Pending callbacks are not executed if this was an list entry */
378 if (status != SILC_STATUS_OK &&
379 status != SILC_STATUS_LIST_END) {
380 silc_server_command_reply_free(cmd);
385 SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_WHOWAS);
386 SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_WHOWAS);
387 silc_server_command_reply_free(cmd);
390 /* Caches the received IDENTIFY information. */
393 silc_server_command_reply_identify_save(SilcServerCommandReplyContext cmd)
395 SilcServer server = cmd->server;
397 unsigned char *id_data;
398 char *nickname, *username;
399 SilcClientID *client_id;
400 SilcClientEntry client;
401 SilcIDCacheEntry cache = NULL;
405 id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
406 nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
407 username = silc_argument_get_arg_type(cmd->args, 4, &len);
411 client_id = silc_id_payload_parse_id(id_data, id_len);
415 /* Check if we have this client cached already. */
417 client = silc_idlist_find_client_by_id(server->local_list, client_id,
420 client = silc_idlist_find_client_by_id(server->global_list,
426 /* If router did not find such Client ID in its lists then this must
427 be bogus client or some router in the net is buggy. */
428 if (server->server_type == SILC_ROUTER)
431 /* Take hostname out of nick string if it includes it. */
433 if (strchr(nickname, '@')) {
434 int len = strcspn(nickname, "@");
435 nick = silc_calloc(len + 1, sizeof(char));
436 memcpy(nick, nickname, len);
438 nick = strdup(nickname);
442 /* We don't have that client anywhere, add it. The client is added
443 to global list since server didn't have it in the lists so it must be
445 client = silc_idlist_add_client(server->global_list, nick, strlen(nick),
446 username ? strdup(username) : NULL, NULL,
447 client_id, cmd->sock->user_data, NULL);
448 client->data.registered = TRUE;
450 /* We have the client already, update the data */
452 SILC_LOG_DEBUG(("Updating client data"));
454 /* Take hostname out of nick string if it includes it. */
456 if (strchr(nickname, '@')) {
457 int len = strcspn(nickname, "@");
458 nick = silc_calloc(len + 1, sizeof(char));
459 memcpy(nick, nickname, len);
461 nick = strdup(nickname);
465 if (nickname && client->nickname)
466 silc_free(client->nickname);
469 client->nickname = nick;
471 if (username && client->username) {
472 silc_free(client->username);
473 client->username = strdup(username);
476 if (nickname && cache) {
478 cache->data_len = strlen(nick);
479 silc_idcache_sort_by_data(global ? server->global_list->clients :
480 server->local_list->clients);
483 silc_free(client_id);
489 /* Received reply for forwarded IDENTIFY command. We have received the
490 requested identify information now and we will cache it. After this we
491 will call the pending command so that the requestee gets the information
494 SILC_SERVER_CMD_REPLY_FUNC(identify)
496 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
497 SilcCommandStatus status;
499 COMMAND_CHECK_STATUS_LIST;
501 if (!silc_server_command_reply_identify_save(cmd))
504 /* Pending callbacks are not executed if this was an list entry */
505 if (status != SILC_STATUS_OK &&
506 status != SILC_STATUS_LIST_END) {
507 silc_server_command_reply_free(cmd);
512 SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
513 SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_IDENTIFY);
514 silc_server_command_reply_free(cmd);
517 /* Received reply fro INFO command. Cache the server and its information */
519 SILC_SERVER_CMD_REPLY_FUNC(info)
521 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
522 SilcServer server = cmd->server;
523 SilcCommandStatus status;
524 SilcServerEntry entry;
525 SilcServerID *server_id;
526 unsigned int tmp_len;
527 unsigned char *tmp, *name;
529 COMMAND_CHECK_STATUS;
532 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
535 server_id = silc_id_payload_parse_id(tmp, tmp_len);
540 name = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
544 entry = silc_idlist_find_server_by_id(server->local_list, server_id, NULL);
546 entry = silc_idlist_find_server_by_id(server->global_list, server_id,
549 /* Add the server to global list */
550 server_id = silc_id_dup(server_id, SILC_ID_SERVER);
551 entry = silc_idlist_add_server(server->global_list, name, 0,
552 server_id, NULL, NULL);
554 silc_free(server_id);
560 /* Get the info string */
561 tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
565 entry->server_info = tmp ? strdup(tmp) : NULL;
568 SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_INFO);
569 SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INFO);
570 silc_server_command_reply_free(cmd);
573 /* Received reply fro MOTD command. */
575 SILC_SERVER_CMD_REPLY_FUNC(motd)
577 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
578 SilcServer server = cmd->server;
579 SilcCommandStatus status;
580 SilcServerEntry entry = NULL;
581 SilcServerID *server_id;
582 unsigned int tmp_len;
585 COMMAND_CHECK_STATUS;
588 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
591 server_id = silc_id_payload_parse_id(tmp, tmp_len);
595 entry = silc_idlist_find_server_by_id(server->local_list, server_id, NULL);
597 entry = silc_idlist_find_server_by_id(server->global_list, server_id,
604 tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
611 SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_MOTD);
612 SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_MOTD);
613 silc_server_command_reply_free(cmd);
619 /* Received reply for forwarded JOIN command. Router has created or joined
620 the client to the channel. We save some channel information locally
623 SILC_SERVER_CMD_REPLY_FUNC(join)
625 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
626 SilcServer server = cmd->server;
627 SilcIDCacheEntry cache = NULL;
628 SilcCommandStatus status;
630 SilcClientID *client_id = NULL;
631 SilcChannelEntry entry;
632 SilcHmac hmac = NULL;
633 unsigned int id_len, len, list_count;
634 unsigned char *id_string;
635 char *channel_name, *tmp;
636 unsigned int mode, created;
637 SilcBuffer keyp = NULL, client_id_list = NULL, client_mode_list = NULL;
639 COMMAND_CHECK_STATUS;
641 /* Get channel name */
642 channel_name = silc_argument_get_arg_type(cmd->args, 2, NULL);
647 id_string = silc_argument_get_arg_type(cmd->args, 3, &id_len);
652 tmp = silc_argument_get_arg_type(cmd->args, 4, &len);
655 client_id = silc_id_payload_parse_id(tmp, len);
660 tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
663 SILC_GET32_MSB(mode, tmp);
665 /* Get created boolean value */
666 tmp = silc_argument_get_arg_type(cmd->args, 6, NULL);
669 SILC_GET32_MSB(created, tmp);
670 if (created != 0 && created != 1)
673 /* Get channel key */
674 tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
676 keyp = silc_buffer_alloc(len);
677 silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
678 silc_buffer_put(keyp, tmp, len);
681 id = silc_id_payload_parse_id(id_string, id_len);
686 tmp = silc_argument_get_arg_type(cmd->args, 11, NULL);
688 if (!silc_hmac_alloc(tmp, NULL, &hmac))
692 /* Get the list count */
693 tmp = silc_argument_get_arg_type(cmd->args, 12, &len);
696 SILC_GET32_MSB(list_count, tmp);
698 /* Get Client ID list */
699 tmp = silc_argument_get_arg_type(cmd->args, 13, &len);
703 client_id_list = silc_buffer_alloc(len);
704 silc_buffer_pull_tail(client_id_list, len);
705 silc_buffer_put(client_id_list, tmp, len);
707 /* Get client mode list */
708 tmp = silc_argument_get_arg_type(cmd->args, 14, &len);
712 client_mode_list = silc_buffer_alloc(len);
713 silc_buffer_pull_tail(client_mode_list, len);
714 silc_buffer_put(client_mode_list, tmp, len);
716 /* See whether we already have the channel. */
717 entry = silc_idlist_find_channel_by_name(server->local_list,
718 channel_name, &cache);
720 /* Add new channel */
722 SILC_LOG_DEBUG(("Adding new [%s] channel %s id(%s)",
723 (created == 0 ? "existing" : "created"), channel_name,
724 silc_id_render(id, SILC_ID_CHANNEL)));
726 /* Add the channel to our local list. */
727 entry = silc_idlist_add_channel(server->local_list, strdup(channel_name),
728 SILC_CHANNEL_MODE_NONE, id,
729 server->router, NULL, hmac);
735 /* The entry exists. */
737 silc_free(cache->id);
739 cache->id = entry->id;
741 /* Remove the founder auth data if the mode is not set but we have
743 if (!(mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) && entry->founder_key) {
744 silc_pkcs_public_key_free(entry->founder_key);
745 if (entry->founder_passwd) {
746 silc_free(entry->founder_passwd);
747 entry->founder_passwd = NULL;
752 if (entry->hmac_name && hmac) {
753 silc_free(entry->hmac_name);
754 entry->hmac_name = strdup(hmac->hmac->name);
757 /* Get the ban list */
758 tmp = silc_argument_get_arg_type(cmd->args, 8, &len);
761 silc_free(entry->ban_list);
762 entry->ban_list = silc_calloc(len, sizeof(*entry->ban_list));
763 memcpy(entry->ban_list, tmp, len);
766 /* Get the invite list */
767 tmp = silc_argument_get_arg_type(cmd->args, 9, &len);
769 if (entry->invite_list)
770 silc_free(entry->invite_list);
771 entry->invite_list = silc_calloc(len, sizeof(*entry->invite_list));
772 memcpy(entry->invite_list, tmp, len);
776 tmp = silc_argument_get_arg_type(cmd->args, 10, &len);
779 silc_free(entry->topic);
780 entry->topic = strdup(tmp);
783 /* If channel was not created we know there is global users on the
785 entry->global_users = (created == 0 ? TRUE : FALSE);
787 /* If channel was just created the mask must be zero */
788 if (!entry->global_users && mode) {
789 SILC_LOG_DEBUG(("Buggy router `%s' sent non-zero mode mask for "
790 "new channel, forcing it to zero", cmd->sock->hostname));
794 /* Save channel mode */
797 /* Save channel key */
798 if (!(entry->mode & SILC_CHANNEL_MODE_PRIVKEY))
799 silc_server_save_channel_key(server, keyp, entry);
801 silc_buffer_free(keyp);
803 /* Save the users to the channel */
804 silc_server_save_users_on_channel(server, cmd->sock, entry,
805 client_id, client_id_list,
806 client_mode_list, list_count);
809 SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
810 SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_JOIN);
812 silc_free(client_id);
813 silc_server_command_reply_free(cmd);
816 silc_buffer_free(client_id_list);
817 if (client_mode_list)
818 silc_buffer_free(client_mode_list);
821 SILC_SERVER_CMD_REPLY_FUNC(users)
823 SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
824 SilcServer server = cmd->server;
825 SilcCommandStatus status;
826 SilcChannelEntry channel;
827 SilcChannelID *channel_id = NULL;
828 SilcBuffer client_id_list;
829 SilcBuffer client_mode_list;
831 unsigned int tmp_len;
832 unsigned int list_count;
834 COMMAND_CHECK_STATUS;
837 tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
840 channel_id = silc_id_payload_parse_id(tmp, tmp_len);
844 /* Get channel entry */
845 channel = silc_idlist_find_channel_by_id(server->local_list,
848 channel = silc_idlist_find_channel_by_id(server->global_list,
854 /* Get the list count */
855 tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
858 SILC_GET32_MSB(list_count, tmp);
860 /* Get Client ID list */
861 tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
865 client_id_list = silc_buffer_alloc(tmp_len);
866 silc_buffer_pull_tail(client_id_list, tmp_len);
867 silc_buffer_put(client_id_list, tmp, tmp_len);
869 /* Get client mode list */
870 tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
874 client_mode_list = silc_buffer_alloc(tmp_len);
875 silc_buffer_pull_tail(client_mode_list, tmp_len);
876 silc_buffer_put(client_mode_list, tmp, tmp_len);
878 /* Save the users to the channel */
879 silc_server_save_users_on_channel(server, cmd->sock, channel, NULL,
880 client_id_list, client_mode_list,
883 silc_buffer_free(client_id_list);
884 silc_buffer_free(client_mode_list);
887 SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
888 SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_USERS);
890 silc_free(channel_id);
891 silc_server_command_reply_free(cmd);