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.6 2000/07/06 07:14:36 priikone
24 * Fixes to NAMES command handling.
25 * Fixes when leaving from channel.
27 * Revision 1.5 2000/07/05 06:12:05 priikone
28 * Global cosmetic changes.
30 * Revision 1.4 2000/07/04 08:28:03 priikone
31 * Added INVITE, PING and NAMES command.
33 * Revision 1.3 2000/07/03 05:49:49 priikone
34 * Implemented LEAVE command. Minor bug fixes.
36 * Revision 1.2 2000/06/27 19:38:40 priikone
37 * Added missing goto flag.
39 * Revision 1.1.1.1 2000/06/27 11:36:56 priikone
40 * Imported from internal CVS/Added Log headers.
45 #include "clientincludes.h"
47 /* Client command list. */
48 SilcClientCommand silc_command_list[] =
50 SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", SILC_CF_LAG | SILC_CF_REG, 3),
51 SILC_CLIENT_CMD(whowas, WHOWAS, "WHOWAS", SILC_CF_LAG | SILC_CF_REG, 3),
52 SILC_CLIENT_CMD(identify, IDENTIFY, "IDENTIFY",
53 SILC_CF_LAG | SILC_CF_REG, 3),
54 SILC_CLIENT_CMD(nick, NICK, "NICK", SILC_CF_LAG | SILC_CF_REG, 2),
55 SILC_CLIENT_CMD(list, LIST, "LIST", SILC_CF_LAG | SILC_CF_REG, 2),
56 SILC_CLIENT_CMD(topic, TOPIC, "TOPIC", SILC_CF_LAG | SILC_CF_REG, 2),
57 SILC_CLIENT_CMD(invite, INVITE, "INVITE", SILC_CF_LAG | SILC_CF_REG, 3),
58 SILC_CLIENT_CMD(quit, QUIT, "QUIT", SILC_CF_LAG | SILC_CF_REG, 1),
59 SILC_CLIENT_CMD(kill, KILL, "KILL",
60 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
61 SILC_CLIENT_CMD(info, INFO, "INFO", SILC_CF_LAG | SILC_CF_REG, 2),
62 SILC_CLIENT_CMD(connect, CONNECT, "CONNECT",
63 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
64 SILC_CLIENT_CMD(ping, PING, "PING", SILC_CF_LAG | SILC_CF_REG, 2),
65 SILC_CLIENT_CMD(oper, OPER, "OPER",
66 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
67 SILC_CLIENT_CMD(join, JOIN, "JOIN", SILC_CF_LAG | SILC_CF_REG, 2),
68 SILC_CLIENT_CMD(motd, MOTD, "MOTD", SILC_CF_LAG | SILC_CF_REG, 2),
69 SILC_CLIENT_CMD(umode, UMODE, "UMODE", SILC_CF_LAG | SILC_CF_REG, 2),
70 SILC_CLIENT_CMD(cmode, CMODE, "CMODE", SILC_CF_LAG | SILC_CF_REG, 2),
71 SILC_CLIENT_CMD(kick, KICK, "KICK", SILC_CF_LAG | SILC_CF_REG, 2),
72 SILC_CLIENT_CMD(restart, RESTART, "RESTART",
73 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
74 SILC_CLIENT_CMD(close, CLOSE, "CLOSE",
75 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
76 SILC_CLIENT_CMD(die, DIE, "DIE",
77 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
78 SILC_CLIENT_CMD(silcoper, SILCOPER, "SILOPER",
79 SILC_CF_LAG | SILC_CF_REG | SILC_CF_SILC_OPER, 2),
80 SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", SILC_CF_LAG | SILC_CF_REG, 2),
81 SILC_CLIENT_CMD(names, NAMES, "NAMES", SILC_CF_LAG | SILC_CF_REG, 2),
84 * Local. client specific commands
86 SILC_CLIENT_CMD(help, HELP, "HELP", SILC_CF_NONE, 2),
87 SILC_CLIENT_CMD(clear, CLEAR, "CLEAR", SILC_CF_NONE, 1),
88 SILC_CLIENT_CMD(version, VERSION, "VERSION", SILC_CF_NONE, 1),
89 SILC_CLIENT_CMD(server, SERVER, "SERVER", SILC_CF_NONE, 2),
90 SILC_CLIENT_CMD(msg, MSG, "MSG", SILC_CF_NONE, 3),
91 SILC_CLIENT_CMD(away, AWAY, "AWAY", SILC_CF_NONE, 2),
96 #define SILC_NOT_CONNECTED(x) \
97 silc_say((x), "You are not connected to a server, use /SERVER to connect");
99 /* List of pending commands. */
100 SilcClientCommandPending *silc_command_pending = NULL;
102 /* Add new pending command to the list of pending commands. Currently
103 pending commands are executed from command replies, thus we can
104 execute any command after receiving some specific command reply.
106 The argument `reply_cmd' is the command reply from where the callback
107 function is to be called, thus, it IS NOT the command to be executed.
109 XXX: If needed in the future this support may be extended for
110 commands as well, when any command could be executed after executing
111 some specific command. */
113 void silc_client_command_pending(SilcCommand reply_cmd,
114 SilcClientCommandCallback callback,
117 SilcClientCommandPending *reply, *r;
119 reply = silc_calloc(1, sizeof(*reply));
120 reply->reply_cmd = reply_cmd;
121 reply->context = context;
122 reply->callback = callback;
124 if (silc_command_pending == NULL) {
125 silc_command_pending = reply;
129 for (r = silc_command_pending; r; r = r->next) {
130 if (r->next == NULL) {
137 /* Deletes pending command by reply command type. */
139 void silc_client_command_pending_del(SilcCommand reply_cmd)
141 SilcClientCommandPending *r, *tmp;
143 if (silc_command_pending) {
144 if (silc_command_pending->reply_cmd == reply_cmd) {
145 silc_free(silc_command_pending);
146 silc_command_pending = NULL;
150 for (r = silc_command_pending; r; r = r->next) {
151 if (r->next && r->next->reply_cmd == reply_cmd) {
153 r->next = r->next->next;
161 /* Free command context and its internals */
163 static void silc_client_command_free(SilcClientCommandContext cmd)
168 for (i = 0; i < cmd->argc; i++)
169 silc_free(cmd->argv[i]);
174 /* Command WHOIS. This command is used to query information about
177 SILC_CLIENT_CMD_FUNC(whois)
179 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
182 if (cmd->argc < 2 || cmd->argc > 3) {
183 silc_say(cmd->client, "Usage: /WHOIS <nickname>[@<server>] [<count>]");
187 if (!cmd->client->current_win->sock) {
188 SILC_NOT_CONNECTED(cmd->client);
192 buffer = silc_command_encode_payload(SILC_COMMAND_WHOIS,
193 cmd->argc - 1, ++cmd->argv,
194 ++cmd->argv_lens, ++cmd->argv_types);
195 silc_client_packet_send(cmd->client, cmd->client->current_win->sock,
196 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
197 buffer->data, buffer->len, TRUE);
198 silc_buffer_free(buffer);
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;
219 if (cmd->argc < 2 || cmd->argc > 3) {
220 silc_say(cmd->client, "Usage: /IDENTIFY <nickname>[@<server>] [<count>]");
224 if (!cmd->client->current_win->sock) {
225 SILC_NOT_CONNECTED(cmd->client);
229 buffer = silc_command_encode_payload(SILC_COMMAND_IDENTIFY,
230 cmd->argc - 1, ++cmd->argv,
231 ++cmd->argv_lens, ++cmd->argv_types);
232 silc_client_packet_send(cmd->client, cmd->client->current_win->sock,
233 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
234 buffer->data, buffer->len, TRUE);
235 silc_buffer_free(buffer);
241 silc_client_command_free(cmd);
244 /* Command NICK. Shows current nickname/sets new nickname on current
247 SILC_CLIENT_CMD_FUNC(nick)
249 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
250 SilcClientWindow win = NULL;
254 SILC_NOT_CONNECTED(cmd->client);
258 /* Show current nickname */
261 silc_say(cmd->client, "Your nickname is %s on server %s",
262 win->nickname, win->remote_host);
264 silc_say(cmd->client, "Your nickname is %s", win->nickname);
269 win = (SilcClientWindow)cmd->sock->user_data;
271 /* Set new nickname */
272 buffer = silc_command_encode_payload(SILC_COMMAND_NICK,
273 cmd->argc - 1, ++cmd->argv,
274 ++cmd->argv_lens, ++cmd->argv_types);
275 silc_client_packet_send(cmd->client, cmd->sock,
276 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
277 buffer->data, buffer->len, TRUE);
278 silc_buffer_free(buffer);
283 silc_free(win->nickname);
284 win->nickname = strdup(cmd->argv[1]);
287 silc_client_command_free(cmd);
290 /* Command SERVER. Connects to remote SILC server. This is local command. */
292 SILC_CLIENT_CMD_FUNC(server)
294 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
299 /* Show current servers */
300 if (!cmd->client->current_win->sock) {
301 silc_say(cmd->client, "You are not connected to any server");
302 silc_say(cmd->client, "Usage: /SERVER [<server>[:<port>]]");
309 /* See if port is included and then extract it */
310 if (strchr(cmd->argv[1], ':')) {
311 len = strcspn(cmd->argv[1], ":");
312 hostname = silc_calloc(len + 1, sizeof(char));
313 memcpy(hostname, cmd->argv[1], len);
314 port = atoi(cmd->argv[1] + 1 + len);
316 hostname = cmd->argv[1];
320 /* Connect asynchronously to not to block user interface */
321 silc_client_connect_to_server(cmd->client, port, hostname);
324 silc_client_command_free(cmd);
327 SILC_CLIENT_CMD_FUNC(list)
331 SILC_CLIENT_CMD_FUNC(topic)
335 /* Command INVITE. Invites specific client to join a channel. */
337 SILC_CLIENT_CMD_FUNC(invite)
339 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
340 SilcClientWindow win = NULL;
342 SilcIDCache *id_cache;
343 unsigned char *client_id, *channel_id;
345 #define CIDC(x) win->client_id_cache[(x) - 32], \
346 win->client_id_cache_count[(x) - 32]
347 #define CHIDC(x) win->channel_id_cache[(x) - 32], \
348 win->channel_id_cache_count[(x) - 32]
350 if (cmd->argc != 3) {
351 silc_say(cmd->client, "Usage: /INVITE <nickname>[@<server>] <channel>");
355 if (!cmd->client->current_win->sock) {
356 SILC_NOT_CONNECTED(cmd->client);
360 win = (SilcClientWindow)cmd->sock->user_data;
362 /* Get client ID of the client to be invited. If we don't have it
363 we will request it and cache it. This same command will be called
364 again after we have received the reply (ie. pending). */
365 if (!silc_idcache_find_by_data(CIDC(cmd->argv[1][0]), cmd->argv[1],
367 SilcClientCommandContext ctx;
370 ctx = silc_calloc(1, sizeof(*ctx));
371 ctx->client = cmd->client;
372 ctx->sock = cmd->sock;
373 memset(ident, 0, sizeof(ident));
374 snprintf(ident, sizeof(ident), "/IDENTIFY %s", cmd->argv[1]);
375 silc_client_parse_command_line(ident, &ctx->argv, &ctx->argv_lens,
376 &ctx->argv_types, &ctx->argc, 2);
377 silc_client_command_identify(ctx);
378 silc_client_command_pending(SILC_COMMAND_IDENTIFY,
379 silc_client_command_invite, context);
383 client_id = silc_id_id2str(id_cache->id, SILC_ID_CLIENT);
385 /* Get Channel ID of the channel. */
386 if (!silc_idcache_find_by_data(CHIDC(cmd->argv[2][0]), cmd->argv[2],
388 silc_say(cmd->client, "You are not on that channel");
389 silc_free(client_id);
393 channel_id = silc_id_id2str(id_cache->id, SILC_ID_CHANNEL);
395 buffer = silc_command_encode_payload_va(SILC_COMMAND_INVITE, 2,
396 1, client_id, SILC_ID_CLIENT_LEN,
397 2, channel_id, SILC_ID_CHANNEL_LEN);
398 silc_client_packet_send(cmd->client, cmd->sock,
399 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
400 buffer->data, buffer->len, TRUE);
401 silc_buffer_free(buffer);
403 silc_say(cmd->client, "Inviting %s to channel %s", cmd->argv[1],
407 silc_client_command_free(cmd);
412 /* Command QUIT. Closes connection with current server. */
414 SILC_CLIENT_CMD_FUNC(quit)
416 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
419 if (!cmd->client->current_win->sock) {
420 SILC_NOT_CONNECTED(cmd->client);
424 buffer = silc_command_encode_payload(SILC_COMMAND_QUIT, cmd->argc - 1,
425 ++cmd->argv, ++cmd->argv_lens,
427 silc_client_packet_send(cmd->client, cmd->client->current_win->sock,
428 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
429 buffer->data, buffer->len, TRUE);
430 silc_buffer_free(buffer);
435 /* Close connection */
436 silc_client_close_connection(cmd->client, cmd->sock);
437 cmd->client->screen->bottom_line->connection = NULL;
438 silc_screen_print_bottom_line(cmd->client->screen, 0);
441 silc_client_command_free(cmd);
444 SILC_CLIENT_CMD_FUNC(kill)
448 SILC_CLIENT_CMD_FUNC(info)
452 SILC_CLIENT_CMD_FUNC(connect)
456 /* Command PING. Sends ping to server. This is used to test the
457 communication channel. */
459 SILC_CLIENT_CMD_FUNC(ping)
461 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
462 SilcClientWindow win = NULL;
469 SILC_NOT_CONNECTED(cmd->client);
473 win = (SilcClientWindow)cmd->sock->user_data;
475 if (cmd->argc == 1 || !strcmp(cmd->argv[1], win->remote_host))
476 name = strdup(win->remote_host);
478 id = silc_id_str2id(win->remote_id_data, SILC_ID_SERVER);
480 /* Send the command */
481 buffer = silc_command_encode_payload_va(SILC_COMMAND_PING, 1,
482 1, win->remote_id_data,
484 silc_client_packet_send(cmd->client, cmd->client->current_win->sock,
485 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
486 buffer->data, buffer->len, TRUE);
487 silc_buffer_free(buffer);
489 /* Start counting time */
490 for (i = 0; i < win->ping_count; i++) {
491 if (win->ping[i].dest_id == NULL) {
492 win->ping[i].start_time = time(NULL);
493 win->ping[i].dest_id = id;
494 win->ping[i].dest_name = name;
499 if (i >= win->ping_count) {
501 win->ping = silc_realloc(win->ping, sizeof(*win->ping) * (i + 1));
502 win->ping[i].start_time = time(NULL);
503 win->ping[i].dest_id = id;
504 win->ping[i].dest_name = name;
509 silc_client_command_free(cmd);
512 SILC_CLIENT_CMD_FUNC(oper)
516 SILC_CLIENT_CMD_FUNC(trace)
520 SILC_CLIENT_CMD_FUNC(notice)
524 /* Command JOIN. Joins to a channel. */
526 SILC_CLIENT_CMD_FUNC(join)
528 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
529 SilcClientWindow win = NULL;
530 SilcIDCache *id_cache = NULL;
533 #define CIDC(x) win->channel_id_cache[(x) - 32]
534 #define CIDCC(x) win->channel_id_cache_count[(x) - 32]
537 /* Show channels currently joined to */
538 if (!cmd->client->current_win->sock) {
539 silc_say(cmd->client, "No current channel for this window");
540 SILC_NOT_CONNECTED(cmd->client);
548 if (!cmd->client->current_win->sock) {
549 SILC_NOT_CONNECTED(cmd->client);
553 win = (SilcClientWindow)cmd->sock->user_data;
555 /* See if we have joined to the requested channel already */
556 silc_idcache_find_by_data(CIDC(cmd->argv[1][0]), CIDCC(cmd->argv[1][0]),
557 cmd->argv[1], &id_cache);
560 silc_say(cmd->client, "You are talking to channel %s", cmd->argv[1]);
561 win->current_channel = (SilcChannelEntry)id_cache->context;
562 cmd->client->screen->bottom_line->channel = cmd->argv[1];
563 silc_screen_print_bottom_line(cmd->client->screen, 0);
567 /* Send JOIN command to the server */
568 buffer = silc_command_encode_payload(SILC_COMMAND_JOIN,
569 cmd->argc - 1, ++cmd->argv,
570 ++cmd->argv_lens, ++cmd->argv_types);
571 silc_client_packet_send(cmd->client, cmd->client->current_win->sock,
572 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
573 buffer->data, buffer->len, TRUE);
574 silc_buffer_free(buffer);
580 silc_client_command_free(cmd);
585 SILC_CLIENT_CMD_FUNC(motd)
589 SILC_CLIENT_CMD_FUNC(umode)
593 SILC_CLIENT_CMD_FUNC(cmode)
597 SILC_CLIENT_CMD_FUNC(kick)
601 SILC_CLIENT_CMD_FUNC(restart)
605 SILC_CLIENT_CMD_FUNC(close)
609 SILC_CLIENT_CMD_FUNC(die)
613 SILC_CLIENT_CMD_FUNC(silcoper)
617 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
619 SILC_CLIENT_CMD_FUNC(leave)
621 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
622 SilcClientWindow win = NULL;
623 SilcIDCache *id_cache = NULL;
625 unsigned char *id_string;
628 #define CIDC(x) win->channel_id_cache[(x) - 32]
629 #define CIDCC(x) win->channel_id_cache_count[(x) - 32]
631 if (cmd->argc != 2) {
632 silc_say(cmd->client, "Usage: /LEAVE <channel>");
636 if (!cmd->client->current_win->sock) {
637 SILC_NOT_CONNECTED(cmd->client);
641 win = (SilcClientWindow)cmd->sock->user_data;
643 if (cmd->argv[1][0] == '*') {
644 if (!win->current_channel) {
645 silc_say(cmd->client, "You are not on any chanenl");
648 name = win->current_channel->channel_name;
653 if (!win->current_channel) {
654 silc_say(cmd->client, "You are not on that channel");
658 /* Get the Channel ID of the channel */
659 silc_idcache_find_by_data(CIDC(name[0]), CIDCC(name[0]), name, &id_cache);
661 silc_say(cmd->client, "You are not on that channel");
665 /* Send LEAVE command to the server */
666 id_string = silc_id_id2str(id_cache->id, SILC_ID_CHANNEL);
667 buffer = silc_command_encode_payload_va(SILC_COMMAND_LEAVE, 1,
668 1, id_string, SILC_ID_CHANNEL_LEN);
669 silc_client_packet_send(cmd->client, cmd->client->current_win->sock,
670 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
671 buffer->data, buffer->len, TRUE);
672 silc_buffer_free(buffer);
674 /* We won't talk anymore on this channel */
675 silc_say(cmd->client, "You have left channel %s", name);
676 cmd->client->screen->bottom_line->channel = NULL;
677 silc_screen_print_bottom_line(cmd->client->screen, 0);
679 silc_idcache_del_by_id(CIDC(name[0]), CIDCC(name[0]),
680 SILC_ID_CHANNEL, win->current_channel->id);
681 silc_free(win->current_channel->channel_name);
682 silc_free(win->current_channel->id);
683 silc_free(win->current_channel->key);
684 silc_cipher_free(win->current_channel->channel_key);
685 silc_free(win->current_channel);
686 win->current_channel = NULL;
687 silc_free(id_string);
690 silc_client_command_free(cmd);
695 /* Command NAMES. Requests the names of the clients joined on requested
698 SILC_CLIENT_CMD_FUNC(names)
700 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
701 SilcClientWindow win = NULL;
702 SilcIDCache *id_cache = NULL;
705 unsigned char *id_string;
707 #define CIDC(x) win->channel_id_cache[(x) - 32]
708 #define CIDCC(x) win->channel_id_cache_count[(x) - 32]
710 if (cmd->argc != 2) {
711 silc_say(cmd->client, "Usage: /NAMES <channel>");
715 if (!cmd->client->current_win->sock) {
716 SILC_NOT_CONNECTED(cmd->client);
720 win = (SilcClientWindow)cmd->sock->user_data;
722 if (cmd->argv[1][0] == '*')
723 name = win->current_channel->channel_name;
727 /* Get the Channel ID of the channel */
728 silc_idcache_find_by_data(CIDC(name[0]), CIDCC(name[0]), name, &id_cache);
730 /* XXX should resolve the channel ID; LIST command */
731 silc_say(cmd->client, "You are not on that channel", name);
735 /* Send NAMES command to the server */
736 id_string = silc_id_id2str(id_cache->id, SILC_ID_CHANNEL);
737 buffer = silc_command_encode_payload_va(SILC_COMMAND_NAMES, 1,
738 1, id_string, SILC_ID_CHANNEL_LEN);
739 silc_client_packet_send(cmd->client, cmd->client->current_win->sock,
740 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
741 buffer->data, buffer->len, TRUE);
742 silc_buffer_free(buffer);
743 silc_free(id_string);
745 /* Register dummy pending command that will tell the reply command
746 that user called this command. Server may send reply to this command
747 even if user did not send this command thus we want to handle things
748 differently when user sent the command. This is dummy and won't be
750 /* XXX this is kludge and should be removed after pending command reply
751 support is added. Currently only commands may be pending not command
753 silc_client_command_pending(SILC_COMMAND_NAMES, silc_client_command_names,
757 silc_client_command_free(cmd);
766 /* HELP command. This is local command and shows help on SILC */
768 SILC_CLIENT_CMD_FUNC(help)
773 /* CLEAR command. This is local command and clears current output window */
775 SILC_CLIENT_CMD_FUNC(clear)
777 SilcClient client = (SilcClient)context;
779 assert(client->current_win != NULL);
780 wclear((WINDOW *)client->current_win->screen);
781 wrefresh((WINDOW *)client->current_win->screen);
784 /* VERSION command. This is local command and shows version of the client */
786 SILC_CLIENT_CMD_FUNC(version)
791 /* Command MSG. Sends private message to user or list of users. */
792 /* XXX supports only one destination */
794 SILC_CLIENT_CMD_FUNC(msg)
796 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
797 SilcClientWindow win = NULL;
798 SilcClient client = cmd->client;
799 SilcIDCache *id_cache;
801 #define CIDC(x) win->client_id_cache[(x) - 32], \
802 win->client_id_cache_count[(x) - 32]
805 silc_say(cmd->client, "Usage: /MSG <nickname> <message>");
809 if (!cmd->client->current_win->sock) {
810 SILC_NOT_CONNECTED(cmd->client);
814 win = (SilcClientWindow)cmd->sock->user_data;
816 /* Find ID from cache */
817 if (silc_idcache_find_by_data(CIDC(cmd->argv[1][0]), cmd->argv[1],
818 &id_cache) == FALSE) {
819 SilcClientCommandContext ctx;
822 SILC_LOG_DEBUG(("Requesting Client ID from server"));
824 /* No ID found. Do query from the server. The query is done by
825 sending simple IDENTIFY command to the server. */
826 ctx = silc_calloc(1, sizeof(*ctx));
827 ctx->client = client;
828 ctx->sock = cmd->sock;
829 memset(ident, 0, sizeof(ident));
830 snprintf(ident, sizeof(ident), "/IDENTIFY %s", cmd->argv[1]);
831 silc_client_parse_command_line(ident, &ctx->argv, &ctx->argv_lens,
832 &ctx->argv_types, &ctx->argc, 2);
833 silc_client_command_identify(ctx);
835 /* Mark this command to be pending command and to be executed after
836 we have received the IDENTIFY reply from server. */
837 silc_client_command_pending(SILC_COMMAND_IDENTIFY,
838 silc_client_command_msg, context);
842 /* Display the message for our eyes. */
843 silc_print(client, "-> *%s* %s", cmd->argv[1], cmd->argv[2]);
845 /* Send the private message */
846 silc_client_packet_send_private_message(client, cmd->sock, id_cache->context,
847 cmd->argv[2], cmd->argv_lens[2],
851 silc_client_command_free(cmd);
855 SILC_CLIENT_CMD_FUNC(away)