X-Git-Url: http://git.silcnet.org/gitweb/?p=silc.git;a=blobdiff_plain;f=lib%2Fsilcclient%2Fcommand_reply.c;fp=apps%2Fsilc%2Fcommand_reply.c;h=604389376afaa09e6ac221de992ab4d4e297ddff;hp=491808ec8e8bfee1fb062ab945e5cd3d5302a46d;hb=318d79b391bf6288e3e28c840217a7097f3d0392;hpb=0f9738ce962b8498bbed0a75d5fb6fa127e3577f diff --git a/apps/silc/command_reply.c b/lib/silcclient/command_reply.c similarity index 50% rename from apps/silc/command_reply.c rename to lib/silcclient/command_reply.c index 491808ec..60438937 100644 --- a/apps/silc/command_reply.c +++ b/lib/silcclient/command_reply.c @@ -21,16 +21,9 @@ * Command reply functions are "the otherside" of the command functions. * Reply to a command sent by server is handled by these functions. */ -/* - * $Id$ - * $Log$ - * Revision 1.1.1.1 2000/06/27 11:36:56 priikone - * Importet from internal CVS/Added Log headers. - * - * - */ +/* $Id$ */ -#include "clientincludes.h" +#include "clientlibincludes.h" /* Client command reply list. */ SilcClientCommandReply silc_command_reply_list[] = @@ -45,7 +38,6 @@ SilcClientCommandReply silc_command_reply_list[] = SILC_CLIENT_CMD_REPLY(quit, QUIT), SILC_CLIENT_CMD_REPLY(kill, KILL), SILC_CLIENT_CMD_REPLY(info, INFO), - SILC_CLIENT_CMD_REPLY(away, AWAY), SILC_CLIENT_CMD_REPLY(connect, CONNECT), SILC_CLIENT_CMD_REPLY(ping, PING), SILC_CLIENT_CMD_REPLY(oper, OPER), @@ -59,7 +51,7 @@ SilcClientCommandReply silc_command_reply_list[] = SILC_CLIENT_CMD_REPLY(die, DIE), SILC_CLIENT_CMD_REPLY(silcoper, SILCOPER), SILC_CLIENT_CMD_REPLY(leave, LEAVE), - SILC_CLIENT_CMD_REPLY(names, LEAVE), + SILC_CLIENT_CMD_REPLY(names, NAMES), { NULL, 0 }, }; @@ -83,6 +75,7 @@ const SilcCommandStatusMessage silc_command_status_messages[] = { { STAT(WILDCARDS), "Unknown command" }, { STAT(NO_CLIENT_ID), "No Client ID given" }, { STAT(NO_CHANNEL_ID), "No Channel ID given" }, + { STAT(NO_SERVER_ID), "No Server ID given" }, { STAT(BAD_CLIENT_ID), "Bad Client ID" }, { STAT(BAD_CHANNEL_ID), "Bad Channel ID" }, { STAT(NO_SUCH_CLIENT_ID), "No such Client ID" }, @@ -111,12 +104,23 @@ const SilcCommandStatusMessage silc_command_status_messages[] = { { 0, NULL } }; +/* Command reply operation that is called at the end of all command replys. + Usage: COMMAND_REPLY((ARGS, argument1, argument2, etc...)), */ +#define COMMAND_REPLY(args) cmd->client->ops->command_reply args +#define ARGS cmd->client, cmd->sock->user_data, \ + cmd->payload, TRUE, silc_command_get(cmd->payload) + +/* Error reply to application. Usage: COMMAND_REPLY_ERROR; */ +#define COMMAND_REPLY_ERROR cmd->client->ops->command_reply(cmd->client, \ + cmd->sock->user_data, cmd->payload, FALSE, silc_command_get(cmd->payload)) + /* Process received command reply. */ void silc_client_command_reply_process(SilcClient client, SilcSocketConnection sock, - SilcBuffer buffer) + SilcPacketContext *packet) { + SilcBuffer buffer = packet->buffer; SilcClientCommandReplyContext ctx; SilcCommandPayload payload; @@ -134,6 +138,7 @@ void silc_client_command_reply_process(SilcClient client, ctx->client = client; ctx->sock = sock; ctx->payload = payload; + ctx->packet = packet; /* Check for pending commands and mark to be exeucted */ SILC_CLIENT_COMMAND_CHECK_PENDING(ctx); @@ -172,11 +177,12 @@ void silc_client_command_reply_free(SilcClientCommandReplyContext cmd) /* Received reply for WHOIS command. This maybe called several times for one WHOIS command as server may reply with list of results. */ +/* Sends to application: (no arguments) */ SILC_CLIENT_CMD_REPLY_FUNC(whois) { SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; - SilcClient client = cmd->client; + SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; SilcCommandStatus status; unsigned char *tmp; @@ -186,12 +192,20 @@ SILC_CLIENT_CMD_REPLY_FUNC(whois) SILC_GET16_MSB(status, tmp); if (status != SILC_STATUS_OK) { if (status == SILC_STATUS_ERR_NO_SUCH_NICK) { - tmp += 2; - silc_say(cmd->client, "%s: %s", tmp, - silc_client_command_status_message(status)); + /* Take nickname which may be provided */ + tmp = silc_command_get_arg_type(cmd->payload, 3, NULL); + if (tmp) + cmd->client->ops->say(cmd->client, conn, "%s: %s", tmp, + silc_client_command_status_message(status)); + else + cmd->client->ops->say(cmd->client, conn, "%s", + silc_client_command_status_message(status)); + COMMAND_REPLY_ERROR; goto out; } else { - silc_say(cmd->client, "%s", silc_client_command_status_message(status)); + cmd->client->ops->say(cmd->client, conn, + "%s", silc_client_command_status_message(status)); + COMMAND_REPLY_ERROR; goto out; } } @@ -203,7 +217,6 @@ SILC_CLIENT_CMD_REPLY_FUNC(whois) unsigned char *id_data; char *nickname = NULL, *username = NULL; char *realname = NULL; - void *id; memset(buf, 0, sizeof(buf)); @@ -232,16 +245,19 @@ SILC_CLIENT_CMD_REPLY_FUNC(whois) /* Save received Client ID to ID cache */ /* XXX Maybe should not be saved as /MSG will get confused */ id = silc_id_str2id(id_data, SILC_ID_CLIENT); - client->current_win->client_id_cache_count[(int)nickname[0] - 32] = - silc_idcache_add(&client->current_win-> + client->current_conn->client_id_cache_count[(int)nickname[0] - 32] = + silc_idcache_add(&client->current_conn-> client_id_cache[(int)nickname[0] - 32], - client->current_win-> + client->current_conn-> client_id_cache_count[(int)nickname[0] - 32], strdup(nickname), SILC_ID_CLIENT, id, NULL); #endif - silc_say(cmd->client, "%s", buf); - } + cmd->client->ops->say(cmd->client, conn, "%s", buf); + + /* Notify application */ + COMMAND_REPLY((ARGS)); + } if (status == SILC_STATUS_LIST_START) { @@ -268,26 +284,31 @@ SILC_CLIENT_CMD_REPLY_FUNC(whowas) SILC_CLIENT_CMD_REPLY_FUNC(identify) { SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; - SilcClientWindow win = (SilcClientWindow)cmd->sock->user_data; + SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; SilcClientEntry client_entry; SilcCommandStatus status; unsigned char *tmp; SILC_LOG_DEBUG(("Start")); -#define CIDC(x) win->client_id_cache[(x) - 32] -#define CIDCC(x) win->client_id_cache_count[(x) - 32] - tmp = silc_command_get_arg_type(cmd->payload, 1, NULL); SILC_GET16_MSB(status, tmp); if (status != SILC_STATUS_OK) { if (status == SILC_STATUS_ERR_NO_SUCH_NICK) { - tmp += 2; - silc_say(cmd->client, "%s: %s", tmp, - silc_client_command_status_message(status)); + /* Take nickname which may be provided */ + tmp = silc_command_get_arg_type(cmd->payload, 3, NULL); + if (tmp) + cmd->client->ops->say(cmd->client, conn, "%s: %s", tmp, + silc_client_command_status_message(status)); + else + cmd->client->ops->say(cmd->client, conn, "%s", + silc_client_command_status_message(status)); + COMMAND_REPLY_ERROR; goto out; } else { - silc_say(cmd->client, "%s", silc_client_command_status_message(status)); + cmd->client->ops->say(cmd->client, conn, + "%s", silc_client_command_status_message(status)); + COMMAND_REPLY_ERROR; goto out; } } @@ -306,10 +327,8 @@ SILC_CLIENT_CMD_REPLY_FUNC(identify) client_entry->nickname = strdup(nickname); /* Save received Client ID to ID cache */ - CIDCC(nickname[0]) = - silc_idcache_add(&CIDC(nickname[0]), CIDCC(nickname[0]), - client_entry->nickname, SILC_ID_CLIENT, - client_entry->id, client_entry); + silc_idcache_add(conn->client_cache, client_entry->nickname, + SILC_ID_CLIENT, client_entry->id, client_entry, TRUE); } if (status == SILC_STATUS_LIST_START) { @@ -324,17 +343,16 @@ SILC_CLIENT_CMD_REPLY_FUNC(identify) out: silc_client_command_reply_free(cmd); -#undef CIDC -#undef CIDCC } /* Received reply for command NICK. If everything went without errors we just received our new Client ID. */ +/* Sends to application: char * (nickname). */ SILC_CLIENT_CMD_REPLY_FUNC(nick) { SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; - SilcClientWindow win = (SilcClientWindow)cmd->sock->user_data; + SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; SilcCommandStatus status; unsigned char *tmp, *id_string; int argc; @@ -344,14 +362,17 @@ SILC_CLIENT_CMD_REPLY_FUNC(nick) tmp = silc_command_get_arg_type(cmd->payload, 1, NULL); SILC_GET16_MSB(status, tmp); if (status != SILC_STATUS_OK) { - silc_say(cmd->client, "Cannot set nickname: %s", + cmd->client->ops->say(cmd->client, conn, "Cannot set nickname: %s", silc_client_command_status_message(status)); + COMMAND_REPLY_ERROR; goto out; } argc = silc_command_get_arg_num(cmd->payload); if (argc < 2 || argc > 2) { - silc_say(cmd->client, "Cannot set nickname: bad reply to command"); + cmd->client->ops->say(cmd->client, conn, + "Cannot set nickname: bad reply to command"); + COMMAND_REPLY_ERROR; goto out; } @@ -359,9 +380,8 @@ SILC_CLIENT_CMD_REPLY_FUNC(nick) id_string = silc_command_get_arg_type(cmd->payload, 2, NULL); silc_client_receive_new_id(cmd->client, cmd->sock, id_string); - /* Update nickname on screen */ - cmd->client->screen->bottom_line->nickname = win->nickname; - silc_screen_print_bottom_line(cmd->client->screen, 0); + /* Notify application */ + COMMAND_REPLY((ARGS, conn->nickname)); out: silc_client_command_reply_free(cmd); @@ -375,8 +395,30 @@ SILC_CLIENT_CMD_REPLY_FUNC(topic) { } +/* Received reply to invite command. */ +/* Sends to application: (no arguments) */ + SILC_CLIENT_CMD_REPLY_FUNC(invite) { + SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; + SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; + SilcCommandStatus status; + unsigned char *tmp; + + tmp = silc_command_get_arg_type(cmd->payload, 1, NULL); + SILC_GET16_MSB(status, tmp); + if (status != SILC_STATUS_OK) { + cmd->client->ops->say(cmd->client, conn, + "%s", silc_client_command_status_message(status)); + silc_client_command_reply_free(cmd); + COMMAND_REPLY_ERROR; + return; + } + + /* Notify application */ + COMMAND_REPLY((ARGS)); + + silc_client_command_reply_free(cmd); } SILC_CLIENT_CMD_REPLY_FUNC(quit) @@ -387,20 +429,100 @@ SILC_CLIENT_CMD_REPLY_FUNC(kill) { } +/* Received reply to INFO command. We receive the server ID and some + information about the server user requested. */ +/* Sends to application: char * (server information) */ + SILC_CLIENT_CMD_REPLY_FUNC(info) { -} + SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; + SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; + SilcClient client = cmd->client; + SilcCommandStatus status; + unsigned char *tmp; -SILC_CLIENT_CMD_REPLY_FUNC(away) -{ + tmp = silc_command_get_arg_type(cmd->payload, 1, NULL); + SILC_GET16_MSB(status, tmp); + if (status != SILC_STATUS_OK) { + cmd->client->ops->say(cmd->client, conn, + "%s", silc_client_command_status_message(status)); + silc_client_command_reply_free(cmd); + COMMAND_REPLY_ERROR; + return; + } + + /* Get server ID */ + tmp = silc_command_get_arg_type(cmd->payload, 2, NULL); + if (!tmp) + goto out; + + /* XXX save server id */ + + /* Get server info */ + tmp = silc_command_get_arg_type(cmd->payload, 3, NULL); + if (!tmp) + goto out; + + client->ops->say(cmd->client, conn, "Info: %s", tmp); + + /* Notify application */ + COMMAND_REPLY((ARGS, (char *)tmp)); + + out: + silc_client_command_reply_free(cmd); } SILC_CLIENT_CMD_REPLY_FUNC(connect) { } +/* Received reply to PING command. The reply time is shown to user. */ +/* Sends to application: (no arguments) */ + SILC_CLIENT_CMD_REPLY_FUNC(ping) { + SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; + SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; + SilcCommandStatus status; + void *id; + char *tmp; + int i; + time_t diff, curtime; + + tmp = silc_command_get_arg_type(cmd->payload, 1, NULL); + SILC_GET16_MSB(status, tmp); + if (status != SILC_STATUS_OK) { + cmd->client->ops->say(cmd->client, conn, + "%s", silc_client_command_status_message(status)); + COMMAND_REPLY_ERROR; + goto out; + } + + curtime = time(NULL); + id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_type); + + for (i = 0; i < conn->ping_count; i++) { + if (!SILC_ID_SERVER_COMPARE(conn->ping[i].dest_id, id)) { + diff = curtime - conn->ping[i].start_time; + cmd->client->ops->say(cmd->client, conn, + "Ping reply from %s: %d second%s", + conn->ping[i].dest_name, diff, + diff == 1 ? "" : "s"); + + conn->ping[i].start_time = 0; + silc_free(conn->ping[i].dest_id); + conn->ping[i].dest_id = NULL; + silc_free(conn->ping[i].dest_name); + conn->ping[i].dest_name = NULL; + + /* Notify application */ + COMMAND_REPLY((ARGS)); + goto out; + } + } + + out: + silc_client_command_reply_free(cmd); } SILC_CLIENT_CMD_REPLY_FUNC(oper) @@ -412,9 +534,10 @@ SILC_CLIENT_CMD_REPLY_FUNC(oper) SILC_CLIENT_CMD_REPLY_FUNC(join) { SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; + SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; SilcClient client = cmd->client; SilcCommandStatus status; - unsigned int argc; + unsigned int argc, mode; unsigned char *id_string; char *topic, *tmp, *channel_name; @@ -423,35 +546,59 @@ SILC_CLIENT_CMD_REPLY_FUNC(join) tmp = silc_command_get_arg_type(cmd->payload, 1, NULL); SILC_GET16_MSB(status, tmp); if (status != SILC_STATUS_OK) { - silc_say(cmd->client, "%s", silc_client_command_status_message(status)); + cmd->client->ops->say(cmd->client, conn, + "%s", silc_client_command_status_message(status)); + COMMAND_REPLY_ERROR; goto out; } argc = silc_command_get_arg_num(cmd->payload); if (argc < 3 || argc > 4) { - silc_say(cmd->client, "Cannot join channel: Bad reply packet"); + cmd->client->ops->say(cmd->client, conn, + "Cannot join channel: Bad reply packet"); + COMMAND_REPLY_ERROR; goto out; } /* Get channel name */ tmp = silc_command_get_arg_type(cmd->payload, 2, NULL); + if (!tmp) { + cmd->client->ops->say(cmd->client, conn, + "Cannot join channel: Bad reply packet"); + COMMAND_REPLY_ERROR; + goto out; + } channel_name = strdup(tmp); - /* Get channel ID */ + /* Get Channel ID */ id_string = silc_command_get_arg_type(cmd->payload, 3, NULL); + if (!id_string) { + cmd->client->ops->say(cmd->client, conn, + "Cannot join channel: Bad reply packet"); + COMMAND_REPLY_ERROR; + goto out; + } + + /* Get channel mode */ + tmp = silc_command_get_arg_type(cmd->payload, 4, NULL); + if (tmp) + SILC_GET32_MSB(mode, tmp); + else + mode = 0; /* Get topic */ - topic = silc_command_get_arg_type(cmd->payload, 4, NULL); + topic = silc_command_get_arg_type(cmd->payload, 5, NULL); /* Save received Channel ID */ - silc_client_new_channel_id(cmd->client, cmd->sock, channel_name, id_string); - - /* Print channel name on screen */ - client->screen->bottom_line->channel = channel_name; - silc_screen_print_bottom_line(client->screen, 0); + silc_client_new_channel_id(cmd->client, cmd->sock, channel_name, + mode, id_string); if (topic) - silc_say(client, "Topic for %s: %s", channel_name, topic); + client->ops->say(cmd->client, conn, + "Topic for %s: %s", channel_name, topic); + + /* Notify application */ + COMMAND_REPLY((ARGS, channel_name, topic)); out: silc_client_command_reply_free(cmd); @@ -489,63 +636,152 @@ SILC_CLIENT_CMD_REPLY_FUNC(silcoper) { } +/* Reply to LEAVE command. */ +/* Sends to application: (no arguments) */ + SILC_CLIENT_CMD_REPLY_FUNC(leave) { -} + SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; + SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; + SilcCommandStatus status; + unsigned char *tmp; -SILC_CLIENT_CMD_REPLY_FUNC(names) -{ + tmp = silc_command_get_arg_type(cmd->payload, 1, NULL); + SILC_GET16_MSB(status, tmp); + if (status != SILC_STATUS_OK) { + cmd->client->ops->say(cmd->client, conn, + "%s", silc_client_command_status_message(status)); + COMMAND_REPLY_ERROR; + return; + } + + /* Notify application */ + COMMAND_REPLY((ARGS)); + + silc_client_command_reply_free(cmd); } -/* Private message received. This processes the private message and - finally displays it on the screen. */ +/* Reply to NAMES command. Received list of client names on the channel + we requested. */ -SILC_CLIENT_CMD_REPLY_FUNC(msg) +SILC_CLIENT_CMD_REPLY_FUNC(names) { SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; - SilcClient client = cmd->client; - SilcBuffer buffer = (SilcBuffer)cmd->context; - unsigned short nick_len; - unsigned char *nickname, *message; - SilcIDCache *id_cache; - unsigned char *id_string; - void *id; + SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; + SilcCommandStatus status; + SilcIDCacheEntry id_cache = NULL; + SilcChannelEntry channel; + SilcChannelID *channel_id = NULL; + SilcBuffer client_id_list; + unsigned char *tmp; + char *name_list; + int i, len1, len2, list_count = 0; - /* Get nickname */ - silc_buffer_unformat(buffer, - SILC_STR_UI16_NSTRING_ALLOC(&nickname, &nick_len), - SILC_STR_END); - silc_buffer_pull(buffer, 2 + nick_len); + SILC_LOG_DEBUG(("Start")); -#if 0 - /* Get ID of the sender */ - id_string = silc_calloc(SILC_ID_CLIENT_LEN, sizeof(unsigned char *)); - silc_buffer_push(buffer, SILC_ID_CLIENT_LEN + SILC_ID_CLIENT_LEN); - memcpy(id_string, buffer->data, SILC_ID_CLIENT_LEN); - silc_buffer_pull(buffer, SILC_ID_CLIENT_LEN + SILC_ID_CLIENT_LEN); - id = silc_id_str2id(id_string, SILC_ID_CLIENT); - silc_free(id_string); + tmp = silc_command_get_arg_type(cmd->payload, 1, NULL); + SILC_GET16_MSB(status, tmp); + if (status != SILC_STATUS_OK) { + cmd->client->ops->say(cmd->client, conn, + "%s", silc_client_command_status_message(status)); + COMMAND_REPLY_ERROR; + goto out; + } - /* Nickname should be verified if we don't have it in the cache */ - if (silc_idcache_find_by_data(client->current_win-> - client_id_cache[nickname[0] - 32], - client->current_win-> - client_id_cache_count[nickname[0] - 32], - nickname, &id_cache) == FALSE) { + /* Get channel ID */ + tmp = silc_command_get_arg_type(cmd->payload, 2, NULL); + if (!tmp) { + cmd->client->ops->say(cmd->client, conn, + "Cannot get user list: Bad reply packet"); + COMMAND_REPLY_ERROR; + goto out; + } + channel_id = silc_id_str2id(tmp, SILC_ID_CHANNEL); + + /* Get the name list of the channel */ + name_list = silc_command_get_arg_type(cmd->payload, 3, &len1); + if (!name_list) { + cmd->client->ops->say(cmd->client, conn, + "Cannot get user list: Bad reply packet"); + COMMAND_REPLY_ERROR; + goto out; + } - SilcClientCommandContext ctx; - char whois[255]; + /* Get Client ID list */ + tmp = silc_command_get_arg_type(cmd->payload, 4, &len2); + if (!tmp) { + cmd->client->ops->say(cmd->client, conn, + "Cannot get user list: Bad reply packet"); + COMMAND_REPLY_ERROR; + goto out; + } - /* Private message from unknown source, try to resolve it. */ + client_id_list = silc_buffer_alloc(len2); + silc_buffer_pull_tail(client_id_list, len2); + silc_buffer_put(client_id_list, tmp, len2); + /* Get the channel name */ + if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id, + SILC_ID_CHANNEL, &id_cache)) { + COMMAND_REPLY_ERROR; + goto out; + } + + channel = (SilcChannelEntry)id_cache->context; - return; + /* If there is pending command we know that user has called this command + and we will handle the name list differently. */ + if (cmd->callback) { + /* We will resolve all the necessary information about the people + on the channel. Only after that will we display the user list. */ + for (i = 0; i < len1; i++) { + /* XXX */ + + } + silc_client_command_pending_del(SILC_COMMAND_NAMES); + } else { + /* there is no pending callback it means that this command reply + has been received without calling the command, ie. server has sent + the reply without getting the command from us first. This happens + with SILC servers that sends NAMES reply after joining to a channel. */ + + /* Remove commas from list */ + for (i = 0; i < len1; i++) + if (name_list[i] == ',') { + name_list[i] = ' '; + list_count++; + } + + cmd->client->ops->say(cmd->client, conn, + "Users on %s: %s", channel->channel_name, name_list); } -#endif - - message = silc_calloc(buffer->len + 1, sizeof(char)); - memcpy(message, buffer->data, buffer->len); - silc_print(client, "*%s* %s", nickname, message); - memset(message, 0, buffer->len); - silc_free(message); + + /* Cache the received name list and client ID's. This cache expires + whenever server sends notify message to channel. It means two things; + some user has joined or leaved the channel. */ + for (i = 0; i < list_count; i++) { + int nick_len = strcspn(name_list, " "); + char *nickname = silc_calloc(nick_len, sizeof(*nickname)); + SilcClientID *client_id; + SilcClientEntry client; + + memcpy(nickname, name_list, nick_len); + client_id = silc_id_str2id(client_id_list->data, SILC_ID_CLIENT); + silc_buffer_pull(client_id_list, SILC_ID_CLIENT_LEN); + + client = silc_calloc(1, sizeof(*client)); + client->id = client_id; + client->nickname = nickname; + + silc_idcache_add(conn->client_cache, nickname, SILC_ID_CLIENT, + client_id, (void *)client, TRUE); + name_list = name_list + nick_len + 1; + } + + silc_buffer_free(client_id_list); + + out: + if (channel_id) + silc_free(channel_id); + silc_client_command_reply_free(cmd); }