X-Git-Url: http://git.silcnet.org/gitweb/?p=silc.git;a=blobdiff_plain;f=lib%2Fsilcclient%2Fcommand.c;h=3131900efdc680fb6379fb6dafc5765c79639bef;hp=83e6bb69c77debbac2ce804e9e31f383403be579;hb=382d15d447b7a95390decfa783836ae4fe255b3d;hpb=5d90b0684f07a8a51d5dff5cd108a328ec82ffa9 diff --git a/lib/silcclient/command.c b/lib/silcclient/command.c index 83e6bb69..3131900e 100644 --- a/lib/silcclient/command.c +++ b/lib/silcclient/command.c @@ -48,6 +48,8 @@ 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); @@ -65,25 +67,93 @@ 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 @@ -103,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. */ @@ -119,15 +190,16 @@ void silc_client_command_pending_del(SilcClientConnection conn, { SilcClientCommandPending *r; - if (!conn->pending_commands) + if (!conn->internal->pending_commands) return; - silc_dlist_start(conn->pending_commands); - while ((r = silc_dlist_get(conn->pending_commands)) != SILC_LIST_END) { + 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->pending_commands, r); + silc_dlist_del(conn->internal->pending_commands, r); silc_free(r); } } @@ -147,8 +219,9 @@ silc_client_command_pending_check(SilcClientConnection conn, SilcClientCommandPendingCallbacks callbacks = NULL; int i = 0; - silc_dlist_start(conn->pending_commands); - while ((r = silc_dlist_get(conn->pending_commands)) != SILC_LIST_END) { + 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)); @@ -185,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); @@ -438,7 +512,8 @@ 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); } @@ -507,7 +582,8 @@ SILC_CLIENT_CMD_FUNC(topic) } /* 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)) { COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL); goto out; } @@ -548,10 +624,11 @@ 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); @@ -611,12 +688,35 @@ SILC_CLIENT_CMD_FUNC(invite) 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,24 +725,29 @@ SILC_CLIENT_CMD_FUNC(invite) 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, + ++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, + ++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, 0, NULL, NULL, buffer->data, buffer->len, TRUE); silc_buffer_free(buffer); silc_buffer_free(chidp); + silc_buffer_free(args), /* Notify application */ COMMAND(SILC_STATUS_OK); @@ -662,7 +767,7 @@ SILC_TASK_CALLBACK(silc_client_command_quit_cb) QuitInternal q = (QuitInternal)context; /* Close connection */ - q->client->internal->ops->disconnect(q->client, q->conn, 0, NULL); + 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); @@ -770,9 +875,9 @@ 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); @@ -782,7 +887,7 @@ SILC_CLIENT_CMD_FUNC(kill) if (cmd->argc < 2) { SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, - "Usage: /KILL []"); + "Usage: /KILL [] [-pubkey]"); COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } @@ -812,22 +917,35 @@ SILC_CLIENT_CMD_FUNC(kill) 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(SILC_STATUS_OK); @@ -921,7 +1039,7 @@ SILC_CLIENT_CMD_FUNC(ping) { SilcClientCommandContext cmd = (SilcClientCommandContext)context; SilcClientConnection conn = cmd->conn; - SilcBuffer buffer; + SilcBuffer buffer, idp; void *id; int i; @@ -931,14 +1049,15 @@ SILC_CLIENT_CMD_FUNC(ping) 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)); + 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); @@ -949,21 +1068,23 @@ SILC_CLIENT_CMD_FUNC(ping) } /* 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 */ @@ -1018,8 +1139,7 @@ SILC_CLIENT_CMD_FUNC(join) auth = silc_auth_public_key_auth_generate(cmd->client->public_key, cmd->client->private_key, cmd->client->rng, - cmd->client->internal-> - sha1hash, + cmd->client->sha1hash, conn->local_id, SILC_ID_CLIENT); i++; @@ -1264,7 +1384,7 @@ 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; @@ -1426,13 +1546,29 @@ 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; - auth = silc_auth_public_key_auth_generate(cmd->client->public_key, - cmd->client->private_key, + + 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->internal-> - sha1hash, + cmd->client->sha1hash, conn->local_id, SILC_ID_CLIENT); arg = auth->data; @@ -1455,13 +1591,15 @@ SILC_CLIENT_CMD_FUNC(cmode) that requires an argument. */ if (type && arg) { buffer = - silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 3, + 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, + silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 2, 1, chidp->data, chidp->len, 2, modebuf, sizeof(modebuf)); } @@ -1470,8 +1608,8 @@ SILC_CLIENT_CMD_FUNC(cmode) 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(SILC_STATUS_OK); @@ -1580,11 +1718,25 @@ SILC_CLIENT_CMD_FUNC(cumode) break; case 'f': if (add) { - auth = silc_auth_public_key_auth_generate(cmd->client->public_key, - cmd->client->private_key, + 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->internal-> - sha1hash, + cmd->client->sha1hash, conn->local_id, SILC_ID_CLIENT); mode |= SILC_CHANNEL_UMODE_CHANFO; @@ -1702,7 +1854,8 @@ SILC_CLIENT_CMD_FUNC(kick) } /* 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)) { COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL); goto out; } @@ -1763,7 +1916,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 { @@ -1833,7 +1987,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 { @@ -1898,10 +2053,11 @@ 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(SILC_STATUS_ERR_NOT_REGISTERED); @@ -1935,25 +2091,51 @@ SILC_CLIENT_CMD_FUNC(ban) 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 */ buffer = silc_command_payload_encode_va(SILC_COMMAND_BAN, - ++conn->cmd_ident, 2, + ++conn->cmd_ident, 3, 1, chidp->data, chidp->len, - type, ban, ban ? strlen(ban) : 0); + 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(SILC_STATUS_OK); @@ -2471,7 +2653,7 @@ 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(stats, STATS, "STATS", 0); SILC_CLIENT_CMD(ping, PING, "PING", 2); @@ -2479,8 +2661,8 @@ void silc_client_commands_register(SilcClient client) 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(detach, DETACH, "DETACH", 0);