X-Git-Url: http://git.silcnet.org/gitweb/?p=silc.git;a=blobdiff_plain;f=lib%2Fsilcclient%2Fcommand.c;h=557a539c9c8186daa7d4e62a3c345c43372dd95f;hp=48a0fa56231cd50841b5b8f375d6fd1d5731c718;hb=413da0f8686910f5e627393157566ae729ca99c4;hpb=030955992dd0663ddb907a06fd92af62afb9a015 diff --git a/lib/silcclient/command.c b/lib/silcclient/command.c index 48a0fa56..557a539c 100644 --- a/lib/silcclient/command.c +++ b/lib/silcclient/command.c @@ -1,10 +1,10 @@ /* - command.c + command.c Author: Pekka Riikonen - Copyright (C) 1997 - 2002 Pekka Riikonen + Copyright (C) 1997 - 2003 Pekka Riikonen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -26,14 +26,15 @@ x->internal->ops->say((x), (c), SILC_CLIENT_MESSAGE_ERROR, \ "You are not connected to a server, use /SERVER to connect"); -/* Command operation that is called at the end of all commands. - Usage: COMMAND; */ -#define COMMAND cmd->client->internal->ops->command(cmd->client, cmd->conn, \ - cmd, TRUE, cmd->command->cmd) +/* Command operation that is called at the end of all commands. + Usage: COMMAND(status); */ +#define COMMAND(status) cmd->client->internal->ops->command(cmd->client, \ + cmd->conn, cmd, TRUE, cmd->command->cmd, (status)) -/* Error to application. Usage: COMMAND_ERROR; */ -#define COMMAND_ERROR cmd->client->internal->ops->command(cmd->client, \ - cmd->conn, cmd, FALSE, cmd->command->cmd) +/* Error to application. Usage: COMMAND_ERROR(status); */ +#define COMMAND_ERROR(status) \ + cmd->client->internal->ops->command(cmd->client, \ + cmd->conn, cmd, FALSE, cmd->command->cmd, (status)) #define SAY cmd->client->internal->ops->say @@ -47,11 +48,13 @@ void silc_client_command_send(SilcClient client, SilcClientConnection conn, SilcBuffer packet; va_list ap; + assert(client && conn); + va_start(ap, argc); packet = silc_command_payload_encode_vap(command, ident, argc, ap); - silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND, - NULL, 0, NULL, NULL, packet->data, + silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND, + NULL, 0, NULL, NULL, packet->data, packet->len, TRUE); silc_buffer_free(packet); } @@ -64,32 +67,103 @@ SilcClientCommand silc_client_command_find(SilcClient client, { SilcClientCommand cmd; + assert(client); + silc_list_start(client->internal->commands); while ((cmd = silc_list_get(client->internal->commands)) != SILC_LIST_END) { - if (cmd->name && !strcmp(cmd->name, name)) + if (cmd->name && !strcasecmp(cmd->name, name)) return cmd; } return NULL; } -/* Calls the command (executes it). Application can call this after - it has allocated the SilcClientCommandContext with the function - silc_client_command_alloc and found the command from the client - library by calling silc_client_command_find. This will execute - the command. */ +/* Executes a command */ -void silc_client_command_call(SilcClientCommand command, - SilcClientCommandContext cmd) +bool silc_client_command_call(SilcClient client, + SilcClientConnection conn, + const char *command_line, ...) { - (*command->command)((void *)cmd, NULL); + va_list va; + SilcUInt32 argc = 0; + unsigned char **argv = NULL; + SilcUInt32 *argv_lens = NULL, *argv_types = NULL; + SilcClientCommand cmd; + SilcClientCommandContext ctx; + char *arg; + + assert(client); + + /* Parse arguments */ + va_start(va, command_line); + if (command_line) { + char *command_name; + + /* Get command name */ + command_name = silc_memdup(command_line, strcspn(command_line, " ")); + if (!command_name) + return FALSE; + + /* Find command by name */ + cmd = silc_client_command_find(client, command_name); + if (!cmd) { + silc_free(command_name); + return FALSE; + } + + /* Parse command line */ + silc_parse_command_line((char *)command_line, &argv, &argv_lens, + &argv_types, &argc, cmd->max_args); + + silc_free(command_name); + } else { + arg = va_arg(va, char *); + if (!arg) + return FALSE; + + /* Find command by name */ + cmd = silc_client_command_find(client, arg); + if (!cmd) + return FALSE; + + while (arg) { + argv = silc_realloc(argv, sizeof(*argv) * (argc + 1)); + argv_lens = silc_realloc(argv_lens, sizeof(*argv_lens) * (argc + 1)); + argv_types = silc_realloc(argv_types, sizeof(*argv_types) * (argc + 1)); + argv[argc] = silc_memdup(arg, strlen(arg)); + argv_lens[argc] = strlen(arg); + argv_types[argc] = argc; + argc++; + arg = va_arg(va, char *); + } + } + + /* Allocate command context. */ + ctx = silc_client_command_alloc(); + ctx->client = client; + ctx->conn = conn; + ctx->command = cmd; + ctx->argc = argc; + ctx->argv = argv; + ctx->argv_lens = argv_lens; + ctx->argv_types = argv_types; + + /* Call the command */ + cmd->command(ctx, NULL); + + va_end(va); + + return TRUE; } /* Add new pending command to be executed when reply to a command has been - received. The `reply_cmd' is the command that will call the `callback' - with `context' when reply has been received. If `ident is non-zero - the `callback' will be executed when received reply with command - identifier `ident'. */ + received. The `reply_cmd' is the command that will call the `callback' + with `context' when reply has been received. It can be SILC_COMMAND_NONE + to match any command with the `ident'. If `ident' is non-zero + the `callback' will be executed when received reply with command + identifier `ident'. If there already exists pending command for the + specified command, ident, callback and context this function has no + effect. */ void silc_client_command_pending(SilcClientConnection conn, SilcCommand reply_cmd, @@ -99,12 +173,13 @@ void silc_client_command_pending(SilcClientConnection conn, { SilcClientCommandPending *reply; + assert(conn); reply = silc_calloc(1, sizeof(*reply)); reply->reply_cmd = reply_cmd; reply->ident = ident; reply->context = context; reply->callback = callback; - silc_dlist_add(conn->pending_commands, reply); + silc_dlist_add(conn->internal->pending_commands, reply); } /* Deletes pending command by reply command type. */ @@ -115,36 +190,51 @@ void silc_client_command_pending_del(SilcClientConnection conn, { SilcClientCommandPending *r; - silc_dlist_start(conn->pending_commands); - while ((r = silc_dlist_get(conn->pending_commands)) != SILC_LIST_END) { - if (r->reply_cmd == reply_cmd && r->ident == ident) { - silc_dlist_del(conn->pending_commands, r); - break; + if (!conn->internal->pending_commands) + return; + + silc_dlist_start(conn->internal->pending_commands); + while ((r = silc_dlist_get(conn->internal->pending_commands)) + != SILC_LIST_END) { + if ((r->reply_cmd == reply_cmd || (r->reply_cmd == SILC_COMMAND_NONE && + r->reply_check)) + && r->ident == ident) { + silc_dlist_del(conn->internal->pending_commands, r); + silc_free(r); } } } /* Checks for pending commands and marks callbacks to be called from - the command reply function. Returns TRUE if there were pending command. */ + the command reply function. */ -int silc_client_command_pending_check(SilcClientConnection conn, - SilcClientCommandReplyContext ctx, - SilcCommand command, - SilcUInt16 ident) +SilcClientCommandPendingCallbacks +silc_client_command_pending_check(SilcClientConnection conn, + SilcClientCommandReplyContext ctx, + SilcCommand command, + SilcUInt16 ident, + SilcUInt32 *callbacks_count) { SilcClientCommandPending *r; - - silc_dlist_start(conn->pending_commands); - while ((r = silc_dlist_get(conn->pending_commands)) != SILC_LIST_END) { - if (r->reply_cmd == command && r->ident == ident) { - ctx->context = r->context; - ctx->callback = r->callback; + SilcClientCommandPendingCallbacks callbacks = NULL; + int i = 0; + + silc_dlist_start(conn->internal->pending_commands); + while ((r = silc_dlist_get(conn->internal->pending_commands)) + != SILC_LIST_END) { + if ((r->reply_cmd == command || r->reply_cmd == SILC_COMMAND_NONE) + && r->ident == ident) { + callbacks = silc_realloc(callbacks, sizeof(*callbacks) * (i + 1)); + callbacks[i].context = r->context; + callbacks[i].callback = r->callback; + r->reply_check = TRUE; ctx->ident = ident; - return TRUE; + i++; } } - return FALSE; + *callbacks_count = i; + return callbacks; } /* Allocate Command Context */ @@ -168,6 +258,7 @@ void silc_client_command_free(SilcClientCommandContext ctx) for (i = 0; i < ctx->argc; i++) silc_free(ctx->argv[i]); + silc_free(ctx->argv); silc_free(ctx->argv_lens); silc_free(ctx->argv_types); silc_free(ctx); @@ -185,45 +276,61 @@ SilcClientCommandContext silc_client_command_dup(SilcClientCommandContext ctx) return ctx; } -/* Command WHOIS. This command is used to query information about +/* Command WHOIS. This command is used to query information about specific user. */ SILC_CLIENT_CMD_FUNC(whois) { SilcClientCommandContext cmd = (SilcClientCommandContext)context; SilcClientConnection conn = cmd->conn; - SilcBuffer buffer; + SilcBuffer buffer, attrs = NULL; + unsigned char count[4], *tmp = NULL; if (!cmd->conn) { SILC_NOT_CONNECTED(cmd->client, cmd->conn); - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED); goto out; } /* Given without arguments fetches client's own information */ if (cmd->argc < 2) { buffer = silc_id_payload_encode(cmd->conn->local_id, SILC_ID_CLIENT); - silc_client_command_send(cmd->client, cmd->conn, SILC_COMMAND_WHOIS, + silc_client_command_send(cmd->client, cmd->conn, SILC_COMMAND_WHOIS, ++conn->cmd_ident, - 1, 3, buffer->data, buffer->len); + 1, 4, buffer->data, buffer->len); silc_buffer_free(buffer); goto out; } - buffer = silc_command_payload_encode(SILC_COMMAND_WHOIS, - cmd->argc - 1, ++cmd->argv, - ++cmd->argv_lens, ++cmd->argv_types, - 0); + if (cmd->argc == 2) { + buffer = silc_command_payload_encode_va(SILC_COMMAND_WHOIS, + ++conn->cmd_ident, 1, + 1, cmd->argv[1], + cmd->argv_lens[1]); + } else { + if (!strcasecmp(cmd->argv[2], "-details")) + attrs = silc_client_attributes_request(0); + + if (!attrs || cmd->argc > 3) { + int c = atoi(cmd->argc > 3 ? cmd->argv[3] : cmd->argv[2]); + SILC_PUT32_MSB(c, count); + tmp = count; + } + + buffer = silc_command_payload_encode_va(SILC_COMMAND_WHOIS, + ++conn->cmd_ident, 3, + 1, cmd->argv[1], cmd->argv_lens[1], + 2, tmp ? tmp : NULL, tmp ? 4 : 0, + 3, attrs ? attrs->data : NULL, + attrs ? attrs->len : 0); + } silc_client_packet_send(cmd->client, cmd->conn->sock, SILC_PACKET_COMMAND, NULL, 0, NULL, NULL, buffer->data, buffer->len, TRUE); silc_buffer_free(buffer); - cmd->argv--; - cmd->argv_lens--; - cmd->argv_types--; /* Notify application */ - COMMAND; + COMMAND(SILC_STATUS_OK); out: silc_client_command_free(cmd); @@ -237,41 +344,50 @@ SILC_CLIENT_CMD_FUNC(whowas) SilcClientCommandContext cmd = (SilcClientCommandContext)context; SilcClientConnection conn = cmd->conn; SilcBuffer buffer; + unsigned char count[4]; if (!cmd->conn) { SILC_NOT_CONNECTED(cmd->client, cmd->conn); - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED); goto out; } if (cmd->argc < 2 || cmd->argc > 3) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "Usage: /WHOWAS [@] []"); - COMMAND_ERROR; + COMMAND_ERROR((cmd->argc < 2 ? SILC_STATUS_ERR_NOT_ENOUGH_PARAMS : + SILC_STATUS_ERR_TOO_MANY_PARAMS)); goto out; } - buffer = silc_command_payload_encode(SILC_COMMAND_WHOWAS, - cmd->argc - 1, ++cmd->argv, - ++cmd->argv_lens, ++cmd->argv_types, - 0); + if (cmd->argc == 2) { + buffer = silc_command_payload_encode_va(SILC_COMMAND_WHOWAS, + ++conn->cmd_ident, 1, + 1, cmd->argv[1], + cmd->argv_lens[1]); + } else { + int c = atoi(cmd->argv[2]); + memset(count, 0, sizeof(count)); + SILC_PUT32_MSB(c, count); + buffer = silc_command_payload_encode_va(SILC_COMMAND_WHOWAS, + ++conn->cmd_ident, 2, + 1, cmd->argv[1], cmd->argv_lens[1], + 2, count, sizeof(count)); + } silc_client_packet_send(cmd->client, cmd->conn->sock, SILC_PACKET_COMMAND, NULL, 0, NULL, NULL, buffer->data, buffer->len, TRUE); silc_buffer_free(buffer); - cmd->argv--; - cmd->argv_lens--; - cmd->argv_types--; /* Notify application */ - COMMAND; + COMMAND(SILC_STATUS_OK); out: silc_client_command_free(cmd); } -/* Command IDENTIFY. This command is used to query information about - specific user, especially ID's. +/* Command IDENTIFY. This command is used to query information about + specific user, especially ID's. NOTE: This command is used only internally by the client library and application MUST NOT call this command directly. */ @@ -281,27 +397,32 @@ SILC_CLIENT_CMD_FUNC(identify) SilcClientCommandContext cmd = (SilcClientCommandContext)context; SilcClientConnection conn = cmd->conn; SilcBuffer buffer; + unsigned char count[4]; if (!cmd->conn) { SILC_NOT_CONNECTED(cmd->client, cmd->conn); + COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED); goto out; } if (cmd->argc < 2 || cmd->argc > 3) goto out; - if (cmd->argc == 2) - buffer = silc_command_payload_encode_va(SILC_COMMAND_IDENTIFY, + if (cmd->argc == 2) { + buffer = silc_command_payload_encode_va(SILC_COMMAND_IDENTIFY, ++conn->cmd_ident, 1, 1, cmd->argv[1], cmd->argv_lens[1]); - else - buffer = silc_command_payload_encode_va(SILC_COMMAND_IDENTIFY, + } else { + int c = atoi(cmd->argv[2]); + memset(count, 0, sizeof(count)); + SILC_PUT32_MSB(c, count); + buffer = silc_command_payload_encode_va(SILC_COMMAND_IDENTIFY, ++conn->cmd_ident, 2, 1, cmd->argv[1], cmd->argv_lens[1], - 4, cmd->argv[2], - cmd->argv_lens[2]); + 4, count, sizeof(count)); + } silc_client_packet_send(cmd->client, cmd->conn->sock, SILC_PACKET_COMMAND, NULL, 0, NULL, NULL, @@ -312,37 +433,6 @@ SILC_CLIENT_CMD_FUNC(identify) silc_client_command_free(cmd); } -/* Pending callbcak that will be called after the NICK command was - replied by the server. This sets the nickname if there were no - errors. */ - -SILC_CLIENT_CMD_FUNC(nick_change) -{ - SilcClientCommandContext cmd = (SilcClientCommandContext)context; - SilcClientConnection conn = cmd->conn; - SilcClientCommandReplyContext reply = - (SilcClientCommandReplyContext)context2; - SilcCommandStatus status; - - SILC_GET16_MSB(status, silc_argument_get_arg_type(reply->args, 1, NULL)); - if (status == SILC_STATUS_OK) { - /* Set the nickname */ - silc_idcache_del_by_context(conn->client_cache, conn->local_entry); - if (conn->nickname) - silc_free(conn->nickname); - conn->nickname = strdup(cmd->argv[1]); - conn->local_entry->nickname = conn->nickname; - silc_client_nickname_format(cmd->client, conn, conn->local_entry); - silc_idcache_add(conn->client_cache, strdup(cmd->argv[1]), - conn->local_entry->id, conn->local_entry, 0, NULL); - COMMAND; - } else { - COMMAND_ERROR; - } - - silc_client_command_free(cmd); -} - /* Command NICK. Shows current nickname/sets new nickname on current window. */ @@ -354,14 +444,14 @@ SILC_CLIENT_CMD_FUNC(nick) if (!cmd->conn) { SILC_NOT_CONNECTED(cmd->client, cmd->conn); - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED); goto out; } if (cmd->argc < 2) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "Usage: /NICK "); - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } @@ -371,15 +461,15 @@ SILC_CLIENT_CMD_FUNC(nick) /* Show current nickname */ if (cmd->argc < 2) { if (cmd->conn) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, - "Your nickname is %s on server %s", + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, + "Your nickname is %s on server %s", conn->nickname, conn->remote_host); } else { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "Your nickname is %s", conn->nickname); } - COMMAND; + COMMAND(SILC_STATUS_OK); goto out; } @@ -389,7 +479,7 @@ SILC_CLIENT_CMD_FUNC(nick) /* Send the NICK command */ buffer = silc_command_payload_encode(SILC_COMMAND_NICK, 1, &cmd->argv[1], - &cmd->argv_lens[1], + &cmd->argv_lens[1], &cmd->argv_types[1], ++cmd->conn->cmd_ident); silc_client_packet_send(cmd->client, cmd->conn->sock, @@ -397,14 +487,6 @@ SILC_CLIENT_CMD_FUNC(nick) buffer->data, buffer->len, TRUE); silc_buffer_free(buffer); - /* Register pending callback that will actually set the new nickname - if there were no errors returned by the server. */ - silc_client_command_pending(conn, SILC_COMMAND_NICK, - cmd->conn->cmd_ident, - silc_client_command_nick_change, - silc_client_command_dup(cmd)); - cmd->pending = TRUE; - out: silc_client_command_free(cmd); } @@ -422,7 +504,7 @@ SILC_CLIENT_CMD_FUNC(list) if (!cmd->conn) { SILC_NOT_CONNECTED(cmd->client, cmd->conn); - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED); goto out; } @@ -430,17 +512,18 @@ SILC_CLIENT_CMD_FUNC(list) name = cmd->argv[1]; /* Get the Channel ID of the channel */ - if (silc_idcache_find_by_name_one(conn->channel_cache, name, &id_cache)) { + if (silc_idcache_find_by_name_one(conn->internal->channel_cache, + name, &id_cache)) { channel = (SilcChannelEntry)id_cache->context; idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL); } } if (!idp) - buffer = silc_command_payload_encode_va(SILC_COMMAND_LIST, + buffer = silc_command_payload_encode_va(SILC_COMMAND_LIST, ++conn->cmd_ident, 0); else - buffer = silc_command_payload_encode_va(SILC_COMMAND_LIST, + buffer = silc_command_payload_encode_va(SILC_COMMAND_LIST, ++conn->cmd_ident, 1, 1, idp->data, idp->len); @@ -452,7 +535,7 @@ SILC_CLIENT_CMD_FUNC(list) silc_buffer_free(idp); /* Notify application */ - COMMAND; + COMMAND(SILC_STATUS_OK); out: silc_client_command_free(cmd); @@ -471,22 +554,21 @@ SILC_CLIENT_CMD_FUNC(topic) if (!cmd->conn) { SILC_NOT_CONNECTED(cmd->client, cmd->conn); - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED); goto out; } if (cmd->argc < 2 || cmd->argc > 3) { SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "Usage: /TOPIC []"); - COMMAND_ERROR; + COMMAND_ERROR((cmd->argc < 2 ? SILC_STATUS_ERR_NOT_ENOUGH_PARAMS : + SILC_STATUS_ERR_TOO_MANY_PARAMS)); goto out; } if (cmd->argv[1][0] == '*') { if (!conn->current_channel) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, - "You are not on any channel"); - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL); goto out; } name = conn->current_channel->channel_name; @@ -495,17 +577,14 @@ SILC_CLIENT_CMD_FUNC(topic) } if (!conn->current_channel) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, - "You are not on that channel"); - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL); goto out; } /* Get the Channel ID of the channel */ - if (!silc_idcache_find_by_name_one(conn->channel_cache, name, &id_cache)) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, - "You are not on that channel"); - COMMAND_ERROR; + if (!silc_idcache_find_by_name_one(conn->internal->channel_cache, + name, &id_cache)) { + COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL); goto out; } @@ -514,22 +593,22 @@ SILC_CLIENT_CMD_FUNC(topic) /* Send TOPIC command to the server */ idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL); if (cmd->argc > 2) - buffer = silc_command_payload_encode_va(SILC_COMMAND_TOPIC, - ++conn->cmd_ident, 2, + buffer = silc_command_payload_encode_va(SILC_COMMAND_TOPIC, + ++conn->cmd_ident, 2, 1, idp->data, idp->len, - 2, cmd->argv[2], + 2, cmd->argv[2], strlen(cmd->argv[2])); else - buffer = silc_command_payload_encode_va(SILC_COMMAND_TOPIC, + buffer = silc_command_payload_encode_va(SILC_COMMAND_TOPIC, ++conn->cmd_ident, 1, 1, idp->data, idp->len); - silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, + silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 0, NULL, NULL, buffer->data, buffer->len, TRUE); silc_buffer_free(buffer); silc_buffer_free(idp); /* Notify application */ - COMMAND; + COMMAND(SILC_STATUS_OK); out: silc_client_command_free(cmd); @@ -545,14 +624,15 @@ SILC_CLIENT_CMD_FUNC(invite) SilcClientConnection conn = cmd->conn; SilcClientEntry client_entry = NULL; SilcChannelEntry channel; - SilcBuffer buffer, clidp, chidp; - SilcUInt32 type = 0; + SilcBuffer buffer, clidp, chidp, args = NULL; + SilcPublicKey pubkey = NULL; char *nickname = NULL, *name; char *invite = NULL; + unsigned char action[1]; if (!cmd->conn) { SILC_NOT_CONNECTED(cmd->client, cmd->conn); - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED); goto out; } @@ -560,15 +640,13 @@ SILC_CLIENT_CMD_FUNC(invite) SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "Usage: /INVITE [[@server>]" "[+|-[[@[![@hostname>]]]]]"); - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } if (cmd->argv[1][0] == '*') { if (!conn->current_channel) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, - "You are not on any channel"); - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL); goto out; } @@ -578,9 +656,7 @@ SILC_CLIENT_CMD_FUNC(invite) channel = silc_client_get_channel(cmd->client, conn, name); if (!channel) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, - "You are on that channel"); - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL); goto out; } } @@ -594,30 +670,53 @@ SILC_CLIENT_CMD_FUNC(invite) nickname = strdup(cmd->argv[2]); /* Find client entry */ - client_entry = silc_idlist_get_client(client, conn, nickname, + client_entry = silc_idlist_get_client(client, conn, nickname, cmd->argv[2], TRUE); if (!client_entry) { if (cmd->pending) { - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK); goto out; } - + /* Client entry not found, it was requested thus mark this to be pending command. */ - silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, + silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident, - silc_client_command_invite, + silc_client_command_invite, silc_client_command_dup(cmd)); cmd->pending = 1; goto out; } } else { - invite = cmd->argv[2]; - invite++; if (cmd->argv[2][0] == '+') - type = 3; + action[0] = 0x00; else - type = 4; + action[0] = 0x01; + + /* Check if it is public key file to be added to invite list */ + if (!silc_pkcs_load_public_key(cmd->argv[2] + 1, &pubkey, + SILC_PKCS_FILE_PEM)) + silc_pkcs_load_public_key(cmd->argv[2] + 1, &pubkey, + SILC_PKCS_FILE_BIN); + invite = cmd->argv[2]; + if (!pubkey) + invite++; + } + } + + if (invite) { + args = silc_buffer_alloc_size(2); + silc_buffer_format(args, + SILC_STR_UI_SHORT(1), + SILC_STR_END); + if (pubkey) { + chidp = silc_pkcs_public_key_payload_encode(pubkey); + args = silc_argument_payload_encode_one(args, chidp->data, + chidp->len, 2); + silc_buffer_free(chidp); + silc_pkcs_public_key_free(pubkey); + } else { + args = silc_argument_payload_encode_one(args, invite, strlen(invite), 1); } } @@ -625,28 +724,33 @@ SILC_CLIENT_CMD_FUNC(invite) chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL); if (client_entry) { clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT); - buffer = silc_command_payload_encode_va(SILC_COMMAND_INVITE, - ++conn->cmd_ident, 3, + buffer = silc_command_payload_encode_va(SILC_COMMAND_INVITE, + ++conn->cmd_ident, 4, 1, chidp->data, chidp->len, 2, clidp->data, clidp->len, - type, invite, invite ? - strlen(invite) : 0); + 3, args ? action : NULL, + args ? 1 : 0, + 4, args ? args->data : NULL, + args ? args->len : 0); silc_buffer_free(clidp); } else { - buffer = silc_command_payload_encode_va(SILC_COMMAND_INVITE, - ++conn->cmd_ident, 2, + buffer = silc_command_payload_encode_va(SILC_COMMAND_INVITE, + ++conn->cmd_ident, 3, 1, chidp->data, chidp->len, - type, invite, invite ? - strlen(invite) : 0); + 3, args ? action : NULL, + args ? 1 : 0, + 4, args ? args->data : NULL, + args ? args->len : 0); } - silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, + silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 0, NULL, NULL, buffer->data, buffer->len, TRUE); silc_buffer_free(buffer); silc_buffer_free(chidp); + silc_buffer_free(args), /* Notify application */ - COMMAND; + COMMAND(SILC_STATUS_OK); out: silc_free(nickname); @@ -663,14 +767,14 @@ SILC_TASK_CALLBACK(silc_client_command_quit_cb) QuitInternal q = (QuitInternal)context; /* Close connection */ - q->client->internal->ops->disconnect(q->client, q->conn); - silc_client_close_connection(q->client, NULL, q->conn->sock->user_data); + q->client->internal->ops->disconnected(q->client, q->conn, 0, NULL); + silc_client_close_connection(q->client, q->conn->sock->user_data); silc_free(q); } /* Command QUIT. Closes connection with current server. */ - + SILC_CLIENT_CMD_FUNC(quit) { SilcClientCommandContext cmd = (SilcClientCommandContext)context; @@ -679,19 +783,19 @@ SILC_CLIENT_CMD_FUNC(quit) if (!cmd->conn) { SILC_NOT_CONNECTED(cmd->client, cmd->conn); - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED); goto out; } if (cmd->argc > 1) - buffer = silc_command_payload_encode(SILC_COMMAND_QUIT, cmd->argc - 1, + buffer = silc_command_payload_encode(SILC_COMMAND_QUIT, cmd->argc - 1, &cmd->argv[1], &cmd->argv_lens[1], &cmd->argv_types[1], 0); else buffer = silc_command_payload_encode(SILC_COMMAND_QUIT, 0, NULL, NULL, NULL, 0); - silc_client_packet_send(cmd->client, cmd->conn->sock, SILC_PACKET_COMMAND, - NULL, 0, NULL, NULL, + silc_client_packet_send(cmd->client, cmd->conn->sock, SILC_PACKET_COMMAND, + NULL, 0, NULL, NULL, buffer->data, buffer->len, TRUE); silc_buffer_free(buffer); @@ -708,7 +812,7 @@ SILC_CLIENT_CMD_FUNC(quit) 1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL); /* Notify application */ - COMMAND; + COMMAND(SILC_STATUS_OK); out: silc_client_command_free(cmd); @@ -723,7 +827,7 @@ SILC_TASK_CALLBACK(silc_client_command_kill_remove_later) SilcClientConnection conn = cmd->conn; SilcClientEntry target; char *nickname = NULL; - + /* Parse the typed nickname. */ if (client->internal->params->nickname_parse) client->internal->params->nickname_parse(cmd->argv[1], &nickname); @@ -731,7 +835,7 @@ SILC_TASK_CALLBACK(silc_client_command_kill_remove_later) nickname = strdup(cmd->argv[1]); /* Get the target client */ - target = silc_idlist_get_client(cmd->client, conn, nickname, + target = silc_idlist_get_client(cmd->client, conn, nickname, cmd->argv[1], FALSE); if (target) /* Remove the client from all channels and free it */ @@ -747,11 +851,11 @@ SILC_TASK_CALLBACK(silc_client_command_kill_remove_later) SILC_CLIENT_CMD_FUNC(kill_remove) { SilcClientCommandContext cmd = (SilcClientCommandContext)context; - SilcClientCommandReplyContext reply = + SilcClientCommandReplyContext reply = (SilcClientCommandReplyContext)context2; - SilcCommandStatus status; + SilcStatus status; - SILC_GET16_MSB(status, silc_argument_get_arg_type(reply->args, 1, NULL)); + silc_command_get_status(reply->payload, &status, NULL); if (status == SILC_STATUS_OK) { /* Remove with timeout */ silc_schedule_task_add(cmd->client->schedule, cmd->conn->sock->sock, @@ -771,20 +875,20 @@ SILC_CLIENT_CMD_FUNC(kill) SilcClientCommandContext cmd = (SilcClientCommandContext)context; SilcClient client = cmd->client; SilcClientConnection conn = cmd->conn; - SilcBuffer buffer, idp; + SilcBuffer buffer, idp, auth = NULL; SilcClientEntry target; - char *nickname = NULL; + char *nickname = NULL, *comment = NULL; if (!cmd->conn) { SILC_NOT_CONNECTED(cmd->client, cmd->conn); - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED); goto out; } if (cmd->argc < 2) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, - "Usage: /KILL []"); - COMMAND_ERROR; + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, + "Usage: /KILL [] [-pubkey]"); + COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } @@ -795,43 +899,56 @@ SILC_CLIENT_CMD_FUNC(kill) nickname = strdup(cmd->argv[1]); /* Get the target client */ - target = silc_idlist_get_client(cmd->client, conn, nickname, + target = silc_idlist_get_client(cmd->client, conn, nickname, cmd->argv[1], TRUE); if (!target) { if (cmd->pending) { - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK); goto out; } /* Client entry not found, it was requested thus mark this to be pending command. */ - silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, - conn->cmd_ident, - silc_client_command_kill, + silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, + conn->cmd_ident, + silc_client_command_kill, silc_client_command_dup(cmd)); cmd->pending = 1; goto out; } + if (cmd->argc >= 3) { + if (strcasecmp(cmd->argv[2], "-pubkey")) + comment = cmd->argv[2]; + + if (!strcasecmp(cmd->argv[2], "-pubkey") || + (cmd->argc >= 4 && !strcasecmp(cmd->argv[3], "-pubkey"))) { + /* Encode the public key authentication payload */ + auth = silc_auth_public_key_auth_generate(cmd->client->public_key, + cmd->client->private_key, + cmd->client->rng, + client->sha1hash, + target->id, SILC_ID_CLIENT); + } + } + /* Send the KILL command to the server */ idp = silc_id_payload_encode(target->id, SILC_ID_CLIENT); - if (cmd->argc == 2) - buffer = silc_command_payload_encode_va(SILC_COMMAND_KILL, - ++conn->cmd_ident, 1, - 1, idp->data, idp->len); - else - buffer = silc_command_payload_encode_va(SILC_COMMAND_KILL, - ++conn->cmd_ident, 2, - 1, idp->data, idp->len, - 2, cmd->argv[2], - strlen(cmd->argv[2])); + buffer = + silc_command_payload_encode_va(SILC_COMMAND_KILL, + ++conn->cmd_ident, 3, + 1, idp->data, idp->len, + 2, comment, comment ? strlen(comment) : 0, + 3, auth ? auth->data : NULL, + auth ? auth->len : 0); silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 0, NULL, NULL, buffer->data, buffer->len, TRUE); silc_buffer_free(buffer); silc_buffer_free(idp); + silc_buffer_free(auth); /* Notify application */ - COMMAND; + COMMAND(SILC_STATUS_OK); /* Register a pending callback that will actually remove the killed client from our cache. */ @@ -856,7 +973,7 @@ SILC_CLIENT_CMD_FUNC(info) if (!cmd->conn) { SILC_NOT_CONNECTED(cmd->client, cmd->conn); - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED); goto out; } @@ -865,78 +982,113 @@ SILC_CLIENT_CMD_FUNC(info) /* Send the command */ if (name) - buffer = silc_command_payload_encode_va(SILC_COMMAND_INFO, 0, 1, + buffer = silc_command_payload_encode_va(SILC_COMMAND_INFO, 0, 1, 1, name, strlen(name)); else buffer = silc_command_payload_encode(SILC_COMMAND_INFO, 0, NULL, NULL, NULL, 0); - silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, + silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 0, NULL, NULL, buffer->data, buffer->len, TRUE); silc_buffer_free(buffer); if (name) silc_free(name); /* Notify application */ - COMMAND; + COMMAND(SILC_STATUS_OK); + + out: + silc_client_command_free(cmd); +} + +/* Command STATS. Shows server and network statistics. */ + +SILC_CLIENT_CMD_FUNC(stats) +{ + SilcClientCommandContext cmd = (SilcClientCommandContext)context; + SilcClientConnection conn = cmd->conn; + SilcBuffer buffer, idp = NULL; + + if (!cmd->conn) { + SILC_NOT_CONNECTED(cmd->client, cmd->conn); + COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED); + goto out; + } + + idp = silc_id_payload_encode(conn->remote_id, SILC_ID_SERVER); + + /* Send the command */ + buffer = silc_command_payload_encode_va(SILC_COMMAND_STATS, + ++conn->cmd_ident, 1, + SILC_ID_SERVER, idp->data, idp->len); + silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, + 0, NULL, NULL, buffer->data, buffer->len, TRUE); + silc_buffer_free(buffer); + silc_buffer_free(idp); + + /* Notify application */ + COMMAND(SILC_STATUS_OK); out: silc_client_command_free(cmd); } -/* Command PING. Sends ping to server. This is used to test the +/* Command PING. Sends ping to server. This is used to test the communication channel. */ SILC_CLIENT_CMD_FUNC(ping) { SilcClientCommandContext cmd = (SilcClientCommandContext)context; SilcClientConnection conn = cmd->conn; - SilcBuffer buffer; + SilcBuffer buffer, idp; void *id; int i; if (!cmd->conn) { SILC_NOT_CONNECTED(cmd->client, cmd->conn); - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED); goto out; } + idp = silc_id_payload_encode(conn->remote_id, SILC_ID_SERVER); + /* Send the command */ - buffer = silc_command_payload_encode_va(SILC_COMMAND_PING, 0, 1, - 1, conn->remote_id_data, - silc_id_get_len(conn->remote_id, - SILC_ID_SERVER)); - silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, + buffer = silc_command_payload_encode_va(SILC_COMMAND_PING, 0, 1, + 1, idp->data, idp->len); + silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 0, NULL, NULL, buffer->data, buffer->len, TRUE); silc_buffer_free(buffer); + silc_buffer_free(idp); id = silc_id_str2id(conn->remote_id_data, conn->remote_id_data_len, SILC_ID_SERVER); if (!id) { SILC_NOT_CONNECTED(cmd->client, cmd->conn); - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED); goto out; } /* Start counting time */ - for (i = 0; i < conn->ping_count; i++) { - if (conn->ping[i].dest_id == NULL) { - conn->ping[i].start_time = time(NULL); - conn->ping[i].dest_id = id; - conn->ping[i].dest_name = strdup(conn->remote_host); + for (i = 0; i < conn->internal->ping_count; i++) { + if (conn->internal->ping[i].dest_id == NULL) { + conn->internal->ping[i].start_time = time(NULL); + conn->internal->ping[i].dest_id = id; + conn->internal->ping[i].dest_name = strdup(conn->remote_host); break; } } - if (i >= conn->ping_count) { - i = conn->ping_count; - conn->ping = silc_realloc(conn->ping, sizeof(*conn->ping) * (i + 1)); - conn->ping[i].start_time = time(NULL); - conn->ping[i].dest_id = id; - conn->ping[i].dest_name = strdup(conn->remote_host); - conn->ping_count++; + if (i >= conn->internal->ping_count) { + i = conn->internal->ping_count; + conn->internal->ping = + silc_realloc(conn->internal->ping, + sizeof(*conn->internal->ping) * (i + 1)); + conn->internal->ping[i].start_time = time(NULL); + conn->internal->ping[i].dest_id = id; + conn->internal->ping[i].dest_name = strdup(conn->remote_host); + conn->internal->ping_count++; } - + /* Notify application */ - COMMAND; + COMMAND(SILC_STATUS_OK); out: silc_client_command_free(cmd); @@ -949,21 +1101,21 @@ SILC_CLIENT_CMD_FUNC(join) SilcClientCommandContext cmd = (SilcClientCommandContext)context; SilcClientConnection conn = cmd->conn; SilcChannelEntry channel; - SilcBuffer buffer, idp, auth = NULL; - char *name, *passphrase = NULL, *cipher = NULL, *hmac = NULL; - int i; + SilcBuffer buffer, idp, auth = NULL, cauth = NULL; + char *name, *passphrase = NULL, *pu8, *cipher = NULL, *hmac = NULL; + int i, passphrase_len = 0; if (!cmd->conn) { SILC_NOT_CONNECTED(cmd->client, cmd->conn); - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED); goto out; } if (cmd->argc < 2) { - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } - + /* See if we have joined to the requested channel already */ channel = silc_client_get_channel(cmd->client, conn, cmd->argv[1]); if (channel && silc_client_on_channel(channel, conn->local_entry)) @@ -983,44 +1135,89 @@ SILC_CLIENT_CMD_FUNC(join) } else if (!strcasecmp(cmd->argv[i], "-hmac") && cmd->argc > i + 1) { hmac = cmd->argv[i + 1]; i++; - } else if (!strcasecmp(cmd->argv[i], "-founder") && cmd->argc > i + 1) { - if (!strcasecmp(cmd->argv[i + 1], "-pubkey")) { - auth = silc_auth_public_key_auth_generate(cmd->client->public_key, - cmd->client->private_key, - cmd->client->rng, conn->hash, - conn->local_id, - SILC_ID_CLIENT); - } else { - auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0, - cmd->argv[i + 1], - cmd->argv_lens[i + 1]); + } else if (!strcasecmp(cmd->argv[i], "-founder")) { + auth = silc_auth_public_key_auth_generate(cmd->client->public_key, + cmd->client->private_key, + cmd->client->rng, + cmd->client->sha1hash, + conn->local_id, + SILC_ID_CLIENT); + i++; + } else if (!strcasecmp(cmd->argv[i], "-auth")) { + SilcPublicKey pubkey = cmd->client->public_key; + SilcPrivateKey privkey = cmd->client->private_key; + unsigned char *pk, pkhash[20], *pubdata; + SilcUInt32 pk_len; + + if (cmd->argc >= i + 3) { + char *pass = ""; + if (cmd->argc >= i + 4) { + pass = cmd->argv[i + 3]; + i++; + } + if (!silc_load_key_pair(cmd->argv[i + 1], cmd->argv[i + 2], pass, + NULL, &pubkey, &privkey)) { + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, + "Could not load key pair, check your arguments"); + COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); + goto out; + } + i += 2; } + + pk = silc_pkcs_public_key_encode(pubkey, &pk_len); + silc_hash_make(cmd->client->sha1hash, pk, pk_len, pkhash); + silc_free(pk); + pubdata = silc_rng_get_rn_data(cmd->client->rng, 128); + memcpy(pubdata, pkhash, 20); + cauth = silc_auth_public_key_auth_generate_wpub(pubkey, privkey, + pubdata, 128, + cmd->client->sha1hash, + conn->local_id, + SILC_ID_CLIENT); + memset(pubdata, 0, 128); + silc_free(pubdata); i++; } else { - passphrase = cmd->argv[i]; + /* Passphrases must be UTF-8 encoded, so encode if it is not */ + if (!silc_utf8_valid(cmd->argv[i], cmd->argv_lens[i])) { + passphrase_len = silc_utf8_encoded_len(cmd->argv[i], + cmd->argv_lens[i], 0); + pu8 = silc_calloc(passphrase_len, sizeof(*pu8)); + passphrase_len = silc_utf8_encode(cmd->argv[i], cmd->argv_lens[i], + 0, pu8, passphrase_len); + passphrase = pu8; + } else { + passphrase = strdup(cmd->argv[i]); + passphrase_len = cmd->argv_lens[i]; + } } } /* Send JOIN command to the server */ buffer = - silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 6, + silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 7, 1, name, strlen(name), 2, idp->data, idp->len, - 3, passphrase, - passphrase ? strlen(passphrase) : 0, + 3, passphrase, passphrase_len, 4, cipher, cipher ? strlen(cipher) : 0, 5, hmac, hmac ? strlen(hmac) : 0, 6, auth ? auth->data : NULL, - auth ? auth->len : 0); - silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, + auth ? auth->len : 0, + 7, cauth ? cauth->data : NULL, + cauth ? cauth->len : 0); + silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 0, NULL, NULL, buffer->data, buffer->len, TRUE); silc_buffer_free(buffer); silc_buffer_free(idp); - if (auth) - silc_buffer_free(auth); + silc_buffer_free(auth); + silc_buffer_free(cauth); + if (passphrase) + memset(passphrase, 0, strlen(passphrase)); + silc_free(passphrase); /* Notify application */ - COMMAND; + COMMAND(SILC_STATUS_OK); out: silc_client_command_free(cmd); @@ -1036,32 +1233,33 @@ SILC_CLIENT_CMD_FUNC(motd) if (!cmd->conn) { SILC_NOT_CONNECTED(cmd->client, cmd->conn); - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED); goto out; } if (cmd->argc < 1 || cmd->argc > 2) { SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "Usage: /MOTD []"); - COMMAND_ERROR; + COMMAND_ERROR((cmd->argc < 1 ? SILC_STATUS_ERR_NOT_ENOUGH_PARAMS : + SILC_STATUS_ERR_TOO_MANY_PARAMS)); goto out; } /* Send TOPIC command to the server */ if (cmd->argc == 1) - buffer = silc_command_payload_encode_va(SILC_COMMAND_MOTD, 0, 1, - 1, conn->remote_host, + buffer = silc_command_payload_encode_va(SILC_COMMAND_MOTD, 0, 1, + 1, conn->remote_host, strlen(conn->remote_host)); else - buffer = silc_command_payload_encode_va(SILC_COMMAND_MOTD, 0, 1, - 1, cmd->argv[1], + buffer = silc_command_payload_encode_va(SILC_COMMAND_MOTD, 0, 1, + 1, cmd->argv[1], cmd->argv_lens[1]); - silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, + silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 0, NULL, NULL, buffer->data, buffer->len, TRUE); silc_buffer_free(buffer); /* Notify application */ - COMMAND; + COMMAND(SILC_STATUS_OK); out: silc_client_command_free(cmd); @@ -1081,14 +1279,14 @@ SILC_CLIENT_CMD_FUNC(umode) if (!cmd->conn) { SILC_NOT_CONNECTED(cmd->client, cmd->conn); - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED); goto out; } if (cmd->argc < 2) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "Usage: /UMODE +|-"); - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } @@ -1110,6 +1308,14 @@ SILC_CLIENT_CMD_FUNC(umode) mode = 0; mode |= SILC_UMODE_SERVER_OPERATOR; mode |= SILC_UMODE_ROUTER_OPERATOR; + mode |= SILC_UMODE_GONE; + mode |= SILC_UMODE_INDISPOSED; + mode |= SILC_UMODE_BUSY; + mode |= SILC_UMODE_PAGE; + mode |= SILC_UMODE_HYPER; + mode |= SILC_UMODE_ROBOT; + mode |= SILC_UMODE_BLOCK_PRIVMSG; + mode |= SILC_UMODE_REJECT_WATCHING; } else { mode = SILC_UMODE_NONE; } @@ -1132,8 +1338,56 @@ SILC_CLIENT_CMD_FUNC(umode) else mode &= ~SILC_UMODE_GONE; break; + case 'i': + if (add) + mode |= SILC_UMODE_INDISPOSED; + else + mode &= ~SILC_UMODE_INDISPOSED; + break; + case 'b': + if (add) + mode |= SILC_UMODE_BUSY; + else + mode &= ~SILC_UMODE_BUSY; + break; + case 'p': + if (add) + mode |= SILC_UMODE_PAGE; + else + mode &= ~SILC_UMODE_PAGE; + break; + case 'h': + if (add) + mode |= SILC_UMODE_HYPER; + else + mode &= ~SILC_UMODE_HYPER; + break; + case 't': + if (add) + mode |= SILC_UMODE_ROBOT; + else + mode &= ~SILC_UMODE_ROBOT; + break; + case 'P': + if (add) + mode |= SILC_UMODE_BLOCK_PRIVMSG; + else + mode &= ~SILC_UMODE_BLOCK_PRIVMSG; + break; + case 'w': + if (add) + mode |= SILC_UMODE_REJECT_WATCHING; + else + mode &= ~SILC_UMODE_REJECT_WATCHING; + break; + case 'I': + if (add) + mode |= SILC_UMODE_BLOCK_INVITE; + else + mode &= ~SILC_UMODE_BLOCK_INVITE; + break; default: - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE); goto out; break; } @@ -1144,17 +1398,17 @@ SILC_CLIENT_CMD_FUNC(umode) /* Send the command packet. We support sending only one mode at once that requires an argument. */ - buffer = - silc_command_payload_encode_va(SILC_COMMAND_UMODE, ++conn->cmd_ident, 2, - 1, idp->data, idp->len, + buffer = + silc_command_payload_encode_va(SILC_COMMAND_UMODE, ++conn->cmd_ident, 2, + 1, idp->data, idp->len, 2, modebuf, sizeof(modebuf)); - silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, + silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 0, NULL, NULL, buffer->data, buffer->len, TRUE); silc_buffer_free(buffer); silc_buffer_free(idp); /* Notify application */ - COMMAND; + COMMAND(SILC_STATUS_OK); out: silc_client_command_free(cmd); @@ -1169,29 +1423,27 @@ SILC_CLIENT_CMD_FUNC(cmode) SilcClientCommandContext cmd = (SilcClientCommandContext)context; SilcClientConnection conn = cmd->conn; SilcChannelEntry channel; - SilcBuffer buffer, chidp, auth = NULL; + SilcBuffer buffer, chidp, auth = NULL, pk = NULL; unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL; SilcUInt32 mode, add, type, len, arg_len = 0; int i; if (!cmd->conn) { SILC_NOT_CONNECTED(cmd->client, cmd->conn); - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED); goto out; } if (cmd->argc < 3) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "Usage: /CMODE +|- [{ }]"); - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } if (cmd->argv[1][0] == '*') { if (!conn->current_channel) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, - "You are not on any channel"); - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL); goto out; } @@ -1201,9 +1453,7 @@ SILC_CLIENT_CMD_FUNC(cmode) channel = silc_client_get_channel(cmd->client, conn, name); if (!channel) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, - "You are on that channel"); - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL); goto out; } } @@ -1254,15 +1504,27 @@ SILC_CLIENT_CMD_FUNC(cmode) else mode &= ~SILC_CHANNEL_MODE_TOPIC; break; + case 'm': + if (add) + mode |= SILC_CHANNEL_MODE_SILENCE_USERS; + else + mode &= ~SILC_CHANNEL_MODE_SILENCE_USERS; + break; + case 'M': + if (add) + mode |= SILC_CHANNEL_MODE_SILENCE_OPERS; + else + mode &= ~SILC_CHANNEL_MODE_SILENCE_OPERS; + break; case 'l': if (add) { int ll; mode |= SILC_CHANNEL_MODE_ULIMIT; type = 3; if (cmd->argc < 4) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "Usage: /CMODE +|- [{ }]"); - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } ll = atoi(cmd->argv[3]); @@ -1278,9 +1540,9 @@ SILC_CLIENT_CMD_FUNC(cmode) mode |= SILC_CHANNEL_MODE_PASSPHRASE; type = 4; if (cmd->argc < 4) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "Usage: /CMODE +|- [{ }]"); - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } arg = cmd->argv[3]; @@ -1294,9 +1556,9 @@ SILC_CLIENT_CMD_FUNC(cmode) mode |= SILC_CHANNEL_MODE_CIPHER; type = 5; if (cmd->argc < 4) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "Usage: /CMODE +|- [{ }]"); - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } arg = cmd->argv[3]; @@ -1310,9 +1572,9 @@ SILC_CLIENT_CMD_FUNC(cmode) mode |= SILC_CHANNEL_MODE_HMAC; type = 6; if (cmd->argc < 4) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "Usage: /CMODE +|- [{ }]"); - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } arg = cmd->argv[3]; @@ -1323,36 +1585,98 @@ SILC_CLIENT_CMD_FUNC(cmode) break; case 'f': if (add) { + SilcPublicKey pubkey = cmd->client->public_key; + SilcPrivateKey privkey = cmd->client->private_key; + mode |= SILC_CHANNEL_MODE_FOUNDER_AUTH; type = 7; - if (cmd->argc < 4) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, - "Usage: /CMODE +|- [{ }]"); - COMMAND_ERROR; + if (cmd->argc >= 5) { + char *pass = ""; + if (cmd->argc >= 6) + pass = cmd->argv[5]; + if (!silc_load_key_pair(cmd->argv[3], cmd->argv[4], pass, + NULL, &pubkey, &privkey)) { + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, + "Could not load key pair, check your arguments"); + COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); + goto out; + } + } + + pk = silc_pkcs_public_key_payload_encode(pubkey); + auth = silc_auth_public_key_auth_generate(pubkey, privkey, + cmd->client->rng, + cmd->client->sha1hash, + conn->local_id, + SILC_ID_CLIENT); + arg = auth->data; + arg_len = auth->len; + } else { + mode &= ~SILC_CHANNEL_MODE_FOUNDER_AUTH; + } + break; + case 'C': + if (add) { + int k; + bool chadd = FALSE; + SilcPublicKey chpk = NULL; + + mode |= SILC_CHANNEL_MODE_CHANNEL_AUTH; + type = 9; + + if (cmd->argc == 3) { + /* Send empty command to receive the public key list. */ + chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL); + silc_client_command_send(cmd->client, conn, SILC_COMMAND_CMODE, + 0, 1, 1, chidp->data, chidp->len); + silc_buffer_free(chidp); + + /* Notify application */ + COMMAND(SILC_STATUS_OK); goto out; } - if (!strcasecmp(cmd->argv[3], "-pubkey")) { - auth = silc_auth_public_key_auth_generate(cmd->client->public_key, - cmd->client->private_key, - cmd->client->rng, - conn->hash, - conn->local_id, - SILC_ID_CLIENT); - } else { - auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0, - cmd->argv[3], cmd->argv_lens[3]); + if (cmd->argc >= 4) { + auth = silc_buffer_alloc_size(2); + silc_buffer_format(auth, + SILC_STR_UI_SHORT(cmd->argc - 3), + SILC_STR_END); + } + + for (k = 3; k < cmd->argc; k++) { + if (cmd->argv[k][0] == '+') + chadd = TRUE; + if (!silc_pkcs_load_public_key(cmd->argv[k] + 1, &chpk, + SILC_PKCS_FILE_PEM)) + if (!silc_pkcs_load_public_key(cmd->argv[k] + 1, &chpk, + SILC_PKCS_FILE_BIN)) { + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, + "Could not load public key %s, check the filename", + cmd->argv[k]); + COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); + silc_buffer_free(auth); + goto out; + } + + if (chpk) { + pk = silc_pkcs_public_key_payload_encode(chpk); + auth = silc_argument_payload_encode_one(auth, pk->data, pk->len, + chadd ? 0x00 : 0x01); + silc_pkcs_public_key_free(chpk); + silc_buffer_free(pk); + pk = NULL; + } } arg = auth->data; arg_len = auth->len; } else { - mode &= ~SILC_CHANNEL_MODE_FOUNDER_AUTH; + mode &= ~SILC_CHANNEL_MODE_CHANNEL_AUTH; } break; default: - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE); goto out; break; } @@ -1364,27 +1688,29 @@ SILC_CLIENT_CMD_FUNC(cmode) /* Send the command packet. We support sending only one mode at once that requires an argument. */ if (type && arg) { - buffer = - silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 3, - 1, chidp->data, chidp->len, + buffer = + silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 4, + 1, chidp->data, chidp->len, 2, modebuf, sizeof(modebuf), - type, arg, arg_len); + type, arg, arg_len, + 8, pk ? pk->data : NULL, + pk ? pk->len : 0); } else { - buffer = - silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 2, - 1, chidp->data, chidp->len, + buffer = + silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 2, + 1, chidp->data, chidp->len, 2, modebuf, sizeof(modebuf)); } - silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, + silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 0, NULL, NULL, buffer->data, buffer->len, TRUE); silc_buffer_free(buffer); silc_buffer_free(chidp); - if (auth) - silc_buffer_free(auth); + silc_buffer_free(auth); + silc_buffer_free(pk); /* Notify application */ - COMMAND; + COMMAND(SILC_STATUS_OK); out: silc_client_command_free(cmd); @@ -1408,22 +1734,20 @@ SILC_CLIENT_CMD_FUNC(cumode) if (!cmd->conn) { SILC_NOT_CONNECTED(cmd->client, cmd->conn); - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED); goto out; } if (cmd->argc < 4) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "Usage: /CUMODE +|- [@]"); - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } if (cmd->argv[1][0] == '*') { if (!conn->current_channel) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, - "You are not on any channel"); - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL); goto out; } @@ -1433,9 +1757,7 @@ SILC_CLIENT_CMD_FUNC(cumode) channel = silc_client_get_channel(cmd->client, conn, name); if (!channel) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, - "You are on that channel"); - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL); goto out; } } @@ -1451,20 +1773,20 @@ SILC_CLIENT_CMD_FUNC(cumode) cmd->argv[3], TRUE); if (!client_entry) { if (cmd->pending) { - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK); goto out; } /* Client entry not found, it was requested thus mark this to be pending command. */ - silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, - conn->cmd_ident, - silc_client_command_cumode, + silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, + conn->cmd_ident, + silc_client_command_cumode, silc_client_command_dup(cmd)); cmd->pending = 1; goto out; } - + /* Get the current mode */ chu = silc_client_on_channel(channel, client_entry); if (chu) @@ -1485,25 +1807,36 @@ SILC_CLIENT_CMD_FUNC(cumode) if (add) { mode |= SILC_CHANNEL_UMODE_CHANFO; mode |= SILC_CHANNEL_UMODE_CHANOP; + mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES; + mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS; + mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS; } else { mode = SILC_CHANNEL_UMODE_NONE; } break; case 'f': if (add) { - if (cmd->argc == 5) { - if (!strcasecmp(cmd->argv[4], "-pubkey")) { - auth = silc_auth_public_key_auth_generate(cmd->client->public_key, - cmd->client->private_key, - cmd->client->rng, - conn->hash, - conn->local_id, - SILC_ID_CLIENT); - } else { - auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0, - cmd->argv[4], cmd->argv_lens[4]); + SilcPublicKey pubkey = cmd->client->public_key; + SilcPrivateKey privkey = cmd->client->private_key; + + if (cmd->argc >= 6) { + char *pass = ""; + if (cmd->argc >= 7) + pass = cmd->argv[6]; + if (!silc_load_key_pair(cmd->argv[4], cmd->argv[5], pass, + NULL, &pubkey, &privkey)) { + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, + "Could not load key pair, check your arguments"); + COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); + goto out; } } + + auth = silc_auth_public_key_auth_generate(pubkey, privkey, + cmd->client->rng, + cmd->client->sha1hash, + conn->local_id, + SILC_ID_CLIENT); mode |= SILC_CHANNEL_UMODE_CHANFO; } else { mode &= ~SILC_CHANNEL_UMODE_CHANFO; @@ -1515,8 +1848,32 @@ SILC_CLIENT_CMD_FUNC(cumode) else mode &= ~SILC_CHANNEL_UMODE_CHANOP; break; + case 'b': + if (add) + mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES; + else + mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES; + break; + case 'u': + if (add) + mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS; + else + mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS; + break; + case 'r': + if (add) + mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS; + else + mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS; + break; + case 'q': + if (add) + mode |= SILC_CHANNEL_UMODE_QUIET; + else + mode &= ~SILC_CHANNEL_UMODE_QUIET; + break; default: - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE); goto out; break; } @@ -1528,24 +1885,24 @@ SILC_CLIENT_CMD_FUNC(cumode) /* Send the command packet. We support sending only one mode at once that requires an argument. */ - buffer = silc_command_payload_encode_va(SILC_COMMAND_CUMODE, 0, - auth ? 4 : 3, - 1, chidp->data, chidp->len, + buffer = silc_command_payload_encode_va(SILC_COMMAND_CUMODE, 0, + auth ? 4 : 3, + 1, chidp->data, chidp->len, 2, modebuf, 4, 3, clidp->data, clidp->len, - 4, auth ? auth->data : NULL, + 4, auth ? auth->data : NULL, auth ? auth->len : 0); - - silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, + + silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 0, NULL, NULL, buffer->data, buffer->len, TRUE); silc_buffer_free(buffer); silc_buffer_free(chidp); silc_buffer_free(clidp); if (auth) silc_buffer_free(auth); - + /* Notify application */ - COMMAND; + COMMAND(SILC_STATUS_OK); out: silc_free(nickname); @@ -1568,22 +1925,20 @@ SILC_CLIENT_CMD_FUNC(kick) if (!cmd->conn) { SILC_NOT_CONNECTED(cmd->client, cmd->conn); - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED); goto out; } if (cmd->argc < 3) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "Usage: /KICK []"); - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } if (cmd->argv[1][0] == '*') { if (!conn->current_channel) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, - "You are not on any channel"); - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL); goto out; } name = conn->current_channel->channel_name; @@ -1592,17 +1947,14 @@ SILC_CLIENT_CMD_FUNC(kick) } if (!conn->current_channel) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, - "You are not on that channel"); - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL); goto out; } /* Get the Channel ID of the channel */ - if (!silc_idcache_find_by_name_one(conn->channel_cache, name, &id_cache)) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, - "You are not on that channel"); - COMMAND_ERROR; + if (!silc_idcache_find_by_name_one(conn->internal->channel_cache, + name, &id_cache)) { + COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL); goto out; } @@ -1615,12 +1967,12 @@ SILC_CLIENT_CMD_FUNC(kick) nickname = strdup(cmd->argv[2]); /* Get the target client */ - target = silc_idlist_get_client(cmd->client, conn, nickname, + target = silc_idlist_get_client(cmd->client, conn, nickname, cmd->argv[2], FALSE); if (!target) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "No such client: %s", cmd->argv[2]); - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK); goto out; } @@ -1628,14 +1980,14 @@ SILC_CLIENT_CMD_FUNC(kick) idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL); idp2 = silc_id_payload_encode(target->id, SILC_ID_CLIENT); if (cmd->argc == 3) - buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 2, + buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 2, 1, idp->data, idp->len, 2, idp2->data, idp2->len); else - buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 3, + buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 3, 1, idp->data, idp->len, 2, idp2->data, idp2->len, - 3, cmd->argv[3], + 3, cmd->argv[3], strlen(cmd->argv[3])); silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 0, NULL, NULL, buffer->data, buffer->len, TRUE); @@ -1644,7 +1996,7 @@ SILC_CLIENT_CMD_FUNC(kick) silc_buffer_free(idp2); /* Notify application */ - COMMAND; + COMMAND(SILC_STATUS_OK); out: silc_free(nickname); @@ -1662,7 +2014,8 @@ static void silc_client_command_oper_send(unsigned char *data, /* Encode the public key authentication payload */ auth = silc_auth_public_key_auth_generate(cmd->client->public_key, cmd->client->private_key, - cmd->client->rng, conn->hash, + cmd->client->rng, + conn->internal->hash, conn->local_id, SILC_ID_CLIENT); } else { @@ -1671,18 +2024,20 @@ static void silc_client_command_oper_send(unsigned char *data, data, data_len); } - buffer = silc_command_payload_encode_va(SILC_COMMAND_OPER, 0, 2, - 1, cmd->argv[1], + buffer = silc_command_payload_encode_va(SILC_COMMAND_OPER, 0, 2, + 1, cmd->argv[1], strlen(cmd->argv[1]), - 2, auth->data, auth->len); + 2, auth ? auth->data : NULL, + auth ? auth->len : 0); silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 0, NULL, NULL, buffer->data, buffer->len, TRUE); silc_buffer_free(buffer); + silc_buffer_clear(auth); silc_buffer_free(auth); /* Notify application */ - COMMAND; + COMMAND(SILC_STATUS_OK); } /* OPER command. Used to obtain server operator privileges. */ @@ -1694,14 +2049,14 @@ SILC_CLIENT_CMD_FUNC(oper) if (!cmd->conn) { SILC_NOT_CONNECTED(cmd->client, cmd->conn); - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED); goto out; } if (cmd->argc < 2) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "Usage: /OPER [-pubkey]"); - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } @@ -1720,7 +2075,8 @@ SILC_CLIENT_CMD_FUNC(oper) } static void silc_client_command_silcoper_send(unsigned char *data, - SilcUInt32 data_len, void *context) + SilcUInt32 data_len, + void *context) { SilcClientCommandContext cmd = (SilcClientCommandContext)context; SilcClientConnection conn = cmd->conn; @@ -1730,7 +2086,8 @@ static void silc_client_command_silcoper_send(unsigned char *data, /* Encode the public key authentication payload */ auth = silc_auth_public_key_auth_generate(cmd->client->public_key, cmd->client->private_key, - cmd->client->rng, conn->hash, + cmd->client->rng, + conn->internal->hash, conn->local_id, SILC_ID_CLIENT); } else { @@ -1739,18 +2096,20 @@ static void silc_client_command_silcoper_send(unsigned char *data, data, data_len); } - buffer = silc_command_payload_encode_va(SILC_COMMAND_SILCOPER, 0, 2, - 1, cmd->argv[1], + buffer = silc_command_payload_encode_va(SILC_COMMAND_SILCOPER, 0, 2, + 1, cmd->argv[1], strlen(cmd->argv[1]), - 2, auth->data, auth->len); + 2, auth ? auth->data : NULL, + auth ? auth->len : 0); silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 0, NULL, NULL, buffer->data, buffer->len, TRUE); silc_buffer_free(buffer); + silc_buffer_clear(auth); silc_buffer_free(auth); /* Notify application */ - COMMAND; + COMMAND(SILC_STATUS_OK); } /* SILCOPER command. Used to obtain router operator privileges. */ @@ -1762,14 +2121,14 @@ SILC_CLIENT_CMD_FUNC(silcoper) if (!cmd->conn) { SILC_NOT_CONNECTED(cmd->client, cmd->conn); - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED); goto out; } if (cmd->argc < 2) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "Usage: /SILCOPER [-pubkey]"); - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } @@ -1787,54 +2146,6 @@ SILC_CLIENT_CMD_FUNC(silcoper) silc_client_command_free(cmd); } -/* CONNECT command. Connects the server to another server. */ - -SILC_CLIENT_CMD_FUNC(connect) -{ - SilcClientCommandContext cmd = (SilcClientCommandContext)context; - SilcClientConnection conn = cmd->conn; - SilcBuffer buffer; - unsigned char port[4]; - SilcUInt32 tmp; - - if (!cmd->conn) { - SILC_NOT_CONNECTED(cmd->client, cmd->conn); - COMMAND_ERROR; - goto out; - } - - if (cmd->argc < 2) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, - "Usage: /CONNECT []"); - COMMAND_ERROR; - goto out; - } - - if (cmd->argc == 3) { - tmp = atoi(cmd->argv[2]); - SILC_PUT32_MSB(tmp, port); - } - - if (cmd->argc == 3) - buffer = silc_command_payload_encode_va(SILC_COMMAND_CONNECT, 0, 2, - 1, cmd->argv[1], - strlen(cmd->argv[1]), - 2, port, 4); - else - buffer = silc_command_payload_encode_va(SILC_COMMAND_CONNECT, 0, 1, - 1, cmd->argv[1], - strlen(cmd->argv[1])); - silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, - 0, NULL, NULL, buffer->data, buffer->len, TRUE); - silc_buffer_free(buffer); - - /* Notify application */ - COMMAND; - - out: - silc_client_command_free(cmd); -} - /* Command BAN. This is used to manage the ban list of the channel. */ SILC_CLIENT_CMD_FUNC(ban) @@ -1842,29 +2153,28 @@ SILC_CLIENT_CMD_FUNC(ban) SilcClientCommandContext cmd = (SilcClientCommandContext)context; SilcClientConnection conn = cmd->conn; SilcChannelEntry channel; - SilcBuffer buffer, chidp; - int type = 0; + SilcBuffer buffer, chidp, args = NULL; char *name, *ban = NULL; + unsigned char action[1]; + SilcPublicKey pubkey = NULL; if (!cmd->conn) { SILC_NOT_CONNECTED(cmd->client, cmd->conn); - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED); goto out; } if (cmd->argc < 2) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "Usage: /BAN " "[+|-[[@[![@hostname>]]]]]"); - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } if (cmd->argv[1][0] == '*') { if (!conn->current_channel) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, - "You are not on any channel"); - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL); goto out; } @@ -1874,114 +2184,139 @@ SILC_CLIENT_CMD_FUNC(ban) channel = silc_client_get_channel(cmd->client, conn, name); if (!channel) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, - "You are on that channel"); - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL); goto out; } } if (cmd->argc == 3) { if (cmd->argv[2][0] == '+') - type = 2; + action[0] = 0x00; else - type = 3; + action[0] = 0x01; + /* Check if it is public key file to be added to invite list */ + if (!silc_pkcs_load_public_key(cmd->argv[2] + 1, &pubkey, + SILC_PKCS_FILE_PEM)) + silc_pkcs_load_public_key(cmd->argv[2] + 1, &pubkey, + SILC_PKCS_FILE_BIN); ban = cmd->argv[2]; - ban++; + if (!pubkey) + ban++; + } + + if (ban) { + args = silc_buffer_alloc_size(2); + silc_buffer_format(args, + SILC_STR_UI_SHORT(1), + SILC_STR_END); + if (pubkey) { + chidp = silc_pkcs_public_key_payload_encode(pubkey); + args = silc_argument_payload_encode_one(args, chidp->data, + chidp->len, 2); + silc_buffer_free(chidp); + silc_pkcs_public_key_free(pubkey); + } else { + args = silc_argument_payload_encode_one(args, ban, strlen(ban), 1); + } } chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL); /* Send the command */ - if (ban) - buffer = silc_command_payload_encode_va(SILC_COMMAND_BAN, 0, 2, - 1, chidp->data, chidp->len, - type, ban, strlen(ban)); - else - buffer = silc_command_payload_encode_va(SILC_COMMAND_BAN, 0, 1, - 1, chidp->data, chidp->len); - - silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, + buffer = silc_command_payload_encode_va(SILC_COMMAND_BAN, + ++conn->cmd_ident, 3, + 1, chidp->data, chidp->len, + 2, args ? action : NULL, + args ? 1 : 0, + 3, args ? args->data : NULL, + args ? args->len : 0); + silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 0, NULL, NULL, buffer->data, buffer->len, TRUE); silc_buffer_free(buffer); silc_buffer_free(chidp); + silc_buffer_free(args); /* Notify application */ - COMMAND; + COMMAND(SILC_STATUS_OK); out: silc_client_command_free(cmd); } -/* CLOSE command. Close server connection to the remote server */ - -SILC_CLIENT_CMD_FUNC(close) +/* Command DETACH. This is used to detach from the server */ + +SILC_CLIENT_CMD_FUNC(detach) { SilcClientCommandContext cmd = (SilcClientCommandContext)context; SilcClientConnection conn = cmd->conn; SilcBuffer buffer; - unsigned char port[4]; - SilcUInt32 tmp; if (!cmd->conn) { SILC_NOT_CONNECTED(cmd->client, cmd->conn); - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED); goto out; } - if (cmd->argc < 2) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, - "Usage: /CLOSE []"); - COMMAND_ERROR; - goto out; - } - - if (cmd->argc == 3) { - tmp = atoi(cmd->argv[2]); - SILC_PUT32_MSB(tmp, port); - } - - if (cmd->argc == 3) - buffer = silc_command_payload_encode_va(SILC_COMMAND_CLOSE, 0, 2, - 1, cmd->argv[1], - strlen(cmd->argv[1]), - 2, port, 4); - else - buffer = silc_command_payload_encode_va(SILC_COMMAND_CLOSE, 0, 1, - 1, cmd->argv[1], - strlen(cmd->argv[1])); + buffer = silc_command_payload_encode_va(SILC_COMMAND_DETACH, + ++conn->cmd_ident, 0); silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 0, NULL, NULL, buffer->data, buffer->len, TRUE); silc_buffer_free(buffer); /* Notify application */ - COMMAND; + COMMAND(SILC_STATUS_OK); out: silc_client_command_free(cmd); } - -/* SHUTDOWN command. Shutdowns the server. */ -SILC_CLIENT_CMD_FUNC(shutdown) +/* Command WATCH. */ + +SILC_CLIENT_CMD_FUNC(watch) { SilcClientCommandContext cmd = (SilcClientCommandContext)context; + SilcClientConnection conn = cmd->conn; + SilcBuffer buffer, idp = NULL; + int type = 0; if (!cmd->conn) { SILC_NOT_CONNECTED(cmd->client, cmd->conn); - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED); goto out; } - /* Send the command */ - silc_client_command_send(cmd->client, cmd->conn, - SILC_COMMAND_SHUTDOWN, 0, 0); + if (cmd->argc < 3) { + COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); + goto out; + } + + idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT); + + if (!strcasecmp(cmd->argv[1], "-add")) { + type = 2; + } else if (!strcasecmp(cmd->argv[1], "-del")) { + type = 3; + } else { + COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); + goto out; + } + + buffer = silc_command_payload_encode_va(SILC_COMMAND_WATCH, + ++conn->cmd_ident, 2, + 1, idp->data, idp->len, + type, cmd->argv[2], + cmd->argv_lens[2]); + silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, + 0, NULL, NULL, buffer->data, buffer->len, TRUE); + silc_buffer_free(buffer); /* Notify application */ - COMMAND; + COMMAND(SILC_STATUS_OK); out: + if (idp) + silc_buffer_free(idp); silc_client_command_free(cmd); } @@ -1992,28 +2327,25 @@ SILC_CLIENT_CMD_FUNC(leave) SilcClientCommandContext cmd = (SilcClientCommandContext)context; SilcClientConnection conn = cmd->conn; SilcChannelEntry channel; - SilcChannelUser chu; SilcBuffer buffer, idp; char *name; if (!cmd->conn) { SILC_NOT_CONNECTED(cmd->client, cmd->conn); - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED); goto out; } if (cmd->argc != 2) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "Usage: /LEAVE "); - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } if (cmd->argv[1][0] == '*') { if (!conn->current_channel) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, - "You are not on any channel"); - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL); goto out; } name = conn->current_channel->channel_name; @@ -2024,37 +2356,25 @@ SILC_CLIENT_CMD_FUNC(leave) /* Get the channel entry */ channel = silc_client_get_channel(cmd->client, conn, name); if (!channel) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, - "You are not on that channel"); - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL); goto out; } - /* Remove us from channel */ - chu = silc_client_on_channel(channel, conn->local_entry); - if (chu) { - silc_hash_table_del(chu->client->channels, chu->channel); - silc_hash_table_del(chu->channel->user_list, chu->client); - silc_free(chu); - } - /* Send LEAVE command to the server */ idp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL); - buffer = silc_command_payload_encode_va(SILC_COMMAND_LEAVE, 0, 1, + buffer = silc_command_payload_encode_va(SILC_COMMAND_LEAVE, 0, 1, 1, idp->data, idp->len); - silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, + silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 0, NULL, NULL, buffer->data, buffer->len, TRUE); silc_buffer_free(buffer); silc_buffer_free(idp); /* Notify application */ - COMMAND; + COMMAND(SILC_STATUS_OK); if (conn->current_channel == channel) conn->current_channel = NULL; - silc_client_del_channel(cmd->client, cmd->conn, channel); - out: silc_client_command_free(cmd); } @@ -2071,22 +2391,20 @@ SILC_CLIENT_CMD_FUNC(users) if (!cmd->conn) { SILC_NOT_CONNECTED(cmd->client, cmd->conn); - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED); goto out; } if (cmd->argc != 2) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "Usage: /USERS "); - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } if (cmd->argv[1][0] == '*') { if (!conn->current_channel) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, - "You are not on any channel"); - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL); goto out; } name = conn->current_channel->channel_name; @@ -2095,16 +2413,16 @@ SILC_CLIENT_CMD_FUNC(users) } /* Send USERS command to the server */ - buffer = silc_command_payload_encode_va(SILC_COMMAND_USERS, - ++conn->cmd_ident, 1, + buffer = silc_command_payload_encode_va(SILC_COMMAND_USERS, + ++conn->cmd_ident, 1, 2, name, strlen(name)); - silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, - NULL, 0, NULL, NULL, buffer->data, + silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, + NULL, 0, NULL, NULL, buffer->data, buffer->len, TRUE); silc_buffer_free(buffer); /* Notify application */ - COMMAND; + COMMAND(SILC_STATUS_OK); out: silc_client_command_free(cmd); @@ -2126,14 +2444,14 @@ SILC_CLIENT_CMD_FUNC(getkey) if (!cmd->conn) { SILC_NOT_CONNECTED(cmd->client, cmd->conn); - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED); goto out; } if (cmd->argc < 2) { - client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_INFO, + client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_INFO, "Usage: /GETKEY "); - COMMAND_ERROR; + COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } @@ -2158,48 +2476,47 @@ SILC_CLIENT_CMD_FUNC(getkey) if (!cmd->pending) { /* This will send the IDENTIFY command for nickname */ silc_idlist_get_client(client, conn, nickname, cmd->argv[1], TRUE); - silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, - conn->cmd_ident, - silc_client_command_getkey, + silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, + conn->cmd_ident, + silc_client_command_getkey, silc_client_command_dup(cmd)); cmd->pending = 1; goto out; } else { - SilcClientCommandReplyContext reply = + SilcClientCommandReplyContext reply = (SilcClientCommandReplyContext)context2; - SilcCommandStatus status; - unsigned char *tmp = silc_argument_get_arg_type(reply->args, 1, NULL); - SILC_GET16_MSB(status, tmp); - + SilcStatus error; + /* If nickname was not found, then resolve the server. */ - if (status == SILC_STATUS_ERR_NO_SUCH_NICK) { + silc_command_get_status(reply->payload, NULL, &error); + if (error == SILC_STATUS_ERR_NO_SUCH_NICK) { /* This sends the IDENTIFY command to resolve the server. */ - silc_client_command_register(client, SILC_COMMAND_IDENTIFY, + silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL, silc_client_command_reply_identify_i, 0, ++conn->cmd_ident); silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY, - conn->cmd_ident, 1, + conn->cmd_ident, 1, 2, cmd->argv[1], cmd->argv_lens[1]); - silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, - conn->cmd_ident, - silc_client_command_getkey, + silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, + conn->cmd_ident, + silc_client_command_getkey, silc_client_command_dup(cmd)); goto out; } /* If server was not found, then we've resolved both nickname and server and did not find anybody. */ - if (status == SILC_STATUS_ERR_NO_SUCH_SERVER) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, "%s", - silc_client_command_status_message(SILC_STATUS_ERR_NO_SUCH_NICK)); - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, "%s", - silc_client_command_status_message(status)); - COMMAND_ERROR; + if (error == SILC_STATUS_ERR_NO_SUCH_SERVER) { + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, "%s", + silc_get_status_message(SILC_STATUS_ERR_NO_SUCH_NICK)); + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, "%s", + silc_get_status_message(error)); + COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK); goto out; } - COMMAND_ERROR; + COMMAND_ERROR(error); goto out; } } @@ -2209,15 +2526,15 @@ SILC_CLIENT_CMD_FUNC(getkey) idp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT); } - buffer = silc_command_payload_encode_va(SILC_COMMAND_GETKEY, 0, 1, + buffer = silc_command_payload_encode_va(SILC_COMMAND_GETKEY, 0, 1, 1, idp->data, idp->len); - silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, + silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 0, NULL, NULL, buffer->data, buffer->len, TRUE); silc_buffer_free(buffer); silc_buffer_free(idp); /* Notify application */ - COMMAND; + COMMAND(SILC_STATUS_OK); out: silc_free(nickname); @@ -2229,11 +2546,11 @@ SILC_CLIENT_CMD_FUNC(getkey) searched using the silc_client_command_find by that name. The `command_function' is the function to be called when the command is executed, and the `command_reply_function' is the function to be - called after the server has sent reply back to the command. + called after the server has sent reply back to the command. The `ident' is optional identifier for the command. If non-zero the `command_reply_function' for the command type `command' will be - called only if the command reply sent by server includes the + called only if the command reply sent by server includes the command identifier `ident'. Application usually does not need it and set it to zero value. */ @@ -2286,12 +2603,135 @@ bool silc_client_command_unregister(SilcClient client, return FALSE; } +/* Private range commands, specific to this implementation (and compatible + with SILC Server). */ + +/* CONNECT command. Connects the server to another server. */ + +SILC_CLIENT_CMD_FUNC(connect) +{ + SilcClientCommandContext cmd = (SilcClientCommandContext)context; + SilcClientConnection conn = cmd->conn; + SilcBuffer buffer; + unsigned char port[4]; + SilcUInt32 tmp; + + if (!cmd->conn) { + SILC_NOT_CONNECTED(cmd->client, cmd->conn); + COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED); + goto out; + } + + if (cmd->argc < 2) { + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, + "Usage: /CONNECT []"); + COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); + goto out; + } + + if (cmd->argc == 3) { + tmp = atoi(cmd->argv[2]); + SILC_PUT32_MSB(tmp, port); + } + + if (cmd->argc == 3) + buffer = silc_command_payload_encode_va(SILC_COMMAND_PRIV_CONNECT, 0, 2, + 1, cmd->argv[1], + strlen(cmd->argv[1]), + 2, port, 4); + else + buffer = silc_command_payload_encode_va(SILC_COMMAND_PRIV_CONNECT, 0, 1, + 1, cmd->argv[1], + strlen(cmd->argv[1])); + silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, + 0, NULL, NULL, buffer->data, buffer->len, TRUE); + silc_buffer_free(buffer); + + /* Notify application */ + COMMAND(SILC_STATUS_OK); + + out: + silc_client_command_free(cmd); +} + + +/* CLOSE command. Close server connection to the remote server */ + +SILC_CLIENT_CMD_FUNC(close) +{ + SilcClientCommandContext cmd = (SilcClientCommandContext)context; + SilcClientConnection conn = cmd->conn; + SilcBuffer buffer; + unsigned char port[4]; + SilcUInt32 tmp; + + if (!cmd->conn) { + SILC_NOT_CONNECTED(cmd->client, cmd->conn); + COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED); + goto out; + } + + if (cmd->argc < 2) { + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, + "Usage: /CLOSE []"); + COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); + goto out; + } + + if (cmd->argc == 3) { + tmp = atoi(cmd->argv[2]); + SILC_PUT32_MSB(tmp, port); + } + + if (cmd->argc == 3) + buffer = silc_command_payload_encode_va(SILC_COMMAND_PRIV_CLOSE, 0, 2, + 1, cmd->argv[1], + strlen(cmd->argv[1]), + 2, port, 4); + else + buffer = silc_command_payload_encode_va(SILC_COMMAND_PRIV_CLOSE, 0, 1, + 1, cmd->argv[1], + strlen(cmd->argv[1])); + silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, + 0, NULL, NULL, buffer->data, buffer->len, TRUE); + silc_buffer_free(buffer); + + /* Notify application */ + COMMAND(SILC_STATUS_OK); + + out: + silc_client_command_free(cmd); +} + +/* SHUTDOWN command. Shutdowns the server. */ + +SILC_CLIENT_CMD_FUNC(shutdown) +{ + SilcClientCommandContext cmd = (SilcClientCommandContext)context; + + if (!cmd->conn) { + SILC_NOT_CONNECTED(cmd->client, cmd->conn); + COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED); + goto out; + } + + /* Send the command */ + silc_client_command_send(cmd->client, cmd->conn, + SILC_COMMAND_PRIV_SHUTDOWN, 0, 0); + + /* Notify application */ + COMMAND(SILC_STATUS_OK); + + out: + silc_client_command_free(cmd); +} + /* Register all default commands provided by the client library for the application. */ void silc_client_commands_register(SilcClient client) { - silc_list_init(client->internal->commands, struct SilcClientCommandStruct, + silc_list_init(client->internal->commands, struct SilcClientCommandStruct, next); SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", 3); @@ -2302,24 +2742,28 @@ void silc_client_commands_register(SilcClient client) SILC_CLIENT_CMD(topic, TOPIC, "TOPIC", 3); SILC_CLIENT_CMD(invite, INVITE, "INVITE", 3); SILC_CLIENT_CMD(quit, QUIT, "QUIT", 2); - SILC_CLIENT_CMD(kill, KILL, "KILL", 3); + SILC_CLIENT_CMD(kill, KILL, "KILL", 4); SILC_CLIENT_CMD(info, INFO, "INFO", 2); - SILC_CLIENT_CMD(connect, CONNECT, "CONNECT", 3); + SILC_CLIENT_CMD(stats, STATS, "STATS", 0); SILC_CLIENT_CMD(ping, PING, "PING", 2); SILC_CLIENT_CMD(oper, OPER, "OPER", 3); SILC_CLIENT_CMD(join, JOIN, "JOIN", 9); SILC_CLIENT_CMD(motd, MOTD, "MOTD", 2); SILC_CLIENT_CMD(umode, UMODE, "UMODE", 2); - SILC_CLIENT_CMD(cmode, CMODE, "CMODE", 4); - SILC_CLIENT_CMD(cumode, CUMODE, "CUMODE", 5); + SILC_CLIENT_CMD(cmode, CMODE, "CMODE", 6); + SILC_CLIENT_CMD(cumode, CUMODE, "CUMODE", 9); SILC_CLIENT_CMD(kick, KICK, "KICK", 4); SILC_CLIENT_CMD(ban, BAN, "BAN", 3); - SILC_CLIENT_CMD(close, CLOSE, "CLOSE", 3); - SILC_CLIENT_CMD(shutdown, SHUTDOWN, "SHUTDOWN", 1); + SILC_CLIENT_CMD(detach, DETACH, "DETACH", 0); + SILC_CLIENT_CMD(watch, WATCH, "WATCH", 3); SILC_CLIENT_CMD(silcoper, SILCOPER, "SILCOPER", 3); SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", 2); SILC_CLIENT_CMD(users, USERS, "USERS", 2); SILC_CLIENT_CMD(getkey, GETKEY, "GETKEY", 2); + + SILC_CLIENT_CMD(connect, PRIV_CONNECT, "CONNECT", 3); + SILC_CLIENT_CMD(close, PRIV_CLOSE, "CLOSE", 3); + SILC_CLIENT_CMD(shutdown, PRIV_SHUTDOWN, "SHUTDOWN", 1); } /* Unregister all commands. */ @@ -2336,7 +2780,7 @@ void silc_client_commands_unregister(SilcClient client) SILC_CLIENT_CMDU(quit, QUIT, "QUIT"); SILC_CLIENT_CMDU(kill, KILL, "KILL"); SILC_CLIENT_CMDU(info, INFO, "INFO"); - SILC_CLIENT_CMDU(connect, CONNECT, "CONNECT"); + SILC_CLIENT_CMDU(stats, STATS, "STATS"); SILC_CLIENT_CMDU(ping, PING, "PING"); SILC_CLIENT_CMDU(oper, OPER, "OPER"); SILC_CLIENT_CMDU(join, JOIN, "JOIN"); @@ -2346,10 +2790,108 @@ void silc_client_commands_unregister(SilcClient client) SILC_CLIENT_CMDU(cumode, CUMODE, "CUMODE"); SILC_CLIENT_CMDU(kick, KICK, "KICK"); SILC_CLIENT_CMDU(ban, BAN, "BAN"); - SILC_CLIENT_CMDU(close, CLOSE, "CLOSE"); - SILC_CLIENT_CMDU(shutdown, SHUTDOWN, "SHUTDOWN"); + SILC_CLIENT_CMDU(detach, DETACH, "DETACH"); + SILC_CLIENT_CMDU(watch, WATCH, "WATCH"); SILC_CLIENT_CMDU(silcoper, SILCOPER, "SILCOPER"); SILC_CLIENT_CMDU(leave, LEAVE, "LEAVE"); SILC_CLIENT_CMDU(users, USERS, "USERS"); SILC_CLIENT_CMDU(getkey, GETKEY, "GETKEY"); + + SILC_CLIENT_CMDU(connect, PRIV_CONNECT, "CONNECT"); + SILC_CLIENT_CMDU(close, PRIV_CLOSE, "CLOSE"); + SILC_CLIENT_CMDU(shutdown, PRIV_SHUTDOWN, "SHUTDOWN"); +} + +/**** Client side incoming command handling **********************************/ + +void silc_client_command_process_whois(SilcClient client, + SilcSocketConnection sock, + SilcCommandPayload payload, + SilcArgumentPayload args); + +/* Client is able to receive some command packets even though they are + special case. Server may send WHOIS command to the client to retrieve + Requested Attributes information for WHOIS query the server is + processing. This function currently handles only the WHOIS command, + but if in the future for commands may arrive then this can be made + to support other commands too. */ + +void silc_client_command_process(SilcClient client, + SilcSocketConnection sock, + SilcPacketContext *packet) +{ + SilcCommandPayload payload; + SilcCommand command; + SilcArgumentPayload args; + + /* Get command payload from packet */ + payload = silc_command_payload_parse(packet->buffer->data, + packet->buffer->len); + if (!payload) { + /* Silently ignore bad reply packet */ + SILC_LOG_DEBUG(("Bad command packet")); + return; + } + + /* Get arguments */ + args = silc_command_get_args(payload); + + /* Get the command */ + command = silc_command_get(payload); + switch (command) { + + case SILC_COMMAND_WHOIS: + /* Ignore everything if requested by application */ + if (client->internal->params->ignore_requested_attributes) + break; + + silc_client_command_process_whois(client, sock, payload, args); + break; + + default: + break; + } + + silc_command_payload_free(payload); +} + +void silc_client_command_process_whois(SilcClient client, + SilcSocketConnection sock, + SilcCommandPayload payload, + SilcArgumentPayload args) +{ + SilcDList attrs; + unsigned char *tmp; + SilcUInt32 tmp_len; + SilcBuffer buffer, packet; + + SILC_LOG_DEBUG(("Received WHOIS command")); + + /* Try to take the Requested Attributes */ + tmp = silc_argument_get_arg_type(args, 3, &tmp_len); + if (!tmp) + return; + + attrs = silc_attribute_payload_parse(tmp, tmp_len); + if (!attrs) + return; + + /* Process requested attributes */ + buffer = silc_client_attributes_process(client, sock, attrs); + if (!buffer) { + silc_attribute_payload_list_free(attrs); + return; + } + + /* Send the attributes back */ + packet = + silc_command_reply_payload_encode_va(SILC_COMMAND_WHOIS, + SILC_STATUS_OK, 0, + silc_command_get_ident(payload), + 1, 11, buffer->data, buffer->len); + silc_client_packet_send(client, sock, SILC_PACKET_COMMAND_REPLY, + NULL, 0, NULL, NULL, packet->data, + packet->len, TRUE); + silc_buffer_free(packet); + silc_buffer_free(buffer); }