Implement message authenticity check
[shigofumi.git] / src / shigofumi.c
blob95fafb9f02a41ef3623213f84b678a8e6c22ea43
1 #define _XOPEN_SOURCE 600
2 #include <stdlib.h>
3 #include <stdio.h>
4 #include <isds.h>
5 #include <string.h>
6 #include <readline/readline.h>
7 #include <readline/history.h>
8 #include <locale.h>
9 #include <unistd.h>
10 #include <string.h>
11 #include <stdint.h>
12 #include <confuse.h>
13 #include <errno.h>
14 #include <libgen.h>
15 #include <limits.h>
17 #include "shigofumi.h"
18 #include "completition.h"
19 #include "data.h"
20 #include "io.h"
21 #include "utils.h"
23 #define CONFIG_FILE ".shigofumirc"
24 #define CONFIG_SERVER "base_url"
25 #define CONFIG_USERNAME "username"
26 #define CONFIG_PASSWORD "password"
27 #define CONFIG_DEBUGLEVEL "debug_level"
28 #define CONFIG_VERIFYSERVER "verify_server"
29 #define CONFIG_CAFILE "ca_file"
30 #define CONFIG_CADIRECTORY "ca_directory"
31 #define CONFIG_CRLFILE "crl_file"
32 #define CONFIG_TIMEOUT "timeout"
33 #define CONFIG_LOGFACILITIES "log_facilities"
34 #define CONFIG_LOGFILE "log_file"
35 #define CONFIG_LOGLEVEL "log_level"
36 #define CONFIG_NORMALIZEMIMETYPE "normalize_mime_type"
37 #define CZPDEPOSIT_URL "https://www.czechpoint.cz/uschovna/"
39 #define TIMEOUT 10000
40 #define LOG_LEVEL 20
42 /* Configuration */
43 cfg_opt_t configuration_syntax[] = {
44 CFG_STR(CONFIG_SERVER, (char *)isds_locator, CFGF_NONE),
45 CFG_STR(CONFIG_USERNAME, NULL, CFGF_NODEFAULT),
46 CFG_STR(CONFIG_PASSWORD, NULL, CFGF_NODEFAULT),
47 /*CFG_STR(CONFIG_DEBUGLEVEL, NULL, CFGF_NODEFAULT),*/
48 CFG_BOOL(CONFIG_VERIFYSERVER, cfg_true, CFGF_NONE),
49 CFG_STR(CONFIG_CAFILE, NULL, CFGF_NODEFAULT),
50 CFG_STR(CONFIG_CADIRECTORY, NULL, CFGF_NODEFAULT),
51 CFG_STR(CONFIG_CRLFILE, NULL, CFGF_NODEFAULT),
52 CFG_INT(CONFIG_TIMEOUT, TIMEOUT, CFGF_NONE),
53 CFG_STR_LIST(CONFIG_LOGFACILITIES, "{none}", CFGF_NONE),
54 CFG_STR(CONFIG_LOGFILE, NULL, CFGF_NODEFAULT),
55 CFG_INT(CONFIG_LOGLEVEL, LOG_LEVEL, CFGF_NONE),
56 CFG_BOOL(CONFIG_NORMALIZEMIMETYPE, cfg_true, CFGF_NONE),
57 CFG_END()
59 cfg_t *configuration;
61 /* Logger */
62 int logger_fd = -1;
64 /* UI */
65 _Bool batch_mode = 0;
66 char *prompt = NULL;
67 struct command (*commands)[] = NULL;
69 /* Data */
70 struct isds_ctx *cisds = NULL;
71 char *server = NULL;
72 char *username = NULL;
73 char *password = NULL;
74 struct isds_list *boxes = NULL;
75 struct isds_message *message = NULL;
76 _Bool messages_are_outgoing = 0;
77 struct isds_list *messages = NULL;
78 unsigned long int total_messages = 0;
79 struct isds_ctx *czechpoint = NULL;
81 static void discard_credentials(void) {
82 zfree(server);
83 zfree(username);
84 zfree(password);
88 /* Do the cleanup and exit */
89 static void shi_exit(int exit_code) {
90 /* Data */
91 discard_credentials();
92 isds_list_free(&boxes);
93 isds_message_free(&message);
94 isds_list_free(&messages);
96 if (cisds) {
97 printf(_("Logging out...\n"));
98 isds_logout(cisds);
99 isds_ctx_free(&cisds);
101 isds_ctx_free(&czechpoint);
102 isds_cleanup();
104 /* Configuration */
105 cfg_free(configuration);
107 /* UI */
108 free(prompt);
109 free(commands);
111 exit(exit_code);
114 /* Set prompt. if @format is NULL, switch to default prompt */
115 static void set_prompt(const char *format, ...) {
116 char *buffer = NULL;
117 va_list ap;
118 if (format) {
119 va_start(ap, format);
120 shi_vasprintf(&buffer, format, ap);
121 va_end(ap);
123 if (buffer) {
124 shi_asprintf(&prompt, _("%s> "), buffer);
125 if (prompt) {
126 free(buffer);
127 return;
131 free(buffer);
132 free(prompt);
133 prompt = strdup(_("> "));
134 return;
137 zfree(prompt);
141 static int shi_load_configuration(const char *config_file) {
142 char *config_name = NULL;
143 int ret;
145 /* Get config file */
146 if (config_file) {
147 config_name = (char *) config_file;
148 } else {
149 if (-1 == shi_asprintf(&config_name, "%s/%s", getenv("HOME"),
150 CONFIG_FILE)) {
151 fprintf(stderr, _("Could not build configuration file name\n"));
152 return -1;
156 /* Parse configuration */
157 configuration = cfg_init(configuration_syntax, CFGF_NONE);
158 ret = cfg_parse(configuration, config_name);
159 if (ret) {
160 if (ret == CFG_FILE_ERROR) {
161 fprintf(stderr,
162 _("Error while opening configuration file `%s': %s\n"),
163 config_name, strerror(errno));
164 } else {
165 fprintf(stderr, _("Error while parsing configuration file `%s'\n"),
166 config_name);
168 printf(_("Using default configuration\n"));
171 if (config_name != config_file) free(config_name);
172 return 0;
176 static void finish_isds_operation(struct isds_ctx *ctx, isds_error err) {
177 shi_progressbar_finish();
178 if (err) {
179 if (isds_long_message(ctx))
180 printf(_("Error occured: %s: %s\n"), isds_strerror(err),
181 isds_long_message(ctx));
182 else
183 printf(_("Error occured: %s\n"), isds_strerror(err));
188 void logger(isds_log_facility facility, isds_log_level level,
189 const char *message, int length, void *data) {
190 int fd;
191 ssize_t written, left = length;
193 if (!data) return;
194 fd = *((int *) data);
195 /*printf("\033[32mLOG(%02d,%02d): ", facility, level);
196 printf("%.*s", length, message);
197 printf("\033[m");*/
199 while (left) {
200 written = write(fd, message + length - left, left);
201 if (written == -1) {
202 fprintf(stderr,
203 _("Could not save log message into log file: %s\n"
204 "Log message discarded!\n"),
205 strerror(errno));
206 /*close(fd);
207 fd = -1;*/
208 return;
210 left-=written;
215 /* Redirect ISDS log to file if @file is not NULL. */
216 static int do_log_to_file(const char *file) {
217 if (file && *file) {
218 logger_fd = open_file_for_writing(file, 0);
219 if (logger_fd == -1) return -1;
220 isds_set_log_callback(logger, &logger_fd);
222 return 0;
226 /* Add log facility based on its name. */
227 static int add_log_facility(isds_log_facility *facilities, const char *name) {
228 if (!facilities) return -1;
230 if (!strcmp(name, "none")) *facilities |= ILF_NONE;
231 else if (!strcmp(name, "http")) *facilities |= ILF_HTTP;
232 else if (!strcmp(name, "soap")) *facilities |= ILF_SOAP;
233 else if (!strcmp(name, "isds")) *facilities |= ILF_ISDS;
234 else if (!strcmp(name, "file")) *facilities |= ILF_FILE;
235 else if (!strcmp(name, "sec")) *facilities |= ILF_SEC;
236 else if (!strcmp(name, "xml")) *facilities |= ILF_XML;
237 else if (!strcmp(name, "all")) *facilities |= ILF_ALL;
238 else {
239 printf(_("%s: Unknown log facility\n"), name);
240 return -1;
243 return 0;
247 /* Clamp long int to unsigned int */
248 static unsigned int normalize_timeout(long int raw) {
249 if (raw < 0) {
250 printf(_("Configured network timeout is less then 0. Clamped to 0.\n"));
251 return 0;
253 if (raw > UINT_MAX ) {
254 printf(_("Configured network timeout is greater then %1$u. "
255 "Clamped to %1$u.\n"), UINT_MAX);
256 return UINT_MAX;
258 return (unsigned int) raw;
262 /* Clamp long int to <0;100> */
263 static unsigned int normalize_log_level(long int raw) {
264 if (raw < 0) {
265 printf(_("Configured log level is less then 0. Clamped to 0.\n"));
266 return 0;
268 if (raw > ILL_ALL) {
269 printf(_("Configured log level is greater then %1$u. "
270 "Clamped to %1$u.\n"), ILL_ALL);
271 return ILL_ALL;
273 if (raw > UINT_MAX ) {
274 printf(_("Configured log level is greater then %1$u. "
275 "Clamped to %1$u.\n"), UINT_MAX);
276 return UINT_MAX;
278 return (unsigned int) raw;
282 static int shi_init(const char *config_file) {
283 isds_error err;
284 char *value;
285 unsigned int timeout, log_level;
286 isds_log_facility log_facility = ILF_NONE;
288 printf(_("This is Shigofumi, an ISDS client. Have a nice e-government.\n"));
290 /* Do not permute arguments in getopt() */
291 if (setenv("POSIXLY_CORRECT", "", 1)) {
292 fprintf(stderr,
293 _("Could not set POSIXLY_CORRECT environment variable\n"));
294 return -1;
297 /* Load configuration */
298 if (shi_load_configuration(config_file))
299 return -1;
300 timeout = normalize_timeout(cfg_getint(configuration, CONFIG_TIMEOUT));
301 log_level = normalize_log_level(cfg_getint(configuration, CONFIG_LOGLEVEL));
303 /* Init readline */
304 rl_readline_name = "shigofumi";
305 rl_filename_quote_characters = "\\ >";
306 rl_filename_quoting_function = shi_quote_filename;
307 rl_filename_dequoting_function = shi_dequote_filename;
308 rl_char_is_quoted_p = shi_char_is_quoted;
310 /* Initialize ISDS */
311 err = isds_init();
312 if (err) {
313 fprintf(stderr, _("Could not initialize libisds library: %s\n"),
314 isds_strerror(err));
315 return -1;
318 /* Set ISDS logging */
319 value = cfg_getstr(configuration, CONFIG_LOGFILE);
320 if (do_log_to_file(value)) {
321 fprintf(stderr, _("Could not redirect ISDS log to file `%s'\n"), value);
322 return -1;
324 for (int i = 0; i < cfg_size(configuration, CONFIG_LOGFACILITIES); i++) {
325 if (add_log_facility(&log_facility,
326 cfg_getnstr(configuration, CONFIG_LOGFACILITIES, i)))
327 return -1;
330 isds_set_logging(log_facility, log_level);
332 /* Set ISDS context up */
333 cisds = isds_ctx_create();
334 if (!cisds) {
335 fprintf(stderr, _("Could not create ISDS context\n"));
336 return -1;
338 err = isds_set_timeout(cisds, timeout);
339 if (err) {
340 fprintf(stderr, _("Could not set ISDS network timeout: %s\n"),
341 isds_strerror(err));
343 err = isds_set_progress_callback(cisds, shi_progressbar, NULL);
344 if (err) {
345 fprintf(stderr, _("Could not register network progress bar: %s: %s\n"),
346 isds_strerror(err), isds_long_message(cisds));
348 err = isds_set_opt(cisds, IOPT_NORMALIZE_MIME_TYPE,
349 cfg_getbool(configuration, CONFIG_NORMALIZEMIMETYPE));
350 if (err) {
351 fprintf(stderr,
352 cfg_getbool(configuration, CONFIG_NORMALIZEMIMETYPE) ?
353 _("Could not enable MIME type normalization: %s: %s\n") :
354 _("Could not enable MIME type normalization: %s: %s\n"),
355 isds_strerror(err), isds_long_message(cisds));
357 if (!cfg_getbool(configuration, CONFIG_VERIFYSERVER)) {
358 printf(_("Warning: Shigofumi disabled server identity verification "
359 "on user request!\n"));
360 err = isds_set_opt(cisds, IOPT_TLS_VERIFY_SERVER, 0);
361 if (err) {
362 fprintf(stderr,
363 _("Could not disable server identity verification: "
364 "%s: %s\n"),
365 isds_strerror(err), isds_long_message(cisds));
368 if ((value = cfg_getstr(configuration, CONFIG_CAFILE))) {
369 err = isds_set_opt(cisds, IOPT_TLS_CA_FILE, value);
370 if (err) {
371 fprintf(stderr,
372 _("Could not set file with CA certificates: %s: %s: %s\n"),
373 value, isds_strerror(err), isds_long_message(cisds));
376 if ((value = cfg_getstr(configuration, CONFIG_CADIRECTORY))) {
377 err = isds_set_opt(cisds, IOPT_TLS_CA_DIRECTORY, value);
378 if (err) {
379 fprintf(stderr,
380 _("Could not set directory with CA certificates: "
381 "%s: %s: %s\n"),
382 value, isds_strerror(err), isds_long_message(cisds));
385 if ((value = cfg_getstr(configuration, CONFIG_CRLFILE))) {
386 err = isds_set_opt(cisds, IOPT_TLS_CRL_FILE, value);
387 if (err) {
388 fprintf(stderr, _("Could not set file with CRL: %s: %s: %s\n"),
389 value, isds_strerror(err), isds_long_message(cisds));
394 /* Set Czech POINT context up */
395 czechpoint = isds_ctx_create();
396 if (!czechpoint) {
397 fprintf(stderr, _("Could not create Czech POINT context\n"));
398 return -1;
400 err = isds_set_timeout(czechpoint, timeout);
401 if (err) {
402 fprintf(stderr, _("Could not set Czech POINT network timeout: %s\n"),
403 isds_strerror(err));
405 err = isds_set_progress_callback(czechpoint, shi_progressbar, NULL);
406 if (err) {
407 fprintf(stderr, "Could not register network progress bar: %s: %s\n",
408 isds_strerror(err), isds_long_message(cisds));
411 return 0;
415 static int shi_quit(int argc, const char **argv) {
416 shi_exit(EXIT_SUCCESS);
417 return 0;
422 static void shi_help_usage(const char *command) {
423 printf(_(
424 "Usage: %s [COMMAND]\n"
425 "Show COMMAND manual or list of currently available commands.\n"
427 command);
431 static int shi_help(int argc, const char **argv) {
432 size_t command_width = 14;
433 int i;
435 if (!commands) {
436 printf(_("No command is available\n"));
437 return 0;
440 if (argc == 2 && argv[1] && *argv[1]) {
441 /* Show usage for given command */
442 for (i = 0; (*commands)[i].name; i++) {
443 if (!strcmp((*commands)[i].name, argv[1])) {
444 if ((*commands)[i].usage)
445 (*commands)[i].usage((*commands)[i].name);
446 else if ((*commands)[i].description) {
447 fhprint(stdout, (*commands)[i].name, command_width);
448 printf(" %s\n", _((*commands)[i].description));
450 else
451 printf(_("%s: %s: Command description not defined\n"),
452 argv[0], argv[1]);
453 return 0;
456 printf(_("%s: %s: No such command exists\n"), argv[0], argv[1]);
457 return -1;
460 /* Or list all commands */
461 printf(_("Following commands are available:\n"));
462 for (i = 0; (*commands)[i].name; i++) {
463 fhprint(stdout, (*commands)[i].name, command_width);
464 printf(" %s\n", _((*commands)[i].description));
467 return 0;
471 static void show_version(void) {
472 char *libisds_version = isds_version();
474 printf(_("This is Shigofumi version %s.\n"), PACKAGE_VERSION);
475 printf(_("\n"
476 "Used libraries\n"
477 "Readline: %s\n"
478 "libisds: %s\n"
480 rl_library_version, libisds_version);
481 free(libisds_version);
485 static int shi_version(int argc, const char **argv) {
486 show_version();
487 printf(_(
488 "\n"
489 "-----\n"
490 "It's a shigofumi. A letter delivered from the afterlife. (Fumika)\n"
491 "A message can not be delivered to dead person. (ISDS specification)\n"
492 "Virtual and real world. They can be compatible. (Program author)\n"
494 return 0;
498 static int shi_copying(int argc, const char **argv) {
499 printf(_(
500 "This is Shigofumi, an ISDS client.\n"
501 "Copyright (C) 2010 Petr Pisar\n"
502 "\n"
503 "This program is free software: you can redistribute it and/or modify\n"
504 "it under the terms of the GNU General Public License as published by\n"
505 "the Free Software Foundation, either version 3 of the License, or\n"
506 "(at your option) any later version.\n"
507 "\n"
508 "This program is distributed in the hope that it will be useful,\n"
509 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
510 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
511 "GNU General Public License for more details.\n"
512 "\n"
513 "You should have received a copy of the GNU General Public License\n"
514 "along with this program. If not, see <http://www.gnu.org/licenses/>.\n"
516 return 0;
520 static int shi_cache(int argc, const char **argv) {
521 const struct isds_list *item;
522 size_t i;
524 if (boxes) {
525 for (item = boxes, i = 0; item; item = item->next, i++);
526 printf(_(
527 "Cached box list: %zu\n"),
531 if (messages) {
532 printf(_(
533 "Cached message list:\n"
534 "\tDirection: %s\n"
535 "\tMessages: %'lu\n"),
536 (messages_are_outgoing) ? _("Outgoing") : _("Incoming"),
537 total_messages);
540 if (message) {
541 printf(_("Cached message: %s\n"),
542 (message->envelope && message->envelope->dmID) ?
543 message->envelope->dmID : _("<Uknown ID>"));
546 return 0;
550 static void shi_chdir_usage(const char *command) {
551 printf(_(
552 "Usage: %s [DIRECTORY]\n"
553 "Change working directory to DIRECTORY.\n"
554 "If no DIRECTORY is supplied, HOME directory will be used.\n"),
555 command);
559 static int shi_chdir(int argc, const char **argv) {
560 const char *directory = NULL;
562 if (!argv || argc > 2) {
563 shi_chdir_usage((argv) ? argv[0] : NULL);
564 return -1;
567 if (argc == 2 && argv[1] && *argv[1])
568 directory = argv[1];
569 else {
570 directory = getenv("HOME");
571 if (!directory) {
572 printf("Environment variable HOME does not exist\n");
573 return -1;
576 if (chdir(directory)) {
577 printf(_("Could not change working directory: %s: %s\n"), directory,
578 strerror(errno));
579 return -1;
582 return 0;
586 static int shi_pwd(int argc, const char **argv) {
587 char *buffer = NULL, *newbuffer;
588 size_t length = 0;
590 while (length += 1024) {
591 newbuffer = realloc(buffer, length);
592 if (!newbuffer) {
593 printf(_("Error: Not enough memory\n"));
594 free(buffer);
595 return -1;
597 buffer = newbuffer;
599 if (getcwd(buffer, length)) {
600 printf("%s\n", buffer);
601 free(buffer);
602 return 0;
606 printf(_("Error: Current directory string is tooo long\n"));
607 free(buffer);
608 return -1;
612 /* Login to ISDS */
613 static int do_login(void) {
614 isds_error err;
616 if (batch_mode) {
617 printf(_("Unattended mode detected. "
618 "Make sure credentials have been preset.\n"));
619 } else {
620 printf(_("You are going to insert credentials for your account.\n"
621 "Leave blank line to choose default value.\n"));
623 select_completition(COMPL_NONE);
625 /* Ask for server base URL */
626 shi_replace_string(&server, _("Input ISDS base URL: "),
627 cfg_getstr(configuration, CONFIG_SERVER), batch_mode);
629 /* Ask for username */
630 shi_replace_string(&username, _("Input ISDS user name: "),
631 cfg_getstr(configuration, CONFIG_USERNAME), batch_mode);
633 /*if (!password) {
634 password = ask_for_password(_("Input ISDS password: "));
636 if (!password) {
637 fprintf(stderr, _("Could not read ISDS password\n"));
638 shi_exit(EXIT_FAILURE);
641 shi_replace_password(&password, _("Input ISDS password: "),
642 cfg_getstr(configuration, CONFIG_PASSWORD), batch_mode);
644 select_completition(COMPL_COMMAND);
645 set_prompt(NULL);
647 printf(_("Logging in...\n"));
648 err = isds_login(cisds, server, username, password, NULL);
649 finish_isds_operation(cisds, err);
650 if (err) {
651 printf(_("Login failed\n"));
652 return -1;
655 printf(_("Logged in.\n"));
656 return 0;
660 static struct isds_DbOwnerInfo *do_box(void) {
661 isds_error err;
662 struct isds_DbOwnerInfo *box = NULL;
664 printf(_("Getting box details you are logged in...\n"));
665 err = isds_GetOwnerInfoFromLogin(cisds, &box);
666 finish_isds_operation(cisds, err);
668 return box;
672 static int shi_box(int argc, const char **argv) {
673 struct isds_DbOwnerInfo *box = NULL;
675 box = do_box();
676 if (!box) return -1;
678 format_DbOwnerInfo(box);
680 isds_DbOwnerInfo_free(&box);
681 return 0;
685 /* Get info about box with @id.
686 * @id is UTF-8 encoded
687 * Return NULL in case of error, otherwise box description that caller must
688 * free. */
689 static struct isds_DbOwnerInfo *stat_box(const char *id) {
690 isds_error err;
691 struct isds_DbOwnerInfo criteria;
692 struct isds_list *boxes = NULL, *item;
693 struct isds_DbOwnerInfo *box = NULL;
694 char *id_locale = NULL;
696 if (!id || !*id) return NULL;
698 id_locale = utf82locale(id);
699 memset(&criteria, 0, sizeof(criteria));
700 criteria.dbID = (char *) id;
702 printf(_("Getting details about box with ID `%s'...\n"), id_locale);
703 err = isds_FindDataBox(cisds, &criteria, &boxes);
704 finish_isds_operation(cisds, err);
705 if (err) goto leave;
707 for(item = boxes; item; item = item->next) {
708 if (!item->data) continue;
710 if (item->next) {
711 printf(_("Error: More boxes match ID `%s'\n"), id_locale);
712 goto leave;
715 box = (struct isds_DbOwnerInfo *) item->data;
716 item->data = NULL;
717 break;
720 leave:
721 free(id_locale);
722 isds_list_free(&boxes);
724 return box;
728 static void shi_commercial_usage(const char *command) {
729 printf(_(
730 "Usage: %s [-0|-1] [BOX_ID]\n"
731 "Manipulate commercial receiving box status.\n"
732 " -O switch off receving of commercial messages\n"
733 " -1 switch on receving of commercial messages\n"
734 " BOX_ID affects box with ID BOX_ID; default is box you are logged in\n"
735 "If no option is given, show current commercial receiving status.\n"),
736 command);
740 /* Manipulate commercial receiving box status */
741 static int shi_commercial(int argc, const char **argv) {
742 isds_error err;
743 struct isds_DbOwnerInfo *box = NULL;
744 int opt;
745 int action = -1;
746 char *box_id = NULL, *box_id_locale = NULL;
747 _Bool static_box_id;
748 int retval = 0;
750 optind = 0;
751 while ((opt = getopt(argc, (char * const *)argv, "01")) != -1) {
752 switch (opt) {
753 case '0':
754 action = 0;
755 break;
756 case '1':
757 action = 1;
758 break;
759 default:
760 shi_commercial_usage((argv)?argv[0]:NULL);
761 return -1;
764 if (optind + 1 < argc) {
765 printf(_("Bad invocation\n"));
766 shi_commercial_usage((argv)?argv[0]:NULL);
767 return -1;
770 if (!argv[optind] || !*argv[optind]) {
771 /* Get current box ID */
772 box = do_box();
773 if (!box || !box->dbID || !*box->dbID) {
774 isds_DbOwnerInfo_free(&box);
775 printf(_("Could not get current box ID\n"));
776 return -1;
778 box_id = box->dbID; static_box_id = 1;
779 box_id_locale = utf82locale(box_id);
780 } else {
781 /* Box ID supplied as argument */
782 box_id_locale = (char *) argv[optind];
783 box_id = locale2utf8(box_id_locale); static_box_id = 0;
784 if (!box_id) {
785 printf(_("Could not convert box ID `%s' to UTF-8\n"),
786 box_id_locale);
787 return -1;
791 if (action == -1) {
792 if (!box) box = stat_box(box_id);
793 if (!box) {
794 printf(_("Could not get details about box ID `%s'\n"),
795 box_id_locale);
796 retval = -1;
797 goto leave;
800 printf(_("Commercial receiving status of box `%s': "), box_id_locale);
801 if (!box->dbOpenAddressing)
802 printf(_("Unknown\n"));
803 else if (*box->dbOpenAddressing)
804 printf(_("Positive\n"));
805 else
806 printf(_("Negative\n"));
807 } else {
808 char *refnumber = NULL;
809 printf((action) ?
810 _("Switching `%s' box commercial receiving on...\n"):
811 _("Switching `%s' box commercial receiving off...\n"),
812 box_id_locale);
813 err = isds_switch_commercial_receiving(cisds, box_id, action,
814 NULL, &refnumber);
815 finish_isds_operation(cisds, err);
817 if (!err) {
818 char *refnumber_locale = utf82locale(refnumber);
819 printf(_("Commercial receiving status successfully changed. "
820 "Assigned refference number: %s\n"),
821 refnumber_locale);
822 } else {
823 printf(_("Commercial receiving status has not been changed.\n"));
824 retval = -1;
826 free(refnumber);
829 leave:
830 if (!static_box_id) free(box_id);
831 else free(box_id_locale);
832 isds_DbOwnerInfo_free(&box);
833 return retval;
837 static int shi_user(int argc, const char **argv) {
838 isds_error err;
839 struct isds_DbUserInfo *user = NULL;
841 printf(_("Getting user details you are logged as...\n"));
842 err = isds_GetUserInfoFromLogin(cisds, &user);
843 finish_isds_operation(cisds, err);
844 if (err) return -1;
846 format_DbUserInfo(user);
848 isds_DbUserInfo_free(&user);
849 return 0;
853 static void shi_users_usage(const char *command) {
854 printf(_(
855 "Usage: %s BOX_ID\n"
856 "Get list of users having access to box with BOX_ID.\n"),
857 command);
861 static int shi_users(int argc, const char **argv) {
862 isds_error err;
863 struct isds_list *users = NULL, *item;
864 int ordinar;
866 if (!argv || !argv[1] || !*argv[1]) {
867 shi_users_usage((argv)?argv[0]:NULL);
868 return -1;
871 printf(_("Getting users of box with ID `%s'...\n"), argv[1]);
872 err = isds_GetDataBoxUsers(cisds, argv[1], &users);
873 finish_isds_operation(cisds, err);
874 if (err) return -1;
876 for (item = users, ordinar = 0; item; item=item->next) {
877 if (!item->data) continue;
878 ordinar++;
879 printf(_("\n* User #%d:\n"), ordinar);
880 format_DbUserInfo(item->data);
882 if (ordinar == 0)
883 printf(_("Empty list of users returned.\n"));
885 isds_list_free(&users);
886 return 0;
890 static int show_password_expiration(void) {
891 isds_error err;
892 struct timeval *expiration = NULL;
894 err = isds_get_password_expiration(cisds, &expiration);
895 finish_isds_operation(cisds, err);
896 if (err) {
897 fprintf(stderr, "Could not get password expiration time\n");
898 return -1;
901 print_header_timeval(_("Your password expires at"), expiration);
902 free(expiration);
903 return 0;
907 /* Change password in ISDS */
908 static int do_passwd(void) {
909 char *old_password = NULL;
910 char *new_password = NULL;
911 char *new_password2 = NULL;
912 isds_error err = IE_ERROR;
913 int retval = 0;
915 select_completition(COMPL_NONE);
917 printf(_(
918 "You are going to change your password. If you don't want to change your\n"
919 "password, insert empty string or EOF.\n"
920 "\n"
921 "You will be asked for your current (old) password and then for new password.\n"
922 "ISDS forces some criteria new password must fulfill. Current rules are:\n"
923 "\tLength: minimal 8, maximal 32 characters\n"
924 "\tMust contain at least: 1 upper case letter, 1 lower case letter, 1 digit\n"
925 "\tAllowed alphabet: [a-z][A-Z][0-9][!#$%%&()*+,-.:=?@[]_{}|~]\n"
926 "\tMust differ from last 255 passwords\n"
927 "\tMust not contain user ID\n"
928 "\tMust not contain sequence of three or more same characters\n"
929 "\tMust not start with `qwert', `asdgf', or `12345'\n"
930 "Finally, you must repeat your new password to avoid mistakes.\n"
931 "After password change will be confirmed, you must log in again as password\n"
932 "is transmitted to server on each request.\n"
933 "\n"));
935 old_password = ask_for_password(_("Old password: "));
936 if (!old_password || *old_password == '\0') {
937 fprintf(stderr, _("No pasword supplied\n"));
938 goto error;
941 new_password = ask_for_password(_("New password: "));
942 if (!new_password || *new_password == '\0') {
943 fprintf(stderr, _("No pasword supplied\n"));
944 goto error;
947 new_password2 = ask_for_password(_("Repeat new password: "));
948 if (!new_password2 || new_password2 == '\0') {
949 fprintf(stderr, _("No pasword supplied\n"));
950 goto error;
953 if (strcmp(new_password, new_password2)) {
954 fprintf(stderr, _("New passwords differ\n"));
955 goto error;
958 printf(_("Changing password...\n"));
959 err = isds_change_password(cisds, old_password, new_password);
960 finish_isds_operation(cisds, err);
961 if (err) {
962 printf(_("Password change failed\n"));
963 goto error;
964 } else {
965 printf(_("Password HAS been successfully changed.\n"));
966 goto leave;
969 error:
970 retval = -1;
971 printf(_("Password has NOT been changed!\n"));
973 leave:
974 free(old_password);
975 free(new_password);
976 free(new_password2);
978 set_prompt(NULL);
979 select_completition(COMPL_COMMAND);
981 printf(_("\n"
982 "Remember, ISDS password has limited life time.\n"));
983 return retval;
987 static void shi_passwd_usage(const char *command) {
988 printf(_(
989 "Usage: %s [-S]\n"
990 "Manipulate user password or change it if no option given.\n"
991 "\n"
992 "Options:\n"
993 " -S show password expiration time\n"
994 ), command);
998 static int shi_passwd(int argc, const char **argv) {
999 int opt;
1001 optind = 0;
1002 while ((opt = getopt(argc, (char * const *)argv, "S")) != -1) {
1003 switch (opt) {
1004 case 'S':
1005 return show_password_expiration();
1006 default:
1007 shi_passwd_usage(argv[0]);
1008 return -1;
1011 if (optind != argc || argc > 1) {
1012 printf(_("Bad invocation\n"));
1013 shi_passwd_usage((argv)?argv[0]:NULL);
1014 return -1;
1017 return do_passwd();
1021 static int shi_login(int argc, const char **argv) {
1022 discard_credentials();
1023 if (do_login()) return -1;
1024 show_password_expiration();
1025 return 0;
1029 static void shi_debug_usage(const char *command) {
1030 printf(_(
1031 "Usage: %s -l LEVEL [-f FACILITY...] [{-e | -o FILE}]\n"
1032 "Debug FACILTIES on LEVEL.\n"
1033 "\n"
1034 "-l LEVEL set log level, valid interval <%d,%d>, default is %d\n"
1035 " %d is no logging, %d critical, %d errors,\n"
1036 " %d warnings, %d info, %d debug, %d all\n"
1037 "-f FACILITY debug only given facily, repeat this option to debug\n"
1038 " more facilities; valid values: none, http, soap, isds,\n"
1039 " file, sec, xml, all; default is none\n"
1040 "-e write debug log into stderr\n"
1041 "-o FILE append debug log to FILE\n"
1043 command,
1044 ILL_NONE, ILL_ALL, ILL_NONE,
1045 ILL_NONE, ILL_CRIT, ILL_ERR, ILL_WARNING,
1046 ILL_INFO, ILL_DEBUG, ILL_ALL);
1049 static int shi_debug(int argc, const char **argv) {
1050 int opt;
1051 int log_level = ILL_NONE;
1052 isds_log_facility log_facility = ILF_NONE;
1053 char *file = NULL;
1054 _Bool close_log = 0;
1056 optind = 0;
1057 while ((opt = getopt(argc, (char * const *)argv, "l:f:eo:")) != -1) {
1058 switch (opt) {
1059 case 'l':
1060 log_level = normalize_log_level(atoi(optarg));
1061 break;
1062 case 'f':
1063 if (add_log_facility(&log_facility, optarg)) return -1;
1064 break;
1065 case 'e':
1066 close_log = 1;
1067 case 'o':
1068 file = optarg;
1069 break;
1070 default:
1071 shi_debug_usage(argv[0]);
1072 return -1;
1075 if (optind == 1 || optind != argc) {
1076 printf(_("Bad invocation\n"));
1077 shi_debug_usage(argv[0]);
1078 return -1;
1081 /* Redirect log */
1082 if (close_log) {
1083 isds_set_log_callback(NULL, NULL);
1085 if (logger_fd != -1) {
1086 if (-1 == close(logger_fd)) {
1087 fprintf(stderr, _("Closing log file failed: %s\n"),
1088 strerror(errno));
1089 return -1;
1093 if (do_log_to_file(file))
1094 return -1;
1096 /* Set log levels */
1097 isds_set_logging(log_facility, log_level);
1098 return 0;
1102 static void shi_find_box_usage(const char *command) {
1103 printf(_(
1104 "Usage: %s {OPTION... | BOX_ID}\n"
1105 "Get information about box with BOX_ID or boxes meeting other criteria.\n"
1106 "Each search option requires an argument:\n"
1107 " -t box type; accepted values:\n"
1108 " FO Private individual\n"
1109 " PFO Self-employed individual\n"
1110 " PFO_ADVOK Lawyer\n"
1111 " PFO_DANPOR Tax advisor\n"
1112 " PFO_INSSPR Insolvency administrator\n"
1113 " PO Organisation\n"
1114 " PO_ZAK Organization based by law\n"
1115 " PO_REQ Organization based on request\n"
1116 " OVM Public authority\n"
1117 " OVM_NOTAR Notary\n"
1118 " OVM_EXEKUT Executor\n"
1119 " OVM_REQ Public authority based on request\n"
1120 " -j identity number\n"
1121 "\n"
1122 "Person name options:\n"
1123 " -f first name\n"
1124 " -m midle name\n"
1125 " -l last name\n"
1126 " -b last name at birth\n"
1127 " -s subject name\n"
1128 "\n"
1129 "Birth options:\n"
1130 " -d birth date (locale or full ISO 8601 date)\n"
1131 " -w birth city\n"
1132 " -y birth county\n"
1133 " -c birth state\n"
1134 "\n"
1135 "Address:\n"
1136 " -W city\n"
1137 " -S street\n"
1138 " -z number in street\n"
1139 " -Z number in municipality\n"
1140 " -P ZIP code\n"
1141 " -C state\n"
1142 "\n"
1143 "Other options:\n"
1144 " -n nationality\n"
1145 " -e e-mail\n"
1146 " -p phone number\n"
1147 " -i identifier\n"
1148 " -r registry code\n"
1149 " -a box status; accepted values:\n"
1150 " ACCESSIBLE Accessible\n"
1151 " TEMP_UNACCESSIBLE Temporary unaccessible\n"
1152 " NOT_YET_ACCESSIBLE Not yet accessible\n"
1153 " PERM_UNACCESSIBLE Permanently unaccessible\n"
1154 " REMOVED Deleted\n"
1155 " -o act as public authority; boolean values: 0 is false, 1 is true\n"
1156 " -k receive commerical messages; boolean values\n"
1157 "\n"
1158 "Not all option combinations are meaningfull or allowed. For example box\n"
1159 "type is always required (except direct box ID query).\n"
1160 "ISDS can refuse to answer to much broad query. Not all boxes are searchable\n"
1161 "by every user.\n"
1163 command);
1167 /* Allow reassignment */
1168 #define FILL_OR_LEAVE(variable, locale) { \
1169 zfree(variable); \
1170 (variable) = locale2utf8(locale); \
1171 if (!(variable)) { \
1172 printf(_("Error: Not enough memory\n")); \
1173 retval = -1; \
1174 goto leave; \
1178 #define CALLOC_OR_LEAVE(structure) { \
1179 if (!(structure)) { \
1180 (structure) = calloc(1, sizeof(*(structure))); \
1181 if (!(structure)) { \
1182 printf(_("Error: Not enough memory\n")); \
1183 retval = -1; \
1184 goto leave; \
1189 #define FILL_BOOLEAN_OR_LEAVE(variable, locale) { \
1190 zfree(variable); \
1191 (variable) = malloc(sizeof(*(variable))); \
1192 if (!(variable)) { \
1193 printf(_("Error: Not enough memory\n")); \
1194 retval = -1; \
1195 goto leave; \
1197 if (!strcmp((locale), "0")) *(variable) = 0; \
1198 else if (!strcmp((locale), "1")) *(variable) = 1; \
1199 else { \
1200 printf(_("%s: %s: Unknown boolean value\n"), argv[0], (locale)); \
1201 retval = -1; \
1202 goto leave; \
1206 #define FILL_LONGINT_OR_LEAVE(variable, locale) { \
1207 if (!(locale) || !*(locale)) { \
1208 printf(_("%s: Empty integer value\n"), argv[0]); \
1209 retval = -1; \
1210 goto leave; \
1212 char *endptr; \
1213 zfree(variable); \
1214 (variable) = malloc(sizeof(*(variable))); \
1215 if (!(variable)) { \
1216 printf(_("Error: Not enough memory\n")); \
1217 retval = -1; \
1218 goto leave; \
1220 (*variable) = strtol((locale), &endptr, 0); \
1221 if (*endptr) { \
1222 printf(_("%s: %s: Invalid integer value\n"), argv[0], (locale)); \
1223 retval = -1; \
1224 goto leave; \
1228 static int shi_find_box(int argc, const char **argv) {
1229 int opt;
1230 isds_error err;
1231 struct isds_DbOwnerInfo *criteria = NULL;
1232 struct isds_list *item;
1233 int order = 0;
1234 int retval = 0;
1236 if (!argv || !argv[1] || !*argv[1]) {
1237 printf(_("Error: No argument supplied\n"));
1238 shi_find_box_usage((argv)?argv[0]:NULL);
1239 return -1;
1242 criteria = calloc(1, sizeof(*criteria));
1243 if (!criteria) {
1244 printf(_("Error: Not enough memory\n"));
1245 retval = -1;
1246 goto leave;
1249 /* Parse options */
1250 optind = 0;
1251 while ((opt = getopt(argc, (char * const *)argv, "t:j:s:"
1252 "f:m:l:b:s:" "d:w:y:c:" "W:S:z:Z:P:C:"
1253 "n:e:p:i:r:a:o:k:")) != -1) {
1254 switch (opt) {
1255 case 't':
1256 criteria->dbType = malloc(sizeof(*criteria->dbType));
1257 if (!criteria->dbType) {
1258 printf(_("Error: Not enough memory\n"));
1259 retval = -1;
1260 goto leave;
1262 if (!strcmp(optarg, "FO"))
1263 *criteria->dbType = DBTYPE_FO;
1264 else if (!strcmp(optarg, "PFO"))
1265 *criteria->dbType = DBTYPE_PFO;
1266 else if (!strcmp(optarg, "PFO_ADVOK"))
1267 *criteria->dbType = DBTYPE_PFO_ADVOK;
1268 else if (!strcmp(optarg, "PFO_DANPOR"))
1269 *criteria->dbType = DBTYPE_PFO_DANPOR;
1270 else if (!strcmp(optarg, "PFO_INSSPR"))
1271 *criteria->dbType = DBTYPE_PFO_INSSPR;
1272 else if (!strcmp(optarg, "PO"))
1273 *criteria->dbType = DBTYPE_PO;
1274 else if (!strcmp(optarg, "PO_ZAK"))
1275 *criteria->dbType = DBTYPE_PO_ZAK;
1276 else if (!strcmp(optarg, "PO_REQ"))
1277 *criteria->dbType = DBTYPE_PO_REQ;
1278 else if (!strcmp(optarg, "OVM"))
1279 *criteria->dbType = DBTYPE_OVM;
1280 else if (!strcmp(optarg, "OVM_NOTAR"))
1281 *criteria->dbType = DBTYPE_OVM_NOTAR;
1282 else if (!strcmp(optarg, "OVM_EXEKUT"))
1283 *criteria->dbType = DBTYPE_OVM_EXEKUT;
1284 else if (!strcmp(optarg, "OVM_REQ"))
1285 *criteria->dbType = DBTYPE_OVM_REQ;
1286 else {
1287 printf(_("%s: %s: Unknown box type\n"), argv[0], optarg);
1288 retval = -1;
1289 goto leave;
1291 break;
1293 case 'j':
1294 FILL_OR_LEAVE(criteria->ic, optarg);
1295 break;
1297 /* Person name */
1298 case 'f':
1299 CALLOC_OR_LEAVE(criteria->personName);
1300 FILL_OR_LEAVE(criteria->personName->pnFirstName, optarg);
1301 break;
1302 case 'm':
1303 CALLOC_OR_LEAVE(criteria->personName);
1304 FILL_OR_LEAVE(criteria->personName->pnMiddleName, optarg);
1305 break;
1306 case 'l':
1307 CALLOC_OR_LEAVE(criteria->personName);
1308 FILL_OR_LEAVE(criteria->personName->pnLastName, optarg);
1309 break;
1310 case 'b':
1311 CALLOC_OR_LEAVE(criteria->personName);
1312 FILL_OR_LEAVE(criteria->personName->pnLastNameAtBirth, optarg);
1313 break;
1314 case 's':
1315 FILL_OR_LEAVE(criteria->firmName, optarg);
1316 break;
1318 /* Birth */
1319 case 'd':
1320 CALLOC_OR_LEAVE(criteria->birthInfo);
1321 criteria->birthInfo->biDate = datestring2tm(optarg);
1322 if (!criteria->birthInfo->biDate) {
1323 printf(_("Error: Could not parse date: %s\n"), optarg);
1324 retval = -1;
1325 goto leave;
1327 break;
1328 case 'w':
1329 CALLOC_OR_LEAVE(criteria->birthInfo);
1330 FILL_OR_LEAVE(criteria->birthInfo->biCity, optarg);
1331 break;
1332 case 'y':
1333 CALLOC_OR_LEAVE(criteria->birthInfo);
1334 FILL_OR_LEAVE(criteria->birthInfo->biCounty, optarg);
1335 break;
1336 case 'c':
1337 CALLOC_OR_LEAVE(criteria->birthInfo);
1338 FILL_OR_LEAVE(criteria->birthInfo->biState, optarg);
1339 break;
1341 /* Address */
1342 case 'W':
1343 CALLOC_OR_LEAVE(criteria->address);
1344 FILL_OR_LEAVE(criteria->address->adCity, optarg);
1345 break;
1346 case 'S':
1347 CALLOC_OR_LEAVE(criteria->address);
1348 FILL_OR_LEAVE(criteria->address->adStreet, optarg);
1349 break;
1350 case 'z':
1351 CALLOC_OR_LEAVE(criteria->address);
1352 FILL_OR_LEAVE(criteria->address->adNumberInStreet, optarg);
1353 break;
1354 case 'Z':
1355 CALLOC_OR_LEAVE(criteria->address);
1356 FILL_OR_LEAVE(criteria->address->adNumberInMunicipality,
1357 optarg);
1358 break;
1359 case 'P':
1360 CALLOC_OR_LEAVE(criteria->address);
1361 FILL_OR_LEAVE(criteria->address->adZipCode, optarg);
1362 break;
1363 case 'C':
1364 CALLOC_OR_LEAVE(criteria->address);
1365 FILL_OR_LEAVE(criteria->address->adState, optarg);
1366 break;
1368 /* Other options */
1369 case 'n':
1370 FILL_OR_LEAVE(criteria->nationality, optarg);
1371 break;
1372 case 'e':
1373 FILL_OR_LEAVE(criteria->email, optarg);
1374 break;
1375 case 'p':
1376 FILL_OR_LEAVE(criteria->telNumber, optarg);
1377 break;
1378 case 'i':
1379 FILL_OR_LEAVE(criteria->identifier, optarg);
1380 break;
1381 case 'r':
1382 FILL_OR_LEAVE(criteria->registryCode, optarg);
1383 break;
1384 case 'a':
1385 criteria->dbState = malloc(sizeof(*criteria->dbState));
1386 if (!criteria->dbState) {
1387 printf(_("Error: Not enough memory\n"));
1388 retval = -1;
1389 goto leave;
1391 if (!strcmp(optarg, "ACCESSIBLE"))
1392 *criteria->dbState = DBSTATE_ACCESSIBLE;
1393 else if (!strcmp(optarg, "TEMP_UNACCESSIBLE"))
1394 *criteria->dbState = DBSTATE_TEMP_UNACCESSIBLE;
1395 else if (!strcmp(optarg, "NOT_YET_ACCESSIBLE"))
1396 *criteria->dbState = DBSTATE_NOT_YET_ACCESSIBLE;
1397 else if (!strcmp(optarg, "PERM_UNACCESSIBLE"))
1398 *criteria->dbState = DBSTATE_PERM_UNACCESSIBLE;
1399 else if (!strcmp(optarg, "REMOVED"))
1400 *criteria->dbState = DBSTATE_REMOVED;
1401 else {
1402 printf(_("%s: %s: Unknown box status\n"), argv[0], optarg);
1403 retval = -1;
1404 goto leave;
1406 break;
1407 case 'o':
1408 FILL_BOOLEAN_OR_LEAVE(criteria->dbEffectiveOVM, optarg);
1409 break;
1410 case 'k':
1411 FILL_BOOLEAN_OR_LEAVE(criteria->dbOpenAddressing, optarg);
1412 break;
1414 default:
1415 shi_find_box_usage(argv[0]);
1416 retval = -1;
1417 goto leave;
1421 /* There must be an option and all of them must be recognized, if not only
1422 * BOX_ID supplied */
1423 if (argc > 2 && optind != argc) {
1424 printf(_("Error: Superfluous argument\n"));
1425 shi_find_box_usage(argv[0]);
1426 retval = -1;
1427 goto leave;
1430 /* If only box ID is supplied use it */
1431 if (argc == 2 && argv[1] && *argv[1]) {
1432 criteria->dbID = locale2utf8(argv[1]);
1433 if (!criteria->dbID) {
1434 printf(_("Error: Not enough memory\n"));
1435 retval = -1;
1436 goto leave;
1440 printf(_("Searching boxes...\n"));
1441 err = isds_FindDataBox(cisds, criteria, &boxes);
1442 finish_isds_operation(cisds, err);
1443 if (err) return -1;
1445 for(item = boxes; item; item = item->next) {
1446 if (!item->data) continue;
1447 order++;
1449 printf(_("\n* Result #%d:\n"), order);
1450 format_DbOwnerInfo(item->data);
1453 leave:
1454 isds_DbOwnerInfo_free(&criteria);
1455 return retval;
1459 static void shi_stat_box_usage(const char *command) {
1460 printf(_(
1461 "Usage: %s BOX_ID...\n"
1462 "Get status of box with BOX_ID. More boxes can be specified.\n"),
1463 command);
1467 /* Get boxes status */
1468 static int shi_stat_box(int argc, const char **argv) {
1469 isds_error err;
1470 char *id = NULL;
1471 long int status;
1473 if (!argv || !*argv || argc < 2 || !argv[1]) {
1474 printf(_("Missing box ID\n"));
1475 shi_stat_box_usage((argv[0])?argv[0]:NULL);
1476 return -1;
1479 for (int i = 1; i < argc; i++) {
1480 if (!argv[i] || !*argv[i]) continue;
1482 free(id);
1483 id = locale2utf8(argv[i]);
1484 if (!id) {
1485 printf(_("%s: Could not covert box ID to UTF-8\n"), argv[i]);
1486 return -1;
1489 printf(_("Getting status of box `%s'...\n"), argv[i]);
1490 err = isds_CheckDataBox(cisds, id, &status);
1491 finish_isds_operation(cisds, err);
1492 if (err) return -1;
1494 printf(_("Status of box `%s': %s\n"),
1495 argv[i], DbState2string(&status));
1498 return 0;
1502 static void shi_compose_usage(const char *command) {
1503 printf(_(
1504 "Usage: %s OPTION...\n"
1505 "Compose and send a message to recipient defined by his BOX_ID.\n"
1506 "Each option requires an argument (if not stated otherwise):\n"
1507 " -s * message subject\n"
1508 "\n"
1509 "Recpient options:\n"
1510 " -b * recipient box ID\n"
1511 " -U organisation unit name\n"
1512 " -N organisation unit number\n"
1513 " -P to hands of given person\n"
1514 "\n"
1515 "Sender organisation structure options:\n"
1516 " -u unit name\n"
1517 " -n unit number\n"
1518 "\n"
1519 "Message identifier options:\n"
1520 " -r sender reference number\n"
1521 " -f sender file ID\n"
1522 " -R recipient reference number\n"
1523 " -F recipient file ID\n"
1524 "\n"
1525 "Legal title options:\n"
1526 " -y year act has been issued\n"
1527 " -a ordinar number of act in a yead\n"
1528 " -e section of the act\n"
1529 " -o paragraph of the act\n"
1530 " -i point of the paragraph of the act\n"
1531 "\n"
1532 "Delivery options:\n"
1533 " -p personal delivery required\n"
1534 " -t allow substitutable delivery\n"
1535 " -A non-OVM sender acts as public authority\n"
1536 "\n"
1537 "Document options:\n"
1538 " -d * read document from local file\n"
1539 " -D document name (defaults to base local file name)\n"
1540 " -m override MIME type (guessed on -d)\n"
1541 " -g document ID (must be unique per message)\n"
1542 " -G reference to other document using its ID\n"
1543 " -c document is digital signature of other document (NO argument\n"
1544 " allowed)\n"
1545 "\n"
1546 "Options marked with asterisk are mandatory, other are optional. Another soft\n"
1547 "dependecies can emerge upon using specific option. They are not mandated by\n"
1548 "ISDS currently, but client library or this program can force them to assure\n"
1549 "semantically complete message. Following soft dependecies are recommended:\n"
1550 " -y <=> -a act number and year must be used at the same time\n"
1551 " -i => -o act point requires act parahraph\n"
1552 " -o => -e act paragraph requires act section\n"
1553 " -e => -a act section requires act number\n"
1554 " -G => -g document with referenced ID must exist\n"
1555 " -c => -G signature must refer to signed document\n"
1556 " -c first document cannot be signature\n"
1557 "\n"
1558 "More documents can be attached to a message by repeating `-d' option.\n"
1559 "Document order will be preserved. Other document options affect immediately\n"
1560 "preceding `-d' document only. E.g. `-d /tmp/foo.pdf -m application/pdf\n"
1561 "-d /tmp/bar.txt -m text/plain' attaches first PDF file, then textual file.\n"
1562 "\n"
1563 "The same applies to recipient options that must start with box ID (-b).\n"
1564 "If more recipients specified, each of them will get a copy of composed\n"
1565 "message. ISDS will assign message identifier to each copy in turn.\n"
1567 command);
1571 static int shi_compose(int argc, const char **argv) {
1572 int opt;
1573 isds_error err;
1574 int retval = 0;
1575 struct isds_message *message = NULL;
1576 struct isds_envelope *envelope = NULL;
1577 struct isds_list *documents = NULL;
1578 struct isds_document *document = NULL;
1579 struct isds_list *copies = NULL, *copy_item = NULL;
1580 struct isds_message_copy *copy = NULL;
1581 char *message_id_locale = NULL, *recipient_id_locale = NULL,
1582 *dmStatus_locale = NULL;
1584 if (!argv || !argv[1] || !*argv[1]) {
1585 printf(_("Error: No argument supplied\n"));
1586 shi_compose_usage((argv)?argv[0]:NULL);
1587 return -1;
1590 message = calloc(1, sizeof(*message));
1591 if (!message) {
1592 printf(_("Error: Not enough memory\n"));
1593 retval = -1;
1594 goto leave;
1596 envelope = calloc(1, sizeof(*envelope));
1597 if (!envelope) {
1598 printf(_("Error: Not enough memory\n"));
1599 retval = -1;
1600 goto leave;
1602 message->envelope = envelope;
1604 /* Parse options */
1605 optind = 0;
1606 while ((opt = getopt(argc, (char * const *)argv, "s:" "b:U:N:P:" "u:n:"
1607 "r:f:R:F:" "y:a:e:o:i:" "p:t:A:" "d:D:m:g:G:c"
1608 )) != -1) {
1609 switch (opt) {
1610 case 's':
1611 FILL_OR_LEAVE(envelope->dmAnnotation, optarg);
1612 break;
1614 /* Recipient options */
1615 case 'b':
1616 copy = NULL;
1617 if (!copies) {
1618 /* First recipient */
1619 CALLOC_OR_LEAVE(copies);
1620 copies->destructor =
1621 (void(*)(void **)) isds_message_copy_free;
1622 copy_item = copies;
1623 } else {
1624 /* Next recipient */
1625 CALLOC_OR_LEAVE(copy_item->next);
1626 copy_item->next->destructor =
1627 (void(*)(void **)) isds_message_copy_free;
1628 copy_item = copy_item->next;
1630 CALLOC_OR_LEAVE(copy);
1631 copy_item->data = copy;
1633 /* Copy recipient box ID */
1634 FILL_OR_LEAVE(copy->dbIDRecipient, optarg);
1635 break;
1636 case 'U':
1637 if (!copy) {
1638 printf(_("Error: %s: Recipient box ID (-b) must precede "
1639 "recipient organisation unit name (-%c)\n"),
1640 optarg, opt);
1641 retval = -1;
1642 goto leave;
1644 FILL_OR_LEAVE(copy->dmRecipientOrgUnit, optarg);
1645 break;
1646 case 'N':
1647 if (!copy) {
1648 printf(_("Error: %s: Recipient box ID (-b) must precede "
1649 "recipient organisation unit number (-%c)\n"),
1650 optarg, opt);
1651 retval = -1;
1652 goto leave;
1654 FILL_LONGINT_OR_LEAVE(copy->dmRecipientOrgUnitNum, optarg);
1655 break;
1656 case 'P':
1657 if (!copy) {
1658 printf(_("Error: %s: Recipient box ID (-b) must precede "
1659 "to-hands option (-%c)\n"), optarg, opt);
1660 retval = -1;
1661 goto leave;
1663 FILL_OR_LEAVE(copy->dmToHands, optarg);
1664 break;
1666 /* Sender organisation structure options */
1667 case 'u':
1668 FILL_OR_LEAVE(envelope->dmSenderOrgUnit, optarg);
1669 break;
1670 case 'n':
1671 FILL_LONGINT_OR_LEAVE(envelope->dmSenderOrgUnitNum, optarg);
1672 break;
1674 /* Messae identifier options */
1675 case 'r':
1676 FILL_OR_LEAVE(envelope->dmSenderRefNumber, optarg);
1677 break;
1678 case 'f':
1679 FILL_OR_LEAVE(envelope->dmSenderIdent, optarg);
1680 break;
1681 case 'R':
1682 FILL_OR_LEAVE(envelope->dmRecipientRefNumber, optarg);
1683 break;
1684 case 'F':
1685 FILL_OR_LEAVE(envelope->dmRecipientIdent, optarg);
1686 break;
1688 /* Legal title options */
1689 case 'y':
1690 FILL_LONGINT_OR_LEAVE(envelope->dmLegalTitleYear, optarg);
1691 break;
1692 case 'a':
1693 FILL_LONGINT_OR_LEAVE(envelope->dmLegalTitleLaw, optarg);
1694 break;
1695 case 'e':
1696 FILL_OR_LEAVE(envelope->dmLegalTitleSect, optarg);
1697 break;
1698 case 'o':
1699 FILL_OR_LEAVE(envelope->dmLegalTitlePar, optarg);
1700 break;
1701 case 'i':
1702 FILL_OR_LEAVE(envelope->dmLegalTitlePoint, optarg);
1703 break;
1705 /* Delivery options */
1706 case 'p':
1707 FILL_BOOLEAN_OR_LEAVE(envelope->dmPersonalDelivery, optarg);
1708 break;
1709 case 't':
1710 FILL_BOOLEAN_OR_LEAVE(envelope->dmAllowSubstDelivery, optarg);
1711 break;
1712 case 'A':
1713 FILL_BOOLEAN_OR_LEAVE(envelope->dmOVM, optarg);
1714 break;
1716 /* Document options */
1717 case 'd':
1718 document = NULL;
1719 if (!documents) {
1720 /* First document */
1721 CALLOC_OR_LEAVE(message->documents);
1722 message->documents->destructor =
1723 (void(*)(void **)) isds_document_free;
1724 documents = message->documents;
1725 CALLOC_OR_LEAVE(document);
1726 documents->data = document;
1727 document->dmFileMetaType = FILEMETATYPE_MAIN;
1728 } else {
1729 /* Next document */
1730 CALLOC_OR_LEAVE(documents->next);
1731 documents->next->destructor =
1732 (void(*)(void **)) isds_document_free;
1733 documents = documents->next;
1734 CALLOC_OR_LEAVE(document);
1735 documents->data = document;
1736 document->dmFileMetaType = FILEMETATYPE_ENCLOSURE;
1739 if (load_data_from_file(optarg, &document->data,
1740 &document->data_length, &document->dmMimeType)) {
1741 retval = -1;
1742 goto leave;
1744 /* XXX: POSIX basename() modifies argument */
1745 FILL_OR_LEAVE(document->dmFileDescr, basename(optarg));
1746 break;
1747 case 'D':
1748 if (!document) {
1749 printf(_("Error: %s: Document file (-d) must precede "
1750 "document name (-%c)\n"), optarg, opt);
1751 retval = -1;
1752 goto leave;
1754 FILL_OR_LEAVE(document->dmFileDescr, optarg);
1755 break;
1756 case 'm':
1757 if (!document) {
1758 printf(_("Error: %s: Document file (-d) must precede "
1759 "MIME type (-%c)\n"), optarg, opt);
1760 retval = -1;
1761 goto leave;
1763 FILL_OR_LEAVE(document->dmMimeType, optarg);
1764 break;
1765 case 'g':
1766 if (!document) {
1767 printf(_("Error: %s: Document file (-d) must precede "
1768 "document ID (-%c)\n"), optarg, opt);
1769 retval = -1;
1770 goto leave;
1772 FILL_OR_LEAVE(document->dmFileGuid, optarg);
1773 break;
1774 case 'G':
1775 if (!document) {
1776 printf(_("Error: %s: Document file (-d) must precede "
1777 "document reference (-%c)\n"), optarg, opt);
1778 retval = -1;
1779 goto leave;
1781 FILL_OR_LEAVE(document->dmUpFileGuid, optarg);
1782 break;
1783 case 'c':
1784 if (!document) {
1785 printf(_("Error: Document file (-d) must precede "
1786 "document signature type (-%c)\n"), opt);
1787 retval = -1;
1788 goto leave;
1790 document->dmFileMetaType = FILEMETATYPE_SIGNATURE;
1791 break;
1793 default:
1794 shi_compose_usage(argv[0]);
1795 retval = -1;
1796 goto leave;
1800 /* All options must be recognized */
1801 if (optind != argc) {
1802 printf(_("Error: Superfluous argument\n"));
1803 shi_compose_usage(argv[0]);
1804 retval = -1;
1805 goto leave;
1808 if (!copies) {
1809 printf(_("Error: No recipient box ID specified\n"));
1810 shi_compose_usage(argv[0]);
1811 retval = -1;
1812 goto leave;
1815 /* TODO: Check Legal Title soft dependencies */
1817 /* Preview */
1818 printf(_("Following message has been composed:\n"));
1819 format_message(message);
1820 printf(_("Following recipients have been specified:\n"));
1821 format_copies(copies);
1822 /* TODO: Confirmation, correction */
1824 /* Send a message */
1825 printf(_("Sending message...\n"));
1826 err = isds_send_message_to_multiple_recipients(cisds, message, copies);
1827 finish_isds_operation(cisds, err);
1828 if (err && err != IE_PARTIAL_SUCCESS) {
1829 retval = -1;
1830 goto leave;
1833 /* Show results for each copy */
1834 for (copy_item = copies; copy_item; copy_item = copy_item->next) {
1835 if (!copy_item->data) continue;
1836 copy = (struct isds_message_copy *) copy_item->data;
1837 recipient_id_locale = utf82locale(copy->dbIDRecipient);
1839 if (copy->error) {
1840 retval = -1;
1841 if (copy->dmStatus) dmStatus_locale = utf82locale(copy->dmStatus);
1842 if (dmStatus_locale)
1843 printf(_("%s: Failed: %s: %s\n"),
1844 recipient_id_locale,
1845 isds_strerror(copy->error), dmStatus_locale);
1846 else
1847 printf(_("%s: Failed: %s\n"), recipient_id_locale,
1848 isds_strerror(copy->error));
1849 zfree(dmStatus_locale);
1850 } else {
1851 message_id_locale = utf82locale(copy->dmID);
1852 printf(_("%s: Succeded. Assigned message ID: %s\n"),
1853 recipient_id_locale, message_id_locale);
1854 free(message_id_locale);
1857 free(recipient_id_locale);
1860 leave:
1861 isds_message_free(&message);
1862 isds_list_free(&copies);
1863 return retval;
1866 #undef FILL_LONGINT_OR_LEAVE
1867 #undef FILL_BOOLEAN_OR_LEAVE
1868 #undef CALLOC_OR_LEAVE
1869 #undef FILL_OR_LEAVE
1872 static void shi_delivery_usage(const char *command) {
1873 printf(_(
1874 "Usage: %s MESSAGE_ID\n"
1875 "Get delivery data about message with MESSAGE_ID.\n"),
1876 command);
1880 static int shi_delivery(int argc, const char **argv) {
1881 isds_error err;
1882 const char *id;
1884 if (!argv || !argv[1] || !*argv[1]) {
1885 shi_delivery_usage(argv[0]);
1886 return -1;
1888 id = argv[1];
1890 printf(_("Getting delivery info...\n"));
1891 err = isds_get_signed_delivery_info(cisds, id, &message);
1892 finish_isds_operation(cisds, err);
1893 if (err) {
1894 set_prompt(NULL);
1895 select_completition(COMPL_COMMAND);
1896 return -1;
1899 format_message(message);
1901 if (message->envelope && message->envelope->dmID)
1902 set_prompt(_("%s %s"), argv[0], message->envelope->dmID);
1903 else
1904 set_prompt("%s", argv[0]);
1905 select_completition(COMPL_MSG);
1906 return 0;
1910 static int shi_dump_message(int argc, const char **argv) {
1911 if (!message) {
1912 printf(_("No message loaded\n"));
1913 return -1;
1916 print_message(message);
1917 return 0;
1921 static void shi_hash_usage(const char *command) {
1922 printf(_(
1923 "Usage: %s [MESSAGE_ID]\n"
1924 "Retrieve message hash stored in ISDS.\n"
1925 "If MESSAGE_ID is defined, query for that message.\n"
1926 "Otherwise use current message.\n"),
1927 command);
1931 static int shi_hash(int argc, const char **argv) {
1932 isds_error err;
1933 const char *id = NULL;
1934 struct isds_hash *hash = NULL;
1935 char *hash_string = NULL;
1937 if (!argv || argc > 2) {
1938 shi_hash_usage((argv)?argv[0]:NULL);
1939 return -1;
1941 if (argc == 2 && argv[1] && *argv[1])
1942 id = argv[1];
1943 else {
1944 if (!message) {
1945 printf(_("No message loaded\n"));
1946 return -1;
1948 if (!message->envelope || !message->envelope->dmID) {
1949 printf(_("Current message is missing ID\n"));
1950 return -1;
1952 id = message->envelope->dmID;
1955 printf(_("Getting message hash...\n"));
1956 err = isds_download_message_hash(cisds, id, &hash);
1957 finish_isds_operation(cisds, err);
1958 if (err) return -1;
1960 hash_string = hash2string(hash);
1961 printf(_("ISDS states message with `%s' ID has following hash:\n%s\n"),
1962 id, hash_string);
1964 free(hash_string);
1965 isds_hash_free(&hash);
1966 return 0;
1970 static int shi_verify(int argc, const char **argv) {
1971 isds_error err;
1972 int retval = 0;
1973 struct isds_hash *retrieved_hash = NULL, *stored_hash = NULL;
1974 char *hash_string = NULL;
1975 size_t width = 4;
1977 if (!message) {
1978 printf(_("No message loaded\n"));
1979 return -1;
1982 if (!message->envelope) {
1983 printf(_("Current message is missing envelope\n"));
1984 return -1;
1986 stored_hash = message->envelope->hash;
1987 message->envelope->hash = NULL;
1989 if (message->envelope->dmID) {
1990 /* Verify remote hash */
1991 printf(_("Remote hash check:\n"));
1993 printf(_("Getting message hash...\n"));
1994 err = isds_download_message_hash(cisds, message->envelope->dmID,
1995 &retrieved_hash);
1996 finish_isds_operation(cisds, err);
1998 if (retrieved_hash) {
1999 hash_string = hash2string(retrieved_hash);
2000 fhprint(stdout, _("Retrieved:"), width);
2001 printf("%s\n", hash_string);
2002 zfree(hash_string);
2005 if (retrieved_hash && message->raw) {
2006 err = isds_compute_message_hash(cisds, message,
2007 retrieved_hash->algorithm);
2008 finish_isds_operation(cisds, err);
2010 if (!err) {
2011 hash_string = hash2string(message->envelope->hash);
2012 fhprint(stdout, _("Computed:"), width);
2013 printf("%s\n", hash_string);
2014 zfree(hash_string);
2018 err = isds_hash_cmp(retrieved_hash, message->envelope->hash);
2019 switch (err) {
2020 case IE_SUCCESS:
2021 printf(_("Hashes match.\n")); break;
2022 case IE_NOTUNIQ:
2023 printf(_("Hashes do not match.\n"));
2024 retval = -1;
2025 break;
2026 default:
2027 printf(_("Hashes could not be compared.\n"));
2028 retval = -1;
2029 break;
2032 free(retrieved_hash);
2036 if (stored_hash) {
2037 /* Verify stored hash */
2038 printf(_("Stored hash check:\n"));
2040 hash_string = hash2string(stored_hash);
2041 fhprint(stdout, _("Stored:"), width);
2042 printf("%s\n", hash_string);
2043 zfree(hash_string);
2045 if (message->raw) {
2046 err = isds_compute_message_hash(cisds, message,
2047 stored_hash->algorithm);
2048 finish_isds_operation(cisds, err);
2050 if (!err) {
2051 hash_string = hash2string(message->envelope->hash);
2052 fhprint(stdout, _("Computed:"), width);
2053 printf("%s\n", hash_string);
2054 zfree(hash_string);
2058 err = isds_hash_cmp(stored_hash, message->envelope->hash);
2059 switch (err) {
2060 case IE_SUCCESS:
2061 printf(_("Hashes match.\n")); break;
2062 case IE_NOTUNIQ:
2063 printf(_("Hashes do not match.\n"));
2064 retval = -1;
2065 break;
2066 default:
2067 printf(_("Hashes could not be compared.\n"));
2068 retval = -1;
2069 break;
2072 isds_hash_free(&message->envelope->hash);
2075 message->envelope->hash = stored_hash;
2076 return retval;
2080 static int shi_authenticate(int argc, const char **argv) {
2081 isds_error err;
2082 int retval = 0;
2084 if (!message) {
2085 printf(_("No message loaded\n"));
2086 return -1;
2088 if (!message->raw || message->raw_length == 0) {
2089 printf(_("Current message is missing raw representation\n"));
2090 return -1;
2093 printf(_("Submitting message to authenticity check...\n"));
2094 err = isds_authenticate_message(cisds, message->raw, message->raw_length);
2095 finish_isds_operation(cisds, (err == IE_NOTUNIQ) ? IE_SUCCESS : err);
2097 switch (err) {
2098 case IE_SUCCESS:
2099 printf(_("Message originates in ISDS.\n")); break;
2100 case IE_NOTUNIQ:
2101 printf(_("Message is unknown to ISDS or has been tampered.\n"));
2102 retval = -1;
2103 break;
2104 default:
2105 retval = -1;
2106 break;
2109 return retval;
2113 static void shi_accept_message_usage(const char *command) {
2114 printf(_(
2115 "Usage: %s [MESSAGE_ID...]\n"
2116 "Accept commercial message moving its state to received.\n"
2117 "If MESSAGE_ID is defined, accept that message. More messages can be specified.\n"
2118 "Otherwise accept all commercial incoming messages.\n"),
2119 command);
2123 static int shi_accept_message(int argc, const char **argv) {
2124 isds_error err;
2125 char *id = NULL;
2127 /* Proeccess messages named in argv */
2128 for (int i = 1; i < argc; i++) {
2129 if (!argv[i] || !*argv[i]) continue;
2131 id = locale2utf8(argv[i]);
2132 if (!id) {
2133 printf(_("Error: Could not convert message ID to UTF-8: %s\n"),
2134 argv[i]);
2135 return -1;
2138 printf(_("Accepting message `%s'...\n"), argv[i]);
2139 err = isds_mark_message_received(cisds, id);
2140 finish_isds_operation(cisds, err);
2141 if (err) {
2142 free(id);
2143 return -1;
2146 printf(_("Message `%s' accepted\n"), argv[i]);
2147 free(id);
2150 if (argc < 2) {
2151 /* TODO: list commercial not receievd messages and accept all of them
2152 * */
2153 printf(_("Error: No message ID supplied. Accepting all commercial "
2154 "messages not implemented yet.\n"));
2155 return -1;
2158 return 0;
2162 /* Mark message as read. At one form of ID must be provided.
2163 * @id is UTF-8 encoded message ID
2164 * @id_locale is locale encoded message ID. @id takes preference. */
2165 static int do_read_message(const char *id, const char *id_locale) {
2166 _Bool static_id;
2167 isds_error err;
2169 if ((!id || !*id) && (!id_locale || !*id_locale)) return -1;
2171 if (id) {
2172 static_id = 1;
2173 id_locale = utf82locale(id);
2174 } else {
2175 static_id = 0;
2176 id = locale2utf8(id_locale);
2177 if (!id) {
2178 printf(_("Error: Could not convert message ID to UTF-8: %s\n"),
2179 id_locale);
2180 return -1;
2184 printf(_("Marking message `%s' as read...\n"), id_locale);
2185 err = isds_mark_message_read(cisds, id);
2186 finish_isds_operation(cisds, err);
2188 if (!err)
2189 printf(_("Message `%s' marked as read\n"), id_locale);
2191 if (static_id) free((char *) id_locale);
2192 else free((char *) id);
2194 return (err) ? -1 : 0;
2198 static void shi_read_message_usage(const char *command) {
2199 printf(_(
2200 "Usage: %s [MESSAGE_ID...]\n"
2201 "Mark message as read moving its state to read.\n"
2202 "\n"
2203 "When new incoming message is download, its state is not changed on server.\n"
2204 "Client must mark such message as read explicitely. You can use this command\n"
2205 "to do so, if not done automatically at download time by your client.\n"
2206 "\n"
2207 "If MESSAGE_ID is defined, mark that message. More messages can be specified.\n"
2208 "Otherwise marks currently loaded message.\n"),
2209 command);
2213 static int shi_read_message(int argc, const char **argv) {
2214 for (int i = 1; i < argc; i++) {
2215 if (!argv[i] || !*argv[i]) continue;
2216 if (do_read_message(NULL, argv[i]))
2217 return -1;
2220 if (argc < 2) {
2221 if (!message) {
2222 printf(_("No message loaded\n"));
2223 return -1;
2225 if (!message->envelope) {
2226 printf(_("Loaded message is missing envelope\n"));
2227 return -1;
2229 if (!message->envelope->dmID || !*message->envelope->dmID) {
2230 printf(_("Loaded message is missing ID\n"));
2231 return -1;
2233 return do_read_message(message->envelope->dmID, NULL);
2236 return 0;
2240 static int shi_show_message(int argc, const char **argv) {
2241 if (!message) {
2242 printf(_("No message loaded\n"));
2243 return -1;
2246 format_message(message);
2247 return 0;
2251 static void shi_incoming_message_usage(const char *command) {
2252 printf(_(
2253 "Usage: %s MESSAGE_ID\n"
2254 "Get incomming message with MESSAGE_ID.\n"),
2255 command);
2259 static int shi_incoming_message(int argc, const char **argv) {
2260 isds_error err;
2261 const char *id;
2263 if (!argv || !argv[1] || !*argv[1]) {
2264 shi_incoming_message_usage(argv[0]);
2265 return -1;
2267 id = argv[1];
2269 printf(_("Getting incoming message...\n"));
2270 err = isds_get_signed_received_message(cisds, id, &message);
2271 finish_isds_operation(cisds, err);
2272 if (err) {
2273 set_prompt(NULL);
2274 select_completition(COMPL_COMMAND);
2275 return -1;
2278 format_message(message);
2280 if (message->envelope && message->envelope->dmID)
2281 set_prompt(_("%s %s"), argv[0], message->envelope->dmID);
2282 else
2283 set_prompt("%s", argv[0]);
2284 select_completition(COMPL_MSG);
2285 return 0;
2289 static void shi_outgoing_message_usage(const char *command) {
2290 printf(_(
2291 "Usage: %s MESSAGE_ID\n"
2292 "Get outgoing message with MESSAGE_ID.\n"),
2293 command);
2297 static int shi_outgoing_message(int argc, const char **argv) {
2298 isds_error err;
2299 const char *id;
2301 if (!argv || !argv[1] || !*argv[1]) {
2302 shi_outgoing_message_usage(argv[0]);
2303 return -1;
2305 id = argv[1];
2307 printf(_("Getting outgoing message...\n"));
2308 err = isds_get_signed_sent_message(cisds, id, &message);
2309 finish_isds_operation(cisds, err);
2310 if (err) {
2311 set_prompt(NULL);
2312 select_completition(COMPL_COMMAND);
2313 return -1;
2316 format_message(message);
2317 if (message->envelope && message->envelope->dmID)
2318 set_prompt(_("%s %s"), argv[0], message->envelope->dmID);
2319 else
2320 set_prompt("%s", argv[0]);
2321 select_completition(COMPL_MSG);
2322 return 0;
2326 static void shi_load_anything_usage(const char *command) {
2327 printf(_(
2328 "Usage: %s FILE\n"
2329 "Load message or message delivery details from local FILE.\n"),
2330 command);
2334 static int shi_load_anything(int argc, const char **argv) {
2335 int fd;
2336 void *buffer = NULL;
2337 size_t length;
2338 isds_raw_type raw_type;
2339 isds_error err;
2340 char *type_name = NULL;
2342 if (!argv || !argv[1] || !*argv[1]) {
2343 shi_load_anything_usage((argv)?argv[0]:NULL);
2344 return -1;
2347 printf(_("Loading file `%s'...\n"), argv[1]);
2349 if (mmap_file(argv[1], &fd, &buffer, &length)) return -1;
2351 printf(_("Detecting file format...\n"));
2352 err = isds_guess_raw_type(cisds, &raw_type, buffer, length);
2353 finish_isds_operation(cisds, err);
2355 if (err) {
2356 if (err == IE_NOTSUP)
2357 printf(_("Unknown format. Could not parse the file.\n"));
2358 else
2359 printf(_("Error while detecting format. "
2360 "Could not parse the file.\n"));
2361 } else {
2362 switch (raw_type) {
2363 case RAWTYPE_INCOMING_MESSAGE:
2364 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
2365 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
2366 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
2367 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
2368 err = isds_load_message(cisds, raw_type,
2369 buffer, length, &message, BUFFER_COPY);
2370 finish_isds_operation(cisds, err);
2371 type_name = N_("message");
2372 break;
2374 case RAWTYPE_DELIVERYINFO:
2375 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO:
2376 case RAWTYPE_CMS_SIGNED_DELIVERYINFO:
2377 err = isds_load_delivery_info(cisds, raw_type,
2378 buffer, length, &message, BUFFER_COPY);
2379 finish_isds_operation(cisds, err);
2380 type_name = N_("delivery");
2381 break;
2383 default:
2384 printf(_("Unsupported format. Could not parse the file.\n"));
2385 err = IE_NOTSUP;
2389 munmap_file(fd, buffer, length);
2391 if (err) {
2392 set_prompt(NULL);
2393 select_completition(COMPL_COMMAND);
2394 return -1;
2397 format_message(message);
2399 if (message->envelope && message->envelope->dmID)
2400 set_prompt(_("%s %s"), _(type_name), message->envelope->dmID);
2401 else
2402 set_prompt("%s", _(type_name));
2403 select_completition(COMPL_MSG);
2404 return 0;
2408 static void shi_save_message_usage(const char *command) {
2409 printf(_(
2410 "Usage: %s FILE\n"
2411 "Save message into local FILE.\n"),
2412 command);
2416 static const char *raw_type2mime(isds_raw_type raw_type) {
2417 switch (raw_type) {
2418 case RAWTYPE_INCOMING_MESSAGE:
2419 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
2420 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
2421 case RAWTYPE_DELIVERYINFO:
2422 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO:
2423 return "text/xml";
2425 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
2426 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
2427 case RAWTYPE_CMS_SIGNED_DELIVERYINFO:
2428 return "application/pkcs7-mime";
2430 default:
2431 return NULL;
2436 static int shi_save_message(int argc, const char **argv) {
2437 if (!argv || !argv[1] || !*argv[1]) {
2438 shi_save_message_usage(argv[0]);
2439 return -1;
2442 if (!message) {
2443 printf(_("No message loaded\n"));
2444 return -1;
2446 if (!message->raw || message->raw_length == 0) {
2447 printf(_("Loaded message is missing raw represantion\n"));
2448 return -1;
2451 return save_data_to_file(argv[1], message->raw, message->raw_length,
2452 raw_type2mime(message->raw_type));
2456 /* Return document of current message identified by ordinar number expressed
2457 * as string. In case of error return NULL. */
2458 static const struct isds_document *locate_document_by_ordinal_string(
2459 const char *number) {
2460 const struct isds_list *item;
2461 const struct isds_document *document = NULL;
2462 int ordinar, i;
2464 if (!number) return NULL;
2466 ordinar = atoi(number);
2467 if (ordinar <= 0) {
2468 printf(_("%s: Document number must be positive number\n"), number);
2469 return NULL;
2472 if (!message) {
2473 printf(_("No message loaded\n"));
2474 return NULL;
2477 /* Find document */
2478 for (item = message->documents, i = 0; item; item = item->next) {
2479 if (!item->data) continue;
2480 if (++i == ordinar) {
2481 document = (const struct isds_document *) item->data;
2482 break;
2485 if (i != ordinar) {
2486 printf(_("Message does not contain document #%d\n"), ordinar);
2487 return NULL;
2490 return document;
2494 static void shi_save_document_usage(const char *command) {
2495 printf(_(
2496 "Usage: %s NUMBER [DESTINATION]\n"
2497 "Save document having ordinar NUMBER within current message into local file.\n"
2498 "If DESTINATION is file (or does not exist yet), document will be saved into\n"
2499 "this file.\n"
2500 "If DESTINATION is existing directory, file name equaled to document name\n"
2501 "will be saved into DESTINATION.\n"
2502 "If DESTINATION is missing, document name will be used as file name and\n"
2503 "saved into working directory.\n"
2504 "Be aware that document name does not embed malicious characters (slashes).\n"
2506 command);
2510 static int shi_save_document(int argc, const char **argv) {
2511 const struct isds_document *document;
2512 const char *dirname = NULL;
2513 char *filename = NULL, *path = NULL;
2514 int retval = 0;
2516 if (!argv || !argv[1] || !*argv[1] || argc > 3) {
2517 shi_save_document_usage(argv[0]);
2518 return -1;
2521 document = locate_document_by_ordinal_string(argv[1]);
2522 if (!document) return -1;
2524 /* Select directory and file name */
2525 if (argv[2] && *argv[2]) {
2526 if (!is_directory(argv[2])) {
2527 dirname = argv[2];
2528 } else {
2529 filename = strdup(argv[2]);
2530 if (!filename) {
2531 printf(_("Not enough memory\n"));
2532 return -1;
2536 if (!filename && document->dmFileDescr && &document->dmFileDescr) {
2537 filename = utf82locale(document->dmFileDescr);
2538 if (!filename) {
2539 printf(_("Not enough memory\n"));
2540 return -1;
2543 if (!filename) {
2544 printf(_("File name neither supplied, nor document name exists\n"
2545 "Please, supply one.\n"));
2546 return -1;
2549 /* Build path */
2550 if (dirname) {
2551 path = astrcat3(dirname, "/", filename);
2552 zfree(filename);
2553 } else {
2554 path = filename;
2555 filename = NULL;
2557 if (!path) {
2558 printf(_("Not enough memory\n"));
2559 return -1;
2562 /* Save document */
2563 retval = save_data_to_file(path, document->data, document->data_length,
2564 document->dmMimeType);
2565 free(path);
2566 return retval;
2570 static void shi_save_stamp_usage(const char *command) {
2571 printf(_(
2572 "Usage: %s FILE\n"
2573 "Save message time stamp into local FILE.\n"),
2574 command);
2578 static int shi_save_stamp(int argc, const char **argv) {
2579 if (!argv || !argv[1] || !*argv[1]) {
2580 shi_save_message_usage(argv[0]);
2581 return -1;
2584 if (!message) {
2585 printf(_("No message loaded\n"));
2586 return -1;
2588 if (!message->envelope || !message->envelope->timestamp||
2589 message->envelope->timestamp_length == 0) {
2590 printf(_("Loaded message is missing time stamp\n"));
2591 return -1;
2594 return save_data_to_file(argv[1],
2595 message->envelope->timestamp, message->envelope->timestamp_length,
2596 "application/timestamp-reply");
2600 static int shi_show_list(int argc, const char **argv) {
2601 if (!messages) {
2602 printf(_("No message list loaded\n"));
2603 return -1;
2606 printf((messages_are_outgoing) ?
2607 ngettext("You have %'lu outgoing message\n",
2608 "You have %'lu outgoing messages\n", total_messages) :
2609 ngettext("You have %'lu incomming message\n",
2610 "You have %'lu incomming messages\n", total_messages),
2611 total_messages);
2612 print_message_list(messages, messages_are_outgoing);
2613 return 0;
2617 static int shi_list_incoming(int argc, const char **argv) {
2618 isds_error err;
2620 printf(_("Listing incoming messages...\n"));
2621 err = isds_get_list_of_received_messages(cisds, NULL, NULL, NULL,
2622 MESSAGESTATE_ANY, 0, &total_messages, &messages);
2623 finish_isds_operation(cisds, err);
2624 if (err) {
2625 set_prompt(NULL);
2626 select_completition(COMPL_COMMAND);
2627 return -1;
2629 messages_are_outgoing = 0;
2631 shi_show_list(0, NULL);
2633 set_prompt(_("%s %'lu"), argv[0], total_messages);
2634 select_completition(COMPL_LIST);
2635 return 0;
2639 static int shi_list_outgoing(int argc, const char **argv) {
2640 isds_error err;
2642 printf(_("Listing outgoing messages...\n"));
2643 err = isds_get_list_of_sent_messages(cisds, NULL, NULL, NULL,
2644 MESSAGESTATE_ANY, 0, &total_messages, &messages);
2645 finish_isds_operation(cisds, err);
2646 if (err) {
2647 set_prompt(NULL);
2648 select_completition(COMPL_COMMAND);
2649 return -1;
2651 messages_are_outgoing = 1;
2653 shi_show_list(0, NULL);
2655 set_prompt(_("%s %'lu"), argv[0], total_messages);
2656 select_completition(COMPL_LIST);
2657 return 0;
2661 /* Submit document for conversion and print assigned identifier */
2662 static int do_convert(const struct isds_document *document) {
2663 isds_error err;
2664 char *id = NULL;
2665 struct tm *date = NULL;
2667 if (!document) return -1;
2669 printf(_("Submitting document for authorized conversion...\n"));
2671 err = czp_convert_document(czechpoint, document, &id, &date);
2672 finish_isds_operation(czechpoint, err);
2674 if (!err) {
2675 char *name_locale = utf82locale(document->dmFileDescr);
2676 char *date_string = tm2string(date);
2677 char *id_locale = utf82locale(id);
2678 printf(_(
2679 "Document submitted for authorized conversion successfully under name\n"
2680 "`%s' on %s.\n"
2681 "Submit identifier assigned by Czech POINT deposit is `%s'.\n"),
2682 name_locale, date_string, id_locale);
2683 free(name_locale);
2684 free(date_string);
2685 free(id_locale);
2686 printf(_("Be ware that submitted document has restricted lifetime "
2687 "(30 days).\n"));
2688 printf(_("See <%s> for more details.\n"), CZPDEPOSIT_URL);
2691 free(id); free(date);
2692 return (err) ? -1 : 0;
2696 static void shi_convert_file_usage(const char *command) {
2697 printf(_(
2698 "Usage: %s FILE [NAME]\n"
2699 "Submit local FILE to authorized conversion under NAME. If NAME is missing,\n"
2700 "it will use FILE name.\n"), command);
2701 printf(_(
2702 "\n"
2703 "If Czech POINT deposit accepts document, it will return document identifier\n"
2704 "that user is supposed to provide to officer at Czech POINT contact place.\n"
2705 "Currently only PDF 1.3 and higher version files are accepted.\n"));
2706 printf(_("See <%s> for more details.\n"), CZPDEPOSIT_URL);
2710 static int shi_convert_file(int argc, const char **argv) {
2711 int fd;
2712 struct isds_document document;
2713 int retval = 0;
2715 if (!argv || argc > 3 || !argv[1] || !*argv[1]) {
2716 shi_convert_file_usage((argv)?argv[0]:NULL);
2717 return -1;
2720 memset(&document, 0, sizeof(document));
2722 if (argc == 3 && argv[2] && *argv[2])
2723 document.dmFileDescr = locale2utf8(argv[2]);
2724 else
2725 document.dmFileDescr = locale2utf8(argv[1]);
2726 if (!document.dmFileDescr) {
2727 printf(_("Could not convert document name to UTF-8\n"));
2728 return -1;
2731 printf(_("Loading document from file `%s'...\n"), argv[1]);
2732 if (mmap_file(argv[1], &fd, &document.data, &document.data_length)) {
2733 free(document.dmFileDescr);
2734 return -1;
2737 retval = do_convert(&document);
2739 munmap_file(fd, document.data, document.data_length);
2740 free(document.dmFileDescr);
2741 return retval;
2745 static void shi_convert_document_usage(const char *command) {
2746 printf(_(
2747 "Usage: %s NUMBER\n"
2748 "Submit message document with ordinal NUMBER to authorized conversion.\n"),
2749 command);
2750 printf(_(
2751 "\n"
2752 "If Czech POINT deposit accepts document, it will return document identifier\n"
2753 "that user is supposed to provide to officer at Czech POINT contact place.\n"
2754 "Currently only PDF 1.3 and higher version files are accepted.\n"));
2755 printf(_("See <%s> for more details.\n"), CZPDEPOSIT_URL);
2759 static int shi_convert_document(int argc, const char **argv) {
2760 const struct isds_document *document;
2762 if (!argv || !argv[1] || !*argv[1]) {
2763 shi_convert_document_usage((argv)?argv[0]:NULL);
2764 return -1;
2767 document = locate_document_by_ordinal_string(argv[1]);
2768 if (!document) return -1;
2770 return do_convert(document);
2774 #if ENABLE_DEBUG
2775 static void shi_print_usage(const char *command) {
2776 printf(_(
2777 "Usage: %s STRING LENGTH\n"
2778 "Prints STRING into LENGTH columns. Negative LENGHT means not to cut\n"
2779 "overflowing string.\n"
2780 "This should be locale and terminal agnostic.\n"),
2781 command);
2785 static int shi_print(int argc, const char **argv) {
2786 long int width;
2788 if (!argv || !argv[1] || !argv[2]) {
2789 shi_print_usage((argv)?argv[0]:NULL);
2790 return -1;
2793 width = strtol(argv[2], NULL, 10);
2794 if (width < INT_MIN) {
2795 fprintf(stderr,
2796 _("Length argument must not lesser than %d.\n"), INT_MIN);
2797 return -1;
2799 if (width > INT_MAX) {
2800 fprintf(stderr,
2801 _("Length argument must not be greater than %d.\n"), INT_MAX);
2802 return -1;
2805 printf(_(">"));
2806 fnprint(stdout, argv[1], width);
2807 printf(_("<\n"));
2809 return 0;
2813 static int shi_tokenize(int argc, const char **argv) {
2815 if (!argv) return 0;
2817 for (int i = 0; i < argc; i++) {
2818 printf(_(">%s<\n"), argv[i]);
2820 return 0;
2824 static int shi_quote(int argc, const char **argv) {
2825 char *escaped, *unescaped;
2827 if (!argv) return 0;
2829 printf(_("Original\tQuoted\tDequoted\n"));
2830 for (int i = 0; i < argc; i++) {
2831 escaped = shi_quote_filename((char *) argv[i], 0, NULL);
2832 unescaped = shi_dequote_filename((char *) argv[i], 0);
2833 printf(_(">%s<\t>%s<\t>%s<\n"), argv[i], escaped, unescaped);
2834 free(escaped);
2835 free(unescaped);
2837 return 0;
2839 #endif
2842 /* Interactive loop */
2843 void shi_loop(void) {
2844 char *command_line = NULL;
2845 char **command_argv = NULL;
2846 int command_argc;
2847 struct command *command = NULL;
2849 printf(_("Use `help' command to get list of available commands.\n"));
2851 select_completition(COMPL_COMMAND);
2852 set_prompt(NULL);
2854 while (1) {
2855 command_line = readline((prompt) ? prompt : _("shigofumi> "));
2856 /* Remeber not parsable commands too to user be able to get back to
2857 * fix command */
2858 if (command_line && *command_line) {
2859 /* TODO: Ommit blank lines */
2860 add_history(command_line);
2863 command_argv = tokenize(command_line, &command_argc);
2865 if (command_argv && command_argv[0]) {
2866 command = find_command(command_argv[0]);
2868 if (!command) {
2869 fprintf(stderr, _("Command not understood\n"));
2870 } else {
2871 command->function(command_argc, (const char **) command_argv);
2875 argv_free(command_argv);
2876 zfree(command_line);
2881 /* Non-interactive mode. Commands from @lines are processed until any command
2882 * lines remains or no error occured. First failure terminates processing.
2883 * @lines is sequence of commands seperated by '\n' or '\r'. The content is
2884 * modified during this call.
2885 * @return 0 if all command succeed, otherwose non-zero value
2887 int shi_batch(char *lines) {
2888 char *command_line;
2889 char **command_argv = NULL;
2890 int command_argc;
2891 struct command *command = NULL;
2892 int retval = 0;
2894 printf(_("Batch mode started.\n"));
2895 batch_mode = 1;
2896 select_completition(COMPL_COMMAND);
2898 while (!retval && (command_line = strtok(lines, "\n\r"))) {
2899 lines = NULL; /* strtok(3) requires it for subsequent calls */
2901 printf(_("Processing command: %s\n"), command_line);
2903 command_argv = tokenize(command_line, &command_argc);
2905 if (command_argv && command_argv[0]) {
2906 command = find_command(command_argv[0]);
2908 if (!command) {
2909 fprintf(stderr, _("Command not understood\n"));
2910 retval = -1;
2911 } else {
2912 retval = command->function(command_argc,
2913 (const char **) command_argv);
2917 argv_free(command_argv);
2920 if (retval)
2921 fprintf(stderr, _("Command failed!\n"));
2922 return retval;
2926 #define COMMON_COMMANDS \
2927 { "accept", shi_accept_message, N_("accept commercial message"), \
2928 shi_accept_message_usage, ARGTYPE_MSGID }, \
2929 { "box", shi_box, N_("show current box details"), NULL, \
2930 ARGTYPE_NONE }, \
2931 { "cache", shi_cache, N_("show cache details"), NULL, \
2932 ARGTYPE_NONE }, \
2933 { "cd", shi_chdir, N_("change working directory"), shi_chdir_usage, \
2934 ARGTYPE_FILE }, \
2935 { "commercial", shi_commercial, \
2936 N_("manipulate commerical receiving box status"), \
2937 shi_commercial_usage, ARGTYPE_BOXID }, \
2938 { "compose", shi_compose, N_("compose a message"), shi_compose_usage, \
2939 ARGTYPE_FILE }, \
2940 { "convert", shi_convert_file, \
2941 N_("submit local document for authorized conversion"), \
2942 shi_convert_file_usage, ARGTYPE_FILE }, \
2943 { "copying", shi_copying, N_("show this program licence excerpt"), NULL, \
2944 ARGTYPE_NONE }, \
2945 { "debug", shi_debug, N_("set debugging"), shi_debug_usage, \
2946 ARGTYPE_FILE }, \
2947 { "delivery", shi_delivery, N_("get message delivery details"), \
2948 shi_delivery_usage, ARGTYPE_MSGID }, \
2949 { "findbox", shi_find_box, N_("search for a box"), shi_find_box_usage, \
2950 ARGTYPE_BOXID }, \
2951 { "hash", shi_hash, N_("query ISDS for message hash"), \
2952 shi_hash_usage, ARGTYPE_MSGID }, \
2953 { "help", shi_help, N_("describe commands"), shi_help_usage, \
2954 ARGTYPE_COMMAND }, \
2955 { "load", shi_load_anything, \
2956 N_("load message or message delivery details from local file"), \
2957 shi_load_anything_usage, ARGTYPE_FILE }, \
2958 { "login", shi_login, N_("log into ISDS"), NULL, ARGTYPE_NONE }, \
2959 { "lsi", shi_list_incoming, N_("list received messages"), NULL, \
2960 ARGTYPE_NONE }, \
2961 { "lso", shi_list_outgoing, N_("list sent messages"), NULL, \
2962 ARGTYPE_NONE }, \
2963 { "msgi", shi_incoming_message, N_("get incoming message"), \
2964 shi_incoming_message_usage, ARGTYPE_MSGID }, \
2965 { "msgo", shi_outgoing_message, N_("get outgoing message"), \
2966 shi_outgoing_message_usage, ARGTYPE_MSGID }, \
2967 { "passwd", shi_passwd, N_("manipulate user password"), shi_passwd_usage, \
2968 ARGTYPE_NONE }, \
2969 { "pwd", shi_pwd, N_("print working directory"), NULL, ARGTYPE_NONE }, \
2970 { "quit", shi_quit, N_("exit shigofumi"), NULL, ARGTYPE_NONE }, \
2971 { "read", shi_read_message, N_("mark message as read"), \
2972 shi_read_message_usage, ARGTYPE_MSGID }, \
2973 { "statbox", shi_stat_box, N_("get status of a box"), shi_stat_box_usage, \
2974 ARGTYPE_BOXID }, \
2975 { "user", shi_user, N_("show current user details"), NULL, \
2976 ARGTYPE_NONE }, \
2977 { "users", shi_users, N_("show box users"), shi_users_usage, \
2978 ARGTYPE_NONE }, \
2979 { "version", shi_version, N_("show version of this program"), NULL, \
2980 ARGTYPE_NONE}, \
2981 { NULL, NULL, NULL, NULL, ARGTYPE_NONE }
2983 struct command base_commands[] = {
2984 #if ENABLE_DEBUG
2985 { "quote", shi_quote, N_("demonstrate argument escaping"), NULL,
2986 ARGTYPE_FILE },
2987 { "print", shi_print, N_("print string into given width"),
2988 shi_print_usage, ARGTYPE_NONE },
2989 { "tokenize", shi_tokenize, N_("demonstrate arguments tokenization"), NULL,
2990 ARGTYPE_FILE },
2991 #endif
2992 COMMON_COMMANDS
2995 struct command message_commands[] = {
2996 { "authenticate", shi_authenticate, N_("check message authenticity"),
2997 NULL, ARGTYPE_NONE },
2998 { "convertdoc", shi_convert_document,
2999 N_("submit document of current message for authorized conversion"),
3000 shi_convert_document_usage, ARGTYPE_DOCID },
3001 { "dump", shi_dump_message, N_("dump current message structure"),
3002 NULL, ARGTYPE_NONE },
3003 { "savestamp", shi_save_stamp,
3004 N_("save time stamp of current message into local file"),
3005 shi_save_stamp_usage, ARGTYPE_FILE },
3006 { "savedoc", shi_save_document,
3007 N_("save document of current message into local file"),
3008 shi_save_document_usage, ARGTYPE_FILE },
3009 { "save", shi_save_message, N_("save current message into local file"),
3010 shi_save_message_usage, ARGTYPE_FILE },
3011 { "show", shi_show_message, N_("show current message"), NULL,
3012 ARGTYPE_NONE },
3013 { "verify", shi_verify, N_("verify current message hash"), NULL,
3014 ARGTYPE_NONE },
3015 COMMON_COMMANDS
3018 struct command list_commands[] = {
3019 { "show", shi_show_list, N_("show current message list"), NULL,
3020 ARGTYPE_NONE },
3021 COMMON_COMMANDS
3024 #undef COMMON_COMMANDS
3027 static void main_version(void) {
3028 isds_init();
3029 show_version();
3030 isds_cleanup();
3031 printf("\n");
3032 shi_copying(0, NULL);
3036 static void main_usage(const char *command) {
3037 printf(_(
3038 "Usage: %s [OPTION...]\n"
3039 "Access ISDS, proccess local data box messages or delivery details, submit\n"
3040 "document to authorized conversion.\n"
3041 "\n"
3042 "Options:\n"
3043 " -c FILE use the FILE as configuration file instead of ~/%s\n"
3044 " -e COMMANDS execute COMMANDS (new line separated) and exit\n"
3045 " -V show version info and exit\n"
3047 (command) ? command : "shigofumi",
3048 CONFIG_FILE);
3052 int main(int argc, char **argv) {
3053 int opt;
3054 char *config_file = NULL;
3055 char *batch_commands = NULL;
3056 int retval = EXIT_SUCCESS;
3058 setlocale(LC_ALL, "");
3059 #if ENABLE_NLS
3060 /* Initialize gettext */
3061 bindtextdomain(PACKAGE, LOCALEDIR);
3062 textdomain(PACKAGE);
3063 #endif
3065 /* Parse arguments */
3066 optind = 0;
3067 while ((opt = getopt(argc, (char * const *)argv, "c:e:V")) != -1) {
3068 switch (opt) {
3069 case 'c':
3070 config_file = optarg;
3071 break;
3072 case 'e':
3073 batch_commands = optarg;
3074 break;
3075 case 'V':
3076 main_version();
3077 shi_exit(EXIT_SUCCESS);
3078 break;
3079 default:
3080 main_usage((argv[0]) ? basename(argv[0]): NULL);
3081 shi_exit(EXIT_FAILURE);
3086 if (shi_init(config_file)) {
3087 shi_exit(EXIT_FAILURE);
3090 /*shi_login(NULL);*/
3092 if (batch_commands) {
3093 if (shi_batch(batch_commands))
3094 retval = EXIT_FAILURE;
3095 } else {
3096 shi_loop();
3099 shi_exit(retval);