5 Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
7 Copyright (C) 1997 - 2000 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 * Command reply functions are "the otherside" of the command functions.
22 * Reply to a command sent by server is handled by these functions.
26 #include "clientlibincludes.h"
28 /* Client command reply list. */
29 SilcClientCommandReply silc_command_reply_list[] =
31 SILC_CLIENT_CMD_REPLY(whois, WHOIS),
32 SILC_CLIENT_CMD_REPLY(whowas, WHOWAS),
33 SILC_CLIENT_CMD_REPLY(identify, IDENTIFY),
34 SILC_CLIENT_CMD_REPLY(nick, NICK),
35 SILC_CLIENT_CMD_REPLY(list, LIST),
36 SILC_CLIENT_CMD_REPLY(topic, TOPIC),
37 SILC_CLIENT_CMD_REPLY(invite, INVITE),
38 SILC_CLIENT_CMD_REPLY(quit, QUIT),
39 SILC_CLIENT_CMD_REPLY(kill, KILL),
40 SILC_CLIENT_CMD_REPLY(info, INFO),
41 SILC_CLIENT_CMD_REPLY(connect, CONNECT),
42 SILC_CLIENT_CMD_REPLY(ping, PING),
43 SILC_CLIENT_CMD_REPLY(oper, OPER),
44 SILC_CLIENT_CMD_REPLY(join, JOIN),
45 SILC_CLIENT_CMD_REPLY(motd, MOTD),
46 SILC_CLIENT_CMD_REPLY(umode, UMODE),
47 SILC_CLIENT_CMD_REPLY(cmode, CMODE),
48 SILC_CLIENT_CMD_REPLY(kick, KICK),
49 SILC_CLIENT_CMD_REPLY(restart, RESTART),
50 SILC_CLIENT_CMD_REPLY(close, CLOSE),
51 SILC_CLIENT_CMD_REPLY(die, DIE),
52 SILC_CLIENT_CMD_REPLY(silcoper, SILCOPER),
53 SILC_CLIENT_CMD_REPLY(leave, LEAVE),
54 SILC_CLIENT_CMD_REPLY(names, NAMES),
59 /* Status message structure. Messages are defined below. */
61 SilcCommandStatus status;
63 } SilcCommandStatusMessage;
65 /* Status messages returned by the server */
66 #define STAT(x) SILC_STATUS_ERR_##x
67 const SilcCommandStatusMessage silc_command_status_messages[] = {
69 { STAT(NO_SUCH_NICK), "No such nickname" },
70 { STAT(NO_SUCH_CHANNEL), "No such channel" },
71 { STAT(NO_SUCH_SERVER), "No such server" },
72 { STAT(TOO_MANY_TARGETS), "Duplicate recipients. No message delivered" },
73 { STAT(NO_RECIPIENT), "No recipient given" },
74 { STAT(UNKNOWN_COMMAND), "Unknown command" },
75 { STAT(WILDCARDS), "Unknown command" },
76 { STAT(NO_CLIENT_ID), "No Client ID given" },
77 { STAT(NO_CHANNEL_ID), "No Channel ID given" },
78 { STAT(NO_SERVER_ID), "No Server ID given" },
79 { STAT(BAD_CLIENT_ID), "Bad Client ID" },
80 { STAT(BAD_CHANNEL_ID), "Bad Channel ID" },
81 { STAT(NO_SUCH_CLIENT_ID), "No such Client ID" },
82 { STAT(NO_SUCH_CHANNEL_ID),"No such Channel ID" },
83 { STAT(NICKNAME_IN_USE), "Nickname already exists" },
84 { STAT(NOT_ON_CHANNEL), "You are not on that channel" },
85 { STAT(USER_ON_CHANNEL), "User already on channel" },
86 { STAT(NOT_REGISTERED), "You have not registered" },
87 { STAT(NOT_ENOUGH_PARAMS), "Not enough parameters" },
88 { STAT(TOO_MANY_PARAMS), "Too many parameters" },
89 { STAT(PERM_DENIED), "Your host is not among the privileged" },
90 { STAT(BANNED_FROM_SERVER),"You are banned from this server" },
91 { STAT(BAD_PASSWORD), "Cannot join channel. Incorrect password" },
92 { STAT(CHANNEL_IS_FULL), "Cannot join channel. Channel is full" },
93 { STAT(NOT_INVITED), "Cannot join channel. You have not been invited" },
94 { STAT(BANNED_FROM_CHANNEL), "Cannot join channel. You have been banned" },
95 { STAT(UNKNOWN_MODE), "Unknown mode" },
96 { STAT(NOT_YOU), "Cannot change mode for other users" },
97 { STAT(NO_CHANNEL_PRIV), "Permission denied. You are not channel operator" },
98 { STAT(NO_SERVER_PRIV), "Permission denied. You are not server operator" },
99 { STAT(NO_ROUTER_PRIV), "Permission denied. You are not SILC operator" },
100 { STAT(BAD_NICKNAME), "Bad nickname" },
101 { STAT(BAD_CHANNEL), "Bad channel name" },
102 { STAT(AUTH_FAILED), "Authentication failed" },
107 /* Command reply operation that is called at the end of all command replys.
108 Usage: COMMAND_REPLY((ARGS, argument1, argument2, etc...)), */
109 #define COMMAND_REPLY(args) cmd->client->ops->command_reply args
110 #define ARGS cmd->client, cmd->sock->user_data, \
111 cmd->payload, TRUE, silc_command_get(cmd->payload)
113 /* Error reply to application. Usage: COMMAND_REPLY_ERROR; */
114 #define COMMAND_REPLY_ERROR cmd->client->ops->command_reply(cmd->client, \
115 cmd->sock->user_data, cmd->payload, FALSE, silc_command_get(cmd->payload))
117 /* Process received command reply. */
119 void silc_client_command_reply_process(SilcClient client,
120 SilcSocketConnection sock,
121 SilcPacketContext *packet)
123 SilcBuffer buffer = packet->buffer;
124 SilcClientCommandReplyContext ctx;
125 SilcCommandPayload payload;
127 /* Get command reply payload from packet */
128 payload = silc_command_parse_payload(buffer);
130 /* Silently ignore bad reply packet */
131 SILC_LOG_DEBUG(("Bad command reply packet"));
135 /* Allocate command reply context. This must be free'd by the
136 command reply routine receiving it. */
137 ctx = silc_calloc(1, sizeof(*ctx));
138 ctx->client = client;
140 ctx->payload = payload;
141 ctx->packet = packet;
143 /* Check for pending commands and mark to be exeucted */
144 SILC_CLIENT_COMMAND_CHECK_PENDING(ctx);
146 /* Execute command reply */
147 SILC_CLIENT_COMMAND_REPLY_EXEC(ctx);
150 /* Returns status message string */
153 silc_client_command_status_message(SilcCommandStatus status)
157 for (i = 0; silc_command_status_messages[i].message; i++) {
158 if (silc_command_status_messages[i].status == status)
162 if (silc_command_status_messages[i].message == NULL)
165 return silc_command_status_messages[i].message;
168 /* Free command reply context and its internals. */
170 void silc_client_command_reply_free(SilcClientCommandReplyContext cmd)
173 silc_command_free_payload(cmd->payload);
178 /* Received reply for WHOIS command. This maybe called several times
179 for one WHOIS command as server may reply with list of results. */
180 /* Sends to application: (no arguments) */
182 SILC_CLIENT_CMD_REPLY_FUNC(whois)
184 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
185 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
186 SilcCommandStatus status;
189 SILC_LOG_DEBUG(("Start"));
191 tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
192 SILC_GET16_MSB(status, tmp);
193 if (status != SILC_STATUS_OK) {
194 if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
195 /* Take nickname which may be provided */
196 tmp = silc_command_get_arg_type(cmd->payload, 3, NULL);
198 cmd->client->ops->say(cmd->client, conn, "%s: %s", tmp,
199 silc_client_command_status_message(status));
201 cmd->client->ops->say(cmd->client, conn, "%s",
202 silc_client_command_status_message(status));
206 cmd->client->ops->say(cmd->client, conn,
207 "%s", silc_client_command_status_message(status));
213 /* Display one whois reply */
214 if (status == SILC_STATUS_OK) {
217 unsigned char *id_data;
218 char *nickname = NULL, *username = NULL;
219 char *realname = NULL;
221 memset(buf, 0, sizeof(buf));
223 argc = silc_command_get_arg_num(cmd->payload);
224 id_data = silc_command_get_arg_type(cmd->payload, 2, NULL);
226 nickname = silc_command_get_arg_type(cmd->payload, 3, &len);
228 strncat(buf, nickname, len);
229 strncat(buf, " is ", 4);
232 username = silc_command_get_arg_type(cmd->payload, 4, &len);
234 strncat(buf, username, len);
237 realname = silc_command_get_arg_type(cmd->payload, 5, &len);
239 strncat(buf, " (", 2);
240 strncat(buf, realname, len);
241 strncat(buf, ")", 1);
245 /* Save received Client ID to ID cache */
246 /* XXX Maybe should not be saved as /MSG will get confused */
247 id = silc_id_str2id(id_data, SILC_ID_CLIENT);
248 client->current_conn->client_id_cache_count[(int)nickname[0] - 32] =
249 silc_idcache_add(&client->current_conn->
250 client_id_cache[(int)nickname[0] - 32],
251 client->current_conn->
252 client_id_cache_count[(int)nickname[0] - 32],
253 strdup(nickname), SILC_ID_CLIENT, id, NULL);
256 cmd->client->ops->say(cmd->client, conn, "%s", buf);
258 /* Notify application */
259 COMMAND_REPLY((ARGS));
262 if (status == SILC_STATUS_LIST_START) {
266 if (status == SILC_STATUS_LIST_END) {
270 SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_WHOIS);
273 silc_client_command_reply_free(cmd);
276 SILC_CLIENT_CMD_REPLY_FUNC(whowas)
280 /* Received reply for IDENTIFY command. This maybe called several times
281 for one IDENTIFY command as server may reply with list of results.
282 This is totally silent and does not print anything on screen. */
284 SILC_CLIENT_CMD_REPLY_FUNC(identify)
286 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
287 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
288 SilcClientEntry client_entry;
289 SilcCommandStatus status;
292 SILC_LOG_DEBUG(("Start"));
294 tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
295 SILC_GET16_MSB(status, tmp);
296 if (status != SILC_STATUS_OK) {
297 if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
298 /* Take nickname which may be provided */
299 tmp = silc_command_get_arg_type(cmd->payload, 3, NULL);
301 cmd->client->ops->say(cmd->client, conn, "%s: %s", tmp,
302 silc_client_command_status_message(status));
304 cmd->client->ops->say(cmd->client, conn, "%s",
305 silc_client_command_status_message(status));
309 cmd->client->ops->say(cmd->client, conn,
310 "%s", silc_client_command_status_message(status));
316 /* Display one whois reply */
317 if (status == SILC_STATUS_OK) {
318 unsigned char *id_data;
321 id_data = silc_command_get_arg_type(cmd->payload, 2, NULL);
322 nickname = silc_command_get_arg_type(cmd->payload, 3, NULL);
324 /* Allocate client entry */
325 client_entry = silc_calloc(1, sizeof(*client_entry));
326 client_entry->id = silc_id_str2id(id_data, SILC_ID_CLIENT);
327 client_entry->nickname = strdup(nickname);
329 /* Save received Client ID to ID cache */
330 silc_idcache_add(conn->client_cache, client_entry->nickname,
331 SILC_ID_CLIENT, client_entry->id, client_entry, TRUE);
334 if (status == SILC_STATUS_LIST_START) {
338 if (status == SILC_STATUS_LIST_END) {
342 SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_IDENTIFY);
345 silc_client_command_reply_free(cmd);
348 /* Received reply for command NICK. If everything went without errors
349 we just received our new Client ID. */
350 /* Sends to application: char * (nickname). */
352 SILC_CLIENT_CMD_REPLY_FUNC(nick)
354 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
355 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
356 SilcCommandStatus status;
357 unsigned char *tmp, *id_string;
360 SILC_LOG_DEBUG(("Start"));
362 tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
363 SILC_GET16_MSB(status, tmp);
364 if (status != SILC_STATUS_OK) {
365 cmd->client->ops->say(cmd->client, conn, "Cannot set nickname: %s",
366 silc_client_command_status_message(status));
371 argc = silc_command_get_arg_num(cmd->payload);
372 if (argc < 2 || argc > 2) {
373 cmd->client->ops->say(cmd->client, conn,
374 "Cannot set nickname: bad reply to command");
379 /* Take received Client ID */
380 id_string = silc_command_get_arg_type(cmd->payload, 2, NULL);
381 silc_client_receive_new_id(cmd->client, cmd->sock, id_string);
383 /* Notify application */
384 COMMAND_REPLY((ARGS, conn->nickname));
387 silc_client_command_reply_free(cmd);
390 SILC_CLIENT_CMD_REPLY_FUNC(list)
394 SILC_CLIENT_CMD_REPLY_FUNC(topic)
398 /* Received reply to invite command. */
399 /* Sends to application: (no arguments) */
401 SILC_CLIENT_CMD_REPLY_FUNC(invite)
403 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
404 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
405 SilcCommandStatus status;
408 tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
409 SILC_GET16_MSB(status, tmp);
410 if (status != SILC_STATUS_OK) {
411 cmd->client->ops->say(cmd->client, conn,
412 "%s", silc_client_command_status_message(status));
413 silc_client_command_reply_free(cmd);
418 /* Notify application */
419 COMMAND_REPLY((ARGS));
421 silc_client_command_reply_free(cmd);
424 SILC_CLIENT_CMD_REPLY_FUNC(quit)
428 SILC_CLIENT_CMD_REPLY_FUNC(kill)
432 /* Received reply to INFO command. We receive the server ID and some
433 information about the server user requested. */
434 /* Sends to application: char * (server information) */
436 SILC_CLIENT_CMD_REPLY_FUNC(info)
438 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
439 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
440 SilcClient client = cmd->client;
441 SilcCommandStatus status;
444 tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
445 SILC_GET16_MSB(status, tmp);
446 if (status != SILC_STATUS_OK) {
447 cmd->client->ops->say(cmd->client, conn,
448 "%s", silc_client_command_status_message(status));
449 silc_client_command_reply_free(cmd);
455 tmp = silc_command_get_arg_type(cmd->payload, 2, NULL);
459 /* XXX save server id */
461 /* Get server info */
462 tmp = silc_command_get_arg_type(cmd->payload, 3, NULL);
466 client->ops->say(cmd->client, conn, "Info: %s", tmp);
468 /* Notify application */
469 COMMAND_REPLY((ARGS, (char *)tmp));
472 silc_client_command_reply_free(cmd);
475 SILC_CLIENT_CMD_REPLY_FUNC(connect)
479 /* Received reply to PING command. The reply time is shown to user. */
480 /* Sends to application: (no arguments) */
482 SILC_CLIENT_CMD_REPLY_FUNC(ping)
484 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
485 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
486 SilcCommandStatus status;
490 time_t diff, curtime;
492 tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
493 SILC_GET16_MSB(status, tmp);
494 if (status != SILC_STATUS_OK) {
495 cmd->client->ops->say(cmd->client, conn,
496 "%s", silc_client_command_status_message(status));
501 curtime = time(NULL);
502 id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_type);
504 for (i = 0; i < conn->ping_count; i++) {
505 if (!SILC_ID_SERVER_COMPARE(conn->ping[i].dest_id, id)) {
506 diff = curtime - conn->ping[i].start_time;
507 cmd->client->ops->say(cmd->client, conn,
508 "Ping reply from %s: %d second%s",
509 conn->ping[i].dest_name, diff,
510 diff == 1 ? "" : "s");
512 conn->ping[i].start_time = 0;
513 silc_free(conn->ping[i].dest_id);
514 conn->ping[i].dest_id = NULL;
515 silc_free(conn->ping[i].dest_name);
516 conn->ping[i].dest_name = NULL;
518 /* Notify application */
519 COMMAND_REPLY((ARGS));
525 silc_client_command_reply_free(cmd);
528 SILC_CLIENT_CMD_REPLY_FUNC(oper)
532 /* Received reply for JOIN command. */
534 SILC_CLIENT_CMD_REPLY_FUNC(join)
536 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
537 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
538 SilcClient client = cmd->client;
539 SilcCommandStatus status;
540 unsigned int argc, mode;
541 unsigned char *id_string;
542 char *topic, *tmp, *channel_name;
544 SILC_LOG_DEBUG(("Start"));
546 tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
547 SILC_GET16_MSB(status, tmp);
548 if (status != SILC_STATUS_OK) {
549 cmd->client->ops->say(cmd->client, conn,
550 "%s", silc_client_command_status_message(status));
555 argc = silc_command_get_arg_num(cmd->payload);
556 if (argc < 3 || argc > 4) {
557 cmd->client->ops->say(cmd->client, conn,
558 "Cannot join channel: Bad reply packet");
563 /* Get channel name */
564 tmp = silc_command_get_arg_type(cmd->payload, 2, NULL);
566 cmd->client->ops->say(cmd->client, conn,
567 "Cannot join channel: Bad reply packet");
571 channel_name = strdup(tmp);
574 id_string = silc_command_get_arg_type(cmd->payload, 3, NULL);
576 cmd->client->ops->say(cmd->client, conn,
577 "Cannot join channel: Bad reply packet");
582 /* Get channel mode */
583 tmp = silc_command_get_arg_type(cmd->payload, 4, NULL);
585 SILC_GET32_MSB(mode, tmp);
590 topic = silc_command_get_arg_type(cmd->payload, 5, NULL);
592 /* Save received Channel ID */
593 silc_client_new_channel_id(cmd->client, cmd->sock, channel_name,
597 client->ops->say(cmd->client, conn,
598 "Topic for %s: %s", channel_name, topic);
600 /* Notify application */
601 COMMAND_REPLY((ARGS, channel_name, topic));
604 silc_client_command_reply_free(cmd);
607 SILC_CLIENT_CMD_REPLY_FUNC(motd)
611 SILC_CLIENT_CMD_REPLY_FUNC(umode)
615 SILC_CLIENT_CMD_REPLY_FUNC(cmode)
619 SILC_CLIENT_CMD_REPLY_FUNC(kick)
623 SILC_CLIENT_CMD_REPLY_FUNC(restart)
627 SILC_CLIENT_CMD_REPLY_FUNC(close)
631 SILC_CLIENT_CMD_REPLY_FUNC(die)
635 SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
639 /* Reply to LEAVE command. */
640 /* Sends to application: (no arguments) */
642 SILC_CLIENT_CMD_REPLY_FUNC(leave)
644 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
645 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
646 SilcCommandStatus status;
649 tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
650 SILC_GET16_MSB(status, tmp);
651 if (status != SILC_STATUS_OK) {
652 cmd->client->ops->say(cmd->client, conn,
653 "%s", silc_client_command_status_message(status));
658 /* Notify application */
659 COMMAND_REPLY((ARGS));
661 silc_client_command_reply_free(cmd);
664 /* Reply to NAMES command. Received list of client names on the channel
667 SILC_CLIENT_CMD_REPLY_FUNC(names)
669 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
670 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
671 SilcCommandStatus status;
672 SilcIDCacheEntry id_cache = NULL;
673 SilcChannelEntry channel;
674 SilcChannelID *channel_id = NULL;
675 SilcBuffer client_id_list;
678 int i, len1, len2, list_count = 0;
680 SILC_LOG_DEBUG(("Start"));
682 tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
683 SILC_GET16_MSB(status, tmp);
684 if (status != SILC_STATUS_OK) {
685 cmd->client->ops->say(cmd->client, conn,
686 "%s", silc_client_command_status_message(status));
692 tmp = silc_command_get_arg_type(cmd->payload, 2, NULL);
694 cmd->client->ops->say(cmd->client, conn,
695 "Cannot get user list: Bad reply packet");
699 channel_id = silc_id_str2id(tmp, SILC_ID_CHANNEL);
701 /* Get the name list of the channel */
702 name_list = silc_command_get_arg_type(cmd->payload, 3, &len1);
704 cmd->client->ops->say(cmd->client, conn,
705 "Cannot get user list: Bad reply packet");
710 /* Get Client ID list */
711 tmp = silc_command_get_arg_type(cmd->payload, 4, &len2);
713 cmd->client->ops->say(cmd->client, conn,
714 "Cannot get user list: Bad reply packet");
719 client_id_list = silc_buffer_alloc(len2);
720 silc_buffer_pull_tail(client_id_list, len2);
721 silc_buffer_put(client_id_list, tmp, len2);
723 /* Get the channel name */
724 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
725 SILC_ID_CHANNEL, &id_cache)) {
730 channel = (SilcChannelEntry)id_cache->context;
732 /* If there is pending command we know that user has called this command
733 and we will handle the name list differently. */
735 /* We will resolve all the necessary information about the people
736 on the channel. Only after that will we display the user list. */
737 for (i = 0; i < len1; i++) {
741 silc_client_command_pending_del(SILC_COMMAND_NAMES);
743 /* there is no pending callback it means that this command reply
744 has been received without calling the command, ie. server has sent
745 the reply without getting the command from us first. This happens
746 with SILC servers that sends NAMES reply after joining to a channel. */
748 /* Remove commas from list */
749 for (i = 0; i < len1; i++)
750 if (name_list[i] == ',') {
755 cmd->client->ops->say(cmd->client, conn,
756 "Users on %s: %s", channel->channel_name, name_list);
759 /* Cache the received name list and client ID's. This cache expires
760 whenever server sends notify message to channel. It means two things;
761 some user has joined or leaved the channel. */
762 for (i = 0; i < list_count; i++) {
763 int nick_len = strcspn(name_list, " ");
764 char *nickname = silc_calloc(nick_len, sizeof(*nickname));
765 SilcClientID *client_id;
766 SilcClientEntry client;
768 memcpy(nickname, name_list, nick_len);
769 client_id = silc_id_str2id(client_id_list->data, SILC_ID_CLIENT);
770 silc_buffer_pull(client_id_list, SILC_ID_CLIENT_LEN);
772 client = silc_calloc(1, sizeof(*client));
773 client->id = client_id;
774 client->nickname = nickname;
776 silc_idcache_add(conn->client_cache, nickname, SILC_ID_CLIENT,
777 client_id, (void *)client, TRUE);
778 name_list = name_list + nick_len + 1;
781 silc_buffer_free(client_id_list);
785 silc_free(channel_id);
786 silc_client_command_reply_free(cmd);