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