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