7b6e8c17e5c9a769ca6e361a0847668e9d076115
[silc.git] / apps / silc / command.c
1 /*
2
3   command.c
4
5   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   Copyright (C) 1997 - 2000 Pekka Riikonen
8
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.
13   
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.
18
19 */
20 /*
21  * $Id$
22  * $Log$
23  * Revision 1.6  2000/07/06 07:14:36  priikone
24  *      Fixes to NAMES command handling.
25  *      Fixes when leaving from channel.
26  *
27  * Revision 1.5  2000/07/05 06:12:05  priikone
28  *      Global cosmetic changes.
29  *
30  * Revision 1.4  2000/07/04 08:28:03  priikone
31  *      Added INVITE, PING and NAMES command.
32  *
33  * Revision 1.3  2000/07/03 05:49:49  priikone
34  *      Implemented LEAVE command.  Minor bug fixes.
35  *
36  * Revision 1.2  2000/06/27 19:38:40  priikone
37  *      Added missing goto flag.
38  *
39  * Revision 1.1.1.1  2000/06/27 11:36:56  priikone
40  *      Imported from internal CVS/Added Log headers.
41  *
42  *
43  */
44
45 #include "clientincludes.h"
46
47 /* Client command list. */
48 SilcClientCommand silc_command_list[] =
49 {
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),
82
83   /*
84    * Local. client specific commands
85    */
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),
92
93   { NULL, 0, NULL, 0},
94 };
95
96 #define SILC_NOT_CONNECTED(x) \
97   silc_say((x), "You are not connected to a server, use /SERVER to connect");
98
99 /* List of pending commands. */
100 SilcClientCommandPending *silc_command_pending = NULL;
101
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.
105
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.
108
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. */
112
113 void silc_client_command_pending(SilcCommand reply_cmd,
114                                  SilcClientCommandCallback callback,
115                                  void *context)
116 {
117   SilcClientCommandPending *reply, *r;
118
119   reply = silc_calloc(1, sizeof(*reply));
120   reply->reply_cmd = reply_cmd;
121   reply->context = context;
122   reply->callback = callback;
123
124   if (silc_command_pending == NULL) {
125     silc_command_pending = reply;
126     return;
127   }
128
129   for (r = silc_command_pending; r; r = r->next) {
130     if (r->next == NULL) {
131       r->next = reply;
132       break;
133     }
134   }
135 }
136
137 /* Deletes pending command by reply command type. */
138
139 void silc_client_command_pending_del(SilcCommand reply_cmd)
140 {
141   SilcClientCommandPending *r, *tmp;
142   
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;
147       return;
148     }
149
150     for (r = silc_command_pending; r; r = r->next) {
151       if (r->next && r->next->reply_cmd == reply_cmd) {
152         tmp = r->next;
153         r->next = r->next->next;
154         silc_free(tmp);
155         break;
156       }
157     }
158   }
159 }
160
161 /* Free command context and its internals */
162
163 static void silc_client_command_free(SilcClientCommandContext cmd)
164 {
165   int i;
166
167   if (cmd) {
168     for (i = 0; i < cmd->argc; i++)
169       silc_free(cmd->argv[i]);
170     silc_free(cmd);
171   }
172 }
173
174 /* Command WHOIS. This command is used to query information about 
175    specific user. */
176
177 SILC_CLIENT_CMD_FUNC(whois)
178 {
179   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
180   SilcBuffer buffer;
181
182   if (cmd->argc < 2 || cmd->argc > 3) {
183     silc_say(cmd->client, "Usage: /WHOIS <nickname>[@<server>] [<count>]");
184     goto out;
185   }
186
187   if (!cmd->client->current_win->sock) {
188     SILC_NOT_CONNECTED(cmd->client);
189     goto out;
190   }
191
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);
199   cmd->argv--;
200   cmd->argv_lens--;
201   cmd->argv_types--;
202
203  out:
204   silc_client_command_free(cmd);
205 }
206
207 SILC_CLIENT_CMD_FUNC(whowas)
208 {
209 }
210
211 /* Command IDENTIFY. This command is used to query information about 
212    specific user, especially ID's. */
213
214 SILC_CLIENT_CMD_FUNC(identify)
215 {
216   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
217   SilcBuffer buffer;
218
219   if (cmd->argc < 2 || cmd->argc > 3) {
220     silc_say(cmd->client, "Usage: /IDENTIFY <nickname>[@<server>] [<count>]");
221     goto out;
222   }
223
224   if (!cmd->client->current_win->sock) {
225     SILC_NOT_CONNECTED(cmd->client);
226     goto out;
227   }
228
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);
236   cmd->argv--;
237   cmd->argv_lens--;
238   cmd->argv_types--;
239
240  out:
241   silc_client_command_free(cmd);
242 }
243
244 /* Command NICK. Shows current nickname/sets new nickname on current
245    window. */
246
247 SILC_CLIENT_CMD_FUNC(nick)
248 {
249   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
250   SilcClientWindow win = NULL;
251   SilcBuffer buffer;
252
253   if (!cmd->sock) {
254     SILC_NOT_CONNECTED(cmd->client);
255     goto out;
256   }
257
258   /* Show current nickname */
259   if (cmd->argc < 2) {
260     if (cmd->sock) {
261       silc_say(cmd->client, "Your nickname is %s on server %s", 
262                win->nickname, win->remote_host);
263     } else {
264       silc_say(cmd->client, "Your nickname is %s", win->nickname);
265     }
266     goto out;
267   }
268
269   win = (SilcClientWindow)cmd->sock->user_data;
270
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);
279   cmd->argv--;
280   cmd->argv_lens--;
281   cmd->argv_types--;
282   if (win->nickname)
283     silc_free(win->nickname);
284   win->nickname = strdup(cmd->argv[1]);
285
286  out:
287   silc_client_command_free(cmd);
288 }
289
290 /* Command SERVER. Connects to remote SILC server. This is local command. */
291
292 SILC_CLIENT_CMD_FUNC(server)
293 {
294   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
295   int len, port;
296   char *hostname;
297
298   if (cmd->argc < 2) {
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>]]");
303       goto out;
304     }
305
306     goto out;
307   }
308
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);
315   } else {
316     hostname = cmd->argv[1];
317     port = 706;
318   }
319
320   /* Connect asynchronously to not to block user interface */
321   silc_client_connect_to_server(cmd->client, port, hostname);
322
323  out:
324   silc_client_command_free(cmd);
325 }
326
327 SILC_CLIENT_CMD_FUNC(list)
328 {
329 }
330
331 SILC_CLIENT_CMD_FUNC(topic)
332 {
333 }
334
335 /* Command INVITE. Invites specific client to join a channel. */
336
337 SILC_CLIENT_CMD_FUNC(invite)
338 {
339   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
340   SilcClientWindow win = NULL;
341   SilcBuffer buffer;
342   SilcIDCache *id_cache;
343   unsigned char *client_id, *channel_id;
344
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]
349
350   if (cmd->argc != 3) {
351     silc_say(cmd->client, "Usage: /INVITE <nickname>[@<server>] <channel>");
352     goto out;
353   }
354
355   if (!cmd->client->current_win->sock) {
356     SILC_NOT_CONNECTED(cmd->client);
357     goto out;
358   }
359
360   win = (SilcClientWindow)cmd->sock->user_data;
361
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], 
366                                 &id_cache)) {
367     SilcClientCommandContext ctx;
368     char ident[512];
369
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);
380     return;
381   }
382
383   client_id = silc_id_id2str(id_cache->id, SILC_ID_CLIENT);
384
385   /* Get Channel ID of the channel. */
386   if (!silc_idcache_find_by_data(CHIDC(cmd->argv[2][0]), cmd->argv[2],
387                                  &id_cache)) {
388     silc_say(cmd->client, "You are not on that channel");
389     silc_free(client_id);
390     goto out;
391   }
392
393   channel_id = silc_id_id2str(id_cache->id, SILC_ID_CHANNEL);
394
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);
402
403   silc_say(cmd->client, "Inviting %s to channel %s", cmd->argv[1], 
404            cmd->argv[2]);
405
406  out:
407   silc_client_command_free(cmd);
408 #undef CIDC
409 #undef CHIDC
410 }
411
412 /* Command QUIT. Closes connection with current server. */
413  
414 SILC_CLIENT_CMD_FUNC(quit)
415 {
416   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
417   SilcBuffer buffer;
418
419   if (!cmd->client->current_win->sock) {
420     SILC_NOT_CONNECTED(cmd->client);
421     goto out;
422   }
423
424   buffer = silc_command_encode_payload(SILC_COMMAND_QUIT, cmd->argc - 1, 
425                                        ++cmd->argv, ++cmd->argv_lens,
426                                        ++cmd->argv_types);
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);
431   cmd->argv--;
432   cmd->argv_lens--;
433   cmd->argv_types--;
434
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);
439
440 out:
441   silc_client_command_free(cmd);
442 }
443
444 SILC_CLIENT_CMD_FUNC(kill)
445 {
446 }
447
448 SILC_CLIENT_CMD_FUNC(info)
449 {
450 }
451
452 SILC_CLIENT_CMD_FUNC(connect)
453 {
454 }
455
456 /* Command PING. Sends ping to server. This is used to test the 
457    communication channel. */
458
459 SILC_CLIENT_CMD_FUNC(ping)
460 {
461   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
462   SilcClientWindow win = NULL;
463   SilcBuffer buffer;
464   void *id;
465   int i;
466   char *name = NULL;
467
468   if (!cmd->sock) {
469     SILC_NOT_CONNECTED(cmd->client);
470     goto out;
471   }
472
473   win = (SilcClientWindow)cmd->sock->user_data;
474
475   if (cmd->argc == 1 || !strcmp(cmd->argv[1], win->remote_host))
476     name = strdup(win->remote_host);
477
478   id = silc_id_str2id(win->remote_id_data, SILC_ID_SERVER);
479
480   /* Send the command */
481   buffer = silc_command_encode_payload_va(SILC_COMMAND_PING, 1, 
482                                           1, win->remote_id_data, 
483                                           SILC_ID_SERVER_LEN);
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);
488
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;
495       win->ping_count++;
496       break;
497     }
498   }
499   if (i >= win->ping_count) {
500     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;
505     win->ping_count++;
506   }
507   
508  out:
509   silc_client_command_free(cmd);
510 }
511
512 SILC_CLIENT_CMD_FUNC(oper)
513 {
514 }
515
516 SILC_CLIENT_CMD_FUNC(trace)
517 {
518 }
519
520 SILC_CLIENT_CMD_FUNC(notice)
521 {
522 }
523
524 /* Command JOIN. Joins to a channel. */
525
526 SILC_CLIENT_CMD_FUNC(join)
527 {
528   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
529   SilcClientWindow win = NULL;
530   SilcIDCache *id_cache = NULL;
531   SilcBuffer buffer;
532
533 #define CIDC(x) win->channel_id_cache[(x) - 32]
534 #define CIDCC(x) win->channel_id_cache_count[(x) - 32]
535
536   if (cmd->argc < 2) {
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);
541       goto out;
542
543     }
544
545     goto out;
546   }
547
548   if (!cmd->client->current_win->sock) {
549     SILC_NOT_CONNECTED(cmd->client);
550     goto out;
551   }
552
553   win = (SilcClientWindow)cmd->sock->user_data;
554
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);
558
559   if (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);
564     goto out;
565   }
566
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);
575   cmd->argv--;
576   cmd->argv_lens--;
577   cmd->argv_types--;
578
579  out:
580   silc_client_command_free(cmd);
581 #undef CIDC
582 #undef CIDCC
583 }
584
585 SILC_CLIENT_CMD_FUNC(motd)
586 {
587 }
588
589 SILC_CLIENT_CMD_FUNC(umode)
590 {
591 }
592
593 SILC_CLIENT_CMD_FUNC(cmode)
594 {
595 }
596
597 SILC_CLIENT_CMD_FUNC(kick)
598 {
599 }
600
601 SILC_CLIENT_CMD_FUNC(restart)
602 {
603 }
604  
605 SILC_CLIENT_CMD_FUNC(close)
606 {
607 }
608  
609 SILC_CLIENT_CMD_FUNC(die)
610 {
611 }
612  
613 SILC_CLIENT_CMD_FUNC(silcoper)
614 {
615 }
616
617 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
618
619 SILC_CLIENT_CMD_FUNC(leave)
620 {
621   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
622   SilcClientWindow win = NULL;
623   SilcIDCache *id_cache = NULL;
624   SilcBuffer buffer;
625   unsigned char *id_string;
626   char *name;
627
628 #define CIDC(x) win->channel_id_cache[(x) - 32]
629 #define CIDCC(x) win->channel_id_cache_count[(x) - 32]
630
631   if (cmd->argc != 2) {
632     silc_say(cmd->client, "Usage: /LEAVE <channel>");
633     goto out;
634   }
635
636   if (!cmd->client->current_win->sock) {
637     SILC_NOT_CONNECTED(cmd->client);
638     goto out;
639   }
640
641   win = (SilcClientWindow)cmd->sock->user_data;
642
643   if (cmd->argv[1][0] == '*') {
644     if (!win->current_channel) {
645       silc_say(cmd->client, "You are not on any chanenl");
646       goto out;
647     }
648     name = win->current_channel->channel_name;
649   } else {
650     name = cmd->argv[1];
651   }
652
653   if (!win->current_channel) {
654     silc_say(cmd->client, "You are not on that channel");
655     goto out;
656   }
657
658   /* Get the Channel ID of the channel */
659   silc_idcache_find_by_data(CIDC(name[0]), CIDCC(name[0]), name, &id_cache);
660   if (!id_cache) {
661     silc_say(cmd->client, "You are not on that channel");
662     goto out;
663   }
664
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);
673
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);
678
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);
688
689  out:
690   silc_client_command_free(cmd);
691 #undef CIDC
692 #undef CIDCC
693 }
694
695 /* Command NAMES. Requests the names of the clients joined on requested
696    channel. */
697
698 SILC_CLIENT_CMD_FUNC(names)
699 {
700   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
701   SilcClientWindow win = NULL;
702   SilcIDCache *id_cache = NULL;
703   SilcBuffer buffer;
704   char *name;
705   unsigned char *id_string;
706
707 #define CIDC(x) win->channel_id_cache[(x) - 32]
708 #define CIDCC(x) win->channel_id_cache_count[(x) - 32]
709
710   if (cmd->argc != 2) {
711     silc_say(cmd->client, "Usage: /NAMES <channel>");
712     goto out;
713   }
714
715   if (!cmd->client->current_win->sock) {
716     SILC_NOT_CONNECTED(cmd->client);
717     goto out;
718   }
719
720   win = (SilcClientWindow)cmd->sock->user_data;
721
722   if (cmd->argv[1][0] == '*')
723     name = win->current_channel->channel_name;
724   else
725     name = cmd->argv[1];
726
727   /* Get the Channel ID of the channel */
728   silc_idcache_find_by_data(CIDC(name[0]), CIDCC(name[0]), name, &id_cache);
729   if (!id_cache) {
730     /* XXX should resolve the channel ID; LIST command */
731     silc_say(cmd->client, "You are not on that channel", name);
732     goto out;
733   }
734
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);
744
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
749      execute. */
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
752      replies. */
753   silc_client_command_pending(SILC_COMMAND_NAMES, silc_client_command_names,
754                               NULL);
755
756  out:
757   silc_client_command_free(cmd);
758 #undef CIDC
759 #undef CIDCC
760 }
761
762 /*
763  * Local commands
764  */
765
766 /* HELP command. This is local command and shows help on SILC */
767
768 SILC_CLIENT_CMD_FUNC(help)
769 {
770
771 }
772
773 /* CLEAR command. This is local command and clears current output window */
774
775 SILC_CLIENT_CMD_FUNC(clear)
776 {
777   SilcClient client = (SilcClient)context;
778
779   assert(client->current_win != NULL);
780   wclear((WINDOW *)client->current_win->screen);
781   wrefresh((WINDOW *)client->current_win->screen);
782 }
783
784 /* VERSION command. This is local command and shows version of the client */
785
786 SILC_CLIENT_CMD_FUNC(version)
787 {
788
789 }
790
791 /* Command MSG. Sends private message to user or list of users. */
792 /* XXX supports only one destination */
793
794 SILC_CLIENT_CMD_FUNC(msg)
795 {
796   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
797   SilcClientWindow win = NULL;
798   SilcClient client = cmd->client;
799   SilcIDCache *id_cache;
800
801 #define CIDC(x) win->client_id_cache[(x) - 32], \
802                 win->client_id_cache_count[(x) - 32]
803
804   if (cmd->argc < 3) {
805     silc_say(cmd->client, "Usage: /MSG <nickname> <message>");
806     goto out;
807   }
808
809   if (!cmd->client->current_win->sock) {
810     SILC_NOT_CONNECTED(cmd->client);
811     goto out;
812   }
813
814   win = (SilcClientWindow)cmd->sock->user_data;
815
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;
820     char ident[512];
821
822     SILC_LOG_DEBUG(("Requesting Client ID from server"));
823
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);
834
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);
839     return;
840   }
841
842   /* Display the message for our eyes. */
843   silc_print(client, "-> *%s* %s", cmd->argv[1], cmd->argv[2]);
844
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],
848                                           TRUE);
849
850  out:
851   silc_client_command_free(cmd);
852 #undef CIDC
853 }
854
855 SILC_CLIENT_CMD_FUNC(away)
856 {
857 }