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