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: silc.c,v 1.25 2002/02/24 11:14:43 priikone Exp $ */
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 }
700
This page was automatically generated by the LXR engine.
Free-text search provided by Glimpse