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