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