X-Git-Url: http://git.silcnet.org/gitweb/?a=blobdiff_plain;f=lib%2Fsilcclient%2Fcommand_reply.c;h=8b2f0c35273793f87cb2f0e0451fe0931b9ce8b1;hb=bb1973faaa81ead23b3d5e05b45cddd8a47d51f7;hp=604389376afaa09e6ac221de992ab4d4e297ddff;hpb=318d79b391bf6288e3e28c840217a7097f3d0392;p=silc.git diff --git a/lib/silcclient/command_reply.c b/lib/silcclient/command_reply.c index 60438937..8b2f0c35 100644 --- a/lib/silcclient/command_reply.c +++ b/lib/silcclient/command_reply.c @@ -20,6 +20,16 @@ /* * Command reply functions are "the otherside" of the command functions. * Reply to a command sent by server is handled by these functions. + * + * The arguments received from server are also passed to the calling + * application through command_reply client operation. The arguments are + * exactly same and in same order as the server sent it. However, ID's are + * not sent to the application. Instead, corresponding ID entry is sent + * to the application. For example, instead of sending Client ID the + * corresponding SilcClientEntry is sent to the application. The case is + * same with for example Channel ID's. This way application has all the + * necessary data already in hand without redundant searching. If ID is + * received but ID entry does not exist, NULL is sent. */ /* $Id$ */ @@ -45,6 +55,7 @@ SilcClientCommandReply silc_command_reply_list[] = SILC_CLIENT_CMD_REPLY(motd, MOTD), SILC_CLIENT_CMD_REPLY(umode, UMODE), SILC_CLIENT_CMD_REPLY(cmode, CMODE), + SILC_CLIENT_CMD_REPLY(cumode, CUMODE), SILC_CLIENT_CMD_REPLY(kick, KICK), SILC_CLIENT_CMD_REPLY(restart, RESTART), SILC_CLIENT_CMD_REPLY(close, CLOSE), @@ -82,7 +93,8 @@ const SilcCommandStatusMessage silc_command_status_messages[] = { { STAT(NO_SUCH_CHANNEL_ID),"No such Channel ID" }, { STAT(NICKNAME_IN_USE), "Nickname already exists" }, { STAT(NOT_ON_CHANNEL), "You are not on that channel" }, - { STAT(USER_ON_CHANNEL), "User already on channel" }, + { STAT(USER_NOT_ON_CHANNEL),"They are not on the channel" }, + { STAT(USER_ON_CHANNEL), "User already on the channel" }, { STAT(NOT_REGISTERED), "You have not registered" }, { STAT(NOT_ENOUGH_PARAMS), "Not enough parameters" }, { STAT(TOO_MANY_PARAMS), "Too many parameters" }, @@ -108,11 +120,12 @@ const SilcCommandStatusMessage silc_command_status_messages[] = { 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) + cmd->payload, TRUE, silc_command_get(cmd->payload), status /* 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)) + cmd->sock->user_data, cmd->payload, FALSE, \ + silc_command_get(cmd->payload), status) /* Process received command reply. */ @@ -125,7 +138,7 @@ void silc_client_command_reply_process(SilcClient client, SilcCommandPayload payload; /* Get command reply payload from packet */ - payload = silc_command_parse_payload(buffer); + payload = silc_command_payload_parse(buffer); if (!payload) { /* Silently ignore bad reply packet */ SILC_LOG_DEBUG(("Bad command reply packet")); @@ -138,6 +151,7 @@ void silc_client_command_reply_process(SilcClient client, ctx->client = client; ctx->sock = sock; ctx->payload = payload; + ctx->args = silc_command_get_args(ctx->payload); ctx->packet = packet; /* Check for pending commands and mark to be exeucted */ @@ -175,9 +189,77 @@ void silc_client_command_reply_free(SilcClientCommandReplyContext cmd) } } +static void +silc_client_command_reply_whois_print(SilcClientCommandReplyContext cmd, + SilcCommandStatus status) +{ + char buf[256]; + int argc, len; + unsigned char *id_data; + char *nickname = NULL, *username = NULL; + char *realname = NULL; + SilcClientID *client_id; + SilcIDCacheEntry id_cache = NULL; + SilcClientEntry client_entry = NULL; + SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; + + memset(buf, 0, sizeof(buf)); + + argc = silc_argument_get_arg_num(cmd->args); + + id_data = silc_argument_get_arg_type(cmd->args, 2, &len); + if (!id_data) { + COMMAND_REPLY_ERROR; + return; + } + + client_id = silc_id_payload_parse_id(id_data, len); + + nickname = silc_argument_get_arg_type(cmd->args, 3, &len); + if (nickname) { + strncat(buf, nickname, len); + strncat(buf, " is ", 4); + } + + username = silc_argument_get_arg_type(cmd->args, 4, &len); + if (username) { + strncat(buf, username, len); + } + + realname = silc_argument_get_arg_type(cmd->args, 5, &len); + if (realname) { + strncat(buf, " (", 2); + strncat(buf, realname, len); + strncat(buf, ")", 1); + } + + /* Check if we have this client cached already. */ + if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id, + SILC_ID_CLIENT, &id_cache)) { + client_entry = silc_calloc(1, sizeof(*client_entry)); + client_entry->id = client_id; + silc_parse_nickname(nickname, &client_entry->nickname, + &client_entry->server, &client_entry->num); + client_entry->username = strdup(username); + + /* Add client to cache */ + silc_idcache_add(conn->client_cache, client_entry->nickname, + SILC_ID_CLIENT, client_id, (void *)client_entry, TRUE); + } else { + client_entry = (SilcClientEntry)id_cache->context; + silc_free(client_id); + } + + if (!cmd->callback) + cmd->client->ops->say(cmd->client, conn, "%s", buf); + + /* Notify application */ + COMMAND_REPLY((ARGS, client_entry, nickname, + username, realname, NULL, NULL)); +} + /* 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) { @@ -188,12 +270,15 @@ SILC_CLIENT_CMD_REPLY_FUNC(whois) SILC_LOG_DEBUG(("Start")); - tmp = silc_command_get_arg_type(cmd->payload, 1, NULL); + tmp = silc_argument_get_arg_type(cmd->args, 1, NULL); SILC_GET16_MSB(status, tmp); - if (status != SILC_STATUS_OK) { + if (status != SILC_STATUS_OK && + status != SILC_STATUS_LIST_START && + status != SILC_STATUS_LIST_ITEM && + status != SILC_STATUS_LIST_END) { if (status == SILC_STATUS_ERR_NO_SUCH_NICK) { /* Take nickname which may be provided */ - tmp = silc_command_get_arg_type(cmd->payload, 3, NULL); + tmp = silc_argument_get_arg_type(cmd->args, 3, NULL); if (tmp) cmd->client->ops->say(cmd->client, conn, "%s: %s", tmp, silc_client_command_status_message(status)); @@ -212,59 +297,20 @@ SILC_CLIENT_CMD_REPLY_FUNC(whois) /* Display one whois reply */ if (status == SILC_STATUS_OK) { - char buf[256]; - int argc, len; - unsigned char *id_data; - char *nickname = NULL, *username = NULL; - char *realname = NULL; - - memset(buf, 0, sizeof(buf)); - - argc = silc_command_get_arg_num(cmd->payload); - id_data = silc_command_get_arg_type(cmd->payload, 2, NULL); - - nickname = silc_command_get_arg_type(cmd->payload, 3, &len); - if (nickname) { - strncat(buf, nickname, len); - strncat(buf, " is ", 4); - } - - username = silc_command_get_arg_type(cmd->payload, 4, &len); - if (username) { - strncat(buf, username, len); - } - - realname = silc_command_get_arg_type(cmd->payload, 5, &len); - if (realname) { - strncat(buf, " (", 2); - strncat(buf, realname, len); - strncat(buf, ")", 1); - } - -#if 0 - /* 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_conn->client_id_cache_count[(int)nickname[0] - 32] = - silc_idcache_add(&client->current_conn-> - client_id_cache[(int)nickname[0] - 32], - client->current_conn-> - client_id_cache_count[(int)nickname[0] - 32], - strdup(nickname), SILC_ID_CLIENT, id, NULL); -#endif - - cmd->client->ops->say(cmd->client, conn, "%s", buf); - - /* Notify application */ - COMMAND_REPLY((ARGS)); + silc_client_command_reply_whois_print(cmd, status); } + /* XXX list should not be displayed untill all items has been received. */ if (status == SILC_STATUS_LIST_START) { + silc_client_command_reply_whois_print(cmd, status); + } + if (status == SILC_STATUS_LIST_ITEM) { + silc_client_command_reply_whois_print(cmd, status); } if (status == SILC_STATUS_LIST_END) { - + silc_client_command_reply_whois_print(cmd, status); } SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_WHOIS); @@ -273,8 +319,11 @@ SILC_CLIENT_CMD_REPLY_FUNC(whois) silc_client_command_reply_free(cmd); } +/* Received reply for WHOWAS command. */ + SILC_CLIENT_CMD_REPLY_FUNC(whowas) { + } /* Received reply for IDENTIFY command. This maybe called several times @@ -291,12 +340,12 @@ SILC_CLIENT_CMD_REPLY_FUNC(identify) SILC_LOG_DEBUG(("Start")); - tmp = silc_command_get_arg_type(cmd->payload, 1, NULL); + tmp = silc_argument_get_arg_type(cmd->args, 1, NULL); SILC_GET16_MSB(status, tmp); if (status != SILC_STATUS_OK) { if (status == SILC_STATUS_ERR_NO_SUCH_NICK) { /* Take nickname which may be provided */ - tmp = silc_command_get_arg_type(cmd->payload, 3, NULL); + tmp = silc_argument_get_arg_type(cmd->args, 3, NULL); if (tmp) cmd->client->ops->say(cmd->client, conn, "%s: %s", tmp, silc_client_command_status_message(status)); @@ -315,16 +364,23 @@ SILC_CLIENT_CMD_REPLY_FUNC(identify) /* Display one whois reply */ if (status == SILC_STATUS_OK) { + unsigned int len; unsigned char *id_data; char *nickname; + char *username; - id_data = silc_command_get_arg_type(cmd->payload, 2, NULL); - nickname = silc_command_get_arg_type(cmd->payload, 3, NULL); + id_data = silc_argument_get_arg_type(cmd->args, 2, &len); + nickname = silc_argument_get_arg_type(cmd->args, 3, NULL); + username = silc_argument_get_arg_type(cmd->args, 4, NULL); /* Allocate client entry */ client_entry = silc_calloc(1, sizeof(*client_entry)); - client_entry->id = silc_id_str2id(id_data, SILC_ID_CLIENT); - client_entry->nickname = strdup(nickname); + client_entry->id = silc_id_payload_parse_id(id_data, len); + if (nickname) + silc_parse_nickname(nickname, &client_entry->nickname, + &client_entry->server, &client_entry->num); + if (username) + client_entry->username = strdup(username); /* Save received Client ID to ID cache */ silc_idcache_add(conn->client_cache, client_entry->nickname, @@ -347,20 +403,19 @@ SILC_CLIENT_CMD_REPLY_FUNC(identify) /* 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; SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; SilcCommandStatus status; - unsigned char *tmp, *id_string; - int argc; + SilcIDPayload idp; + unsigned char *tmp; + unsigned int argc, len; SILC_LOG_DEBUG(("Start")); - tmp = silc_command_get_arg_type(cmd->payload, 1, NULL); - SILC_GET16_MSB(status, tmp); + SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL)); if (status != SILC_STATUS_OK) { cmd->client->ops->say(cmd->client, conn, "Cannot set nickname: %s", silc_client_command_status_message(status)); @@ -368,7 +423,7 @@ SILC_CLIENT_CMD_REPLY_FUNC(nick) goto out; } - argc = silc_command_get_arg_num(cmd->payload); + argc = silc_argument_get_arg_num(cmd->args); if (argc < 2 || argc > 2) { cmd->client->ops->say(cmd->client, conn, "Cannot set nickname: bad reply to command"); @@ -377,11 +432,12 @@ SILC_CLIENT_CMD_REPLY_FUNC(nick) } /* Take received Client ID */ - id_string = silc_command_get_arg_type(cmd->payload, 2, NULL); - silc_client_receive_new_id(cmd->client, cmd->sock, id_string); - + tmp = silc_argument_get_arg_type(cmd->args, 2, &len); + idp = silc_id_payload_parse_data(tmp, len); + silc_client_receive_new_id(cmd->client, cmd->sock, idp); + /* Notify application */ - COMMAND_REPLY((ARGS, conn->nickname)); + COMMAND_REPLY((ARGS, conn->local_entry)); out: silc_client_command_reply_free(cmd); @@ -391,12 +447,69 @@ SILC_CLIENT_CMD_REPLY_FUNC(list) { } +/* Received reply to topic command. */ + SILC_CLIENT_CMD_REPLY_FUNC(topic) { + SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; + SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; + SilcCommandStatus status; + SilcChannelEntry channel; + SilcChannelID *channel_id = NULL; + SilcIDCacheEntry id_cache = NULL; + unsigned char *tmp; + char *topic; + unsigned int argc, len; + + SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL)); + if (status != SILC_STATUS_OK) { + cmd->client->ops->say(cmd->client, conn, + "%s", silc_client_command_status_message(status)); + COMMAND_REPLY_ERROR; + silc_client_command_reply_free(cmd); + return; + } + + argc = silc_argument_get_arg_num(cmd->args); + if (argc < 1 || argc > 3) { + COMMAND_REPLY_ERROR; + goto out; + } + + /* Take Channel ID */ + tmp = silc_argument_get_arg_type(cmd->args, 2, &len); + if (!tmp) + goto out; + + /* Take topic */ + topic = silc_argument_get_arg_type(cmd->args, 3, NULL); + if (!topic) + goto out; + + channel_id = silc_id_payload_parse_id(tmp, len); + + /* Get the channel name */ + if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id, + SILC_ID_CHANNEL, &id_cache)) { + silc_free(channel_id); + COMMAND_REPLY_ERROR; + goto out; + } + + channel = (SilcChannelEntry)id_cache->context; + + cmd->client->ops->say(cmd->client, conn, + "Topic on channel %s: %s", channel->channel_name, + topic); + + /* Notify application */ + COMMAND_REPLY((ARGS, channel, topic)); + + out: + silc_client_command_reply_free(cmd); } /* Received reply to invite command. */ -/* Sends to application: (no arguments) */ SILC_CLIENT_CMD_REPLY_FUNC(invite) { @@ -405,13 +518,13 @@ SILC_CLIENT_CMD_REPLY_FUNC(invite) SilcCommandStatus status; unsigned char *tmp; - tmp = silc_command_get_arg_type(cmd->payload, 1, NULL); + tmp = silc_argument_get_arg_type(cmd->args, 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; + silc_client_command_reply_free(cmd); return; } @@ -431,7 +544,6 @@ 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) { @@ -441,32 +553,32 @@ SILC_CLIENT_CMD_REPLY_FUNC(info) SilcCommandStatus status; unsigned char *tmp; - tmp = silc_command_get_arg_type(cmd->payload, 1, NULL); + tmp = silc_argument_get_arg_type(cmd->args, 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; + silc_client_command_reply_free(cmd); return; } /* Get server ID */ - tmp = silc_command_get_arg_type(cmd->payload, 2, NULL); + tmp = silc_argument_get_arg_type(cmd->args, 2, NULL); if (!tmp) goto out; /* XXX save server id */ /* Get server info */ - tmp = silc_command_get_arg_type(cmd->payload, 3, NULL); + tmp = silc_argument_get_arg_type(cmd->args, 3, NULL); if (!tmp) goto out; client->ops->say(cmd->client, conn, "Info: %s", tmp); /* Notify application */ - COMMAND_REPLY((ARGS, (char *)tmp)); + COMMAND_REPLY((ARGS, NULL, (char *)tmp)); out: silc_client_command_reply_free(cmd); @@ -477,7 +589,6 @@ 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) { @@ -485,12 +596,10 @@ SILC_CLIENT_CMD_REPLY_FUNC(ping) 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); + SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL)); if (status != SILC_STATUS_OK) { cmd->client->ops->say(cmd->client, conn, "%s", silc_client_command_status_message(status)); @@ -517,10 +626,12 @@ SILC_CLIENT_CMD_REPLY_FUNC(ping) /* Notify application */ COMMAND_REPLY((ARGS)); - goto out; + break; } } + silc_free(id); + out: silc_client_command_reply_free(cmd); } @@ -537,14 +648,13 @@ SILC_CLIENT_CMD_REPLY_FUNC(join) SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; SilcClient client = cmd->client; SilcCommandStatus status; - unsigned int argc, mode; - unsigned char *id_string; + SilcIDPayload idp; + unsigned int argc, mode, len; char *topic, *tmp, *channel_name; SILC_LOG_DEBUG(("Start")); - tmp = silc_command_get_arg_type(cmd->payload, 1, NULL); - SILC_GET16_MSB(status, tmp); + SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL)); if (status != SILC_STATUS_OK) { cmd->client->ops->say(cmd->client, conn, "%s", silc_client_command_status_message(status)); @@ -552,8 +662,8 @@ SILC_CLIENT_CMD_REPLY_FUNC(join) goto out; } - argc = silc_command_get_arg_num(cmd->payload); - if (argc < 3 || argc > 4) { + argc = silc_argument_get_arg_num(cmd->args); + if (argc < 3 || argc > 5) { cmd->client->ops->say(cmd->client, conn, "Cannot join channel: Bad reply packet"); COMMAND_REPLY_ERROR; @@ -561,7 +671,7 @@ SILC_CLIENT_CMD_REPLY_FUNC(join) } /* Get channel name */ - tmp = silc_command_get_arg_type(cmd->payload, 2, NULL); + tmp = silc_argument_get_arg_type(cmd->args, 2, NULL); if (!tmp) { cmd->client->ops->say(cmd->client, conn, "Cannot join channel: Bad reply packet"); @@ -571,49 +681,185 @@ SILC_CLIENT_CMD_REPLY_FUNC(join) channel_name = strdup(tmp); /* Get Channel ID */ - id_string = silc_command_get_arg_type(cmd->payload, 3, NULL); - if (!id_string) { + tmp = silc_argument_get_arg_type(cmd->args, 3, &len); + if (!tmp) { cmd->client->ops->say(cmd->client, conn, "Cannot join channel: Bad reply packet"); COMMAND_REPLY_ERROR; goto out; } + idp = silc_id_payload_parse_data(tmp, len); /* Get channel mode */ - tmp = silc_command_get_arg_type(cmd->payload, 4, NULL); + tmp = silc_argument_get_arg_type(cmd->args, 4, NULL); if (tmp) SILC_GET32_MSB(mode, tmp); else mode = 0; /* Get topic */ - topic = silc_command_get_arg_type(cmd->payload, 5, NULL); + topic = silc_argument_get_arg_type(cmd->args, 5, NULL); /* Save received Channel ID */ silc_client_new_channel_id(cmd->client, cmd->sock, channel_name, - mode, id_string); + mode, idp); + silc_id_payload_free(idp); if (topic) client->ops->say(cmd->client, conn, "Topic for %s: %s", channel_name, topic); /* Notify application */ - COMMAND_REPLY((ARGS, channel_name, topic)); + COMMAND_REPLY((ARGS, channel_name, conn->current_channel, mode, + NULL, NULL, topic)); out: silc_client_command_reply_free(cmd); } +/* Received reply for MOTD command */ + SILC_CLIENT_CMD_REPLY_FUNC(motd) { + SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; + SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; + SilcCommandStatus status; + unsigned int argc, i; + unsigned char *tmp; + char *motd = NULL, *cp, line[256]; + + tmp = silc_argument_get_arg_type(cmd->args, 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; + } + + argc = silc_argument_get_arg_num(cmd->args); + if (argc > 2) { + COMMAND_REPLY_ERROR; + goto out; + } + + if (argc == 2) { + motd = silc_argument_get_arg_type(cmd->args, 2, NULL); + if (!motd) { + COMMAND_REPLY_ERROR; + goto out; + } + + i = 0; + cp = motd; + while(cp[i] != 0) { + if (cp[i++] == '\n') { + memset(line, 0, sizeof(line)); + strncat(line, cp, i - 1); + cp += i; + + if (i == 2) + line[0] = ' '; + + cmd->client->ops->say(cmd->client, conn, "%s", line); + + if (!strlen(cp)) + break; + i = 0; + } + } + } + + /* Notify application */ + COMMAND_REPLY((ARGS, motd)); + + out: + silc_client_command_reply_free(cmd); } SILC_CLIENT_CMD_REPLY_FUNC(umode) { } +/* Received reply for CMODE command. */ + SILC_CLIENT_CMD_REPLY_FUNC(cmode) { + SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; + SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; + SilcCommandStatus status; + unsigned char *tmp; + + SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL)); + if (status != SILC_STATUS_OK) { + cmd->client->ops->say(cmd->client, conn, + "%s", silc_client_command_status_message(status)); + COMMAND_REPLY_ERROR; + goto out; + } + + /* Get channel mode */ + tmp = silc_argument_get_arg_type(cmd->args, 2, NULL); + if (!tmp) { + COMMAND_REPLY_ERROR; + goto out; + } + + /* Notify application */ + COMMAND_REPLY((ARGS, tmp)); + + out: + silc_client_command_reply_free(cmd); +} + +/* Received reply for CUMODE command */ + +SILC_CLIENT_CMD_REPLY_FUNC(cumode) +{ + SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; + SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; + SilcCommandStatus status; + SilcIDCacheEntry id_cache = NULL; + SilcClientID *client_id; + unsigned char *tmp, *id; + unsigned int len; + + SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL)); + if (status != SILC_STATUS_OK) { + cmd->client->ops->say(cmd->client, conn, + "%s", silc_client_command_status_message(status)); + COMMAND_REPLY_ERROR; + goto out; + } + + /* Get channel mode */ + tmp = silc_argument_get_arg_type(cmd->args, 2, NULL); + if (!tmp) { + COMMAND_REPLY_ERROR; + goto out; + } + + /* Get Client ID */ + id = silc_argument_get_arg_type(cmd->args, 3, &len); + if (!id) { + COMMAND_REPLY_ERROR; + goto out; + } + client_id = silc_id_payload_parse_id(id, len); + + /* Get client entry */ + if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id, + SILC_ID_CLIENT, &id_cache)) { + COMMAND_REPLY_ERROR; + goto out; + } + + /* Notify application */ + COMMAND_REPLY((ARGS, tmp, (SilcClientEntry)id_cache->context)); + silc_free(client_id); + + out: + silc_client_command_reply_free(cmd); } SILC_CLIENT_CMD_REPLY_FUNC(kick) @@ -637,7 +883,6 @@ SILC_CLIENT_CMD_REPLY_FUNC(silcoper) } /* Reply to LEAVE command. */ -/* Sends to application: (no arguments) */ SILC_CLIENT_CMD_REPLY_FUNC(leave) { @@ -646,7 +891,7 @@ SILC_CLIENT_CMD_REPLY_FUNC(leave) SilcCommandStatus status; unsigned char *tmp; - tmp = silc_command_get_arg_type(cmd->payload, 1, NULL); + tmp = silc_argument_get_arg_type(cmd->args, 1, NULL); SILC_GET16_MSB(status, tmp); if (status != SILC_STATUS_OK) { cmd->client->ops->say(cmd->client, conn, @@ -673,13 +918,14 @@ SILC_CLIENT_CMD_REPLY_FUNC(names) SilcChannelEntry channel; SilcChannelID *channel_id = NULL; SilcBuffer client_id_list; + SilcBuffer client_mode_list; unsigned char *tmp; - char *name_list; - int i, len1, len2, list_count = 0; + char *name_list, *cp; + int i, k, len1, len2, list_count = 0; SILC_LOG_DEBUG(("Start")); - tmp = silc_command_get_arg_type(cmd->payload, 1, NULL); + tmp = silc_argument_get_arg_type(cmd->args, 1, NULL); SILC_GET16_MSB(status, tmp); if (status != SILC_STATUS_OK) { cmd->client->ops->say(cmd->client, conn, @@ -689,17 +935,17 @@ SILC_CLIENT_CMD_REPLY_FUNC(names) } /* Get channel ID */ - tmp = silc_command_get_arg_type(cmd->payload, 2, NULL); + tmp = silc_argument_get_arg_type(cmd->args, 2, &len1); if (!tmp) { cmd->client->ops->say(cmd->client, conn, - "Cannot get user list: Bad reply packet"); + "Cannot Channel ID: Bad reply packet"); COMMAND_REPLY_ERROR; goto out; } - channel_id = silc_id_str2id(tmp, SILC_ID_CHANNEL); + channel_id = silc_id_payload_parse_id(tmp, len1); /* Get the name list of the channel */ - name_list = silc_command_get_arg_type(cmd->payload, 3, &len1); + name_list = silc_argument_get_arg_type(cmd->args, 3, &len1); if (!name_list) { cmd->client->ops->say(cmd->client, conn, "Cannot get user list: Bad reply packet"); @@ -708,7 +954,7 @@ SILC_CLIENT_CMD_REPLY_FUNC(names) } /* Get Client ID list */ - tmp = silc_command_get_arg_type(cmd->payload, 4, &len2); + tmp = silc_argument_get_arg_type(cmd->args, 4, &len2); if (!tmp) { cmd->client->ops->say(cmd->client, conn, "Cannot get user list: Bad reply packet"); @@ -720,6 +966,19 @@ SILC_CLIENT_CMD_REPLY_FUNC(names) silc_buffer_pull_tail(client_id_list, len2); silc_buffer_put(client_id_list, tmp, len2); + /* Get client mode list */ + tmp = silc_argument_get_arg_type(cmd->args, 5, &len2); + if (!tmp) { + cmd->client->ops->say(cmd->client, conn, + "Cannot get user list: Bad reply packet"); + COMMAND_REPLY_ERROR; + goto out; + } + + client_mode_list = silc_buffer_alloc(len2); + silc_buffer_pull_tail(client_mode_list, len2); + silc_buffer_put(client_mode_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)) { @@ -751,34 +1010,133 @@ SILC_CLIENT_CMD_REPLY_FUNC(names) name_list[i] = ' '; list_count++; } + list_count++; + } - cmd->client->ops->say(cmd->client, conn, - "Users on %s: %s", channel->channel_name, name_list); + /* Remove old client list from channel, if exists */ + if (channel->clients) { + silc_free(channel->clients); + channel->clients = NULL; + channel->clients_count = 0; } - /* Cache the received name list and client ID's. This cache expires + /* Allocate room for clients in the channel */ + channel->clients = silc_calloc(list_count, sizeof(*channel->clients)); + + /* Cache the received name list, client ID's and modes. This cache expires whenever server sends notify message to channel. It means two things; some user has joined or leaved the channel. */ + cp = name_list; for (i = 0; i < list_count; i++) { int nick_len = strcspn(name_list, " "); - char *nickname = silc_calloc(nick_len, sizeof(*nickname)); + unsigned short idp_len; + unsigned int mode; + char *nickname = silc_calloc(nick_len + 1, 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); + SILC_GET16_MSB(idp_len, client_id_list->data + 2); + idp_len += 4; + client_id = silc_id_payload_parse_id(client_id_list->data, idp_len); + silc_buffer_pull(client_id_list, idp_len); + + SILC_GET32_MSB(mode, client_mode_list->data); + silc_buffer_pull(client_mode_list, 4); + + /* Check if we have this client cached already. */ + if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id, + SILC_ID_CLIENT, &id_cache)) { + client = silc_calloc(1, sizeof(*client)); + client->id = client_id; + silc_parse_nickname(nickname, &client->nickname, &client->server, + &client->num); + silc_free(nickname); + + /* Add client to cache */ + silc_idcache_add(conn->client_cache, client->nickname, SILC_ID_CLIENT, + client_id, (void *)client, TRUE); + } else { + client = (SilcClientEntry)id_cache->context; + silc_free(client_id); + silc_free(nickname); + id_cache = NULL; + } - client = silc_calloc(1, sizeof(*client)); - client->id = client_id; - client->nickname = nickname; + channel->clients[channel->clients_count].client = client; + channel->clients[channel->clients_count].mode = mode; + channel->clients_count++; - silc_idcache_add(conn->client_cache, nickname, SILC_ID_CLIENT, - client_id, (void *)client, TRUE); - name_list = name_list + nick_len + 1; + name_list += nick_len + 1; } + name_list = cp; + for (i = 0; i < list_count; i++) { + int c; + int nick_len = strcspn(name_list, " "); + char *nickname = silc_calloc(nick_len + 1, sizeof(*nickname)); + memcpy(nickname, name_list, nick_len); + + for (c = 0, k = 0; k < channel->clients_count; k++) { + if (channel->clients[k].client && + !strncmp(channel->clients[k].client->nickname, + nickname, strlen(channel->clients[k].client->nickname))) { + char t[8]; + + if (!c) { + c++; + continue; + } + + memset(t, 0, sizeof(t)); + channel->clients[k].client->nickname = + silc_calloc(strlen(nickname) + 8, sizeof(*channel->clients[k]. + client->nickname)); + snprintf(t, sizeof(t), "[%d]", c++); + strncat(channel->clients[k].client->nickname, t, strlen(t)); + strncat(channel->clients[k].client->nickname, nickname, + strlen(nickname)); + } + } + + silc_free(nickname); + } + + name_list = NULL; + len1 = 0; + for (k = 0; k < channel->clients_count; k++) { + char *m, *n = channel->clients[k].client->nickname; + len2 = strlen(n); + len1 += len2; + + name_list = silc_realloc(name_list, sizeof(*name_list) * (len1 + 3)); + + m = silc_client_chumode_char(channel->clients[k].mode); + if (m) { + memcpy(name_list + (len1 - len2), m, strlen(m)); + len1 += strlen(m); + silc_free(m); + } + + memcpy(name_list + (len1 - len2), n, len2); + name_list[len1] = 0; + + if (k == channel->clients_count - 1) + break; + memcpy(name_list + len1, " ", 1); + len1++; + } + + cmd->client->ops->say(cmd->client, conn, + "Users on %s: %s", channel->channel_name, name_list); + + /* Notify application */ + COMMAND_REPLY((ARGS, channel, name_list, client_id_list->head, + client_mode_list->head)); + + silc_free(name_list); silc_buffer_free(client_id_list); + silc_buffer_free(client_mode_list); out: if (channel_id)