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