5 Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
7 Copyright (C) 1997 - 2000 Pekka Riikonen
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.
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.
22 #include "clientincludes.h"
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);
31 void silc_client_create_main_window(SilcClientInternal app);
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);
38 /* Long command line options */
39 static struct option long_opts[] =
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 },
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' },
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;
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;
84 /* SILC Client operations */
85 extern SilcClientOperations ops;
87 /* Prints out the usage of silc client */
92 Usage: silc [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\
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\
119 int main(int argc, char **argv)
121 int opt, option_index = 1;
123 SilcClient silc = NULL;
124 SilcClientInternal app = NULL;
131 getopt_long(argc, argv,
132 "s:p:n:c:b:k:f:qdhVCS:",
133 long_opts, &option_index)) != EOF)
142 opt_server = strdup(optarg);
146 opt_port = atoi(optarg);
150 opt_nickname = strdup(optarg);
154 opt_channel = strdup(optarg);
158 opt_cipher = strdup(optarg);
162 opt_public_key = strdup(optarg);
166 opt_private_key = strdup(optarg);
170 opt_config_file = strdup(optarg);
173 opt_no_silcrc = TRUE;
184 SILC Secure Internet Live Conferencing, version %s\n",
187 (c) 1997 - 2000 Pekka Riikonen <priikone@poseidon.pspt.fi>\n");
191 silc_client_list_ciphers();
195 silc_client_list_hash_funcs();
199 silc_client_list_pkcs();
204 * Key management options
207 opt_create_keypair = TRUE;
211 opt_pkcs = strdup(optarg);
215 opt_bits = atoi(optarg);
220 opt_keyfile = strdup(optarg);
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);
247 if (opt_create_keypair == TRUE) {
248 /* Create new key pair and exit */
249 silc_client_create_key_pair(opt_pkcs, opt_bits,
250 NULL, NULL, NULL, NULL, NULL);
255 if (opt_show_key == TRUE) {
257 silc_client_show_key(opt_keyfile);
258 silc_free(opt_keyfile);
262 /* Default configuration file */
263 if (!opt_config_file)
264 opt_config_file = strdup(SILC_CLIENT_CONFIG_FILE);
266 /* Allocate internal application context */
267 app = silc_calloc(1, sizeof(*app));
269 /* Allocate new client */
270 app->client = silc = silc_client_alloc(&ops, app);
274 /* Read global configuration file. */
275 app->config = silc_client_config_alloc(opt_config_file);
277 /* XXX Read local configuration file */
279 /* Check ~/.silc directory and public and private keys */
280 if (silc_client_check_silc_dir() == FALSE)
283 /* Get user information */
284 silc->username = silc_get_username();
285 silc->hostname = silc_net_localhost();
286 silc->realname = silc_get_real_name();
288 /* Register all configured ciphers, PKCS and hash functions. */
290 app->config->client = (void *)app;
291 if (!silc_client_config_register_ciphers(app->config))
292 silc_cipher_register_default();
293 if (!silc_client_config_register_pkcs(app->config))
294 silc_pkcs_register_default();
295 if (!silc_client_config_register_hashfuncs(app->config))
296 silc_hash_register_default();
297 if (!silc_client_config_register_hmacs(app->config))
298 silc_hmac_register_default();
300 /* Register default ciphers, pkcs, hash funtions and hmacs. */
301 silc_cipher_register_default();
302 silc_pkcs_register_default();
303 silc_hash_register_default();
304 silc_hmac_register_default();
307 /* Load public and private key */
308 if (silc_client_load_keys(silc) == FALSE)
311 /* Initialize the client. This initializes the client library and
312 sets everything ready for silc_client_run. */
313 ret = silc_client_init(silc);
317 /* Register the main task that is used in client. This receives
318 the key pressings. */
319 silc_task_register(silc->io_queue, fileno(stdin),
320 silc_client_process_key_press,
323 SILC_TASK_PRI_NORMAL);
325 /* Register timeout task that updates clock every minute. */
326 silc_task_register(silc->timeout_queue, 0,
327 silc_client_update_clock,
329 silc_client_time_til_next_min(), 0,
333 if (app->config && app->config->commands) {
334 /* Run user configured commands with timeout */
335 silc_task_register(silc->timeout_queue, 0,
336 silc_client_run_commands,
342 /* Allocate the input buffer used to save typed characters */
343 app->input_buffer = silc_buffer_alloc(SILC_SCREEN_INPUT_WIN_SIZE);
344 silc_buffer_pull_tail(app->input_buffer,
345 SILC_BUFFER_END(app->input_buffer));
347 /* Initialize the screen */
348 silc_client_create_main_window(app);
349 silc_screen_print_coordinates(app->screen, 0);
351 /* Run the client. When this returns the application will be
353 silc_client_run(silc);
355 /* Stop the client. This probably has been done already but it
356 doesn't hurt to do it here again. */
357 silc_client_stop(silc);
358 silc_client_free(silc);
364 silc_free(opt_config_file);
366 silc_client_config_free(app->config);
368 silc_client_free(silc);
372 /* Creates the main window used in SILC client. This is called always
373 at the initialization of the client. If user wants to create more
374 than one windows a new windows are always created by calling
375 silc_client_add_window. */
377 void silc_client_create_main_window(SilcClientInternal app)
381 SILC_LOG_DEBUG(("Creating main window"));
383 app->screen = silc_screen_init();
384 app->screen->input_buffer = app->input_buffer->data;
385 app->screen->u_stat_line.program_name = silc_name;
386 app->screen->u_stat_line.program_version = silc_version;
388 /* Create the actual screen */
389 screen = (void *)silc_screen_create_output_window(app->screen);
390 silc_screen_create_input_window(app->screen);
391 silc_screen_init_upper_status_line(app->screen);
392 silc_screen_init_output_status_line(app->screen);
394 app->screen->bottom_line->nickname = silc_get_username();
395 silc_screen_print_bottom_line(app->screen, 0);
398 /* The main task on SILC client. This processes the key pressings user
401 SILC_TASK_CALLBACK(silc_client_process_key_press)
403 SilcClient client = (SilcClient)context;
404 SilcClientInternal app = (SilcClientInternal)client->application;
407 /* There is data pending in stdin, this gets it directly */
408 c = wgetch(app->screen->input_win);
409 if (silc_client_bad_keys(c))
412 SILC_LOG_DEBUG(("Pressed key: %d", c));
416 * Special character handling
423 SILC_LOG_DEBUG(("RIGHT"));
424 silc_screen_input_cursor_right(app->screen);
428 SILC_LOG_DEBUG(("LEFT"));
429 silc_screen_input_cursor_left(app->screen);
436 silc_screen_input_backspace(app->screen);
442 /* Insert switch. Turns on/off insert on input window */
443 silc_screen_input_insert(app->screen);
447 /* Enter, Return. User pressed enter we are ready to
448 process the message. */
449 silc_client_process_message(app);
452 /* Refresh screen, Ctrl^l */
453 silc_screen_refresh_all(app->screen);
460 /* Beginning, Home */
461 silc_screen_input_cursor_home(app->screen);
469 silc_screen_input_cursor_end(app->screen);
478 silc_client_clear_input(app);
485 /* Control codes are printed as reversed */
487 wattron(app->screen->input_win, A_REVERSE);
488 silc_screen_input_print(app->screen, c);
489 wattroff(app->screen->input_win, A_REVERSE);
491 /* Normal character */
492 silc_screen_input_print(app->screen, c);
496 silc_screen_print_coordinates(app->screen, 0);
497 silc_screen_refresh_win(app->screen->input_win);
500 static int silc_client_bad_keys(unsigned char key)
502 /* these are explained in curses.h */
521 case '\E': /* we ignore ESC */
528 /* Clears input buffer */
530 static void silc_client_clear_input(SilcClientInternal app)
532 silc_buffer_clear(app->input_buffer);
533 silc_buffer_pull_tail(app->input_buffer,
534 SILC_BUFFER_END(app->input_buffer));
535 silc_screen_input_reset(app->screen);
538 /* Processes messages user has typed on the screen. This either sends
539 a packet out to network or if command were written executes it. */
541 static void silc_client_process_message(SilcClientInternal app)
546 SILC_LOG_DEBUG(("Start"));
548 data = app->input_buffer->data;
551 if (data[0] == '/' && data[1] != ' ') {
554 unsigned char **argv, *tmpcmd;
555 uint32 *argv_lens, *argv_types;
556 SilcClientCommand *cmd;
557 SilcClientCommandContext ctx;
559 /* Get the command */
560 tmpcmd = silc_client_parse_command(data);
561 cmd = silc_client_local_command_find(tmpcmd);
562 if (!cmd && (cmd = silc_client_command_find(tmpcmd)) == NULL) {
563 silc_say(app->client, app->current_win, "Invalid command: %s", tmpcmd);
568 /* Now parse all arguments */
569 silc_parse_command_line(data + 1, &argv, &argv_lens,
570 &argv_types, &argc, cmd->max_args);
573 SILC_LOG_DEBUG(("Executing command: %s", cmd->name));
575 /* Allocate command context. This and its internals must be free'd
576 by the command routine receiving it. */
577 ctx = silc_client_command_alloc();
578 ctx->client = app->client;
579 ctx->conn = app->conn;
583 ctx->argv_lens = argv_lens;
584 ctx->argv_types = argv_types;
586 /* Execute command */
590 /* Normal message to a channel */
591 if (len && app->conn && app->conn->current_channel &&
592 app->conn->current_channel->on_channel == TRUE) {
593 silc_print(app->client, "> %s", data);
594 silc_client_send_channel_message(app->client,
596 app->conn->current_channel, NULL,
597 0, data, strlen(data), TRUE);
602 /* Clear the input buffer */
603 silc_client_clear_input(app);
606 /* Returns the command fetched from user typed command line */
608 static char *silc_client_parse_command(unsigned char *buffer)
611 const char *cp = buffer;
614 len = strcspn(cp, " ");
615 ret = silc_to_upper((char *)++cp);
621 /* Updates clock on the screen every minute. */
623 SILC_TASK_CALLBACK(silc_client_update_clock)
625 SilcClient client = (SilcClient)context;
626 SilcClientInternal app = (SilcClientInternal)client->application;
628 /* Update the clock on the screen */
629 silc_screen_print_clock(app->screen);
631 /* Re-register this same task */
632 silc_task_register(qptr, 0, silc_client_update_clock, context,
633 silc_client_time_til_next_min(), 0,
637 silc_screen_refresh_win(app->screen->input_win);
640 /* Runs commands user configured in configuration file. This is
641 called when initializing client. */
643 SILC_TASK_CALLBACK(silc_client_run_commands)
645 SilcClient client = (SilcClient)context;
646 SilcClientInternal app = (SilcClientInternal)client->application;
647 SilcClientConfigSectionCommand *cs;
649 SILC_LOG_DEBUG(("Start"));
651 cs = app->config->commands;
654 unsigned char **argv, *tmpcmd;
655 uint32 *argv_lens, *argv_types;
656 SilcClientCommand *cmd;
657 SilcClientCommandContext ctx;
659 /* Get the command */
660 tmpcmd = silc_client_parse_command(cs->command);
661 cmd = silc_client_local_command_find(tmpcmd);
662 if (!cmd && (cmd = silc_client_command_find(tmpcmd)) == NULL) {
663 silc_say(client, app->conn, "Invalid command: %s", tmpcmd);
668 /* Now parse all arguments */
669 silc_parse_command_line(cs->command + 1, &argv, &argv_lens,
670 &argv_types, &argc, cmd->max_args);
673 SILC_LOG_DEBUG(("Executing command: %s", cmd->name));
675 /* Allocate command context. This and its internals must be free'd
676 by the command routine receiving it. */
677 ctx = silc_client_command_alloc();
678 ctx->client = client;
679 ctx->conn = app->conn;
683 ctx->argv_lens = argv_lens;
684 ctx->argv_types = argv_types;
686 /* Execute command */