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