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, status, 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, status, \
116 silc_command_get(cmd->payload))
118 /* Process received command reply. */
120 void silc_client_command_reply_process(SilcClient client,
121 SilcSocketConnection sock,
122 SilcPacketContext *packet)
124 SilcBuffer buffer = packet->buffer;
125 SilcClientCommandReplyContext ctx;
126 SilcCommandPayload payload;
128 /* Get command reply payload from packet */
129 payload = silc_command_parse_payload(buffer);
131 /* Silently ignore bad reply packet */
132 SILC_LOG_DEBUG(("Bad command reply packet"));
136 /* Allocate command reply context. This must be free'd by the
137 command reply routine receiving it. */
138 ctx = silc_calloc(1, sizeof(*ctx));
139 ctx->client = client;
141 ctx->payload = payload;
142 ctx->packet = packet;
144 /* Check for pending commands and mark to be exeucted */
145 SILC_CLIENT_COMMAND_CHECK_PENDING(ctx);
147 /* Execute command reply */
148 SILC_CLIENT_COMMAND_REPLY_EXEC(ctx);
151 /* Returns status message string */
154 silc_client_command_status_message(SilcCommandStatus status)
158 for (i = 0; silc_command_status_messages[i].message; i++) {
159 if (silc_command_status_messages[i].status == status)
163 if (silc_command_status_messages[i].message == NULL)
166 return silc_command_status_messages[i].message;
169 /* Free command reply context and its internals. */
171 void silc_client_command_reply_free(SilcClientCommandReplyContext cmd)
174 silc_command_free_payload(cmd->payload);
179 /* Received reply for WHOIS command. This maybe called several times
180 for one WHOIS command as server may reply with list of results. */
181 /* Sends to application: (no arguments) */
183 SILC_CLIENT_CMD_REPLY_FUNC(whois)
185 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
186 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
187 SilcCommandStatus status;
190 SILC_LOG_DEBUG(("Start"));
192 tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
193 SILC_GET16_MSB(status, tmp);
194 if (status != SILC_STATUS_OK) {
195 if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
196 /* Take nickname which may be provided */
197 tmp = silc_command_get_arg_type(cmd->payload, 3, NULL);
199 cmd->client->ops->say(cmd->client, conn, "%s: %s", tmp,
200 silc_client_command_status_message(status));
202 cmd->client->ops->say(cmd->client, conn, "%s",
203 silc_client_command_status_message(status));
207 cmd->client->ops->say(cmd->client, conn,
208 "%s", silc_client_command_status_message(status));
214 /* Display one whois reply */
215 if (status == SILC_STATUS_OK) {
218 unsigned char *id_data;
219 char *nickname = NULL, *username = NULL;
220 char *realname = NULL;
222 memset(buf, 0, sizeof(buf));
224 argc = silc_command_get_arg_num(cmd->payload);
225 id_data = silc_command_get_arg_type(cmd->payload, 2, NULL);
227 nickname = silc_command_get_arg_type(cmd->payload, 3, &len);
229 strncat(buf, nickname, len);
230 strncat(buf, " is ", 4);
233 username = silc_command_get_arg_type(cmd->payload, 4, &len);
235 strncat(buf, username, len);
238 realname = silc_command_get_arg_type(cmd->payload, 5, &len);
240 strncat(buf, " (", 2);
241 strncat(buf, realname, len);
242 strncat(buf, ")", 1);
246 /* Save received Client ID to ID cache */
247 /* XXX Maybe should not be saved as /MSG will get confused */
248 id = silc_id_str2id(id_data, SILC_ID_CLIENT);
249 client->current_conn->client_id_cache_count[(int)nickname[0] - 32] =
250 silc_idcache_add(&client->current_conn->
251 client_id_cache[(int)nickname[0] - 32],
252 client->current_conn->
253 client_id_cache_count[(int)nickname[0] - 32],
254 strdup(nickname), SILC_ID_CLIENT, id, NULL);
257 cmd->client->ops->say(cmd->client, conn, "%s", buf);
259 /* Notify application */
260 COMMAND_REPLY((ARGS));
263 if (status == SILC_STATUS_LIST_START) {
267 if (status == SILC_STATUS_LIST_END) {
271 SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_WHOIS);
274 silc_client_command_reply_free(cmd);
277 SILC_CLIENT_CMD_REPLY_FUNC(whowas)
281 /* Received reply for IDENTIFY command. This maybe called several times
282 for one IDENTIFY command as server may reply with list of results.
283 This is totally silent and does not print anything on screen. */
285 SILC_CLIENT_CMD_REPLY_FUNC(identify)
287 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
288 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
289 SilcClientEntry client_entry;
290 SilcCommandStatus status;
293 SILC_LOG_DEBUG(("Start"));
295 tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
296 SILC_GET16_MSB(status, tmp);
297 if (status != SILC_STATUS_OK) {
298 if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
299 /* Take nickname which may be provided */
300 tmp = silc_command_get_arg_type(cmd->payload, 3, NULL);
302 cmd->client->ops->say(cmd->client, conn, "%s: %s", tmp,
303 silc_client_command_status_message(status));
305 cmd->client->ops->say(cmd->client, conn, "%s",
306 silc_client_command_status_message(status));
310 cmd->client->ops->say(cmd->client, conn,
311 "%s", silc_client_command_status_message(status));
317 /* Display one whois reply */
318 if (status == SILC_STATUS_OK) {
319 unsigned char *id_data;
322 id_data = silc_command_get_arg_type(cmd->payload, 2, NULL);
323 nickname = silc_command_get_arg_type(cmd->payload, 3, NULL);
325 /* Allocate client entry */
326 client_entry = silc_calloc(1, sizeof(*client_entry));
327 client_entry->id = silc_id_str2id(id_data, SILC_ID_CLIENT);
328 client_entry->nickname = strdup(nickname);
330 /* Save received Client ID to ID cache */
331 silc_idcache_add(conn->client_cache, client_entry->nickname,
332 SILC_ID_CLIENT, client_entry->id, client_entry, TRUE);
335 if (status == SILC_STATUS_LIST_START) {
339 if (status == SILC_STATUS_LIST_END) {
343 SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_IDENTIFY);
346 silc_client_command_reply_free(cmd);
349 /* Received reply for command NICK. If everything went without errors
350 we just received our new Client ID. */
351 /* Sends to application: char * (nickname). */
353 SILC_CLIENT_CMD_REPLY_FUNC(nick)
355 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
356 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
357 SilcCommandStatus status;
358 unsigned char *tmp, *id_string;
361 SILC_LOG_DEBUG(("Start"));
363 tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
364 SILC_GET16_MSB(status, tmp);
365 if (status != SILC_STATUS_OK) {
366 cmd->client->ops->say(cmd->client, conn, "Cannot set nickname: %s",
367 silc_client_command_status_message(status));
372 argc = silc_command_get_arg_num(cmd->payload);
373 if (argc < 2 || argc > 2) {
374 cmd->client->ops->say(cmd->client, conn,
375 "Cannot set nickname: bad reply to command");
380 /* Take received Client ID */
381 id_string = silc_command_get_arg_type(cmd->payload, 2, NULL);
382 silc_client_receive_new_id(cmd->client, cmd->sock, id_string);
384 /* Notify application */
385 COMMAND_REPLY((ARGS, conn->nickname));
388 silc_client_command_reply_free(cmd);
391 SILC_CLIENT_CMD_REPLY_FUNC(list)
395 SILC_CLIENT_CMD_REPLY_FUNC(topic)
399 /* Received reply to invite command. */
400 /* Sends to application: (no arguments) */
402 SILC_CLIENT_CMD_REPLY_FUNC(invite)
404 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
405 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
406 SilcCommandStatus status;
409 tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
410 SILC_GET16_MSB(status, tmp);
411 if (status != SILC_STATUS_OK) {
412 cmd->client->ops->say(cmd->client, conn,
413 "%s", silc_client_command_status_message(status));
414 silc_client_command_reply_free(cmd);
419 /* Notify application */
420 COMMAND_REPLY((ARGS));
422 silc_client_command_reply_free(cmd);
425 SILC_CLIENT_CMD_REPLY_FUNC(quit)
429 SILC_CLIENT_CMD_REPLY_FUNC(kill)
433 /* Received reply to INFO command. We receive the server ID and some
434 information about the server user requested. */
435 /* Sends to application: char * (server information) */
437 SILC_CLIENT_CMD_REPLY_FUNC(info)
439 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
440 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
441 SilcClient client = cmd->client;
442 SilcCommandStatus status;
445 tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
446 SILC_GET16_MSB(status, tmp);
447 if (status != SILC_STATUS_OK) {
448 cmd->client->ops->say(cmd->client, conn,
449 "%s", silc_client_command_status_message(status));
450 silc_client_command_reply_free(cmd);
456 tmp = silc_command_get_arg_type(cmd->payload, 2, NULL);
460 /* XXX save server id */
462 /* Get server info */
463 tmp = silc_command_get_arg_type(cmd->payload, 3, NULL);
467 client->ops->say(cmd->client, conn, "Info: %s", tmp);
469 /* Notify application */
470 COMMAND_REPLY((ARGS, (char *)tmp));
473 silc_client_command_reply_free(cmd);
476 SILC_CLIENT_CMD_REPLY_FUNC(connect)
480 /* Received reply to PING command. The reply time is shown to user. */
481 /* Sends to application: (no arguments) */
483 SILC_CLIENT_CMD_REPLY_FUNC(ping)
485 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
486 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
487 SilcCommandStatus status;
491 time_t diff, curtime;
493 tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
494 SILC_GET16_MSB(status, tmp);
495 if (status != SILC_STATUS_OK) {
496 cmd->client->ops->say(cmd->client, conn,
497 "%s", silc_client_command_status_message(status));
502 curtime = time(NULL);
503 id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_type);
505 for (i = 0; i < conn->ping_count; i++) {
506 if (!SILC_ID_SERVER_COMPARE(conn->ping[i].dest_id, id)) {
507 diff = curtime - conn->ping[i].start_time;
508 cmd->client->ops->say(cmd->client, conn,
509 "Ping reply from %s: %d second%s",
510 conn->ping[i].dest_name, diff,
511 diff == 1 ? "" : "s");
513 conn->ping[i].start_time = 0;
514 silc_free(conn->ping[i].dest_id);
515 conn->ping[i].dest_id = NULL;
516 silc_free(conn->ping[i].dest_name);
517 conn->ping[i].dest_name = NULL;
519 /* Notify application */
520 COMMAND_REPLY((ARGS));
526 silc_client_command_reply_free(cmd);
529 SILC_CLIENT_CMD_REPLY_FUNC(oper)
533 /* Received reply for JOIN command. */
535 SILC_CLIENT_CMD_REPLY_FUNC(join)
537 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
538 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
539 SilcClient client = cmd->client;
540 SilcCommandStatus status;
541 unsigned int argc, mode;
542 unsigned char *id_string;
543 char *topic, *tmp, *channel_name;
545 SILC_LOG_DEBUG(("Start"));
547 tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
548 SILC_GET16_MSB(status, tmp);
549 if (status != SILC_STATUS_OK) {
550 cmd->client->ops->say(cmd->client, conn,
551 "%s", silc_client_command_status_message(status));
556 argc = silc_command_get_arg_num(cmd->payload);
557 if (argc < 3 || argc > 4) {
558 cmd->client->ops->say(cmd->client, conn,
559 "Cannot join channel: Bad reply packet");
564 /* Get channel name */
565 tmp = silc_command_get_arg_type(cmd->payload, 2, NULL);
567 cmd->client->ops->say(cmd->client, conn,
568 "Cannot join channel: Bad reply packet");
572 channel_name = strdup(tmp);
575 id_string = silc_command_get_arg_type(cmd->payload, 3, NULL);
577 cmd->client->ops->say(cmd->client, conn,
578 "Cannot join channel: Bad reply packet");
583 /* Get channel mode */
584 tmp = silc_command_get_arg_type(cmd->payload, 4, NULL);
586 SILC_GET32_MSB(mode, tmp);
591 topic = silc_command_get_arg_type(cmd->payload, 5, NULL);
593 /* Save received Channel ID */
594 silc_client_new_channel_id(cmd->client, cmd->sock, channel_name,
598 client->ops->say(cmd->client, conn,
599 "Topic for %s: %s", channel_name, topic);
601 /* Notify application */
602 COMMAND_REPLY((ARGS, channel_name, topic));
605 silc_client_command_reply_free(cmd);
608 SILC_CLIENT_CMD_REPLY_FUNC(motd)
612 SILC_CLIENT_CMD_REPLY_FUNC(umode)
616 SILC_CLIENT_CMD_REPLY_FUNC(cmode)
620 SILC_CLIENT_CMD_REPLY_FUNC(kick)
624 SILC_CLIENT_CMD_REPLY_FUNC(restart)
628 SILC_CLIENT_CMD_REPLY_FUNC(close)
632 SILC_CLIENT_CMD_REPLY_FUNC(die)
636 SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
640 /* Reply to LEAVE command. */
641 /* Sends to application: (no arguments) */
643 SILC_CLIENT_CMD_REPLY_FUNC(leave)
645 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
646 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
647 SilcCommandStatus status;
650 tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
651 SILC_GET16_MSB(status, tmp);
652 if (status != SILC_STATUS_OK) {
653 cmd->client->ops->say(cmd->client, conn,
654 "%s", silc_client_command_status_message(status));
659 /* Notify application */
660 COMMAND_REPLY((ARGS));
662 silc_client_command_reply_free(cmd);
665 /* Reply to NAMES command. Received list of client names on the channel
668 SILC_CLIENT_CMD_REPLY_FUNC(names)
670 SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
671 SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
672 SilcCommandStatus status;
673 SilcIDCacheEntry id_cache = NULL;
674 SilcChannelEntry channel;
675 SilcChannelID *channel_id = NULL;
676 SilcBuffer client_id_list;
679 int i, len1, len2, list_count = 0;
681 SILC_LOG_DEBUG(("Start"));
683 tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
684 SILC_GET16_MSB(status, tmp);
685 if (status != SILC_STATUS_OK) {
686 cmd->client->ops->say(cmd->client, conn,
687 "%s", silc_client_command_status_message(status));
693 tmp = silc_command_get_arg_type(cmd->payload, 2, NULL);
695 cmd->client->ops->say(cmd->client, conn,
696 "Cannot get user list: Bad reply packet");
700 channel_id = silc_id_str2id(tmp, SILC_ID_CHANNEL);
702 /* Get the name list of the channel */
703 name_list = silc_command_get_arg_type(cmd->payload, 3, &len1);
705 cmd->client->ops->say(cmd->client, conn,
706 "Cannot get user list: Bad reply packet");
711 /* Get Client ID list */
712 tmp = silc_command_get_arg_type(cmd->payload, 4, &len2);
714 cmd->client->ops->say(cmd->client, conn,
715 "Cannot get user list: Bad reply packet");
720 client_id_list = silc_buffer_alloc(len2);
721 silc_buffer_pull_tail(client_id_list, len2);
722 silc_buffer_put(client_id_list, tmp, len2);
724 /* Get the channel name */
725 if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
726 SILC_ID_CHANNEL, &id_cache)) {
731 channel = (SilcChannelEntry)id_cache->context;
733 /* If there is pending command we know that user has called this command
734 and we will handle the name list differently. */
736 /* We will resolve all the necessary information about the people
737 on the channel. Only after that will we display the user list. */
738 for (i = 0; i < len1; i++) {
742 silc_client_command_pending_del(SILC_COMMAND_NAMES);
744 /* there is no pending callback it means that this command reply
745 has been received without calling the command, ie. server has sent
746 the reply without getting the command from us first. This happens
747 with SILC servers that sends NAMES reply after joining to a channel. */
749 /* Remove commas from list */
750 for (i = 0; i < len1; i++)
751 if (name_list[i] == ',') {
756 cmd->client->ops->say(cmd->client, conn,
757 "Users on %s: %s", channel->channel_name, name_list);
760 /* Cache the received name list and client ID's. This cache expires
761 whenever server sends notify message to channel. It means two things;
762 some user has joined or leaved the channel. */
763 for (i = 0; i < list_count; i++) {
764 int nick_len = strcspn(name_list, " ");
765 char *nickname = silc_calloc(nick_len, sizeof(*nickname));
766 SilcClientID *client_id;
767 SilcClientEntry client;
769 memcpy(nickname, name_list, nick_len);
770 client_id = silc_id_str2id(client_id_list->data, SILC_ID_CLIENT);
771 silc_buffer_pull(client_id_list, SILC_ID_CLIENT_LEN);
773 client = silc_calloc(1, sizeof(*client));
774 client->id = client_id;
775 client->nickname = nickname;
777 silc_idcache_add(conn->client_cache, nickname, SILC_ID_CLIENT,
778 client_id, (void *)client, TRUE);
779 name_list = name_list + nick_len + 1;
782 silc_buffer_free(client_id_list);
786 silc_free(channel_id);
787 silc_client_command_reply_free(cmd);