Make execute_system_command() generic
[shigofumi.git] / src / shigofumi.c
blob406ea33bdd686eb77d46aec1ae4d1c44e5aa7168
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>
16 #include <sys/types.h>
17 #include <sys/wait.h>
19 #include "shigofumi.h"
20 #include "completition.h"
21 #include "data.h"
22 #include "io.h"
23 #include "ui.h"
24 #include "utils.h"
26 #define CONFIG_FILE ".shigofumirc"
27 #define CONFIG_SERVER "base_url"
28 #define CONFIG_USERNAME "username"
29 #define CONFIG_PASSWORD "password"
30 #define CONFIG_DEBUGLEVEL "debug_level"
31 #define CONFIG_VERIFYSERVER "verify_server"
32 #define CONFIG_CAFILE "ca_file"
33 #define CONFIG_CADIRECTORY "ca_directory"
34 #define CONFIG_CRLFILE "crl_file"
35 #define CONFIG_TIMEOUT "timeout"
36 #define CONFIG_LOGFACILITIES "log_facilities"
37 #define CONFIG_LOGFILE "log_file"
38 #define CONFIG_LOGLEVEL "log_level"
39 #define CONFIG_MARKMESSAGEREAD "mark_message_read"
40 #define CONFIG_NORMALIZEMIMETYPE "normalize_mime_type"
41 #define CONFIG_OVERWRITEFILES "overwrite_files"
42 #define CZPDEPOSIT_URL "https://www.czechpoint.cz/uschovna/"
44 #define TIMEOUT 10000
45 #define LOG_LEVEL 20
47 /* Configuration */
48 cfg_opt_t configuration_syntax[] = {
49 CFG_STR(CONFIG_SERVER, (char *)isds_locator, CFGF_NONE),
50 CFG_STR(CONFIG_USERNAME, NULL, CFGF_NODEFAULT),
51 CFG_STR(CONFIG_PASSWORD, NULL, CFGF_NODEFAULT),
52 /*CFG_STR(CONFIG_DEBUGLEVEL, NULL, CFGF_NODEFAULT),*/
53 CFG_BOOL(CONFIG_VERIFYSERVER, cfg_true, CFGF_NONE),
54 CFG_STR(CONFIG_CAFILE, NULL, CFGF_NODEFAULT),
55 CFG_STR(CONFIG_CADIRECTORY, NULL, CFGF_NODEFAULT),
56 CFG_STR(CONFIG_CRLFILE, NULL, CFGF_NODEFAULT),
57 CFG_INT(CONFIG_TIMEOUT, TIMEOUT, CFGF_NONE),
58 CFG_STR_LIST(CONFIG_LOGFACILITIES, "{none}", CFGF_NONE),
59 CFG_STR(CONFIG_LOGFILE, NULL, CFGF_NODEFAULT),
60 CFG_INT(CONFIG_LOGLEVEL, LOG_LEVEL, CFGF_NONE),
61 CFG_BOOL(CONFIG_MARKMESSAGEREAD, cfg_false, CFGF_NONE),
62 CFG_BOOL(CONFIG_NORMALIZEMIMETYPE, cfg_true, CFGF_NONE),
63 CFG_BOOL(CONFIG_OVERWRITEFILES, cfg_true, CFGF_NONE),
64 CFG_END()
66 cfg_t *configuration;
68 /* Logger */
69 int logger_fd = -1;
71 /* UI */
72 _Bool batch_mode = 0;
73 char *prompt = NULL;
74 struct command (*commands)[] = NULL;
75 FILE *output = NULL;
77 /* Data */
78 struct isds_ctx *cisds = NULL;
79 char *server = NULL;
80 char *username = NULL;
81 char *password = NULL;
82 struct isds_list *boxes = NULL;
83 struct isds_message *message = NULL;
84 _Bool messages_are_outgoing = 0;
85 struct isds_list *messages = NULL;
86 unsigned long int total_messages = 0;
87 struct isds_ctx *czechpoint = NULL;
89 static void discard_credentials(void) {
90 zfree(server);
91 zfree(username);
92 zfree(password);
96 /* Do the cleanup and exit */
97 static void shi_exit(int exit_code) {
98 /* Data */
99 discard_credentials();
100 isds_list_free(&boxes);
101 isds_message_free(&message);
102 isds_list_free(&messages);
104 if (cisds) {
105 oprintf(_("Logging out...\n"));
106 isds_logout(cisds);
107 isds_ctx_free(&cisds);
109 isds_ctx_free(&czechpoint);
110 isds_cleanup();
112 /* Configuration */
113 cfg_free(configuration);
115 /* UI */
116 free(prompt);
117 free(commands);
119 exit(exit_code);
122 /* Set prompt. if @format is NULL, switch to default prompt */
123 static void set_prompt(const char *format, ...) {
124 char *buffer = NULL;
125 va_list ap;
126 if (format) {
127 va_start(ap, format);
128 shi_vasprintf(&buffer, format, ap);
129 va_end(ap);
131 if (buffer) {
132 shi_asprintf(&prompt, _("%s> "), buffer);
133 if (prompt) {
134 free(buffer);
135 return;
139 free(buffer);
140 free(prompt);
141 prompt = strdup(_("> "));
142 return;
145 zfree(prompt);
149 static int shi_load_configuration(const char *config_file) {
150 char *config_name = NULL;
151 int ret;
153 /* Get config file */
154 if (config_file) {
155 config_name = (char *) config_file;
156 } else {
157 if (-1 == shi_asprintf(&config_name, "%s/%s", getenv("HOME"),
158 CONFIG_FILE)) {
159 fprintf(stderr, _("Could not build configuration file name\n"));
160 return -1;
164 /* Parse configuration */
165 configuration = cfg_init(configuration_syntax, CFGF_NONE);
166 ret = cfg_parse(configuration, config_name);
167 if (ret) {
168 if (ret == CFG_FILE_ERROR) {
169 fprintf(stderr,
170 _("Error while opening configuration file `%s': %s\n"),
171 config_name, strerror(errno));
172 } else {
173 fprintf(stderr, _("Error while parsing configuration file `%s'\n"),
174 config_name);
176 oprintf(_("Using default configuration\n"));
179 if (config_name != config_file) free(config_name);
180 return 0;
184 static void finish_isds_operation(struct isds_ctx *ctx, isds_error err) {
185 shi_progressbar_finish();
186 if (err) {
187 if (isds_long_message(ctx))
188 fprintf(stderr, _("Error occurred: %s: %s\n"), isds_strerror(err),
189 isds_long_message(ctx));
190 else
191 fprintf(stderr, _("Error occurred: %s\n"), isds_strerror(err));
196 void logger(isds_log_facility facility, isds_log_level level,
197 const char *message, int length, void *data) {
198 int fd;
199 ssize_t written, left = length;
201 if (!data) return;
202 fd = *((int *) data);
203 /*printf("\033[32mLOG(%02d,%02d): ", facility, level);
204 printf("%.*s", length, message);
205 printf("\033[m");*/
207 while (left) {
208 written = write(fd, message + length - left, left);
209 if (written == -1) {
210 fprintf(stderr,
211 _("Could not save log message into log file: %s\n"
212 "Log message discarded!\n"),
213 strerror(errno));
214 /*close(fd);
215 fd = -1;*/
216 return;
218 left-=written;
223 /* Redirect ISDS log to file if @file is not NULL. */
224 static int do_log_to_file(const char *file) {
225 if (file && *file) {
226 logger_fd = open_file_for_writing(file, 0, 1);
227 if (logger_fd == -1) {
228 fprintf(stderr, _("Could not redirect ISDS log to file `%s'\n"),
229 file);
230 return -1;
232 isds_set_log_callback(logger, &logger_fd);
234 return 0;
238 /* Add log facility based on its name. */
239 static int add_log_facility(isds_log_facility *facilities, const char *name) {
240 if (!facilities) return -1;
242 if (!strcmp(name, "none")) *facilities |= ILF_NONE;
243 else if (!strcmp(name, "http")) *facilities |= ILF_HTTP;
244 else if (!strcmp(name, "soap")) *facilities |= ILF_SOAP;
245 else if (!strcmp(name, "isds")) *facilities |= ILF_ISDS;
246 else if (!strcmp(name, "file")) *facilities |= ILF_FILE;
247 else if (!strcmp(name, "sec")) *facilities |= ILF_SEC;
248 else if (!strcmp(name, "xml")) *facilities |= ILF_XML;
249 else if (!strcmp(name, "all")) *facilities |= ILF_ALL;
250 else {
251 fprintf(stderr, _("%s: Unknown log facility\n"), name);
252 return -1;
255 return 0;
259 /* Save log facility into confuse configuration */
260 static void save_log_facility(int level) {
261 cfg_setlist(configuration, CONFIG_LOGFACILITIES, 0);
263 if (level == ILF_ALL) {
264 cfg_addlist(configuration, CONFIG_LOGFACILITIES, 1, "all");
265 return;
267 if (level == ILF_NONE) {
268 cfg_addlist(configuration, CONFIG_LOGFACILITIES, 1, "none");
269 return;
271 if (level & ILF_HTTP)
272 cfg_addlist(configuration, CONFIG_LOGFACILITIES, 1, "http");
273 if (level & ILF_SOAP)
274 cfg_addlist(configuration, CONFIG_LOGFACILITIES, 1, "soap");
275 if (level & ILF_ISDS)
276 cfg_addlist(configuration, CONFIG_LOGFACILITIES, 1, "isds");
277 if (level & ILF_FILE)
278 cfg_addlist(configuration, CONFIG_LOGFACILITIES, 1, "file");
279 if (level & ILF_SEC)
280 cfg_addlist(configuration, CONFIG_LOGFACILITIES, 1, "sec");
281 if (level & ILF_XML)
282 cfg_addlist(configuration, CONFIG_LOGFACILITIES, 1, "xml");
286 /* Clamp long int to unsigned int */
287 static unsigned int normalize_timeout(long int raw) {
288 if (raw < 0) {
289 oprintf(_("Configured network timeout is less then 0. "
290 "Clamped to 0.\n"));
291 return 0;
293 if (raw > UINT_MAX ) {
294 oprintf(_("Configured network timeout is greater then %1$u. "
295 "Clamped to %1$u.\n"), UINT_MAX);
296 return UINT_MAX;
298 return (unsigned int) raw;
302 /* Clamp long int to <0;100> */
303 static unsigned int normalize_log_level(long int raw) {
304 if (raw < 0) {
305 oprintf(_("Configured log level is less then 0. Clamped to 0.\n"));
306 return 0;
308 if (raw > ILL_ALL) {
309 oprintf(_("Configured log level is greater then %1$u. "
310 "Clamped to %1$u.\n"), ILL_ALL);
311 return ILL_ALL;
313 if (raw > UINT_MAX ) {
314 oprintf(_("Configured log level is greater then %1$u. "
315 "Clamped to %1$u.\n"), UINT_MAX);
316 return UINT_MAX;
318 return (unsigned int) raw;
322 static int shi_init(const char *config_file) {
323 isds_error err;
324 char *value;
325 unsigned int timeout, log_level;
326 isds_log_facility log_facility = ILF_NONE;
328 oprintf(_("This is Shigofumi, an ISDS client. "
329 "Have a nice e-government.\n"));
331 /* Do not permute arguments in getopt() */
332 if (setenv("POSIXLY_CORRECT", "", 1)) {
333 fprintf(stderr,
334 _("Could not set POSIXLY_CORRECT environment variable\n"));
335 return -1;
338 /* Load configuration */
339 if (shi_load_configuration(config_file))
340 return -1;
341 timeout = normalize_timeout(cfg_getint(configuration, CONFIG_TIMEOUT));
342 log_level = normalize_log_level(cfg_getint(configuration, CONFIG_LOGLEVEL));
344 /* Init readline */
345 rl_readline_name = "shigofumi";
346 rl_filename_quote_characters = "\\ >";
347 rl_filename_quoting_function = shi_quote_filename;
348 rl_filename_dequoting_function = shi_dequote_filename;
349 rl_char_is_quoted_p = shi_char_is_quoted;
351 /* Initialize ISDS */
352 err = isds_init();
353 if (err) {
354 fprintf(stderr, _("Could not initialize libisds library: %s\n"),
355 isds_strerror(err));
356 return -1;
359 /* Set ISDS logging */
360 value = cfg_getstr(configuration, CONFIG_LOGFILE);
361 if (do_log_to_file(value))
362 return -1;
363 for (int i = 0; i < cfg_size(configuration, CONFIG_LOGFACILITIES); i++) {
364 if (add_log_facility(&log_facility,
365 cfg_getnstr(configuration, CONFIG_LOGFACILITIES, i)))
366 return -1;
369 isds_set_logging(log_facility, log_level);
371 /* Set ISDS context up */
372 cisds = isds_ctx_create();
373 if (!cisds) {
374 fprintf(stderr, _("Could not create ISDS context\n"));
375 return -1;
377 err = isds_set_timeout(cisds, timeout);
378 if (err) {
379 fprintf(stderr, _("Could not set ISDS network timeout: %s\n"),
380 isds_strerror(err));
382 err = isds_set_progress_callback(cisds, shi_progressbar, NULL);
383 if (err) {
384 fprintf(stderr, _("Could not register network progress bar: %s: %s\n"),
385 isds_strerror(err), isds_long_message(cisds));
387 err = isds_set_opt(cisds, IOPT_NORMALIZE_MIME_TYPE,
388 cfg_getbool(configuration, CONFIG_NORMALIZEMIMETYPE));
389 if (err) {
390 fprintf(stderr,
391 cfg_getbool(configuration, CONFIG_NORMALIZEMIMETYPE) ?
392 _("Could not enable MIME type normalization: %s: %s\n") :
393 _("Could not disable MIME type normalization: %s: %s\n"),
394 isds_strerror(err), isds_long_message(cisds));
396 if (!cfg_getbool(configuration, CONFIG_VERIFYSERVER)) {
397 oprintf(_("Warning: Shigofumi disabled server identity verification "
398 "on user request!\n"));
399 err = isds_set_opt(cisds, IOPT_TLS_VERIFY_SERVER, 0);
400 if (err) {
401 fprintf(stderr,
402 _("Could not disable server identity verification: "
403 "%s: %s\n"),
404 isds_strerror(err), isds_long_message(cisds));
407 if ((value = cfg_getstr(configuration, CONFIG_CAFILE))) {
408 err = isds_set_opt(cisds, IOPT_TLS_CA_FILE, value);
409 if (err) {
410 fprintf(stderr,
411 _("Could not set file with CA certificates: %s: %s: %s\n"),
412 value, isds_strerror(err), isds_long_message(cisds));
415 if ((value = cfg_getstr(configuration, CONFIG_CADIRECTORY))) {
416 err = isds_set_opt(cisds, IOPT_TLS_CA_DIRECTORY, value);
417 if (err) {
418 fprintf(stderr,
419 _("Could not set directory with CA certificates: "
420 "%s: %s: %s\n"),
421 value, isds_strerror(err), isds_long_message(cisds));
424 if ((value = cfg_getstr(configuration, CONFIG_CRLFILE))) {
425 err = isds_set_opt(cisds, IOPT_TLS_CRL_FILE, value);
426 if (err) {
427 fprintf(stderr, _("Could not set file with CRL: %s: %s: %s\n"),
428 value, isds_strerror(err), isds_long_message(cisds));
433 /* Set Czech POINT context up */
434 czechpoint = isds_ctx_create();
435 if (!czechpoint) {
436 fprintf(stderr, _("Could not create Czech POINT context\n"));
437 return -1;
439 err = isds_set_timeout(czechpoint, timeout);
440 if (err) {
441 fprintf(stderr, _("Could not set Czech POINT network timeout: %s\n"),
442 isds_strerror(err));
444 err = isds_set_progress_callback(czechpoint, shi_progressbar, NULL);
445 if (err) {
446 fprintf(stderr, "Could not register network progress bar: %s: %s\n",
447 isds_strerror(err), isds_long_message(cisds));
450 return 0;
454 static int shi_quit(int argc, const char **argv) {
455 shi_exit(EXIT_SUCCESS);
456 return 0;
461 static void shi_help_usage(const char *command) {
462 oprintf(_(
463 "Usage: %s [COMMAND]\n"
464 "Show COMMAND manual or list of currently available commands.\n"
466 command);
470 static int shi_help(int argc, const char **argv) {
471 size_t command_width = 14;
472 int i;
474 if (!commands) {
475 fprintf(stderr, _("No command is available\n"));
476 return -1;
479 if (argc == 2 && argv[1] && *argv[1]) {
480 /* Show usage for given command */
481 for (i = 0; (*commands)[i].name; i++) {
482 if (!strcmp((*commands)[i].name, argv[1])) {
483 if ((*commands)[i].usage)
484 (*commands)[i].usage((*commands)[i].name);
485 else if ((*commands)[i].description) {
486 ohprint((*commands)[i].name, command_width);
487 oprintf(" %s\n", _((*commands)[i].description));
489 else
490 fprintf(stderr,
491 _("%s: %s: Command description not defined\n"),
492 argv[0], argv[1]);
493 return 0;
496 fprintf(stderr, _("%s: %s: No such command exists\n"), argv[0], argv[1]);
497 return -1;
500 /* Or list all commands */
501 oprintf(_("Following commands are available:\n"));
502 for (i = 0; (*commands)[i].name; i++) {
503 ohprint((*commands)[i].name, command_width);
504 oprintf(" %s\n", _((*commands)[i].description));
507 return 0;
511 static void show_version(void) {
512 char *libisds_version = isds_version();
514 oprintf(_("This is Shigofumi version %s.\n"), PACKAGE_VERSION);
515 oprintf(_("\n"
516 "Used libraries\n"
517 "Readline: %s\n"
518 "libisds: %s\n"
520 rl_library_version, libisds_version);
521 free(libisds_version);
525 static int shi_version(int argc, const char **argv) {
526 show_version();
527 oprintf(_(
528 "\n"
529 "-----\n"
530 "It's a shigofumi. A letter delivered from the afterlife. (Fumika)\n"
531 "A message can not be delivered to dead person. (ISDS specification)\n"
532 "Virtual and real world. They can be compatible. (Program author)\n"
534 return 0;
538 static int shi_copying(int argc, const char **argv) {
539 oprintf(_(
540 "This is Shigofumi, an ISDS client.\n"
541 "Copyright (C) 2010 Petr Pisar\n"
542 "\n"
543 "This program is free software: you can redistribute it and/or modify\n"
544 "it under the terms of the GNU General Public License as published by\n"
545 "the Free Software Foundation, either version 3 of the License, or\n"
546 "(at your option) any later version.\n"
547 "\n"
548 "This program is distributed in the hope that it will be useful,\n"
549 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
550 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
551 "GNU General Public License for more details.\n"
552 "\n"
553 "You should have received a copy of the GNU General Public License\n"
554 "along with this program. If not, see <http://www.gnu.org/licenses/>.\n"
556 return 0;
560 static int shi_cache(int argc, const char **argv) {
561 const struct isds_list *item;
562 size_t i;
564 if (boxes) {
565 for (item = boxes, i = 0; item; item = item->next, i++);
566 oprintf(_(
567 "Cached box list: %zu\n"),
571 if (messages) {
572 oprintf(_(
573 "Cached message list:\n"
574 "\tDirection: %s\n"
575 "\tMessages: %'lu\n"),
576 (messages_are_outgoing) ? _("Outgoing") : _("Incoming"),
577 total_messages);
580 if (message) {
581 oprintf(_("Cached message: %s\n"),
582 (message->envelope && message->envelope->dmID) ?
583 message->envelope->dmID : _("<Unknown ID>"));
586 return 0;
590 static void shi_chdir_usage(const char *command) {
591 oprintf(_(
592 "Usage: %s [DIRECTORY]\n"
593 "Change working directory to DIRECTORY.\n"
594 "If no DIRECTORY is supplied, HOME directory will be used.\n"),
595 command);
599 static int shi_chdir(int argc, const char **argv) {
600 const char *directory = NULL;
602 if (!argv || argc > 2) {
603 shi_chdir_usage((argv) ? argv[0] : NULL);
604 return -1;
607 if (argc == 2 && argv[1] && *argv[1])
608 directory = argv[1];
609 else {
610 directory = getenv("HOME");
611 if (!directory) {
612 oprintf("Environment variable HOME does not exist\n");
613 return -1;
616 if (chdir(directory)) {
617 oprintf(_("Could not change working directory: %s: %s\n"), directory,
618 strerror(errno));
619 return -1;
622 return 0;
626 static int shi_pwd(int argc, const char **argv) {
627 char *buffer = NULL, *newbuffer;
628 size_t length = 0;
630 while (length += 1024) {
631 newbuffer = realloc(buffer, length);
632 if (!newbuffer) {
633 fprintf(stderr, _("Error: Not enough memory\n"));
634 free(buffer);
635 return -1;
637 buffer = newbuffer;
639 if (getcwd(buffer, length)) {
640 oprintf("%s\n", buffer);
641 free(buffer);
642 return 0;
646 fprintf(stderr, _("Error: Current directory string is too long\n"));
647 free(buffer);
648 return -1;
652 /* Log-in to ISDS */
653 static int do_login(void) {
654 isds_error err;
656 if (batch_mode) {
657 oprintf(_("Unattended mode detected. "
658 "Make sure credentials have been preset.\n"));
659 } else {
660 oprintf(_("You are going to insert credentials for your account.\n"
661 "Leave blank line to choose default value.\n"));
663 select_completition(COMPL_NONE);
665 /* Ask for server base URL */
666 shi_replace_string(&server, _("Input ISDS base URL: "),
667 cfg_getstr(configuration, CONFIG_SERVER), batch_mode);
669 /* Ask for user name */
670 shi_replace_string(&username, _("Input ISDS user name: "),
671 cfg_getstr(configuration, CONFIG_USERNAME), batch_mode);
673 /*if (!password) {
674 password = ask_for_password(_("Input ISDS password: "));
676 if (!password) {
677 fprintf(stderr, _("Could not read ISDS password\n"));
678 shi_exit(EXIT_FAILURE);
681 shi_replace_password(&password, _("Input ISDS password: "),
682 cfg_getstr(configuration, CONFIG_PASSWORD), batch_mode);
684 select_completition(COMPL_COMMAND);
685 set_prompt(NULL);
687 printf(_("Logging in...\n"));
688 err = isds_login(cisds, server, username, password, NULL);
689 finish_isds_operation(cisds, err);
690 if (err) {
691 printf(_("Log-in failed\n"));
692 return -1;
695 oprintf(_("Logged in.\n"));
696 return 0;
700 static struct isds_DbOwnerInfo *do_box(void) {
701 isds_error err;
702 struct isds_DbOwnerInfo *box = NULL;
704 printf(_("Getting box details you are logged in...\n"));
705 err = isds_GetOwnerInfoFromLogin(cisds, &box);
706 finish_isds_operation(cisds, err);
708 return box;
712 static int shi_box(int argc, const char **argv) {
713 struct isds_DbOwnerInfo *box = NULL;
715 box = do_box();
716 if (!box) return -1;
718 format_DbOwnerInfo(box);
720 isds_DbOwnerInfo_free(&box);
721 return 0;
725 /* Get info about box with @id.
726 * @id is UTF-8 encoded
727 * Return NULL in case of error, otherwise box description that caller must
728 * free. */
729 static struct isds_DbOwnerInfo *stat_box(const char *id) {
730 isds_error err;
731 struct isds_DbOwnerInfo criteria;
732 struct isds_list *boxes = NULL, *item;
733 struct isds_DbOwnerInfo *box = NULL;
734 char *id_locale = NULL;
736 if (!id || !*id) return NULL;
738 id_locale = utf82locale(id);
739 memset(&criteria, 0, sizeof(criteria));
740 criteria.dbID = (char *) id;
742 printf(_("Getting details about box with ID `%s'...\n"), id_locale);
743 err = isds_FindDataBox(cisds, &criteria, &boxes);
744 finish_isds_operation(cisds, err);
745 if (err) goto leave;
747 for(item = boxes; item; item = item->next) {
748 if (!item->data) continue;
750 if (item->next) {
751 fprintf(stderr, _("Error: More boxes match ID `%s'\n"), id_locale);
752 goto leave;
755 box = (struct isds_DbOwnerInfo *) item->data;
756 item->data = NULL;
757 break;
760 leave:
761 free(id_locale);
762 isds_list_free(&boxes);
764 return box;
768 static void shi_commercial_usage(const char *command) {
769 oprintf(_(
770 "Usage: %s [-0|-1] [BOX_ID]\n"
771 "Manipulate commercial receiving box status.\n"
772 " -O switch off receiving of commercial messages\n"
773 " -1 switch on receiving of commercial messages\n"
774 " BOX_ID affects box with ID BOX_ID; default is box you are logged in\n"
775 "If no option is given, show current commercial receiving status.\n"),
776 command);
780 /* Manipulate commercial receiving box status */
781 static int shi_commercial(int argc, const char **argv) {
782 isds_error err;
783 struct isds_DbOwnerInfo *box = NULL;
784 int opt;
785 int action = -1;
786 char *box_id = NULL, *box_id_locale = NULL;
787 _Bool static_box_id;
788 int retval = 0;
790 optind = 0;
791 while ((opt = getopt(argc, (char * const *)argv, "01")) != -1) {
792 switch (opt) {
793 case '0':
794 action = 0;
795 break;
796 case '1':
797 action = 1;
798 break;
799 default:
800 shi_commercial_usage((argv)?argv[0]:NULL);
801 return -1;
804 if (optind + 1 < argc) {
805 fprintf(stderr, _("Bad invocation\n"));
806 shi_commercial_usage((argv)?argv[0]:NULL);
807 return -1;
810 if (!argv[optind] || !*argv[optind]) {
811 /* Get current box ID */
812 box = do_box();
813 if (!box || !box->dbID || !*box->dbID) {
814 isds_DbOwnerInfo_free(&box);
815 fprintf(stderr, _("Could not get current box ID\n"));
816 return -1;
818 box_id = box->dbID; static_box_id = 1;
819 box_id_locale = utf82locale(box_id);
820 } else {
821 /* Box ID supplied as argument */
822 box_id_locale = (char *) argv[optind];
823 box_id = locale2utf8(box_id_locale); static_box_id = 0;
824 if (!box_id) {
825 fprintf(stderr, _("Could not convert box ID `%s' to UTF-8\n"),
826 box_id_locale);
827 return -1;
831 if (action == -1) {
832 if (!box) box = stat_box(box_id);
833 if (!box) {
834 fprintf(stderr, _("Could not get details about box ID `%s'\n"),
835 box_id_locale);
836 retval = -1;
837 goto leave;
840 oprintf(_("Commercial receiving status of box `%s': "), box_id_locale);
841 if (!box->dbOpenAddressing)
842 oprintf(_("Unknown\n"));
843 else if (*box->dbOpenAddressing)
844 oprintf(_("Positive\n"));
845 else
846 oprintf(_("Negative\n"));
847 } else {
848 char *refnumber = NULL;
849 printf((action) ?
850 _("Switching `%s' box commercial receiving on...\n"):
851 _("Switching `%s' box commercial receiving off...\n"),
852 box_id_locale);
853 err = isds_switch_commercial_receiving(cisds, box_id, action,
854 NULL, &refnumber);
855 finish_isds_operation(cisds, err);
857 if (!err) {
858 char *refnumber_locale = utf82locale(refnumber);
859 oprintf(_("Commercial receiving status successfully changed. "
860 "Assigned reference number: %s\n"),
861 refnumber_locale);
862 } else {
863 oprintf(_("Commercial receiving status has not been changed.\n"));
864 retval = -1;
866 free(refnumber);
869 leave:
870 if (!static_box_id) free(box_id);
871 else free(box_id_locale);
872 isds_DbOwnerInfo_free(&box);
873 return retval;
877 static int shi_user(int argc, const char **argv) {
878 isds_error err;
879 struct isds_DbUserInfo *user = NULL;
881 printf(_("Getting user details you are logged as...\n"));
882 err = isds_GetUserInfoFromLogin(cisds, &user);
883 finish_isds_operation(cisds, err);
884 if (err) return -1;
886 format_DbUserInfo(user);
888 isds_DbUserInfo_free(&user);
889 return 0;
893 static void shi_users_usage(const char *command) {
894 oprintf(_(
895 "Usage: %s BOX_ID\n"
896 "Get list of users having access to box with BOX_ID.\n"),
897 command);
901 static int shi_users(int argc, const char **argv) {
902 isds_error err;
903 struct isds_list *users = NULL, *item;
904 int ordinar;
906 if (!argv || !argv[1] || !*argv[1]) {
907 shi_users_usage((argv)?argv[0]:NULL);
908 return -1;
911 printf(_("Getting users of box with ID `%s'...\n"), argv[1]);
912 err = isds_GetDataBoxUsers(cisds, argv[1], &users);
913 finish_isds_operation(cisds, err);
914 if (err) return -1;
916 for (item = users, ordinar = 0; item; item=item->next) {
917 if (!item->data) continue;
918 ordinar++;
919 oprintf(_("\n* User #%d:\n"), ordinar);
920 format_DbUserInfo(item->data);
922 if (ordinar == 0)
923 oprintf(_("Empty list of users returned.\n"));
925 isds_list_free(&users);
926 return 0;
930 static int show_password_expiration(void) {
931 isds_error err;
932 struct timeval *expiration = NULL;
934 err = isds_get_password_expiration(cisds, &expiration);
935 finish_isds_operation(cisds, err);
936 if (err) {
937 fprintf(stderr, "Could not get password expiration time\n");
938 return -1;
941 print_header_timeval(_("Your password expires at"), expiration);
942 free(expiration);
943 return 0;
947 /* Change password in ISDS */
948 static int do_passwd(void) {
949 char *old_password = NULL;
950 char *new_password = NULL;
951 char *new_password2 = NULL;
952 isds_error err = IE_ERROR;
953 int retval = 0;
955 select_completition(COMPL_NONE);
957 oprintf(_(
958 "You are going to change your password. If you don't want to change your\n"
959 "password, insert empty string or EOF.\n"
960 "\n"
961 "You will be asked for your current (old) password and then for new password.\n"
962 "ISDS forces some criteria new password must fulfill. Current rules are:\n"
963 "\tLength: minimal 8, maximal 32 characters\n"
964 "\tMust contain at least: 1 upper case letter, 1 lower case letter, 1 digit\n"
965 "\tAllowed alphabet: [a-z][A-Z][0-9][!#$%%&()*+,-.:=?@[]_{}|~]\n"
966 "\tMust differ from last 255 passwords\n"
967 "\tMust not contain user ID\n"
968 "\tMust not contain sequence of three or more same characters\n"
969 "\tMust not start with `qwert', `asdgf', or `12345'\n"
970 "Finally, you must repeat your new password to avoid mistakes.\n"
971 "After password change will be confirmed, you must log in again as password\n"
972 "is transmitted to server on each request.\n"
973 "\n"));
975 old_password = ask_for_password(_("Old password: "));
976 if (!old_password || *old_password == '\0') {
977 fprintf(stderr, _("No password supplied\n"));
978 goto error;
981 new_password = ask_for_password(_("New password: "));
982 if (!new_password || *new_password == '\0') {
983 fprintf(stderr, _("No password supplied\n"));
984 goto error;
987 new_password2 = ask_for_password(_("Repeat new password: "));
988 if (!new_password2 || new_password2 == '\0') {
989 fprintf(stderr, _("No password supplied\n"));
990 goto error;
993 if (strcmp(new_password, new_password2)) {
994 fprintf(stderr, _("New passwords differ\n"));
995 goto error;
998 printf(_("Changing password...\n"));
999 err = isds_change_password(cisds, old_password, new_password);
1000 finish_isds_operation(cisds, err);
1001 if (err) {
1002 printf(_("Password change failed\n"));
1003 goto error;
1004 } else {
1005 oprintf(_("Password HAS been successfully changed.\n"));
1006 goto leave;
1009 error:
1010 retval = -1;
1011 oprintf(_("Password has NOT been changed!\n"));
1013 leave:
1014 free(old_password);
1015 free(new_password);
1016 free(new_password2);
1018 set_prompt(NULL);
1019 select_completition(COMPL_COMMAND);
1021 oprintf(_("\n"
1022 "Remember, ISDS password has limited life time.\n"));
1023 return retval;
1027 static void shi_passwd_usage(const char *command) {
1028 oprintf(_(
1029 "Usage: %s [-S]\n"
1030 "Manipulate user password or change it if no option given.\n"
1031 "\n"
1032 "Options:\n"
1033 " -S show password expiration time\n"
1034 ), command);
1038 static int shi_passwd(int argc, const char **argv) {
1039 int opt;
1041 optind = 0;
1042 while ((opt = getopt(argc, (char * const *)argv, "S")) != -1) {
1043 switch (opt) {
1044 case 'S':
1045 return show_password_expiration();
1046 default:
1047 shi_passwd_usage(argv[0]);
1048 return -1;
1051 if (optind != argc || argc > 1) {
1052 fprintf(stderr, _("Bad invocation\n"));
1053 shi_passwd_usage((argv)?argv[0]:NULL);
1054 return -1;
1057 return do_passwd();
1061 static int shi_login(int argc, const char **argv) {
1062 discard_credentials();
1063 if (do_login()) return -1;
1064 cfg_setstr(configuration, CONFIG_SERVER, server);
1065 cfg_setstr(configuration, CONFIG_USERNAME, username);
1066 cfg_setstr(configuration, CONFIG_PASSWORD, password);
1067 show_password_expiration();
1068 return 0;
1072 static void shi_debug_usage(const char *command) {
1073 oprintf(_(
1074 "Usage: %s -l LEVEL [-f FACILITY...] [{-e | -o FILE}]\n"
1075 "Debug FACILITIES on LEVEL.\n"
1076 "\n"
1077 "-l LEVEL set log level, valid interval <%d,%d>, default is %d\n"
1078 " %d is no logging, %d critical, %d errors,\n"
1079 " %d warnings, %d info, %d debug, %d all\n"
1080 "-f FACILITY debug only given facility, repeat this option to debug\n"
1081 " more facilities; valid values: none, http, soap, isds,\n"
1082 " file, sec, xml, all; default is none\n"
1083 "-e write debug log into stderr\n"
1084 "-o FILE append debug log to FILE\n"
1086 command,
1087 ILL_NONE, ILL_ALL, ILL_NONE,
1088 ILL_NONE, ILL_CRIT, ILL_ERR, ILL_WARNING,
1089 ILL_INFO, ILL_DEBUG, ILL_ALL);
1092 static int shi_debug(int argc, const char **argv) {
1093 int opt;
1094 int log_level = ILL_NONE;
1095 isds_log_facility log_facility = ILF_NONE;
1096 char *file = NULL;
1097 _Bool close_log = 0;
1099 optind = 0;
1100 while ((opt = getopt(argc, (char * const *)argv, "l:f:eo:")) != -1) {
1101 switch (opt) {
1102 case 'l':
1103 log_level = normalize_log_level(atoi(optarg));
1104 break;
1105 case 'f':
1106 if (add_log_facility(&log_facility, optarg)) return -1;
1107 break;
1108 case 'e':
1109 close_log = 1;
1110 case 'o':
1111 file = optarg;
1112 break;
1113 default:
1114 shi_debug_usage(argv[0]);
1115 return -1;
1118 if (optind == 1 || optind != argc) {
1119 fprintf(stderr, _("Bad invocation\n"));
1120 shi_debug_usage(argv[0]);
1121 return -1;
1124 /* Redirect log */
1125 if (close_log) {
1126 isds_set_log_callback(NULL, NULL);
1128 if (logger_fd != -1) {
1129 if (-1 == close(logger_fd)) {
1130 fprintf(stderr, _("Closing log file failed: %s\n"),
1131 strerror(errno));
1132 return -1;
1135 cfg_setstr(configuration, CONFIG_LOGFILE, NULL);
1137 if (do_log_to_file(file))
1138 return -1;
1139 if (file) cfg_setstr(configuration, CONFIG_LOGFILE, file);
1141 /* Set log levels */
1142 isds_set_logging(log_facility, log_level);
1143 cfg_setint(configuration, CONFIG_LOGLEVEL, log_level);
1144 save_log_facility(log_facility);
1146 return 0;
1150 static void show_setting_str(const char *variable, _Bool cenzore) {
1151 if (!variable) return;
1153 const char *value = cfg_getstr(configuration, variable);
1155 if (value) {
1156 if (cenzore)
1157 oprintf(_("%s = <set>\n"), variable);
1158 else
1159 oprintf(_("%s = `%s'\n"), variable, value);
1160 } else {
1161 oprintf(_("%s = <unset>\n"), variable);
1166 static void show_setting_boolean(const char *variable) {
1167 if (!variable) return;
1169 _Bool value = cfg_getbool(configuration, variable);
1171 if (value) {
1172 oprintf(_("%s = <true>\n"), variable);
1173 } else {
1174 oprintf(_("%s = <false>\n"), variable);
1179 static void show_setting_int(const char *variable) {
1180 if (!variable) return;
1182 long int value = cfg_getint(configuration, variable);
1184 oprintf(_("%s = %ld\n"), variable, value);
1188 static void show_setting_strlist(const char *variable) {
1189 if (!variable) return;
1191 int length = cfg_size(configuration, variable);
1193 if (length <= 0) {
1194 oprintf(_("%s = <unset>\n"), variable);
1195 } else {
1196 oprintf(_("%s = {"), variable);
1197 for (int i = 0; i < length; i++) {
1198 const char *value = cfg_getnstr(configuration, variable, i);
1199 if (i < length - 1)
1200 oprintf(_("`%s', "), value);
1201 else
1202 oprintf(_("`%s'}\n"), value);
1208 static int shi_settings(int argc, const char **argv) {
1209 /*int opt;
1210 int log_level = ILL_NONE;
1211 isds_log_facility log_facility = ILF_NONE;
1212 char *file = NULL;
1213 _Bool close_log = 0;*/
1216 optind = 0;
1217 while ((opt = getopt(argc, (char * const *)argv, "l:f:eo:")) != -1) {
1218 switch (opt) {
1219 case 'l':
1220 log_level = normalize_log_level(atoi(optarg));
1221 break;
1222 case 'f':
1223 if (add_log_facility(&log_facility, optarg)) return -1;
1224 break;
1225 case 'e':
1226 close_log = 1;
1227 case 'o':
1228 file = optarg;
1229 break;
1230 default:
1231 shi_debug_usage(argv[0]);
1232 return -1;
1235 if (optind == 1 || optind != argc) {
1236 printf(_("Bad invocation\n"));
1237 shi_debug_usage(argv[0]);
1238 return -1;
1242 oprintf(_("Current settings:\n"));
1244 show_setting_str(CONFIG_SERVER, 0);
1245 show_setting_str(CONFIG_USERNAME, 0);
1246 show_setting_str(CONFIG_PASSWORD, 1),
1247 show_setting_boolean(CONFIG_VERIFYSERVER);
1248 show_setting_str(CONFIG_CAFILE, 0);
1249 show_setting_str(CONFIG_CADIRECTORY, 0);
1250 show_setting_str(CONFIG_CRLFILE, 0);
1251 show_setting_int(CONFIG_TIMEOUT);
1252 show_setting_strlist(CONFIG_LOGFACILITIES);
1253 show_setting_str(CONFIG_LOGFILE, 0);
1254 show_setting_int(CONFIG_LOGLEVEL);
1255 show_setting_boolean(CONFIG_MARKMESSAGEREAD);
1256 show_setting_boolean(CONFIG_NORMALIZEMIMETYPE);
1257 show_setting_boolean(CONFIG_OVERWRITEFILES);
1259 return 0;
1263 static void shi_find_box_usage(const char *command) {
1264 oprintf(_(
1265 "Usage: %s {OPTION... | BOX_ID}\n"
1266 "Get information about box with BOX_ID or boxes meeting other criteria.\n"
1267 "Each search option requires an argument:\n"
1268 " -t box type; accepted values:\n"
1269 " FO Private individual\n"
1270 " PFO Self-employed individual\n"
1271 " PFO_ADVOK Lawyer\n"
1272 " PFO_DANPOR Tax advisor\n"
1273 " PFO_INSSPR Insolvency administrator\n"
1274 " PO Organisation\n"
1275 " PO_ZAK Organization based by law\n"
1276 " PO_REQ Organization based on request\n"
1277 " OVM Public authority\n"
1278 " OVM_NOTAR Notary\n"
1279 " OVM_EXEKUT Executor\n"
1280 " OVM_REQ Public authority based on request\n"
1281 " -j identity number\n"
1282 "\n"
1283 "Person name options:\n"
1284 " -f first name\n"
1285 " -m middle name\n"
1286 " -l last name\n"
1287 " -b last name at birth\n"
1288 " -s subject name\n"
1289 "\n"
1290 "Birth options:\n"
1291 " -d birth date (locale or full ISO 8601 date)\n"
1292 " -w birth city\n"
1293 " -y birth county\n"
1294 " -c birth state\n"
1295 "\n"
1296 "Address:\n"
1297 " -W city\n"
1298 " -S street\n"
1299 " -z number in street\n"
1300 " -Z number in municipality\n"
1301 " -P ZIP code\n"
1302 " -C state\n"
1303 "\n"
1304 "Other options:\n"
1305 " -n nationality\n"
1306 " -e e-mail\n"
1307 " -p phone number\n"
1308 " -i identifier\n"
1309 " -r registry code\n"
1310 " -a box status; accepted values:\n"
1311 " ACCESSIBLE Accessible\n"
1312 " TEMP_INACCESSIBLE Temporary inaccessible\n"
1313 " NOT_YET_ACCESSIBLE Not yet accessible\n"
1314 " PERM_INACCESSIBLE Permanently inaccessible\n"
1315 " REMOVED Deleted\n"
1316 " -o act as public authority; boolean values: 0 is false, 1 is true\n"
1317 " -k receive commercial messages; boolean values\n"
1318 "\n"
1319 "Not all option combinations are meaningful or allowed. For example box\n"
1320 "type is always required (except direct box ID query).\n"
1321 "ISDS can refuse to answer to much broad query. Not all boxes are searchable\n"
1322 "by every user.\n"
1324 command);
1328 /* Allow reassignment */
1329 #define FILL_OR_LEAVE(variable, locale) { \
1330 zfree(variable); \
1331 (variable) = locale2utf8(locale); \
1332 if (!(variable)) { \
1333 fprintf(stderr, _("Error: Not enough memory\n")); \
1334 retval = -1; \
1335 goto leave; \
1339 #define CALLOC_OR_LEAVE(structure) { \
1340 if (!(structure)) { \
1341 (structure) = calloc(1, sizeof(*(structure))); \
1342 if (!(structure)) { \
1343 fprintf(stderr, _("Error: Not enough memory\n")); \
1344 retval = -1; \
1345 goto leave; \
1350 #define FILL_BOOLEAN_OR_LEAVE(variable, locale) { \
1351 zfree(variable); \
1352 (variable) = malloc(sizeof(*(variable))); \
1353 if (!(variable)) { \
1354 fprintf(stderr, _("Error: Not enough memory\n")); \
1355 retval = -1; \
1356 goto leave; \
1358 if (!strcmp((locale), "0")) *(variable) = 0; \
1359 else if (!strcmp((locale), "1")) *(variable) = 1; \
1360 else { \
1361 fprintf(stderr, _("%s: %s: Unknown boolean value\n"), \
1362 argv[0], (locale)); \
1363 retval = -1; \
1364 goto leave; \
1368 #define FILL_LONGINT_OR_LEAVE(variable, locale) { \
1369 if (!(locale) || !*(locale)) { \
1370 fprintf(stderr, _("%s: Empty integer value\n"), argv[0]); \
1371 retval = -1; \
1372 goto leave; \
1374 char *endptr; \
1375 zfree(variable); \
1376 (variable) = malloc(sizeof(*(variable))); \
1377 if (!(variable)) { \
1378 fprintf(stderr, _("Error: Not enough memory\n")); \
1379 retval = -1; \
1380 goto leave; \
1382 (*variable) = strtol((locale), &endptr, 0); \
1383 if (*endptr) { \
1384 fprintf(stderr, _("%s: %s: Invalid integer value\n"), \
1385 argv[0], (locale)); \
1386 retval = -1; \
1387 goto leave; \
1391 static int shi_find_box(int argc, const char **argv) {
1392 int opt;
1393 isds_error err;
1394 struct isds_DbOwnerInfo *criteria = NULL;
1395 struct isds_list *item;
1396 int order = 0;
1397 int retval = 0;
1399 if (!argv || !argv[1] || !*argv[1]) {
1400 fprintf(stderr, _("Error: No argument supplied\n"));
1401 shi_find_box_usage((argv)?argv[0]:NULL);
1402 return -1;
1405 criteria = calloc(1, sizeof(*criteria));
1406 if (!criteria) {
1407 fprintf(stderr, _("Error: Not enough memory\n"));
1408 retval = -1;
1409 goto leave;
1412 /* Parse options */
1413 optind = 0;
1414 while ((opt = getopt(argc, (char * const *)argv, "t:j:s:"
1415 "f:m:l:b:s:" "d:w:y:c:" "W:S:z:Z:P:C:"
1416 "n:e:p:i:r:a:o:k:")) != -1) {
1417 switch (opt) {
1418 case 't':
1419 criteria->dbType = malloc(sizeof(*criteria->dbType));
1420 if (!criteria->dbType) {
1421 fprintf(stderr, _("Error: Not enough memory\n"));
1422 retval = -1;
1423 goto leave;
1425 if (!strcmp(optarg, "FO"))
1426 *criteria->dbType = DBTYPE_FO;
1427 else if (!strcmp(optarg, "PFO"))
1428 *criteria->dbType = DBTYPE_PFO;
1429 else if (!strcmp(optarg, "PFO_ADVOK"))
1430 *criteria->dbType = DBTYPE_PFO_ADVOK;
1431 else if (!strcmp(optarg, "PFO_DANPOR"))
1432 *criteria->dbType = DBTYPE_PFO_DANPOR;
1433 else if (!strcmp(optarg, "PFO_INSSPR"))
1434 *criteria->dbType = DBTYPE_PFO_INSSPR;
1435 else if (!strcmp(optarg, "PO"))
1436 *criteria->dbType = DBTYPE_PO;
1437 else if (!strcmp(optarg, "PO_ZAK"))
1438 *criteria->dbType = DBTYPE_PO_ZAK;
1439 else if (!strcmp(optarg, "PO_REQ"))
1440 *criteria->dbType = DBTYPE_PO_REQ;
1441 else if (!strcmp(optarg, "OVM"))
1442 *criteria->dbType = DBTYPE_OVM;
1443 else if (!strcmp(optarg, "OVM_NOTAR"))
1444 *criteria->dbType = DBTYPE_OVM_NOTAR;
1445 else if (!strcmp(optarg, "OVM_EXEKUT"))
1446 *criteria->dbType = DBTYPE_OVM_EXEKUT;
1447 else if (!strcmp(optarg, "OVM_REQ"))
1448 *criteria->dbType = DBTYPE_OVM_REQ;
1449 else {
1450 fprintf(stderr, _("%s: %s: Unknown box type\n"),
1451 argv[0], optarg);
1452 retval = -1;
1453 goto leave;
1455 break;
1457 case 'j':
1458 FILL_OR_LEAVE(criteria->ic, optarg);
1459 break;
1461 /* Person name */
1462 case 'f':
1463 CALLOC_OR_LEAVE(criteria->personName);
1464 FILL_OR_LEAVE(criteria->personName->pnFirstName, optarg);
1465 break;
1466 case 'm':
1467 CALLOC_OR_LEAVE(criteria->personName);
1468 FILL_OR_LEAVE(criteria->personName->pnMiddleName, optarg);
1469 break;
1470 case 'l':
1471 CALLOC_OR_LEAVE(criteria->personName);
1472 FILL_OR_LEAVE(criteria->personName->pnLastName, optarg);
1473 break;
1474 case 'b':
1475 CALLOC_OR_LEAVE(criteria->personName);
1476 FILL_OR_LEAVE(criteria->personName->pnLastNameAtBirth, optarg);
1477 break;
1478 case 's':
1479 FILL_OR_LEAVE(criteria->firmName, optarg);
1480 break;
1482 /* Birth */
1483 case 'd':
1484 CALLOC_OR_LEAVE(criteria->birthInfo);
1485 criteria->birthInfo->biDate = datestring2tm(optarg);
1486 if (!criteria->birthInfo->biDate) {
1487 fprintf(stderr, _("Error: Could not parse date: %s\n"),
1488 optarg);
1489 retval = -1;
1490 goto leave;
1492 break;
1493 case 'w':
1494 CALLOC_OR_LEAVE(criteria->birthInfo);
1495 FILL_OR_LEAVE(criteria->birthInfo->biCity, optarg);
1496 break;
1497 case 'y':
1498 CALLOC_OR_LEAVE(criteria->birthInfo);
1499 FILL_OR_LEAVE(criteria->birthInfo->biCounty, optarg);
1500 break;
1501 case 'c':
1502 CALLOC_OR_LEAVE(criteria->birthInfo);
1503 FILL_OR_LEAVE(criteria->birthInfo->biState, optarg);
1504 break;
1506 /* Address */
1507 case 'W':
1508 CALLOC_OR_LEAVE(criteria->address);
1509 FILL_OR_LEAVE(criteria->address->adCity, optarg);
1510 break;
1511 case 'S':
1512 CALLOC_OR_LEAVE(criteria->address);
1513 FILL_OR_LEAVE(criteria->address->adStreet, optarg);
1514 break;
1515 case 'z':
1516 CALLOC_OR_LEAVE(criteria->address);
1517 FILL_OR_LEAVE(criteria->address->adNumberInStreet, optarg);
1518 break;
1519 case 'Z':
1520 CALLOC_OR_LEAVE(criteria->address);
1521 FILL_OR_LEAVE(criteria->address->adNumberInMunicipality,
1522 optarg);
1523 break;
1524 case 'P':
1525 CALLOC_OR_LEAVE(criteria->address);
1526 FILL_OR_LEAVE(criteria->address->adZipCode, optarg);
1527 break;
1528 case 'C':
1529 CALLOC_OR_LEAVE(criteria->address);
1530 FILL_OR_LEAVE(criteria->address->adState, optarg);
1531 break;
1533 /* Other options */
1534 case 'n':
1535 FILL_OR_LEAVE(criteria->nationality, optarg);
1536 break;
1537 case 'e':
1538 FILL_OR_LEAVE(criteria->email, optarg);
1539 break;
1540 case 'p':
1541 FILL_OR_LEAVE(criteria->telNumber, optarg);
1542 break;
1543 case 'i':
1544 FILL_OR_LEAVE(criteria->identifier, optarg);
1545 break;
1546 case 'r':
1547 FILL_OR_LEAVE(criteria->registryCode, optarg);
1548 break;
1549 case 'a':
1550 criteria->dbState = malloc(sizeof(*criteria->dbState));
1551 if (!criteria->dbState) {
1552 fprintf(stderr, _("Error: Not enough memory\n"));
1553 retval = -1;
1554 goto leave;
1556 if (!strcmp(optarg, "ACCESSIBLE"))
1557 *criteria->dbState = DBSTATE_ACCESSIBLE;
1558 else if (!strcmp(optarg, "TEMP_INACCESSIBLE"))
1559 *criteria->dbState = DBSTATE_TEMP_UNACCESSIBLE;
1560 else if (!strcmp(optarg, "NOT_YET_ACCESSIBLE"))
1561 *criteria->dbState = DBSTATE_NOT_YET_ACCESSIBLE;
1562 else if (!strcmp(optarg, "PERM_INACCESSIBLE"))
1563 *criteria->dbState = DBSTATE_PERM_UNACCESSIBLE;
1564 else if (!strcmp(optarg, "REMOVED"))
1565 *criteria->dbState = DBSTATE_REMOVED;
1566 else {
1567 fprintf(stderr, _("%s: %s: Unknown box status\n"),
1568 argv[0], optarg);
1569 retval = -1;
1570 goto leave;
1572 break;
1573 case 'o':
1574 FILL_BOOLEAN_OR_LEAVE(criteria->dbEffectiveOVM, optarg);
1575 break;
1576 case 'k':
1577 FILL_BOOLEAN_OR_LEAVE(criteria->dbOpenAddressing, optarg);
1578 break;
1580 default:
1581 shi_find_box_usage(argv[0]);
1582 retval = -1;
1583 goto leave;
1587 /* There must be an option and all of them must be recognized, if not only
1588 * BOX_ID supplied */
1589 if (argc > 2 && optind != argc) {
1590 fprintf(stderr, _("Error: Superfluous argument\n"));
1591 shi_find_box_usage(argv[0]);
1592 retval = -1;
1593 goto leave;
1596 /* If only box ID is supplied use it */
1597 if (argc == 2 && argv[1] && *argv[1]) {
1598 criteria->dbID = locale2utf8(argv[1]);
1599 if (!criteria->dbID) {
1600 fprintf(stderr, _("Error: Not enough memory\n"));
1601 retval = -1;
1602 goto leave;
1606 printf(_("Searching boxes...\n"));
1607 err = isds_FindDataBox(cisds, criteria, &boxes);
1608 finish_isds_operation(cisds, err);
1609 if (err) return -1;
1611 for(item = boxes; item; item = item->next) {
1612 if (!item->data) continue;
1613 order++;
1615 oprintf(_("\n* Result #%d:\n"), order);
1616 format_DbOwnerInfo(item->data);
1619 leave:
1620 isds_DbOwnerInfo_free(&criteria);
1621 return retval;
1625 static void shi_stat_box_usage(const char *command) {
1626 oprintf(_(
1627 "Usage: %s BOX_ID...\n"
1628 "Get status of box with BOX_ID. More boxes can be specified.\n"),
1629 command);
1633 /* Get boxes status */
1634 static int shi_stat_box(int argc, const char **argv) {
1635 isds_error err;
1636 char *id = NULL;
1637 long int status;
1639 if (!argv || !*argv || argc < 2 || !argv[1]) {
1640 fprintf(stderr, _("Missing box ID\n"));
1641 shi_stat_box_usage((argv[0])?argv[0]:NULL);
1642 return -1;
1645 for (int i = 1; i < argc; i++) {
1646 if (!argv[i] || !*argv[i]) continue;
1648 free(id);
1649 id = locale2utf8(argv[i]);
1650 if (!id) {
1651 fprintf(stderr, _("%s: Could not covert box ID to UTF-8\n"),
1652 argv[i]);
1653 return -1;
1656 printf(_("Getting status of box `%s'...\n"), argv[i]);
1657 err = isds_CheckDataBox(cisds, id, &status);
1658 finish_isds_operation(cisds, err);
1659 if (err) return -1;
1661 oprintf(_("Status of box `%s': %s\n"),
1662 argv[i], DbState2string(&status));
1665 return 0;
1669 static void shi_compose_usage(const char *command) {
1670 oprintf(_(
1671 "Usage: %s OPTION...\n"
1672 "Compose and send a message to recipient defined by his box ID.\n"
1673 "Each option requires an argument (if not stated otherwise):\n"
1674 " -s * message subject\n"
1675 "\n"
1676 "Recipient options:\n"
1677 " -b * recipient box ID\n"
1678 " -U organisation unit name\n"
1679 " -N organisation unit number\n"
1680 " -P to hands of given person\n"
1681 "\n"
1682 "Sender organisation structure options:\n"
1683 " -u unit name\n"
1684 " -n unit number\n"
1685 "\n"
1686 "Message identifier options:\n"
1687 " -r sender reference number\n"
1688 " -f sender file ID\n"
1689 " -R recipient reference number\n"
1690 " -F recipient file ID\n"
1691 "\n"
1692 "Legal title options:\n"
1693 " -y year act has been issued\n"
1694 " -a ordinal number of act in a year\n"
1695 " -e section of the act\n"
1696 " -o paragraph of the act\n"
1697 " -i point of the paragraph of the act\n"
1698 "\n"
1699 "Delivery options:\n"
1700 " -p personal delivery required\n"
1701 " -t allow substitutable delivery\n"
1702 " -A non-OVM sender acts as public authority\n"
1703 "\n"
1704 "Document options:\n"
1705 " -d * read document from local file\n"
1706 " -D document name (defaults to base local file name)\n"
1707 " -x transport subset of the document as a XML.\n"
1708 " Argument is XPath expression specifying desired node set\n"
1709 " -m override MIME type (guessed on -d)\n"
1710 " -g document ID (must be unique per message)\n"
1711 " -G reference to other document using its ID\n"
1712 " -c document is digital signature of other document (NO argument\n"
1713 " allowed)\n"
1714 "\n"
1715 "Options marked with asterisk are mandatory, other are optional. Another soft\n"
1716 "dependencies can emerge upon using specific option. They are not mandated by\n"
1717 "ISDS currently, but client library or this program can force them to assure\n"
1718 "semantically complete message. Following soft dependencies are recommended:\n"
1719 " -y <=> -a act number and year must be used at the same time\n"
1720 " -i => -o act point requires act paragraph\n"
1721 " -o => -e act paragraph requires act section\n"
1722 " -e => -a act section requires act number\n"
1723 " -G => -g document with referenced ID must exist\n"
1724 " -c => -G signature must refer to signed document\n"
1725 " -c first document cannot be signature\n"
1726 "\n"
1727 "More documents can be attached to a message by repeating `-d' option.\n"
1728 "Document order will be preserved. Other document options affect immediately\n"
1729 "preceding `-d' document only. E.g. `-d /tmp/foo.pdf -m application/pdf\n"
1730 "-d /tmp/bar.txt -m text/plain' attaches first PDF file, then textual file.\n"
1731 "\n"
1732 "The same applies to recipient options that must start with box ID (-b).\n"
1733 "If more recipients specified, each of them will get a copy of composed\n"
1734 "message. ISDS will assign message identifier to each copy in turn.\n"
1736 command);
1740 static int shi_compose(int argc, const char **argv) {
1741 int opt;
1742 isds_error err;
1743 int retval = 0;
1744 struct isds_message *message = NULL;
1745 struct isds_envelope *envelope = NULL;
1746 struct isds_list *documents = NULL;
1747 struct isds_document *document = NULL;
1748 struct isds_list *copies = NULL, *copy_item = NULL;
1749 struct isds_message_copy *copy = NULL;
1750 char *message_id_locale = NULL, *recipient_id_locale = NULL,
1751 *dmStatus_locale = NULL;
1753 if (!argv || !argv[1] || !*argv[1]) {
1754 fprintf(stderr, _("Error: No argument supplied\n"));
1755 shi_compose_usage((argv)?argv[0]:NULL);
1756 return -1;
1759 message = calloc(1, sizeof(*message));
1760 if (!message) {
1761 fprintf(stderr, _("Error: Not enough memory\n"));
1762 retval = -1;
1763 goto leave;
1765 envelope = calloc(1, sizeof(*envelope));
1766 if (!envelope) {
1767 fprintf(stderr, _("Error: Not enough memory\n"));
1768 retval = -1;
1769 goto leave;
1771 message->envelope = envelope;
1773 /* Parse options */
1774 optind = 0;
1775 while ((opt = getopt(argc, (char * const *)argv, "s:" "b:U:N:P:" "u:n:"
1776 "r:f:R:F:" "y:a:e:o:i:" "p:t:A:" "d:D:x:m:g:G:c"
1777 )) != -1) {
1778 switch (opt) {
1779 case 's':
1780 FILL_OR_LEAVE(envelope->dmAnnotation, optarg);
1781 break;
1783 /* Recipient options */
1784 case 'b':
1785 copy = NULL;
1786 if (!copies) {
1787 /* First recipient */
1788 CALLOC_OR_LEAVE(copies);
1789 copies->destructor =
1790 (void(*)(void **)) isds_message_copy_free;
1791 copy_item = copies;
1792 } else {
1793 /* Next recipient */
1794 CALLOC_OR_LEAVE(copy_item->next);
1795 copy_item->next->destructor =
1796 (void(*)(void **)) isds_message_copy_free;
1797 copy_item = copy_item->next;
1799 CALLOC_OR_LEAVE(copy);
1800 copy_item->data = copy;
1802 /* Copy recipient box ID */
1803 FILL_OR_LEAVE(copy->dbIDRecipient, optarg);
1804 break;
1805 case 'U':
1806 if (!copy) {
1807 fprintf(stderr,
1808 _("Error: %s: Recipient box ID (-b) must precede "
1809 "recipient organisation unit name (-%c)\n"),
1810 optarg, opt);
1811 retval = -1;
1812 goto leave;
1814 FILL_OR_LEAVE(copy->dmRecipientOrgUnit, optarg);
1815 break;
1816 case 'N':
1817 if (!copy) {
1818 fprintf(stderr,
1819 _("Error: %s: Recipient box ID (-b) must precede "
1820 "recipient organisation unit number (-%c)\n"),
1821 optarg, opt);
1822 retval = -1;
1823 goto leave;
1825 FILL_LONGINT_OR_LEAVE(copy->dmRecipientOrgUnitNum, optarg);
1826 break;
1827 case 'P':
1828 if (!copy) {
1829 fprintf(stderr,
1830 _("Error: %s: Recipient box ID (-b) must precede "
1831 "to-hands option (-%c)\n"), optarg, opt);
1832 retval = -1;
1833 goto leave;
1835 FILL_OR_LEAVE(copy->dmToHands, optarg);
1836 break;
1838 /* Sender organisation structure options */
1839 case 'u':
1840 FILL_OR_LEAVE(envelope->dmSenderOrgUnit, optarg);
1841 break;
1842 case 'n':
1843 FILL_LONGINT_OR_LEAVE(envelope->dmSenderOrgUnitNum, optarg);
1844 break;
1846 /* Message identifier options */
1847 case 'r':
1848 FILL_OR_LEAVE(envelope->dmSenderRefNumber, optarg);
1849 break;
1850 case 'f':
1851 FILL_OR_LEAVE(envelope->dmSenderIdent, optarg);
1852 break;
1853 case 'R':
1854 FILL_OR_LEAVE(envelope->dmRecipientRefNumber, optarg);
1855 break;
1856 case 'F':
1857 FILL_OR_LEAVE(envelope->dmRecipientIdent, optarg);
1858 break;
1860 /* Legal title options */
1861 case 'y':
1862 FILL_LONGINT_OR_LEAVE(envelope->dmLegalTitleYear, optarg);
1863 break;
1864 case 'a':
1865 FILL_LONGINT_OR_LEAVE(envelope->dmLegalTitleLaw, optarg);
1866 break;
1867 case 'e':
1868 FILL_OR_LEAVE(envelope->dmLegalTitleSect, optarg);
1869 break;
1870 case 'o':
1871 FILL_OR_LEAVE(envelope->dmLegalTitlePar, optarg);
1872 break;
1873 case 'i':
1874 FILL_OR_LEAVE(envelope->dmLegalTitlePoint, optarg);
1875 break;
1877 /* Delivery options */
1878 case 'p':
1879 FILL_BOOLEAN_OR_LEAVE(envelope->dmPersonalDelivery, optarg);
1880 break;
1881 case 't':
1882 FILL_BOOLEAN_OR_LEAVE(envelope->dmAllowSubstDelivery, optarg);
1883 break;
1884 case 'A':
1885 FILL_BOOLEAN_OR_LEAVE(envelope->dmOVM, optarg);
1886 break;
1888 /* Document options */
1889 case 'd':
1890 document = NULL;
1891 if (!documents) {
1892 /* First document */
1893 CALLOC_OR_LEAVE(message->documents);
1894 message->documents->destructor =
1895 (void(*)(void **)) isds_document_free;
1896 documents = message->documents;
1897 CALLOC_OR_LEAVE(document);
1898 documents->data = document;
1899 document->dmFileMetaType = FILEMETATYPE_MAIN;
1900 } else {
1901 /* Next document */
1902 CALLOC_OR_LEAVE(documents->next);
1903 documents->next->destructor =
1904 (void(*)(void **)) isds_document_free;
1905 documents = documents->next;
1906 CALLOC_OR_LEAVE(document);
1907 documents->data = document;
1908 document->dmFileMetaType = FILEMETATYPE_ENCLOSURE;
1911 if (load_data_from_file(optarg, &document->data,
1912 &document->data_length, &document->dmMimeType)) {
1913 retval = -1;
1914 goto leave;
1916 /* XXX: POSIX basename() modifies argument */
1917 FILL_OR_LEAVE(document->dmFileDescr, basename(optarg));
1918 break;
1919 case 'D':
1920 if (!document) {
1921 fprintf(stderr,
1922 _("Error: %s: Document file (-d) must precede "
1923 "document name (-%c)\n"), optarg, opt);
1924 retval = -1;
1925 goto leave;
1927 FILL_OR_LEAVE(document->dmFileDescr, optarg);
1928 break;
1929 case 'x':
1930 if (!document) {
1931 fprintf(stderr,
1932 _("Error: %s: Document file (-d) must precede "
1933 "XPath expression (-%c)\n"), optarg, opt);
1934 retval = -1;
1935 goto leave;
1937 /* Load XML node list */
1938 char *xpath_expr = NULL;
1939 FILL_OR_LEAVE(xpath_expr, optarg);
1940 retval = load_xml_subtree_from_memory(
1941 document->data, document->data_length,
1942 &document->xml_node_list, xpath_expr);
1943 if (retval) {
1944 free(xpath_expr);
1945 goto leave;
1947 /* Switch document type to XML */
1948 document->is_xml = 1;
1949 zfree(document->data);
1950 document->data_length = 0;
1951 documents->destructor =
1952 (void(*)(void **)) free_document_with_xml_node_list;
1953 break;
1954 case 'm':
1955 if (!document) {
1956 fprintf(stderr,
1957 _("Error: %s: Document file (-d) must precede "
1958 "MIME type (-%c)\n"), optarg, opt);
1959 retval = -1;
1960 goto leave;
1962 FILL_OR_LEAVE(document->dmMimeType, optarg);
1963 break;
1964 case 'g':
1965 if (!document) {
1966 fprintf(stderr,
1967 _("Error: %s: Document file (-d) must precede "
1968 "document ID (-%c)\n"), optarg, opt);
1969 retval = -1;
1970 goto leave;
1972 FILL_OR_LEAVE(document->dmFileGuid, optarg);
1973 break;
1974 case 'G':
1975 if (!document) {
1976 fprintf(stderr,
1977 _("Error: %s: Document file (-d) must precede "
1978 "document reference (-%c)\n"), optarg, opt);
1979 retval = -1;
1980 goto leave;
1982 FILL_OR_LEAVE(document->dmUpFileGuid, optarg);
1983 break;
1984 case 'c':
1985 if (!document) {
1986 fprintf(stderr,
1987 _("Error: Document file (-d) must precede "
1988 "document signature type (-%c)\n"), opt);
1989 retval = -1;
1990 goto leave;
1992 document->dmFileMetaType = FILEMETATYPE_SIGNATURE;
1993 break;
1995 default:
1996 shi_compose_usage(argv[0]);
1997 retval = -1;
1998 goto leave;
2002 /* All options must be recognized */
2003 if (optind != argc) {
2004 fprintf(stderr, _("Error: Superfluous argument\n"));
2005 shi_compose_usage(argv[0]);
2006 retval = -1;
2007 goto leave;
2010 if (!copies) {
2011 fprintf(stderr, _("Error: No recipient box ID specified\n"));
2012 shi_compose_usage(argv[0]);
2013 retval = -1;
2014 goto leave;
2017 /* TODO: Check Legal Title soft dependencies */
2019 /* Preview */
2020 oprintf(_("Following message has been composed:\n"));
2021 format_message(message);
2022 oprintf(_("Following recipients have been specified:\n"));
2023 format_copies(copies);
2024 /* TODO: Confirmation, correction */
2026 /* Send a message */
2027 printf(_("Sending message...\n"));
2028 err = isds_send_message_to_multiple_recipients(cisds, message, copies);
2029 finish_isds_operation(cisds, err);
2030 if (err && err != IE_PARTIAL_SUCCESS) {
2031 retval = -1;
2032 goto leave;
2035 /* Show results for each copy */
2036 for (copy_item = copies; copy_item; copy_item = copy_item->next) {
2037 if (!copy_item->data) continue;
2038 copy = (struct isds_message_copy *) copy_item->data;
2039 recipient_id_locale = utf82locale(copy->dbIDRecipient);
2041 if (copy->error) {
2042 retval = -1;
2043 if (copy->dmStatus) dmStatus_locale = utf82locale(copy->dmStatus);
2044 if (dmStatus_locale)
2045 oprintf(_("%s: Failed: %s: %s\n"),
2046 recipient_id_locale,
2047 isds_strerror(copy->error), dmStatus_locale);
2048 else
2049 oprintf(_("%s: Failed: %s\n"), recipient_id_locale,
2050 isds_strerror(copy->error));
2051 zfree(dmStatus_locale);
2052 } else {
2053 message_id_locale = utf82locale(copy->dmID);
2054 oprintf(_("%s: Succeeded. Assigned message ID: %s\n"),
2055 recipient_id_locale, message_id_locale);
2056 free(message_id_locale);
2059 free(recipient_id_locale);
2062 leave:
2063 isds_message_free(&message);
2064 isds_list_free(&copies);
2065 return retval;
2068 #undef FILL_LONGINT_OR_LEAVE
2069 #undef FILL_BOOLEAN_OR_LEAVE
2070 #undef CALLOC_OR_LEAVE
2071 #undef FILL_OR_LEAVE
2074 static void shi_delivery_usage(const char *command) {
2075 oprintf(_(
2076 "Usage: %s MESSAGE_ID\n"
2077 "Get delivery data about message with MESSAGE_ID.\n"),
2078 command);
2082 static int shi_delivery(int argc, const char **argv) {
2083 isds_error err;
2084 const char *id;
2086 if (!argv || !argv[1] || !*argv[1]) {
2087 shi_delivery_usage(argv[0]);
2088 return -1;
2090 id = argv[1];
2092 printf(_("Getting delivery info...\n"));
2093 err = isds_get_signed_delivery_info(cisds, id, &message);
2094 finish_isds_operation(cisds, err);
2095 if (err) {
2096 set_prompt(NULL);
2097 select_completition(COMPL_COMMAND);
2098 return -1;
2101 format_message(message);
2103 if (message->envelope && message->envelope->dmID)
2104 set_prompt(_("%s %s"), argv[0], message->envelope->dmID);
2105 else
2106 set_prompt("%s", argv[0]);
2107 select_completition(COMPL_MSG);
2108 return 0;
2112 static int shi_dump_message(int argc, const char **argv) {
2113 if (!message) {
2114 fprintf(stderr, _("No message loaded\n"));
2115 return -1;
2118 print_message(message);
2119 return 0;
2123 static void shi_hash_usage(const char *command) {
2124 oprintf(_(
2125 "Usage: %s [MESSAGE_ID]\n"
2126 "Retrieve message hash stored in ISDS.\n"
2127 "If MESSAGE_ID is defined, query for that message.\n"
2128 "Otherwise use current message.\n"),
2129 command);
2133 static int shi_hash(int argc, const char **argv) {
2134 isds_error err;
2135 const char *id = NULL;
2136 struct isds_hash *hash = NULL;
2137 char *hash_string = NULL;
2139 if (!argv || argc > 2) {
2140 shi_hash_usage((argv)?argv[0]:NULL);
2141 return -1;
2143 if (argc == 2 && argv[1] && *argv[1])
2144 id = argv[1];
2145 else {
2146 if (!message) {
2147 fprintf(stderr, _("No message loaded\n"));
2148 return -1;
2150 if (!message->envelope || !message->envelope->dmID) {
2151 fprintf(stderr, _("Current message is missing ID\n"));
2152 return -1;
2154 id = message->envelope->dmID;
2157 printf(_("Getting message hash...\n"));
2158 err = isds_download_message_hash(cisds, id, &hash);
2159 finish_isds_operation(cisds, err);
2160 if (err) return -1;
2162 hash_string = hash2string(hash);
2163 oprintf(_("ISDS states message with `%s' ID has following hash:\n%s\n"),
2164 id, hash_string);
2166 free(hash_string);
2167 isds_hash_free(&hash);
2168 return 0;
2172 static int shi_verify(int argc, const char **argv) {
2173 isds_error err;
2174 int retval = 0;
2175 struct isds_hash *retrieved_hash = NULL, *stored_hash = NULL;
2176 char *hash_string = NULL;
2177 size_t width = 4;
2179 if (!message) {
2180 fprintf(stderr, _("No message loaded\n"));
2181 return -1;
2184 if (!message->envelope) {
2185 fprintf(stderr, _("Current message is missing envelope\n"));
2186 return -1;
2188 stored_hash = message->envelope->hash;
2189 message->envelope->hash = NULL;
2191 if (message->envelope->dmID) {
2192 /* Verify remote hash */
2193 oprintf(_("Remote hash check:\n"));
2195 printf(_("Getting message hash...\n"));
2196 err = isds_download_message_hash(cisds, message->envelope->dmID,
2197 &retrieved_hash);
2198 finish_isds_operation(cisds, err);
2200 if (retrieved_hash) {
2201 hash_string = hash2string(retrieved_hash);
2202 ohprint(_("Retrieved:"), width);
2203 oprintf("%s\n", hash_string);
2204 zfree(hash_string);
2207 if (retrieved_hash && message->raw) {
2208 err = isds_compute_message_hash(cisds, message,
2209 retrieved_hash->algorithm);
2210 finish_isds_operation(cisds, err);
2212 if (!err) {
2213 hash_string = hash2string(message->envelope->hash);
2214 ohprint(_("Computed:"), width);
2215 oprintf("%s\n", hash_string);
2216 zfree(hash_string);
2220 err = isds_hash_cmp(retrieved_hash, message->envelope->hash);
2221 switch (err) {
2222 case IE_SUCCESS:
2223 oprintf(_("Hashes match.\n")); break;
2224 case IE_NOTUNIQ:
2225 oprintf(_("Hashes do not match.\n"));
2226 retval = -1;
2227 break;
2228 default:
2229 oprintf(_("Hashes could not be compared.\n"));
2230 retval = -1;
2231 break;
2234 free(retrieved_hash);
2238 if (stored_hash) {
2239 /* Verify stored hash */
2240 oprintf(_("Stored hash check:\n"));
2242 hash_string = hash2string(stored_hash);
2243 ohprint(_("Stored:"), width);
2244 oprintf("%s\n", hash_string);
2245 zfree(hash_string);
2247 if (message->raw) {
2248 err = isds_compute_message_hash(cisds, message,
2249 stored_hash->algorithm);
2250 finish_isds_operation(cisds, err);
2252 if (!err) {
2253 hash_string = hash2string(message->envelope->hash);
2254 ohprint(_("Computed:"), width);
2255 oprintf("%s\n", hash_string);
2256 zfree(hash_string);
2260 err = isds_hash_cmp(stored_hash, message->envelope->hash);
2261 switch (err) {
2262 case IE_SUCCESS:
2263 oprintf(_("Hashes match.\n")); break;
2264 case IE_NOTUNIQ:
2265 oprintf(_("Hashes do not match.\n"));
2266 retval = -1;
2267 break;
2268 default:
2269 oprintf(_("Hashes could not be compared.\n"));
2270 retval = -1;
2271 break;
2274 isds_hash_free(&message->envelope->hash);
2277 message->envelope->hash = stored_hash;
2278 return retval;
2282 static int shi_authenticate(int argc, const char **argv) {
2283 isds_error err;
2284 int retval = 0;
2286 if (!message) {
2287 fprintf(stderr, _("No message loaded\n"));
2288 return -1;
2290 if (!message->raw || message->raw_length == 0) {
2291 fprintf(stderr, _("Current message is missing raw representation\n"));
2292 return -1;
2295 printf(_("Submitting message to authenticity check...\n"));
2296 err = isds_authenticate_message(cisds, message->raw, message->raw_length);
2297 finish_isds_operation(cisds, (err == IE_NOTUNIQ) ? IE_SUCCESS : err);
2299 switch (err) {
2300 case IE_SUCCESS:
2301 oprintf(_("Message originates in ISDS.\n")); break;
2302 case IE_NOTUNIQ:
2303 oprintf(_("Message is unknown to ISDS or has been tampered.\n"));
2304 retval = -1;
2305 break;
2306 default:
2307 retval = -1;
2308 break;
2311 return retval;
2315 static void shi_accept_message_usage(const char *command) {
2316 oprintf(_(
2317 "Usage: %s [MESSAGE_ID...]\n"
2318 "Accept commercial message moving its state to received.\n"
2319 "If MESSAGE_ID is defined, accept that message. More messages can be specified.\n"
2320 "Otherwise accept all commercial incoming messages.\n"),
2321 command);
2325 static int shi_accept_message(int argc, const char **argv) {
2326 isds_error err;
2327 char *id = NULL;
2329 /* Process messages named in argv */
2330 for (int i = 1; i < argc; i++) {
2331 if (!argv[i] || !*argv[i]) continue;
2333 id = locale2utf8(argv[i]);
2334 if (!id) {
2335 fprintf(stderr,
2336 _("Error: Could not convert message ID to UTF-8: %s\n"),
2337 argv[i]);
2338 return -1;
2341 printf(_("Accepting message `%s'...\n"), argv[i]);
2342 err = isds_mark_message_received(cisds, id);
2343 finish_isds_operation(cisds, err);
2344 if (err) {
2345 free(id);
2346 return -1;
2349 oprintf(_("Message `%s' accepted\n"), argv[i]);
2350 free(id);
2353 if (argc < 2) {
2354 /* TODO: list commercial not received messages and accept all of them
2355 * */
2356 fprintf(stderr,
2357 _("Error: No message ID supplied. Accepting all commercial "
2358 "messages not implemented yet.\n"));
2359 return -1;
2362 return 0;
2366 /* Mark message as read. At one form of ID must be provided.
2367 * @id is UTF-8 encoded message ID
2368 * @id_locale is locale encoded message ID. @id takes preference. */
2369 static int do_read_message(const char *id, const char *id_locale) {
2370 _Bool static_id;
2371 isds_error err;
2373 if ((!id || !*id) && (!id_locale || !*id_locale)) return -1;
2375 if (id) {
2376 static_id = 1;
2377 id_locale = utf82locale(id);
2378 } else {
2379 static_id = 0;
2380 id = locale2utf8(id_locale);
2381 if (!id) {
2382 fprintf(stderr,
2383 _("Error: Could not convert message ID to UTF-8: %s\n"),
2384 id_locale);
2385 return -1;
2389 printf(_("Marking message `%s' as read...\n"), id_locale);
2390 err = isds_mark_message_read(cisds, id);
2391 finish_isds_operation(cisds, err);
2393 if (!err)
2394 oprintf(_("Message `%s' marked as read\n"), id_locale);
2396 if (static_id) free((char *) id_locale);
2397 else free((char *) id);
2399 return (err) ? -1 : 0;
2403 static void shi_read_message_usage(const char *command) {
2404 oprintf(_(
2405 "Usage: %s [MESSAGE_ID...]\n"
2406 "Mark message as read moving its state to read.\n"
2407 "\n"
2408 "When new incoming message is download, its state is not changed on server.\n"
2409 "Client must mark such message as read explicitly. You can use this command\n"
2410 "to do so, if not done automatically at download time by your client.\n"
2411 "\n"
2412 "If MESSAGE_ID is defined, mark that message. More messages can be specified.\n"
2413 "Otherwise marks currently loaded message.\n"),
2414 command);
2418 static int shi_read_message(int argc, const char **argv) {
2419 for (int i = 1; i < argc; i++) {
2420 if (!argv[i] || !*argv[i]) continue;
2421 if (do_read_message(NULL, argv[i]))
2422 return -1;
2425 if (argc < 2) {
2426 if (!message) {
2427 fprintf(stderr, _("No message loaded\n"));
2428 return -1;
2430 if (!message->envelope) {
2431 fprintf(stderr, _("Loaded message is missing envelope\n"));
2432 return -1;
2434 if (!message->envelope->dmID || !*message->envelope->dmID) {
2435 fprintf(stderr, _("Loaded message is missing ID\n"));
2436 return -1;
2438 return do_read_message(message->envelope->dmID, NULL);
2441 return 0;
2445 static void shi_cat_message_usage(const char *command) {
2446 oprintf(_(
2447 "Usage: %s\n"
2448 "Print unformated raw representation of current message.\n"
2449 "\n"
2450 "This is the same content you would get into file by `save' command.\n"
2451 "\n"
2452 "Be ware the binary stream can screw your terminal. No new line character\n"
2453 "will be appended to the end of the output.\n"),
2454 command);
2458 static int shi_cat_message(int argc, const char **argv) {
2459 if (!message) {
2460 fprintf(stderr, _("No message loaded\n"));
2461 return -1;
2464 if (!message->raw || !message->raw_length) {
2465 fprintf(stderr, _("Current message is missing raw representation\n"));
2466 return -1;
2469 if (owrite(message->raw, message->raw_length) != message->raw_length) {
2470 fprintf(stderr, _("Error while printing message content\n"));
2471 return -1;
2474 return 0;
2478 static int shi_show_message(int argc, const char **argv) {
2479 if (!message) {
2480 fprintf(stderr, _("No message loaded\n"));
2481 return -1;
2484 format_message(message);
2485 return 0;
2489 static void shi_incoming_message_usage(const char *command) {
2490 oprintf(_(
2491 "Usage: %s [-r] MESSAGE_ID\n"
2492 "Get incoming message with MESSAGE_ID.\n"
2493 "Options:\n"
2494 " -r Mark mesage as read\n"),
2495 command);
2499 static int shi_incoming_message(int argc, const char **argv) {
2500 isds_error err;
2501 const char *id;
2502 int opt;
2503 _Bool mark_as_read = 0;
2505 optind = 0;
2506 while ((opt = getopt(argc, (char * const *)argv, "r")) != -1) {
2507 switch (opt) {
2508 case 'r':
2509 mark_as_read = 1;
2510 break;
2511 default:
2512 shi_incoming_message_usage((argv)?argv[0]:NULL);
2513 return -1;
2516 if (optind + 1 != argc || !argv || !argv[optind] || !*argv[optind]) {
2517 fprintf(stderr, _("Bad invocation\n"));
2518 shi_incoming_message_usage((argv)?argv[0]:NULL);
2519 return -1;
2521 id = argv[optind];
2523 printf(_("Getting incoming message...\n"));
2524 err = isds_get_signed_received_message(cisds, id, &message);
2525 finish_isds_operation(cisds, err);
2526 if (err) {
2527 set_prompt(NULL);
2528 select_completition(COMPL_COMMAND);
2529 return -1;
2532 format_message(message);
2534 if (message->envelope && message->envelope->dmID)
2535 set_prompt(_("%s %s"), argv[0], message->envelope->dmID);
2536 else
2537 set_prompt("%s", argv[0]);
2538 select_completition(COMPL_MSG);
2540 if (mark_as_read || cfg_getbool(configuration, CONFIG_MARKMESSAGEREAD)) {
2541 if (message->envelope && message->envelope->dmMessageStatus &&
2542 ! (*message->envelope->dmMessageStatus & MESSAGESTATE_READ))
2543 return do_read_message(id, NULL);
2545 return 0;
2549 static void shi_outgoing_message_usage(const char *command) {
2550 oprintf(_(
2551 "Usage: %s MESSAGE_ID\n"
2552 "Get outgoing message with MESSAGE_ID.\n"),
2553 command);
2557 static int shi_outgoing_message(int argc, const char **argv) {
2558 isds_error err;
2559 const char *id;
2561 if (!argv || !argv[1] || !*argv[1]) {
2562 shi_outgoing_message_usage(argv[0]);
2563 return -1;
2565 id = argv[1];
2567 printf(_("Getting outgoing message...\n"));
2568 err = isds_get_signed_sent_message(cisds, id, &message);
2569 finish_isds_operation(cisds, err);
2570 if (err) {
2571 set_prompt(NULL);
2572 select_completition(COMPL_COMMAND);
2573 return -1;
2576 format_message(message);
2577 if (message->envelope && message->envelope->dmID)
2578 set_prompt(_("%s %s"), argv[0], message->envelope->dmID);
2579 else
2580 set_prompt("%s", argv[0]);
2581 select_completition(COMPL_MSG);
2582 return 0;
2586 static void shi_load_anything_usage(const char *command) {
2587 oprintf(_(
2588 "Usage: %s FILE\n"
2589 "Load message or message delivery details from local FILE.\n"),
2590 command);
2594 static int shi_load_anything(int argc, const char **argv) {
2595 int fd;
2596 void *buffer = NULL;
2597 size_t length;
2598 isds_raw_type raw_type;
2599 isds_error err;
2600 char *type_name = NULL;
2602 if (!argv || !argv[1] || !*argv[1]) {
2603 shi_load_anything_usage((argv)?argv[0]:NULL);
2604 return -1;
2607 printf(_("Loading file `%s'...\n"), argv[1]);
2609 if (mmap_file(argv[1], &fd, &buffer, &length)) return -1;
2611 printf(_("Detecting file format...\n"));
2612 err = isds_guess_raw_type(cisds, &raw_type, buffer, length);
2613 finish_isds_operation(cisds, err);
2615 if (err) {
2616 if (err == IE_NOTSUP)
2617 fprintf(stderr, _("Unknown format. Could not parse the file.\n"));
2618 else
2619 fprintf(stderr, _("Error while detecting format. "
2620 "Could not parse the file.\n"));
2621 } else {
2622 switch (raw_type) {
2623 case RAWTYPE_INCOMING_MESSAGE:
2624 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
2625 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
2626 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
2627 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
2628 err = isds_load_message(cisds, raw_type,
2629 buffer, length, &message, BUFFER_COPY);
2630 finish_isds_operation(cisds, err);
2631 type_name = N_("message");
2632 break;
2634 case RAWTYPE_DELIVERYINFO:
2635 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO:
2636 case RAWTYPE_CMS_SIGNED_DELIVERYINFO:
2637 err = isds_load_delivery_info(cisds, raw_type,
2638 buffer, length, &message, BUFFER_COPY);
2639 finish_isds_operation(cisds, err);
2640 type_name = N_("delivery");
2641 break;
2643 default:
2644 fprintf(stderr,
2645 _("Unsupported format. Could not parse the file.\n"));
2646 err = IE_NOTSUP;
2650 munmap_file(fd, buffer, length);
2652 if (err) {
2653 set_prompt(NULL);
2654 select_completition(COMPL_COMMAND);
2655 return -1;
2658 format_message(message);
2660 if (message->envelope && message->envelope->dmID)
2661 set_prompt(_("%s %s"), _(type_name), message->envelope->dmID);
2662 else
2663 set_prompt("%s", _(type_name));
2664 select_completition(COMPL_MSG);
2665 return 0;
2669 static void shi_save_message_usage(const char *command) {
2670 oprintf(_(
2671 "Usage: %s FILE\n"
2672 "Save message into local FILE.\n"),
2673 command);
2677 static const char *raw_type2mime(isds_raw_type raw_type) {
2678 switch (raw_type) {
2679 case RAWTYPE_INCOMING_MESSAGE:
2680 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
2681 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
2682 case RAWTYPE_DELIVERYINFO:
2683 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO:
2684 return "text/xml";
2686 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
2687 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
2688 case RAWTYPE_CMS_SIGNED_DELIVERYINFO:
2689 return "application/pkcs7-mime";
2691 default:
2692 return NULL;
2697 static int shi_save_message(int argc, const char **argv) {
2698 _Bool overwrite = cfg_getbool(configuration, CONFIG_OVERWRITEFILES);
2700 if (!argv || !argv[1] || !*argv[1]) {
2701 shi_save_message_usage(argv[0]);
2702 return -1;
2705 if (!message) {
2706 fprintf(stderr, _("No message loaded\n"));
2707 return -1;
2709 if (!message->raw || message->raw_length == 0) {
2710 fprintf(stderr, _("Loaded message is missing raw representation\n"));
2711 return -1;
2714 return save_data_to_file(argv[1], -1, message->raw, message->raw_length,
2715 raw_type2mime(message->raw_type), overwrite);
2719 /* Return document of current message identified by ordinal number expressed
2720 * as string. In case of error return NULL. */
2721 static const struct isds_document *locate_document_by_ordinal_string(
2722 const char *number) {
2723 const struct isds_list *item;
2724 const struct isds_document *document = NULL;
2725 int ordinar, i;
2727 if (!number) return NULL;
2729 ordinar = atoi(number);
2730 if (ordinar <= 0) {
2731 fprintf(stderr, _("%s: Document number must be positive number\n"),
2732 number);
2733 return NULL;
2736 if (!message) {
2737 fprintf(stderr, _("No message loaded\n"));
2738 return NULL;
2741 /* Find document */
2742 for (item = message->documents, i = 0; item; item = item->next) {
2743 if (!item->data) continue;
2744 if (++i == ordinar) {
2745 document = (const struct isds_document *) item->data;
2746 break;
2749 if (i != ordinar) {
2750 fprintf(stderr, _("Message does not contain document #%d\n"), ordinar);
2751 return NULL;
2754 return document;
2758 static void shi_cat_document_usage(const char *command) {
2759 oprintf(_(
2760 "Usage: %s NUMBER\n"
2761 "Print document selected with ordinal NUMBER.\n"),
2762 command);
2765 static int shi_cat_document(int argc, const char **argv) {
2766 const struct isds_document *document;
2768 if (!argv || !argv[1] || !*argv[1] || argc > 3) {
2769 shi_cat_document_usage(argv[0]);
2770 return -1;
2773 document = locate_document_by_ordinal_string(argv[1]);
2774 if (!document) return -1;
2776 if (document->is_xml) {
2777 xmlBufferPtr buffer = NULL;
2778 size_t written;
2780 if (serialize_xml_to_buffer(&buffer, document->xml_node_list))
2781 return -1;
2783 written = owrite(buffer->content, buffer->use);
2784 xmlBufferFree(buffer);
2785 if (written != buffer->use) {
2786 fprintf(stderr, _("Error while printing document content\n"));
2787 return -1;
2789 } else {
2790 if (!document->data || !document->data_length) {
2791 fprintf(stderr, _("Document is missing raw representation\n"));
2792 return -1;
2795 if (owrite(document->data, document->data_length) != document->data_length) {
2796 fprintf(stderr, _("Error while printing document content\n"));
2797 return -1;
2801 return 0;
2805 static void shi_save_document_usage(const char *command) {
2806 oprintf(_(
2807 "Usage: %s NUMBER [DESTINATION]\n"
2808 "Save document having ordinal NUMBER within current message into local file.\n"
2809 "If DESTINATION is file (or does not exist yet), document will be saved into\n"
2810 "this file.\n"
2811 "If DESTINATION is existing directory, file name equaled to document name\n"
2812 "will be saved into DESTINATION.\n"
2813 "If DESTINATION is missing, document name will be used as file name and\n"
2814 "saved into working directory.\n"
2815 "Be aware that document name does not embed malicious characters (slashes).\n"
2816 "\n"
2817 "If the document is a binary stream, image of the document will be copied\n"
2818 "into a file. If the document is a XML document, the XML tree will be serialized\n"
2819 "into a file. If XML document stands for one element or one text node, the node\n"
2820 "(and its children recursively) will be serialized. If XML document compounds\n"
2821 "more nodes or a comment or a processing instruction, parent node from ISDS name\n"
2822 "space will be used to ensure output serialized XML well-formness.\n"
2824 command);
2828 static int shi_save_document(int argc, const char **argv) {
2829 const struct isds_document *document;
2830 const char *dirname = NULL;
2831 char *filename = NULL, *path = NULL;
2832 int retval = 0;
2833 _Bool overwrite = cfg_getbool(configuration, CONFIG_OVERWRITEFILES);
2835 if (!argv || !argv[1] || !*argv[1] || argc > 3) {
2836 shi_save_document_usage(argv[0]);
2837 return -1;
2840 document = locate_document_by_ordinal_string(argv[1]);
2841 if (!document) return -1;
2843 /* Select directory and file name */
2844 if (argv[2] && *argv[2]) {
2845 if (!is_directory(argv[2])) {
2846 dirname = argv[2];
2847 } else {
2848 filename = strdup(argv[2]);
2849 if (!filename) {
2850 fprintf(stderr, _("Not enough memory\n"));
2851 return -1;
2855 if (!filename && document->dmFileDescr && &document->dmFileDescr) {
2856 filename = utf82locale(document->dmFileDescr);
2857 if (!filename) {
2858 fprintf(stderr, _("Not enough memory\n"));
2859 return -1;
2862 if (!filename) {
2863 fprintf(stderr,
2864 _("File name neither supplied, nor document name exists\n"
2865 "Please, supply one.\n"));
2866 return -1;
2869 /* Build path */
2870 if (dirname) {
2871 path = astrcat3(dirname, "/", filename);
2872 zfree(filename);
2873 } else {
2874 path = filename;
2875 filename = NULL;
2877 if (!path) {
2878 fprintf(stderr, _("Not enough memory\n"));
2879 return -1;
2882 /* Save document */
2883 if (document->is_xml)
2884 retval = save_xml_to_file(path, -1, document->xml_node_list,
2885 document->dmMimeType, overwrite);
2886 else
2887 retval = save_data_to_file(path, -1, document->data,
2888 document->data_length, document->dmMimeType, overwrite);
2889 free(path);
2890 return retval;
2894 /* Execute program specified as NULL terminated array of arguments. argv[0] is
2895 * subject of PATH search variable look-up. The program is executed directly,
2896 * it's not a shell command. */
2897 static int execute_system_command(char *const argv[]) { pid_t pid;
2899 if (!argv || !argv[0]) return -1;
2901 pid = fork();
2902 if (pid == -1) {
2903 /* Could not fork */
2904 fprintf(stderr, _("Could not fork\n"));
2905 return -1;
2906 } else if (pid == 0) {
2907 /* Child */
2908 execvp(argv[0], argv);
2909 fprintf(stderr, _("Could not execute:"));
2910 for (char *const *arg = argv; *arg; arg++)
2911 fprintf(stderr, " %s", *arg);
2912 fprintf(stderr, _(": %s\n"), strerror(errno));
2913 return -1;
2914 } else {
2915 /* Wait for the command */
2916 int retval;
2918 if (-1 == waitpid(pid, &retval, 0)) {
2919 fprintf(stderr, _("Could not wait for executed command\n"));
2920 return -1;
2923 if (retval == -1)
2924 fprintf(stderr, _("Exit code of command could not "
2925 "be determined\n"));
2926 else if (WIFEXITED(retval) && WEXITSTATUS(retval))
2927 printf(_("Command exited with code %d\n"),
2928 WEXITSTATUS(retval));
2929 else if (WIFSIGNALED(retval))
2930 printf(_("Command terminated by signal "
2931 "#%d\n"), WTERMSIG(retval));
2932 return retval;
2937 static void shi_open_document_usage(const char *command) {
2938 oprintf(_(
2939 "Usage: %s NUMBER\n"
2940 "Save document having ordinal NUMBER within current message into temporal\n"
2941 "local file, open the file by xdg-open utility and then remove the file.\n"
2943 command);
2947 static int shi_open_document(int argc, const char **argv) {
2948 const struct isds_document *document;
2949 char filename[10] = "shiXXXXXX";
2950 int fd;
2951 char **command = NULL;
2952 int retval = 0;
2954 if (!argv || !argv[1] || !*argv[1] || argc > 3) {
2955 shi_open_document_usage(argv[0]);
2956 return -1;
2959 document = locate_document_by_ordinal_string(argv[1]);
2960 if (!document) return -1;
2962 /* Create temporary file for the document */
2963 fd = mkstemp(filename);
2964 if (fd == -1) {
2965 fprintf(stderr, _("Could not create temporary file: %s\n"), strerror(errno));
2966 return -1;
2969 /* Save document */
2970 if (document->is_xml)
2971 retval = save_xml_to_file(filename, fd, document->xml_node_list,
2972 document->dmMimeType, 0);
2973 else
2974 retval = save_data_to_file(filename, fd, document->data,
2975 document->data_length, document->dmMimeType, 0);
2977 /* Open the file with external utility */
2978 if (!retval) {
2979 /* Construct command arguments to execute */
2980 command = malloc(3 * sizeof(*command));
2981 if (!command) {
2982 fprintf(stderr, _("Error: Not enough memory\n"));
2983 return -1;
2985 command[0] = "xdg-open";
2986 command[1] = filename;
2987 command[2] = NULL;
2989 /* XXX: Do not use system(3) as we cannot escape uknown shell */
2990 retval = execute_system_command(command);
2992 free(command);
2995 /* Remove the file */
2996 remove(filename);
2997 return retval;
3001 static void shi_save_stamp_usage(const char *command) {
3002 oprintf(_(
3003 "Usage: %s FILE\n"
3004 "Save message time stamp into local FILE.\n"),
3005 command);
3009 static int shi_save_stamp(int argc, const char **argv) {
3010 _Bool overwrite = cfg_getbool(configuration, CONFIG_OVERWRITEFILES);
3012 if (!argv || !argv[1] || !*argv[1]) {
3013 shi_save_message_usage(argv[0]);
3014 return -1;
3017 if (!message) {
3018 fprintf(stderr, _("No message loaded\n"));
3019 return -1;
3021 if (!message->envelope || !message->envelope->timestamp||
3022 message->envelope->timestamp_length == 0) {
3023 fprintf(stderr, _("Loaded message is missing time stamp\n"));
3024 return -1;
3027 return save_data_to_file(argv[1], -1,
3028 message->envelope->timestamp, message->envelope->timestamp_length,
3029 "application/timestamp-reply", overwrite);
3033 static int shi_show_list(int argc, const char **argv) {
3034 if (!messages) {
3035 fprintf(stderr, _("No message list loaded\n"));
3036 return -1;
3039 oprintf((messages_are_outgoing) ?
3040 ngettext("You have %'lu outgoing message\n",
3041 "You have %'lu outgoing messages\n", total_messages) :
3042 ngettext("You have %'lu incoming message\n",
3043 "You have %'lu incoming messages\n", total_messages),
3044 total_messages);
3045 print_message_list(messages, messages_are_outgoing);
3046 return 0;
3050 static int shi_list_incoming(int argc, const char **argv) {
3051 isds_error err;
3053 printf(_("Listing incoming messages...\n"));
3054 err = isds_get_list_of_received_messages(cisds, NULL, NULL, NULL,
3055 MESSAGESTATE_ANY, 0, &total_messages, &messages);
3056 finish_isds_operation(cisds, err);
3057 if (err) {
3058 set_prompt(NULL);
3059 select_completition(COMPL_COMMAND);
3060 return -1;
3062 messages_are_outgoing = 0;
3064 shi_show_list(0, NULL);
3066 set_prompt(_("%s %'lu"), argv[0], total_messages);
3067 select_completition(COMPL_LIST);
3068 return 0;
3072 static int shi_list_outgoing(int argc, const char **argv) {
3073 isds_error err;
3075 printf(_("Listing outgoing messages...\n"));
3076 err = isds_get_list_of_sent_messages(cisds, NULL, NULL, NULL,
3077 MESSAGESTATE_ANY, 0, &total_messages, &messages);
3078 finish_isds_operation(cisds, err);
3079 if (err) {
3080 set_prompt(NULL);
3081 select_completition(COMPL_COMMAND);
3082 return -1;
3084 messages_are_outgoing = 1;
3086 shi_show_list(0, NULL);
3088 set_prompt(_("%s %'lu"), argv[0], total_messages);
3089 select_completition(COMPL_LIST);
3090 return 0;
3094 /* Submit document for conversion and print assigned identifier */
3095 static int do_convert(const struct isds_document *document) {
3096 isds_error err;
3097 char *id = NULL;
3098 struct tm *date = NULL;
3100 if (!document) return -1;
3102 printf(_("Submitting document for authorized conversion...\n"));
3104 err = czp_convert_document(czechpoint, document, &id, &date);
3105 finish_isds_operation(czechpoint, err);
3107 if (!err) {
3108 char *name_locale = utf82locale(document->dmFileDescr);
3109 char *date_string = tm2string(date);
3110 char *id_locale = utf82locale(id);
3111 oprintf(_(
3112 "Document submitted for authorized conversion successfully under name\n"
3113 "`%s' on %s.\n"
3114 "Submit identifier assigned by Czech POINT deposit is `%s'.\n"),
3115 name_locale, date_string, id_locale);
3116 free(name_locale);
3117 free(date_string);
3118 free(id_locale);
3119 oprintf(_("Be ware that submitted document has restricted lifetime "
3120 "(30 days).\n"));
3121 oprintf(_("See <%s> for more details.\n"), CZPDEPOSIT_URL);
3124 free(id); free(date);
3125 return (err) ? -1 : 0;
3129 static void shi_convert_file_usage(const char *command) {
3130 oprintf(_(
3131 "Usage: %s FILE [NAME]\n"
3132 "Submit local FILE to authorized conversion under NAME. If NAME is missing,\n"
3133 "it will use FILE name.\n"), command);
3134 oprintf(_(
3135 "\n"
3136 "If Czech POINT deposit accepts document, it will return document identifier\n"
3137 "that user is supposed to provide to officer at Czech POINT contact place.\n"
3138 "Currently only PDF 1.3 and higher version files are accepted.\n"));
3139 oprintf(_("See <%s> for more details.\n"), CZPDEPOSIT_URL);
3143 static int shi_convert_file(int argc, const char **argv) {
3144 int fd;
3145 struct isds_document document;
3146 int retval = 0;
3148 if (!argv || argc > 3 || !argv[1] || !*argv[1]) {
3149 shi_convert_file_usage((argv)?argv[0]:NULL);
3150 return -1;
3153 memset(&document, 0, sizeof(document));
3155 if (argc == 3 && argv[2] && *argv[2])
3156 document.dmFileDescr = locale2utf8(argv[2]);
3157 else
3158 document.dmFileDescr = locale2utf8(argv[1]);
3159 if (!document.dmFileDescr) {
3160 printf(_("Could not convert document name to UTF-8\n"));
3161 return -1;
3164 printf(_("Loading document from file `%s'...\n"), argv[1]);
3165 if (mmap_file(argv[1], &fd, &document.data, &document.data_length)) {
3166 free(document.dmFileDescr);
3167 return -1;
3170 retval = do_convert(&document);
3172 munmap_file(fd, document.data, document.data_length);
3173 free(document.dmFileDescr);
3174 return retval;
3178 static void shi_convert_document_usage(const char *command) {
3179 oprintf(_(
3180 "Usage: %s NUMBER\n"
3181 "Submit message document with ordinal NUMBER to authorized conversion.\n"),
3182 command);
3183 oprintf(_(
3184 "\n"
3185 "If Czech POINT deposit accepts document, it will return document identifier\n"
3186 "that user is supposed to provide to officer at Czech POINT contact place.\n"
3187 "Currently only PDF 1.3 and higher version files are accepted.\n"));
3188 oprintf(_("See <%s> for more details.\n"), CZPDEPOSIT_URL);
3192 static int shi_convert_document(int argc, const char **argv) {
3193 const struct isds_document *document;
3195 if (!argv || !argv[1] || !*argv[1]) {
3196 shi_convert_document_usage((argv)?argv[0]:NULL);
3197 return -1;
3200 document = locate_document_by_ordinal_string(argv[1]);
3201 if (!document) return -1;
3203 return do_convert(document);
3207 #if ENABLE_DEBUG
3208 static void shi_print_usage(const char *command) {
3209 oprintf(_(
3210 "Usage: %s STRING LENGTH\n"
3211 "Prints STRING into LENGTH columns. Negative LENGTH means not to cut\n"
3212 "overflowing string.\n"
3213 "This should be locale and terminal agnostic.\n"),
3214 command);
3218 static int shi_print(int argc, const char **argv) {
3219 long int width;
3221 if (!argv || !argv[1] || !argv[2]) {
3222 shi_print_usage((argv)?argv[0]:NULL);
3223 return -1;
3226 width = strtol(argv[2], NULL, 10);
3227 if (width < INT_MIN) {
3228 fprintf(stderr,
3229 _("Length argument must not lesser than %d.\n"), INT_MIN);
3230 return -1;
3232 if (width > INT_MAX) {
3233 fprintf(stderr,
3234 _("Length argument must not be greater than %d.\n"), INT_MAX);
3235 return -1;
3238 oprintf(_(">"));
3239 onprint(argv[1], width);
3240 oprintf(_("<\n"));
3242 return 0;
3246 static int shi_tokenize(int argc, const char **argv) {
3248 if (!argv) return 0;
3250 for (int i = 0; i < argc; i++) {
3251 oprintf(_(">%s<\n"), argv[i]);
3253 return 0;
3257 static int shi_quote(int argc, const char **argv) {
3258 char *escaped, *unescaped;
3260 if (!argv) return 0;
3262 oprintf(_("Original\tQuoted\tDequoted\n"));
3263 for (int i = 0; i < argc; i++) {
3264 escaped = shi_quote_filename((char *) argv[i], 0, NULL);
3265 unescaped = shi_dequote_filename((char *) argv[i], 0);
3266 oprintf(_(">%s<\t>%s<\t>%s<\n"), argv[i], escaped, unescaped);
3267 free(escaped);
3268 free(unescaped);
3270 return 0;
3272 #endif
3275 /* pclose(pipe), restore ouput to stdout, show error return code */
3276 int wait_for_shell(FILE **pipe) {
3277 int retval = 0;
3279 if (pipe && *pipe) {
3280 retval = pclose(*pipe);
3281 *pipe = NULL;
3282 output = stdout;
3284 if (retval == -1)
3285 fprintf(stderr, _("Exit code of shell command could not "
3286 "be determined\n"));
3287 else if (WIFEXITED(retval) && WEXITSTATUS(retval))
3288 printf(_("Shell command exited with code %d\n"),
3289 WEXITSTATUS(retval));
3290 else if (WIFSIGNALED(retval))
3291 printf(_("Shell command terminated by signal "
3292 "#%d\n"), WTERMSIG(retval));
3295 return retval;
3299 /* Interactive loop */
3300 void shi_loop(void) {
3301 char *command_line = NULL;
3302 char **command_argv = NULL;
3303 int command_argc;
3304 char *shell = NULL;
3305 struct command *command = NULL;
3306 FILE *pipe = NULL;
3307 int failed = 0;
3309 oprintf(_("Use `help' command to get list of available commands.\n"));
3311 select_completition(COMPL_COMMAND);
3312 set_prompt(NULL);
3314 while (1) {
3315 command_line = readline((prompt) ? prompt : _("shigofumi> "));
3316 /* Remember not parsable commands too to user be able to get back to
3317 * fix command */
3318 if (command_line && *command_line) {
3319 /* TODO: Omit blank lines */
3320 add_history(command_line);
3323 command_argv = tokenize(command_line, &command_argc, &shell);
3325 if (command_argv && command_argv[0]) {
3326 command = find_command(command_argv[0]);
3328 if (!command) {
3329 fprintf(stderr, _("Command not understood\n"));
3330 } else {
3331 failed = 0;
3332 if (shell) {
3333 fflush(stdout);
3334 pipe = popen(shell, "w");
3335 if (!pipe) {
3336 fprintf(stderr, _("Could not run shell command `%s':"
3337 " %s\n"), shell, strerror(errno));
3338 failed = 1;
3340 output = pipe;
3342 if (!failed) {
3343 command->function(command_argc,
3344 (const char **) command_argv);
3345 wait_for_shell(&pipe);
3350 argv_free(command_argv);
3351 zfree(shell);
3352 zfree(command_line);
3357 /* Non-interactive mode. Commands from @lines are processed until any command
3358 * lines remains or no error occurred. First failure terminates processing.
3359 * @lines is sequence of commands separated by '\n' or '\r'. The content is
3360 * modified during this call.
3361 * @return 0 if all command succeed, otherwise non-zero value
3363 int shi_batch(char *lines) {
3364 char *command_line;
3365 char **command_argv = NULL;
3366 int command_argc;
3367 char *shell = NULL;
3368 struct command *command = NULL;
3369 int retval = 0;
3370 FILE *pipe = NULL;
3371 int failed = 0;
3373 oprintf(_("Batch mode started.\n"));
3374 batch_mode = 1;
3375 select_completition(COMPL_COMMAND);
3377 while (!retval && (command_line = strtok(lines, "\n\r"))) {
3378 lines = NULL; /* strtok(3) requires it for subsequent calls */
3380 printf(_("Processing command: %s\n"), command_line);
3382 command_argv = tokenize(command_line, &command_argc, &shell);
3384 if (command_argv && command_argv[0]) {
3385 command = find_command(command_argv[0]);
3387 if (!command) {
3388 fprintf(stderr, _("Command not understood\n"));
3389 retval = -1;
3390 } else {
3391 failed = 0;
3392 if (shell) {
3393 fflush(stdout);
3394 pipe = popen(shell, "w");
3395 if (!pipe) {
3396 fprintf(stderr, _("Could not run shell command `%s':"
3397 " %s\n"), shell, strerror(errno));
3398 failed = 1;
3400 output = pipe;
3402 if (!failed) {
3403 retval = command->function(command_argc,
3404 (const char **) command_argv);
3405 if (wait_for_shell(&pipe)) retval = -1;
3410 argv_free(command_argv);
3411 zfree(shell);
3414 if (retval)
3415 fprintf(stderr, _("Command failed!\n"));
3416 return retval;
3420 #define COMMON_COMMANDS \
3421 { "accept", shi_accept_message, N_("accept commercial message"), \
3422 shi_accept_message_usage, ARGTYPE_MSGID }, \
3423 { "box", shi_box, N_("show current box details"), NULL, \
3424 ARGTYPE_NONE }, \
3425 { "cache", shi_cache, N_("show cache details"), NULL, \
3426 ARGTYPE_NONE }, \
3427 { "cd", shi_chdir, N_("change working directory"), shi_chdir_usage, \
3428 ARGTYPE_FILE }, \
3429 { "commercial", shi_commercial, \
3430 N_("manipulate commercial receiving box status"), \
3431 shi_commercial_usage, ARGTYPE_BOXID }, \
3432 { "compose", shi_compose, N_("compose a message"), shi_compose_usage, \
3433 ARGTYPE_FILE }, \
3434 { "convert", shi_convert_file, \
3435 N_("submit local document for authorized conversion"), \
3436 shi_convert_file_usage, ARGTYPE_FILE }, \
3437 { "copying", shi_copying, N_("show this program licence excerpt"), NULL, \
3438 ARGTYPE_NONE }, \
3439 { "debug", shi_debug, N_("set debugging"), shi_debug_usage, \
3440 ARGTYPE_FILE }, \
3441 { "delivery", shi_delivery, N_("get message delivery details"), \
3442 shi_delivery_usage, ARGTYPE_MSGID }, \
3443 { "findbox", shi_find_box, N_("search for a box"), shi_find_box_usage, \
3444 ARGTYPE_BOXID }, \
3445 { "hash", shi_hash, N_("query ISDS for message hash"), \
3446 shi_hash_usage, ARGTYPE_MSGID }, \
3447 { "help", shi_help, N_("describe commands"), shi_help_usage, \
3448 ARGTYPE_COMMAND }, \
3449 { "load", shi_load_anything, \
3450 N_("load message or message delivery details from local file"), \
3451 shi_load_anything_usage, ARGTYPE_FILE }, \
3452 { "login", shi_login, N_("log into ISDS"), NULL, ARGTYPE_NONE }, \
3453 { "lsi", shi_list_incoming, N_("list received messages"), NULL, \
3454 ARGTYPE_NONE }, \
3455 { "lso", shi_list_outgoing, N_("list sent messages"), NULL, \
3456 ARGTYPE_NONE }, \
3457 { "msgi", shi_incoming_message, N_("get incoming message"), \
3458 shi_incoming_message_usage, ARGTYPE_MSGID }, \
3459 { "msgo", shi_outgoing_message, N_("get outgoing message"), \
3460 shi_outgoing_message_usage, ARGTYPE_MSGID }, \
3461 { "passwd", shi_passwd, N_("manipulate user password"), shi_passwd_usage, \
3462 ARGTYPE_NONE }, \
3463 { "pwd", shi_pwd, N_("print working directory"), NULL, ARGTYPE_NONE }, \
3464 { "quit", shi_quit, N_("exit shigofumi"), NULL, ARGTYPE_NONE }, \
3465 { "read", shi_read_message, N_("mark message as read"), \
3466 shi_read_message_usage, ARGTYPE_MSGID }, \
3467 { "set", shi_settings, N_("show settings"), NULL, ARGTYPE_NONE }, \
3468 { "statbox", shi_stat_box, N_("get status of a box"), shi_stat_box_usage, \
3469 ARGTYPE_BOXID }, \
3470 { "user", shi_user, N_("show current user details"), NULL, \
3471 ARGTYPE_NONE }, \
3472 { "users", shi_users, N_("show box users"), shi_users_usage, \
3473 ARGTYPE_NONE }, \
3474 { "version", shi_version, N_("show version of this program"), NULL, \
3475 ARGTYPE_NONE}, \
3476 { NULL, NULL, NULL, NULL, ARGTYPE_NONE }
3478 struct command base_commands[] = {
3479 #if ENABLE_DEBUG
3480 { "quote", shi_quote, N_("demonstrate argument escaping"), NULL,
3481 ARGTYPE_FILE },
3482 { "print", shi_print, N_("print string into given width"),
3483 shi_print_usage, ARGTYPE_NONE },
3484 { "tokenize", shi_tokenize, N_("demonstrate arguments tokenization"), NULL,
3485 ARGTYPE_FILE },
3486 #endif
3487 COMMON_COMMANDS
3490 struct command message_commands[] = {
3491 { "authenticate", shi_authenticate, N_("check message authenticity"),
3492 NULL, ARGTYPE_NONE },
3493 { "cat", shi_cat_message, N_("show raw current message"),
3494 shi_cat_message_usage, ARGTYPE_NONE },
3495 { "catdoc", shi_cat_document, N_("show raw document"),
3496 shi_cat_document_usage, ARGTYPE_DOCID },
3497 { "convertdoc", shi_convert_document,
3498 N_("submit document of current message for authorized conversion"),
3499 shi_convert_document_usage, ARGTYPE_DOCID },
3500 { "dump", shi_dump_message, N_("dump current message structure"),
3501 NULL, ARGTYPE_NONE },
3502 { "opendoc", shi_open_document, N_("open document using external utility"),
3503 shi_open_document_usage, ARGTYPE_DOCID },
3504 { "savestamp", shi_save_stamp,
3505 N_("save time stamp of current message into local file"),
3506 shi_save_stamp_usage, ARGTYPE_FILE },
3507 { "savedoc", shi_save_document,
3508 N_("save document of current message into local file"),
3509 shi_save_document_usage, ARGTYPE_FILE },
3510 { "save", shi_save_message, N_("save current message into local file"),
3511 shi_save_message_usage, ARGTYPE_FILE },
3512 { "show", shi_show_message, N_("show current message"), NULL,
3513 ARGTYPE_NONE },
3514 { "verify", shi_verify, N_("verify current message hash"), NULL,
3515 ARGTYPE_NONE },
3516 COMMON_COMMANDS
3519 struct command list_commands[] = {
3520 { "show", shi_show_list, N_("show current message list"), NULL,
3521 ARGTYPE_NONE },
3522 COMMON_COMMANDS
3525 #undef COMMON_COMMANDS
3528 static void main_version(void) {
3529 isds_init();
3530 show_version();
3531 isds_cleanup();
3532 printf("\n");
3533 shi_copying(0, NULL);
3537 static void main_usage(const char *command) {
3538 oprintf(_(
3539 "Usage: %s [OPTION...]\n"
3540 "Access ISDS, process local data box messages or delivery details, submit\n"
3541 "document to authorized conversion.\n"
3542 "\n"
3543 "Options:\n"
3544 " -c FILE use the FILE as configuration file instead of ~/%s\n"
3545 " -e COMMANDS execute COMMANDS (new line separated) and exit\n"
3546 " -V show version info and exit\n"
3548 (command) ? command : "shigofumi",
3549 CONFIG_FILE);
3553 int main(int argc, char **argv) {
3554 int opt;
3555 char *config_file = NULL;
3556 char *batch_commands = NULL;
3557 int retval = EXIT_SUCCESS;
3559 setlocale(LC_ALL, "");
3560 #if ENABLE_NLS
3561 /* Initialize gettext */
3562 bindtextdomain(PACKAGE, LOCALEDIR);
3563 textdomain(PACKAGE);
3564 #endif
3566 /* Default output */
3567 output = stdout;
3569 /* Parse arguments */
3570 optind = 0;
3571 while ((opt = getopt(argc, (char * const *)argv, "c:e:V")) != -1) {
3572 switch (opt) {
3573 case 'c':
3574 config_file = optarg;
3575 break;
3576 case 'e':
3577 batch_commands = optarg;
3578 break;
3579 case 'V':
3580 main_version();
3581 shi_exit(EXIT_SUCCESS);
3582 break;
3583 default:
3584 main_usage((argv[0]) ? basename(argv[0]): NULL);
3585 shi_exit(EXIT_FAILURE);
3590 if (shi_init(config_file)) {
3591 shi_exit(EXIT_FAILURE);
3594 /*shi_login(NULL);*/
3596 if (batch_commands) {
3597 if (shi_batch(batch_commands))
3598 retval = EXIT_FAILURE;
3599 } else {
3600 shi_loop();
3603 shi_exit(retval);