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