Add -r option to msgi
[shigofumi.git] / src / shigofumi.c
blob07becb31207dab1ee5a946993a60699e4a5e9f63
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_MARKMESSAGEREAD "mark_message_read"
37 #define CONFIG_NORMALIZEMIMETYPE "normalize_mime_type"
38 #define CONFIG_OVERWRITEFILES "overwrite_files"
39 #define CZPDEPOSIT_URL "https://www.czechpoint.cz/uschovna/"
41 #define TIMEOUT 10000
42 #define LOG_LEVEL 20
44 /* Configuration */
45 cfg_opt_t configuration_syntax[] = {
46 CFG_STR(CONFIG_SERVER, (char *)isds_locator, CFGF_NONE),
47 CFG_STR(CONFIG_USERNAME, NULL, CFGF_NODEFAULT),
48 CFG_STR(CONFIG_PASSWORD, NULL, CFGF_NODEFAULT),
49 /*CFG_STR(CONFIG_DEBUGLEVEL, NULL, CFGF_NODEFAULT),*/
50 CFG_BOOL(CONFIG_VERIFYSERVER, cfg_true, CFGF_NONE),
51 CFG_STR(CONFIG_CAFILE, NULL, CFGF_NODEFAULT),
52 CFG_STR(CONFIG_CADIRECTORY, NULL, CFGF_NODEFAULT),
53 CFG_STR(CONFIG_CRLFILE, NULL, CFGF_NODEFAULT),
54 CFG_INT(CONFIG_TIMEOUT, TIMEOUT, CFGF_NONE),
55 CFG_STR_LIST(CONFIG_LOGFACILITIES, "{none}", CFGF_NONE),
56 CFG_STR(CONFIG_LOGFILE, NULL, CFGF_NODEFAULT),
57 CFG_INT(CONFIG_LOGLEVEL, LOG_LEVEL, CFGF_NONE),
58 CFG_BOOL(CONFIG_MARKMESSAGEREAD, cfg_false, CFGF_NONE),
59 CFG_BOOL(CONFIG_NORMALIZEMIMETYPE, cfg_true, CFGF_NONE),
60 CFG_BOOL(CONFIG_OVERWRITEFILES, cfg_true, CFGF_NONE),
61 CFG_END()
63 cfg_t *configuration;
65 /* Logger */
66 int logger_fd = -1;
68 /* UI */
69 _Bool batch_mode = 0;
70 char *prompt = NULL;
71 struct command (*commands)[] = NULL;
73 /* Data */
74 struct isds_ctx *cisds = NULL;
75 char *server = NULL;
76 char *username = NULL;
77 char *password = NULL;
78 struct isds_list *boxes = NULL;
79 struct isds_message *message = NULL;
80 _Bool messages_are_outgoing = 0;
81 struct isds_list *messages = NULL;
82 unsigned long int total_messages = 0;
83 struct isds_ctx *czechpoint = NULL;
85 static void discard_credentials(void) {
86 zfree(server);
87 zfree(username);
88 zfree(password);
92 /* Do the cleanup and exit */
93 static void shi_exit(int exit_code) {
94 /* Data */
95 discard_credentials();
96 isds_list_free(&boxes);
97 isds_message_free(&message);
98 isds_list_free(&messages);
100 if (cisds) {
101 printf(_("Logging out...\n"));
102 isds_logout(cisds);
103 isds_ctx_free(&cisds);
105 isds_ctx_free(&czechpoint);
106 isds_cleanup();
108 /* Configuration */
109 cfg_free(configuration);
111 /* UI */
112 free(prompt);
113 free(commands);
115 exit(exit_code);
118 /* Set prompt. if @format is NULL, switch to default prompt */
119 static void set_prompt(const char *format, ...) {
120 char *buffer = NULL;
121 va_list ap;
122 if (format) {
123 va_start(ap, format);
124 shi_vasprintf(&buffer, format, ap);
125 va_end(ap);
127 if (buffer) {
128 shi_asprintf(&prompt, _("%s> "), buffer);
129 if (prompt) {
130 free(buffer);
131 return;
135 free(buffer);
136 free(prompt);
137 prompt = strdup(_("> "));
138 return;
141 zfree(prompt);
145 static int shi_load_configuration(const char *config_file) {
146 char *config_name = NULL;
147 int ret;
149 /* Get config file */
150 if (config_file) {
151 config_name = (char *) config_file;
152 } else {
153 if (-1 == shi_asprintf(&config_name, "%s/%s", getenv("HOME"),
154 CONFIG_FILE)) {
155 fprintf(stderr, _("Could not build configuration file name\n"));
156 return -1;
160 /* Parse configuration */
161 configuration = cfg_init(configuration_syntax, CFGF_NONE);
162 ret = cfg_parse(configuration, config_name);
163 if (ret) {
164 if (ret == CFG_FILE_ERROR) {
165 fprintf(stderr,
166 _("Error while opening configuration file `%s': %s\n"),
167 config_name, strerror(errno));
168 } else {
169 fprintf(stderr, _("Error while parsing configuration file `%s'\n"),
170 config_name);
172 printf(_("Using default configuration\n"));
175 if (config_name != config_file) free(config_name);
176 return 0;
180 static void finish_isds_operation(struct isds_ctx *ctx, isds_error err) {
181 shi_progressbar_finish();
182 if (err) {
183 if (isds_long_message(ctx))
184 printf(_("Error occurred: %s: %s\n"), isds_strerror(err),
185 isds_long_message(ctx));
186 else
187 printf(_("Error occurred: %s\n"), isds_strerror(err));
192 void logger(isds_log_facility facility, isds_log_level level,
193 const char *message, int length, void *data) {
194 int fd;
195 ssize_t written, left = length;
197 if (!data) return;
198 fd = *((int *) data);
199 /*printf("\033[32mLOG(%02d,%02d): ", facility, level);
200 printf("%.*s", length, message);
201 printf("\033[m");*/
203 while (left) {
204 written = write(fd, message + length - left, left);
205 if (written == -1) {
206 fprintf(stderr,
207 _("Could not save log message into log file: %s\n"
208 "Log message discarded!\n"),
209 strerror(errno));
210 /*close(fd);
211 fd = -1;*/
212 return;
214 left-=written;
219 /* Redirect ISDS log to file if @file is not NULL. */
220 static int do_log_to_file(const char *file) {
221 if (file && *file) {
222 logger_fd = open_file_for_writing(file, 0, 1);
223 if (logger_fd == -1) {
224 fprintf(stderr, _("Could not redirect ISDS log to file `%s'\n"),
225 file);
226 return -1;
228 isds_set_log_callback(logger, &logger_fd);
230 return 0;
234 /* Add log facility based on its name. */
235 static int add_log_facility(isds_log_facility *facilities, const char *name) {
236 if (!facilities) return -1;
238 if (!strcmp(name, "none")) *facilities |= ILF_NONE;
239 else if (!strcmp(name, "http")) *facilities |= ILF_HTTP;
240 else if (!strcmp(name, "soap")) *facilities |= ILF_SOAP;
241 else if (!strcmp(name, "isds")) *facilities |= ILF_ISDS;
242 else if (!strcmp(name, "file")) *facilities |= ILF_FILE;
243 else if (!strcmp(name, "sec")) *facilities |= ILF_SEC;
244 else if (!strcmp(name, "xml")) *facilities |= ILF_XML;
245 else if (!strcmp(name, "all")) *facilities |= ILF_ALL;
246 else {
247 printf(_("%s: Unknown log facility\n"), name);
248 return -1;
251 return 0;
255 /* Save log facility into confuse configuration */
256 static void save_log_facility(int level) {
257 cfg_setlist(configuration, CONFIG_LOGFACILITIES, 0);
259 if (level == ILF_ALL) {
260 cfg_addlist(configuration, CONFIG_LOGFACILITIES, 1, "all");
261 return;
263 if (level == ILF_NONE) {
264 cfg_addlist(configuration, CONFIG_LOGFACILITIES, 1, "none");
265 return;
267 if (level & ILF_HTTP)
268 cfg_addlist(configuration, CONFIG_LOGFACILITIES, 1, "http");
269 if (level & ILF_SOAP)
270 cfg_addlist(configuration, CONFIG_LOGFACILITIES, 1, "soap");
271 if (level & ILF_ISDS)
272 cfg_addlist(configuration, CONFIG_LOGFACILITIES, 1, "isds");
273 if (level & ILF_FILE)
274 cfg_addlist(configuration, CONFIG_LOGFACILITIES, 1, "file");
275 if (level & ILF_SEC)
276 cfg_addlist(configuration, CONFIG_LOGFACILITIES, 1, "sec");
277 if (level & ILF_XML)
278 cfg_addlist(configuration, CONFIG_LOGFACILITIES, 1, "xml");
282 /* Clamp long int to unsigned int */
283 static unsigned int normalize_timeout(long int raw) {
284 if (raw < 0) {
285 printf(_("Configured network timeout is less then 0. Clamped to 0.\n"));
286 return 0;
288 if (raw > UINT_MAX ) {
289 printf(_("Configured network timeout is greater then %1$u. "
290 "Clamped to %1$u.\n"), UINT_MAX);
291 return UINT_MAX;
293 return (unsigned int) raw;
297 /* Clamp long int to <0;100> */
298 static unsigned int normalize_log_level(long int raw) {
299 if (raw < 0) {
300 printf(_("Configured log level is less then 0. Clamped to 0.\n"));
301 return 0;
303 if (raw > ILL_ALL) {
304 printf(_("Configured log level is greater then %1$u. "
305 "Clamped to %1$u.\n"), ILL_ALL);
306 return ILL_ALL;
308 if (raw > UINT_MAX ) {
309 printf(_("Configured log level is greater then %1$u. "
310 "Clamped to %1$u.\n"), UINT_MAX);
311 return UINT_MAX;
313 return (unsigned int) raw;
317 static int shi_init(const char *config_file) {
318 isds_error err;
319 char *value;
320 unsigned int timeout, log_level;
321 isds_log_facility log_facility = ILF_NONE;
323 printf(_("This is Shigofumi, an ISDS client. Have a nice e-government.\n"));
325 /* Do not permute arguments in getopt() */
326 if (setenv("POSIXLY_CORRECT", "", 1)) {
327 fprintf(stderr,
328 _("Could not set POSIXLY_CORRECT environment variable\n"));
329 return -1;
332 /* Load configuration */
333 if (shi_load_configuration(config_file))
334 return -1;
335 timeout = normalize_timeout(cfg_getint(configuration, CONFIG_TIMEOUT));
336 log_level = normalize_log_level(cfg_getint(configuration, CONFIG_LOGLEVEL));
338 /* Init readline */
339 rl_readline_name = "shigofumi";
340 rl_filename_quote_characters = "\\ >";
341 rl_filename_quoting_function = shi_quote_filename;
342 rl_filename_dequoting_function = shi_dequote_filename;
343 rl_char_is_quoted_p = shi_char_is_quoted;
345 /* Initialize ISDS */
346 err = isds_init();
347 if (err) {
348 fprintf(stderr, _("Could not initialize libisds library: %s\n"),
349 isds_strerror(err));
350 return -1;
353 /* Set ISDS logging */
354 value = cfg_getstr(configuration, CONFIG_LOGFILE);
355 if (do_log_to_file(value))
356 return -1;
357 for (int i = 0; i < cfg_size(configuration, CONFIG_LOGFACILITIES); i++) {
358 if (add_log_facility(&log_facility,
359 cfg_getnstr(configuration, CONFIG_LOGFACILITIES, i)))
360 return -1;
363 isds_set_logging(log_facility, log_level);
365 /* Set ISDS context up */
366 cisds = isds_ctx_create();
367 if (!cisds) {
368 fprintf(stderr, _("Could not create ISDS context\n"));
369 return -1;
371 err = isds_set_timeout(cisds, timeout);
372 if (err) {
373 fprintf(stderr, _("Could not set ISDS network timeout: %s\n"),
374 isds_strerror(err));
376 err = isds_set_progress_callback(cisds, shi_progressbar, NULL);
377 if (err) {
378 fprintf(stderr, _("Could not register network progress bar: %s: %s\n"),
379 isds_strerror(err), isds_long_message(cisds));
381 err = isds_set_opt(cisds, IOPT_NORMALIZE_MIME_TYPE,
382 cfg_getbool(configuration, CONFIG_NORMALIZEMIMETYPE));
383 if (err) {
384 fprintf(stderr,
385 cfg_getbool(configuration, CONFIG_NORMALIZEMIMETYPE) ?
386 _("Could not enable MIME type normalization: %s: %s\n") :
387 _("Could not disable MIME type normalization: %s: %s\n"),
388 isds_strerror(err), isds_long_message(cisds));
390 if (!cfg_getbool(configuration, CONFIG_VERIFYSERVER)) {
391 printf(_("Warning: Shigofumi disabled server identity verification "
392 "on user request!\n"));
393 err = isds_set_opt(cisds, IOPT_TLS_VERIFY_SERVER, 0);
394 if (err) {
395 fprintf(stderr,
396 _("Could not disable server identity verification: "
397 "%s: %s\n"),
398 isds_strerror(err), isds_long_message(cisds));
401 if ((value = cfg_getstr(configuration, CONFIG_CAFILE))) {
402 err = isds_set_opt(cisds, IOPT_TLS_CA_FILE, value);
403 if (err) {
404 fprintf(stderr,
405 _("Could not set file with CA certificates: %s: %s: %s\n"),
406 value, isds_strerror(err), isds_long_message(cisds));
409 if ((value = cfg_getstr(configuration, CONFIG_CADIRECTORY))) {
410 err = isds_set_opt(cisds, IOPT_TLS_CA_DIRECTORY, value);
411 if (err) {
412 fprintf(stderr,
413 _("Could not set directory with CA certificates: "
414 "%s: %s: %s\n"),
415 value, isds_strerror(err), isds_long_message(cisds));
418 if ((value = cfg_getstr(configuration, CONFIG_CRLFILE))) {
419 err = isds_set_opt(cisds, IOPT_TLS_CRL_FILE, value);
420 if (err) {
421 fprintf(stderr, _("Could not set file with CRL: %s: %s: %s\n"),
422 value, isds_strerror(err), isds_long_message(cisds));
427 /* Set Czech POINT context up */
428 czechpoint = isds_ctx_create();
429 if (!czechpoint) {
430 fprintf(stderr, _("Could not create Czech POINT context\n"));
431 return -1;
433 err = isds_set_timeout(czechpoint, timeout);
434 if (err) {
435 fprintf(stderr, _("Could not set Czech POINT network timeout: %s\n"),
436 isds_strerror(err));
438 err = isds_set_progress_callback(czechpoint, shi_progressbar, NULL);
439 if (err) {
440 fprintf(stderr, "Could not register network progress bar: %s: %s\n",
441 isds_strerror(err), isds_long_message(cisds));
444 return 0;
448 static int shi_quit(int argc, const char **argv) {
449 shi_exit(EXIT_SUCCESS);
450 return 0;
455 static void shi_help_usage(const char *command) {
456 printf(_(
457 "Usage: %s [COMMAND]\n"
458 "Show COMMAND manual or list of currently available commands.\n"
460 command);
464 static int shi_help(int argc, const char **argv) {
465 size_t command_width = 14;
466 int i;
468 if (!commands) {
469 printf(_("No command is available\n"));
470 return 0;
473 if (argc == 2 && argv[1] && *argv[1]) {
474 /* Show usage for given command */
475 for (i = 0; (*commands)[i].name; i++) {
476 if (!strcmp((*commands)[i].name, argv[1])) {
477 if ((*commands)[i].usage)
478 (*commands)[i].usage((*commands)[i].name);
479 else if ((*commands)[i].description) {
480 fhprint(stdout, (*commands)[i].name, command_width);
481 printf(" %s\n", _((*commands)[i].description));
483 else
484 printf(_("%s: %s: Command description not defined\n"),
485 argv[0], argv[1]);
486 return 0;
489 printf(_("%s: %s: No such command exists\n"), argv[0], argv[1]);
490 return -1;
493 /* Or list all commands */
494 printf(_("Following commands are available:\n"));
495 for (i = 0; (*commands)[i].name; i++) {
496 fhprint(stdout, (*commands)[i].name, command_width);
497 printf(" %s\n", _((*commands)[i].description));
500 return 0;
504 static void show_version(void) {
505 char *libisds_version = isds_version();
507 printf(_("This is Shigofumi version %s.\n"), PACKAGE_VERSION);
508 printf(_("\n"
509 "Used libraries\n"
510 "Readline: %s\n"
511 "libisds: %s\n"
513 rl_library_version, libisds_version);
514 free(libisds_version);
518 static int shi_version(int argc, const char **argv) {
519 show_version();
520 printf(_(
521 "\n"
522 "-----\n"
523 "It's a shigofumi. A letter delivered from the afterlife. (Fumika)\n"
524 "A message can not be delivered to dead person. (ISDS specification)\n"
525 "Virtual and real world. They can be compatible. (Program author)\n"
527 return 0;
531 static int shi_copying(int argc, const char **argv) {
532 printf(_(
533 "This is Shigofumi, an ISDS client.\n"
534 "Copyright (C) 2010 Petr Pisar\n"
535 "\n"
536 "This program is free software: you can redistribute it and/or modify\n"
537 "it under the terms of the GNU General Public License as published by\n"
538 "the Free Software Foundation, either version 3 of the License, or\n"
539 "(at your option) any later version.\n"
540 "\n"
541 "This program is distributed in the hope that it will be useful,\n"
542 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
543 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
544 "GNU General Public License for more details.\n"
545 "\n"
546 "You should have received a copy of the GNU General Public License\n"
547 "along with this program. If not, see <http://www.gnu.org/licenses/>.\n"
549 return 0;
553 static int shi_cache(int argc, const char **argv) {
554 const struct isds_list *item;
555 size_t i;
557 if (boxes) {
558 for (item = boxes, i = 0; item; item = item->next, i++);
559 printf(_(
560 "Cached box list: %zu\n"),
564 if (messages) {
565 printf(_(
566 "Cached message list:\n"
567 "\tDirection: %s\n"
568 "\tMessages: %'lu\n"),
569 (messages_are_outgoing) ? _("Outgoing") : _("Incoming"),
570 total_messages);
573 if (message) {
574 printf(_("Cached message: %s\n"),
575 (message->envelope && message->envelope->dmID) ?
576 message->envelope->dmID : _("<Unknown ID>"));
579 return 0;
583 static void shi_chdir_usage(const char *command) {
584 printf(_(
585 "Usage: %s [DIRECTORY]\n"
586 "Change working directory to DIRECTORY.\n"
587 "If no DIRECTORY is supplied, HOME directory will be used.\n"),
588 command);
592 static int shi_chdir(int argc, const char **argv) {
593 const char *directory = NULL;
595 if (!argv || argc > 2) {
596 shi_chdir_usage((argv) ? argv[0] : NULL);
597 return -1;
600 if (argc == 2 && argv[1] && *argv[1])
601 directory = argv[1];
602 else {
603 directory = getenv("HOME");
604 if (!directory) {
605 printf("Environment variable HOME does not exist\n");
606 return -1;
609 if (chdir(directory)) {
610 printf(_("Could not change working directory: %s: %s\n"), directory,
611 strerror(errno));
612 return -1;
615 return 0;
619 static int shi_pwd(int argc, const char **argv) {
620 char *buffer = NULL, *newbuffer;
621 size_t length = 0;
623 while (length += 1024) {
624 newbuffer = realloc(buffer, length);
625 if (!newbuffer) {
626 printf(_("Error: Not enough memory\n"));
627 free(buffer);
628 return -1;
630 buffer = newbuffer;
632 if (getcwd(buffer, length)) {
633 printf("%s\n", buffer);
634 free(buffer);
635 return 0;
639 printf(_("Error: Current directory string is too long\n"));
640 free(buffer);
641 return -1;
645 /* Log-in to ISDS */
646 static int do_login(void) {
647 isds_error err;
649 if (batch_mode) {
650 printf(_("Unattended mode detected. "
651 "Make sure credentials have been preset.\n"));
652 } else {
653 printf(_("You are going to insert credentials for your account.\n"
654 "Leave blank line to choose default value.\n"));
656 select_completition(COMPL_NONE);
658 /* Ask for server base URL */
659 shi_replace_string(&server, _("Input ISDS base URL: "),
660 cfg_getstr(configuration, CONFIG_SERVER), batch_mode);
662 /* Ask for user name */
663 shi_replace_string(&username, _("Input ISDS user name: "),
664 cfg_getstr(configuration, CONFIG_USERNAME), batch_mode);
666 /*if (!password) {
667 password = ask_for_password(_("Input ISDS password: "));
669 if (!password) {
670 fprintf(stderr, _("Could not read ISDS password\n"));
671 shi_exit(EXIT_FAILURE);
674 shi_replace_password(&password, _("Input ISDS password: "),
675 cfg_getstr(configuration, CONFIG_PASSWORD), batch_mode);
677 select_completition(COMPL_COMMAND);
678 set_prompt(NULL);
680 printf(_("Logging in...\n"));
681 err = isds_login(cisds, server, username, password, NULL);
682 finish_isds_operation(cisds, err);
683 if (err) {
684 printf(_("Log-in failed\n"));
685 return -1;
688 printf(_("Logged in.\n"));
689 return 0;
693 static struct isds_DbOwnerInfo *do_box(void) {
694 isds_error err;
695 struct isds_DbOwnerInfo *box = NULL;
697 printf(_("Getting box details you are logged in...\n"));
698 err = isds_GetOwnerInfoFromLogin(cisds, &box);
699 finish_isds_operation(cisds, err);
701 return box;
705 static int shi_box(int argc, const char **argv) {
706 struct isds_DbOwnerInfo *box = NULL;
708 box = do_box();
709 if (!box) return -1;
711 format_DbOwnerInfo(box);
713 isds_DbOwnerInfo_free(&box);
714 return 0;
718 /* Get info about box with @id.
719 * @id is UTF-8 encoded
720 * Return NULL in case of error, otherwise box description that caller must
721 * free. */
722 static struct isds_DbOwnerInfo *stat_box(const char *id) {
723 isds_error err;
724 struct isds_DbOwnerInfo criteria;
725 struct isds_list *boxes = NULL, *item;
726 struct isds_DbOwnerInfo *box = NULL;
727 char *id_locale = NULL;
729 if (!id || !*id) return NULL;
731 id_locale = utf82locale(id);
732 memset(&criteria, 0, sizeof(criteria));
733 criteria.dbID = (char *) id;
735 printf(_("Getting details about box with ID `%s'...\n"), id_locale);
736 err = isds_FindDataBox(cisds, &criteria, &boxes);
737 finish_isds_operation(cisds, err);
738 if (err) goto leave;
740 for(item = boxes; item; item = item->next) {
741 if (!item->data) continue;
743 if (item->next) {
744 printf(_("Error: More boxes match ID `%s'\n"), id_locale);
745 goto leave;
748 box = (struct isds_DbOwnerInfo *) item->data;
749 item->data = NULL;
750 break;
753 leave:
754 free(id_locale);
755 isds_list_free(&boxes);
757 return box;
761 static void shi_commercial_usage(const char *command) {
762 printf(_(
763 "Usage: %s [-0|-1] [BOX_ID]\n"
764 "Manipulate commercial receiving box status.\n"
765 " -O switch off receiving of commercial messages\n"
766 " -1 switch on receiving of commercial messages\n"
767 " BOX_ID affects box with ID BOX_ID; default is box you are logged in\n"
768 "If no option is given, show current commercial receiving status.\n"),
769 command);
773 /* Manipulate commercial receiving box status */
774 static int shi_commercial(int argc, const char **argv) {
775 isds_error err;
776 struct isds_DbOwnerInfo *box = NULL;
777 int opt;
778 int action = -1;
779 char *box_id = NULL, *box_id_locale = NULL;
780 _Bool static_box_id;
781 int retval = 0;
783 optind = 0;
784 while ((opt = getopt(argc, (char * const *)argv, "01")) != -1) {
785 switch (opt) {
786 case '0':
787 action = 0;
788 break;
789 case '1':
790 action = 1;
791 break;
792 default:
793 shi_commercial_usage((argv)?argv[0]:NULL);
794 return -1;
797 if (optind + 1 < argc) {
798 printf(_("Bad invocation\n"));
799 shi_commercial_usage((argv)?argv[0]:NULL);
800 return -1;
803 if (!argv[optind] || !*argv[optind]) {
804 /* Get current box ID */
805 box = do_box();
806 if (!box || !box->dbID || !*box->dbID) {
807 isds_DbOwnerInfo_free(&box);
808 printf(_("Could not get current box ID\n"));
809 return -1;
811 box_id = box->dbID; static_box_id = 1;
812 box_id_locale = utf82locale(box_id);
813 } else {
814 /* Box ID supplied as argument */
815 box_id_locale = (char *) argv[optind];
816 box_id = locale2utf8(box_id_locale); static_box_id = 0;
817 if (!box_id) {
818 printf(_("Could not convert box ID `%s' to UTF-8\n"),
819 box_id_locale);
820 return -1;
824 if (action == -1) {
825 if (!box) box = stat_box(box_id);
826 if (!box) {
827 printf(_("Could not get details about box ID `%s'\n"),
828 box_id_locale);
829 retval = -1;
830 goto leave;
833 printf(_("Commercial receiving status of box `%s': "), box_id_locale);
834 if (!box->dbOpenAddressing)
835 printf(_("Unknown\n"));
836 else if (*box->dbOpenAddressing)
837 printf(_("Positive\n"));
838 else
839 printf(_("Negative\n"));
840 } else {
841 char *refnumber = NULL;
842 printf((action) ?
843 _("Switching `%s' box commercial receiving on...\n"):
844 _("Switching `%s' box commercial receiving off...\n"),
845 box_id_locale);
846 err = isds_switch_commercial_receiving(cisds, box_id, action,
847 NULL, &refnumber);
848 finish_isds_operation(cisds, err);
850 if (!err) {
851 char *refnumber_locale = utf82locale(refnumber);
852 printf(_("Commercial receiving status successfully changed. "
853 "Assigned reference number: %s\n"),
854 refnumber_locale);
855 } else {
856 printf(_("Commercial receiving status has not been changed.\n"));
857 retval = -1;
859 free(refnumber);
862 leave:
863 if (!static_box_id) free(box_id);
864 else free(box_id_locale);
865 isds_DbOwnerInfo_free(&box);
866 return retval;
870 static int shi_user(int argc, const char **argv) {
871 isds_error err;
872 struct isds_DbUserInfo *user = NULL;
874 printf(_("Getting user details you are logged as...\n"));
875 err = isds_GetUserInfoFromLogin(cisds, &user);
876 finish_isds_operation(cisds, err);
877 if (err) return -1;
879 format_DbUserInfo(user);
881 isds_DbUserInfo_free(&user);
882 return 0;
886 static void shi_users_usage(const char *command) {
887 printf(_(
888 "Usage: %s BOX_ID\n"
889 "Get list of users having access to box with BOX_ID.\n"),
890 command);
894 static int shi_users(int argc, const char **argv) {
895 isds_error err;
896 struct isds_list *users = NULL, *item;
897 int ordinar;
899 if (!argv || !argv[1] || !*argv[1]) {
900 shi_users_usage((argv)?argv[0]:NULL);
901 return -1;
904 printf(_("Getting users of box with ID `%s'...\n"), argv[1]);
905 err = isds_GetDataBoxUsers(cisds, argv[1], &users);
906 finish_isds_operation(cisds, err);
907 if (err) return -1;
909 for (item = users, ordinar = 0; item; item=item->next) {
910 if (!item->data) continue;
911 ordinar++;
912 printf(_("\n* User #%d:\n"), ordinar);
913 format_DbUserInfo(item->data);
915 if (ordinar == 0)
916 printf(_("Empty list of users returned.\n"));
918 isds_list_free(&users);
919 return 0;
923 static int show_password_expiration(void) {
924 isds_error err;
925 struct timeval *expiration = NULL;
927 err = isds_get_password_expiration(cisds, &expiration);
928 finish_isds_operation(cisds, err);
929 if (err) {
930 fprintf(stderr, "Could not get password expiration time\n");
931 return -1;
934 print_header_timeval(_("Your password expires at"), expiration);
935 free(expiration);
936 return 0;
940 /* Change password in ISDS */
941 static int do_passwd(void) {
942 char *old_password = NULL;
943 char *new_password = NULL;
944 char *new_password2 = NULL;
945 isds_error err = IE_ERROR;
946 int retval = 0;
948 select_completition(COMPL_NONE);
950 printf(_(
951 "You are going to change your password. If you don't want to change your\n"
952 "password, insert empty string or EOF.\n"
953 "\n"
954 "You will be asked for your current (old) password and then for new password.\n"
955 "ISDS forces some criteria new password must fulfill. Current rules are:\n"
956 "\tLength: minimal 8, maximal 32 characters\n"
957 "\tMust contain at least: 1 upper case letter, 1 lower case letter, 1 digit\n"
958 "\tAllowed alphabet: [a-z][A-Z][0-9][!#$%%&()*+,-.:=?@[]_{}|~]\n"
959 "\tMust differ from last 255 passwords\n"
960 "\tMust not contain user ID\n"
961 "\tMust not contain sequence of three or more same characters\n"
962 "\tMust not start with `qwert', `asdgf', or `12345'\n"
963 "Finally, you must repeat your new password to avoid mistakes.\n"
964 "After password change will be confirmed, you must log in again as password\n"
965 "is transmitted to server on each request.\n"
966 "\n"));
968 old_password = ask_for_password(_("Old password: "));
969 if (!old_password || *old_password == '\0') {
970 fprintf(stderr, _("No password supplied\n"));
971 goto error;
974 new_password = ask_for_password(_("New password: "));
975 if (!new_password || *new_password == '\0') {
976 fprintf(stderr, _("No password supplied\n"));
977 goto error;
980 new_password2 = ask_for_password(_("Repeat new password: "));
981 if (!new_password2 || new_password2 == '\0') {
982 fprintf(stderr, _("No password supplied\n"));
983 goto error;
986 if (strcmp(new_password, new_password2)) {
987 fprintf(stderr, _("New passwords differ\n"));
988 goto error;
991 printf(_("Changing password...\n"));
992 err = isds_change_password(cisds, old_password, new_password);
993 finish_isds_operation(cisds, err);
994 if (err) {
995 printf(_("Password change failed\n"));
996 goto error;
997 } else {
998 printf(_("Password HAS been successfully changed.\n"));
999 goto leave;
1002 error:
1003 retval = -1;
1004 printf(_("Password has NOT been changed!\n"));
1006 leave:
1007 free(old_password);
1008 free(new_password);
1009 free(new_password2);
1011 set_prompt(NULL);
1012 select_completition(COMPL_COMMAND);
1014 printf(_("\n"
1015 "Remember, ISDS password has limited life time.\n"));
1016 return retval;
1020 static void shi_passwd_usage(const char *command) {
1021 printf(_(
1022 "Usage: %s [-S]\n"
1023 "Manipulate user password or change it if no option given.\n"
1024 "\n"
1025 "Options:\n"
1026 " -S show password expiration time\n"
1027 ), command);
1031 static int shi_passwd(int argc, const char **argv) {
1032 int opt;
1034 optind = 0;
1035 while ((opt = getopt(argc, (char * const *)argv, "S")) != -1) {
1036 switch (opt) {
1037 case 'S':
1038 return show_password_expiration();
1039 default:
1040 shi_passwd_usage(argv[0]);
1041 return -1;
1044 if (optind != argc || argc > 1) {
1045 printf(_("Bad invocation\n"));
1046 shi_passwd_usage((argv)?argv[0]:NULL);
1047 return -1;
1050 return do_passwd();
1054 static int shi_login(int argc, const char **argv) {
1055 discard_credentials();
1056 if (do_login()) return -1;
1057 cfg_setstr(configuration, CONFIG_SERVER, server);
1058 cfg_setstr(configuration, CONFIG_USERNAME, username);
1059 cfg_setstr(configuration, CONFIG_PASSWORD, password);
1060 show_password_expiration();
1061 return 0;
1065 static void shi_debug_usage(const char *command) {
1066 printf(_(
1067 "Usage: %s -l LEVEL [-f FACILITY...] [{-e | -o FILE}]\n"
1068 "Debug FACILITIES on LEVEL.\n"
1069 "\n"
1070 "-l LEVEL set log level, valid interval <%d,%d>, default is %d\n"
1071 " %d is no logging, %d critical, %d errors,\n"
1072 " %d warnings, %d info, %d debug, %d all\n"
1073 "-f FACILITY debug only given facility, repeat this option to debug\n"
1074 " more facilities; valid values: none, http, soap, isds,\n"
1075 " file, sec, xml, all; default is none\n"
1076 "-e write debug log into stderr\n"
1077 "-o FILE append debug log to FILE\n"
1079 command,
1080 ILL_NONE, ILL_ALL, ILL_NONE,
1081 ILL_NONE, ILL_CRIT, ILL_ERR, ILL_WARNING,
1082 ILL_INFO, ILL_DEBUG, ILL_ALL);
1085 static int shi_debug(int argc, const char **argv) {
1086 int opt;
1087 int log_level = ILL_NONE;
1088 isds_log_facility log_facility = ILF_NONE;
1089 char *file = NULL;
1090 _Bool close_log = 0;
1092 optind = 0;
1093 while ((opt = getopt(argc, (char * const *)argv, "l:f:eo:")) != -1) {
1094 switch (opt) {
1095 case 'l':
1096 log_level = normalize_log_level(atoi(optarg));
1097 break;
1098 case 'f':
1099 if (add_log_facility(&log_facility, optarg)) return -1;
1100 break;
1101 case 'e':
1102 close_log = 1;
1103 case 'o':
1104 file = optarg;
1105 break;
1106 default:
1107 shi_debug_usage(argv[0]);
1108 return -1;
1111 if (optind == 1 || optind != argc) {
1112 printf(_("Bad invocation\n"));
1113 shi_debug_usage(argv[0]);
1114 return -1;
1117 /* Redirect log */
1118 if (close_log) {
1119 isds_set_log_callback(NULL, NULL);
1121 if (logger_fd != -1) {
1122 if (-1 == close(logger_fd)) {
1123 fprintf(stderr, _("Closing log file failed: %s\n"),
1124 strerror(errno));
1125 return -1;
1128 cfg_setstr(configuration, CONFIG_LOGFILE, NULL);
1130 if (do_log_to_file(file))
1131 return -1;
1132 if (file) cfg_setstr(configuration, CONFIG_LOGFILE, file);
1134 /* Set log levels */
1135 isds_set_logging(log_facility, log_level);
1136 cfg_setint(configuration, CONFIG_LOGLEVEL, log_level);
1137 save_log_facility(log_facility);
1139 return 0;
1143 static void show_setting_str(const char *variable, _Bool cenzore) {
1144 if (!variable) return;
1146 const char *value = cfg_getstr(configuration, variable);
1148 if (value) {
1149 if (cenzore)
1150 printf(_("%s = <set>\n"), variable);
1151 else
1152 printf(_("%s = `%s'\n"), variable, value);
1153 } else {
1154 printf(_("%s = <unset>\n"), variable);
1159 static void show_setting_boolean(const char *variable) {
1160 if (!variable) return;
1162 _Bool value = cfg_getbool(configuration, variable);
1164 if (value) {
1165 printf(_("%s = <true>\n"), variable);
1166 } else {
1167 printf(_("%s = <false>\n"), variable);
1172 static void show_setting_int(const char *variable) {
1173 if (!variable) return;
1175 long int value = cfg_getint(configuration, variable);
1177 printf(_("%s = %ld\n"), variable, value);
1181 static void show_setting_strlist(const char *variable) {
1182 if (!variable) return;
1184 int length = cfg_size(configuration, variable);
1186 if (length <= 0) {
1187 printf(_("%s = <unset>\n"), variable);
1188 } else {
1189 printf(_("%s = {"), variable);
1190 for (int i = 0; i < length; i++) {
1191 const char *value = cfg_getnstr(configuration, variable, i);
1192 if (i < length - 1)
1193 printf(_("`%s', "), value);
1194 else
1195 printf(_("`%s'}\n"), value);
1201 static int shi_settings(int argc, const char **argv) {
1202 /*int opt;
1203 int log_level = ILL_NONE;
1204 isds_log_facility log_facility = ILF_NONE;
1205 char *file = NULL;
1206 _Bool close_log = 0;*/
1209 optind = 0;
1210 while ((opt = getopt(argc, (char * const *)argv, "l:f:eo:")) != -1) {
1211 switch (opt) {
1212 case 'l':
1213 log_level = normalize_log_level(atoi(optarg));
1214 break;
1215 case 'f':
1216 if (add_log_facility(&log_facility, optarg)) return -1;
1217 break;
1218 case 'e':
1219 close_log = 1;
1220 case 'o':
1221 file = optarg;
1222 break;
1223 default:
1224 shi_debug_usage(argv[0]);
1225 return -1;
1228 if (optind == 1 || optind != argc) {
1229 printf(_("Bad invocation\n"));
1230 shi_debug_usage(argv[0]);
1231 return -1;
1235 printf(_("Current settings:\n"));
1237 show_setting_str(CONFIG_SERVER, 0);
1238 show_setting_str(CONFIG_USERNAME, 0);
1239 show_setting_str(CONFIG_PASSWORD, 1),
1240 show_setting_boolean(CONFIG_VERIFYSERVER);
1241 show_setting_str(CONFIG_CAFILE, 0);
1242 show_setting_str(CONFIG_CADIRECTORY, 0);
1243 show_setting_str(CONFIG_CRLFILE, 0);
1244 show_setting_int(CONFIG_TIMEOUT);
1245 show_setting_strlist(CONFIG_LOGFACILITIES);
1246 show_setting_str(CONFIG_LOGFILE, 0);
1247 show_setting_int(CONFIG_LOGLEVEL);
1248 show_setting_boolean(CONFIG_MARKMESSAGEREAD);
1249 show_setting_boolean(CONFIG_NORMALIZEMIMETYPE);
1250 show_setting_boolean(CONFIG_OVERWRITEFILES);
1252 return 0;
1256 static void shi_find_box_usage(const char *command) {
1257 printf(_(
1258 "Usage: %s {OPTION... | BOX_ID}\n"
1259 "Get information about box with BOX_ID or boxes meeting other criteria.\n"
1260 "Each search option requires an argument:\n"
1261 " -t box type; accepted values:\n"
1262 " FO Private individual\n"
1263 " PFO Self-employed individual\n"
1264 " PFO_ADVOK Lawyer\n"
1265 " PFO_DANPOR Tax advisor\n"
1266 " PFO_INSSPR Insolvency administrator\n"
1267 " PO Organisation\n"
1268 " PO_ZAK Organization based by law\n"
1269 " PO_REQ Organization based on request\n"
1270 " OVM Public authority\n"
1271 " OVM_NOTAR Notary\n"
1272 " OVM_EXEKUT Executor\n"
1273 " OVM_REQ Public authority based on request\n"
1274 " -j identity number\n"
1275 "\n"
1276 "Person name options:\n"
1277 " -f first name\n"
1278 " -m middle name\n"
1279 " -l last name\n"
1280 " -b last name at birth\n"
1281 " -s subject name\n"
1282 "\n"
1283 "Birth options:\n"
1284 " -d birth date (locale or full ISO 8601 date)\n"
1285 " -w birth city\n"
1286 " -y birth county\n"
1287 " -c birth state\n"
1288 "\n"
1289 "Address:\n"
1290 " -W city\n"
1291 " -S street\n"
1292 " -z number in street\n"
1293 " -Z number in municipality\n"
1294 " -P ZIP code\n"
1295 " -C state\n"
1296 "\n"
1297 "Other options:\n"
1298 " -n nationality\n"
1299 " -e e-mail\n"
1300 " -p phone number\n"
1301 " -i identifier\n"
1302 " -r registry code\n"
1303 " -a box status; accepted values:\n"
1304 " ACCESSIBLE Accessible\n"
1305 " TEMP_INACCESSIBLE Temporary inaccessible\n"
1306 " NOT_YET_ACCESSIBLE Not yet accessible\n"
1307 " PERM_INACCESSIBLE Permanently inaccessible\n"
1308 " REMOVED Deleted\n"
1309 " -o act as public authority; boolean values: 0 is false, 1 is true\n"
1310 " -k receive commercial messages; boolean values\n"
1311 "\n"
1312 "Not all option combinations are meaningful or allowed. For example box\n"
1313 "type is always required (except direct box ID query).\n"
1314 "ISDS can refuse to answer to much broad query. Not all boxes are searchable\n"
1315 "by every user.\n"
1317 command);
1321 /* Allow reassignment */
1322 #define FILL_OR_LEAVE(variable, locale) { \
1323 zfree(variable); \
1324 (variable) = locale2utf8(locale); \
1325 if (!(variable)) { \
1326 printf(_("Error: Not enough memory\n")); \
1327 retval = -1; \
1328 goto leave; \
1332 #define CALLOC_OR_LEAVE(structure) { \
1333 if (!(structure)) { \
1334 (structure) = calloc(1, sizeof(*(structure))); \
1335 if (!(structure)) { \
1336 printf(_("Error: Not enough memory\n")); \
1337 retval = -1; \
1338 goto leave; \
1343 #define FILL_BOOLEAN_OR_LEAVE(variable, locale) { \
1344 zfree(variable); \
1345 (variable) = malloc(sizeof(*(variable))); \
1346 if (!(variable)) { \
1347 printf(_("Error: Not enough memory\n")); \
1348 retval = -1; \
1349 goto leave; \
1351 if (!strcmp((locale), "0")) *(variable) = 0; \
1352 else if (!strcmp((locale), "1")) *(variable) = 1; \
1353 else { \
1354 printf(_("%s: %s: Unknown boolean value\n"), argv[0], (locale)); \
1355 retval = -1; \
1356 goto leave; \
1360 #define FILL_LONGINT_OR_LEAVE(variable, locale) { \
1361 if (!(locale) || !*(locale)) { \
1362 printf(_("%s: Empty integer value\n"), argv[0]); \
1363 retval = -1; \
1364 goto leave; \
1366 char *endptr; \
1367 zfree(variable); \
1368 (variable) = malloc(sizeof(*(variable))); \
1369 if (!(variable)) { \
1370 printf(_("Error: Not enough memory\n")); \
1371 retval = -1; \
1372 goto leave; \
1374 (*variable) = strtol((locale), &endptr, 0); \
1375 if (*endptr) { \
1376 printf(_("%s: %s: Invalid integer value\n"), argv[0], (locale)); \
1377 retval = -1; \
1378 goto leave; \
1382 static int shi_find_box(int argc, const char **argv) {
1383 int opt;
1384 isds_error err;
1385 struct isds_DbOwnerInfo *criteria = NULL;
1386 struct isds_list *item;
1387 int order = 0;
1388 int retval = 0;
1390 if (!argv || !argv[1] || !*argv[1]) {
1391 printf(_("Error: No argument supplied\n"));
1392 shi_find_box_usage((argv)?argv[0]:NULL);
1393 return -1;
1396 criteria = calloc(1, sizeof(*criteria));
1397 if (!criteria) {
1398 printf(_("Error: Not enough memory\n"));
1399 retval = -1;
1400 goto leave;
1403 /* Parse options */
1404 optind = 0;
1405 while ((opt = getopt(argc, (char * const *)argv, "t:j:s:"
1406 "f:m:l:b:s:" "d:w:y:c:" "W:S:z:Z:P:C:"
1407 "n:e:p:i:r:a:o:k:")) != -1) {
1408 switch (opt) {
1409 case 't':
1410 criteria->dbType = malloc(sizeof(*criteria->dbType));
1411 if (!criteria->dbType) {
1412 printf(_("Error: Not enough memory\n"));
1413 retval = -1;
1414 goto leave;
1416 if (!strcmp(optarg, "FO"))
1417 *criteria->dbType = DBTYPE_FO;
1418 else if (!strcmp(optarg, "PFO"))
1419 *criteria->dbType = DBTYPE_PFO;
1420 else if (!strcmp(optarg, "PFO_ADVOK"))
1421 *criteria->dbType = DBTYPE_PFO_ADVOK;
1422 else if (!strcmp(optarg, "PFO_DANPOR"))
1423 *criteria->dbType = DBTYPE_PFO_DANPOR;
1424 else if (!strcmp(optarg, "PFO_INSSPR"))
1425 *criteria->dbType = DBTYPE_PFO_INSSPR;
1426 else if (!strcmp(optarg, "PO"))
1427 *criteria->dbType = DBTYPE_PO;
1428 else if (!strcmp(optarg, "PO_ZAK"))
1429 *criteria->dbType = DBTYPE_PO_ZAK;
1430 else if (!strcmp(optarg, "PO_REQ"))
1431 *criteria->dbType = DBTYPE_PO_REQ;
1432 else if (!strcmp(optarg, "OVM"))
1433 *criteria->dbType = DBTYPE_OVM;
1434 else if (!strcmp(optarg, "OVM_NOTAR"))
1435 *criteria->dbType = DBTYPE_OVM_NOTAR;
1436 else if (!strcmp(optarg, "OVM_EXEKUT"))
1437 *criteria->dbType = DBTYPE_OVM_EXEKUT;
1438 else if (!strcmp(optarg, "OVM_REQ"))
1439 *criteria->dbType = DBTYPE_OVM_REQ;
1440 else {
1441 printf(_("%s: %s: Unknown box type\n"), argv[0], optarg);
1442 retval = -1;
1443 goto leave;
1445 break;
1447 case 'j':
1448 FILL_OR_LEAVE(criteria->ic, optarg);
1449 break;
1451 /* Person name */
1452 case 'f':
1453 CALLOC_OR_LEAVE(criteria->personName);
1454 FILL_OR_LEAVE(criteria->personName->pnFirstName, optarg);
1455 break;
1456 case 'm':
1457 CALLOC_OR_LEAVE(criteria->personName);
1458 FILL_OR_LEAVE(criteria->personName->pnMiddleName, optarg);
1459 break;
1460 case 'l':
1461 CALLOC_OR_LEAVE(criteria->personName);
1462 FILL_OR_LEAVE(criteria->personName->pnLastName, optarg);
1463 break;
1464 case 'b':
1465 CALLOC_OR_LEAVE(criteria->personName);
1466 FILL_OR_LEAVE(criteria->personName->pnLastNameAtBirth, optarg);
1467 break;
1468 case 's':
1469 FILL_OR_LEAVE(criteria->firmName, optarg);
1470 break;
1472 /* Birth */
1473 case 'd':
1474 CALLOC_OR_LEAVE(criteria->birthInfo);
1475 criteria->birthInfo->biDate = datestring2tm(optarg);
1476 if (!criteria->birthInfo->biDate) {
1477 printf(_("Error: Could not parse date: %s\n"), optarg);
1478 retval = -1;
1479 goto leave;
1481 break;
1482 case 'w':
1483 CALLOC_OR_LEAVE(criteria->birthInfo);
1484 FILL_OR_LEAVE(criteria->birthInfo->biCity, optarg);
1485 break;
1486 case 'y':
1487 CALLOC_OR_LEAVE(criteria->birthInfo);
1488 FILL_OR_LEAVE(criteria->birthInfo->biCounty, optarg);
1489 break;
1490 case 'c':
1491 CALLOC_OR_LEAVE(criteria->birthInfo);
1492 FILL_OR_LEAVE(criteria->birthInfo->biState, optarg);
1493 break;
1495 /* Address */
1496 case 'W':
1497 CALLOC_OR_LEAVE(criteria->address);
1498 FILL_OR_LEAVE(criteria->address->adCity, optarg);
1499 break;
1500 case 'S':
1501 CALLOC_OR_LEAVE(criteria->address);
1502 FILL_OR_LEAVE(criteria->address->adStreet, optarg);
1503 break;
1504 case 'z':
1505 CALLOC_OR_LEAVE(criteria->address);
1506 FILL_OR_LEAVE(criteria->address->adNumberInStreet, optarg);
1507 break;
1508 case 'Z':
1509 CALLOC_OR_LEAVE(criteria->address);
1510 FILL_OR_LEAVE(criteria->address->adNumberInMunicipality,
1511 optarg);
1512 break;
1513 case 'P':
1514 CALLOC_OR_LEAVE(criteria->address);
1515 FILL_OR_LEAVE(criteria->address->adZipCode, optarg);
1516 break;
1517 case 'C':
1518 CALLOC_OR_LEAVE(criteria->address);
1519 FILL_OR_LEAVE(criteria->address->adState, optarg);
1520 break;
1522 /* Other options */
1523 case 'n':
1524 FILL_OR_LEAVE(criteria->nationality, optarg);
1525 break;
1526 case 'e':
1527 FILL_OR_LEAVE(criteria->email, optarg);
1528 break;
1529 case 'p':
1530 FILL_OR_LEAVE(criteria->telNumber, optarg);
1531 break;
1532 case 'i':
1533 FILL_OR_LEAVE(criteria->identifier, optarg);
1534 break;
1535 case 'r':
1536 FILL_OR_LEAVE(criteria->registryCode, optarg);
1537 break;
1538 case 'a':
1539 criteria->dbState = malloc(sizeof(*criteria->dbState));
1540 if (!criteria->dbState) {
1541 printf(_("Error: Not enough memory\n"));
1542 retval = -1;
1543 goto leave;
1545 if (!strcmp(optarg, "ACCESSIBLE"))
1546 *criteria->dbState = DBSTATE_ACCESSIBLE;
1547 else if (!strcmp(optarg, "TEMP_INACCESSIBLE"))
1548 *criteria->dbState = DBSTATE_TEMP_UNACCESSIBLE;
1549 else if (!strcmp(optarg, "NOT_YET_ACCESSIBLE"))
1550 *criteria->dbState = DBSTATE_NOT_YET_ACCESSIBLE;
1551 else if (!strcmp(optarg, "PERM_INACCESSIBLE"))
1552 *criteria->dbState = DBSTATE_PERM_UNACCESSIBLE;
1553 else if (!strcmp(optarg, "REMOVED"))
1554 *criteria->dbState = DBSTATE_REMOVED;
1555 else {
1556 printf(_("%s: %s: Unknown box status\n"), argv[0], optarg);
1557 retval = -1;
1558 goto leave;
1560 break;
1561 case 'o':
1562 FILL_BOOLEAN_OR_LEAVE(criteria->dbEffectiveOVM, optarg);
1563 break;
1564 case 'k':
1565 FILL_BOOLEAN_OR_LEAVE(criteria->dbOpenAddressing, optarg);
1566 break;
1568 default:
1569 shi_find_box_usage(argv[0]);
1570 retval = -1;
1571 goto leave;
1575 /* There must be an option and all of them must be recognized, if not only
1576 * BOX_ID supplied */
1577 if (argc > 2 && optind != argc) {
1578 printf(_("Error: Superfluous argument\n"));
1579 shi_find_box_usage(argv[0]);
1580 retval = -1;
1581 goto leave;
1584 /* If only box ID is supplied use it */
1585 if (argc == 2 && argv[1] && *argv[1]) {
1586 criteria->dbID = locale2utf8(argv[1]);
1587 if (!criteria->dbID) {
1588 printf(_("Error: Not enough memory\n"));
1589 retval = -1;
1590 goto leave;
1594 printf(_("Searching boxes...\n"));
1595 err = isds_FindDataBox(cisds, criteria, &boxes);
1596 finish_isds_operation(cisds, err);
1597 if (err) return -1;
1599 for(item = boxes; item; item = item->next) {
1600 if (!item->data) continue;
1601 order++;
1603 printf(_("\n* Result #%d:\n"), order);
1604 format_DbOwnerInfo(item->data);
1607 leave:
1608 isds_DbOwnerInfo_free(&criteria);
1609 return retval;
1613 static void shi_stat_box_usage(const char *command) {
1614 printf(_(
1615 "Usage: %s BOX_ID...\n"
1616 "Get status of box with BOX_ID. More boxes can be specified.\n"),
1617 command);
1621 /* Get boxes status */
1622 static int shi_stat_box(int argc, const char **argv) {
1623 isds_error err;
1624 char *id = NULL;
1625 long int status;
1627 if (!argv || !*argv || argc < 2 || !argv[1]) {
1628 printf(_("Missing box ID\n"));
1629 shi_stat_box_usage((argv[0])?argv[0]:NULL);
1630 return -1;
1633 for (int i = 1; i < argc; i++) {
1634 if (!argv[i] || !*argv[i]) continue;
1636 free(id);
1637 id = locale2utf8(argv[i]);
1638 if (!id) {
1639 printf(_("%s: Could not covert box ID to UTF-8\n"), argv[i]);
1640 return -1;
1643 printf(_("Getting status of box `%s'...\n"), argv[i]);
1644 err = isds_CheckDataBox(cisds, id, &status);
1645 finish_isds_operation(cisds, err);
1646 if (err) return -1;
1648 printf(_("Status of box `%s': %s\n"),
1649 argv[i], DbState2string(&status));
1652 return 0;
1656 static void shi_compose_usage(const char *command) {
1657 printf(_(
1658 "Usage: %s OPTION...\n"
1659 "Compose and send a message to recipient defined by his box ID.\n"
1660 "Each option requires an argument (if not stated otherwise):\n"
1661 " -s * message subject\n"
1662 "\n"
1663 "Recipient options:\n"
1664 " -b * recipient box ID\n"
1665 " -U organisation unit name\n"
1666 " -N organisation unit number\n"
1667 " -P to hands of given person\n"
1668 "\n"
1669 "Sender organisation structure options:\n"
1670 " -u unit name\n"
1671 " -n unit number\n"
1672 "\n"
1673 "Message identifier options:\n"
1674 " -r sender reference number\n"
1675 " -f sender file ID\n"
1676 " -R recipient reference number\n"
1677 " -F recipient file ID\n"
1678 "\n"
1679 "Legal title options:\n"
1680 " -y year act has been issued\n"
1681 " -a ordinal number of act in a year\n"
1682 " -e section of the act\n"
1683 " -o paragraph of the act\n"
1684 " -i point of the paragraph of the act\n"
1685 "\n"
1686 "Delivery options:\n"
1687 " -p personal delivery required\n"
1688 " -t allow substitutable delivery\n"
1689 " -A non-OVM sender acts as public authority\n"
1690 "\n"
1691 "Document options:\n"
1692 " -d * read document from local file\n"
1693 " -D document name (defaults to base local file name)\n"
1694 " -x transport subset of the document as a XML.\n"
1695 " Argument is XPath expression specifying desired node set\n"
1696 " -m override MIME type (guessed on -d)\n"
1697 " -g document ID (must be unique per message)\n"
1698 " -G reference to other document using its ID\n"
1699 " -c document is digital signature of other document (NO argument\n"
1700 " allowed)\n"
1701 "\n"
1702 "Options marked with asterisk are mandatory, other are optional. Another soft\n"
1703 "dependencies can emerge upon using specific option. They are not mandated by\n"
1704 "ISDS currently, but client library or this program can force them to assure\n"
1705 "semantically complete message. Following soft dependencies are recommended:\n"
1706 " -y <=> -a act number and year must be used at the same time\n"
1707 " -i => -o act point requires act paragraph\n"
1708 " -o => -e act paragraph requires act section\n"
1709 " -e => -a act section requires act number\n"
1710 " -G => -g document with referenced ID must exist\n"
1711 " -c => -G signature must refer to signed document\n"
1712 " -c first document cannot be signature\n"
1713 "\n"
1714 "More documents can be attached to a message by repeating `-d' option.\n"
1715 "Document order will be preserved. Other document options affect immediately\n"
1716 "preceding `-d' document only. E.g. `-d /tmp/foo.pdf -m application/pdf\n"
1717 "-d /tmp/bar.txt -m text/plain' attaches first PDF file, then textual file.\n"
1718 "\n"
1719 "The same applies to recipient options that must start with box ID (-b).\n"
1720 "If more recipients specified, each of them will get a copy of composed\n"
1721 "message. ISDS will assign message identifier to each copy in turn.\n"
1723 command);
1727 static int shi_compose(int argc, const char **argv) {
1728 int opt;
1729 isds_error err;
1730 int retval = 0;
1731 struct isds_message *message = NULL;
1732 struct isds_envelope *envelope = NULL;
1733 struct isds_list *documents = NULL;
1734 struct isds_document *document = NULL;
1735 struct isds_list *copies = NULL, *copy_item = NULL;
1736 struct isds_message_copy *copy = NULL;
1737 char *message_id_locale = NULL, *recipient_id_locale = NULL,
1738 *dmStatus_locale = NULL;
1740 if (!argv || !argv[1] || !*argv[1]) {
1741 printf(_("Error: No argument supplied\n"));
1742 shi_compose_usage((argv)?argv[0]:NULL);
1743 return -1;
1746 message = calloc(1, sizeof(*message));
1747 if (!message) {
1748 printf(_("Error: Not enough memory\n"));
1749 retval = -1;
1750 goto leave;
1752 envelope = calloc(1, sizeof(*envelope));
1753 if (!envelope) {
1754 printf(_("Error: Not enough memory\n"));
1755 retval = -1;
1756 goto leave;
1758 message->envelope = envelope;
1760 /* Parse options */
1761 optind = 0;
1762 while ((opt = getopt(argc, (char * const *)argv, "s:" "b:U:N:P:" "u:n:"
1763 "r:f:R:F:" "y:a:e:o:i:" "p:t:A:" "d:D:x:m:g:G:c"
1764 )) != -1) {
1765 switch (opt) {
1766 case 's':
1767 FILL_OR_LEAVE(envelope->dmAnnotation, optarg);
1768 break;
1770 /* Recipient options */
1771 case 'b':
1772 copy = NULL;
1773 if (!copies) {
1774 /* First recipient */
1775 CALLOC_OR_LEAVE(copies);
1776 copies->destructor =
1777 (void(*)(void **)) isds_message_copy_free;
1778 copy_item = copies;
1779 } else {
1780 /* Next recipient */
1781 CALLOC_OR_LEAVE(copy_item->next);
1782 copy_item->next->destructor =
1783 (void(*)(void **)) isds_message_copy_free;
1784 copy_item = copy_item->next;
1786 CALLOC_OR_LEAVE(copy);
1787 copy_item->data = copy;
1789 /* Copy recipient box ID */
1790 FILL_OR_LEAVE(copy->dbIDRecipient, optarg);
1791 break;
1792 case 'U':
1793 if (!copy) {
1794 printf(_("Error: %s: Recipient box ID (-b) must precede "
1795 "recipient organisation unit name (-%c)\n"),
1796 optarg, opt);
1797 retval = -1;
1798 goto leave;
1800 FILL_OR_LEAVE(copy->dmRecipientOrgUnit, optarg);
1801 break;
1802 case 'N':
1803 if (!copy) {
1804 printf(_("Error: %s: Recipient box ID (-b) must precede "
1805 "recipient organisation unit number (-%c)\n"),
1806 optarg, opt);
1807 retval = -1;
1808 goto leave;
1810 FILL_LONGINT_OR_LEAVE(copy->dmRecipientOrgUnitNum, optarg);
1811 break;
1812 case 'P':
1813 if (!copy) {
1814 printf(_("Error: %s: Recipient box ID (-b) must precede "
1815 "to-hands option (-%c)\n"), optarg, opt);
1816 retval = -1;
1817 goto leave;
1819 FILL_OR_LEAVE(copy->dmToHands, optarg);
1820 break;
1822 /* Sender organisation structure options */
1823 case 'u':
1824 FILL_OR_LEAVE(envelope->dmSenderOrgUnit, optarg);
1825 break;
1826 case 'n':
1827 FILL_LONGINT_OR_LEAVE(envelope->dmSenderOrgUnitNum, optarg);
1828 break;
1830 /* Message identifier options */
1831 case 'r':
1832 FILL_OR_LEAVE(envelope->dmSenderRefNumber, optarg);
1833 break;
1834 case 'f':
1835 FILL_OR_LEAVE(envelope->dmSenderIdent, optarg);
1836 break;
1837 case 'R':
1838 FILL_OR_LEAVE(envelope->dmRecipientRefNumber, optarg);
1839 break;
1840 case 'F':
1841 FILL_OR_LEAVE(envelope->dmRecipientIdent, optarg);
1842 break;
1844 /* Legal title options */
1845 case 'y':
1846 FILL_LONGINT_OR_LEAVE(envelope->dmLegalTitleYear, optarg);
1847 break;
1848 case 'a':
1849 FILL_LONGINT_OR_LEAVE(envelope->dmLegalTitleLaw, optarg);
1850 break;
1851 case 'e':
1852 FILL_OR_LEAVE(envelope->dmLegalTitleSect, optarg);
1853 break;
1854 case 'o':
1855 FILL_OR_LEAVE(envelope->dmLegalTitlePar, optarg);
1856 break;
1857 case 'i':
1858 FILL_OR_LEAVE(envelope->dmLegalTitlePoint, optarg);
1859 break;
1861 /* Delivery options */
1862 case 'p':
1863 FILL_BOOLEAN_OR_LEAVE(envelope->dmPersonalDelivery, optarg);
1864 break;
1865 case 't':
1866 FILL_BOOLEAN_OR_LEAVE(envelope->dmAllowSubstDelivery, optarg);
1867 break;
1868 case 'A':
1869 FILL_BOOLEAN_OR_LEAVE(envelope->dmOVM, optarg);
1870 break;
1872 /* Document options */
1873 case 'd':
1874 document = NULL;
1875 if (!documents) {
1876 /* First document */
1877 CALLOC_OR_LEAVE(message->documents);
1878 message->documents->destructor =
1879 (void(*)(void **)) isds_document_free;
1880 documents = message->documents;
1881 CALLOC_OR_LEAVE(document);
1882 documents->data = document;
1883 document->dmFileMetaType = FILEMETATYPE_MAIN;
1884 } else {
1885 /* Next document */
1886 CALLOC_OR_LEAVE(documents->next);
1887 documents->next->destructor =
1888 (void(*)(void **)) isds_document_free;
1889 documents = documents->next;
1890 CALLOC_OR_LEAVE(document);
1891 documents->data = document;
1892 document->dmFileMetaType = FILEMETATYPE_ENCLOSURE;
1895 if (load_data_from_file(optarg, &document->data,
1896 &document->data_length, &document->dmMimeType)) {
1897 retval = -1;
1898 goto leave;
1900 /* XXX: POSIX basename() modifies argument */
1901 FILL_OR_LEAVE(document->dmFileDescr, basename(optarg));
1902 break;
1903 case 'D':
1904 if (!document) {
1905 printf(_("Error: %s: Document file (-d) must precede "
1906 "document name (-%c)\n"), optarg, opt);
1907 retval = -1;
1908 goto leave;
1910 FILL_OR_LEAVE(document->dmFileDescr, optarg);
1911 break;
1912 case 'x':
1913 if (!document) {
1914 printf(_("Error: %s: Document file (-d) must precede "
1915 "XPath expression (-%c)\n"), optarg, opt);
1916 retval = -1;
1917 goto leave;
1919 /* Load XML node list */
1920 char *xpath_expr = NULL;
1921 FILL_OR_LEAVE(xpath_expr, optarg);
1922 retval = load_xml_subtree_from_memory(
1923 document->data, document->data_length,
1924 &document->xml_node_list, xpath_expr);
1925 if (retval) {
1926 free(xpath_expr);
1927 goto leave;
1929 /* Switch document type to XML */
1930 document->is_xml = 1;
1931 zfree(document->data);
1932 document->data_length = 0;
1933 documents->destructor =
1934 (void(*)(void **)) free_document_with_xml_node_list;
1935 break;
1936 case 'm':
1937 if (!document) {
1938 printf(_("Error: %s: Document file (-d) must precede "
1939 "MIME type (-%c)\n"), optarg, opt);
1940 retval = -1;
1941 goto leave;
1943 FILL_OR_LEAVE(document->dmMimeType, optarg);
1944 break;
1945 case 'g':
1946 if (!document) {
1947 printf(_("Error: %s: Document file (-d) must precede "
1948 "document ID (-%c)\n"), optarg, opt);
1949 retval = -1;
1950 goto leave;
1952 FILL_OR_LEAVE(document->dmFileGuid, optarg);
1953 break;
1954 case 'G':
1955 if (!document) {
1956 printf(_("Error: %s: Document file (-d) must precede "
1957 "document reference (-%c)\n"), optarg, opt);
1958 retval = -1;
1959 goto leave;
1961 FILL_OR_LEAVE(document->dmUpFileGuid, optarg);
1962 break;
1963 case 'c':
1964 if (!document) {
1965 printf(_("Error: Document file (-d) must precede "
1966 "document signature type (-%c)\n"), opt);
1967 retval = -1;
1968 goto leave;
1970 document->dmFileMetaType = FILEMETATYPE_SIGNATURE;
1971 break;
1973 default:
1974 shi_compose_usage(argv[0]);
1975 retval = -1;
1976 goto leave;
1980 /* All options must be recognized */
1981 if (optind != argc) {
1982 printf(_("Error: Superfluous argument\n"));
1983 shi_compose_usage(argv[0]);
1984 retval = -1;
1985 goto leave;
1988 if (!copies) {
1989 printf(_("Error: No recipient box ID specified\n"));
1990 shi_compose_usage(argv[0]);
1991 retval = -1;
1992 goto leave;
1995 /* TODO: Check Legal Title soft dependencies */
1997 /* Preview */
1998 printf(_("Following message has been composed:\n"));
1999 format_message(message);
2000 printf(_("Following recipients have been specified:\n"));
2001 format_copies(copies);
2002 /* TODO: Confirmation, correction */
2004 /* Send a message */
2005 printf(_("Sending message...\n"));
2006 err = isds_send_message_to_multiple_recipients(cisds, message, copies);
2007 finish_isds_operation(cisds, err);
2008 if (err && err != IE_PARTIAL_SUCCESS) {
2009 retval = -1;
2010 goto leave;
2013 /* Show results for each copy */
2014 for (copy_item = copies; copy_item; copy_item = copy_item->next) {
2015 if (!copy_item->data) continue;
2016 copy = (struct isds_message_copy *) copy_item->data;
2017 recipient_id_locale = utf82locale(copy->dbIDRecipient);
2019 if (copy->error) {
2020 retval = -1;
2021 if (copy->dmStatus) dmStatus_locale = utf82locale(copy->dmStatus);
2022 if (dmStatus_locale)
2023 printf(_("%s: Failed: %s: %s\n"),
2024 recipient_id_locale,
2025 isds_strerror(copy->error), dmStatus_locale);
2026 else
2027 printf(_("%s: Failed: %s\n"), recipient_id_locale,
2028 isds_strerror(copy->error));
2029 zfree(dmStatus_locale);
2030 } else {
2031 message_id_locale = utf82locale(copy->dmID);
2032 printf(_("%s: Succeeded. Assigned message ID: %s\n"),
2033 recipient_id_locale, message_id_locale);
2034 free(message_id_locale);
2037 free(recipient_id_locale);
2040 leave:
2041 isds_message_free(&message);
2042 isds_list_free(&copies);
2043 return retval;
2046 #undef FILL_LONGINT_OR_LEAVE
2047 #undef FILL_BOOLEAN_OR_LEAVE
2048 #undef CALLOC_OR_LEAVE
2049 #undef FILL_OR_LEAVE
2052 static void shi_delivery_usage(const char *command) {
2053 printf(_(
2054 "Usage: %s MESSAGE_ID\n"
2055 "Get delivery data about message with MESSAGE_ID.\n"),
2056 command);
2060 static int shi_delivery(int argc, const char **argv) {
2061 isds_error err;
2062 const char *id;
2064 if (!argv || !argv[1] || !*argv[1]) {
2065 shi_delivery_usage(argv[0]);
2066 return -1;
2068 id = argv[1];
2070 printf(_("Getting delivery info...\n"));
2071 err = isds_get_signed_delivery_info(cisds, id, &message);
2072 finish_isds_operation(cisds, err);
2073 if (err) {
2074 set_prompt(NULL);
2075 select_completition(COMPL_COMMAND);
2076 return -1;
2079 format_message(message);
2081 if (message->envelope && message->envelope->dmID)
2082 set_prompt(_("%s %s"), argv[0], message->envelope->dmID);
2083 else
2084 set_prompt("%s", argv[0]);
2085 select_completition(COMPL_MSG);
2086 return 0;
2090 static int shi_dump_message(int argc, const char **argv) {
2091 if (!message) {
2092 printf(_("No message loaded\n"));
2093 return -1;
2096 print_message(message);
2097 return 0;
2101 static void shi_hash_usage(const char *command) {
2102 printf(_(
2103 "Usage: %s [MESSAGE_ID]\n"
2104 "Retrieve message hash stored in ISDS.\n"
2105 "If MESSAGE_ID is defined, query for that message.\n"
2106 "Otherwise use current message.\n"),
2107 command);
2111 static int shi_hash(int argc, const char **argv) {
2112 isds_error err;
2113 const char *id = NULL;
2114 struct isds_hash *hash = NULL;
2115 char *hash_string = NULL;
2117 if (!argv || argc > 2) {
2118 shi_hash_usage((argv)?argv[0]:NULL);
2119 return -1;
2121 if (argc == 2 && argv[1] && *argv[1])
2122 id = argv[1];
2123 else {
2124 if (!message) {
2125 printf(_("No message loaded\n"));
2126 return -1;
2128 if (!message->envelope || !message->envelope->dmID) {
2129 printf(_("Current message is missing ID\n"));
2130 return -1;
2132 id = message->envelope->dmID;
2135 printf(_("Getting message hash...\n"));
2136 err = isds_download_message_hash(cisds, id, &hash);
2137 finish_isds_operation(cisds, err);
2138 if (err) return -1;
2140 hash_string = hash2string(hash);
2141 printf(_("ISDS states message with `%s' ID has following hash:\n%s\n"),
2142 id, hash_string);
2144 free(hash_string);
2145 isds_hash_free(&hash);
2146 return 0;
2150 static int shi_verify(int argc, const char **argv) {
2151 isds_error err;
2152 int retval = 0;
2153 struct isds_hash *retrieved_hash = NULL, *stored_hash = NULL;
2154 char *hash_string = NULL;
2155 size_t width = 4;
2157 if (!message) {
2158 printf(_("No message loaded\n"));
2159 return -1;
2162 if (!message->envelope) {
2163 printf(_("Current message is missing envelope\n"));
2164 return -1;
2166 stored_hash = message->envelope->hash;
2167 message->envelope->hash = NULL;
2169 if (message->envelope->dmID) {
2170 /* Verify remote hash */
2171 printf(_("Remote hash check:\n"));
2173 printf(_("Getting message hash...\n"));
2174 err = isds_download_message_hash(cisds, message->envelope->dmID,
2175 &retrieved_hash);
2176 finish_isds_operation(cisds, err);
2178 if (retrieved_hash) {
2179 hash_string = hash2string(retrieved_hash);
2180 fhprint(stdout, _("Retrieved:"), width);
2181 printf("%s\n", hash_string);
2182 zfree(hash_string);
2185 if (retrieved_hash && message->raw) {
2186 err = isds_compute_message_hash(cisds, message,
2187 retrieved_hash->algorithm);
2188 finish_isds_operation(cisds, err);
2190 if (!err) {
2191 hash_string = hash2string(message->envelope->hash);
2192 fhprint(stdout, _("Computed:"), width);
2193 printf("%s\n", hash_string);
2194 zfree(hash_string);
2198 err = isds_hash_cmp(retrieved_hash, message->envelope->hash);
2199 switch (err) {
2200 case IE_SUCCESS:
2201 printf(_("Hashes match.\n")); break;
2202 case IE_NOTUNIQ:
2203 printf(_("Hashes do not match.\n"));
2204 retval = -1;
2205 break;
2206 default:
2207 printf(_("Hashes could not be compared.\n"));
2208 retval = -1;
2209 break;
2212 free(retrieved_hash);
2216 if (stored_hash) {
2217 /* Verify stored hash */
2218 printf(_("Stored hash check:\n"));
2220 hash_string = hash2string(stored_hash);
2221 fhprint(stdout, _("Stored:"), width);
2222 printf("%s\n", hash_string);
2223 zfree(hash_string);
2225 if (message->raw) {
2226 err = isds_compute_message_hash(cisds, message,
2227 stored_hash->algorithm);
2228 finish_isds_operation(cisds, err);
2230 if (!err) {
2231 hash_string = hash2string(message->envelope->hash);
2232 fhprint(stdout, _("Computed:"), width);
2233 printf("%s\n", hash_string);
2234 zfree(hash_string);
2238 err = isds_hash_cmp(stored_hash, message->envelope->hash);
2239 switch (err) {
2240 case IE_SUCCESS:
2241 printf(_("Hashes match.\n")); break;
2242 case IE_NOTUNIQ:
2243 printf(_("Hashes do not match.\n"));
2244 retval = -1;
2245 break;
2246 default:
2247 printf(_("Hashes could not be compared.\n"));
2248 retval = -1;
2249 break;
2252 isds_hash_free(&message->envelope->hash);
2255 message->envelope->hash = stored_hash;
2256 return retval;
2260 static int shi_authenticate(int argc, const char **argv) {
2261 isds_error err;
2262 int retval = 0;
2264 if (!message) {
2265 printf(_("No message loaded\n"));
2266 return -1;
2268 if (!message->raw || message->raw_length == 0) {
2269 printf(_("Current message is missing raw representation\n"));
2270 return -1;
2273 printf(_("Submitting message to authenticity check...\n"));
2274 err = isds_authenticate_message(cisds, message->raw, message->raw_length);
2275 finish_isds_operation(cisds, (err == IE_NOTUNIQ) ? IE_SUCCESS : err);
2277 switch (err) {
2278 case IE_SUCCESS:
2279 printf(_("Message originates in ISDS.\n")); break;
2280 case IE_NOTUNIQ:
2281 printf(_("Message is unknown to ISDS or has been tampered.\n"));
2282 retval = -1;
2283 break;
2284 default:
2285 retval = -1;
2286 break;
2289 return retval;
2293 static void shi_accept_message_usage(const char *command) {
2294 printf(_(
2295 "Usage: %s [MESSAGE_ID...]\n"
2296 "Accept commercial message moving its state to received.\n"
2297 "If MESSAGE_ID is defined, accept that message. More messages can be specified.\n"
2298 "Otherwise accept all commercial incoming messages.\n"),
2299 command);
2303 static int shi_accept_message(int argc, const char **argv) {
2304 isds_error err;
2305 char *id = NULL;
2307 /* Process messages named in argv */
2308 for (int i = 1; i < argc; i++) {
2309 if (!argv[i] || !*argv[i]) continue;
2311 id = locale2utf8(argv[i]);
2312 if (!id) {
2313 printf(_("Error: Could not convert message ID to UTF-8: %s\n"),
2314 argv[i]);
2315 return -1;
2318 printf(_("Accepting message `%s'...\n"), argv[i]);
2319 err = isds_mark_message_received(cisds, id);
2320 finish_isds_operation(cisds, err);
2321 if (err) {
2322 free(id);
2323 return -1;
2326 printf(_("Message `%s' accepted\n"), argv[i]);
2327 free(id);
2330 if (argc < 2) {
2331 /* TODO: list commercial not received messages and accept all of them
2332 * */
2333 printf(_("Error: No message ID supplied. Accepting all commercial "
2334 "messages not implemented yet.\n"));
2335 return -1;
2338 return 0;
2342 /* Mark message as read. At one form of ID must be provided.
2343 * @id is UTF-8 encoded message ID
2344 * @id_locale is locale encoded message ID. @id takes preference. */
2345 static int do_read_message(const char *id, const char *id_locale) {
2346 _Bool static_id;
2347 isds_error err;
2349 if ((!id || !*id) && (!id_locale || !*id_locale)) return -1;
2351 if (id) {
2352 static_id = 1;
2353 id_locale = utf82locale(id);
2354 } else {
2355 static_id = 0;
2356 id = locale2utf8(id_locale);
2357 if (!id) {
2358 printf(_("Error: Could not convert message ID to UTF-8: %s\n"),
2359 id_locale);
2360 return -1;
2364 printf(_("Marking message `%s' as read...\n"), id_locale);
2365 err = isds_mark_message_read(cisds, id);
2366 finish_isds_operation(cisds, err);
2368 if (!err)
2369 printf(_("Message `%s' marked as read\n"), id_locale);
2371 if (static_id) free((char *) id_locale);
2372 else free((char *) id);
2374 return (err) ? -1 : 0;
2378 static void shi_read_message_usage(const char *command) {
2379 printf(_(
2380 "Usage: %s [MESSAGE_ID...]\n"
2381 "Mark message as read moving its state to read.\n"
2382 "\n"
2383 "When new incoming message is download, its state is not changed on server.\n"
2384 "Client must mark such message as read explicitly. You can use this command\n"
2385 "to do so, if not done automatically at download time by your client.\n"
2386 "\n"
2387 "If MESSAGE_ID is defined, mark that message. More messages can be specified.\n"
2388 "Otherwise marks currently loaded message.\n"),
2389 command);
2393 static int shi_read_message(int argc, const char **argv) {
2394 for (int i = 1; i < argc; i++) {
2395 if (!argv[i] || !*argv[i]) continue;
2396 if (do_read_message(NULL, argv[i]))
2397 return -1;
2400 if (argc < 2) {
2401 if (!message) {
2402 printf(_("No message loaded\n"));
2403 return -1;
2405 if (!message->envelope) {
2406 printf(_("Loaded message is missing envelope\n"));
2407 return -1;
2409 if (!message->envelope->dmID || !*message->envelope->dmID) {
2410 printf(_("Loaded message is missing ID\n"));
2411 return -1;
2413 return do_read_message(message->envelope->dmID, NULL);
2416 return 0;
2420 static int shi_show_message(int argc, const char **argv) {
2421 if (!message) {
2422 printf(_("No message loaded\n"));
2423 return -1;
2426 format_message(message);
2427 return 0;
2431 static void shi_incoming_message_usage(const char *command) {
2432 printf(_(
2433 "Usage: %s [-r] MESSAGE_ID\n"
2434 "Get incoming message with MESSAGE_ID.\n"
2435 "Options:\n"
2436 " -r Mark mesage as read\n"),
2437 command);
2441 static int shi_incoming_message(int argc, const char **argv) {
2442 isds_error err;
2443 const char *id;
2444 int opt;
2445 _Bool mark_as_read = 0;
2447 optind = 0;
2448 while ((opt = getopt(argc, (char * const *)argv, "r")) != -1) {
2449 switch (opt) {
2450 case 'r':
2451 mark_as_read = 1;
2452 break;
2453 default:
2454 shi_incoming_message_usage((argv)?argv[0]:NULL);
2455 return -1;
2458 if (optind + 1 != argc || !argv || !argv[optind] || !*argv[optind]) {
2459 printf(_("Bad invocation\n"));
2460 shi_incoming_message_usage((argv)?argv[0]:NULL);
2461 return -1;
2463 id = argv[optind];
2465 printf(_("Getting incoming message...\n"));
2466 err = isds_get_signed_received_message(cisds, id, &message);
2467 finish_isds_operation(cisds, err);
2468 if (err) {
2469 set_prompt(NULL);
2470 select_completition(COMPL_COMMAND);
2471 return -1;
2474 format_message(message);
2476 if (message->envelope && message->envelope->dmID)
2477 set_prompt(_("%s %s"), argv[0], message->envelope->dmID);
2478 else
2479 set_prompt("%s", argv[0]);
2480 select_completition(COMPL_MSG);
2482 if (mark_as_read || cfg_getbool(configuration, CONFIG_MARKMESSAGEREAD)) {
2483 return do_read_message(id, NULL);
2485 return 0;
2489 static void shi_outgoing_message_usage(const char *command) {
2490 printf(_(
2491 "Usage: %s MESSAGE_ID\n"
2492 "Get outgoing message with MESSAGE_ID.\n"),
2493 command);
2497 static int shi_outgoing_message(int argc, const char **argv) {
2498 isds_error err;
2499 const char *id;
2501 if (!argv || !argv[1] || !*argv[1]) {
2502 shi_outgoing_message_usage(argv[0]);
2503 return -1;
2505 id = argv[1];
2507 printf(_("Getting outgoing message...\n"));
2508 err = isds_get_signed_sent_message(cisds, id, &message);
2509 finish_isds_operation(cisds, err);
2510 if (err) {
2511 set_prompt(NULL);
2512 select_completition(COMPL_COMMAND);
2513 return -1;
2516 format_message(message);
2517 if (message->envelope && message->envelope->dmID)
2518 set_prompt(_("%s %s"), argv[0], message->envelope->dmID);
2519 else
2520 set_prompt("%s", argv[0]);
2521 select_completition(COMPL_MSG);
2522 return 0;
2526 static void shi_load_anything_usage(const char *command) {
2527 printf(_(
2528 "Usage: %s FILE\n"
2529 "Load message or message delivery details from local FILE.\n"),
2530 command);
2534 static int shi_load_anything(int argc, const char **argv) {
2535 int fd;
2536 void *buffer = NULL;
2537 size_t length;
2538 isds_raw_type raw_type;
2539 isds_error err;
2540 char *type_name = NULL;
2542 if (!argv || !argv[1] || !*argv[1]) {
2543 shi_load_anything_usage((argv)?argv[0]:NULL);
2544 return -1;
2547 printf(_("Loading file `%s'...\n"), argv[1]);
2549 if (mmap_file(argv[1], &fd, &buffer, &length)) return -1;
2551 printf(_("Detecting file format...\n"));
2552 err = isds_guess_raw_type(cisds, &raw_type, buffer, length);
2553 finish_isds_operation(cisds, err);
2555 if (err) {
2556 if (err == IE_NOTSUP)
2557 printf(_("Unknown format. Could not parse the file.\n"));
2558 else
2559 printf(_("Error while detecting format. "
2560 "Could not parse the file.\n"));
2561 } else {
2562 switch (raw_type) {
2563 case RAWTYPE_INCOMING_MESSAGE:
2564 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
2565 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
2566 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
2567 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
2568 err = isds_load_message(cisds, raw_type,
2569 buffer, length, &message, BUFFER_COPY);
2570 finish_isds_operation(cisds, err);
2571 type_name = N_("message");
2572 break;
2574 case RAWTYPE_DELIVERYINFO:
2575 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO:
2576 case RAWTYPE_CMS_SIGNED_DELIVERYINFO:
2577 err = isds_load_delivery_info(cisds, raw_type,
2578 buffer, length, &message, BUFFER_COPY);
2579 finish_isds_operation(cisds, err);
2580 type_name = N_("delivery");
2581 break;
2583 default:
2584 printf(_("Unsupported format. Could not parse the file.\n"));
2585 err = IE_NOTSUP;
2589 munmap_file(fd, buffer, length);
2591 if (err) {
2592 set_prompt(NULL);
2593 select_completition(COMPL_COMMAND);
2594 return -1;
2597 format_message(message);
2599 if (message->envelope && message->envelope->dmID)
2600 set_prompt(_("%s %s"), _(type_name), message->envelope->dmID);
2601 else
2602 set_prompt("%s", _(type_name));
2603 select_completition(COMPL_MSG);
2604 return 0;
2608 static void shi_save_message_usage(const char *command) {
2609 printf(_(
2610 "Usage: %s FILE\n"
2611 "Save message into local FILE.\n"),
2612 command);
2616 static const char *raw_type2mime(isds_raw_type raw_type) {
2617 switch (raw_type) {
2618 case RAWTYPE_INCOMING_MESSAGE:
2619 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
2620 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
2621 case RAWTYPE_DELIVERYINFO:
2622 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO:
2623 return "text/xml";
2625 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
2626 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
2627 case RAWTYPE_CMS_SIGNED_DELIVERYINFO:
2628 return "application/pkcs7-mime";
2630 default:
2631 return NULL;
2636 static int shi_save_message(int argc, const char **argv) {
2637 _Bool overwrite = cfg_getbool(configuration, CONFIG_OVERWRITEFILES);
2639 if (!argv || !argv[1] || !*argv[1]) {
2640 shi_save_message_usage(argv[0]);
2641 return -1;
2644 if (!message) {
2645 printf(_("No message loaded\n"));
2646 return -1;
2648 if (!message->raw || message->raw_length == 0) {
2649 printf(_("Loaded message is missing raw representation\n"));
2650 return -1;
2653 return save_data_to_file(argv[1], message->raw, message->raw_length,
2654 raw_type2mime(message->raw_type), overwrite);
2658 /* Return document of current message identified by ordinal number expressed
2659 * as string. In case of error return NULL. */
2660 static const struct isds_document *locate_document_by_ordinal_string(
2661 const char *number) {
2662 const struct isds_list *item;
2663 const struct isds_document *document = NULL;
2664 int ordinar, i;
2666 if (!number) return NULL;
2668 ordinar = atoi(number);
2669 if (ordinar <= 0) {
2670 printf(_("%s: Document number must be positive number\n"), number);
2671 return NULL;
2674 if (!message) {
2675 printf(_("No message loaded\n"));
2676 return NULL;
2679 /* Find document */
2680 for (item = message->documents, i = 0; item; item = item->next) {
2681 if (!item->data) continue;
2682 if (++i == ordinar) {
2683 document = (const struct isds_document *) item->data;
2684 break;
2687 if (i != ordinar) {
2688 printf(_("Message does not contain document #%d\n"), ordinar);
2689 return NULL;
2692 return document;
2696 static void shi_save_document_usage(const char *command) {
2697 printf(_(
2698 "Usage: %s NUMBER [DESTINATION]\n"
2699 "Save document having ordinal NUMBER within current message into local file.\n"
2700 "If DESTINATION is file (or does not exist yet), document will be saved into\n"
2701 "this file.\n"
2702 "If DESTINATION is existing directory, file name equaled to document name\n"
2703 "will be saved into DESTINATION.\n"
2704 "If DESTINATION is missing, document name will be used as file name and\n"
2705 "saved into working directory.\n"
2706 "Be aware that document name does not embed malicious characters (slashes).\n"
2707 "\n"
2708 "If the document is a binary stream, image of the document will be copied\n"
2709 "into a file. If the document is a XML document, the XML tree will be serialized\n"
2710 "into a file. If XML document stands for one element or one text node, the node\n"
2711 "(and its children recursively) will be serialized. If XML document compounds\n"
2712 "more nodes or a comment or a processing instruction, parent node from ISDS name\n"
2713 "space will be used to ensure output serialized XML well-formness.\n"
2715 command);
2719 static int shi_save_document(int argc, const char **argv) {
2720 const struct isds_document *document;
2721 const char *dirname = NULL;
2722 char *filename = NULL, *path = NULL;
2723 int retval = 0;
2724 _Bool overwrite = cfg_getbool(configuration, CONFIG_OVERWRITEFILES);
2726 if (!argv || !argv[1] || !*argv[1] || argc > 3) {
2727 shi_save_document_usage(argv[0]);
2728 return -1;
2731 document = locate_document_by_ordinal_string(argv[1]);
2732 if (!document) return -1;
2734 /* Select directory and file name */
2735 if (argv[2] && *argv[2]) {
2736 if (!is_directory(argv[2])) {
2737 dirname = argv[2];
2738 } else {
2739 filename = strdup(argv[2]);
2740 if (!filename) {
2741 printf(_("Not enough memory\n"));
2742 return -1;
2746 if (!filename && document->dmFileDescr && &document->dmFileDescr) {
2747 filename = utf82locale(document->dmFileDescr);
2748 if (!filename) {
2749 printf(_("Not enough memory\n"));
2750 return -1;
2753 if (!filename) {
2754 printf(_("File name neither supplied, nor document name exists\n"
2755 "Please, supply one.\n"));
2756 return -1;
2759 /* Build path */
2760 if (dirname) {
2761 path = astrcat3(dirname, "/", filename);
2762 zfree(filename);
2763 } else {
2764 path = filename;
2765 filename = NULL;
2767 if (!path) {
2768 printf(_("Not enough memory\n"));
2769 return -1;
2772 /* Save document */
2773 if (document->is_xml)
2774 retval = save_xml_to_file(path, document->xml_node_list,
2775 document->dmMimeType, overwrite);
2776 else
2777 retval = save_data_to_file(path, document->data, document->data_length,
2778 document->dmMimeType, overwrite);
2779 free(path);
2780 return retval;
2784 static void shi_save_stamp_usage(const char *command) {
2785 printf(_(
2786 "Usage: %s FILE\n"
2787 "Save message time stamp into local FILE.\n"),
2788 command);
2792 static int shi_save_stamp(int argc, const char **argv) {
2793 _Bool overwrite = cfg_getbool(configuration, CONFIG_OVERWRITEFILES);
2795 if (!argv || !argv[1] || !*argv[1]) {
2796 shi_save_message_usage(argv[0]);
2797 return -1;
2800 if (!message) {
2801 printf(_("No message loaded\n"));
2802 return -1;
2804 if (!message->envelope || !message->envelope->timestamp||
2805 message->envelope->timestamp_length == 0) {
2806 printf(_("Loaded message is missing time stamp\n"));
2807 return -1;
2810 return save_data_to_file(argv[1],
2811 message->envelope->timestamp, message->envelope->timestamp_length,
2812 "application/timestamp-reply", overwrite);
2816 static int shi_show_list(int argc, const char **argv) {
2817 if (!messages) {
2818 printf(_("No message list loaded\n"));
2819 return -1;
2822 printf((messages_are_outgoing) ?
2823 ngettext("You have %'lu outgoing message\n",
2824 "You have %'lu outgoing messages\n", total_messages) :
2825 ngettext("You have %'lu incoming message\n",
2826 "You have %'lu incoming messages\n", total_messages),
2827 total_messages);
2828 print_message_list(messages, messages_are_outgoing);
2829 return 0;
2833 static int shi_list_incoming(int argc, const char **argv) {
2834 isds_error err;
2836 printf(_("Listing incoming messages...\n"));
2837 err = isds_get_list_of_received_messages(cisds, NULL, NULL, NULL,
2838 MESSAGESTATE_ANY, 0, &total_messages, &messages);
2839 finish_isds_operation(cisds, err);
2840 if (err) {
2841 set_prompt(NULL);
2842 select_completition(COMPL_COMMAND);
2843 return -1;
2845 messages_are_outgoing = 0;
2847 shi_show_list(0, NULL);
2849 set_prompt(_("%s %'lu"), argv[0], total_messages);
2850 select_completition(COMPL_LIST);
2851 return 0;
2855 static int shi_list_outgoing(int argc, const char **argv) {
2856 isds_error err;
2858 printf(_("Listing outgoing messages...\n"));
2859 err = isds_get_list_of_sent_messages(cisds, NULL, NULL, NULL,
2860 MESSAGESTATE_ANY, 0, &total_messages, &messages);
2861 finish_isds_operation(cisds, err);
2862 if (err) {
2863 set_prompt(NULL);
2864 select_completition(COMPL_COMMAND);
2865 return -1;
2867 messages_are_outgoing = 1;
2869 shi_show_list(0, NULL);
2871 set_prompt(_("%s %'lu"), argv[0], total_messages);
2872 select_completition(COMPL_LIST);
2873 return 0;
2877 /* Submit document for conversion and print assigned identifier */
2878 static int do_convert(const struct isds_document *document) {
2879 isds_error err;
2880 char *id = NULL;
2881 struct tm *date = NULL;
2883 if (!document) return -1;
2885 printf(_("Submitting document for authorized conversion...\n"));
2887 err = czp_convert_document(czechpoint, document, &id, &date);
2888 finish_isds_operation(czechpoint, err);
2890 if (!err) {
2891 char *name_locale = utf82locale(document->dmFileDescr);
2892 char *date_string = tm2string(date);
2893 char *id_locale = utf82locale(id);
2894 printf(_(
2895 "Document submitted for authorized conversion successfully under name\n"
2896 "`%s' on %s.\n"
2897 "Submit identifier assigned by Czech POINT deposit is `%s'.\n"),
2898 name_locale, date_string, id_locale);
2899 free(name_locale);
2900 free(date_string);
2901 free(id_locale);
2902 printf(_("Be ware that submitted document has restricted lifetime "
2903 "(30 days).\n"));
2904 printf(_("See <%s> for more details.\n"), CZPDEPOSIT_URL);
2907 free(id); free(date);
2908 return (err) ? -1 : 0;
2912 static void shi_convert_file_usage(const char *command) {
2913 printf(_(
2914 "Usage: %s FILE [NAME]\n"
2915 "Submit local FILE to authorized conversion under NAME. If NAME is missing,\n"
2916 "it will use FILE name.\n"), command);
2917 printf(_(
2918 "\n"
2919 "If Czech POINT deposit accepts document, it will return document identifier\n"
2920 "that user is supposed to provide to officer at Czech POINT contact place.\n"
2921 "Currently only PDF 1.3 and higher version files are accepted.\n"));
2922 printf(_("See <%s> for more details.\n"), CZPDEPOSIT_URL);
2926 static int shi_convert_file(int argc, const char **argv) {
2927 int fd;
2928 struct isds_document document;
2929 int retval = 0;
2931 if (!argv || argc > 3 || !argv[1] || !*argv[1]) {
2932 shi_convert_file_usage((argv)?argv[0]:NULL);
2933 return -1;
2936 memset(&document, 0, sizeof(document));
2938 if (argc == 3 && argv[2] && *argv[2])
2939 document.dmFileDescr = locale2utf8(argv[2]);
2940 else
2941 document.dmFileDescr = locale2utf8(argv[1]);
2942 if (!document.dmFileDescr) {
2943 printf(_("Could not convert document name to UTF-8\n"));
2944 return -1;
2947 printf(_("Loading document from file `%s'...\n"), argv[1]);
2948 if (mmap_file(argv[1], &fd, &document.data, &document.data_length)) {
2949 free(document.dmFileDescr);
2950 return -1;
2953 retval = do_convert(&document);
2955 munmap_file(fd, document.data, document.data_length);
2956 free(document.dmFileDescr);
2957 return retval;
2961 static void shi_convert_document_usage(const char *command) {
2962 printf(_(
2963 "Usage: %s NUMBER\n"
2964 "Submit message document with ordinal NUMBER to authorized conversion.\n"),
2965 command);
2966 printf(_(
2967 "\n"
2968 "If Czech POINT deposit accepts document, it will return document identifier\n"
2969 "that user is supposed to provide to officer at Czech POINT contact place.\n"
2970 "Currently only PDF 1.3 and higher version files are accepted.\n"));
2971 printf(_("See <%s> for more details.\n"), CZPDEPOSIT_URL);
2975 static int shi_convert_document(int argc, const char **argv) {
2976 const struct isds_document *document;
2978 if (!argv || !argv[1] || !*argv[1]) {
2979 shi_convert_document_usage((argv)?argv[0]:NULL);
2980 return -1;
2983 document = locate_document_by_ordinal_string(argv[1]);
2984 if (!document) return -1;
2986 return do_convert(document);
2990 #if ENABLE_DEBUG
2991 static void shi_print_usage(const char *command) {
2992 printf(_(
2993 "Usage: %s STRING LENGTH\n"
2994 "Prints STRING into LENGTH columns. Negative LENGTH means not to cut\n"
2995 "overflowing string.\n"
2996 "This should be locale and terminal agnostic.\n"),
2997 command);
3001 static int shi_print(int argc, const char **argv) {
3002 long int width;
3004 if (!argv || !argv[1] || !argv[2]) {
3005 shi_print_usage((argv)?argv[0]:NULL);
3006 return -1;
3009 width = strtol(argv[2], NULL, 10);
3010 if (width < INT_MIN) {
3011 fprintf(stderr,
3012 _("Length argument must not lesser than %d.\n"), INT_MIN);
3013 return -1;
3015 if (width > INT_MAX) {
3016 fprintf(stderr,
3017 _("Length argument must not be greater than %d.\n"), INT_MAX);
3018 return -1;
3021 printf(_(">"));
3022 fnprint(stdout, argv[1], width);
3023 printf(_("<\n"));
3025 return 0;
3029 static int shi_tokenize(int argc, const char **argv) {
3031 if (!argv) return 0;
3033 for (int i = 0; i < argc; i++) {
3034 printf(_(">%s<\n"), argv[i]);
3036 return 0;
3040 static int shi_quote(int argc, const char **argv) {
3041 char *escaped, *unescaped;
3043 if (!argv) return 0;
3045 printf(_("Original\tQuoted\tDequoted\n"));
3046 for (int i = 0; i < argc; i++) {
3047 escaped = shi_quote_filename((char *) argv[i], 0, NULL);
3048 unescaped = shi_dequote_filename((char *) argv[i], 0);
3049 printf(_(">%s<\t>%s<\t>%s<\n"), argv[i], escaped, unescaped);
3050 free(escaped);
3051 free(unescaped);
3053 return 0;
3055 #endif
3058 /* Interactive loop */
3059 void shi_loop(void) {
3060 char *command_line = NULL;
3061 char **command_argv = NULL;
3062 int command_argc;
3063 struct command *command = NULL;
3065 printf(_("Use `help' command to get list of available commands.\n"));
3067 select_completition(COMPL_COMMAND);
3068 set_prompt(NULL);
3070 while (1) {
3071 command_line = readline((prompt) ? prompt : _("shigofumi> "));
3072 /* Remember not parsable commands too to user be able to get back to
3073 * fix command */
3074 if (command_line && *command_line) {
3075 /* TODO: Omit blank lines */
3076 add_history(command_line);
3079 command_argv = tokenize(command_line, &command_argc);
3081 if (command_argv && command_argv[0]) {
3082 command = find_command(command_argv[0]);
3084 if (!command) {
3085 fprintf(stderr, _("Command not understood\n"));
3086 } else {
3087 command->function(command_argc, (const char **) command_argv);
3091 argv_free(command_argv);
3092 zfree(command_line);
3097 /* Non-interactive mode. Commands from @lines are processed until any command
3098 * lines remains or no error occurred. First failure terminates processing.
3099 * @lines is sequence of commands separated by '\n' or '\r'. The content is
3100 * modified during this call.
3101 * @return 0 if all command succeed, otherwise non-zero value
3103 int shi_batch(char *lines) {
3104 char *command_line;
3105 char **command_argv = NULL;
3106 int command_argc;
3107 struct command *command = NULL;
3108 int retval = 0;
3110 printf(_("Batch mode started.\n"));
3111 batch_mode = 1;
3112 select_completition(COMPL_COMMAND);
3114 while (!retval && (command_line = strtok(lines, "\n\r"))) {
3115 lines = NULL; /* strtok(3) requires it for subsequent calls */
3117 printf(_("Processing command: %s\n"), command_line);
3119 command_argv = tokenize(command_line, &command_argc);
3121 if (command_argv && command_argv[0]) {
3122 command = find_command(command_argv[0]);
3124 if (!command) {
3125 fprintf(stderr, _("Command not understood\n"));
3126 retval = -1;
3127 } else {
3128 retval = command->function(command_argc,
3129 (const char **) command_argv);
3133 argv_free(command_argv);
3136 if (retval)
3137 fprintf(stderr, _("Command failed!\n"));
3138 return retval;
3142 #define COMMON_COMMANDS \
3143 { "accept", shi_accept_message, N_("accept commercial message"), \
3144 shi_accept_message_usage, ARGTYPE_MSGID }, \
3145 { "box", shi_box, N_("show current box details"), NULL, \
3146 ARGTYPE_NONE }, \
3147 { "cache", shi_cache, N_("show cache details"), NULL, \
3148 ARGTYPE_NONE }, \
3149 { "cd", shi_chdir, N_("change working directory"), shi_chdir_usage, \
3150 ARGTYPE_FILE }, \
3151 { "commercial", shi_commercial, \
3152 N_("manipulate commercial receiving box status"), \
3153 shi_commercial_usage, ARGTYPE_BOXID }, \
3154 { "compose", shi_compose, N_("compose a message"), shi_compose_usage, \
3155 ARGTYPE_FILE }, \
3156 { "convert", shi_convert_file, \
3157 N_("submit local document for authorized conversion"), \
3158 shi_convert_file_usage, ARGTYPE_FILE }, \
3159 { "copying", shi_copying, N_("show this program licence excerpt"), NULL, \
3160 ARGTYPE_NONE }, \
3161 { "debug", shi_debug, N_("set debugging"), shi_debug_usage, \
3162 ARGTYPE_FILE }, \
3163 { "delivery", shi_delivery, N_("get message delivery details"), \
3164 shi_delivery_usage, ARGTYPE_MSGID }, \
3165 { "findbox", shi_find_box, N_("search for a box"), shi_find_box_usage, \
3166 ARGTYPE_BOXID }, \
3167 { "hash", shi_hash, N_("query ISDS for message hash"), \
3168 shi_hash_usage, ARGTYPE_MSGID }, \
3169 { "help", shi_help, N_("describe commands"), shi_help_usage, \
3170 ARGTYPE_COMMAND }, \
3171 { "load", shi_load_anything, \
3172 N_("load message or message delivery details from local file"), \
3173 shi_load_anything_usage, ARGTYPE_FILE }, \
3174 { "login", shi_login, N_("log into ISDS"), NULL, ARGTYPE_NONE }, \
3175 { "lsi", shi_list_incoming, N_("list received messages"), NULL, \
3176 ARGTYPE_NONE }, \
3177 { "lso", shi_list_outgoing, N_("list sent messages"), NULL, \
3178 ARGTYPE_NONE }, \
3179 { "msgi", shi_incoming_message, N_("get incoming message"), \
3180 shi_incoming_message_usage, ARGTYPE_MSGID }, \
3181 { "msgo", shi_outgoing_message, N_("get outgoing message"), \
3182 shi_outgoing_message_usage, ARGTYPE_MSGID }, \
3183 { "passwd", shi_passwd, N_("manipulate user password"), shi_passwd_usage, \
3184 ARGTYPE_NONE }, \
3185 { "pwd", shi_pwd, N_("print working directory"), NULL, ARGTYPE_NONE }, \
3186 { "quit", shi_quit, N_("exit shigofumi"), NULL, ARGTYPE_NONE }, \
3187 { "read", shi_read_message, N_("mark message as read"), \
3188 shi_read_message_usage, ARGTYPE_MSGID }, \
3189 { "set", shi_settings, N_("show settings"), NULL, ARGTYPE_NONE }, \
3190 { "statbox", shi_stat_box, N_("get status of a box"), shi_stat_box_usage, \
3191 ARGTYPE_BOXID }, \
3192 { "user", shi_user, N_("show current user details"), NULL, \
3193 ARGTYPE_NONE }, \
3194 { "users", shi_users, N_("show box users"), shi_users_usage, \
3195 ARGTYPE_NONE }, \
3196 { "version", shi_version, N_("show version of this program"), NULL, \
3197 ARGTYPE_NONE}, \
3198 { NULL, NULL, NULL, NULL, ARGTYPE_NONE }
3200 struct command base_commands[] = {
3201 #if ENABLE_DEBUG
3202 { "quote", shi_quote, N_("demonstrate argument escaping"), NULL,
3203 ARGTYPE_FILE },
3204 { "print", shi_print, N_("print string into given width"),
3205 shi_print_usage, ARGTYPE_NONE },
3206 { "tokenize", shi_tokenize, N_("demonstrate arguments tokenization"), NULL,
3207 ARGTYPE_FILE },
3208 #endif
3209 COMMON_COMMANDS
3212 struct command message_commands[] = {
3213 { "authenticate", shi_authenticate, N_("check message authenticity"),
3214 NULL, ARGTYPE_NONE },
3215 { "convertdoc", shi_convert_document,
3216 N_("submit document of current message for authorized conversion"),
3217 shi_convert_document_usage, ARGTYPE_DOCID },
3218 { "dump", shi_dump_message, N_("dump current message structure"),
3219 NULL, ARGTYPE_NONE },
3220 { "savestamp", shi_save_stamp,
3221 N_("save time stamp of current message into local file"),
3222 shi_save_stamp_usage, ARGTYPE_FILE },
3223 { "savedoc", shi_save_document,
3224 N_("save document of current message into local file"),
3225 shi_save_document_usage, ARGTYPE_FILE },
3226 { "save", shi_save_message, N_("save current message into local file"),
3227 shi_save_message_usage, ARGTYPE_FILE },
3228 { "show", shi_show_message, N_("show current message"), NULL,
3229 ARGTYPE_NONE },
3230 { "verify", shi_verify, N_("verify current message hash"), NULL,
3231 ARGTYPE_NONE },
3232 COMMON_COMMANDS
3235 struct command list_commands[] = {
3236 { "show", shi_show_list, N_("show current message list"), NULL,
3237 ARGTYPE_NONE },
3238 COMMON_COMMANDS
3241 #undef COMMON_COMMANDS
3244 static void main_version(void) {
3245 isds_init();
3246 show_version();
3247 isds_cleanup();
3248 printf("\n");
3249 shi_copying(0, NULL);
3253 static void main_usage(const char *command) {
3254 printf(_(
3255 "Usage: %s [OPTION...]\n"
3256 "Access ISDS, Process local data box messages or delivery details, submit\n"
3257 "document to authorized conversion.\n"
3258 "\n"
3259 "Options:\n"
3260 " -c FILE use the FILE as configuration file instead of ~/%s\n"
3261 " -e COMMANDS execute COMMANDS (new line separated) and exit\n"
3262 " -V show version info and exit\n"
3264 (command) ? command : "shigofumi",
3265 CONFIG_FILE);
3269 int main(int argc, char **argv) {
3270 int opt;
3271 char *config_file = NULL;
3272 char *batch_commands = NULL;
3273 int retval = EXIT_SUCCESS;
3275 setlocale(LC_ALL, "");
3276 #if ENABLE_NLS
3277 /* Initialize gettext */
3278 bindtextdomain(PACKAGE, LOCALEDIR);
3279 textdomain(PACKAGE);
3280 #endif
3282 /* Parse arguments */
3283 optind = 0;
3284 while ((opt = getopt(argc, (char * const *)argv, "c:e:V")) != -1) {
3285 switch (opt) {
3286 case 'c':
3287 config_file = optarg;
3288 break;
3289 case 'e':
3290 batch_commands = optarg;
3291 break;
3292 case 'V':
3293 main_version();
3294 shi_exit(EXIT_SUCCESS);
3295 break;
3296 default:
3297 main_usage((argv[0]) ? basename(argv[0]): NULL);
3298 shi_exit(EXIT_FAILURE);
3303 if (shi_init(config_file)) {
3304 shi_exit(EXIT_FAILURE);
3307 /*shi_login(NULL);*/
3309 if (batch_commands) {
3310 if (shi_batch(batch_commands))
3311 retval = EXIT_FAILURE;
3312 } else {
3313 shi_loop();
3316 shi_exit(retval);