4d91bd9b68e13ef38a736ad9d23d4e4a4b3933e1
[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.3  2000/07/03 05:49:49  priikone
24  *      Implemented LEAVE command.  Minor bug fixes.
25  *
26  * Revision 1.2  2000/06/27 19:38:40  priikone
27  *      Added missing goto flag.
28  *
29  * Revision 1.1.1.1  2000/06/27 11:36:56  priikone
30  *      Importet from internal CVS/Added Log headers.
31  *
32  *
33  */
34
35 #include "clientincludes.h"
36
37 /* Client command list. */
38 SilcClientCommand silc_command_list[] =
39 {
40   SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", SILC_CF_LAG | SILC_CF_REG, 3),
41   SILC_CLIENT_CMD(whowas, WHOWAS, "WHOWAS", SILC_CF_LAG | SILC_CF_REG, 3),
42   SILC_CLIENT_CMD(identify, IDENTIFY, "IDENTIFY", 
43                   SILC_CF_LAG | SILC_CF_REG, 3),
44   SILC_CLIENT_CMD(nick, NICK, "NICK", SILC_CF_LAG | SILC_CF_REG, 2),
45   SILC_CLIENT_CMD(list, LIST, "LIST", SILC_CF_LAG | SILC_CF_REG, 2),
46   SILC_CLIENT_CMD(topic, TOPIC, "TOPIC", SILC_CF_LAG | SILC_CF_REG, 2),
47   SILC_CLIENT_CMD(invite, INVITE, "INVITE", SILC_CF_LAG | SILC_CF_REG, 2),
48   SILC_CLIENT_CMD(quit, QUIT, "QUIT", SILC_CF_LAG | SILC_CF_REG, 1),
49   SILC_CLIENT_CMD(kill, KILL, "KILL", 
50                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
51   SILC_CLIENT_CMD(info, INFO, "INFO", SILC_CF_LAG | SILC_CF_REG, 2),
52   SILC_CLIENT_CMD(connect, CONNECT, "CONNECT",
53                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
54   SILC_CLIENT_CMD(ping, PING, "PING", SILC_CF_LAG | SILC_CF_REG, 2),
55   SILC_CLIENT_CMD(oper, OPER, "OPER",
56                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
57   SILC_CLIENT_CMD(join, JOIN, "JOIN", SILC_CF_LAG | SILC_CF_REG, 2),
58   SILC_CLIENT_CMD(motd, MOTD, "MOTD", SILC_CF_LAG | SILC_CF_REG, 2),
59   SILC_CLIENT_CMD(umode, UMODE, "UMODE", SILC_CF_LAG | SILC_CF_REG, 2),
60   SILC_CLIENT_CMD(cmode, CMODE, "CMODE", SILC_CF_LAG | SILC_CF_REG, 2),
61   SILC_CLIENT_CMD(kick, KICK, "KICK", SILC_CF_LAG | SILC_CF_REG, 2),
62   SILC_CLIENT_CMD(restart, RESTART, "RESTART",
63                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
64   SILC_CLIENT_CMD(close, CLOSE, "CLOSE",
65                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
66   SILC_CLIENT_CMD(die, DIE, "DIE",
67                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
68   SILC_CLIENT_CMD(silcoper, SILCOPER, "SILOPER",
69                   SILC_CF_LAG | SILC_CF_REG | SILC_CF_SILC_OPER, 2),
70   SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", SILC_CF_LAG | SILC_CF_REG, 2),
71   SILC_CLIENT_CMD(names, NAMES, "NAMES", SILC_CF_LAG | SILC_CF_REG, 2),
72
73   /*
74    * Local. client specific commands
75    */
76   SILC_CLIENT_CMD(help, HELP, "HELP", SILC_CF_NONE, 2),
77   SILC_CLIENT_CMD(clear, CLEAR, "CLEAR", SILC_CF_NONE, 1),
78   SILC_CLIENT_CMD(version, VERSION, "VERSION", SILC_CF_NONE, 1),
79   SILC_CLIENT_CMD(server, SERVER, "SERVER", SILC_CF_NONE, 2),
80   SILC_CLIENT_CMD(msg, MSG, "MSG", SILC_CF_NONE, 3),
81   SILC_CLIENT_CMD(away, AWAY, "AWAY", SILC_CF_NONE, 2),
82
83   { NULL, 0, NULL, 0},
84 };
85
86 #define SILC_NOT_CONNECTED(x) \
87   silc_say((x), "You are not connected to a server, use /SERVER to connect");
88
89 /* List of pending commands. */
90 SilcClientCommandPending *silc_command_pending = NULL;
91
92 /* Add new pending command to the list of pending commands. Currently
93    pending commands are executed from command replies, thus we can
94    execute any command after receiving some specific command reply.
95
96    The argument `reply_cmd' is the command reply from where the callback
97    function is to be called, thus, it IS NOT the command to be executed.
98
99    XXX: If needed in the future this support may be extended for
100    commands as well, when any command could be executed after executing
101    some specific command. */
102
103 void silc_client_command_pending(SilcCommand reply_cmd,
104                                  SilcClientCommandCallback callback,
105                                  void *context)
106 {
107   SilcClientCommandPending *reply, *r;
108
109   reply = silc_calloc(1, sizeof(*reply));
110   reply->reply_cmd = reply_cmd;
111   reply->context = context;
112   reply->callback = callback;
113
114   if (silc_command_pending == NULL) {
115     silc_command_pending = reply;
116     return;
117   }
118
119   for (r = silc_command_pending; r; r = r->next) {
120     if (r->next == NULL) {
121       r->next = reply;
122       break;
123     }
124   }
125 }
126
127 /* Deletes pending command by reply command type. */
128
129 void silc_client_command_pending_del(SilcCommand reply_cmd)
130 {
131   SilcClientCommandPending *r, *tmp;
132   
133   if (silc_command_pending) {
134     if (silc_command_pending->reply_cmd == reply_cmd) {
135       silc_free(silc_command_pending);
136       silc_command_pending = NULL;
137       return;
138     }
139
140     for (r = silc_command_pending; r; r = r->next) {
141       if (r->next && r->next->reply_cmd == reply_cmd) {
142         tmp = r->next;
143         r->next = r->next->next;
144         silc_free(tmp);
145         break;
146       }
147     }
148   }
149 }
150
151 /* Free command context and its internals */
152
153 static void silc_client_command_free(SilcClientCommandContext cmd)
154 {
155   int i;
156
157   if (cmd) {
158     for (i = 0; i < cmd->argc; i++)
159       silc_free(cmd->argv[i]);
160     silc_free(cmd);
161   }
162 }
163
164 /* Command WHOIS. This command is used to query information about 
165    specific user. */
166
167 SILC_CLIENT_CMD_FUNC(whois)
168 {
169   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
170   SilcBuffer buffer;
171
172   if (cmd->argc < 2 || cmd->argc > 3) {
173     silc_say(cmd->client, "Usage: /WHOIS <nickname>[@<server>] [<count>]");
174     goto out;
175   }
176
177   if (!cmd->client->current_win->sock) {
178     SILC_NOT_CONNECTED(cmd->client);
179     goto out;
180   }
181
182   buffer = silc_command_encode_payload(SILC_COMMAND_WHOIS,
183                                        cmd->argc - 1, ++cmd->argv,
184                                        ++cmd->argv_lens, ++cmd->argv_types);
185   silc_client_packet_send(cmd->client, cmd->client->current_win->sock,
186                           SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
187                           buffer->data, buffer->len, TRUE);
188   silc_buffer_free(buffer);
189   cmd->argv--;
190   cmd->argv_lens--;
191   cmd->argv_types--;
192
193  out:
194   silc_client_command_free(cmd);
195 }
196
197 SILC_CLIENT_CMD_FUNC(whowas)
198 {
199 }
200
201 /* Command IDENTIFY. This command is used to query information about 
202    specific user, especially ID's. */
203
204 SILC_CLIENT_CMD_FUNC(identify)
205 {
206   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
207   SilcBuffer buffer;
208
209   if (cmd->argc < 2 || cmd->argc > 3) {
210     silc_say(cmd->client, "Usage: /IDENTIFY <nickname>[@<server>] [<count>]");
211     goto out;
212   }
213
214   if (!cmd->client->current_win->sock) {
215     SILC_NOT_CONNECTED(cmd->client);
216     goto out;
217   }
218
219   buffer = silc_command_encode_payload(SILC_COMMAND_IDENTIFY,
220                                        cmd->argc - 1, ++cmd->argv,
221                                        ++cmd->argv_lens, ++cmd->argv_types);
222   silc_client_packet_send(cmd->client, cmd->client->current_win->sock,
223                           SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
224                           buffer->data, buffer->len, TRUE);
225   silc_buffer_free(buffer);
226   cmd->argv--;
227   cmd->argv_lens--;
228   cmd->argv_types--;
229
230  out:
231   silc_client_command_free(cmd);
232 }
233
234 /* Command NICK. Shows current nickname/sets new nickname on current
235    window. */
236
237 SILC_CLIENT_CMD_FUNC(nick)
238 {
239   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
240   SilcClientWindow win = NULL;
241   SilcBuffer buffer;
242
243   if (!cmd->sock) {
244     SILC_NOT_CONNECTED(cmd->client);
245     goto out;
246   }
247
248   /* Show current nickname */
249   if (cmd->argc < 2) {
250     if (cmd->sock) {
251       silc_say(cmd->client, "Your nickname is %s on server %s", 
252                win->nickname, win->remote_host);
253     } else {
254       silc_say(cmd->client, "Your nickname is %s", win->nickname);
255     }
256     goto out;
257   }
258
259   win = (SilcClientWindow)cmd->sock->user_data;
260
261   /* Set new nickname */
262   buffer = silc_command_encode_payload(SILC_COMMAND_NICK,
263                                        cmd->argc - 1, ++cmd->argv,
264                                        ++cmd->argv_lens, ++cmd->argv_types);
265   silc_client_packet_send(cmd->client, cmd->sock,
266                           SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
267                           buffer->data, buffer->len, TRUE);
268   silc_buffer_free(buffer);
269   cmd->argv--;
270   cmd->argv_lens--;
271   cmd->argv_types--;
272   if (win->nickname)
273     silc_free(win->nickname);
274   win->nickname = strdup(cmd->argv[1]);
275
276  out:
277   silc_client_command_free(cmd);
278 }
279
280 /* Command SERVER. Connects to remote SILC server. This is local command. */
281
282 SILC_CLIENT_CMD_FUNC(server)
283 {
284   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
285   int len, port;
286   char *hostname;
287
288   if (cmd->argc < 2) {
289     /* Show current servers */
290     if (!cmd->client->current_win->sock) {
291       silc_say(cmd->client, "You are not connected to any server");
292       silc_say(cmd->client, "Usage: /SERVER [<server>[:<port>]]");
293       goto out;
294     }
295
296     goto out;
297   }
298
299   /* See if port is included and then extract it */
300   if (strchr(cmd->argv[1], ':')) {
301     len = strcspn(cmd->argv[1], ":");
302     hostname = silc_calloc(len + 1, sizeof(char));
303     memcpy(hostname, cmd->argv[1], len);
304     port = atoi(cmd->argv[1] + 1 + len);
305   } else {
306     hostname = cmd->argv[1];
307     port = 334;
308   }
309
310   /* Connect asynchronously to not to block user interface */
311   silc_client_connect_to_server(cmd->client, port, hostname);
312
313  out:
314   silc_client_command_free(cmd);
315 }
316
317 SILC_CLIENT_CMD_FUNC(list)
318 {
319 }
320
321 SILC_CLIENT_CMD_FUNC(topic)
322 {
323 }
324
325 SILC_CLIENT_CMD_FUNC(invite)
326 {
327 }
328
329 /* Command QUIT. Closes connection with current server. */
330  
331 SILC_CLIENT_CMD_FUNC(quit)
332 {
333   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
334   SilcBuffer buffer;
335
336   if (!cmd->client->current_win->sock) {
337     SILC_NOT_CONNECTED(cmd->client);
338     goto out;
339   }
340
341   buffer = silc_command_encode_payload(SILC_COMMAND_QUIT, cmd->argc - 1, 
342                                        ++cmd->argv, ++cmd->argv_lens,
343                                        ++cmd->argv_types);
344   silc_client_packet_send(cmd->client, cmd->client->current_win->sock,
345                           SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
346                           buffer->data, buffer->len, TRUE);
347   silc_buffer_free(buffer);
348   cmd->argv--;
349   cmd->argv_lens--;
350   cmd->argv_types--;
351
352   /* Close connection */
353   silc_client_close_connection(cmd->client, cmd->sock);
354   cmd->client->screen->bottom_line->connection = NULL;
355   silc_screen_print_bottom_line(cmd->client->screen, 0);
356
357 out:
358   silc_client_command_free(cmd);
359 }
360
361 SILC_CLIENT_CMD_FUNC(kill)
362 {
363 }
364
365 SILC_CLIENT_CMD_FUNC(info)
366 {
367 }
368
369 SILC_CLIENT_CMD_FUNC(connect)
370 {
371 }
372
373 SILC_CLIENT_CMD_FUNC(ping)
374 {
375 }
376
377 SILC_CLIENT_CMD_FUNC(oper)
378 {
379 }
380
381 SILC_CLIENT_CMD_FUNC(trace)
382 {
383 }
384
385 SILC_CLIENT_CMD_FUNC(notice)
386 {
387 }
388
389 /* Command JOIN. Joins to a channel. */
390
391 SILC_CLIENT_CMD_FUNC(join)
392 {
393   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
394   SilcClientWindow win = NULL;
395   SilcIDCache *id_cache = NULL;
396   SilcBuffer buffer;
397
398 #define CIDC(x) win->channel_id_cache[(x) - 32]
399 #define CIDCC(x) win->channel_id_cache_count[(x) - 32]
400
401   if (cmd->argc < 2) {
402     /* Show channels currently joined to */
403     if (!cmd->client->current_win->sock) {
404       silc_say(cmd->client, "No current channel for this window");
405       SILC_NOT_CONNECTED(cmd->client);
406       goto out;
407
408     }
409
410     goto out;
411   }
412
413   if (!cmd->client->current_win->sock) {
414     SILC_NOT_CONNECTED(cmd->client);
415     goto out;
416   }
417
418   win = (SilcClientWindow)cmd->sock->user_data;
419
420   /* See if we have joined to the requested channel already */
421   silc_idcache_find_by_data(CIDC(cmd->argv[1][0]), CIDCC(cmd->argv[1][0]), 
422                             cmd->argv[1], &id_cache);
423
424   if (id_cache) {
425     silc_say(cmd->client, "You are talking to channel %s", cmd->argv[1]);
426     win->current_channel = (SilcChannelEntry)id_cache->context;
427     cmd->client->screen->bottom_line->channel = cmd->argv[1];
428     silc_screen_print_bottom_line(cmd->client->screen, 0);
429     goto out;
430   }
431
432   /* Send JOIN command to the server */
433   buffer = silc_command_encode_payload(SILC_COMMAND_JOIN,
434                                        cmd->argc - 1, ++cmd->argv,
435                                        ++cmd->argv_lens, ++cmd->argv_types);
436   silc_client_packet_send(cmd->client, cmd->client->current_win->sock,
437                           SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
438                           buffer->data, buffer->len, TRUE);
439   silc_buffer_free(buffer);
440   cmd->argv--;
441   cmd->argv_lens--;
442   cmd->argv_types--;
443
444  out:
445   silc_client_command_free(cmd);
446 #undef CIDC
447 #undef CIDCC
448 }
449
450 SILC_CLIENT_CMD_FUNC(motd)
451 {
452 }
453
454 SILC_CLIENT_CMD_FUNC(umode)
455 {
456 }
457
458 SILC_CLIENT_CMD_FUNC(cmode)
459 {
460 }
461
462 SILC_CLIENT_CMD_FUNC(kick)
463 {
464 }
465
466 SILC_CLIENT_CMD_FUNC(restart)
467 {
468 }
469  
470 SILC_CLIENT_CMD_FUNC(close)
471 {
472 }
473  
474 SILC_CLIENT_CMD_FUNC(die)
475 {
476 }
477  
478 SILC_CLIENT_CMD_FUNC(silcoper)
479 {
480 }
481
482 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
483
484 SILC_CLIENT_CMD_FUNC(leave)
485 {
486   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
487   SilcClientWindow win = NULL;
488   SilcIDCache *id_cache = NULL;
489   SilcBuffer buffer;
490   unsigned char *id_string;
491   char *name;
492
493 #define CIDC(x) win->channel_id_cache[(x) - 32]
494 #define CIDCC(x) win->channel_id_cache_count[(x) - 32]
495
496   if (cmd->argc != 2) {
497     silc_say(cmd->client, "Usage: /LEAVE <channel>");
498     goto out;
499   }
500
501   if (!cmd->client->current_win->sock) {
502     SILC_NOT_CONNECTED(cmd->client);
503     goto out;
504   }
505
506   win = (SilcClientWindow)cmd->sock->user_data;
507
508   if (!win->current_channel) {
509     silc_say(cmd->client, "You are not on that channel", name);
510     goto out;
511   }
512
513   if (cmd->argv[1][0] == '*')
514     name = win->current_channel->channel_name;
515   else
516     name = cmd->argv[1];
517
518   /* Get the Channel ID of the channel */
519   silc_idcache_find_by_data(CIDC(name[0]), CIDCC(name[0]), name, &id_cache);
520   if (!id_cache) {
521     silc_say(cmd->client, "You are not on that channel", name);
522     goto out;
523   }
524
525   /* Send LEAVE command to the server */
526   id_string = silc_id_id2str(id_cache->id, SILC_ID_CHANNEL);
527   buffer = silc_command_encode_payload_va(SILC_COMMAND_LEAVE, 1, 
528                                           id_string, SILC_ID_CHANNEL_LEN);
529   silc_client_packet_send(cmd->client, cmd->client->current_win->sock,
530                           SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
531                           buffer->data, buffer->len, TRUE);
532   silc_buffer_free(buffer);
533
534   /* We won't talk anymore on this channel */
535   silc_say(cmd->client, "You have left channel %s", name);
536   cmd->client->screen->bottom_line->channel = NULL;
537   silc_screen_print_bottom_line(cmd->client->screen, 0);
538
539   silc_idcache_del_by_id(CIDC(name[0]), CIDCC(name[0]),
540                          SILC_ID_CHANNEL, win->current_channel->id);
541   silc_free(win->current_channel->channel_name);
542   silc_free(win->current_channel->id);
543   silc_free(win->current_channel->key);
544   silc_cipher_free(win->current_channel->channel_key);
545   silc_free(win->current_channel);
546   win->current_channel = NULL;
547
548  out:
549   silc_client_command_free(cmd);
550 #undef CIDC
551 #undef CIDCC
552 }
553
554 SILC_CLIENT_CMD_FUNC(names)
555 {
556 }
557
558 /*
559  * Local commands
560  */
561
562 /* HELP command. This is local command and shows help on SILC */
563
564 SILC_CLIENT_CMD_FUNC(help)
565 {
566
567 }
568
569 /* CLEAR command. This is local command and clears current output window */
570
571 SILC_CLIENT_CMD_FUNC(clear)
572 {
573   SilcClient client = (SilcClient)context;
574
575   assert(client->current_win != NULL);
576   wclear((WINDOW *)client->current_win->screen);
577   wrefresh((WINDOW *)client->current_win->screen);
578 }
579
580 /* VERSION command. This is local command and shows version of the client */
581
582 SILC_CLIENT_CMD_FUNC(version)
583 {
584
585 }
586
587 /* Command MSG. Sends private message to user or list of users. */
588 /* XXX supports only one destination */
589
590 SILC_CLIENT_CMD_FUNC(msg)
591 {
592   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
593   SilcClientWindow win = NULL;
594   SilcClient client = cmd->client;
595   SilcBuffer buffer;
596   SilcIDCache *id_cache;
597   unsigned int nick_len;
598
599   if (cmd->argc < 3) {
600     silc_say(cmd->client, "Usage: /MSG <nickname> <message>");
601     goto out;
602   }
603
604   if (!cmd->client->current_win->sock) {
605     SILC_NOT_CONNECTED(cmd->client);
606     goto out;
607   }
608
609   win = (SilcClientWindow)cmd->sock->user_data;
610
611 #define CIDC(x) win->client_id_cache[(x) - 32], \
612                 win->client_id_cache_count[(x) - 32]
613
614   /* Find ID from cache */
615   if (silc_idcache_find_by_data(CIDC(cmd->argv[1][0]), cmd->argv[1], 
616                                 &id_cache) == FALSE) {
617     SilcClientCommandContext ctx;
618     char ident[512];
619
620     SILC_LOG_DEBUG(("Requesting Client ID from server"));
621
622     /* No ID found. Do query from the server. The query is done by 
623        sending simple IDENTIFY command to the server. */
624     ctx = silc_calloc(1, sizeof(*ctx));
625     ctx->client = client;
626     ctx->sock = cmd->sock;
627     memset(ident, 0, sizeof(ident));
628     snprintf(ident, sizeof(ident), "/IDENTIFY %s", cmd->argv[1]);
629     silc_client_parse_command_line(ident, &ctx->argv, &ctx->argv_lens, 
630                                    &ctx->argv_types, &ctx->argc, 2);
631     silc_client_command_identify(ctx);
632
633     /* Mark this command to be pending command and to be executed after
634        we have received the IDENTIFY reply from server. */
635     silc_client_command_pending(SILC_COMMAND_IDENTIFY, 
636                                 silc_client_command_msg, context);
637     return;
638   }
639
640   /* Display the message for our eyes. */
641   silc_print(client, "-> *%s* %s", cmd->argv[1], cmd->argv[2]);
642
643   /* Send the private message */
644   silc_client_packet_send_private_message(client, cmd->sock, id_cache->context,
645                                           cmd->argv[2], cmd->argv_lens[2],
646                                           TRUE);
647
648  out:
649   silc_client_command_free(cmd);
650 #undef CIDC
651 }
652
653 SILC_CLIENT_CMD_FUNC(away)
654 {
655 }