5 Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
7 Copyright (C) 1997 - 2000 Pekka Riikonen
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
22 #include "clientlibincludes.h"
24 /* Client command list. */
25 SilcClientCommand silc_command_list[] =
27 SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", SILC_CF_LAG | SILC_CF_REG, 3),
28 SILC_CLIENT_CMD(whowas, WHOWAS, "WHOWAS", SILC_CF_LAG | SILC_CF_REG, 3),
29 SILC_CLIENT_CMD(identify, IDENTIFY, "IDENTIFY",
30 SILC_CF_LAG | SILC_CF_REG, 3),
31 SILC_CLIENT_CMD(nick, NICK, "NICK", SILC_CF_LAG | SILC_CF_REG, 2),
32 SILC_CLIENT_CMD(list, LIST, "LIST", SILC_CF_LAG | SILC_CF_REG, 2),
33 SILC_CLIENT_CMD(topic, TOPIC, "TOPIC", SILC_CF_LAG | SILC_CF_REG, 3),
34 SILC_CLIENT_CMD(invite, INVITE, "INVITE", SILC_CF_LAG | SILC_CF_REG, 3),
35 SILC_CLIENT_CMD(quit, QUIT, "QUIT", SILC_CF_LAG | SILC_CF_REG, 1),
36 SILC_CLIENT_CMD(kill, KILL, "KILL",
37 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
38 SILC_CLIENT_CMD(info, INFO, "INFO", SILC_CF_LAG | SILC_CF_REG, 2),
39 SILC_CLIENT_CMD(connect, CONNECT, "CONNECT",
40 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
41 SILC_CLIENT_CMD(ping, PING, "PING", SILC_CF_LAG | SILC_CF_REG, 2),
42 SILC_CLIENT_CMD(oper, OPER, "OPER",
43 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
44 SILC_CLIENT_CMD(join, JOIN, "JOIN", SILC_CF_LAG | SILC_CF_REG, 2),
45 SILC_CLIENT_CMD(motd, MOTD, "MOTD", SILC_CF_LAG | SILC_CF_REG, 2),
46 SILC_CLIENT_CMD(umode, UMODE, "UMODE", SILC_CF_LAG | SILC_CF_REG, 2),
47 SILC_CLIENT_CMD(cmode, CMODE, "CMODE", SILC_CF_LAG | SILC_CF_REG, 2),
48 SILC_CLIENT_CMD(kick, KICK, "KICK", SILC_CF_LAG | SILC_CF_REG, 2),
49 SILC_CLIENT_CMD(restart, RESTART, "RESTART",
50 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
51 SILC_CLIENT_CMD(close, CLOSE, "CLOSE",
52 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
53 SILC_CLIENT_CMD(die, DIE, "DIE",
54 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
55 SILC_CLIENT_CMD(silcoper, SILCOPER, "SILOPER",
56 SILC_CF_LAG | SILC_CF_REG | SILC_CF_SILC_OPER, 2),
57 SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", SILC_CF_LAG | SILC_CF_REG, 2),
58 SILC_CLIENT_CMD(names, NAMES, "NAMES", SILC_CF_LAG | SILC_CF_REG, 2),
60 { NULL, 0, NULL, 0, 0 },
63 #define SILC_NOT_CONNECTED(x, c) \
64 x->ops->say((x), (c), \
65 "You are not connected to a server, use /SERVER to connect");
67 /* Command operation that is called at the end of all commands.
69 #define COMMAND cmd->client->ops->command(cmd->client, cmd->conn, \
70 cmd, TRUE, cmd->command->cmd)
72 /* Error to application. Usage: COMMAND_ERROR; */
73 #define COMMAND_ERROR cmd->client->ops->command(cmd->client, cmd->conn, \
74 cmd, FALSE, cmd->command->cmd)
76 /* List of pending commands. */
77 SilcClientCommandPending *silc_command_pending = NULL;
79 /* Finds and returns a pointer to the command list. Return NULL if the
80 command is not found. */
82 SilcClientCommand *silc_client_command_find(const char *name)
84 SilcClientCommand *cmd;
86 for (cmd = silc_command_list; cmd->name; cmd++) {
87 if (!strcmp(cmd->name, name))
94 /* Add new pending command to the list of pending commands. Currently
95 pending commands are executed from command replies, thus we can
96 execute any command after receiving some specific command reply.
98 The argument `reply_cmd' is the command reply from where the callback
99 function is to be called, thus, it IS NOT the command to be executed.
101 XXX: If needed in the future this support may be extended for
102 commands as well, when any command could be executed after executing
103 some specific command. */
105 void silc_client_command_pending(SilcCommand reply_cmd,
106 SilcClientCommandCallback callback,
109 SilcClientCommandPending *reply, *r;
111 reply = silc_calloc(1, sizeof(*reply));
112 reply->reply_cmd = reply_cmd;
113 reply->context = context;
114 reply->callback = callback;
116 if (silc_command_pending == NULL) {
117 silc_command_pending = reply;
121 for (r = silc_command_pending; r; r = r->next) {
122 if (r->next == NULL) {
129 /* Deletes pending command by reply command type. */
131 void silc_client_command_pending_del(SilcCommand reply_cmd)
133 SilcClientCommandPending *r, *tmp;
135 if (silc_command_pending) {
136 if (silc_command_pending->reply_cmd == reply_cmd) {
137 silc_free(silc_command_pending);
138 silc_command_pending = NULL;
142 for (r = silc_command_pending; r; r = r->next) {
143 if (r->next && r->next->reply_cmd == reply_cmd) {
145 r->next = r->next->next;
153 /* Free command context and its internals */
155 void silc_client_command_free(SilcClientCommandContext cmd)
160 for (i = 0; i < cmd->argc; i++)
161 silc_free(cmd->argv[i]);
166 /* Command WHOIS. This command is used to query information about
169 SILC_CLIENT_CMD_FUNC(whois)
171 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
172 SilcClientConnection conn = cmd->conn;
176 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
181 if (cmd->argc < 2 || cmd->argc > 3) {
182 cmd->client->ops->say(cmd->client, conn,
183 "Usage: /WHOIS <nickname>[@<server>] [<count>]");
188 buffer = silc_command_payload_encode(SILC_COMMAND_WHOIS,
189 cmd->argc - 1, ++cmd->argv,
190 ++cmd->argv_lens, ++cmd->argv_types,
192 silc_client_packet_send(cmd->client, cmd->conn->sock,
193 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
194 buffer->data, buffer->len, TRUE);
195 silc_buffer_free(buffer);
200 /* Notify application */
204 silc_client_command_free(cmd);
207 SILC_CLIENT_CMD_FUNC(whowas)
211 /* Command IDENTIFY. This command is used to query information about
212 specific user, especially ID's. */
214 SILC_CLIENT_CMD_FUNC(identify)
216 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
217 SilcClientConnection conn = cmd->conn;
221 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
226 if (cmd->argc < 2 || cmd->argc > 3) {
227 cmd->client->ops->say(cmd->client, conn,
228 "Usage: /IDENTIFY <nickname>[@<server>] [<count>]");
233 buffer = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
234 cmd->argc - 1, ++cmd->argv,
235 ++cmd->argv_lens, ++cmd->argv_types,
237 silc_client_packet_send(cmd->client, cmd->conn->sock,
238 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
239 buffer->data, buffer->len, TRUE);
240 silc_buffer_free(buffer);
245 /* Notify application */
249 silc_client_command_free(cmd);
252 /* Command NICK. Shows current nickname/sets new nickname on current
255 SILC_CLIENT_CMD_FUNC(nick)
257 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
258 SilcClientConnection conn = cmd->conn;
262 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
267 /* Show current nickname */
270 cmd->client->ops->say(cmd->client, conn,
271 "Your nickname is %s on server %s",
272 conn->nickname, conn->remote_host);
274 cmd->client->ops->say(cmd->client, conn,
275 "Your nickname is %s", conn->nickname);
277 /* XXX Notify application */
282 /* Set new nickname */
283 buffer = silc_command_payload_encode(SILC_COMMAND_NICK,
284 cmd->argc - 1, ++cmd->argv,
285 ++cmd->argv_lens, ++cmd->argv_types,
287 silc_client_packet_send(cmd->client, cmd->conn->sock,
288 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
289 buffer->data, buffer->len, TRUE);
290 silc_buffer_free(buffer);
295 silc_free(conn->nickname);
296 conn->nickname = strdup(cmd->argv[1]);
298 /* Notify application */
302 silc_client_command_free(cmd);
305 SILC_CLIENT_CMD_FUNC(list)
309 /* Command TOPIC. Sets/shows topic on a channel. */
311 SILC_CLIENT_CMD_FUNC(topic)
313 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
314 SilcClientConnection conn = cmd->conn;
315 SilcIDCacheEntry id_cache = NULL;
316 SilcChannelEntry channel;
318 unsigned char *id_string;
322 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
327 if (cmd->argc < 2 || cmd->argc > 3) {
328 cmd->client->ops->say(cmd->client, conn,
329 "Usage: /TOPIC <channel> [<topic>]");
334 if (cmd->argv[1][0] == '*') {
335 if (!conn->current_channel) {
336 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
340 name = conn->current_channel->channel_name;
345 if (!conn->current_channel) {
346 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
351 /* Get the Channel ID of the channel */
352 if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
353 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
358 channel = (SilcChannelEntry)id_cache->context;
360 /* Send TOPIC command to the server */
361 id_string = silc_id_id2str(id_cache->id, SILC_ID_CHANNEL);
363 buffer = silc_command_payload_encode_va(SILC_COMMAND_TOPIC, 0, 2,
364 1, id_string, SILC_ID_CHANNEL_LEN,
366 strlen(cmd->argv[2]));
368 buffer = silc_command_payload_encode_va(SILC_COMMAND_TOPIC, 1,
369 1, id_string, SILC_ID_CHANNEL_LEN,
371 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
372 0, NULL, NULL, buffer->data, buffer->len, TRUE);
373 silc_buffer_free(buffer);
375 /* Notify application */
379 silc_client_command_free(cmd);
382 /* Command INVITE. Invites specific client to join a channel. */
384 SILC_CLIENT_CMD_FUNC(invite)
386 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
387 SilcClient client = cmd->client;
388 SilcClientConnection conn = cmd->conn;
389 SilcClientEntry client_entry;
390 SilcChannelEntry channel_entry;
392 unsigned int num = 0;
393 char *nickname = NULL, *server = NULL;
394 unsigned char *client_id, *channel_id;
397 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
402 if (cmd->argc != 3) {
403 cmd->client->ops->say(cmd->client, conn,
404 "Usage: /INVITE <nickname>[@<server>] <channel>");
409 /* Parse the typed nickname. */
410 if (!silc_parse_nickname(cmd->argv[1], &nickname, &server, &num)) {
411 cmd->client->ops->say(cmd->client, conn, "Bad nickname");
416 /* Find client entry */
417 client_entry = silc_idlist_get_client(client, conn, nickname, server, num);
419 /* Client entry not found, it was requested thus mark this to be
421 silc_client_command_pending(SILC_COMMAND_IDENTIFY,
422 silc_client_command_invite, context);
426 client_id = silc_id_id2str(client_entry->id, SILC_ID_CLIENT);
428 /* Find channel entry */
429 channel_entry = silc_idlist_get_channel(client, conn, cmd->argv[2]);
430 if (!channel_entry) {
431 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
432 silc_free(client_id);
437 channel_id = silc_id_id2str(channel_entry->id, SILC_ID_CHANNEL);
439 buffer = silc_command_payload_encode_va(SILC_COMMAND_INVITE, 0, 2,
440 1, client_id, SILC_ID_CLIENT_LEN,
441 2, channel_id, SILC_ID_CHANNEL_LEN);
442 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
443 0, NULL, NULL, buffer->data, buffer->len, TRUE);
444 silc_buffer_free(buffer);
446 cmd->client->ops->say(cmd->client, conn,
447 "Inviting %s to channel %s", cmd->argv[1],
450 /* Notify application */
454 silc_client_command_free(cmd);
457 /* Command QUIT. Closes connection with current server. */
459 SILC_CLIENT_CMD_FUNC(quit)
461 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
465 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
470 buffer = silc_command_payload_encode(SILC_COMMAND_QUIT, cmd->argc - 1,
471 ++cmd->argv, ++cmd->argv_lens,
472 ++cmd->argv_types, 0);
473 silc_client_packet_send(cmd->client, cmd->conn->sock, SILC_PACKET_COMMAND,
475 buffer->data, buffer->len, TRUE);
476 silc_buffer_free(buffer);
481 /* Close connection */
482 silc_client_close_connection(cmd->client, cmd->conn->sock);
483 cmd->client->ops->disconnect(cmd->client, cmd->conn);
485 /* Notify application */
489 silc_client_command_free(cmd);
492 SILC_CLIENT_CMD_FUNC(kill)
496 /* Command INFO. Request information about specific server. If specific
497 server is not provided the current server is used. */
499 SILC_CLIENT_CMD_FUNC(info)
501 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
502 SilcClientConnection conn = cmd->conn;
507 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
513 name = strdup(conn->remote_host);
515 name = strdup(cmd->argv[1]);
517 /* Send the command */
518 buffer = silc_command_payload_encode_va(SILC_COMMAND_INFO, 0, 1,
519 1, name, strlen(name));
520 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
521 0, NULL, NULL, buffer->data, buffer->len, TRUE);
522 silc_buffer_free(buffer);
524 /* Notify application */
528 silc_client_command_free(cmd);
531 SILC_CLIENT_CMD_FUNC(connect)
535 /* Command PING. Sends ping to server. This is used to test the
536 communication channel. */
538 SILC_CLIENT_CMD_FUNC(ping)
540 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
541 SilcClientConnection conn = cmd->conn;
548 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
553 if (cmd->argc == 1 || !strcmp(cmd->argv[1], conn->remote_host))
554 name = strdup(conn->remote_host);
556 id = silc_id_str2id(conn->remote_id_data, SILC_ID_SERVER);
558 /* Send the command */
559 buffer = silc_command_payload_encode_va(SILC_COMMAND_PING, 0, 1,
560 1, conn->remote_id_data,
562 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
563 0, NULL, NULL, buffer->data, buffer->len, TRUE);
564 silc_buffer_free(buffer);
566 /* Start counting time */
567 for (i = 0; i < conn->ping_count; i++) {
568 if (conn->ping[i].dest_id == NULL) {
569 conn->ping[i].start_time = time(NULL);
570 conn->ping[i].dest_id = id;
571 conn->ping[i].dest_name = name;
576 if (i >= conn->ping_count) {
577 i = conn->ping_count;
578 conn->ping = silc_realloc(conn->ping, sizeof(*conn->ping) * (i + 1));
579 conn->ping[i].start_time = time(NULL);
580 conn->ping[i].dest_id = id;
581 conn->ping[i].dest_name = name;
585 /* Notify application */
589 silc_client_command_free(cmd);
592 SILC_CLIENT_CMD_FUNC(oper)
596 SILC_CLIENT_CMD_FUNC(trace)
600 SILC_CLIENT_CMD_FUNC(notice)
604 /* Command JOIN. Joins to a channel. */
606 SILC_CLIENT_CMD_FUNC(join)
608 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
609 SilcClientConnection conn = cmd->conn;
610 SilcIDCacheEntry id_cache = NULL;
614 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
620 /* Show channels currently joined to */
625 /* See if we have joined to the requested channel already */
626 if (silc_idcache_find_by_data_one(conn->channel_cache, cmd->argv[1],
628 cmd->client->ops->say(cmd->client, conn,
629 "You are talking to channel %s", cmd->argv[1]);
630 conn->current_channel = (SilcChannelEntry)id_cache->context;
632 cmd->client->screen->bottom_line->channel = cmd->argv[1];
633 silc_screen_print_bottom_line(cmd->client->screen, 0);
638 /* Send JOIN command to the server */
639 buffer = silc_command_payload_encode(SILC_COMMAND_JOIN,
640 cmd->argc - 1, ++cmd->argv,
641 ++cmd->argv_lens, ++cmd->argv_types, 0);
642 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
643 0, NULL, NULL, buffer->data, buffer->len, TRUE);
644 silc_buffer_free(buffer);
649 /* Notify application */
653 silc_client_command_free(cmd);
656 SILC_CLIENT_CMD_FUNC(motd)
658 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
659 SilcClientConnection conn = cmd->conn;
663 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
668 if (cmd->argc < 1 || cmd->argc > 1) {
669 cmd->client->ops->say(cmd->client, conn,
675 /* Send TOPIC command to the server */
676 buffer = silc_command_payload_encode_va(SILC_COMMAND_MOTD, 0, 1,
677 2, conn->remote_host,
678 strlen(conn->remote_host));
679 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
680 0, NULL, NULL, buffer->data, buffer->len, TRUE);
681 silc_buffer_free(buffer);
683 /* Notify application */
687 silc_client_command_free(cmd);
690 SILC_CLIENT_CMD_FUNC(umode)
694 SILC_CLIENT_CMD_FUNC(cmode)
698 SILC_CLIENT_CMD_FUNC(kick)
702 SILC_CLIENT_CMD_FUNC(restart)
706 SILC_CLIENT_CMD_FUNC(close)
710 SILC_CLIENT_CMD_FUNC(die)
714 SILC_CLIENT_CMD_FUNC(silcoper)
718 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
720 SILC_CLIENT_CMD_FUNC(leave)
722 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
723 SilcClientConnection conn = cmd->conn;
724 SilcIDCacheEntry id_cache = NULL;
725 SilcChannelEntry channel;
727 unsigned char *id_string;
731 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
736 if (cmd->argc != 2) {
737 cmd->client->ops->say(cmd->client, conn, "Usage: /LEAVE <channel>");
742 if (cmd->argv[1][0] == '*') {
743 if (!conn->current_channel) {
744 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
748 name = conn->current_channel->channel_name;
753 if (!conn->current_channel) {
754 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
759 /* Get the Channel ID of the channel */
760 if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
761 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
766 channel = (SilcChannelEntry)id_cache->context;
768 /* Send LEAVE command to the server */
769 id_string = silc_id_id2str(id_cache->id, SILC_ID_CHANNEL);
770 buffer = silc_command_payload_encode_va(SILC_COMMAND_LEAVE, 0, 1,
771 1, id_string, SILC_ID_CHANNEL_LEN);
772 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
773 0, NULL, NULL, buffer->data, buffer->len, TRUE);
774 silc_buffer_free(buffer);
776 /* We won't talk anymore on this channel */
777 cmd->client->ops->say(cmd->client, conn, "You have left channel %s", name);
779 conn->current_channel = NULL;
781 silc_idcache_del_by_id(conn->channel_cache, SILC_ID_CHANNEL, channel->id);
782 silc_free(channel->channel_name);
783 silc_free(channel->id);
784 silc_free(channel->key);
785 silc_cipher_free(channel->channel_key);
787 silc_free(id_string);
789 /* Notify application */
793 silc_client_command_free(cmd);
796 /* Command NAMES. Requests the names of the clients joined on requested
799 SILC_CLIENT_CMD_FUNC(names)
801 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
802 SilcClientConnection conn = cmd->conn;
803 SilcIDCacheEntry id_cache = NULL;
806 unsigned char *id_string;
809 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
814 if (cmd->argc != 2) {
815 cmd->client->ops->say(cmd->client, conn, "Usage: /NAMES <channel>");
820 if (cmd->argv[1][0] == '*')
821 name = conn->current_channel->channel_name;
825 /* Get the Channel ID of the channel */
826 if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
827 /* XXX should resolve the channel ID; LIST command */
828 cmd->client->ops->say(cmd->client, conn,
829 "You are not on that channel", name);
834 /* Send NAMES command to the server */
835 id_string = silc_id_id2str(id_cache->id, SILC_ID_CHANNEL);
836 buffer = silc_command_payload_encode_va(SILC_COMMAND_NAMES, 0, 1,
837 1, id_string, SILC_ID_CHANNEL_LEN);
838 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
839 0, NULL, NULL, buffer->data, buffer->len, TRUE);
840 silc_buffer_free(buffer);
841 silc_free(id_string);
843 /* Register dummy pending command that will tell the reply command
844 that user called this command. Server may send reply to this command
845 even if user did not send this command thus we want to handle things
846 differently when user sent the command. This is dummy and won't be
848 /* XXX this is kludge and should be removed after pending command reply
849 support is added. Currently only commands may be pending not command
851 silc_client_command_pending(SILC_COMMAND_NAMES,
852 silc_client_command_names, NULL);
854 /* Notify application */
858 silc_client_command_free(cmd);