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.
23 * Revision 1.7 2000/07/07 06:54:44 priikone
24 * Fixed channel joining bug, do not allow joining twice on the
27 * Revision 1.6 2000/07/06 07:14:36 priikone
28 * Fixes to NAMES command handling.
29 * Fixes when leaving from channel.
31 * Revision 1.5 2000/07/05 06:12:05 priikone
32 * Global cosmetic changes.
34 * Revision 1.4 2000/07/04 08:28:03 priikone
35 * Added INVITE, PING and NAMES command.
37 * Revision 1.3 2000/07/03 05:49:49 priikone
38 * Implemented LEAVE command. Minor bug fixes.
40 * Revision 1.2 2000/06/27 19:38:40 priikone
41 * Added missing goto flag.
43 * Revision 1.1.1.1 2000/06/27 11:36:56 priikone
44 * Imported from internal CVS/Added Log headers.
49 #include "clientincludes.h"
51 /* Client command list. */
52 SilcClientCommand silc_command_list[] =
54 SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", SILC_CF_LAG | SILC_CF_REG, 3),
55 SILC_CLIENT_CMD(whowas, WHOWAS, "WHOWAS", SILC_CF_LAG | SILC_CF_REG, 3),
56 SILC_CLIENT_CMD(identify, IDENTIFY, "IDENTIFY",
57 SILC_CF_LAG | SILC_CF_REG, 3),
58 SILC_CLIENT_CMD(nick, NICK, "NICK", SILC_CF_LAG | SILC_CF_REG, 2),
59 SILC_CLIENT_CMD(list, LIST, "LIST", SILC_CF_LAG | SILC_CF_REG, 2),
60 SILC_CLIENT_CMD(topic, TOPIC, "TOPIC", SILC_CF_LAG | SILC_CF_REG, 2),
61 SILC_CLIENT_CMD(invite, INVITE, "INVITE", SILC_CF_LAG | SILC_CF_REG, 3),
62 SILC_CLIENT_CMD(quit, QUIT, "QUIT", SILC_CF_LAG | SILC_CF_REG, 1),
63 SILC_CLIENT_CMD(kill, KILL, "KILL",
64 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
65 SILC_CLIENT_CMD(info, INFO, "INFO", SILC_CF_LAG | SILC_CF_REG, 2),
66 SILC_CLIENT_CMD(connect, CONNECT, "CONNECT",
67 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
68 SILC_CLIENT_CMD(ping, PING, "PING", SILC_CF_LAG | SILC_CF_REG, 2),
69 SILC_CLIENT_CMD(oper, OPER, "OPER",
70 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
71 SILC_CLIENT_CMD(join, JOIN, "JOIN", SILC_CF_LAG | SILC_CF_REG, 2),
72 SILC_CLIENT_CMD(motd, MOTD, "MOTD", SILC_CF_LAG | SILC_CF_REG, 2),
73 SILC_CLIENT_CMD(umode, UMODE, "UMODE", SILC_CF_LAG | SILC_CF_REG, 2),
74 SILC_CLIENT_CMD(cmode, CMODE, "CMODE", SILC_CF_LAG | SILC_CF_REG, 2),
75 SILC_CLIENT_CMD(kick, KICK, "KICK", SILC_CF_LAG | SILC_CF_REG, 2),
76 SILC_CLIENT_CMD(restart, RESTART, "RESTART",
77 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
78 SILC_CLIENT_CMD(close, CLOSE, "CLOSE",
79 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
80 SILC_CLIENT_CMD(die, DIE, "DIE",
81 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
82 SILC_CLIENT_CMD(silcoper, SILCOPER, "SILOPER",
83 SILC_CF_LAG | SILC_CF_REG | SILC_CF_SILC_OPER, 2),
84 SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", SILC_CF_LAG | SILC_CF_REG, 2),
85 SILC_CLIENT_CMD(names, NAMES, "NAMES", SILC_CF_LAG | SILC_CF_REG, 2),
88 * Local. client specific commands
90 SILC_CLIENT_CMD(help, HELP, "HELP", SILC_CF_NONE, 2),
91 SILC_CLIENT_CMD(clear, CLEAR, "CLEAR", SILC_CF_NONE, 1),
92 SILC_CLIENT_CMD(version, VERSION, "VERSION", SILC_CF_NONE, 1),
93 SILC_CLIENT_CMD(server, SERVER, "SERVER", SILC_CF_NONE, 2),
94 SILC_CLIENT_CMD(msg, MSG, "MSG", SILC_CF_NONE, 3),
95 SILC_CLIENT_CMD(away, AWAY, "AWAY", SILC_CF_NONE, 2),
100 #define SILC_NOT_CONNECTED(x) \
101 silc_say((x), "You are not connected to a server, use /SERVER to connect");
103 /* List of pending commands. */
104 SilcClientCommandPending *silc_command_pending = NULL;
106 /* Add new pending command to the list of pending commands. Currently
107 pending commands are executed from command replies, thus we can
108 execute any command after receiving some specific command reply.
110 The argument `reply_cmd' is the command reply from where the callback
111 function is to be called, thus, it IS NOT the command to be executed.
113 XXX: If needed in the future this support may be extended for
114 commands as well, when any command could be executed after executing
115 some specific command. */
117 void silc_client_command_pending(SilcCommand reply_cmd,
118 SilcClientCommandCallback callback,
121 SilcClientCommandPending *reply, *r;
123 reply = silc_calloc(1, sizeof(*reply));
124 reply->reply_cmd = reply_cmd;
125 reply->context = context;
126 reply->callback = callback;
128 if (silc_command_pending == NULL) {
129 silc_command_pending = reply;
133 for (r = silc_command_pending; r; r = r->next) {
134 if (r->next == NULL) {
141 /* Deletes pending command by reply command type. */
143 void silc_client_command_pending_del(SilcCommand reply_cmd)
145 SilcClientCommandPending *r, *tmp;
147 if (silc_command_pending) {
148 if (silc_command_pending->reply_cmd == reply_cmd) {
149 silc_free(silc_command_pending);
150 silc_command_pending = NULL;
154 for (r = silc_command_pending; r; r = r->next) {
155 if (r->next && r->next->reply_cmd == reply_cmd) {
157 r->next = r->next->next;
165 /* Free command context and its internals */
167 static void silc_client_command_free(SilcClientCommandContext cmd)
172 for (i = 0; i < cmd->argc; i++)
173 silc_free(cmd->argv[i]);
178 /* Command WHOIS. This command is used to query information about
181 SILC_CLIENT_CMD_FUNC(whois)
183 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
186 if (cmd->argc < 2 || cmd->argc > 3) {
187 silc_say(cmd->client, "Usage: /WHOIS <nickname>[@<server>] [<count>]");
191 if (!cmd->client->current_win->sock) {
192 SILC_NOT_CONNECTED(cmd->client);
196 buffer = silc_command_encode_payload(SILC_COMMAND_WHOIS,
197 cmd->argc - 1, ++cmd->argv,
198 ++cmd->argv_lens, ++cmd->argv_types);
199 silc_client_packet_send(cmd->client, cmd->client->current_win->sock,
200 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
201 buffer->data, buffer->len, TRUE);
202 silc_buffer_free(buffer);
208 silc_client_command_free(cmd);
211 SILC_CLIENT_CMD_FUNC(whowas)
215 /* Command IDENTIFY. This command is used to query information about
216 specific user, especially ID's. */
218 SILC_CLIENT_CMD_FUNC(identify)
220 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
223 if (cmd->argc < 2 || cmd->argc > 3) {
224 silc_say(cmd->client, "Usage: /IDENTIFY <nickname>[@<server>] [<count>]");
228 if (!cmd->client->current_win->sock) {
229 SILC_NOT_CONNECTED(cmd->client);
233 buffer = silc_command_encode_payload(SILC_COMMAND_IDENTIFY,
234 cmd->argc - 1, ++cmd->argv,
235 ++cmd->argv_lens, ++cmd->argv_types);
236 silc_client_packet_send(cmd->client, cmd->client->current_win->sock,
237 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
238 buffer->data, buffer->len, TRUE);
239 silc_buffer_free(buffer);
245 silc_client_command_free(cmd);
248 /* Command NICK. Shows current nickname/sets new nickname on current
251 SILC_CLIENT_CMD_FUNC(nick)
253 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
254 SilcClientWindow win = NULL;
258 SILC_NOT_CONNECTED(cmd->client);
262 /* Show current nickname */
265 silc_say(cmd->client, "Your nickname is %s on server %s",
266 win->nickname, win->remote_host);
268 silc_say(cmd->client, "Your nickname is %s", win->nickname);
273 win = (SilcClientWindow)cmd->sock->user_data;
275 /* Set new nickname */
276 buffer = silc_command_encode_payload(SILC_COMMAND_NICK,
277 cmd->argc - 1, ++cmd->argv,
278 ++cmd->argv_lens, ++cmd->argv_types);
279 silc_client_packet_send(cmd->client, cmd->sock,
280 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
281 buffer->data, buffer->len, TRUE);
282 silc_buffer_free(buffer);
287 silc_free(win->nickname);
288 win->nickname = strdup(cmd->argv[1]);
291 silc_client_command_free(cmd);
294 /* Command SERVER. Connects to remote SILC server. This is local command. */
296 SILC_CLIENT_CMD_FUNC(server)
298 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
303 /* Show current servers */
304 if (!cmd->client->current_win->sock) {
305 silc_say(cmd->client, "You are not connected to any server");
306 silc_say(cmd->client, "Usage: /SERVER [<server>[:<port>]]");
313 /* See if port is included and then extract it */
314 if (strchr(cmd->argv[1], ':')) {
315 len = strcspn(cmd->argv[1], ":");
316 hostname = silc_calloc(len + 1, sizeof(char));
317 memcpy(hostname, cmd->argv[1], len);
318 port = atoi(cmd->argv[1] + 1 + len);
320 hostname = cmd->argv[1];
324 /* Connect asynchronously to not to block user interface */
325 silc_client_connect_to_server(cmd->client, port, hostname);
328 silc_client_command_free(cmd);
331 SILC_CLIENT_CMD_FUNC(list)
335 SILC_CLIENT_CMD_FUNC(topic)
339 /* Command INVITE. Invites specific client to join a channel. */
341 SILC_CLIENT_CMD_FUNC(invite)
343 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
344 SilcClientWindow win = NULL;
346 SilcIDCache *id_cache;
347 unsigned char *client_id, *channel_id;
349 #define CIDC(x) win->client_id_cache[(x) - 32], \
350 win->client_id_cache_count[(x) - 32]
351 #define CHIDC(x) win->channel_id_cache[(x) - 32], \
352 win->channel_id_cache_count[(x) - 32]
354 if (cmd->argc != 3) {
355 silc_say(cmd->client, "Usage: /INVITE <nickname>[@<server>] <channel>");
359 if (!cmd->client->current_win->sock) {
360 SILC_NOT_CONNECTED(cmd->client);
364 win = (SilcClientWindow)cmd->sock->user_data;
366 /* Get client ID of the client to be invited. If we don't have it
367 we will request it and cache it. This same command will be called
368 again after we have received the reply (ie. pending). */
369 if (!silc_idcache_find_by_data(CIDC(cmd->argv[1][0]), cmd->argv[1],
371 SilcClientCommandContext ctx;
374 ctx = silc_calloc(1, sizeof(*ctx));
375 ctx->client = cmd->client;
376 ctx->sock = cmd->sock;
377 memset(ident, 0, sizeof(ident));
378 snprintf(ident, sizeof(ident), "/IDENTIFY %s", cmd->argv[1]);
379 silc_client_parse_command_line(ident, &ctx->argv, &ctx->argv_lens,
380 &ctx->argv_types, &ctx->argc, 2);
381 silc_client_command_identify(ctx);
382 silc_client_command_pending(SILC_COMMAND_IDENTIFY,
383 silc_client_command_invite, context);
387 client_id = silc_id_id2str(id_cache->id, SILC_ID_CLIENT);
389 /* Get Channel ID of the channel. */
390 if (!silc_idcache_find_by_data(CHIDC(cmd->argv[2][0]), cmd->argv[2],
392 silc_say(cmd->client, "You are not on that channel");
393 silc_free(client_id);
397 channel_id = silc_id_id2str(id_cache->id, SILC_ID_CHANNEL);
399 buffer = silc_command_encode_payload_va(SILC_COMMAND_INVITE, 2,
400 1, client_id, SILC_ID_CLIENT_LEN,
401 2, channel_id, SILC_ID_CHANNEL_LEN);
402 silc_client_packet_send(cmd->client, cmd->sock,
403 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
404 buffer->data, buffer->len, TRUE);
405 silc_buffer_free(buffer);
407 silc_say(cmd->client, "Inviting %s to channel %s", cmd->argv[1],
411 silc_client_command_free(cmd);
416 /* Command QUIT. Closes connection with current server. */
418 SILC_CLIENT_CMD_FUNC(quit)
420 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
423 if (!cmd->client->current_win->sock) {
424 SILC_NOT_CONNECTED(cmd->client);
428 buffer = silc_command_encode_payload(SILC_COMMAND_QUIT, cmd->argc - 1,
429 ++cmd->argv, ++cmd->argv_lens,
431 silc_client_packet_send(cmd->client, cmd->client->current_win->sock,
432 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
433 buffer->data, buffer->len, TRUE);
434 silc_buffer_free(buffer);
439 /* Close connection */
440 silc_client_close_connection(cmd->client, cmd->sock);
441 cmd->client->screen->bottom_line->connection = NULL;
442 silc_screen_print_bottom_line(cmd->client->screen, 0);
445 silc_client_command_free(cmd);
448 SILC_CLIENT_CMD_FUNC(kill)
452 SILC_CLIENT_CMD_FUNC(info)
456 SILC_CLIENT_CMD_FUNC(connect)
460 /* Command PING. Sends ping to server. This is used to test the
461 communication channel. */
463 SILC_CLIENT_CMD_FUNC(ping)
465 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
466 SilcClientWindow win = NULL;
473 SILC_NOT_CONNECTED(cmd->client);
477 win = (SilcClientWindow)cmd->sock->user_data;
479 if (cmd->argc == 1 || !strcmp(cmd->argv[1], win->remote_host))
480 name = strdup(win->remote_host);
482 id = silc_id_str2id(win->remote_id_data, SILC_ID_SERVER);
484 /* Send the command */
485 buffer = silc_command_encode_payload_va(SILC_COMMAND_PING, 1,
486 1, win->remote_id_data,
488 silc_client_packet_send(cmd->client, cmd->client->current_win->sock,
489 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
490 buffer->data, buffer->len, TRUE);
491 silc_buffer_free(buffer);
493 /* Start counting time */
494 for (i = 0; i < win->ping_count; i++) {
495 if (win->ping[i].dest_id == NULL) {
496 win->ping[i].start_time = time(NULL);
497 win->ping[i].dest_id = id;
498 win->ping[i].dest_name = name;
503 if (i >= win->ping_count) {
505 win->ping = silc_realloc(win->ping, sizeof(*win->ping) * (i + 1));
506 win->ping[i].start_time = time(NULL);
507 win->ping[i].dest_id = id;
508 win->ping[i].dest_name = name;
513 silc_client_command_free(cmd);
516 SILC_CLIENT_CMD_FUNC(oper)
520 SILC_CLIENT_CMD_FUNC(trace)
524 SILC_CLIENT_CMD_FUNC(notice)
528 /* Command JOIN. Joins to a channel. */
530 SILC_CLIENT_CMD_FUNC(join)
532 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
533 SilcClientWindow win = NULL;
534 SilcIDCache *id_cache = NULL;
537 #define CIDC(x) win->channel_id_cache[(x) - 32]
538 #define CIDCC(x) win->channel_id_cache_count[(x) - 32]
541 /* Show channels currently joined to */
542 if (!cmd->client->current_win->sock) {
543 silc_say(cmd->client, "No current channel for this window");
544 SILC_NOT_CONNECTED(cmd->client);
552 if (!cmd->client->current_win->sock) {
553 SILC_NOT_CONNECTED(cmd->client);
557 win = (SilcClientWindow)cmd->sock->user_data;
559 /* See if we have joined to the requested channel already */
560 silc_idcache_find_by_data(CIDC(cmd->argv[1][0]), CIDCC(cmd->argv[1][0]),
561 cmd->argv[1], &id_cache);
564 silc_say(cmd->client, "You are talking to channel %s", cmd->argv[1]);
565 win->current_channel = (SilcChannelEntry)id_cache->context;
566 cmd->client->screen->bottom_line->channel = cmd->argv[1];
567 silc_screen_print_bottom_line(cmd->client->screen, 0);
571 /* Send JOIN command to the server */
572 buffer = silc_command_encode_payload(SILC_COMMAND_JOIN,
573 cmd->argc - 1, ++cmd->argv,
574 ++cmd->argv_lens, ++cmd->argv_types);
575 silc_client_packet_send(cmd->client, cmd->client->current_win->sock,
576 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
577 buffer->data, buffer->len, TRUE);
578 silc_buffer_free(buffer);
584 silc_client_command_free(cmd);
589 SILC_CLIENT_CMD_FUNC(motd)
593 SILC_CLIENT_CMD_FUNC(umode)
597 SILC_CLIENT_CMD_FUNC(cmode)
601 SILC_CLIENT_CMD_FUNC(kick)
605 SILC_CLIENT_CMD_FUNC(restart)
609 SILC_CLIENT_CMD_FUNC(close)
613 SILC_CLIENT_CMD_FUNC(die)
617 SILC_CLIENT_CMD_FUNC(silcoper)
621 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
623 SILC_CLIENT_CMD_FUNC(leave)
625 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
626 SilcClientWindow win = NULL;
627 SilcIDCache *id_cache = NULL;
628 SilcChannelEntry channel;
630 unsigned char *id_string;
633 #define CIDC(x) win->channel_id_cache[(x) - 32]
634 #define CIDCC(x) win->channel_id_cache_count[(x) - 32]
636 if (cmd->argc != 2) {
637 silc_say(cmd->client, "Usage: /LEAVE <channel>");
641 if (!cmd->client->current_win->sock) {
642 SILC_NOT_CONNECTED(cmd->client);
646 win = (SilcClientWindow)cmd->sock->user_data;
648 if (cmd->argv[1][0] == '*') {
649 if (!win->current_channel) {
650 silc_say(cmd->client, "You are not on any chanenl");
653 name = win->current_channel->channel_name;
658 if (!win->current_channel) {
659 silc_say(cmd->client, "You are not on that channel");
663 /* Get the Channel ID of the channel */
664 silc_idcache_find_by_data(CIDC(name[0]), CIDCC(name[0]), name, &id_cache);
666 silc_say(cmd->client, "You are not on that channel");
670 channel = (SilcChannelEntry)id_cache->context;
672 /* Send LEAVE command to the server */
673 id_string = silc_id_id2str(id_cache->id, SILC_ID_CHANNEL);
674 buffer = silc_command_encode_payload_va(SILC_COMMAND_LEAVE, 1,
675 1, id_string, SILC_ID_CHANNEL_LEN);
676 silc_client_packet_send(cmd->client, cmd->client->current_win->sock,
677 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
678 buffer->data, buffer->len, TRUE);
679 silc_buffer_free(buffer);
681 /* We won't talk anymore on this channel */
682 silc_say(cmd->client, "You have left channel %s", name);
684 if (!strncmp(win->current_channel->channel_name, name, strlen(name))) {
685 cmd->client->screen->bottom_line->channel = NULL;
686 silc_screen_print_bottom_line(cmd->client->screen, 0);
687 win->current_channel = NULL;
690 silc_idcache_del_by_id(CIDC(name[0]), CIDCC(name[0]),
691 SILC_ID_CHANNEL, channel->id);
692 silc_free(channel->channel_name);
693 silc_free(channel->id);
694 silc_free(channel->key);
695 silc_cipher_free(channel->channel_key);
697 silc_free(id_string);
700 silc_client_command_free(cmd);
705 /* Command NAMES. Requests the names of the clients joined on requested
708 SILC_CLIENT_CMD_FUNC(names)
710 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
711 SilcClientWindow win = NULL;
712 SilcIDCache *id_cache = NULL;
715 unsigned char *id_string;
717 #define CIDC(x) win->channel_id_cache[(x) - 32]
718 #define CIDCC(x) win->channel_id_cache_count[(x) - 32]
720 if (cmd->argc != 2) {
721 silc_say(cmd->client, "Usage: /NAMES <channel>");
725 if (!cmd->client->current_win->sock) {
726 SILC_NOT_CONNECTED(cmd->client);
730 win = (SilcClientWindow)cmd->sock->user_data;
732 if (cmd->argv[1][0] == '*')
733 name = win->current_channel->channel_name;
737 /* Get the Channel ID of the channel */
738 silc_idcache_find_by_data(CIDC(name[0]), CIDCC(name[0]), name, &id_cache);
740 /* XXX should resolve the channel ID; LIST command */
741 silc_say(cmd->client, "You are not on that channel", name);
745 /* Send NAMES command to the server */
746 id_string = silc_id_id2str(id_cache->id, SILC_ID_CHANNEL);
747 buffer = silc_command_encode_payload_va(SILC_COMMAND_NAMES, 1,
748 1, id_string, SILC_ID_CHANNEL_LEN);
749 silc_client_packet_send(cmd->client, cmd->client->current_win->sock,
750 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
751 buffer->data, buffer->len, TRUE);
752 silc_buffer_free(buffer);
753 silc_free(id_string);
755 /* Register dummy pending command that will tell the reply command
756 that user called this command. Server may send reply to this command
757 even if user did not send this command thus we want to handle things
758 differently when user sent the command. This is dummy and won't be
760 /* XXX this is kludge and should be removed after pending command reply
761 support is added. Currently only commands may be pending not command
763 silc_client_command_pending(SILC_COMMAND_NAMES, silc_client_command_names,
767 silc_client_command_free(cmd);
776 /* HELP command. This is local command and shows help on SILC */
778 SILC_CLIENT_CMD_FUNC(help)
783 /* CLEAR command. This is local command and clears current output window */
785 SILC_CLIENT_CMD_FUNC(clear)
787 SilcClient client = (SilcClient)context;
789 assert(client->current_win != NULL);
790 wclear((WINDOW *)client->current_win->screen);
791 wrefresh((WINDOW *)client->current_win->screen);
794 /* VERSION command. This is local command and shows version of the client */
796 SILC_CLIENT_CMD_FUNC(version)
801 /* Command MSG. Sends private message to user or list of users. */
802 /* XXX supports only one destination */
804 SILC_CLIENT_CMD_FUNC(msg)
806 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
807 SilcClientWindow win = NULL;
808 SilcClient client = cmd->client;
809 SilcIDCache *id_cache;
811 #define CIDC(x) win->client_id_cache[(x) - 32], \
812 win->client_id_cache_count[(x) - 32]
815 silc_say(cmd->client, "Usage: /MSG <nickname> <message>");
819 if (!cmd->client->current_win->sock) {
820 SILC_NOT_CONNECTED(cmd->client);
824 win = (SilcClientWindow)cmd->sock->user_data;
826 /* Find ID from cache */
827 if (silc_idcache_find_by_data(CIDC(cmd->argv[1][0]), cmd->argv[1],
828 &id_cache) == FALSE) {
829 SilcClientCommandContext ctx;
832 SILC_LOG_DEBUG(("Requesting Client ID from server"));
834 /* No ID found. Do query from the server. The query is done by
835 sending simple IDENTIFY command to the server. */
836 ctx = silc_calloc(1, sizeof(*ctx));
837 ctx->client = client;
838 ctx->sock = cmd->sock;
839 memset(ident, 0, sizeof(ident));
840 snprintf(ident, sizeof(ident), "/IDENTIFY %s", cmd->argv[1]);
841 silc_client_parse_command_line(ident, &ctx->argv, &ctx->argv_lens,
842 &ctx->argv_types, &ctx->argc, 2);
843 silc_client_command_identify(ctx);
845 /* Mark this command to be pending command and to be executed after
846 we have received the IDENTIFY reply from server. */
847 silc_client_command_pending(SILC_COMMAND_IDENTIFY,
848 silc_client_command_msg, context);
852 /* Display the message for our eyes. */
853 silc_print(client, "-> *%s* %s", cmd->argv[1], cmd->argv[2]);
855 /* Send the private message */
856 silc_client_packet_send_private_message(client, cmd->sock, id_cache->context,
857 cmd->argv[2], cmd->argv_lens[2],
861 silc_client_command_free(cmd);
865 SILC_CLIENT_CMD_FUNC(away)