60de1b54e3af8c754d955feb4e23a46813969f64
[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.4  2000/07/04 08:28:03  priikone
24  *      Added INVITE, PING and NAMES command.
25  *
26  * Revision 1.3  2000/07/03 05:49:49  priikone
27  *      Implemented LEAVE command.  Minor bug fixes.
28  *
29  * Revision 1.2  2000/06/27 19:38:40  priikone
30  *      Added missing goto flag.
31  *
32  * Revision 1.1.1.1  2000/06/27 11:36:56  priikone
33  *      Importet from internal CVS/Added Log headers.
34  *
35  *
36  */
37
38 #include "clientincludes.h"
39
40 /* Client command list. */
41 SilcClientCommand silc_command_list[] =
42 {
43   SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", SILC_CF_LAG | SILC_CF_REG, 3),
44   SILC_CLIENT_CMD(whowas, WHOWAS, "WHOWAS", SILC_CF_LAG | SILC_CF_REG, 3),
45   SILC_CLIENT_CMD(identify, IDENTIFY, "IDENTIFY", 
46                   SILC_CF_LAG | SILC_CF_REG, 3),
47   SILC_CLIENT_CMD(nick, NICK, "NICK", SILC_CF_LAG | SILC_CF_REG, 2),
48   SILC_CLIENT_CMD(list, LIST, "LIST", SILC_CF_LAG | SILC_CF_REG, 2),
49   SILC_CLIENT_CMD(topic, TOPIC, "TOPIC", SILC_CF_LAG | SILC_CF_REG, 2),
50   SILC_CLIENT_CMD(invite, INVITE, "INVITE", SILC_CF_LAG | SILC_CF_REG, 3),
51   SILC_CLIENT_CMD(quit, QUIT, "QUIT", SILC_CF_LAG | SILC_CF_REG, 1),
52   SILC_CLIENT_CMD(kill, KILL, "KILL", 
53                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
54   SILC_CLIENT_CMD(info, INFO, "INFO", SILC_CF_LAG | SILC_CF_REG, 2),
55   SILC_CLIENT_CMD(connect, CONNECT, "CONNECT",
56                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
57   SILC_CLIENT_CMD(ping, PING, "PING", SILC_CF_LAG | SILC_CF_REG, 2),
58   SILC_CLIENT_CMD(oper, OPER, "OPER",
59                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
60   SILC_CLIENT_CMD(join, JOIN, "JOIN", SILC_CF_LAG | SILC_CF_REG, 2),
61   SILC_CLIENT_CMD(motd, MOTD, "MOTD", SILC_CF_LAG | SILC_CF_REG, 2),
62   SILC_CLIENT_CMD(umode, UMODE, "UMODE", SILC_CF_LAG | SILC_CF_REG, 2),
63   SILC_CLIENT_CMD(cmode, CMODE, "CMODE", SILC_CF_LAG | SILC_CF_REG, 2),
64   SILC_CLIENT_CMD(kick, KICK, "KICK", SILC_CF_LAG | SILC_CF_REG, 2),
65   SILC_CLIENT_CMD(restart, RESTART, "RESTART",
66                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
67   SILC_CLIENT_CMD(close, CLOSE, "CLOSE",
68                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
69   SILC_CLIENT_CMD(die, DIE, "DIE",
70                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
71   SILC_CLIENT_CMD(silcoper, SILCOPER, "SILOPER",
72                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_SILC_OPER, 2),
73   SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", SILC_CF_LAG | SILC_CF_REG, 2),
74   SILC_CLIENT_CMD(names, NAMES, "NAMES", SILC_CF_LAG | SILC_CF_REG, 2),
75
76   /*
77    * Local. client specific commands
78    */
79   SILC_CLIENT_CMD(help, HELP, "HELP", SILC_CF_NONE, 2),
80   SILC_CLIENT_CMD(clear, CLEAR, "CLEAR", SILC_CF_NONE, 1),
81   SILC_CLIENT_CMD(version, VERSION, "VERSION", SILC_CF_NONE, 1),
82   SILC_CLIENT_CMD(server, SERVER, "SERVER", SILC_CF_NONE, 2),
83   SILC_CLIENT_CMD(msg, MSG, "MSG", SILC_CF_NONE, 3),
84   SILC_CLIENT_CMD(away, AWAY, "AWAY", SILC_CF_NONE, 2),
85
86   { NULL, 0, NULL, 0},
87 };
88
89 #define SILC_NOT_CONNECTED(x) \
90   silc_say((x), "You are not connected to a server, use /SERVER to connect");
91
92 /* List of pending commands. */
93 SilcClientCommandPending *silc_command_pending = NULL;
94
95 /* Add new pending command to the list of pending commands. Currently
96    pending commands are executed from command replies, thus we can
97    execute any command after receiving some specific command reply.
98
99    The argument `reply_cmd' is the command reply from where the callback
100    function is to be called, thus, it IS NOT the command to be executed.
101
102    XXX: If needed in the future this support may be extended for
103    commands as well, when any command could be executed after executing
104    some specific command. */
105
106 void silc_client_command_pending(SilcCommand reply_cmd,
107                                  SilcClientCommandCallback callback,
108                                  void *context)
109 {
110   SilcClientCommandPending *reply, *r;
111
112   reply = silc_calloc(1, sizeof(*reply));
113   reply->reply_cmd = reply_cmd;
114   reply->context = context;
115   reply->callback = callback;
116
117   if (silc_command_pending == NULL) {
118     silc_command_pending = reply;
119     return;
120   }
121
122   for (r = silc_command_pending; r; r = r->next) {
123     if (r->next == NULL) {
124       r->next = reply;
125       break;
126     }
127   }
128 }
129
130 /* Deletes pending command by reply command type. */
131
132 void silc_client_command_pending_del(SilcCommand reply_cmd)
133 {
134   SilcClientCommandPending *r, *tmp;
135   
136   if (silc_command_pending) {
137     if (silc_command_pending->reply_cmd == reply_cmd) {
138       silc_free(silc_command_pending);
139       silc_command_pending = NULL;
140       return;
141     }
142
143     for (r = silc_command_pending; r; r = r->next) {
144       if (r->next && r->next->reply_cmd == reply_cmd) {
145         tmp = r->next;
146         r->next = r->next->next;
147         silc_free(tmp);
148         break;
149       }
150     }
151   }
152 }
153
154 /* Free command context and its internals */
155
156 static void silc_client_command_free(SilcClientCommandContext cmd)
157 {
158   int i;
159
160   if (cmd) {
161     for (i = 0; i < cmd->argc; i++)
162       silc_free(cmd->argv[i]);
163     silc_free(cmd);
164   }
165 }
166
167 /* Command WHOIS. This command is used to query information about 
168    specific user. */
169
170 SILC_CLIENT_CMD_FUNC(whois)
171 {
172   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
173   SilcBuffer buffer;
174
175   if (cmd->argc < 2 || cmd->argc > 3) {
176     silc_say(cmd->client, "Usage: /WHOIS <nickname>[@<server>] [<count>]");
177     goto out;
178   }
179
180   if (!cmd->client->current_win->sock) {
181     SILC_NOT_CONNECTED(cmd->client);
182     goto out;
183   }
184
185   buffer = silc_command_encode_payload(SILC_COMMAND_WHOIS,
186                                        cmd->argc - 1, ++cmd->argv,
187                                        ++cmd->argv_lens, ++cmd->argv_types);
188   silc_client_packet_send(cmd->client, cmd->client->current_win->sock,
189                           SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
190                           buffer->data, buffer->len, TRUE);
191   silc_buffer_free(buffer);
192   cmd->argv--;
193   cmd->argv_lens--;
194   cmd->argv_types--;
195
196  out:
197   silc_client_command_free(cmd);
198 }
199
200 SILC_CLIENT_CMD_FUNC(whowas)
201 {
202 }
203
204 /* Command IDENTIFY. This command is used to query information about 
205    specific user, especially ID's. */
206
207 SILC_CLIENT_CMD_FUNC(identify)
208 {
209   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
210   SilcBuffer buffer;
211
212   if (cmd->argc < 2 || cmd->argc > 3) {
213     silc_say(cmd->client, "Usage: /IDENTIFY <nickname>[@<server>] [<count>]");
214     goto out;
215   }
216
217   if (!cmd->client->current_win->sock) {
218     SILC_NOT_CONNECTED(cmd->client);
219     goto out;
220   }
221
222   buffer = silc_command_encode_payload(SILC_COMMAND_IDENTIFY,
223                                        cmd->argc - 1, ++cmd->argv,
224                                        ++cmd->argv_lens, ++cmd->argv_types);
225   silc_client_packet_send(cmd->client, cmd->client->current_win->sock,
226                           SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
227                           buffer->data, buffer->len, TRUE);
228   silc_buffer_free(buffer);
229   cmd->argv--;
230   cmd->argv_lens--;
231   cmd->argv_types--;
232
233  out:
234   silc_client_command_free(cmd);
235 }
236
237 /* Command NICK. Shows current nickname/sets new nickname on current
238    window. */
239
240 SILC_CLIENT_CMD_FUNC(nick)
241 {
242   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
243   SilcClientWindow win = NULL;
244   SilcBuffer buffer;
245
246   if (!cmd->sock) {
247     SILC_NOT_CONNECTED(cmd->client);
248     goto out;
249   }
250
251   /* Show current nickname */
252   if (cmd->argc < 2) {
253     if (cmd->sock) {
254       silc_say(cmd->client, "Your nickname is %s on server %s", 
255                win->nickname, win->remote_host);
256     } else {
257       silc_say(cmd->client, "Your nickname is %s", win->nickname);
258     }
259     goto out;
260   }
261
262   win = (SilcClientWindow)cmd->sock->user_data;
263
264   /* Set new nickname */
265   buffer = silc_command_encode_payload(SILC_COMMAND_NICK,
266                                        cmd->argc - 1, ++cmd->argv,
267                                        ++cmd->argv_lens, ++cmd->argv_types);
268   silc_client_packet_send(cmd->client, cmd->sock,
269                           SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
270                           buffer->data, buffer->len, TRUE);
271   silc_buffer_free(buffer);
272   cmd->argv--;
273   cmd->argv_lens--;
274   cmd->argv_types--;
275   if (win->nickname)
276     silc_free(win->nickname);
277   win->nickname = strdup(cmd->argv[1]);
278
279  out:
280   silc_client_command_free(cmd);
281 }
282
283 /* Command SERVER. Connects to remote SILC server. This is local command. */
284
285 SILC_CLIENT_CMD_FUNC(server)
286 {
287   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
288   int len, port;
289   char *hostname;
290
291   if (cmd->argc < 2) {
292     /* Show current servers */
293     if (!cmd->client->current_win->sock) {
294       silc_say(cmd->client, "You are not connected to any server");
295       silc_say(cmd->client, "Usage: /SERVER [<server>[:<port>]]");
296       goto out;
297     }
298
299     goto out;
300   }
301
302   /* See if port is included and then extract it */
303   if (strchr(cmd->argv[1], ':')) {
304     len = strcspn(cmd->argv[1], ":");
305     hostname = silc_calloc(len + 1, sizeof(char));
306     memcpy(hostname, cmd->argv[1], len);
307     port = atoi(cmd->argv[1] + 1 + len);
308   } else {
309     hostname = cmd->argv[1];
310     port = 334;
311   }
312
313   /* Connect asynchronously to not to block user interface */
314   silc_client_connect_to_server(cmd->client, port, hostname);
315
316  out:
317   silc_client_command_free(cmd);
318 }
319
320 SILC_CLIENT_CMD_FUNC(list)
321 {
322 }
323
324 SILC_CLIENT_CMD_FUNC(topic)
325 {
326 }
327
328 /* Command INVITE. Invites specific client to join a channel. */
329
330 SILC_CLIENT_CMD_FUNC(invite)
331 {
332   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
333   SilcClientWindow win = NULL;
334   SilcBuffer buffer;
335   SilcIDCache *id_cache;
336   unsigned char *client_id, *channel_id;
337
338 #define CIDC(x) win->client_id_cache[(x) - 32], \
339                 win->client_id_cache_count[(x) - 32]
340 #define CHIDC(x) win->channel_id_cache[(x) - 32], \
341                  win->channel_id_cache_count[(x) - 32]
342
343   if (cmd->argc != 3) {
344     silc_say(cmd->client, "Usage: /INVITE <nickname>[@<server>] <channel>");
345     goto out;
346   }
347
348   if (!cmd->client->current_win->sock) {
349     SILC_NOT_CONNECTED(cmd->client);
350     goto out;
351   }
352
353   win = (SilcClientWindow)cmd->sock->user_data;
354
355   /* Get client ID of the client to be invited. If we don't have it
356      we will request it and cache it. This same command will be called
357      again after we have received the reply (ie. pending). */
358   if (!silc_idcache_find_by_data(CIDC(cmd->argv[1][0]), cmd->argv[1], 
359                                 &id_cache)) {
360     SilcClientCommandContext ctx;
361     char ident[512];
362
363     ctx = silc_calloc(1, sizeof(*ctx));
364     ctx->client = cmd->client;
365     ctx->sock = cmd->sock;
366     memset(ident, 0, sizeof(ident));
367     snprintf(ident, sizeof(ident), "/IDENTIFY %s", cmd->argv[1]);
368     silc_client_parse_command_line(ident, &ctx->argv, &ctx->argv_lens, 
369                                    &ctx->argv_types, &ctx->argc, 2);
370     silc_client_command_identify(ctx);
371     silc_client_command_pending(SILC_COMMAND_IDENTIFY, 
372                                 silc_client_command_invite, context);
373     return;
374   }
375
376   client_id = silc_id_id2str(id_cache->id, SILC_ID_CLIENT);
377
378   /* Get Channel ID of the channel. */
379   if (!silc_idcache_find_by_data(CHIDC(cmd->argv[2][0]), cmd->argv[2],
380                                  &id_cache)) {
381     silc_say(cmd->client, "You are not on that channel");
382     silc_free(client_id);
383     goto out;
384   }
385
386   channel_id = silc_id_id2str(id_cache->id, SILC_ID_CHANNEL);
387
388   buffer = silc_command_encode_payload_va(SILC_COMMAND_INVITE, 2,
389                                           client_id, SILC_ID_CLIENT_LEN,
390                                           channel_id, SILC_ID_CHANNEL_LEN);
391   silc_client_packet_send(cmd->client, cmd->sock,
392                           SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
393                           buffer->data, buffer->len, TRUE);
394   silc_buffer_free(buffer);
395
396   silc_say(cmd->client, "Inviting %s to channel %s", cmd->argv[1], 
397            cmd->argv[2]);
398
399  out:
400   silc_client_command_free(cmd);
401 #undef CIDC
402 #undef CHIDC
403 }
404
405 /* Command QUIT. Closes connection with current server. */
406  
407 SILC_CLIENT_CMD_FUNC(quit)
408 {
409   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
410   SilcBuffer buffer;
411
412   if (!cmd->client->current_win->sock) {
413     SILC_NOT_CONNECTED(cmd->client);
414     goto out;
415   }
416
417   buffer = silc_command_encode_payload(SILC_COMMAND_QUIT, cmd->argc - 1, 
418                                        ++cmd->argv, ++cmd->argv_lens,
419                                        ++cmd->argv_types);
420   silc_client_packet_send(cmd->client, cmd->client->current_win->sock,
421                           SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
422                           buffer->data, buffer->len, TRUE);
423   silc_buffer_free(buffer);
424   cmd->argv--;
425   cmd->argv_lens--;
426   cmd->argv_types--;
427
428   /* Close connection */
429   silc_client_close_connection(cmd->client, cmd->sock);
430   cmd->client->screen->bottom_line->connection = NULL;
431   silc_screen_print_bottom_line(cmd->client->screen, 0);
432
433 out:
434   silc_client_command_free(cmd);
435 }
436
437 SILC_CLIENT_CMD_FUNC(kill)
438 {
439 }
440
441 SILC_CLIENT_CMD_FUNC(info)
442 {
443 }
444
445 SILC_CLIENT_CMD_FUNC(connect)
446 {
447 }
448
449 /* Command PING. Sends ping to server. This is used to test the 
450    communication channel. */
451
452 SILC_CLIENT_CMD_FUNC(ping)
453 {
454   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
455   SilcClientWindow win = NULL;
456   SilcBuffer buffer;
457   void *id;
458   int i;
459   char *name = NULL;
460
461   if (!cmd->sock) {
462     SILC_NOT_CONNECTED(cmd->client);
463     goto out;
464   }
465
466   win = (SilcClientWindow)cmd->sock->user_data;
467
468   if (cmd->argc == 1 || !strcmp(cmd->argv[1], win->remote_host))
469     name = strdup(win->remote_host);
470
471   id = silc_id_str2id(win->remote_id_data, SILC_ID_SERVER);
472
473   /* Send the command */
474   buffer = silc_command_encode_payload_va(SILC_COMMAND_PING, 1, 
475                                           win->remote_id_data, 
476                                           SILC_ID_SERVER_LEN);
477   silc_client_packet_send(cmd->client, cmd->client->current_win->sock,
478                           SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
479                           buffer->data, buffer->len, TRUE);
480   silc_buffer_free(buffer);
481
482   /* Start counting time */
483   for (i = 0; i < win->ping_count; i++) {
484     if (win->ping[i].dest_id == NULL) {
485       win->ping[i].start_time = time(NULL);
486       win->ping[i].dest_id = id;
487       win->ping[i].dest_name = name;
488       win->ping_count++;
489       break;
490     }
491   }
492   if (i >= win->ping_count) {
493     i = win->ping_count;
494     win->ping = silc_realloc(win->ping, sizeof(*win->ping) * (i + 1));
495     win->ping[i].start_time = time(NULL);
496     win->ping[i].dest_id = id;
497     win->ping[i].dest_name = name;
498     win->ping_count++;
499   }
500   
501  out:
502   silc_client_command_free(cmd);
503 }
504
505 SILC_CLIENT_CMD_FUNC(oper)
506 {
507 }
508
509 SILC_CLIENT_CMD_FUNC(trace)
510 {
511 }
512
513 SILC_CLIENT_CMD_FUNC(notice)
514 {
515 }
516
517 /* Command JOIN. Joins to a channel. */
518
519 SILC_CLIENT_CMD_FUNC(join)
520 {
521   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
522   SilcClientWindow win = NULL;
523   SilcIDCache *id_cache = NULL;
524   SilcBuffer buffer;
525
526 #define CIDC(x) win->channel_id_cache[(x) - 32]
527 #define CIDCC(x) win->channel_id_cache_count[(x) - 32]
528
529   if (cmd->argc < 2) {
530     /* Show channels currently joined to */
531     if (!cmd->client->current_win->sock) {
532       silc_say(cmd->client, "No current channel for this window");
533       SILC_NOT_CONNECTED(cmd->client);
534       goto out;
535
536     }
537
538     goto out;
539   }
540
541   if (!cmd->client->current_win->sock) {
542     SILC_NOT_CONNECTED(cmd->client);
543     goto out;
544   }
545
546   win = (SilcClientWindow)cmd->sock->user_data;
547
548   /* See if we have joined to the requested channel already */
549   silc_idcache_find_by_data(CIDC(cmd->argv[1][0]), CIDCC(cmd->argv[1][0]), 
550                             cmd->argv[1], &id_cache);
551
552   if (id_cache) {
553     silc_say(cmd->client, "You are talking to channel %s", cmd->argv[1]);
554     win->current_channel = (SilcChannelEntry)id_cache->context;
555     cmd->client->screen->bottom_line->channel = cmd->argv[1];
556     silc_screen_print_bottom_line(cmd->client->screen, 0);
557     goto out;
558   }
559
560   /* Send JOIN command to the server */
561   buffer = silc_command_encode_payload(SILC_COMMAND_JOIN,
562                                        cmd->argc - 1, ++cmd->argv,
563                                        ++cmd->argv_lens, ++cmd->argv_types);
564   silc_client_packet_send(cmd->client, cmd->client->current_win->sock,
565                           SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
566                           buffer->data, buffer->len, TRUE);
567   silc_buffer_free(buffer);
568   cmd->argv--;
569   cmd->argv_lens--;
570   cmd->argv_types--;
571
572  out:
573   silc_client_command_free(cmd);
574 #undef CIDC
575 #undef CIDCC
576 }
577
578 SILC_CLIENT_CMD_FUNC(motd)
579 {
580 }
581
582 SILC_CLIENT_CMD_FUNC(umode)
583 {
584 }
585
586 SILC_CLIENT_CMD_FUNC(cmode)
587 {
588 }
589
590 SILC_CLIENT_CMD_FUNC(kick)
591 {
592 }
593
594 SILC_CLIENT_CMD_FUNC(restart)
595 {
596 }
597  
598 SILC_CLIENT_CMD_FUNC(close)
599 {
600 }
601  
602 SILC_CLIENT_CMD_FUNC(die)
603 {
604 }
605  
606 SILC_CLIENT_CMD_FUNC(silcoper)
607 {
608 }
609
610 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
611
612 SILC_CLIENT_CMD_FUNC(leave)
613 {
614   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
615   SilcClientWindow win = NULL;
616   SilcIDCache *id_cache = NULL;
617   SilcBuffer buffer;
618   unsigned char *id_string;
619   char *name;
620
621 #define CIDC(x) win->channel_id_cache[(x) - 32]
622 #define CIDCC(x) win->channel_id_cache_count[(x) - 32]
623
624   if (cmd->argc != 2) {
625     silc_say(cmd->client, "Usage: /LEAVE <channel>");
626     goto out;
627   }
628
629   if (!cmd->client->current_win->sock) {
630     SILC_NOT_CONNECTED(cmd->client);
631     goto out;
632   }
633
634   win = (SilcClientWindow)cmd->sock->user_data;
635
636   if (cmd->argv[1][0] == '*')
637     name = win->current_channel->channel_name;
638   else
639     name = cmd->argv[1];
640
641   if (!win->current_channel) {
642     silc_say(cmd->client, "You are not on that channel", name);
643     goto out;
644   }
645
646   /* Get the Channel ID of the channel */
647   silc_idcache_find_by_data(CIDC(name[0]), CIDCC(name[0]), name, &id_cache);
648   if (!id_cache) {
649     silc_say(cmd->client, "You are not on that channel", name);
650     goto out;
651   }
652
653   /* Send LEAVE command to the server */
654   id_string = silc_id_id2str(id_cache->id, SILC_ID_CHANNEL);
655   buffer = silc_command_encode_payload_va(SILC_COMMAND_LEAVE, 1, 
656                                           id_string, SILC_ID_CHANNEL_LEN);
657   silc_client_packet_send(cmd->client, cmd->client->current_win->sock,
658                           SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
659                           buffer->data, buffer->len, TRUE);
660   silc_buffer_free(buffer);
661
662   /* We won't talk anymore on this channel */
663   silc_say(cmd->client, "You have left channel %s", name);
664   cmd->client->screen->bottom_line->channel = NULL;
665   silc_screen_print_bottom_line(cmd->client->screen, 0);
666
667   silc_idcache_del_by_id(CIDC(name[0]), CIDCC(name[0]),
668                          SILC_ID_CHANNEL, win->current_channel->id);
669   silc_free(win->current_channel->channel_name);
670   silc_free(win->current_channel->id);
671   silc_free(win->current_channel->key);
672   silc_cipher_free(win->current_channel->channel_key);
673   silc_free(win->current_channel);
674   win->current_channel = NULL;
675   silc_free(id_string);
676
677  out:
678   silc_client_command_free(cmd);
679 #undef CIDC
680 #undef CIDCC
681 }
682
683 /* Command NAMES. Requests the names of the clients joined on requested
684    channel. */
685
686 SILC_CLIENT_CMD_FUNC(names)
687 {
688   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
689   SilcClientWindow win = NULL;
690   SilcIDCache *id_cache = NULL;
691   SilcBuffer buffer;
692   char *name;
693   unsigned char *id_string;
694
695 #define CIDC(x) win->channel_id_cache[(x) - 32]
696 #define CIDCC(x) win->channel_id_cache_count[(x) - 32]
697
698   if (cmd->argc != 2) {
699     silc_say(cmd->client, "Usage: /NAMES <channel>");
700     goto out;
701   }
702
703   if (!cmd->client->current_win->sock) {
704     SILC_NOT_CONNECTED(cmd->client);
705     goto out;
706   }
707
708   win = (SilcClientWindow)cmd->sock->user_data;
709
710   if (cmd->argv[1][0] == '*')
711     name = win->current_channel->channel_name;
712   else
713     name = cmd->argv[1];
714
715   /* Get the Channel ID of the channel */
716   silc_idcache_find_by_data(CIDC(name[0]), CIDCC(name[0]), name, &id_cache);
717   if (!id_cache) {
718     /* XXX should resolve the channel ID; LIST command */
719     silc_say(cmd->client, "You are not on that channel", name);
720     goto out;
721   }
722
723   /* Send NAMES command to the server */
724   id_string = silc_id_id2str(id_cache->id, SILC_ID_CHANNEL);
725   buffer = silc_command_encode_payload_va(SILC_COMMAND_NAMES, 1, 
726                                           id_string, SILC_ID_CHANNEL_LEN);
727   silc_client_packet_send(cmd->client, cmd->client->current_win->sock,
728                           SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
729                           buffer->data, buffer->len, TRUE);
730   silc_buffer_free(buffer);
731   silc_free(id_string);
732
733  out:
734   silc_client_command_free(cmd);
735 #undef CIDC
736 #undef CIDCC
737 }
738
739 /*
740  * Local commands
741  */
742
743 /* HELP command. This is local command and shows help on SILC */
744
745 SILC_CLIENT_CMD_FUNC(help)
746 {
747
748 }
749
750 /* CLEAR command. This is local command and clears current output window */
751
752 SILC_CLIENT_CMD_FUNC(clear)
753 {
754   SilcClient client = (SilcClient)context;
755
756   assert(client->current_win != NULL);
757   wclear((WINDOW *)client->current_win->screen);
758   wrefresh((WINDOW *)client->current_win->screen);
759 }
760
761 /* VERSION command. This is local command and shows version of the client */
762
763 SILC_CLIENT_CMD_FUNC(version)
764 {
765
766 }
767
768 /* Command MSG. Sends private message to user or list of users. */
769 /* XXX supports only one destination */
770
771 SILC_CLIENT_CMD_FUNC(msg)
772 {
773   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
774   SilcClientWindow win = NULL;
775   SilcClient client = cmd->client;
776   SilcIDCache *id_cache;
777
778 #define CIDC(x) win->client_id_cache[(x) - 32], \
779                 win->client_id_cache_count[(x) - 32]
780
781   if (cmd->argc < 3) {
782     silc_say(cmd->client, "Usage: /MSG <nickname> <message>");
783     goto out;
784   }
785
786   if (!cmd->client->current_win->sock) {
787     SILC_NOT_CONNECTED(cmd->client);
788     goto out;
789   }
790
791   win = (SilcClientWindow)cmd->sock->user_data;
792
793   /* Find ID from cache */
794   if (silc_idcache_find_by_data(CIDC(cmd->argv[1][0]), cmd->argv[1], 
795                                 &id_cache) == FALSE) {
796     SilcClientCommandContext ctx;
797     char ident[512];
798
799     SILC_LOG_DEBUG(("Requesting Client ID from server"));
800
801     /* No ID found. Do query from the server. The query is done by 
802        sending simple IDENTIFY command to the server. */
803     ctx = silc_calloc(1, sizeof(*ctx));
804     ctx->client = client;
805     ctx->sock = cmd->sock;
806     memset(ident, 0, sizeof(ident));
807     snprintf(ident, sizeof(ident), "/IDENTIFY %s", cmd->argv[1]);
808     silc_client_parse_command_line(ident, &ctx->argv, &ctx->argv_lens, 
809                                    &ctx->argv_types, &ctx->argc, 2);
810     silc_client_command_identify(ctx);
811
812     /* Mark this command to be pending command and to be executed after
813        we have received the IDENTIFY reply from server. */
814     silc_client_command_pending(SILC_COMMAND_IDENTIFY, 
815                                 silc_client_command_msg, context);
816     return;
817   }
818
819   /* Display the message for our eyes. */
820   silc_print(client, "-> *%s* %s", cmd->argv[1], cmd->argv[2]);
821
822   /* Send the private message */
823   silc_client_packet_send_private_message(client, cmd->sock, id_cache->context,
824                                           cmd->argv[2], cmd->argv_lens[2],
825                                           TRUE);
826
827  out:
828   silc_client_command_free(cmd);
829 #undef CIDC
830 }
831
832 SILC_CLIENT_CMD_FUNC(away)
833 {
834 }