Integer type name change.
[silc.git] / apps / silc / silc.c
1 /*
2
3   silc.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 /* $Id$ */
21
22 #include "clientincludes.h"
23 #include "version.h"
24
25 /* Static function prototypes */
26 static int silc_client_bad_keys(unsigned char key);
27 static void silc_client_clear_input(SilcClientInternal app);
28 static void silc_client_process_message(SilcClientInternal app);
29 static char *silc_client_parse_command(unsigned char *buffer);
30
31 void silc_client_create_main_window(SilcClientInternal app);
32
33 /* Static task callback prototypes */
34 SILC_TASK_CALLBACK(silc_client_update_clock);
35 SILC_TASK_CALLBACK(silc_client_run_commands);
36 SILC_TASK_CALLBACK(silc_client_process_key_press);
37
38 /* Long command line options */
39 static struct option long_opts[] = 
40 {
41   /* Generic options */
42   { "server", 1, NULL, 's' },
43   { "port", 1, NULL, 'p' },
44   { "nickname", 1, NULL, 'n' },
45   { "channel", 1, NULL, 'c' },
46   { "cipher", 1, NULL, 'r' },
47   { "public-key", 1, NULL, 'b' },
48   { "private-key", 1, NULL, 'k' },
49   { "config-file", 1, NULL, 'f' },
50   { "no-silcrc", 0, NULL, 'q' },
51   { "debug", 0, NULL, 'd' },
52   { "help", 0, NULL, 'h' },
53   { "version", 0, NULL, 'V' },
54   { "list-ciphers", 0, NULL, 1 },
55   { "list-hash-funcs", 0, NULL, 2 },
56   { "list-pkcs", 0, NULL, 3 },
57
58   /* Key management options */
59   { "create-key-pair", 0, NULL, 'C' },
60   { "pkcs", 1, NULL, 10 },
61   { "bits", 1, NULL, 11 },
62   { "show-key", 1, NULL, 'S' },
63
64   { NULL, 0, NULL, 0 }
65 };
66
67 /* Command line option variables */
68 static char *opt_server = NULL;
69 static int opt_port = 0;
70 static char *opt_nickname = NULL;
71 static char *opt_channel = NULL;
72 static char *opt_cipher = NULL;
73 static char *opt_public_key = NULL;
74 static char *opt_private_key = NULL;
75 static char *opt_config_file = NULL;
76 static bool opt_no_silcrc = FALSE;
77
78 static bool opt_create_keypair = FALSE;
79 static bool opt_show_key = FALSE;
80 static char *opt_pkcs = NULL;
81 static char *opt_keyfile = NULL;
82 static int opt_bits = 0;
83
84 /* SILC Client operations */
85 extern SilcClientOperations ops;
86
87 /* Prints out the usage of silc client */
88
89 void usage()
90 {
91   printf("\
92 Usage: silc [options]\n\
93 \n\
94   Generic Options:\n\
95   -s, --server=HOST            Open connection to server HOST\n\
96   -p, --port=PORT              Set PORT as default port to connect\n\
97   -n, --nickname=STRING        Set default nickname on startup\n\
98   -c, --channel=STRING         Join channel on startup\n\
99   -r, --cipher=CIPHER          Use CIPHER as default cipher in SILC\n\
100   -b, --public-key=FILE        Public key used in SILC\n\
101   -k, --private-key=FILE       Private key used in SILC\n\
102   -f, --config-file=FILE       Alternate configuration file\n\
103   -q, --no-silcrc              Don't load ~/.silcrc on startup\n\
104   -d, --debug                  Enable debugging\n\
105   -h, --help                   Display this help message\n\
106   -V, --version                Display version\n\
107       --list-ciphers           List supported ciphers\n\
108       --list-hash-funcs        List supported hash functions\n\
109       --list-pkcs              List supported PKCS's\n\
110 \n\
111   Key Management Options:\n\
112   -C, --create-key-pair        Create new public key pair\n\
113       --pkcs=PKCS              Set the PKCS of the public key pair\n\
114       --bits=VALUE             Set length of the public key pair\n\
115   -S, --show-key=FILE          Show the contents of the public key\n\
116 \n");
117 }
118
119 int main(int argc, char **argv)
120 {
121   int opt, option_index = 1;
122   int ret;
123   SilcClient silc = NULL;
124   SilcClientInternal app = NULL;
125
126   silc_debug = FALSE;
127
128   if (argc > 1) 
129     {
130       while ((opt = 
131               getopt_long(argc, argv,
132                           "s:p:n:c:b:k:f:qdhVCS:",
133                           long_opts, &option_index)) != EOF)
134         {
135           switch(opt) 
136             {
137               /* 
138                * Generic options
139                */
140             case 's':
141               if (optarg)
142                 opt_server = strdup(optarg);
143               break;
144             case 'p':
145               if (optarg)
146                 opt_port = atoi(optarg);
147               break;
148             case 'n':
149               if (optarg)
150                 opt_nickname = strdup(optarg);
151               break;
152             case 'c':
153               if (optarg)
154                 opt_channel = strdup(optarg);
155               break;
156             case 'r':
157               if (optarg)
158                 opt_cipher = strdup(optarg);
159               break;
160             case 'b':
161               if (optarg)
162                 opt_public_key = strdup(optarg);
163               break;
164             case 'k':
165               if (optarg)
166                 opt_private_key = strdup(optarg);
167               break;
168             case 'f':
169               if (optarg)
170                 opt_config_file = strdup(optarg);
171               break;
172             case 'q':
173               opt_no_silcrc = TRUE;
174               break;
175             case 'd':
176               silc_debug = TRUE;
177               break;
178             case 'h':
179               usage();
180               exit(0);
181               break;
182             case 'V':
183               printf("\
184 SILC Secure Internet Live Conferencing, version %s\n", 
185                      silc_version);
186               printf("\
187 (c) 1997 - 2000 Pekka Riikonen <priikone@poseidon.pspt.fi>\n");
188               exit(0);
189               break;
190             case 1:
191               silc_client_list_ciphers();
192               exit(0);
193               break;
194             case 2:
195               silc_client_list_hash_funcs();
196               exit(0);
197               break;
198             case 3:
199               silc_client_list_pkcs();
200               exit(0);
201               break;
202
203               /*
204                * Key management options
205                */
206             case 'C':
207               opt_create_keypair = TRUE;
208               break;
209             case 10:
210               if (optarg)
211                 opt_pkcs = strdup(optarg);
212               break;
213             case 11:
214               if (optarg)
215                 opt_bits = atoi(optarg);
216               break;
217             case 'S':
218               opt_show_key = TRUE;
219               if (optarg)
220                 opt_keyfile = strdup(optarg);
221               break;
222
223             default:
224               exit(0);
225               break;
226             }
227         }
228     }
229
230   /* Init signals */
231   signal(SIGHUP, SIG_DFL);
232   signal(SIGTERM, SIG_DFL);
233   signal(SIGPIPE, SIG_IGN);
234   signal(SIGCHLD, SIG_DFL);
235   signal(SIGALRM, SIG_IGN);
236   signal(SIGQUIT, SIG_IGN);
237   signal(SIGSEGV, SIG_DFL);
238   signal(SIGBUS, SIG_DFL);
239   signal(SIGFPE, SIG_DFL);
240   //  signal(SIGINT, SIG_IGN);
241   
242 #ifdef SOCKS
243   /* Init SOCKS */
244   SOCKSinit(argv[0]);
245 #endif
246
247   if (opt_create_keypair == TRUE) {
248     /* Create new key pair and exit */
249     silc_cipher_register_default();
250     silc_pkcs_register_default();
251     silc_hash_register_default();
252     silc_hmac_register_default();
253     silc_client_create_key_pair(opt_pkcs, opt_bits, 
254                                 NULL, NULL, NULL, NULL, NULL);
255     silc_free(opt_pkcs);
256     exit(0);
257   }
258
259   if (opt_show_key == TRUE) {
260     /* Dump the key */
261     silc_cipher_register_default();
262     silc_pkcs_register_default();
263     silc_hash_register_default();
264     silc_hmac_register_default();
265     silc_client_show_key(opt_keyfile);
266     silc_free(opt_keyfile);
267     exit(0);
268   }
269
270   /* Default configuration file */
271   if (!opt_config_file)
272     opt_config_file = strdup(SILC_CLIENT_CONFIG_FILE);
273
274   /* Allocate internal application context */
275   app = silc_calloc(1, sizeof(*app));
276
277   /* Allocate new client */
278   app->client = silc = silc_client_alloc(&ops, app, silc_version_string);
279   if (!silc)
280     goto fail;
281
282   /* Read global configuration file. */
283   app->config = silc_client_config_alloc(opt_config_file);
284
285   /* XXX Read local configuration file */
286
287   /* Get user information */
288   silc->username = silc_get_username();
289   silc->hostname = silc_net_localhost();
290   silc->realname = silc_get_real_name();
291
292   /* Register all configured ciphers, PKCS and hash functions. */
293   if (app->config) {
294     app->config->client = (void *)app;
295     if (!silc_client_config_register_ciphers(app->config))
296       silc_cipher_register_default();
297     if (!silc_client_config_register_pkcs(app->config))
298       silc_pkcs_register_default();
299     if (!silc_client_config_register_hashfuncs(app->config))
300       silc_hash_register_default();
301     if (!silc_client_config_register_hmacs(app->config))
302       silc_hmac_register_default();
303   } else {
304     /* Register default ciphers, pkcs, hash funtions and hmacs. */
305     silc_cipher_register_default();
306     silc_pkcs_register_default();
307     silc_hash_register_default();
308     silc_hmac_register_default();
309   }
310
311   /* Check ~/.silc directory and public and private keys */
312   if (silc_client_check_silc_dir() == FALSE)
313     goto fail;
314
315   /* Load public and private key */
316   if (silc_client_load_keys(silc) == FALSE)
317     goto fail;
318
319   /* Initialize the client. This initializes the client library and
320      sets everything ready for silc_client_run. */
321   ret = silc_client_init(silc);
322   if (ret == FALSE)
323     goto fail;
324
325   /* Register the main task that is used in client. This receives
326      the key pressings. */
327   silc_task_register(silc->io_queue, fileno(stdin), 
328                      silc_client_process_key_press,
329                      (void *)silc, 0, 0, 
330                      SILC_TASK_FD,
331                      SILC_TASK_PRI_NORMAL);
332
333   /* Register timeout task that updates clock every minute. */
334   silc_task_register(silc->timeout_queue, 0,
335                      silc_client_update_clock,
336                      (void *)silc, 
337                      silc_client_time_til_next_min(), 0,
338                      SILC_TASK_TIMEOUT,
339                      SILC_TASK_PRI_LOW);
340
341   if (app->config && app->config->commands) {
342     /* Run user configured commands with timeout */
343     silc_task_register(silc->timeout_queue, 0,
344                        silc_client_run_commands,
345                        (void *)silc, 0, 1,
346                        SILC_TASK_TIMEOUT,
347                        SILC_TASK_PRI_LOW);
348   }
349
350   /* Allocate the input buffer used to save typed characters */
351   app->input_buffer = silc_buffer_alloc(SILC_SCREEN_INPUT_WIN_SIZE);
352   silc_buffer_pull_tail(app->input_buffer, 
353                         SILC_BUFFER_END(app->input_buffer));
354
355   /* Initialize the screen */
356   silc_client_create_main_window(app);
357   silc_screen_print_coordinates(app->screen, 0);
358
359   /* Run the client. When this returns the application will be
360      terminated. */
361   silc_client_run(silc);
362
363   /* Stop the client. This probably has been done already but it
364      doesn't hurt to do it here again. */
365   silc_client_stop(silc);
366   silc_client_free(silc);
367   
368   exit(0);
369
370  fail:
371   if (opt_config_file)
372     silc_free(opt_config_file);
373   if (app->config)
374     silc_client_config_free(app->config);
375   if (silc)
376     silc_client_free(silc);
377   exit(1);
378 }
379
380 /* Creates the main window used in SILC client. This is called always
381    at the initialization of the client. If user wants to create more
382    than one windows a new windows are always created by calling 
383    silc_client_add_window. */
384
385 void silc_client_create_main_window(SilcClientInternal app)
386 {
387   void *screen;
388
389   SILC_LOG_DEBUG(("Creating main window"));
390
391   app->screen = silc_screen_init();
392   app->screen->input_buffer = app->input_buffer->data;
393   app->screen->u_stat_line.program_name = silc_name;
394   app->screen->u_stat_line.program_version = silc_version;
395
396   /* Create the actual screen */
397   screen = (void *)silc_screen_create_output_window(app->screen);
398   silc_screen_create_input_window(app->screen);
399   silc_screen_init_upper_status_line(app->screen);
400   silc_screen_init_output_status_line(app->screen);
401
402   app->screen->bottom_line->nickname = silc_get_username();
403   silc_screen_print_bottom_line(app->screen, 0);
404 }
405
406 /* The main task on SILC client. This processes the key pressings user
407    has made. */
408
409 SILC_TASK_CALLBACK(silc_client_process_key_press)
410 {
411   SilcClient client = (SilcClient)context;
412   SilcClientInternal app = (SilcClientInternal)client->application;
413   int c;
414
415   /* There is data pending in stdin, this gets it directly */
416   c = wgetch(app->screen->input_win);
417   if (silc_client_bad_keys(c))
418     return;
419
420   SILC_LOG_DEBUG(("Pressed key: %d", c));
421
422   switch(c) {
423     /* 
424      * Special character handling
425      */
426   case KEY_UP: 
427   case KEY_DOWN:
428     break;
429   case KEY_RIGHT:
430     /* Right arrow */
431     SILC_LOG_DEBUG(("RIGHT"));
432     silc_screen_input_cursor_right(app->screen);
433     break;
434   case KEY_LEFT:
435     /* Left arrow */
436     SILC_LOG_DEBUG(("LEFT"));
437     silc_screen_input_cursor_left(app->screen);
438     break;
439   case KEY_BACKSPACE:
440   case KEY_DC:
441   case '\177':
442   case '\b':
443     /* Backspace */
444     silc_screen_input_backspace(app->screen);
445     break;
446   case '\011':
447     /* Tabulator */
448     break;
449   case KEY_IC:
450     /* Insert switch. Turns on/off insert on input window */
451     silc_screen_input_insert(app->screen);
452     break;
453   case CTRL('j'):
454   case '\r':
455     /* Enter, Return. User pressed enter we are ready to
456        process the message. */
457     silc_client_process_message(app);
458     break;
459   case CTRL('l'):
460     /* Refresh screen, Ctrl^l */
461     silc_screen_refresh_all(app->screen);
462     break;
463   case CTRL('a'):
464   case KEY_HOME:
465 #ifdef KEY_BEG
466   case KEY_BEG:
467 #endif
468     /* Beginning, Home */
469     silc_screen_input_cursor_home(app->screen);
470     break;
471   case CTRL('e'):
472 #ifdef KEY_END
473   case KEY_END:
474 #endif
475   case KEY_LL:
476     /* End */
477     silc_screen_input_cursor_end(app->screen);
478     break;
479   case CTRL('g'):
480     /* Bell, Ctrl^g */
481     beep();
482     break;
483   case KEY_DL:
484   case CTRL('u'):
485     /* Delete line */
486     silc_client_clear_input(app);
487     break;
488   default:
489     /* 
490      * Other characters 
491      */
492     if (c < 32) {
493       /* Control codes are printed as reversed */
494       c = (c & 127) | 64;
495       wattron(app->screen->input_win, A_REVERSE);
496       silc_screen_input_print(app->screen, c);
497       wattroff(app->screen->input_win, A_REVERSE);
498     } else  {
499       /* Normal character */
500       silc_screen_input_print(app->screen, c);
501     }
502   }
503
504   silc_screen_print_coordinates(app->screen, 0);
505   silc_screen_refresh_win(app->screen->input_win);
506 }
507
508 static int silc_client_bad_keys(unsigned char key)
509 {
510   /* these are explained in curses.h */
511   switch(key) {
512   case KEY_SF:
513   case KEY_SR:
514   case KEY_NPAGE:
515   case KEY_PPAGE:
516   case KEY_PRINT:
517   case KEY_A1:
518   case KEY_A3:
519   case KEY_B2:
520   case KEY_C1:
521   case KEY_C3:
522 #ifdef KEY_UNDO
523   case KEY_UNDO:
524 #endif
525 #ifdef KEY_EXIT
526   case KEY_EXIT:
527 #endif
528   case '\v':           /* VT */
529   case '\E':           /* we ignore ESC */
530     return TRUE;
531   default: 
532     return FALSE; 
533   }
534 }
535
536 /* Clears input buffer */
537
538 static void silc_client_clear_input(SilcClientInternal app)
539 {
540   silc_buffer_clear(app->input_buffer);
541   silc_buffer_pull_tail(app->input_buffer,
542                         SILC_BUFFER_END(app->input_buffer));
543   silc_screen_input_reset(app->screen);
544 }
545
546 /* Processes messages user has typed on the screen. This either sends
547    a packet out to network or if command were written executes it. */
548
549 static void silc_client_process_message(SilcClientInternal app)
550 {
551   unsigned char *data;
552   SilcUInt32 len;
553
554   SILC_LOG_DEBUG(("Start"));
555
556   data = app->input_buffer->data;
557   len = strlen(data);
558
559   if (data[0] == '/' && data[1] != ' ') {
560     /* Command */
561     SilcUInt32 argc = 0;
562     unsigned char **argv, *tmpcmd;
563     SilcUInt32 *argv_lens, *argv_types;
564     SilcClientCommand *cmd;
565     SilcClientCommandContext ctx;
566
567     /* Get the command */
568     tmpcmd = silc_client_parse_command(data);
569     cmd = silc_client_local_command_find(tmpcmd);
570     if (!cmd && (cmd = silc_client_command_find(tmpcmd)) == NULL) {
571       silc_say(app->client, app->current_win, "Invalid command: %s", tmpcmd);
572       silc_free(tmpcmd);
573       goto out;
574     }
575
576     /* Now parse all arguments */
577     silc_parse_command_line(data + 1, &argv, &argv_lens, 
578                             &argv_types, &argc, cmd->max_args);
579     silc_free(tmpcmd);
580
581     SILC_LOG_DEBUG(("Executing command: %s", cmd->name));
582
583     /* Allocate command context. This and its internals must be free'd 
584        by the command routine receiving it. */
585     ctx = silc_client_command_alloc();
586     ctx->client = app->client;
587     ctx->conn = app->conn;
588     ctx->command = cmd;
589     ctx->argc = argc;
590     ctx->argv = argv;
591     ctx->argv_lens = argv_lens;
592     ctx->argv_types = argv_types;
593
594     /* Execute command */
595     (*cmd->cb)(ctx);
596
597   } else {
598     /* Normal message to a channel */
599     if (len && app->conn && app->conn->current_channel &&
600         app->conn->current_channel->on_channel == TRUE) {
601       silc_print(app->client, "> %s", data);
602       silc_client_send_channel_message(app->client, 
603                                        app->conn,
604                                        app->conn->current_channel, NULL,
605                                        0, data, strlen(data), TRUE);
606     }
607   }
608
609  out:
610   /* Clear the input buffer */
611   silc_client_clear_input(app);
612 }
613
614 /* Returns the command fetched from user typed command line */
615
616 static char *silc_client_parse_command(unsigned char *buffer)
617 {
618   char *ret;
619   const char *cp = buffer;
620   int len;
621
622   len = strcspn(cp, " ");
623   ret = silc_to_upper((char *)++cp);
624   ret[len - 1] = 0;
625
626   return ret;
627 }
628
629 /* Updates clock on the screen every minute. */
630
631 SILC_TASK_CALLBACK(silc_client_update_clock)
632 {
633   SilcClient client = (SilcClient)context;
634   SilcClientInternal app = (SilcClientInternal)client->application;
635
636   /* Update the clock on the screen */
637   silc_screen_print_clock(app->screen);
638
639   /* Re-register this same task */
640   silc_task_register(qptr, 0, silc_client_update_clock, context, 
641                      silc_client_time_til_next_min(), 0,
642                      SILC_TASK_TIMEOUT,
643                      SILC_TASK_PRI_LOW);
644
645   silc_screen_refresh_win(app->screen->input_win);
646 }
647
648 /* Runs commands user configured in configuration file. This is
649    called when initializing client. */
650
651 SILC_TASK_CALLBACK(silc_client_run_commands)
652 {
653   SilcClient client = (SilcClient)context;
654   SilcClientInternal app = (SilcClientInternal)client->application;
655   SilcClientConfigSectionCommand *cs;
656
657   SILC_LOG_DEBUG(("Start"));
658
659   cs = app->config->commands;
660   while(cs) {
661     SilcUInt32 argc = 0;
662     unsigned char **argv, *tmpcmd;
663     SilcUInt32 *argv_lens, *argv_types;
664     SilcClientCommand *cmd;
665     SilcClientCommandContext ctx;
666
667     /* Get the command */
668     tmpcmd = silc_client_parse_command(cs->command);
669     cmd = silc_client_local_command_find(tmpcmd);
670     if (!cmd && (cmd = silc_client_command_find(tmpcmd)) == NULL) {
671       silc_say(client, app->conn, "Invalid command: %s", tmpcmd);
672       silc_free(tmpcmd);
673       continue;
674     }
675     
676     /* Now parse all arguments */
677     silc_parse_command_line(cs->command + 1, &argv, &argv_lens, 
678                             &argv_types, &argc, cmd->max_args);
679     silc_free(tmpcmd);
680
681     SILC_LOG_DEBUG(("Executing command: %s", cmd->name));
682
683     /* Allocate command context. This and its internals must be free'd 
684        by the command routine receiving it. */
685     ctx = silc_client_command_alloc();
686     ctx->client = client;
687     ctx->conn = app->conn;
688     ctx->command = cmd;
689     ctx->argc = argc;
690     ctx->argv = argv;
691     ctx->argv_lens = argv_lens;
692     ctx->argv_types = argv_types;
693
694     /* Execute command */
695     (*cmd->cb)(ctx);
696
697     cs = cs->next;
698   }
699 }