Rename a source file in POTFILES.in
[shigofumi.git] / src / shigofumi.c
blob20d70a4dfb21e74607524f43e41aa34322180eba
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/stat.h>
17 #include <sys/types.h>
18 #include <sys/wait.h>
19 #include <strings.h>
20 #include <time.h>
22 #include "shigofumi.h"
23 #include "completion.h"
24 #include "data.h"
25 #include "io.h"
26 #include "ui.h"
27 #include "utils.h"
29 #define CONFIG_FILE ".shigofumirc"
30 #define CONFIG_SERVER "base_url"
31 #define CONFIG_USERNAME "username"
32 #define CONFIG_PASSWORD "password"
33 #define CONFIG_CERT_FORMAT "certificate_format"
34 #define CONFIG_CERT_PATH "certificate_path"
35 #define CONFIG_KEY_ENGINE "key_engine"
36 #define CONFIG_KEY_FORMAT "key_format"
37 #define CONFIG_KEY_PATH "key_path"
38 #define CONFIG_KEY_PASSWORD "key_password"
39 #define CONFIG_OTP_METHOD "otp_method"
40 #define CONFIG_OTP_CODE "otp_code"
41 #define CONFIG_DEBUGLEVEL "debug_level"
42 #define CONFIG_VERIFYSERVER "verify_server"
43 #define CONFIG_CAFILE "ca_file"
44 #define CONFIG_CADIRECTORY "ca_directory"
45 #define CONFIG_CLEAN_TEMPORARY_FILES "clean_temporary_files"
46 #define CONFIG_CONFIRM_SEND "confirm_send"
47 #define CONFIG_CRLFILE "crl_file"
48 #define CONFIG_TIMEOUT "timeout"
49 #define CONFIG_LOGFACILITIES "log_facilities"
50 #define CONFIG_LOGFILE "log_file"
51 #define CONFIG_LOGLEVEL "log_level"
52 #define CONFIG_MARKMESSAGEREAD "mark_message_read"
53 #define CONFIG_NORMALIZEMIMETYPE "normalize_mime_type"
54 #define CONFIG_OPENCOMMAND "open_command"
55 #define CONFIG_OVERWRITEFILES "overwrite_files"
56 #define CZPDEPOSIT_URL "https://www.czechpoint.cz/uschovna/"
58 #define TIMEOUT 10000
59 #define LOG_LEVEL 20
61 /* Configuration */
62 cfg_opt_t configuration_syntax[] = {
63 CFG_STR(CONFIG_SERVER, NULL, CFGF_NONE),
64 CFG_STR(CONFIG_USERNAME, NULL, CFGF_NODEFAULT),
65 CFG_STR(CONFIG_PASSWORD, NULL, CFGF_NODEFAULT),
66 CFG_STR(CONFIG_CERT_FORMAT, NULL, CFGF_NODEFAULT),
67 CFG_STR(CONFIG_CERT_PATH, NULL, CFGF_NODEFAULT),
68 CFG_STR(CONFIG_KEY_ENGINE, NULL, CFGF_NODEFAULT),
69 CFG_STR(CONFIG_KEY_FORMAT, NULL, CFGF_NODEFAULT),
70 CFG_STR(CONFIG_KEY_PATH, NULL, CFGF_NODEFAULT),
71 CFG_STR(CONFIG_KEY_PASSWORD, NULL, CFGF_NODEFAULT),
72 CFG_STR(CONFIG_OTP_METHOD, NULL, CFGF_NODEFAULT),
73 CFG_STR(CONFIG_OTP_CODE, NULL, CFGF_NODEFAULT),
74 /*CFG_STR(CONFIG_DEBUGLEVEL, NULL, CFGF_NODEFAULT),*/
75 CFG_BOOL(CONFIG_VERIFYSERVER, cfg_true, CFGF_NONE),
76 CFG_STR(CONFIG_CAFILE, NULL, CFGF_NODEFAULT),
77 CFG_STR(CONFIG_CADIRECTORY, NULL, CFGF_NODEFAULT),
78 CFG_BOOL(CONFIG_CLEAN_TEMPORARY_FILES, cfg_true, CFGF_NONE),
79 CFG_STR(CONFIG_CRLFILE, NULL, CFGF_NODEFAULT),
80 CFG_INT(CONFIG_TIMEOUT, TIMEOUT, CFGF_NONE),
81 CFG_STR_LIST(CONFIG_LOGFACILITIES, "{none}", CFGF_NONE),
82 CFG_STR(CONFIG_LOGFILE, NULL, CFGF_NODEFAULT),
83 CFG_INT(CONFIG_LOGLEVEL, LOG_LEVEL, CFGF_NONE),
84 CFG_BOOL(CONFIG_CONFIRM_SEND, cfg_true, CFGF_NONE),
85 CFG_BOOL(CONFIG_MARKMESSAGEREAD, cfg_false, CFGF_NONE),
86 CFG_BOOL(CONFIG_NORMALIZEMIMETYPE, cfg_true, CFGF_NONE),
87 CFG_STR_LIST(CONFIG_OPENCOMMAND, "{xdg-open, %f}", CFGF_NONE),
88 CFG_BOOL(CONFIG_OVERWRITEFILES, cfg_true, CFGF_NONE),
89 CFG_END()
91 cfg_t *configuration;
93 /* Logger */
94 int logger_fd = -1;
96 /* UI */
97 _Bool batch_mode = 0;
98 char *prompt = NULL;
99 struct command (*commands)[] = NULL;
100 FILE *output = NULL;
102 /* Data */
103 struct isds_ctx *cisds = NULL;
104 struct isds_list *boxes = NULL;
105 struct isds_message *message = NULL;
106 _Bool messages_are_outgoing = 0;
107 struct isds_list *messages = NULL;
108 unsigned long int total_messages = 0;
109 struct isds_ctx *czechpoint = NULL;
110 struct isds_list *temporary_files = NULL;
112 /* Temporary log-in settings */
113 char *server = NULL;
114 char *username = NULL;
115 char *password = NULL;
116 char *key_password = NULL;
117 char *otp_method = NULL;
118 char *otp_code = NULL;
119 char *pki_engine = NULL;
120 char *pki_certificate_path = NULL;
121 char *pki_certificate_format = NULL;
122 char *pki_key_path = NULL;
123 char *pki_key_format = NULL;
125 static void discard_credentials(void) {
126 zfree(server);
127 zfree(username);
128 zfree(password);
129 zfree(key_password);
130 zfree(otp_method);
131 zfree(otp_code);
132 zfree(pki_engine);
133 zfree(pki_certificate_path);
134 zfree(pki_certificate_format);
135 zfree(pki_key_path);
136 zfree(pki_key_format);
140 /* Remove temporary file */
141 void shi_unlink_temporary_file(void **data) {
142 if (!data || !*data) return;
144 const char *file = (const char *)*data;
145 unlink_file(file);
146 zfree(*data);
150 /* Finish ISDS operation, report error, if the operation returned different
151 * code than @positive_code. */
152 static void finish_isds_operation_with_code(struct isds_ctx *ctx,
153 isds_error err, isds_error positive_code) {
154 shi_progressbar_finish();
155 if (err != positive_code) {
156 if (isds_long_message(ctx))
157 fprintf(stderr, _("Error occurred: %s: %s\n"), isds_strerror(err),
158 isds_long_message(ctx));
159 else
160 fprintf(stderr, _("Error occurred: %s\n"), isds_strerror(err));
165 /* Finish ISDS operation, report error, if the operation did not returned
166 * IE_SUCCESS. */
167 static void finish_isds_operation(struct isds_ctx *ctx, isds_error err) {
168 finish_isds_operation_with_code(ctx, err, IE_SUCCESS);
172 /* Do the cleanup and exit */
173 static void shi_exit(int exit_code) {
174 /* Data */
175 discard_credentials();
176 isds_list_free(&boxes);
177 isds_message_free(&message);
178 isds_list_free(&messages);
179 if (temporary_files) {
180 oprintf(_("Removing temporary files...\n"));
181 isds_list_free(&temporary_files);
184 if (cisds) {
185 isds_error err;
186 oprintf(_("Logging out...\n"));
187 err = isds_logout(cisds);
188 finish_isds_operation(cisds, err);
189 if (err) exit_code = EXIT_FAILURE;
190 isds_ctx_free(&cisds);
192 isds_ctx_free(&czechpoint);
193 isds_cleanup();
195 /* Configuration */
196 cfg_free(configuration);
198 /* UI */
199 free(prompt);
200 free(commands);
202 exit(exit_code);
205 /* Set prompt. if @format is NULL, switch to default prompt */
206 static void set_prompt(const char *format, ...) {
207 char *buffer = NULL;
208 va_list ap;
209 if (format) {
210 va_start(ap, format);
211 shi_vasprintf(&buffer, format, ap);
212 va_end(ap);
214 if (buffer) {
215 shi_asprintf(&prompt, _("%s> "), buffer);
216 if (prompt) {
217 free(buffer);
218 return;
222 free(buffer);
223 free(prompt);
224 prompt = strdup(_("> "));
225 return;
228 zfree(prompt);
232 static int shi_load_configuration(const char *config_file) {
233 char *config_name = NULL;
234 int ret;
236 /* Get config file */
237 if (config_file) {
238 config_name = (char *) config_file;
239 } else {
240 if (-1 == shi_asprintf(&config_name, "%s/%s", getenv("HOME"),
241 CONFIG_FILE)) {
242 fprintf(stderr, _("Could not build configuration file name\n"));
243 return -1;
247 /* Parse configuration */
248 configuration = cfg_init(configuration_syntax, CFGF_NONE);
249 ret = cfg_parse(configuration, config_name);
250 if (ret) {
251 if (ret == CFG_FILE_ERROR) {
252 fprintf(stderr,
253 _("Error while opening configuration file `%s': %s\n"),
254 config_name, strerror(errno));
255 } else {
256 fprintf(stderr, _("Error while parsing configuration file `%s'\n"),
257 config_name);
259 oprintf(_("Using default configuration\n"));
262 if (config_name != config_file) free(config_name);
263 return 0;
267 void logger(isds_log_facility facility, isds_log_level level,
268 const char *message, int length, void *data) {
269 int fd;
270 ssize_t written, left = length;
272 if (!data) return;
273 fd = *((int *) data);
274 /*printf("\033[32mLOG(%02d,%02d): ", facility, level);
275 printf("%.*s", length, message);
276 printf("\033[m");*/
278 while (left) {
279 written = write(fd, message + length - left, left);
280 if (written == -1) {
281 fprintf(stderr,
282 _("Could not save log message into log file: %s\n"
283 "Log message discarded!\n"),
284 strerror(errno));
285 /*close(fd);
286 fd = -1;*/
287 return;
289 left-=written;
294 /* Redirect ISDS log to file if @file is not NULL. */
295 static int do_log_to_file(const char *file) {
296 if (file && *file) {
297 logger_fd = open_file_for_writing(file, 0, 1);
298 if (logger_fd == -1) {
299 fprintf(stderr, _("Could not redirect ISDS log to file `%s'\n"),
300 file);
301 return -1;
303 isds_set_log_callback(logger, &logger_fd);
305 return 0;
309 /* Add log facility based on its name. */
310 static int add_log_facility(isds_log_facility *facilities, const char *name) {
311 if (!facilities) return -1;
313 if (!strcmp(name, "none")) *facilities |= ILF_NONE;
314 else if (!strcmp(name, "http")) *facilities |= ILF_HTTP;
315 else if (!strcmp(name, "soap")) *facilities |= ILF_SOAP;
316 else if (!strcmp(name, "isds")) *facilities |= ILF_ISDS;
317 else if (!strcmp(name, "file")) *facilities |= ILF_FILE;
318 else if (!strcmp(name, "sec")) *facilities |= ILF_SEC;
319 else if (!strcmp(name, "xml")) *facilities |= ILF_XML;
320 else if (!strcmp(name, "all")) *facilities |= ILF_ALL;
321 else {
322 fprintf(stderr, _("%s: Unknown log facility\n"), name);
323 return -1;
326 return 0;
330 /* Save log facility into confuse configuration */
331 static void save_log_facility(int level) {
332 cfg_setlist(configuration, CONFIG_LOGFACILITIES, 0);
334 if (level == ILF_ALL) {
335 cfg_addlist(configuration, CONFIG_LOGFACILITIES, 1, "all");
336 return;
338 if (level == ILF_NONE) {
339 cfg_addlist(configuration, CONFIG_LOGFACILITIES, 1, "none");
340 return;
342 if (level & ILF_HTTP)
343 cfg_addlist(configuration, CONFIG_LOGFACILITIES, 1, "http");
344 if (level & ILF_SOAP)
345 cfg_addlist(configuration, CONFIG_LOGFACILITIES, 1, "soap");
346 if (level & ILF_ISDS)
347 cfg_addlist(configuration, CONFIG_LOGFACILITIES, 1, "isds");
348 if (level & ILF_FILE)
349 cfg_addlist(configuration, CONFIG_LOGFACILITIES, 1, "file");
350 if (level & ILF_SEC)
351 cfg_addlist(configuration, CONFIG_LOGFACILITIES, 1, "sec");
352 if (level & ILF_XML)
353 cfg_addlist(configuration, CONFIG_LOGFACILITIES, 1, "xml");
357 /* Clamp long int to unsigned int */
358 static unsigned int normalize_timeout(long int raw) {
359 if (raw < 0) {
360 oprintf(_("Configured network timeout is less then 0. "
361 "Clamped to 0.\n"));
362 return 0;
364 if (raw > UINT_MAX ) {
365 oprintf(_("Configured network timeout is greater then %1$u. "
366 "Clamped to %1$u.\n"), UINT_MAX);
367 return UINT_MAX;
369 return (unsigned int) raw;
373 /* Clamp long int to <0;100> */
374 static unsigned int normalize_log_level(long int raw) {
375 if (raw < 0) {
376 oprintf(_("Configured log level is less then 0. Clamped to 0.\n"));
377 return 0;
379 if (raw > ILL_ALL) {
380 oprintf(_("Configured log level is greater then %1$u. "
381 "Clamped to %1$u.\n"), ILL_ALL);
382 return ILL_ALL;
384 if (raw > UINT_MAX ) {
385 oprintf(_("Configured log level is greater then %1$u. "
386 "Clamped to %1$u.\n"), UINT_MAX);
387 return UINT_MAX;
389 return (unsigned int) raw;
393 static int shi_init(const char *config_file) {
394 isds_error err;
395 char *value;
396 unsigned int timeout, log_level;
397 isds_log_facility log_facility = ILF_NONE;
399 oprintf(_("This is Shigofumi, an ISDS client. "
400 "Have a nice e-government.\n"));
402 /* Do not permute arguments in getopt() */
403 if (setenv("POSIXLY_CORRECT", "", 1)) {
404 fprintf(stderr,
405 _("Could not set POSIXLY_CORRECT environment variable\n"));
406 return -1;
409 /* Load configuration */
410 if (shi_load_configuration(config_file))
411 return -1;
412 timeout = normalize_timeout(cfg_getint(configuration, CONFIG_TIMEOUT));
413 log_level = normalize_log_level(cfg_getint(configuration, CONFIG_LOGLEVEL));
415 /* Init readline */
416 rl_readline_name = "shigofumi";
417 rl_filename_quote_characters = "\\ >";
418 rl_filename_quoting_function = shi_quote_filename;
419 rl_filename_dequoting_function = shi_dequote_filename;
420 rl_char_is_quoted_p = shi_char_is_quoted;
422 /* Initialize ISDS */
423 err = isds_init();
424 if (err) {
425 fprintf(stderr, _("Could not initialize libisds library: %s\n"),
426 isds_strerror(err));
427 return -1;
430 /* Set ISDS logging */
431 value = cfg_getstr(configuration, CONFIG_LOGFILE);
432 if (do_log_to_file(value))
433 return -1;
434 for (int i = 0; i < cfg_size(configuration, CONFIG_LOGFACILITIES); i++) {
435 if (add_log_facility(&log_facility,
436 cfg_getnstr(configuration, CONFIG_LOGFACILITIES, i)))
437 return -1;
440 isds_set_logging(log_facility, log_level);
442 /* Set ISDS context up */
443 cisds = isds_ctx_create();
444 if (!cisds) {
445 fprintf(stderr, _("Could not create ISDS context\n"));
446 return -1;
448 err = isds_set_timeout(cisds, timeout);
449 if (err) {
450 fprintf(stderr, _("Could not set ISDS network timeout: %s\n"),
451 isds_strerror(err));
453 err = isds_set_progress_callback(cisds, shi_progressbar, NULL);
454 if (err) {
455 fprintf(stderr, _("Could not register network progress bar: %s: %s\n"),
456 isds_strerror(err), isds_long_message(cisds));
458 err = isds_set_opt(cisds, IOPT_NORMALIZE_MIME_TYPE,
459 cfg_getbool(configuration, CONFIG_NORMALIZEMIMETYPE));
460 if (err) {
461 fprintf(stderr,
462 cfg_getbool(configuration, CONFIG_NORMALIZEMIMETYPE) ?
463 _("Could not enable MIME type normalization: %s: %s\n") :
464 _("Could not disable MIME type normalization: %s: %s\n"),
465 isds_strerror(err), isds_long_message(cisds));
467 if (!cfg_getbool(configuration, CONFIG_VERIFYSERVER)) {
468 oprintf(_("Warning: Shigofumi disabled server identity verification "
469 "on user request!\n"));
470 err = isds_set_opt(cisds, IOPT_TLS_VERIFY_SERVER, 0);
471 if (err) {
472 fprintf(stderr,
473 _("Could not disable server identity verification: "
474 "%s: %s\n"),
475 isds_strerror(err), isds_long_message(cisds));
478 if ((value = cfg_getstr(configuration, CONFIG_CAFILE))) {
479 err = isds_set_opt(cisds, IOPT_TLS_CA_FILE, value);
480 if (err) {
481 fprintf(stderr,
482 _("Could not set file with CA certificates: %s: %s: %s\n"),
483 value, isds_strerror(err), isds_long_message(cisds));
486 if ((value = cfg_getstr(configuration, CONFIG_CADIRECTORY))) {
487 err = isds_set_opt(cisds, IOPT_TLS_CA_DIRECTORY, value);
488 if (err) {
489 fprintf(stderr,
490 _("Could not set directory with CA certificates: "
491 "%s: %s: %s\n"),
492 value, isds_strerror(err), isds_long_message(cisds));
495 if ((value = cfg_getstr(configuration, CONFIG_CRLFILE))) {
496 err = isds_set_opt(cisds, IOPT_TLS_CRL_FILE, value);
497 if (err) {
498 fprintf(stderr, _("Could not set file with CRL: %s: %s: %s\n"),
499 value, isds_strerror(err), isds_long_message(cisds));
504 /* Set Czech POINT context up */
505 czechpoint = isds_ctx_create();
506 if (!czechpoint) {
507 fprintf(stderr, _("Could not create Czech POINT context\n"));
508 return -1;
510 err = isds_set_timeout(czechpoint, timeout);
511 if (err) {
512 fprintf(stderr, _("Could not set Czech POINT network timeout: %s\n"),
513 isds_strerror(err));
515 err = isds_set_progress_callback(czechpoint, shi_progressbar, NULL);
516 if (err) {
517 fprintf(stderr, "Could not register network progress bar: %s: %s\n",
518 isds_strerror(err), isds_long_message(cisds));
521 return 0;
525 static int shi_quit(int argc, const char **argv) {
526 shi_exit(EXIT_SUCCESS);
527 return 0;
532 static void shi_help_usage(const char *command) {
533 oprintf(_(
534 "Usage: %s [COMMAND]\n"
535 "Show COMMAND manual or list of currently available commands.\n"
537 command);
541 static int shi_help(int argc, const char **argv) {
542 size_t command_width = 14;
543 int i;
545 if (!commands) {
546 fprintf(stderr, _("No command is available\n"));
547 return -1;
550 if (argc == 2 && argv[1] && *argv[1]) {
551 /* Show usage for given command */
552 for (i = 0; (*commands)[i].name; i++) {
553 if (!strcmp((*commands)[i].name, argv[1])) {
554 if ((*commands)[i].usage)
555 (*commands)[i].usage((*commands)[i].name);
556 else if ((*commands)[i].description) {
557 ohprint((*commands)[i].name, command_width);
558 oprintf(" %s\n", _((*commands)[i].description));
560 else
561 fprintf(stderr,
562 _("%s: %s: Command description not defined\n"),
563 argv[0], argv[1]);
564 return 0;
567 fprintf(stderr, _("%s: %s: No such command exists\n"), argv[0], argv[1]);
568 return -1;
571 /* Or list all commands */
572 oprintf(_("Following commands are available:\n"));
573 for (i = 0; (*commands)[i].name; i++) {
574 ohprint((*commands)[i].name, command_width);
575 oprintf(" %s\n", _((*commands)[i].description));
578 return 0;
582 static void show_version(void) {
583 char *libisds_version = isds_version();
585 oprintf(_("This is Shigofumi version %s.\n"), PACKAGE_VERSION);
586 oprintf(_("\n"
587 "Used libraries\n"
588 "confuse: %s\n"
589 "libisds: %s\n"
590 "libxml2: %s\n"
591 "Readline: %s\n"
593 confuse_version, libisds_version, xmlParserVersion,
594 rl_library_version);
595 free(libisds_version);
599 static int shi_version(int argc, const char **argv) {
600 show_version();
601 oprintf(_(
602 "\n"
603 "-----\n"
604 "It's a shigofumi. A letter delivered from the afterlife. (Fumika)\n"
605 "A message can not be delivered to dead person. (ISDS specification)\n"
606 "Virtual and real world. They can be compatible. (Program author)\n"
608 return 0;
612 static int shi_copying(int argc, const char **argv) {
613 oprintf(_(
614 "This is Shigofumi, an ISDS client.\n"
615 "Copyright (C) 2010, 2011, 2012, 2013 Petr Pisar\n"
616 "\n"
617 "This program is free software: you can redistribute it and/or modify\n"
618 "it under the terms of the GNU General Public License as published by\n"
619 "the Free Software Foundation, either version 3 of the License, or\n"
620 "(at your option) any later version.\n"
621 "\n"
622 "This program is distributed in the hope that it will be useful,\n"
623 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
624 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
625 "GNU General Public License for more details.\n"
626 "\n"
627 "You should have received a copy of the GNU General Public License\n"
628 "along with this program. If not, see <http://www.gnu.org/licenses/>.\n"
630 return 0;
634 static int shi_cache(int argc, const char **argv) {
635 const struct isds_list *item;
636 size_t i;
638 if (boxes) {
639 for (item = boxes, i = 0; item; item = item->next, i++);
640 oprintf(_(
641 "Cached box list: %zu\n"),
645 if (messages) {
646 oprintf(_(
647 "Cached message list:\n"
648 "\tDirection: %s\n"
649 "\tMessages: %'lu\n"),
650 (messages_are_outgoing) ? _("Outgoing") : _("Incoming"),
651 total_messages);
654 if (message) {
655 oprintf(_("Cached message: %s\n"),
656 (message->envelope && message->envelope->dmID) ?
657 message->envelope->dmID : _("<Unknown ID>"));
660 return 0;
664 static void shi_chdir_usage(const char *command) {
665 oprintf(_(
666 "Usage: %s [DIRECTORY]\n"
667 "Change working directory to DIRECTORY.\n"
668 "If no DIRECTORY is supplied, HOME directory will be used.\n"),
669 command);
673 static int shi_chdir(int argc, const char **argv) {
674 const char *directory = NULL;
676 if (!argv || argc > 2) {
677 shi_chdir_usage((argv) ? argv[0] : NULL);
678 return -1;
681 if (argc == 2 && argv[1] && *argv[1])
682 directory = argv[1];
683 else {
684 directory = getenv("HOME");
685 if (!directory) {
686 oprintf("Environment variable HOME does not exist\n");
687 return -1;
690 if (chdir(directory)) {
691 oprintf(_("Could not change working directory: %s: %s\n"), directory,
692 strerror(errno));
693 return -1;
696 return 0;
700 static int shi_pwd(int argc, const char **argv) {
701 char *buffer = NULL, *newbuffer;
702 size_t length = 0;
704 while (length += 1024) {
705 newbuffer = realloc(buffer, length);
706 if (!newbuffer) {
707 fprintf(stderr, _("Error: Not enough memory\n"));
708 free(buffer);
709 return -1;
711 buffer = newbuffer;
713 if (getcwd(buffer, length)) {
714 oprintf("%s\n", buffer);
715 free(buffer);
716 return 0;
720 fprintf(stderr, _("Error: Current directory string is too long\n"));
721 free(buffer);
722 return -1;
726 /* Deallocate *@destination and duplicate @new_value if non-NULL. In case of
727 * error, it prints error message and returns -1. Otherwise it returns 0. */
728 static int replace_string(char **destination, const char *new_value) {
729 if (destination == NULL) return -1;
730 zfree(*destination);
731 if (new_value != NULL) {
732 *destination = strdup(new_value);
733 if (*destination == NULL) {
734 fprintf(stderr, _("Not enough memory\n"));
735 return -1;
738 return 0;
742 /* Convert name of PKI format into ISDS format type.
743 * Return -1 in case of invalid name */
744 static int string2pki_format(const char *name, isds_pki_format *format) {
745 if (!name || !format) { return -1; }
746 if (!strcasecmp(name, "PEM")) {
747 *format = PKI_FORMAT_PEM;
748 } else if (!strcasecmp(name, "DER")) {
749 *format = PKI_FORMAT_DER;
750 } else if (!strcasecmp(name, "ENG")) {
751 *format = PKI_FORMAT_ENG;
752 } else {
753 return -1;
755 return 0;
759 /* Convert name of OTP authentication method into ISDS method type.
760 * Return -1 in case of invalid name */
761 static int string2otp_method(const char *name, isds_otp_method *method) {
762 if (!name || !method) { return -1; }
763 if (!strcasecmp(name, "HOTP")) {
764 *method = OTP_HMAC;
765 } else if (!strcasecmp(name, "TOTP")) {
766 *method = OTP_TIME;
767 } else {
768 return -1;
770 return 0;
774 /* Log-in to ISDS.
775 * Return -1 in case of failure, 0 in case of success, +1 in case of partial
776 * success (e.g. TOTP preauthentication to obtain new code succeeded, but user
777 * is still not logged in because second phase is necessary). */
778 static int do_login(void) {
779 isds_error err;
780 struct isds_pki_credentials pki;
781 struct isds_otp otp;
783 /* Build OTP structure */
784 if (NULL != otp_method) {
785 if (string2otp_method(otp_method, &otp.method)) {
786 fprintf(stderr, _("Error: Invalid one-time password "
787 "authentication method `%s'\n"), otp_method);
788 return -1;
792 /* Announce base URL */
793 oprintf(_("ISDS base URL: %s\n"),
794 (server == NULL) ? _("<default>") : server);
796 if (batch_mode) {
797 oprintf(_("Unattended mode detected. "
798 "Make sure credentials have been preset.\n"));
799 } else {
800 oprintf(_("You are going to insert credentials for your account.\n"
801 "Leave blank line to choose default value.\n"));
803 select_completion(COMPL_NONE);
805 /* Ask for user name if not predefined */
806 if (NULL == username) {
807 shi_ask_for_string(&username, _("Input ISDS user name: "),
808 cfg_getstr(configuration, CONFIG_USERNAME), batch_mode);
811 /* Ask for password */
812 shi_ask_for_password(&password, _("Input ISDS password: "),
813 cfg_getstr(configuration, CONFIG_PASSWORD), batch_mode);
815 /* Ask for key password if PKI authentication requested */
816 if (NULL != pki_key_path) {
817 shi_ask_for_password(&key_password, _("Input private key password: "),
818 cfg_getstr(configuration, CONFIG_KEY_PASSWORD), batch_mode);
821 /* Ask for OTP code if OTP authentication requested */
822 if (NULL != otp_method) {
823 shi_ask_for_password(&otp_code,
824 (otp.method == OTP_TIME) ?
825 _("Input one-time code (empty to send new one): ") :
826 _("Input one-time code: "),
827 cfg_getstr(configuration, CONFIG_OTP_CODE), batch_mode);
828 otp.otp_code = otp_code;
831 select_completion(COMPL_COMMAND);
832 set_prompt(NULL);
834 /* Build PKI structure */
835 if (NULL != pki_certificate_path || NULL != pki_key_path) {
836 pki.engine = pki_engine;
837 if (NULL == pki_certificate_format) {
838 fprintf(stderr, _("Error: No certficate format supplied\n"));
839 return -1;
841 if (string2pki_format(pki_certificate_format, &pki.certificate_format)) {
842 fprintf(stderr, _("Error: Invalid certificate format `%s'\n"),
843 pki_certificate_format);
844 return -1;
846 if (NULL == pki_key_format) {
847 fprintf(stderr, _("Error: No private key format supplied\n"));
848 return -1;
850 if (string2pki_format(pki_key_format, &pki.key_format)) {
851 fprintf(stderr, _("Error: Invalid private key format `%s'\n"),
852 pki_key_format);
853 return -1;
855 pki.certificate = pki_certificate_path;
856 pki.key = pki_key_path;
857 pki.passphrase = key_password;
860 if (NULL != otp_method && OTP_TIME == otp.method && NULL == otp_code)
861 printf(_("Requesting one-time code from server for "
862 "a login...\n"));
863 else
864 printf(_("Logging in...\n"));
865 err = isds_login(cisds, server, username, password,
866 (NULL != pki_certificate_path || NULL != pki_key_path) ?
867 &pki : NULL,
868 (NULL != otp_method) ? &otp : NULL);
869 if (NULL != otp_method && OTP_TIME == otp.method && NULL == otp_code)
870 finish_isds_operation_with_code(cisds, err, IE_PARTIAL_SUCCESS);
871 else
872 finish_isds_operation(cisds, err);
874 if (IE_PARTIAL_SUCCESS == err) {
875 printf(_("OTP code has been sent by ISDS successfully.\n"
876 "Once you receive the code, retry log-in with "
877 "the code.\n"));
878 return 1;
879 } else if (err) {
880 printf(_("Log-in failed\n"));
881 return -1;
884 oprintf(_("Logged in.\n"));
885 return 0;
889 static struct isds_DbOwnerInfo *do_box(void) {
890 isds_error err;
891 struct isds_DbOwnerInfo *box = NULL;
893 printf(_("Getting box details you are logged in...\n"));
894 err = isds_GetOwnerInfoFromLogin(cisds, &box);
895 finish_isds_operation(cisds, err);
897 return box;
901 static int shi_box(int argc, const char **argv) {
902 struct isds_DbOwnerInfo *box = NULL;
904 box = do_box();
905 if (!box) return -1;
907 format_DbOwnerInfo(box);
909 isds_DbOwnerInfo_free(&box);
910 return 0;
914 /* Get info about box with @id.
915 * @id is UTF-8 encoded
916 * Return NULL in case of error, otherwise box description that caller must
917 * free. */
918 static struct isds_DbOwnerInfo *stat_box(const char *id) {
919 isds_error err;
920 struct isds_DbOwnerInfo criteria;
921 struct isds_list *boxes = NULL, *item;
922 struct isds_DbOwnerInfo *box = NULL;
923 char *id_locale = NULL;
925 if (!id || !*id) return NULL;
927 id_locale = utf82locale(id);
928 memset(&criteria, 0, sizeof(criteria));
929 criteria.dbID = (char *) id;
931 printf(_("Getting details about box with ID `%s'...\n"), id_locale);
932 err = isds_FindDataBox(cisds, &criteria, &boxes);
933 finish_isds_operation(cisds, err);
934 if (err) goto leave;
936 for(item = boxes; item; item = item->next) {
937 if (!item->data) continue;
939 if (item->next) {
940 fprintf(stderr, _("Error: More boxes match ID `%s'\n"), id_locale);
941 goto leave;
944 box = (struct isds_DbOwnerInfo *) item->data;
945 item->data = NULL;
946 break;
949 leave:
950 free(id_locale);
951 isds_list_free(&boxes);
953 return box;
957 /* Obtain box ID value either from locale-encoded argument or from current box
958 * the context is logged in.
959 * @arg is locale-encoded box ID or NULL
960 * @box_id outputs pointer to reallocated UTF-8 encoded box ID. Pass NULL if
961 * you are not interreted in it. Will be NULLed on error.
962 * @box_id_locale outputs pointer to reallocated locale-encoded box ID. Pass
963 * NULL if you are not interrested in it. Will be NULLed on error.
964 * @return 0 on sucess, -1 on error. */
965 static int get_current_box_id(const char *arg,
966 char **box_id, char **box_id_locale) {
967 struct isds_DbOwnerInfo *box = NULL;
969 if (NULL != box_id) zfree(*box_id);
970 if (NULL != box_id_locale) zfree(*box_id_locale);
972 if (NULL == arg || '\0' == *arg) {
973 /* Get current box ID */
974 box = do_box();
975 if (NULL == box || NULL == box->dbID || !*box->dbID) {
976 isds_DbOwnerInfo_free(&box);
977 fprintf(stderr, _("Could not get current box ID\n"));
978 goto error;
980 if (NULL != box_id) {
981 *box_id = strdup(box->dbID);
982 if (NULL == *box_id) {
983 fprintf(stderr, _("Not enough memory\n"));
984 goto error;
987 if (NULL != box_id_locale) {
988 *box_id_locale = utf82locale(box->dbID);
989 if (NULL == *box_id_locale) {
990 fprintf(stderr, _("Could not convert box ID to locale\n"));
991 goto error;
994 } else {
995 /* Box ID supplied as argument */
996 if (NULL != box_id_locale) {
997 *box_id_locale = strdup(arg);
998 if (NULL == *box_id_locale) {
999 fprintf(stderr, _("Not enough memory\n"));
1000 goto error;
1003 if (NULL != box_id) {
1004 *box_id = locale2utf8(arg);
1005 if (NULL == *box_id) {
1006 fprintf(stderr, _("Could not convert box ID `%s' to UTF-8\n"),
1007 arg);
1008 goto error;
1013 return 0;
1015 error:
1016 if (NULL != box_id) zfree(*box_id);
1017 if (NULL != box_id_locale) zfree(*box_id_locale);
1018 return -1;
1022 static void shi_commercialcredit_usage(const char *command) {
1023 oprintf(_(
1024 "Usage: %s [OPTION...] [BOX_ID]\n"
1025 "Retrieve details and history of a credit available for sending commercial\n"
1026 "messages from a box with ID BOX_ID. Default is box you are logged in.\n"
1027 "Options require a date argument:\n"
1028 " -f list history from date inclusive (locale or full ISO 8601 date)\n"
1029 " -t list history to date inclusive (locale or full ISO 8601 date)\n"
1030 ), command);
1034 /* Retrieve details about credit for sending commercial messages */
1035 static int shi_commercialcredit(int argc, const char **argv) {
1036 isds_error err;
1037 struct tm *from_date = NULL, *to_date = NULL;
1038 long int current_credit;
1039 char *notification_email = NULL;
1040 struct isds_list *history = NULL, *item;
1041 int opt;
1042 char *box_id = NULL, *box_id_locale = NULL;
1043 int ordinar;
1044 int retval = 0;
1046 optind = 0;
1047 while ((opt = getopt(argc, (char * const *)argv, "f:ht:")) != -1) {
1048 switch (opt) {
1049 case 'f':
1050 free(from_date);
1051 from_date = datestring2tm(optarg);
1052 if (NULL == from_date) {
1053 fprintf(stderr, _("Error: Could not parse date: %s\n"),
1054 optarg);
1055 retval = -1;
1056 goto leave;
1058 break;
1059 case 'h':
1060 shi_commercialcredit_usage((argv)?argv[0]:NULL);
1061 goto leave;
1062 case 't':
1063 free(to_date);
1064 to_date = datestring2tm(optarg);
1065 if (NULL == to_date) {
1066 fprintf(stderr, _("Error: Could not parse date: %s\n"),
1067 optarg);
1068 retval = -1;
1069 goto leave;
1071 break;
1072 default:
1073 shi_commercialcredit_usage((argv)?argv[0]:NULL);
1074 retval = -1;
1075 goto leave;
1078 if (optind + 1 < argc) {
1079 fprintf(stderr, _("Bad invocation\n"));
1080 shi_commercialcredit_usage((argv)?argv[0]:NULL);
1081 retval = -1;
1082 goto leave;
1085 if ((retval = get_current_box_id(argv[optind], &box_id, &box_id_locale))) {
1086 goto leave;
1089 printf(_("Querying `%s' box credit details...\n"),
1090 box_id_locale);
1091 err = isds_get_commercial_credit(cisds, box_id, from_date, to_date,
1092 &current_credit, &notification_email, &history);
1093 finish_isds_operation(cisds, err);
1095 if (!err) {
1096 oprintf(_("Details of credit for sending commercial messages "
1097 "from box `%s':\n"), box_id_locale);
1098 print_header_currency(_("Current credit"), &current_credit);
1099 print_header_utf8(_("Notification e-mail"), notification_email);
1101 if (NULL != history) {
1102 oprintf(_("History of credit change events:\n"));
1103 print_header_tm(_("From"), from_date);
1104 print_header_tm(_("To"), to_date);
1105 for (item = history, ordinar = 0; item; item=item->next) {
1106 if (!item->data) continue;
1107 ordinar++;
1108 oprintf(_("\n* Event #%d:\n"), ordinar);
1109 format_credit_event(item->data);
1111 if (ordinar == 0)
1112 oprintf(_("No event exists.\n"));
1114 } else {
1115 oprintf(_("Could not get details about a credit available for sending "
1116 "commercial messages\n"
1117 "from box `%s'.\n"),
1118 box_id_locale);
1119 retval = -1;
1122 leave:
1123 isds_list_free(&history);
1124 free(notification_email);
1125 free(from_date);
1126 free(to_date);
1127 free(box_id);
1128 free(box_id_locale);
1129 return retval;
1133 static void shi_commercialreceiving_usage(const char *command) {
1134 oprintf(_(
1135 "Usage: %s [-0|-1] [BOX_ID]\n"
1136 "Manipulate commercial receiving box status.\n"
1137 " -O switch off receiving of commercial messages\n"
1138 " -1 switch on receiving of commercial messages\n"
1139 " BOX_ID affects box with ID BOX_ID; default is box you are logged in\n"
1140 "If no option is given, show current commercial receiving status.\n"),
1141 command);
1145 /* Manipulate commercial receiving box status */
1146 static int shi_commercialreceiving(int argc, const char **argv) {
1147 isds_error err;
1148 int opt;
1149 int action = -1;
1150 char *box_id = NULL, *box_id_locale = NULL;
1151 int retval = 0;
1153 optind = 0;
1154 while ((opt = getopt(argc, (char * const *)argv, "01")) != -1) {
1155 switch (opt) {
1156 case '0':
1157 action = 0;
1158 break;
1159 case '1':
1160 action = 1;
1161 break;
1162 default:
1163 shi_commercialreceiving_usage((argv)?argv[0]:NULL);
1164 return -1;
1167 if (optind + 1 < argc) {
1168 fprintf(stderr, _("Bad invocation\n"));
1169 shi_commercialreceiving_usage((argv)?argv[0]:NULL);
1170 return -1;
1173 if ((retval = get_current_box_id(argv[optind], &box_id, &box_id_locale))) {
1174 goto leave;
1177 if (action == -1) {
1178 struct isds_DbOwnerInfo *box = stat_box(box_id);
1179 if (!box) {
1180 fprintf(stderr, _("Could not get details about box ID `%s'\n"),
1181 box_id_locale);
1182 retval = -1;
1183 goto leave;
1186 oprintf(_("Commercial receiving status of box `%s': "), box_id_locale);
1187 if (!box->dbOpenAddressing)
1188 oprintf(_("Unknown\n"));
1189 else if (*box->dbOpenAddressing)
1190 oprintf(_("Positive\n"));
1191 else
1192 oprintf(_("Negative\n"));
1193 isds_DbOwnerInfo_free(&box);
1194 } else {
1195 char *refnumber = NULL;
1196 printf((action) ?
1197 _("Switching `%s' box commercial receiving on...\n"):
1198 _("Switching `%s' box commercial receiving off...\n"),
1199 box_id_locale);
1200 err = isds_switch_commercial_receiving(cisds, box_id, action,
1201 NULL, &refnumber);
1202 finish_isds_operation(cisds, err);
1204 if (!err) {
1205 char *refnumber_locale = utf82locale(refnumber);
1206 oprintf(_("Commercial receiving status successfully changed. "
1207 "Assigned reference number: %s\n"),
1208 refnumber_locale);
1209 free(refnumber_locale);
1210 } else {
1211 oprintf(_("Commercial receiving status has not been changed.\n"));
1212 retval = -1;
1214 free(refnumber);
1217 leave:
1218 free(box_id);
1219 free(box_id_locale);
1220 return retval;
1224 static void shi_commercialsending_usage(const char *command) {
1225 oprintf(_(
1226 "Usage: %s [BOX_ID]\n"
1227 "Retrieve permissions to send commercial messages from a box.\n"
1228 " BOX_ID query permissions for box with ID BOX_ID; default is box you\n"
1229 " are logged in\n"),
1230 command);
1234 /* Retrieve permissions to send commercial messages */
1235 static int shi_commercialsending(int argc, const char **argv) {
1236 isds_error err;
1237 struct isds_DbOwnerInfo *box = NULL;
1238 struct isds_list *permissions = NULL, *item;
1239 int opt;
1240 char *box_id = NULL, *box_id_locale = NULL;
1241 int ordinar;
1242 int retval = 0;
1244 optind = 0;
1245 while ((opt = getopt(argc, (char * const *)argv, "h")) != -1) {
1246 switch (opt) {
1247 case 'h':
1248 shi_commercialsending_usage((argv)?argv[0]:NULL);
1249 return 0;
1250 default:
1251 shi_commercialsending_usage((argv)?argv[0]:NULL);
1252 return -1;
1255 if (optind + 1 < argc) {
1256 fprintf(stderr, _("Bad invocation\n"));
1257 shi_commercialsending_usage((argv)?argv[0]:NULL);
1258 return -1;
1261 if ((retval = get_current_box_id(argv[optind], &box_id, &box_id_locale))) {
1262 return -1;
1265 printf(_("Querying `%s' box commercial sending permissions...\n"),
1266 box_id_locale);
1267 err = isds_get_commercial_permissions(cisds, box_id, &permissions);
1268 finish_isds_operation(cisds, err);
1270 if (!err) {
1271 oprintf(_("Permissions to send commercial messages from box `%s':\n"),
1272 box_id_locale);
1273 for (item = permissions, ordinar = 0; item; item=item->next) {
1274 if (!item->data) continue;
1275 ordinar++;
1276 oprintf(_("\n* Permission #%d:\n"), ordinar);
1277 format_commercial_permission(item->data);
1279 if (ordinar == 0)
1280 oprintf(_("No permission exists.\n"));
1281 } else {
1282 oprintf(_("Could not list permissions to send commercial messages "
1283 "from box `%s'.\n"), box_id_locale);
1284 retval = -1;
1287 isds_list_free(&permissions);
1288 free(box_id);
1289 free(box_id_locale);
1290 isds_DbOwnerInfo_free(&box);
1291 return retval;
1295 static int shi_user(int argc, const char **argv) {
1296 isds_error err;
1297 struct isds_DbUserInfo *user = NULL;
1299 printf(_("Getting user details you are logged as...\n"));
1300 err = isds_GetUserInfoFromLogin(cisds, &user);
1301 finish_isds_operation(cisds, err);
1302 if (err) return -1;
1304 format_DbUserInfo(user);
1306 isds_DbUserInfo_free(&user);
1307 return 0;
1311 static void shi_users_usage(const char *command) {
1312 oprintf(_(
1313 "Usage: %s BOX_ID\n"
1314 "Get list of users having access to box with BOX_ID.\n"),
1315 command);
1319 static int shi_users(int argc, const char **argv) {
1320 isds_error err;
1321 struct isds_list *users = NULL, *item;
1322 int ordinar;
1324 if (!argv || !argv[1] || !*argv[1]) {
1325 shi_users_usage((argv)?argv[0]:NULL);
1326 return -1;
1329 printf(_("Getting users of box with ID `%s'...\n"), argv[1]);
1330 err = isds_GetDataBoxUsers(cisds, argv[1], &users);
1331 finish_isds_operation(cisds, err);
1332 if (err) return -1;
1334 for (item = users, ordinar = 0; item; item=item->next) {
1335 if (!item->data) continue;
1336 ordinar++;
1337 oprintf(_("\n* User #%d:\n"), ordinar);
1338 format_DbUserInfo(item->data);
1340 if (ordinar == 0)
1341 oprintf(_("Empty list of users returned.\n"));
1343 isds_list_free(&users);
1344 return 0;
1348 static int show_password_expiration(void) {
1349 isds_error err;
1350 struct timeval *expiration = NULL;
1352 err = isds_get_password_expiration(cisds, &expiration);
1353 finish_isds_operation(cisds, err);
1354 if (err) {
1355 fprintf(stderr, "Could not get password expiration time\n");
1356 return -1;
1359 print_header_timeval(_("Your password expires at"), expiration);
1360 free(expiration);
1361 return 0;
1365 /* Change password in ISDS */
1366 static int do_passwd(void) {
1367 char *old_password = NULL;
1368 char *new_password = NULL;
1369 char *new_password2 = NULL;
1370 struct isds_otp otp;
1371 char *refnumber = NULL;
1372 isds_error err = IE_ERROR;
1373 int retval = 0;
1375 if (replace_string(&otp_method, cfg_getstr(configuration, CONFIG_OTP_METHOD)))
1376 return -1;
1377 /* Build OTP structure */
1378 if (NULL != otp_method) {
1379 if (string2otp_method(otp_method, &otp.method)) {
1380 fprintf(stderr, _("Error: Invalid one-time password "
1381 "authentication method `%s'\n"), otp_method);
1382 return -1;
1386 select_completion(COMPL_NONE);
1388 oprintf(_(
1389 "You are going to change your password. If you don't want to change your\n"
1390 "password, insert empty string or EOF.\n"
1391 "\n"
1392 "You will be asked for your current (old) password and then for new password.\n"
1393 "ISDS forces some criteria new password must fulfill. Current rules are:\n"
1394 "\tLength: minimal 8, maximal 32 characters\n"
1395 "\tMust contain at least: 1 upper case letter, 1 lower case letter, 1 digit\n"
1396 "\tAllowed alphabet: [a-z][A-Z][0-9][!#$%%&()*+,-.:=?@[]_{}|~]\n"
1397 "\tMust differ from last 255 passwords\n"
1398 "\tMust not contain user ID\n"
1399 "\tMust not contain sequence of three or more same characters\n"
1400 "\tMust not start with `qwert', `asdgf', or `12345'\n"
1401 "Finally, you must repeat your new password to avoid mistakes.\n"
1402 "After password change will be confirmed, you must log in again as password\n"
1403 "is transmitted to server on each request.\n"
1404 "\n"));
1406 old_password = ask_for_password(_("Old password: "));
1407 if (!old_password || *old_password == '\0') {
1408 fprintf(stderr, _("No password supplied\n"));
1409 goto error;
1412 /* Ask for OTP code if OTP authentication requested */
1413 zfree(otp_code);
1414 if (NULL != otp_method) {
1415 shi_ask_for_password(&otp_code,
1416 (otp.method == OTP_TIME) ?
1417 _("One-time code (empty to send new one): ") :
1418 _("One-time code: "),
1419 cfg_getstr(configuration, CONFIG_OTP_CODE), batch_mode);
1420 otp.otp_code = otp_code;
1424 if (NULL == otp_method || NULL != otp_code) {
1425 new_password = ask_for_password(_("New password: "));
1426 if (!new_password || *new_password == '\0') {
1427 fprintf(stderr, _("No password supplied\n"));
1428 goto error;
1431 new_password2 = ask_for_password(_("Repeat new password: "));
1432 if (!new_password2 || new_password2 == '\0') {
1433 fprintf(stderr, _("No password supplied\n"));
1434 goto error;
1437 if (strcmp(new_password, new_password2)) {
1438 fprintf(stderr, _("New passwords differ\n"));
1439 goto error;
1443 if (NULL != otp_method && OTP_TIME == otp.method && NULL == otp_code)
1444 printf(_("Requesting one-time code from server for "
1445 "a password change...\n"));
1446 else
1447 printf(_("Changing password...\n"));
1448 err = isds_change_password(cisds, old_password, new_password,
1449 (NULL != otp_method) ? &otp : NULL, &refnumber);
1450 if (NULL != otp_method && OTP_TIME == otp.method && NULL == otp_code)
1451 finish_isds_operation_with_code(cisds, err, IE_PARTIAL_SUCCESS);
1452 else
1453 finish_isds_operation(cisds, err);
1455 if (NULL != refnumber) {
1456 char *refnumber_locale = utf82locale(refnumber);
1457 free(refnumber);
1458 oprintf(_("Assigned reference number: %s\n"), refnumber_locale);
1459 free(refnumber_locale);
1461 if (IE_PARTIAL_SUCCESS == err) {
1462 oprintf(_("OTP code has been sent by ISDS successfully.\n"
1463 "Once you receive the code, retry changing password with "
1464 "the code.\n"));
1465 goto leave;
1466 } else if (err) {
1467 printf(_("Password change failed\n"));
1468 goto error;
1469 } else {
1470 oprintf(_("Password HAS been successfully changed.\n"));
1471 goto leave;
1474 error:
1475 retval = -1;
1476 oprintf(_("Password has NOT been changed!\n"));
1478 leave:
1479 free(old_password);
1480 free(new_password);
1481 free(new_password2);
1482 zfree(otp_code);
1484 set_prompt(NULL);
1485 select_completion(COMPL_COMMAND);
1487 oprintf(_("\n"
1488 "Remember, ISDS password has limited life time.\n"));
1489 return retval;
1493 static void shi_passwd_usage(const char *command) {
1494 oprintf(_(
1495 "Usage: %s [-S]\n"
1496 "Manipulate user password or change it if no option given.\n"
1497 "\n"
1498 "Options:\n"
1499 " -S show password expiration time\n"
1500 ), command);
1504 static int shi_passwd(int argc, const char **argv) {
1505 int opt;
1507 optind = 0;
1508 while ((opt = getopt(argc, (char * const *)argv, "S")) != -1) {
1509 switch (opt) {
1510 case 'S':
1511 return show_password_expiration();
1512 default:
1513 shi_passwd_usage(argv[0]);
1514 return -1;
1517 if (optind != argc || argc > 1) {
1518 fprintf(stderr, _("Bad invocation\n"));
1519 shi_passwd_usage((argv)?argv[0]:NULL);
1520 return -1;
1523 return do_passwd();
1527 static void shi_login_usage(const char *command) {
1528 oprintf(_(
1529 "Usage: %s [OPTIONS] [USER_NAME]\n"
1530 "Attemp to log into ISDS server.\n"
1531 "\n"
1532 "Options:\n"
1533 " -b URL ISDS server base URL\n"
1534 " -c IDENTIFIER user certificate\n"
1535 " -C FORMAT user certificate format\n"
1536 " -k IDENTIFIER user private key\n"
1537 " -K FORMAT user private key format\n"
1538 " -e IDENTIFIER cryptographic engine\n"
1539 " -o METHOD use one-time password authentication method\n"
1540 "\n"
1541 "Recognized certificate and key FORMATS are:\n"
1542 " PEM Base64 encoded serialization in local file\n"
1543 " DER binary serialization in local file\n"
1544 " ENG material is stored in cryptographic engine\n"
1545 "Identifiers of cryptographic engine, certificate, and private key are\n"
1546 "specific for underlying cryptographic library.\n"
1547 "\n"
1548 "Recognized one-time password methods are:\n"
1549 " HOTP HMAC-based OTP method\n"
1550 " TOTP time-based OTP method\n"
1551 "\n"
1552 "Values of omitted options are taken from configuration file.\n"
1553 ), command);
1557 static int shi_login(int argc, const char **argv) {
1558 int opt;
1559 int status;
1561 discard_credentials();
1563 /* Load stored configuration */
1564 if (replace_string(&server, cfg_getstr(configuration, CONFIG_SERVER)))
1565 return -1;
1566 if (replace_string(&otp_method, cfg_getstr(configuration, CONFIG_OTP_METHOD)))
1567 return -1;
1568 if (replace_string(&pki_engine,
1569 cfg_getstr(configuration, CONFIG_KEY_ENGINE)))
1570 return -1;
1571 if (replace_string(&pki_certificate_path,
1572 cfg_getstr(configuration, CONFIG_CERT_PATH)))
1573 return -1;
1574 if (replace_string(&pki_certificate_format,
1575 cfg_getstr(configuration, CONFIG_CERT_FORMAT)))
1576 return -1;
1577 if (replace_string(&pki_key_path,
1578 cfg_getstr(configuration, CONFIG_KEY_PATH)))
1579 return -1;
1580 if (replace_string(&pki_key_format,
1581 cfg_getstr(configuration, CONFIG_KEY_FORMAT)))
1582 return -1;
1584 /* Override configuration with positional arguments */
1585 optind = 0;
1586 while ((opt = getopt(argc, (char * const *)argv, "b:c:C:k:K:e:o:")) != -1) {
1587 switch (opt) {
1588 case 'b':
1589 if (replace_string(&server, optarg)) return -1;
1590 break;
1591 case 'c':
1592 if (replace_string(&pki_certificate_path, optarg)) return -1;
1593 break;
1594 case 'C':
1595 if (replace_string(&pki_certificate_format, optarg)) return -1;
1596 break;
1597 case 'k':
1598 if (replace_string(&pki_key_path, optarg)) return -1;
1599 break;
1600 case 'K':
1601 if (replace_string(&pki_key_format, optarg)) return -1;
1602 break;
1603 case 'e':
1604 if (replace_string(&pki_engine, optarg)) return -1;
1605 break;
1606 case 'o':
1607 if (replace_string(&otp_method, optarg)) return -1;
1608 break;
1609 default:
1610 shi_login_usage(argv[0]);
1611 return -1;
1614 if (optind < argc - 1) {
1615 fprintf(stderr, _("Bad invocation\n"));
1616 shi_login_usage(argv[0]);
1617 return -1;
1619 if (optind == argc - 1) {
1620 username = strdup(argv[optind]);
1623 /* Proceed log-in */
1624 status = do_login();
1625 if (status < 0) return -1;
1627 /* If log-in passed, store configuration */
1628 cfg_setstr(configuration, CONFIG_SERVER, server);
1629 cfg_setstr(configuration, CONFIG_USERNAME, username);
1630 cfg_setstr(configuration, CONFIG_PASSWORD, password);
1631 cfg_setstr(configuration, CONFIG_KEY_PASSWORD, key_password);
1632 cfg_setstr(configuration, CONFIG_OTP_METHOD, otp_method);
1633 cfg_setstr(configuration, CONFIG_KEY_ENGINE, pki_engine);
1634 cfg_setstr(configuration, CONFIG_CERT_PATH, pki_certificate_path);
1635 cfg_setstr(configuration, CONFIG_CERT_FORMAT, pki_certificate_format);
1636 cfg_setstr(configuration, CONFIG_KEY_PATH, pki_key_path);
1637 cfg_setstr(configuration, CONFIG_KEY_FORMAT, pki_key_format);
1639 /* Get some details only if fully logged in */
1640 if (0 == status) {
1641 show_password_expiration();
1643 return 0;
1647 static void shi_debug_usage(const char *command) {
1648 oprintf(_(
1649 "Usage: %s -l LEVEL [-f FACILITY...] [{-e | -o FILE}]\n"
1650 "Debug FACILITIES on LEVEL.\n"
1651 "\n"
1652 "-l LEVEL set log level, valid interval <%d,%d>, default is %d\n"
1653 " %d is no logging, %d critical, %d errors,\n"
1654 " %d warnings, %d info, %d debug, %d all\n"
1655 "-f FACILITY debug only given facility, repeat this option to debug\n"
1656 " more facilities; valid values: none, http, soap, isds,\n"
1657 " file, sec, xml, all; default is none\n"
1658 "-e write debug log into stderr\n"
1659 "-o FILE append debug log to FILE\n"
1661 command,
1662 ILL_NONE, ILL_ALL, ILL_NONE,
1663 ILL_NONE, ILL_CRIT, ILL_ERR, ILL_WARNING,
1664 ILL_INFO, ILL_DEBUG, ILL_ALL);
1667 static int shi_debug(int argc, const char **argv) {
1668 int opt;
1669 int log_level = ILL_NONE;
1670 isds_log_facility log_facility = ILF_NONE;
1671 char *file = NULL;
1672 _Bool close_log = 0;
1674 optind = 0;
1675 while ((opt = getopt(argc, (char * const *)argv, "l:f:eo:")) != -1) {
1676 switch (opt) {
1677 case 'l':
1678 log_level = normalize_log_level(atoi(optarg));
1679 break;
1680 case 'f':
1681 if (add_log_facility(&log_facility, optarg)) return -1;
1682 break;
1683 case 'e':
1684 close_log = 1;
1685 case 'o':
1686 file = optarg;
1687 break;
1688 default:
1689 shi_debug_usage(argv[0]);
1690 return -1;
1693 if (optind == 1 || optind != argc) {
1694 fprintf(stderr, _("Bad invocation\n"));
1695 shi_debug_usage(argv[0]);
1696 return -1;
1699 /* Redirect log */
1700 if (close_log) {
1701 isds_set_log_callback(NULL, NULL);
1703 if (logger_fd != -1) {
1704 if (-1 == close(logger_fd)) {
1705 fprintf(stderr, _("Closing log file failed: %s\n"),
1706 strerror(errno));
1707 return -1;
1710 cfg_setstr(configuration, CONFIG_LOGFILE, NULL);
1712 if (do_log_to_file(file))
1713 return -1;
1714 if (file) cfg_setstr(configuration, CONFIG_LOGFILE, file);
1716 /* Set log levels */
1717 isds_set_logging(log_facility, log_level);
1718 cfg_setint(configuration, CONFIG_LOGLEVEL, log_level);
1719 save_log_facility(log_facility);
1721 return 0;
1725 static void show_setting_str(const char *variable, _Bool cenzore) {
1726 if (!variable) return;
1728 const char *value = cfg_getstr(configuration, variable);
1730 if (value) {
1731 if (cenzore)
1732 oprintf(_("%s = <set>\n"), variable);
1733 else
1734 oprintf(_("%s = `%s'\n"), variable, value);
1735 } else {
1736 oprintf(_("%s = <unset>\n"), variable);
1741 static void show_setting_boolean(const char *variable) {
1742 if (!variable) return;
1744 _Bool value = cfg_getbool(configuration, variable);
1746 if (value) {
1747 oprintf(_("%s = <true>\n"), variable);
1748 } else {
1749 oprintf(_("%s = <false>\n"), variable);
1754 static void show_setting_int(const char *variable) {
1755 if (!variable) return;
1757 long int value = cfg_getint(configuration, variable);
1759 oprintf(_("%s = %ld\n"), variable, value);
1763 static void show_setting_strlist(const char *variable) {
1764 if (!variable) return;
1766 int length = cfg_size(configuration, variable);
1768 if (length <= 0) {
1769 oprintf(_("%s = <unset>\n"), variable);
1770 } else {
1771 oprintf(_("%s = {"), variable);
1772 for (int i = 0; i < length; i++) {
1773 const char *value = cfg_getnstr(configuration, variable, i);
1774 if (i < length - 1)
1775 oprintf(_("`%s', "), value);
1776 else
1777 oprintf(_("`%s'}\n"), value);
1783 static int shi_settings(int argc, const char **argv) {
1784 /*int opt;
1785 int log_level = ILL_NONE;
1786 isds_log_facility log_facility = ILF_NONE;
1787 char *file = NULL;
1788 _Bool close_log = 0;*/
1791 optind = 0;
1792 while ((opt = getopt(argc, (char * const *)argv, "l:f:eo:")) != -1) {
1793 switch (opt) {
1794 case 'l':
1795 log_level = normalize_log_level(atoi(optarg));
1796 break;
1797 case 'f':
1798 if (add_log_facility(&log_facility, optarg)) return -1;
1799 break;
1800 case 'e':
1801 close_log = 1;
1802 case 'o':
1803 file = optarg;
1804 break;
1805 default:
1806 shi_debug_usage(argv[0]);
1807 return -1;
1810 if (optind == 1 || optind != argc) {
1811 printf(_("Bad invocation\n"));
1812 shi_debug_usage(argv[0]);
1813 return -1;
1817 oprintf(_("Current settings:\n"));
1819 show_setting_str(CONFIG_SERVER, 0);
1820 show_setting_str(CONFIG_USERNAME, 0);
1821 show_setting_str(CONFIG_PASSWORD, 1),
1822 show_setting_str(CONFIG_CERT_FORMAT, 0),
1823 show_setting_str(CONFIG_CERT_PATH, 0),
1824 show_setting_str(CONFIG_KEY_ENGINE, 0),
1825 show_setting_str(CONFIG_KEY_FORMAT, 0),
1826 show_setting_str(CONFIG_KEY_PATH, 0),
1827 show_setting_str(CONFIG_KEY_PASSWORD, 1),
1828 show_setting_str(CONFIG_OTP_METHOD, 0),
1829 show_setting_str(CONFIG_OTP_CODE, 1),
1830 show_setting_boolean(CONFIG_VERIFYSERVER);
1831 show_setting_str(CONFIG_CAFILE, 0);
1832 show_setting_str(CONFIG_CADIRECTORY, 0);
1833 show_setting_boolean(CONFIG_CLEAN_TEMPORARY_FILES);
1834 show_setting_str(CONFIG_CRLFILE, 0);
1835 show_setting_int(CONFIG_TIMEOUT);
1836 show_setting_strlist(CONFIG_LOGFACILITIES);
1837 show_setting_str(CONFIG_LOGFILE, 0);
1838 show_setting_int(CONFIG_LOGLEVEL);
1839 show_setting_boolean(CONFIG_CONFIRM_SEND);
1840 show_setting_boolean(CONFIG_MARKMESSAGEREAD);
1841 show_setting_boolean(CONFIG_NORMALIZEMIMETYPE);
1842 show_setting_strlist(CONFIG_OPENCOMMAND);
1843 show_setting_boolean(CONFIG_OVERWRITEFILES);
1845 return 0;
1849 static void shi_find_box_usage(const char *command) {
1850 oprintf(_(
1851 "Usage: %s {OPTION... | BOX_ID}\n"
1852 "Get information about box with BOX_ID or boxes meeting other criteria.\n"
1853 "Each search option requires an argument:\n"
1854 " -t box type; accepted values:\n"
1855 " FO Private individual\n"
1856 " PFO Self-employed individual\n"
1857 " PFO_ADVOK Lawyer\n"
1858 " PFO_DANPOR Tax advisor\n"
1859 " PFO_INSSPR Insolvency administrator\n"
1860 " PO Organisation\n"
1861 " PO_ZAK Organization based by law\n"
1862 " PO_REQ Organization based on request\n"
1863 " OVM Public authority\n"
1864 " OVM_NOTAR Notary\n"
1865 " OVM_EXEKUT Executor\n"
1866 " OVM_REQ Public authority based on request\n"
1867 " -j identity number\n"
1868 "\n"
1869 "Person name options:\n"
1870 " -f first name\n"
1871 " -m middle name\n"
1872 " -l last name\n"
1873 " -b last name at birth\n"
1874 " -s subject name\n"
1875 "\n"
1876 "Birth options:\n"
1877 " -d birth date (locale or full ISO 8601 date)\n"
1878 " -w birth city\n"
1879 " -y birth county\n"
1880 " -c birth state\n"
1881 "\n"
1882 "Address:\n"
1883 " -W city\n"
1884 " -S street\n"
1885 " -z number in street\n"
1886 " -Z number in municipality\n"
1887 " -P ZIP code\n"
1888 " -C state\n"
1889 "\n"
1890 "Other options:\n"
1891 " -n nationality\n"
1892 " -e e-mail\n"
1893 " -p phone number\n"
1894 " -i identifier\n"
1895 " -r registry code\n"
1896 " -a box status; accepted values:\n"
1897 " ACCESSIBLE Accessible\n"
1898 " TEMP_INACCESSIBLE Temporary inaccessible\n"
1899 " NOT_YET_ACCESSIBLE Not yet accessible\n"
1900 " PERM_INACCESSIBLE Permanently inaccessible\n"
1901 " REMOVED Deleted\n"
1902 " -o act as public authority; boolean values: 0 is false, 1 is true\n"
1903 " -k receive commercial messages; boolean values\n"
1904 "\n"
1905 "Not all option combinations are meaningful or allowed. For example box\n"
1906 "type is always required (except direct box ID query).\n"
1907 "ISDS can refuse to answer to much broad query. Not all boxes are searchable\n"
1908 "by every user.\n"
1910 command);
1914 /* Allow reassignment */
1915 #define FILL_OR_LEAVE(variable, locale) { \
1916 zfree(variable); \
1917 (variable) = locale2utf8(locale); \
1918 if (!(variable)) { \
1919 fprintf(stderr, _("Error: Not enough memory\n")); \
1920 retval = -1; \
1921 goto leave; \
1925 #define CALLOC_OR_LEAVE(structure) { \
1926 if (!(structure)) { \
1927 (structure) = calloc(1, sizeof(*(structure))); \
1928 if (!(structure)) { \
1929 fprintf(stderr, _("Error: Not enough memory\n")); \
1930 retval = -1; \
1931 goto leave; \
1936 #define FILL_BOOLEAN_OR_LEAVE(variable, locale) { \
1937 zfree(variable); \
1938 (variable) = malloc(sizeof(*(variable))); \
1939 if (!(variable)) { \
1940 fprintf(stderr, _("Error: Not enough memory\n")); \
1941 retval = -1; \
1942 goto leave; \
1944 if (!strcmp((locale), "0")) *(variable) = 0; \
1945 else if (!strcmp((locale), "1")) *(variable) = 1; \
1946 else { \
1947 fprintf(stderr, _("%s: %s: Unknown boolean value\n"), \
1948 argv[0], (locale)); \
1949 retval = -1; \
1950 goto leave; \
1954 #define FILL_LONGINT_OR_LEAVE(variable, locale) { \
1955 if (!(locale) || !*(locale)) { \
1956 fprintf(stderr, _("%s: Empty integer value\n"), argv[0]); \
1957 retval = -1; \
1958 goto leave; \
1960 char *endptr; \
1961 zfree(variable); \
1962 (variable) = malloc(sizeof(*(variable))); \
1963 if (!(variable)) { \
1964 fprintf(stderr, _("Error: Not enough memory\n")); \
1965 retval = -1; \
1966 goto leave; \
1968 (*variable) = strtol((locale), &endptr, 0); \
1969 if (*endptr) { \
1970 fprintf(stderr, _("%s: %s: Invalid integer value\n"), \
1971 argv[0], (locale)); \
1972 retval = -1; \
1973 goto leave; \
1977 static int shi_find_box(int argc, const char **argv) {
1978 int opt;
1979 isds_error err;
1980 struct isds_DbOwnerInfo *criteria = NULL;
1981 struct isds_list *item;
1982 int order = 0;
1983 int retval = 0;
1985 if (!argv || !argv[1] || !*argv[1]) {
1986 fprintf(stderr, _("Error: No argument supplied\n"));
1987 shi_find_box_usage((argv)?argv[0]:NULL);
1988 return -1;
1991 criteria = calloc(1, sizeof(*criteria));
1992 if (!criteria) {
1993 fprintf(stderr, _("Error: Not enough memory\n"));
1994 retval = -1;
1995 goto leave;
1998 /* Parse options */
1999 optind = 0;
2000 while ((opt = getopt(argc, (char * const *)argv, "t:j:s:"
2001 "f:m:l:b:s:" "d:w:y:c:" "W:S:z:Z:P:C:"
2002 "n:e:p:i:r:a:o:k:")) != -1) {
2003 switch (opt) {
2004 case 't':
2005 criteria->dbType = malloc(sizeof(*criteria->dbType));
2006 if (!criteria->dbType) {
2007 fprintf(stderr, _("Error: Not enough memory\n"));
2008 retval = -1;
2009 goto leave;
2011 if (!strcmp(optarg, "FO"))
2012 *criteria->dbType = DBTYPE_FO;
2013 else if (!strcmp(optarg, "PFO"))
2014 *criteria->dbType = DBTYPE_PFO;
2015 else if (!strcmp(optarg, "PFO_ADVOK"))
2016 *criteria->dbType = DBTYPE_PFO_ADVOK;
2017 else if (!strcmp(optarg, "PFO_DANPOR"))
2018 *criteria->dbType = DBTYPE_PFO_DANPOR;
2019 else if (!strcmp(optarg, "PFO_INSSPR"))
2020 *criteria->dbType = DBTYPE_PFO_INSSPR;
2021 else if (!strcmp(optarg, "PO"))
2022 *criteria->dbType = DBTYPE_PO;
2023 else if (!strcmp(optarg, "PO_ZAK"))
2024 *criteria->dbType = DBTYPE_PO_ZAK;
2025 else if (!strcmp(optarg, "PO_REQ"))
2026 *criteria->dbType = DBTYPE_PO_REQ;
2027 else if (!strcmp(optarg, "OVM"))
2028 *criteria->dbType = DBTYPE_OVM;
2029 else if (!strcmp(optarg, "OVM_NOTAR"))
2030 *criteria->dbType = DBTYPE_OVM_NOTAR;
2031 else if (!strcmp(optarg, "OVM_EXEKUT"))
2032 *criteria->dbType = DBTYPE_OVM_EXEKUT;
2033 else if (!strcmp(optarg, "OVM_REQ"))
2034 *criteria->dbType = DBTYPE_OVM_REQ;
2035 else {
2036 fprintf(stderr, _("%s: %s: Unknown box type\n"),
2037 argv[0], optarg);
2038 retval = -1;
2039 goto leave;
2041 break;
2043 case 'j':
2044 FILL_OR_LEAVE(criteria->ic, optarg);
2045 break;
2047 /* Person name */
2048 case 'f':
2049 CALLOC_OR_LEAVE(criteria->personName);
2050 FILL_OR_LEAVE(criteria->personName->pnFirstName, optarg);
2051 break;
2052 case 'm':
2053 CALLOC_OR_LEAVE(criteria->personName);
2054 FILL_OR_LEAVE(criteria->personName->pnMiddleName, optarg);
2055 break;
2056 case 'l':
2057 CALLOC_OR_LEAVE(criteria->personName);
2058 FILL_OR_LEAVE(criteria->personName->pnLastName, optarg);
2059 break;
2060 case 'b':
2061 CALLOC_OR_LEAVE(criteria->personName);
2062 FILL_OR_LEAVE(criteria->personName->pnLastNameAtBirth, optarg);
2063 break;
2064 case 's':
2065 FILL_OR_LEAVE(criteria->firmName, optarg);
2066 break;
2068 /* Birth */
2069 case 'd':
2070 CALLOC_OR_LEAVE(criteria->birthInfo);
2071 criteria->birthInfo->biDate = datestring2tm(optarg);
2072 if (!criteria->birthInfo->biDate) {
2073 fprintf(stderr, _("Error: Could not parse date: %s\n"),
2074 optarg);
2075 retval = -1;
2076 goto leave;
2078 break;
2079 case 'w':
2080 CALLOC_OR_LEAVE(criteria->birthInfo);
2081 FILL_OR_LEAVE(criteria->birthInfo->biCity, optarg);
2082 break;
2083 case 'y':
2084 CALLOC_OR_LEAVE(criteria->birthInfo);
2085 FILL_OR_LEAVE(criteria->birthInfo->biCounty, optarg);
2086 break;
2087 case 'c':
2088 CALLOC_OR_LEAVE(criteria->birthInfo);
2089 FILL_OR_LEAVE(criteria->birthInfo->biState, optarg);
2090 break;
2092 /* Address */
2093 case 'W':
2094 CALLOC_OR_LEAVE(criteria->address);
2095 FILL_OR_LEAVE(criteria->address->adCity, optarg);
2096 break;
2097 case 'S':
2098 CALLOC_OR_LEAVE(criteria->address);
2099 FILL_OR_LEAVE(criteria->address->adStreet, optarg);
2100 break;
2101 case 'z':
2102 CALLOC_OR_LEAVE(criteria->address);
2103 FILL_OR_LEAVE(criteria->address->adNumberInStreet, optarg);
2104 break;
2105 case 'Z':
2106 CALLOC_OR_LEAVE(criteria->address);
2107 FILL_OR_LEAVE(criteria->address->adNumberInMunicipality,
2108 optarg);
2109 break;
2110 case 'P':
2111 CALLOC_OR_LEAVE(criteria->address);
2112 FILL_OR_LEAVE(criteria->address->adZipCode, optarg);
2113 break;
2114 case 'C':
2115 CALLOC_OR_LEAVE(criteria->address);
2116 FILL_OR_LEAVE(criteria->address->adState, optarg);
2117 break;
2119 /* Other options */
2120 case 'n':
2121 FILL_OR_LEAVE(criteria->nationality, optarg);
2122 break;
2123 case 'e':
2124 FILL_OR_LEAVE(criteria->email, optarg);
2125 break;
2126 case 'p':
2127 FILL_OR_LEAVE(criteria->telNumber, optarg);
2128 break;
2129 case 'i':
2130 FILL_OR_LEAVE(criteria->identifier, optarg);
2131 break;
2132 case 'r':
2133 FILL_OR_LEAVE(criteria->registryCode, optarg);
2134 break;
2135 case 'a':
2136 criteria->dbState = malloc(sizeof(*criteria->dbState));
2137 if (!criteria->dbState) {
2138 fprintf(stderr, _("Error: Not enough memory\n"));
2139 retval = -1;
2140 goto leave;
2142 if (!strcmp(optarg, "ACCESSIBLE"))
2143 *criteria->dbState = DBSTATE_ACCESSIBLE;
2144 else if (!strcmp(optarg, "TEMP_INACCESSIBLE"))
2145 *criteria->dbState = DBSTATE_TEMP_UNACCESSIBLE;
2146 else if (!strcmp(optarg, "NOT_YET_ACCESSIBLE"))
2147 *criteria->dbState = DBSTATE_NOT_YET_ACCESSIBLE;
2148 else if (!strcmp(optarg, "PERM_INACCESSIBLE"))
2149 *criteria->dbState = DBSTATE_PERM_UNACCESSIBLE;
2150 else if (!strcmp(optarg, "REMOVED"))
2151 *criteria->dbState = DBSTATE_REMOVED;
2152 else {
2153 fprintf(stderr, _("%s: %s: Unknown box status\n"),
2154 argv[0], optarg);
2155 retval = -1;
2156 goto leave;
2158 break;
2159 case 'o':
2160 FILL_BOOLEAN_OR_LEAVE(criteria->dbEffectiveOVM, optarg);
2161 break;
2162 case 'k':
2163 FILL_BOOLEAN_OR_LEAVE(criteria->dbOpenAddressing, optarg);
2164 break;
2166 default:
2167 shi_find_box_usage(argv[0]);
2168 retval = -1;
2169 goto leave;
2173 /* There must be an option and all of them must be recognized, if not only
2174 * BOX_ID supplied */
2175 if (argc > 2 && optind != argc) {
2176 fprintf(stderr, _("Error: Superfluous argument\n"));
2177 shi_find_box_usage(argv[0]);
2178 retval = -1;
2179 goto leave;
2182 /* If only box ID is supplied use it */
2183 if (argc == 2 && argv[1] && *argv[1]) {
2184 criteria->dbID = locale2utf8(argv[1]);
2185 if (!criteria->dbID) {
2186 fprintf(stderr, _("Error: Not enough memory\n"));
2187 retval = -1;
2188 goto leave;
2192 printf(_("Searching boxes...\n"));
2193 err = isds_FindDataBox(cisds, criteria, &boxes);
2194 finish_isds_operation(cisds, err);
2195 if (err) return -1;
2197 for(item = boxes; item; item = item->next) {
2198 if (!item->data) continue;
2199 order++;
2201 oprintf(_("\n* Result #%d:\n"), order);
2202 format_DbOwnerInfo(item->data);
2205 leave:
2206 isds_DbOwnerInfo_free(&criteria);
2207 return retval;
2211 static void shi_stat_box_usage(const char *command) {
2212 oprintf(_(
2213 "Usage: %s BOX_ID...\n"
2214 "Get status of box with BOX_ID. More boxes can be specified.\n"),
2215 command);
2219 /* Get boxes status */
2220 static int shi_stat_box(int argc, const char **argv) {
2221 isds_error err;
2222 char *id = NULL;
2223 long int status;
2225 if (!argv || !*argv || argc < 2 || !argv[1]) {
2226 fprintf(stderr, _("Missing box ID\n"));
2227 shi_stat_box_usage((argv[0])?argv[0]:NULL);
2228 return -1;
2231 for (int i = 1; i < argc; i++) {
2232 if (!argv[i] || !*argv[i]) continue;
2234 free(id);
2235 id = locale2utf8(argv[i]);
2236 if (!id) {
2237 fprintf(stderr, _("%s: Could not covert box ID to UTF-8\n"),
2238 argv[i]);
2239 return -1;
2242 printf(_("Getting status of box `%s'...\n"), argv[i]);
2243 err = isds_CheckDataBox(cisds, id, &status);
2244 finish_isds_operation(cisds, err);
2245 if (err) return -1;
2247 oprintf(_("Status of box `%s': %s\n"),
2248 argv[i], DbState2string(&status));
2251 return 0;
2255 static void shi_boxlist_usage(const char *command) {
2256 oprintf(_(
2257 "Usage: %s LIST_TYPE FILE\n"
2258 "Save latest snapshot of list of boxes of type LIST_TYPE into FILE.\n"
2259 "\n"
2260 "Currently recognized LIST_TYPES are:\n"
2261 " ALL All boxes\n"
2262 " UPG Effectively OVM boxes\n"
2263 " OVM OVM gross type boxes\n"
2264 " OPN Boxes allowing receiving commercial messages\n"
2265 "\n"
2266 "Not all types are available to all users. E.g. only `UPG' is available\n"
2267 "to regular users.\n"
2268 "\n"
2269 "The format of the list is comma separate list that is packed into\n"
2270 "ZIP archive. Name of the list file denotes time of snapshoting\n"
2271 "the list. The snapshot is created by ISDS once a day.\n"),
2272 command);
2276 /* Download list of boxes */
2277 static int shi_boxlist(int argc, const char **argv) {
2278 isds_error err;
2279 const char *type_locale;
2280 char *type = NULL;
2281 void *buffer = NULL;
2282 size_t buffer_length;
2283 int retval;
2285 if (!argv || !*argv || argc < 3 || !argv[1] || !argv[2]) {
2286 fprintf(stderr, _("Bad number of arguments\n"));
2287 shi_boxlist_usage((argv)?argv[0]:NULL);
2288 return -1;
2290 type_locale = argv[1];
2292 type = locale2utf8(type_locale);
2293 if (!type) {
2294 fprintf(stderr, _("%s: Could not covert list type to UTF-8\n"),
2295 type_locale);
2296 return -1;
2299 printf(_("Getting `%s' list of boxes...\n"), type_locale);
2300 err = isds_get_box_list_archive(cisds, type, &buffer, &buffer_length);
2301 finish_isds_operation(cisds, err);
2302 free(type);
2303 if (err) {
2304 return -1;
2307 retval = save_data_to_file(argv[2], -1, buffer, buffer_length,
2308 "application/zip",
2309 cfg_getbool(configuration, CONFIG_OVERWRITEFILES));
2311 free(buffer);
2312 return retval;
2316 static void shi_delivery_usage(const char *command) {
2317 oprintf(_(
2318 "Usage: %s [MESSAGE_ID]\n"
2319 "Get delivery data about a message.\n"
2320 "If MESSAGE_ID is defined, query for that message.\n"
2321 "Otherwise use current message.\n"),
2322 command);
2326 static int shi_delivery(int argc, const char **argv) {
2327 isds_error err;
2328 const char *id = NULL;
2329 struct isds_message *delivery_info = NULL;
2331 if (!argv || argc > 2) {
2332 shi_delivery_usage((argv)?argv[0]:NULL);
2333 return -1;
2335 if (argc == 2 && argv[1] && *argv[1]) {
2336 id = argv[1];
2337 } else {
2338 if (!message) {
2339 fprintf(stderr, _("No message loaded\n"));
2340 return -1;
2342 if (!message->envelope || !message->envelope->dmID) {
2343 fprintf(stderr, _("Current message is missing ID\n"));
2344 return -1;
2346 id = message->envelope->dmID;
2349 printf(_("Getting delivery info...\n"));
2350 err = isds_get_signed_delivery_info(cisds, id, &delivery_info);
2351 finish_isds_operation(cisds, err);
2352 if (err)
2353 return -1;
2355 isds_message_free(&message);
2356 message = delivery_info;
2358 format_message(message);
2360 if (message->envelope && message->envelope->dmID)
2361 set_prompt(_("%s %s"), argv[0], message->envelope->dmID);
2362 else
2363 set_prompt("%s", argv[0]);
2364 select_completion(COMPL_MSG);
2365 return 0;
2369 static int shi_dump_message(int argc, const char **argv) {
2370 if (!message) {
2371 fprintf(stderr, _("No message loaded\n"));
2372 return -1;
2375 print_message(message);
2376 return 0;
2380 static void shi_hash_usage(const char *command) {
2381 oprintf(_(
2382 "Usage: %s [MESSAGE_ID]\n"
2383 "Retrieve message hash stored in ISDS.\n"
2384 "If MESSAGE_ID is defined, query for that message.\n"
2385 "Otherwise use current message.\n"),
2386 command);
2390 static int shi_hash(int argc, const char **argv) {
2391 isds_error err;
2392 const char *id = NULL;
2393 struct isds_hash *hash = NULL;
2394 char *hash_string = NULL;
2396 if (!argv || argc > 2) {
2397 shi_hash_usage((argv)?argv[0]:NULL);
2398 return -1;
2400 if (argc == 2 && argv[1] && *argv[1])
2401 id = argv[1];
2402 else {
2403 if (!message) {
2404 fprintf(stderr, _("No message loaded\n"));
2405 return -1;
2407 if (!message->envelope || !message->envelope->dmID) {
2408 fprintf(stderr, _("Current message is missing ID\n"));
2409 return -1;
2411 id = message->envelope->dmID;
2414 printf(_("Getting message hash...\n"));
2415 err = isds_download_message_hash(cisds, id, &hash);
2416 finish_isds_operation(cisds, err);
2417 if (err) return -1;
2419 hash_string = hash2string(hash);
2420 oprintf(_("ISDS states message with `%s' ID has following hash:\n%s\n"),
2421 id, hash_string);
2423 free(hash_string);
2424 isds_hash_free(&hash);
2425 return 0;
2429 static int shi_verify(int argc, const char **argv) {
2430 isds_error err;
2431 int retval = 0;
2432 struct isds_hash *retrieved_hash = NULL, *stored_hash = NULL;
2433 char *hash_string = NULL;
2434 size_t width = 4;
2436 if (!message) {
2437 fprintf(stderr, _("No message loaded\n"));
2438 return -1;
2441 if (!message->envelope) {
2442 fprintf(stderr, _("Current message is missing envelope\n"));
2443 return -1;
2445 stored_hash = message->envelope->hash;
2446 message->envelope->hash = NULL;
2448 if (message->envelope->dmID) {
2449 /* Verify remote hash */
2450 oprintf(_("Remote hash check:\n"));
2452 printf(_("Getting message hash...\n"));
2453 err = isds_download_message_hash(cisds, message->envelope->dmID,
2454 &retrieved_hash);
2455 finish_isds_operation(cisds, err);
2457 if (retrieved_hash) {
2458 hash_string = hash2string(retrieved_hash);
2459 ohprint(_("Retrieved:"), width);
2460 oprintf("%s\n", hash_string);
2461 zfree(hash_string);
2464 if (retrieved_hash && message->raw) {
2465 err = isds_compute_message_hash(cisds, message,
2466 retrieved_hash->algorithm);
2467 finish_isds_operation(cisds, err);
2469 if (!err) {
2470 hash_string = hash2string(message->envelope->hash);
2471 ohprint(_("Computed:"), width);
2472 oprintf("%s\n", hash_string);
2473 zfree(hash_string);
2477 err = isds_hash_cmp(retrieved_hash, message->envelope->hash);
2478 switch (err) {
2479 case IE_SUCCESS:
2480 oprintf(_("Hashes match.\n")); break;
2481 case IE_NOTUNIQ:
2482 oprintf(_("Hashes do not match.\n"));
2483 retval = -1;
2484 break;
2485 default:
2486 oprintf(_("Hashes could not be compared.\n"));
2487 retval = -1;
2488 break;
2491 free(retrieved_hash);
2495 if (stored_hash) {
2496 /* Verify stored hash */
2497 oprintf(_("Stored hash check:\n"));
2499 hash_string = hash2string(stored_hash);
2500 ohprint(_("Stored:"), width);
2501 oprintf("%s\n", hash_string);
2502 zfree(hash_string);
2504 if (message->raw) {
2505 err = isds_compute_message_hash(cisds, message,
2506 stored_hash->algorithm);
2507 finish_isds_operation(cisds, err);
2509 if (!err) {
2510 hash_string = hash2string(message->envelope->hash);
2511 ohprint(_("Computed:"), width);
2512 oprintf("%s\n", hash_string);
2513 zfree(hash_string);
2517 err = isds_hash_cmp(stored_hash, message->envelope->hash);
2518 switch (err) {
2519 case IE_SUCCESS:
2520 oprintf(_("Hashes match.\n")); break;
2521 case IE_NOTUNIQ:
2522 oprintf(_("Hashes do not match.\n"));
2523 retval = -1;
2524 break;
2525 default:
2526 oprintf(_("Hashes could not be compared.\n"));
2527 retval = -1;
2528 break;
2531 isds_hash_free(&message->envelope->hash);
2534 message->envelope->hash = stored_hash;
2535 return retval;
2539 static int shi_authenticate(int argc, const char **argv) {
2540 isds_error err;
2541 int retval = 0;
2543 if (!message) {
2544 fprintf(stderr, _("No message loaded\n"));
2545 return -1;
2547 if (!message->raw || message->raw_length == 0) {
2548 fprintf(stderr, _("Current message is missing raw representation\n"));
2549 return -1;
2552 printf(_("Submitting message to authenticity check...\n"));
2553 err = isds_authenticate_message(cisds, message->raw, message->raw_length);
2554 finish_isds_operation(cisds, (err == IE_NOTUNIQ) ? IE_SUCCESS : err);
2556 switch (err) {
2557 case IE_SUCCESS:
2558 oprintf(_("Message originates in ISDS.\n")); break;
2559 case IE_NOTUNIQ:
2560 oprintf(_("Message is unknown to ISDS or has been tampered.\n"));
2561 retval = -1;
2562 break;
2563 default:
2564 retval = -1;
2565 break;
2568 return retval;
2572 static void shi_accept_message_usage(const char *command) {
2573 oprintf(_(
2574 "Usage: %s [MESSAGE_ID...]\n"
2575 "Accept commercial message moving its state to received.\n"
2576 "If MESSAGE_ID is defined, accept that message. More messages can be specified.\n"
2577 "Otherwise accept all commercial incoming messages.\n"),
2578 command);
2582 static int shi_accept_message(int argc, const char **argv) {
2583 isds_error err;
2584 char *id = NULL;
2586 /* Process messages named in argv */
2587 for (int i = 1; i < argc; i++) {
2588 if (!argv[i] || !*argv[i]) continue;
2590 id = locale2utf8(argv[i]);
2591 if (!id) {
2592 fprintf(stderr,
2593 _("Error: Could not convert message ID to UTF-8: %s\n"),
2594 argv[i]);
2595 return -1;
2598 printf(_("Accepting message `%s'...\n"), argv[i]);
2599 err = isds_mark_message_received(cisds, id);
2600 finish_isds_operation(cisds, err);
2601 if (err) {
2602 free(id);
2603 return -1;
2606 oprintf(_("Message `%s' accepted\n"), argv[i]);
2607 free(id);
2610 if (argc < 2) {
2611 /* TODO: list commercial not received messages and accept all of them
2612 * */
2613 fprintf(stderr,
2614 _("Error: No message ID supplied. Accepting all commercial "
2615 "messages not implemented yet.\n"));
2616 return -1;
2619 return 0;
2623 static void shi_delete_message_usage(const char *command) {
2624 oprintf(_(
2625 "Usage: %s {-i|-o} MESSAGE_ID...\n"
2626 "Remove message from long term storage.\n"
2627 "Options:\n"
2628 " -i Messages are incoming\n"
2629 " -o Messages are outoging\n"),
2630 command);
2634 static int shi_delete_message(int argc, const char **argv) {
2635 isds_error err;
2636 char *id = NULL;
2637 _Bool incoming = 0;
2638 _Bool direction_specified = 0;
2639 int opt;
2641 optind = 0;
2642 while ((opt = getopt(argc, (char * const *)argv, "io")) != -1) {
2643 switch (opt) {
2644 case 'i':
2645 incoming = 1;
2646 direction_specified = 1;
2647 break;
2648 case 'o':
2649 incoming = 0;
2650 direction_specified = 1;
2651 break;
2652 default:
2653 shi_delete_message_usage((argv)?argv[0]:NULL);
2654 return -1;
2657 if (optind >= argc || !argv || !argv[optind] || !*argv[optind]) {
2658 fprintf(stderr, _("Bad invocation\n"));
2659 shi_delete_message_usage((argv)?argv[0]:NULL);
2660 return -1;
2662 if (!direction_specified) {
2663 fprintf(stderr, _("Message direction has not been specified\n"));
2664 shi_delete_message_usage((argv)?argv[0]:NULL);
2665 return -1;
2668 /* Process messages named in argv */
2669 for (int i = optind; i < argc; i++) {
2670 if (!argv[i] || !*argv[i]) continue;
2672 id = locale2utf8(argv[i]);
2673 if (!id) {
2674 fprintf(stderr,
2675 _("Error: Could not convert message ID to UTF-8: %s\n"),
2676 argv[i]);
2677 return -1;
2680 printf(_("Deleting message `%s'...\n"), argv[i]);
2681 err = isds_delete_message_from_storage(cisds, id, incoming);
2682 finish_isds_operation(cisds, err);
2683 if (err) {
2684 free(id);
2685 return -1;
2688 oprintf(_("Message `%s' deleted\n"), argv[i]);
2689 free(id);
2692 return 0;
2696 /* Convert message ID form locale to UTF-8 or in other direction. If both
2697 * strings are provided, UTF-8 will take precedence. The missing string is
2698 * automatically allocated (but not freed before). If UTF-8 version has been
2699 * provided, @stastic_utf8 will become 1, otherwise 0. You can pass the
2700 * strings and the flags to free_message_id() to free memory properly.*/
2701 static int convert_message_id(char **id_utf8, char **id_locale, _Bool *static_utf8) {
2702 if (!id_utf8 || !id_locale || !static_utf8) return -1;
2703 if (!*id_utf8 && !*id_locale) return -1;
2705 if (*id_utf8) {
2706 *static_utf8 = 1;
2707 *id_locale = utf82locale(*id_utf8);
2708 } else {
2709 *static_utf8 = 0;
2710 *id_utf8 = locale2utf8(*id_locale);
2711 if (!*id_utf8) {
2712 fprintf(stderr,
2713 _("Error: Could not convert message ID to UTF-8: %s\n"),
2714 *id_locale);
2715 return -1;
2719 return 0;
2723 /* Free message ID strings as were allocated by convert_message_id() */
2724 static int free_message_id(char **id_utf8, char **id_locale, _Bool static_utf8) {
2725 if (!id_utf8 || !id_locale) return -1;
2726 if (static_utf8) zfree(*id_locale);
2727 else zfree(*id_utf8);
2728 return 0;
2732 /* Return static UTF-8 encoded ID of current message. In case of error NULL. */
2733 static const char *get_current_message_id(void) {
2734 if (!message) {
2735 fprintf(stderr, _("No message loaded\n"));
2736 return NULL;
2738 if (!message->envelope) {
2739 fprintf(stderr, _("Loaded message is missing envelope\n"));
2740 return NULL;
2742 if (!message->envelope->dmID || !*message->envelope->dmID) {
2743 fprintf(stderr, _("Loaded message is missing ID\n"));
2744 return NULL;
2746 return message->envelope->dmID;
2750 static void shi_message_sender_usage(const char *command) {
2751 oprintf(_(
2752 "Usage: %s [MESSAGE_ID...]\n"
2753 "Get details about sender of a message.\n"
2754 "If MESSAGE_ID is defined, get sender of that message. More messages can be specified.\n"
2755 "Otherwise will get sender of current message, if any is loaded.\n"),
2756 command);
2760 /* Get details about sender of message with given ID. At least one form must
2761 * be specified.
2762 * @message_id is UTF-8 string
2763 * @message_id_locale is string in locale encoding
2764 * @return 0 on success, -1 on failure */
2765 static int do_message_sender(const char *message_id, const char *message_id_locale) {
2766 isds_sender_type *type = NULL;
2767 char *raw_type = NULL;
2768 char *name = NULL;
2769 isds_error err;
2770 _Bool static_id;
2772 if (convert_message_id((char **)&message_id, (char **)&message_id_locale,
2773 &static_id))
2774 return -1;
2776 printf(_("Getting sender of message `%s'...\n"), message_id_locale);
2777 err = isds_get_message_sender(cisds, message_id, &type, &raw_type, &name);
2778 finish_isds_operation(cisds, err);
2779 if (err) {
2780 free_message_id((char **)&message_id, (char **)&message_id_locale,
2781 static_id);
2782 return -1;
2785 format_sender_info(message_id, type, raw_type, name);
2787 free_message_id((char **)&message_id, (char **)&message_id_locale,
2788 static_id);
2789 zfree(type);
2790 zfree(raw_type);
2791 zfree(name);
2792 return 0;
2796 static int shi_message_sender(int argc, const char **argv) {
2797 if (argc < 2) {
2798 return do_message_sender(get_current_message_id(), NULL);
2801 for (int i = 1; i < argc; i++) {
2802 if (!argv[i] || !*argv[i]) continue;
2803 if (do_message_sender(NULL, argv[i]))
2804 return -1;
2807 return 0;
2811 /* Mark message as read. At least one form of ID must be provided.
2812 * @id is UTF-8 encoded message ID
2813 * @id_locale is locale encoded message ID. @id takes preference. */
2814 static int do_read_message(const char *id, const char *id_locale) {
2815 _Bool static_id;
2816 isds_error err;
2818 if ((!id || !*id) && (!id_locale || !*id_locale)) return -1;
2820 if (convert_message_id((char **)&id, (char **)&id_locale, &static_id)) return -1;
2822 printf(_("Marking message `%s' as read...\n"), id_locale);
2823 err = isds_mark_message_read(cisds, id);
2824 finish_isds_operation(cisds, err);
2826 if (!err)
2827 oprintf(_("Message `%s' marked as read\n"), id_locale);
2829 free_message_id((char **)&id, (char **)&id_locale, static_id);
2831 return (err) ? -1 : 0;
2835 static void shi_read_message_usage(const char *command) {
2836 oprintf(_(
2837 "Usage: %s [MESSAGE_ID...]\n"
2838 "Mark message as read moving its state to read.\n"
2839 "\n"
2840 "When new incoming message is download, its state is not changed on server.\n"
2841 "Client must mark such message as read explicitly. You can use this command\n"
2842 "to do so, if not done automatically at download time by your client.\n"
2843 "\n"
2844 "If MESSAGE_ID is defined, mark that message. More messages can be specified.\n"
2845 "Otherwise marks currently loaded message.\n"),
2846 command);
2850 static int shi_read_message(int argc, const char **argv) {
2851 if (argc < 2) {
2852 return do_read_message(get_current_message_id(), NULL);
2855 for (int i = 1; i < argc; i++) {
2856 if (!argv[i] || !*argv[i]) continue;
2857 if (do_read_message(NULL, argv[i]))
2858 return -1;
2861 return 0;
2865 static void shi_cat_message_usage(const char *command) {
2866 oprintf(_(
2867 "Usage: %s\n"
2868 "Print unformated raw representation of current message.\n"
2869 "\n"
2870 "This is the same content you would get into file by `save' command.\n"
2871 "\n"
2872 "Be ware the binary stream can screw your terminal. No new line character\n"
2873 "will be appended to the end of the output.\n"),
2874 command);
2878 static int shi_cat_message(int argc, const char **argv) {
2879 if (!message) {
2880 fprintf(stderr, _("No message loaded\n"));
2881 return -1;
2884 if (!message->raw || !message->raw_length) {
2885 fprintf(stderr, _("Current message is missing raw representation\n"));
2886 return -1;
2889 if (owrite(message->raw, message->raw_length) != message->raw_length) {
2890 fprintf(stderr, _("Error while printing message content\n"));
2891 return -1;
2894 return 0;
2898 static int shi_show_message(int argc, const char **argv) {
2899 if (!message) {
2900 fprintf(stderr, _("No message loaded\n"));
2901 return -1;
2904 format_message(message);
2905 return 0;
2909 static void shi_incoming_message_usage(const char *command) {
2910 oprintf(_(
2911 "Usage: %s [-r] MESSAGE_ID\n"
2912 "Get incoming message with MESSAGE_ID.\n"
2913 "Options:\n"
2914 " -r Mark mesage as read\n"),
2915 command);
2919 static int shi_incoming_message(int argc, const char **argv) {
2920 isds_error err;
2921 const char *id;
2922 int opt;
2923 _Bool mark_as_read = 0;
2925 optind = 0;
2926 while ((opt = getopt(argc, (char * const *)argv, "r")) != -1) {
2927 switch (opt) {
2928 case 'r':
2929 mark_as_read = 1;
2930 break;
2931 default:
2932 shi_incoming_message_usage((argv)?argv[0]:NULL);
2933 return -1;
2936 if (optind + 1 != argc || !argv || !argv[optind] || !*argv[optind]) {
2937 fprintf(stderr, _("Bad invocation\n"));
2938 shi_incoming_message_usage((argv)?argv[0]:NULL);
2939 return -1;
2941 id = argv[optind];
2943 printf(_("Getting incoming message...\n"));
2944 err = isds_get_signed_received_message(cisds, id, &message);
2945 finish_isds_operation(cisds, err);
2946 if (err) {
2947 set_prompt(NULL);
2948 select_completion(COMPL_COMMAND);
2949 return -1;
2952 format_message(message);
2954 if (message->envelope && message->envelope->dmID)
2955 set_prompt(_("%s %s"), argv[0], message->envelope->dmID);
2956 else
2957 set_prompt("%s", argv[0]);
2958 select_completion(COMPL_MSG);
2960 if (mark_as_read || cfg_getbool(configuration, CONFIG_MARKMESSAGEREAD)) {
2961 if (message->envelope && message->envelope->dmMessageStatus &&
2962 ! (*message->envelope->dmMessageStatus & MESSAGESTATE_READ))
2963 return do_read_message(id, NULL);
2965 return 0;
2969 static void shi_outgoing_message_usage(const char *command) {
2970 oprintf(_(
2971 "Usage: %s MESSAGE_ID\n"
2972 "Get outgoing message with MESSAGE_ID.\n"),
2973 command);
2977 static int shi_outgoing_message(int argc, const char **argv) {
2978 isds_error err;
2979 const char *id;
2981 if (!argv || !argv[1] || !*argv[1]) {
2982 shi_outgoing_message_usage(argv[0]);
2983 return -1;
2985 id = argv[1];
2987 printf(_("Getting outgoing message...\n"));
2988 err = isds_get_signed_sent_message(cisds, id, &message);
2989 finish_isds_operation(cisds, err);
2990 if (err) {
2991 set_prompt(NULL);
2992 select_completion(COMPL_COMMAND);
2993 return -1;
2996 format_message(message);
2997 if (message->envelope && message->envelope->dmID)
2998 set_prompt(_("%s %s"), argv[0], message->envelope->dmID);
2999 else
3000 set_prompt("%s", argv[0]);
3001 select_completion(COMPL_MSG);
3002 return 0;
3006 /* Detect type (message or delivery data) and load it. And change completion
3007 * and show the data.
3008 * @buffer is memory with message or delivery data
3009 * @length is size of @buffer in bytes
3010 * @strategy defines how to fill global message variable
3011 * @return 0 for success, otherwise non-zero. */
3012 static int do_load_anything(const void *buffer, size_t length,
3013 isds_buffer_strategy strategy) {
3014 isds_raw_type raw_type;
3015 isds_error err;
3016 char *type_name = NULL;
3018 if (NULL == buffer || 0 == length) {
3019 return -1;
3022 printf(_("Detecting format...\n"));
3023 err = isds_guess_raw_type(cisds, &raw_type, buffer, length);
3024 finish_isds_operation(cisds, err);
3026 if (err) {
3027 if (err == IE_NOTSUP)
3028 fprintf(stderr, _("Unknown format.\n"));
3029 else
3030 fprintf(stderr, _("Error while detecting format.\n"));
3031 } else {
3032 switch (raw_type) {
3033 case RAWTYPE_INCOMING_MESSAGE:
3034 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
3035 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
3036 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
3037 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
3038 err = isds_load_message(cisds, raw_type,
3039 buffer, length, &message, strategy);
3040 finish_isds_operation(cisds, err);
3041 type_name = N_("message");
3042 break;
3044 case RAWTYPE_DELIVERYINFO:
3045 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO:
3046 case RAWTYPE_CMS_SIGNED_DELIVERYINFO:
3047 err = isds_load_delivery_info(cisds, raw_type,
3048 buffer, length, &message, strategy);
3049 finish_isds_operation(cisds, err);
3050 type_name = N_("delivery");
3051 break;
3053 default:
3054 fprintf(stderr,
3055 _("Unsupported format.\n"));
3056 err = IE_NOTSUP;
3060 if (err) {
3061 set_prompt(NULL);
3062 select_completion(COMPL_COMMAND);
3063 return -1;
3066 format_message(message);
3068 if (message->envelope && message->envelope->dmID)
3069 set_prompt(_("%s %s"), _(type_name), message->envelope->dmID);
3070 else
3071 set_prompt("%s", _(type_name));
3072 select_completion(COMPL_MSG);
3073 return 0;
3077 static void shi_load_anything_usage(const char *command) {
3078 oprintf(_(
3079 "Usage: %s FILE\n"
3080 "Load message or message delivery details from local FILE.\n"),
3081 command);
3085 static int shi_load_anything(int argc, const char **argv) {
3086 int fd;
3087 void *buffer = NULL;
3088 size_t length;
3089 int error;
3091 if (!argv || !argv[1] || !*argv[1]) {
3092 shi_load_anything_usage((argv)?argv[0]:NULL);
3093 return -1;
3096 printf(_("Loading file `%s'...\n"), argv[1]);
3098 if (mmap_file(argv[1], &fd, &buffer, &length)) return -1;
3100 error = do_load_anything(buffer, length, BUFFER_COPY);
3102 munmap_file(fd, buffer, length);
3104 return error;
3108 static void shi_save_message_usage(const char *command) {
3109 oprintf(_(
3110 "Usage: %s FILE\n"
3111 "Save message into local FILE.\n"),
3112 command);
3116 static const char *raw_type2mime(isds_raw_type raw_type) {
3117 switch (raw_type) {
3118 case RAWTYPE_INCOMING_MESSAGE:
3119 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
3120 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
3121 case RAWTYPE_DELIVERYINFO:
3122 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO:
3123 return "text/xml";
3125 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
3126 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
3127 case RAWTYPE_CMS_SIGNED_DELIVERYINFO:
3128 return "application/pkcs7-mime";
3130 default:
3131 return NULL;
3136 static int shi_save_message(int argc, const char **argv) {
3137 _Bool overwrite = cfg_getbool(configuration, CONFIG_OVERWRITEFILES);
3139 if (!argv || !argv[1] || !*argv[1]) {
3140 shi_save_message_usage(argv[0]);
3141 return -1;
3144 if (!message) {
3145 fprintf(stderr, _("No message loaded\n"));
3146 return -1;
3148 if (!message->raw || message->raw_length == 0) {
3149 fprintf(stderr, _("Loaded message is missing raw representation\n"));
3150 return -1;
3153 return save_data_to_file(argv[1], -1, message->raw, message->raw_length,
3154 raw_type2mime(message->raw_type), overwrite);
3158 /* Return document of current message identified by ordinal number expressed
3159 * as string. In case of error return NULL. */
3160 static const struct isds_document *locate_document_by_ordinal_string(
3161 const char *number) {
3162 const struct isds_list *item;
3163 const struct isds_document *document = NULL;
3164 int ordinar, i;
3166 if (!number) return NULL;
3168 ordinar = atoi(number);
3169 if (ordinar <= 0) {
3170 fprintf(stderr, _("%s: Document number must be positive number\n"),
3171 number);
3172 return NULL;
3175 if (!message) {
3176 fprintf(stderr, _("No message loaded\n"));
3177 return NULL;
3180 /* Find document */
3181 for (item = message->documents, i = 0; item; item = item->next) {
3182 if (!item->data) continue;
3183 if (++i == ordinar) {
3184 document = (const struct isds_document *) item->data;
3185 break;
3188 if (i != ordinar) {
3189 fprintf(stderr, _("Message does not contain document #%d\n"), ordinar);
3190 return NULL;
3193 return document;
3197 static void shi_cat_document_usage(const char *command) {
3198 oprintf(_(
3199 "Usage: %s NUMBER\n"
3200 "Print document selected with ordinal NUMBER.\n"),
3201 command);
3204 static int shi_cat_document(int argc, const char **argv) {
3205 const struct isds_document *document;
3207 if (!argv || !argv[1] || !*argv[1] || argc > 3) {
3208 shi_cat_document_usage(argv[0]);
3209 return -1;
3212 document = locate_document_by_ordinal_string(argv[1]);
3213 if (!document) return -1;
3215 if (document->is_xml) {
3216 xmlBufferPtr buffer = NULL;
3217 size_t written;
3219 if (serialize_xml_to_buffer(&buffer, document->xml_node_list))
3220 return -1;
3222 written = owrite(buffer->content, buffer->use);
3223 xmlBufferFree(buffer);
3224 if (written != buffer->use) {
3225 fprintf(stderr, _("Error while printing document content\n"));
3226 return -1;
3228 } else {
3229 if (!document->data || !document->data_length) {
3230 fprintf(stderr, _("Document is missing raw representation\n"));
3231 return -1;
3234 if (owrite(document->data, document->data_length) != document->data_length) {
3235 fprintf(stderr, _("Error while printing document content\n"));
3236 return -1;
3240 return 0;
3244 static void shi_save_document_usage(const char *command) {
3245 oprintf(_(
3246 "Usage: %s NUMBER [DESTINATION]\n"
3247 "Save document having ordinal NUMBER within current message into local file.\n"
3248 "If DESTINATION is file (or does not exist yet), document will be saved into\n"
3249 "this file.\n"
3250 "If DESTINATION is existing directory, file name equaled to document name\n"
3251 "will be saved into DESTINATION.\n"
3252 "If DESTINATION is missing, document name will be used as file name and\n"
3253 "saved into working directory.\n"
3254 "Be aware that document name does not embed malicious characters (slashes).\n"
3255 "\n"
3256 "If the document is a binary stream, image of the document will be copied\n"
3257 "into a file. If the document is a XML document, the XML tree will be serialized\n"
3258 "into a file. If XML document stands for one element or one text node, the node\n"
3259 "(and its children recursively) will be serialized. If XML document compounds\n"
3260 "more nodes or a comment or a processing instruction, parent node from ISDS name\n"
3261 "space will be used to ensure output serialized XML well-formness.\n"
3263 command);
3267 static int shi_save_document(int argc, const char **argv) {
3268 const struct isds_document *document;
3269 const char *dirname = NULL;
3270 char *filename = NULL, *path = NULL;
3271 int retval = 0;
3272 _Bool overwrite = cfg_getbool(configuration, CONFIG_OVERWRITEFILES);
3274 if (!argv || !argv[1] || !*argv[1] || argc > 3) {
3275 shi_save_document_usage(argv[0]);
3276 return -1;
3279 document = locate_document_by_ordinal_string(argv[1]);
3280 if (!document) return -1;
3282 /* Select directory and file name */
3283 if (argv[2] && *argv[2]) {
3284 if (!is_directory(argv[2])) {
3285 dirname = argv[2];
3286 } else {
3287 filename = strdup(argv[2]);
3288 if (!filename) {
3289 fprintf(stderr, _("Not enough memory\n"));
3290 return -1;
3294 if (!filename && document->dmFileDescr && &document->dmFileDescr) {
3295 filename = utf82locale(document->dmFileDescr);
3296 if (!filename) {
3297 fprintf(stderr, _("Not enough memory\n"));
3298 return -1;
3301 if (!filename) {
3302 fprintf(stderr,
3303 _("File name neither supplied, nor document name exists\n"
3304 "Please, supply one.\n"));
3305 return -1;
3308 /* Build path */
3309 if (dirname) {
3310 path = astrcat3(dirname, "/", filename);
3311 zfree(filename);
3312 } else {
3313 path = filename;
3314 filename = NULL;
3316 if (!path) {
3317 fprintf(stderr, _("Not enough memory\n"));
3318 return -1;
3321 /* Save document */
3322 if (document->is_xml)
3323 retval = save_xml_to_file(path, -1, document->xml_node_list,
3324 document->dmMimeType, overwrite);
3325 else
3326 retval = save_data_to_file(path, -1, document->data,
3327 document->data_length, document->dmMimeType, overwrite);
3328 free(path);
3329 return retval;
3333 /* Execute program specified as NULL terminated array of arguments. argv[0] is
3334 * subject of PATH search variable look-up. The program is executed directly,
3335 * it's not a shell command. */
3336 static int execute_system_command(char *const argv[]) {
3337 pid_t pid;
3339 if (!argv || !argv[0]) return -1;
3341 pid = fork();
3342 if (pid == -1) {
3343 /* Could not fork */
3344 fprintf(stderr, _("Could not fork\n"));
3345 return -1;
3346 } else if (pid == 0) {
3347 /* Child */
3348 execvp(argv[0], argv);
3349 fprintf(stderr, _("Could not execute:"));
3350 for (char *const *arg = argv; *arg; arg++)
3351 fprintf(stderr, " %s", *arg);
3352 fprintf(stderr, _(": %s\n"), strerror(errno));
3353 exit(EXIT_FAILURE);
3354 } else {
3355 /* Wait for the command */
3356 int retval;
3358 if (-1 == waitpid(pid, &retval, 0)) {
3359 fprintf(stderr, _("Could not wait for executed command\n"));
3360 return -1;
3363 if (retval == -1)
3364 fprintf(stderr, _("Exit code of command could not "
3365 "be determined\n"));
3366 else if (WIFEXITED(retval) && WEXITSTATUS(retval))
3367 printf(_("Command exited with code %d\n"),
3368 WEXITSTATUS(retval));
3369 else if (WIFSIGNALED(retval))
3370 printf(_("Command terminated by signal "
3371 "#%d\n"), WTERMSIG(retval));
3372 return retval;
3377 /* Run editor to create new text document */
3378 static int edit_new_textual_document(struct isds_document *document) {
3379 char filename[14] = "shiXXXXXX.txt";
3380 int fd;
3381 char *command[] = { getenv("VISUAL"), filename, NULL };
3382 int retval = 0;
3383 struct stat file_before, file_after;
3385 if (batch_mode) {
3386 fprintf(stderr, _("Editing is forbidden in batch mode.\n"));
3387 return -1;
3390 if (NULL == document) return -1;
3391 if (NULL == command[0]) command[0] = getenv("EDITOR");
3392 if (NULL == command[0]) {
3393 fprintf(stderr,
3394 _("Neither environment variable VISUAL nor EDITOR are set.\n"));
3395 return -1;
3398 /* Create temporary file for the document */
3399 fd = create_new_file(filename, 4);
3400 if (fd == -1) {
3401 return -1;
3403 if (fstat(fd, &file_before)) {
3404 fprintf(stderr,
3405 _("Could not retrieve modification time for `%s': %s\n"),
3406 filename, strerror(errno));
3407 retval = -1;
3408 goto leave;
3411 /* Open the file with $EDITOR */
3412 if ((retval = execute_system_command(command))) {
3413 fprintf(stderr, _("Editor failed.\n"));
3414 retval = -1;
3415 goto leave;
3418 /* Compare modification times */
3419 /* XXX: fstat(2) does return updated st_mtime. Bug in Linux 3.7.1? */
3420 if (stat(filename, &file_after)) {
3421 fprintf(stderr,
3422 _("Could not retrieve modification time for `%s': %s\n"),
3423 filename, strerror(errno));
3424 retval = -1;
3425 goto leave;
3427 if (file_before.st_mtime == file_after.st_mtime) {
3428 fprintf(stderr, _("Edited document has not been changed.\n"));
3429 retval = -1;
3430 goto leave;
3433 /* Load document */
3434 if (load_data_from_file(filename, &document->data,
3435 &document->data_length, NULL)) {
3436 retval = -1;
3437 goto leave;
3440 /* Set meta-data */
3441 if (NULL == document->dmMimeType)
3442 FILL_OR_LEAVE(document->dmMimeType, "text/plain");
3443 /* XXX: POSIX basename() modifies argument */
3444 if (NULL == document->dmFileDescr)
3445 FILL_OR_LEAVE(document->dmFileDescr, basename(filename));
3447 leave:
3448 /* Remove the file */
3449 unlink_file(filename);
3450 close(fd);
3451 return retval;
3455 /* Append @suffix into @buffer with @size bytes prealocated at position @at.
3456 * Trailing '\0' of @suffix is not carried.
3457 * @buffer can be reallocated if @size is not suffient to fill @suffix
3458 * @size is original @buffer size, can change if @buffer would be reallocated
3459 * @at position where append @suffic to. Outputs new end after appending
3460 * @suffix is NULL rerminated string to append
3461 * @return 0 if success, -1 otherwise. Caller is resposible for freeing
3462 * @buffer.*/
3463 static int append_string_at(char **buffer, size_t *size, char **at,
3464 const char *suffix) {
3466 if (!buffer || !*buffer || !size || !at || !*at) return -1;
3467 if (!suffix) return 0;
3469 while (*suffix) {
3470 if (*at - *buffer + 1 >= *size) {
3471 /* End of buffer, grow it */
3472 if (*size < 8) *size = 8;
3473 *size *= 2;
3474 char *new_buffer = realloc(*buffer, *size);
3475 if (!new_buffer) return -1;
3476 *at = *at - *buffer + new_buffer;
3477 *buffer = new_buffer;
3480 /* Copy a character */
3481 *((*at)++) = *(suffix++);
3484 return 0;
3488 static char *expand_command_arg(const char *format, const char *file,
3489 const char *type) {
3490 char *buffer = NULL;
3491 size_t size = 0;
3492 const char *format_cursor;
3493 char *buffer_cursor;
3495 if (!format) return NULL;
3497 for (format_cursor = format, buffer_cursor = buffer; ; format_cursor++) {
3498 if (buffer_cursor - buffer + 1 >= size) {
3499 /* End of buffer, grow it */
3500 if (size < 8) size = 8;
3501 size *= 2;
3502 char *new_buffer = realloc(buffer, size);
3503 if (!new_buffer) goto error;
3504 buffer_cursor = buffer_cursor - buffer + new_buffer;
3505 buffer = new_buffer;
3508 if (*format_cursor == '%') {
3509 /* Escape */
3510 switch (*(format_cursor+1)) {
3511 case 'f':
3512 if (!file) {
3513 fprintf(stderr, _("Could not expand `%%f' because "
3514 "file name did not exist.\n"));
3515 free(buffer);
3516 return NULL;
3518 if (append_string_at(&buffer, &size, &buffer_cursor, file))
3519 goto error;
3521 format_cursor++;
3522 continue;
3523 case 't':
3524 if (!file) {
3525 fprintf(stderr, _("Could not expand `%%t' because "
3526 "file type did not exist.\n"));
3527 free(buffer);
3528 return NULL;
3530 if (append_string_at(&buffer, &size, &buffer_cursor, type))
3531 goto error;
3533 format_cursor++;
3534 continue;
3535 case '%':
3536 format_cursor++;
3540 /* Copy plain character */
3541 *(buffer_cursor++) = *format_cursor;
3543 if (!*format_cursor) break;
3546 return buffer;
3548 error:
3549 fprintf(stderr, _("Error: Not enough memory\n"));
3550 free(buffer);
3551 return NULL;
3555 /* Expand open_command configuration option by substiting %f and %t with file
3556 * name and file type.
3557 * @file is locale encoded file name
3558 * @type is UTF-8 encoded MIME type
3559 * @return heap allocated arrary of arguments or NULL if error occurs.
3560 * Arguments are coded in locale. */
3561 static char **expand_open_command(const char *file, const char *type) {
3562 char **command = NULL;
3563 int length;
3564 char *type_locale = NULL;
3566 length = cfg_size(configuration, CONFIG_OPENCOMMAND);
3567 if (length <= 0) {
3568 fprintf(stderr, _("%s not set\n"), CONFIG_OPENCOMMAND);
3569 return NULL;
3572 command = malloc((length + 1) * sizeof(*command));
3573 if (!command) {
3574 fprintf(stderr, _("Error: Not enough memory\n"));
3575 return NULL;
3578 if (type) {
3579 type_locale = utf82locale(type);
3580 if (!type_locale) {
3581 printf(_("Could not convert document MIME type to locale "
3582 "encoding\n"));
3583 free(command);
3584 return NULL;
3588 for (int i = 0; i < length; i++) {
3589 const char *value = cfg_getnstr(configuration, CONFIG_OPENCOMMAND, i);
3590 command[i] = expand_command_arg(value, file, type_locale);
3591 if (!command[i]) {
3592 fprintf(stderr, _("Error: Not enough memory\n"));
3593 for (int j = 0; j < i; j++) free(command[j]);
3594 free(command);
3595 free(type_locale);
3596 return NULL;
3599 command[length] = NULL;
3601 free(type_locale);
3602 return command;
3606 /* Register temporary @file for removal at shigofumi exit.
3607 * This is done by destructor call-back in isds_list_free(). */
3608 static int register_temporary_file(const char *file) {
3609 struct isds_list *temporary_file = NULL;
3611 if (!file) return 0;
3613 temporary_file = malloc(sizeof(*temporary_file));
3614 if (!temporary_file) {
3615 fprintf(stderr, _("Error: Not enough memory\n"));
3616 return -1;
3619 temporary_file->data = (void *)strdup(file);
3620 if (!temporary_file) {
3621 fprintf(stderr, _("Error: Not enough memory\n"));
3622 free(temporary_file);
3623 return -1;
3626 temporary_file->destructor = shi_unlink_temporary_file;
3628 if (temporary_files)
3629 temporary_file->next = temporary_files;
3630 else
3631 temporary_file->next = NULL;
3632 temporary_files = temporary_file;
3634 return 0;
3638 static void shi_open_document_usage(const char *command) {
3639 oprintf(_(
3640 "Usage: %s NUMBER\n"
3641 "Save document having ordinal NUMBER within current message into temporal\n"
3642 "local file, open the file by xdg-open utility and then remove the file.\n"
3644 command);
3648 static int shi_open_document(int argc, const char **argv) {
3649 const struct isds_document *document;
3650 char filename[10] = "shiXXXXXX";
3651 int fd;
3652 char **command = NULL;
3653 int retval = 0;
3655 if (!argv || !argv[1] || !*argv[1] || argc > 3) {
3656 shi_open_document_usage(argv[0]);
3657 return -1;
3660 document = locate_document_by_ordinal_string(argv[1]);
3661 if (!document) return -1;
3663 /* Create temporary file for the document */
3664 fd = create_new_file(filename, 0);
3665 if (fd == -1) {
3666 return -1;
3669 /* Save document */
3670 if (document->is_xml)
3671 retval = save_xml_to_file(filename, fd, document->xml_node_list,
3672 document->dmMimeType, 0);
3673 else
3674 retval = save_data_to_file(filename, fd, document->data,
3675 document->data_length, document->dmMimeType, 0);
3677 /* Open the file with external utility */
3678 if (!retval) {
3679 /* Construct command arguments to execute */
3680 command = expand_open_command(filename, document->dmMimeType);
3682 if (!command)
3683 retval = -1;
3684 else {
3685 /* XXX: Do not use system(3) as we cannot escape uknown shell */
3686 retval = execute_system_command(command);
3687 for (char **arg = command; *arg; arg++) free(*arg);
3688 free(command);
3692 /* Remove the file */
3693 /* XXX: We do not know when external program opens the file. We cannot
3694 * remove it immediately. Register the filename and remove all temporary
3695 * files at exit of shigofumi if requested by configuration. */
3696 if (cfg_getbool(configuration, CONFIG_CLEAN_TEMPORARY_FILES)) {
3697 if (register_temporary_file(filename))
3698 fprintf(stderr, _("Warning: Temporary file `%s' could not been "
3699 "registered for later removal. Remove the file by "
3700 "hand, please.\n"), filename);
3702 return retval;
3706 static void shi_compose_usage(const char *command) {
3707 oprintf(_(
3708 "Usage: %s OPTION...\n"
3709 "Compose and send a message to recipient defined by his box ID.\n"
3710 "Each option requires an argument (if not stated otherwise):\n"
3711 " -s * message subject\n"
3712 "\n"
3713 "Recipient options:\n"
3714 " -b * recipient box ID\n"
3715 " -U organisation unit name\n"
3716 " -N organisation unit number\n"
3717 " -P to hands of given person\n"
3718 "\n"
3719 "Sender organisation structure options:\n"
3720 " -I publish user's identity\n"
3721 " -u unit name\n"
3722 " -n unit number\n"
3723 "\n"
3724 "Message identifier options:\n"
3725 " -r sender reference number\n"
3726 " -f sender file ID\n"
3727 " -R recipient reference number\n"
3728 " -F recipient file ID\n"
3729 "\n"
3730 "Legal title options:\n"
3731 " -y year act has been issued\n"
3732 " -a ordinal number of act in a year\n"
3733 " -e section of the act\n"
3734 " -o paragraph of the act\n"
3735 " -i point of the paragraph of the act\n"
3736 "\n"
3737 "Delivery options:\n"
3738 " -p personal delivery required\n"
3739 " -t allow substitutable delivery\n"
3740 " -A non-OVM sender acts as public authority\n"
3741 " -C commercial type; accepted values:\n"
3742 " K Commercial message paid by sender or sponsor\n"
3743 " I Initiatory commercial message offering to pay response\n"
3744 " O Commercial response paid by recipient\n"
3745 " V Public message paid by government\n"
3746 " Missing option defaults to K or O (see `commercialsending' command)\n"
3747 " if sending to non-OVM recipient with enabled commercial receiving,\n"
3748 " otherwise it defaults to V.\n"
3749 "\n"
3750 "Document options:\n"
3751 " -d * read document from local file. If `-' is specified,\n"
3752 " run text editor.\n"
3753 " -D document name (defaults to base local file name)\n"
3754 " -x transport subset of the document as a XML.\n"
3755 " Argument is XPath expression specifying desired node set\n"
3756 " -m override MIME type (guessed on -d)\n"
3757 " -g document ID (must be unique per message)\n"
3758 " -G reference to other document using its ID\n"
3759 " -c document is digital signature of other document (NO argument\n"
3760 " allowed)\n"
3761 "\n"
3762 "Options marked with asterisk are mandatory, other are optional. Another soft\n"
3763 "dependencies can emerge upon using specific option. They are not mandated by\n"
3764 "ISDS currently, but client library or this program can force them to assure\n"
3765 "semantically complete message. Following soft dependencies are recommended:\n"
3766 " -y <=> -a act number and year must be used at the same time\n"
3767 " -i => -o act point requires act paragraph\n"
3768 " -o => -e act paragraph requires act section\n"
3769 " -e => -a act section requires act number\n"
3770 " -G => -g document with referenced ID must exist\n"
3771 " -c => -G signature must refer to signed document\n"
3772 " -c first document cannot be signature\n"
3773 " -C I => -r sender reference number allows responder to reply to this message\n"
3774 " -C O -> -R recipient reference number must match sender reference number of\n"
3775 " initiatory message\n"
3776 "\n"
3777 "More documents can be attached to a message by repeating `-d' option.\n"
3778 "Document order will be preserved. Other document options affect immediately\n"
3779 "preceding `-d' document only. E.g. `-d /tmp/foo.pdf -m application/pdf\n"
3780 "-d /tmp/bar.txt -m text/plain' attaches first PDF file, then textual file.\n"
3781 "\n"
3782 "The same applies to recipient options that must start with box ID (-b).\n"
3783 "If more recipients specified, each of them will get a copy of composed\n"
3784 "message. ISDS will assign message identifier to each copy in turn.\n"
3786 command);
3790 static int shi_compose(int argc, const char **argv) {
3791 int opt;
3792 isds_error err;
3793 int retval = 0;
3794 unsigned int i;
3795 struct isds_message *message = NULL;
3796 struct isds_envelope *envelope = NULL;
3797 struct isds_list *documents = NULL;
3798 struct isds_document *document = NULL;
3799 struct isds_list *copies = NULL, *copy_item = NULL;
3800 struct isds_message_copy *copy = NULL;
3801 char *message_id_locale = NULL, *recipient_id_locale = NULL,
3802 *dmStatus_locale = NULL;
3804 if (!argv || !argv[1] || !*argv[1]) {
3805 fprintf(stderr, _("Error: No argument supplied\n"));
3806 shi_compose_usage((argv)?argv[0]:NULL);
3807 return -1;
3810 message = calloc(1, sizeof(*message));
3811 if (!message) {
3812 fprintf(stderr, _("Error: Not enough memory\n"));
3813 retval = -1;
3814 goto leave;
3816 envelope = calloc(1, sizeof(*envelope));
3817 if (!envelope) {
3818 fprintf(stderr, _("Error: Not enough memory\n"));
3819 retval = -1;
3820 goto leave;
3822 message->envelope = envelope;
3824 /* Parse options */
3825 optind = 0;
3826 while ((opt = getopt(argc, (char * const *)argv, "s:" "b:U:N:P:" "I:u:n:"
3827 "r:f:R:F:" "y:a:e:o:i:" "p:t:A:C:" "d:D:x:m:g:G:c"
3828 )) != -1) {
3829 switch (opt) {
3830 case 's':
3831 FILL_OR_LEAVE(envelope->dmAnnotation, optarg);
3832 break;
3834 /* Recipient options */
3835 case 'b':
3836 copy = NULL;
3837 if (!copies) {
3838 /* First recipient */
3839 CALLOC_OR_LEAVE(copies);
3840 copies->destructor =
3841 (void(*)(void **)) isds_message_copy_free;
3842 copy_item = copies;
3843 } else {
3844 /* Next recipient */
3845 CALLOC_OR_LEAVE(copy_item->next);
3846 copy_item->next->destructor =
3847 (void(*)(void **)) isds_message_copy_free;
3848 copy_item = copy_item->next;
3850 CALLOC_OR_LEAVE(copy);
3851 copy_item->data = copy;
3853 /* Copy recipient box ID */
3854 FILL_OR_LEAVE(copy->dbIDRecipient, optarg);
3855 break;
3856 case 'U':
3857 if (!copy) {
3858 fprintf(stderr,
3859 _("Error: %s: Recipient box ID (-b) must precede "
3860 "recipient organisation unit name (-%c)\n"),
3861 optarg, opt);
3862 retval = -1;
3863 goto leave;
3865 FILL_OR_LEAVE(copy->dmRecipientOrgUnit, optarg);
3866 break;
3867 case 'N':
3868 if (!copy) {
3869 fprintf(stderr,
3870 _("Error: %s: Recipient box ID (-b) must precede "
3871 "recipient organisation unit number (-%c)\n"),
3872 optarg, opt);
3873 retval = -1;
3874 goto leave;
3876 FILL_LONGINT_OR_LEAVE(copy->dmRecipientOrgUnitNum, optarg);
3877 break;
3878 case 'P':
3879 if (!copy) {
3880 fprintf(stderr,
3881 _("Error: %s: Recipient box ID (-b) must precede "
3882 "to-hands option (-%c)\n"), optarg, opt);
3883 retval = -1;
3884 goto leave;
3886 FILL_OR_LEAVE(copy->dmToHands, optarg);
3887 break;
3889 /* Sender organisation structure options */
3890 case 'I':
3891 FILL_BOOLEAN_OR_LEAVE(envelope->dmPublishOwnID, optarg);
3892 break;
3893 case 'u':
3894 FILL_OR_LEAVE(envelope->dmSenderOrgUnit, optarg);
3895 break;
3896 case 'n':
3897 FILL_LONGINT_OR_LEAVE(envelope->dmSenderOrgUnitNum, optarg);
3898 break;
3900 /* Message identifier options */
3901 case 'r':
3902 FILL_OR_LEAVE(envelope->dmSenderRefNumber, optarg);
3903 break;
3904 case 'f':
3905 FILL_OR_LEAVE(envelope->dmSenderIdent, optarg);
3906 break;
3907 case 'R':
3908 FILL_OR_LEAVE(envelope->dmRecipientRefNumber, optarg);
3909 break;
3910 case 'F':
3911 FILL_OR_LEAVE(envelope->dmRecipientIdent, optarg);
3912 break;
3914 /* Legal title options */
3915 case 'y':
3916 FILL_LONGINT_OR_LEAVE(envelope->dmLegalTitleYear, optarg);
3917 break;
3918 case 'a':
3919 FILL_LONGINT_OR_LEAVE(envelope->dmLegalTitleLaw, optarg);
3920 break;
3921 case 'e':
3922 FILL_OR_LEAVE(envelope->dmLegalTitleSect, optarg);
3923 break;
3924 case 'o':
3925 FILL_OR_LEAVE(envelope->dmLegalTitlePar, optarg);
3926 break;
3927 case 'i':
3928 FILL_OR_LEAVE(envelope->dmLegalTitlePoint, optarg);
3929 break;
3931 /* Delivery options */
3932 case 'p':
3933 FILL_BOOLEAN_OR_LEAVE(envelope->dmPersonalDelivery, optarg);
3934 break;
3935 case 't':
3936 FILL_BOOLEAN_OR_LEAVE(envelope->dmAllowSubstDelivery, optarg);
3937 break;
3938 case 'A':
3939 FILL_BOOLEAN_OR_LEAVE(envelope->dmOVM, optarg);
3940 break;
3941 case 'C':
3942 FILL_OR_LEAVE(envelope->dmType, optarg);
3943 break;
3945 /* Document options */
3946 case 'd':
3947 document = NULL;
3948 if (!documents) {
3949 /* First document */
3950 CALLOC_OR_LEAVE(message->documents);
3951 message->documents->destructor =
3952 (void(*)(void **)) isds_document_free;
3953 documents = message->documents;
3954 CALLOC_OR_LEAVE(document);
3955 documents->data = document;
3956 document->dmFileMetaType = FILEMETATYPE_MAIN;
3957 } else {
3958 /* Next document */
3959 CALLOC_OR_LEAVE(documents->next);
3960 documents->next->destructor =
3961 (void(*)(void **)) isds_document_free;
3962 documents = documents->next;
3963 CALLOC_OR_LEAVE(document);
3964 documents->data = document;
3965 document->dmFileMetaType = FILEMETATYPE_ENCLOSURE;
3968 if (strcmp(optarg, "-")) {
3969 /* Load file if specified. Keep editing new file after
3970 * processing all arguments. */
3971 if (load_data_from_file(optarg, &document->data,
3972 &document->data_length, &document->dmMimeType)) {
3973 retval = -1;
3974 goto leave;
3976 /* XXX: POSIX basename() modifies argument */
3977 FILL_OR_LEAVE(document->dmFileDescr, basename(optarg));
3979 break;
3980 case 'D':
3981 if (!document) {
3982 fprintf(stderr,
3983 _("Error: %s: Document file (-d) must precede "
3984 "document name (-%c)\n"), optarg, opt);
3985 retval = -1;
3986 goto leave;
3988 FILL_OR_LEAVE(document->dmFileDescr, optarg);
3989 break;
3990 case 'x':
3991 if (!document) {
3992 fprintf(stderr,
3993 _("Error: %s: Document file (-d) must precede "
3994 "XPath expression (-%c)\n"), optarg, opt);
3995 retval = -1;
3996 goto leave;
3998 /* Load XML node list */
3999 char *xpath_expr = NULL;
4000 FILL_OR_LEAVE(xpath_expr, optarg);
4001 retval = load_xml_subtree_from_memory(
4002 document->data, document->data_length,
4003 &document->xml_node_list, xpath_expr);
4004 if (retval) {
4005 free(xpath_expr);
4006 goto leave;
4008 /* Switch document type to XML */
4009 document->is_xml = 1;
4010 zfree(document->data);
4011 document->data_length = 0;
4012 documents->destructor =
4013 (void(*)(void **)) free_document_with_xml_node_list;
4014 break;
4015 case 'm':
4016 if (!document) {
4017 fprintf(stderr,
4018 _("Error: %s: Document file (-d) must precede "
4019 "MIME type (-%c)\n"), optarg, opt);
4020 retval = -1;
4021 goto leave;
4023 FILL_OR_LEAVE(document->dmMimeType, optarg);
4024 break;
4025 case 'g':
4026 if (!document) {
4027 fprintf(stderr,
4028 _("Error: %s: Document file (-d) must precede "
4029 "document ID (-%c)\n"), optarg, opt);
4030 retval = -1;
4031 goto leave;
4033 FILL_OR_LEAVE(document->dmFileGuid, optarg);
4034 break;
4035 case 'G':
4036 if (!document) {
4037 fprintf(stderr,
4038 _("Error: %s: Document file (-d) must precede "
4039 "document reference (-%c)\n"), optarg, opt);
4040 retval = -1;
4041 goto leave;
4043 FILL_OR_LEAVE(document->dmUpFileGuid, optarg);
4044 break;
4045 case 'c':
4046 if (!document) {
4047 fprintf(stderr,
4048 _("Error: Document file (-d) must precede "
4049 "document signature type (-%c)\n"), opt);
4050 retval = -1;
4051 goto leave;
4053 document->dmFileMetaType = FILEMETATYPE_SIGNATURE;
4054 break;
4056 default:
4057 shi_compose_usage(argv[0]);
4058 retval = -1;
4059 goto leave;
4063 /* All options must be recognized */
4064 if (optind != argc) {
4065 fprintf(stderr, _("Error: Superfluous argument\n"));
4066 shi_compose_usage(argv[0]);
4067 retval = -1;
4068 goto leave;
4071 if (!copies) {
4072 fprintf(stderr, _("Error: No recipient box ID specified\n"));
4073 shi_compose_usage(argv[0]);
4074 retval = -1;
4075 goto leave;
4078 /* TODO: Check Legal Title soft dependencies */
4080 /* Compose missing documents */
4081 for (i = 1, documents = message->documents; NULL != documents;
4082 documents = documents->next, i++) {
4083 document = documents->data;
4084 if (NULL == document->data) {
4085 printf(_("Editing document #%u...\n"), i);
4086 if (edit_new_textual_document(document)) {
4087 fprintf(stderr, _("Composition aborted.\n"));
4088 retval = -1;
4089 goto leave;
4094 /* Preview */
4095 oprintf(_("Following message has been composed:\n"));
4096 format_message(message);
4097 oprintf(_("Following recipients have been specified:\n"));
4098 format_copies(copies);
4099 /* TODO: correction */
4100 if (cfg_getbool(configuration, CONFIG_CONFIRM_SEND)) {
4101 if (!shi_ask_yes_no(_("Send composed message?"), 1, batch_mode)) {
4102 fprintf(stderr, _("Composition aborted.\n"));
4103 retval = -1;
4104 goto leave;
4108 /* Send a message */
4109 printf(_("Sending message...\n"));
4110 err = isds_send_message_to_multiple_recipients(cisds, message, copies);
4111 finish_isds_operation(cisds, err);
4112 if (err && err != IE_PARTIAL_SUCCESS) {
4113 retval = -1;
4114 goto leave;
4117 /* Show results for each copy */
4118 for (copy_item = copies; copy_item; copy_item = copy_item->next) {
4119 if (!copy_item->data) continue;
4120 copy = (struct isds_message_copy *) copy_item->data;
4121 recipient_id_locale = utf82locale(copy->dbIDRecipient);
4123 if (copy->error) {
4124 retval = -1;
4125 if (copy->dmStatus) dmStatus_locale = utf82locale(copy->dmStatus);
4126 if (dmStatus_locale)
4127 oprintf(_("%s: Failed: %s: %s\n"),
4128 recipient_id_locale,
4129 isds_strerror(copy->error), dmStatus_locale);
4130 else
4131 oprintf(_("%s: Failed: %s\n"), recipient_id_locale,
4132 isds_strerror(copy->error));
4133 zfree(dmStatus_locale);
4134 } else {
4135 message_id_locale = utf82locale(copy->dmID);
4136 oprintf(_("%s: Succeeded. Assigned message ID: %s\n"),
4137 recipient_id_locale, message_id_locale);
4138 free(message_id_locale);
4141 free(recipient_id_locale);
4144 leave:
4145 isds_message_free(&message);
4146 isds_list_free(&copies);
4147 return retval;
4151 #undef FILL_LONGINT_OR_LEAVE
4152 #undef FILL_BOOLEAN_OR_LEAVE
4153 #undef CALLOC_OR_LEAVE
4154 #undef FILL_OR_LEAVE
4157 static void shi_save_stamp_usage(const char *command) {
4158 oprintf(_(
4159 "Usage: %s FILE\n"
4160 "Save message time stamp into local FILE.\n"),
4161 command);
4165 static int shi_save_stamp(int argc, const char **argv) {
4166 _Bool overwrite = cfg_getbool(configuration, CONFIG_OVERWRITEFILES);
4168 if (!argv || !argv[1] || !*argv[1]) {
4169 shi_save_message_usage(argv[0]);
4170 return -1;
4173 if (!message) {
4174 fprintf(stderr, _("No message loaded\n"));
4175 return -1;
4177 if (!message->envelope || !message->envelope->timestamp||
4178 message->envelope->timestamp_length == 0) {
4179 fprintf(stderr, _("Loaded message is missing time stamp\n"));
4180 return -1;
4183 return save_data_to_file(argv[1], -1,
4184 message->envelope->timestamp, message->envelope->timestamp_length,
4185 "application/timestamp-reply", overwrite);
4189 /* Return message of current message list identified by message ID expressed
4190 * as string. In case of error return NULL. */
4191 static const struct isds_message *locate_message_by_id(const char *id_locale) {
4192 char *id = NULL;
4193 const struct isds_list *item;
4194 const struct isds_message *message = NULL;
4196 if (NULL == id_locale) return NULL;
4198 id = locale2utf8(id_locale);
4199 if (id == NULL) {
4200 fprintf(stderr, _("Could not convert message ID `%s' to UTF-8\n"),
4201 id_locale);
4202 return NULL;
4205 if (NULL == messages) {
4206 fprintf(stderr, _("No message list loaded\n"));
4207 return NULL;
4210 /* Find message */
4211 for (item = messages; NULL != item; item = item->next) {
4212 if (NULL == item->data) continue;
4213 message = (const struct isds_message *) item->data;
4214 if (NULL == message->envelope || NULL == message->envelope->dmID)
4215 continue;
4216 if (!strcmp(id, message->envelope->dmID)) {
4217 break;
4220 if (NULL == item) {
4221 fprintf(stderr, _("List does not contain message `%s'\n"), id_locale);
4222 return NULL;
4225 return message;
4229 static void shi_show_list_usage(const char *command) {
4230 oprintf(_(
4231 "Usage: %s [MESSAGE_ID]\n"
4232 "If no MESSAGE_ID is given, show current message list.\n"
4233 "If MESSAGE_ID is specified, show details about message with the MESSAGE_ID\n"
4234 "on the current message list.\n"),
4235 command);
4239 static int shi_show_list(int argc, const char **argv) {
4240 if (NULL == messages) {
4241 fprintf(stderr, _("No message list loaded\n"));
4242 return -1;
4245 if (argc > 2 || (2 == argc && NULL == argv[1])) {
4246 shi_show_list_usage((argv[0] == NULL) ? NULL : argv[0]);
4247 return -1;
4250 if (argc == 2) {
4251 /* Show details about one message */
4252 const struct isds_message *message = locate_message_by_id(argv[1]);
4253 if (NULL == message) return -1;
4254 format_message(message);
4255 return 0;
4258 /* Show all list */
4259 oprintf((messages_are_outgoing) ?
4260 ngettext("You have %'lu outgoing message\n",
4261 "You have %'lu outgoing messages\n", total_messages) :
4262 ngettext("You have %'lu incoming message\n",
4263 "You have %'lu incoming messages\n", total_messages),
4264 total_messages);
4265 print_message_list(messages, messages_are_outgoing);
4266 return 0;
4270 static void describe_message_listing_flags(void) {
4271 oprintf(_(
4272 "The third column displays flags describing status of a message:\n"
4273 " First character is a commercial type of the message:\n"
4274 " P public non-commercial message\n"
4275 " C commercial message\n"
4276 " I commercial message offering a paid response\n"
4277 " i like I, but the offer has expired\n"
4278 " R commerical message as a reply to I\n"
4279 " The second character is a delivery status of the message:\n"
4280 " > message has been sent into the system\n"
4281 " t message has been time-stamped by the system\n"
4282 " I message contained an infected document\n"
4283 " N message has been delivered ordinaly\n"
4284 " n message has been delivered substitutingly\n"
4285 " O message has been accepted by the recipient\n"
4286 " \" \" message has been read\n"
4287 " ! message could not been delivered\n"
4288 " D message content has been deleted\n"
4289 " S message has been stored in the long term storage\n"
4290 " ? unrecognized state\n"));
4294 static void shi_list_incoming_usage(const char *command) {
4295 oprintf(_(
4296 "Usage: %s\n"
4297 "List messages received into your box.\n"
4298 "\n"),
4299 command);
4300 describe_message_listing_flags();
4304 static int shi_list_incoming(int argc, const char **argv) {
4305 isds_error err;
4307 printf(_("Listing incoming messages...\n"));
4308 err = isds_get_list_of_received_messages(cisds, NULL, NULL, NULL,
4309 MESSAGESTATE_ANY, 0, &total_messages, &messages);
4310 finish_isds_operation(cisds, err);
4311 if (err) {
4312 set_prompt(NULL);
4313 select_completion(COMPL_COMMAND);
4314 return -1;
4316 messages_are_outgoing = 0;
4318 shi_show_list(0, NULL);
4320 set_prompt(_("%s %'lu"), argv[0], total_messages);
4321 select_completion(COMPL_LIST);
4322 return 0;
4326 static void shi_list_outgoing_usage(const char *command) {
4327 oprintf(_(
4328 "Usage: %s\n"
4329 "List messages sent from your box.\n"
4330 "\n"),
4331 command);
4332 describe_message_listing_flags();
4336 static int shi_list_outgoing(int argc, const char **argv) {
4337 isds_error err;
4339 printf(_("Listing outgoing messages...\n"));
4340 err = isds_get_list_of_sent_messages(cisds, NULL, NULL, NULL,
4341 MESSAGESTATE_ANY, 0, &total_messages, &messages);
4342 finish_isds_operation(cisds, err);
4343 if (err) {
4344 set_prompt(NULL);
4345 select_completion(COMPL_COMMAND);
4346 return -1;
4348 messages_are_outgoing = 1;
4350 shi_show_list(0, NULL);
4352 set_prompt(_("%s %'lu"), argv[0], total_messages);
4353 select_completion(COMPL_LIST);
4354 return 0;
4358 /* Submit document for conversion and print assigned identifier */
4359 static int do_convert(const struct isds_document *document) {
4360 isds_error err;
4361 char *id = NULL;
4362 struct tm *date = NULL;
4364 if (!document) return -1;
4366 printf(_("Submitting document for authorized conversion...\n"));
4368 err = czp_convert_document(czechpoint, document, &id, &date);
4369 finish_isds_operation(czechpoint, err);
4371 if (!err) {
4372 char *name_locale = utf82locale(document->dmFileDescr);
4373 char *date_string = tm2string(date);
4374 char *id_locale = utf82locale(id);
4375 oprintf(_(
4376 "Document submitted for authorized conversion successfully under name\n"
4377 "`%s' on %s.\n"
4378 "Submit identifier assigned by Czech POINT deposit is `%s'.\n"),
4379 name_locale, date_string, id_locale);
4380 free(name_locale);
4381 free(date_string);
4382 free(id_locale);
4383 oprintf(_("Be ware that submitted document has restricted lifetime "
4384 "(30 days).\n"));
4385 oprintf(_("See <%s> for more details.\n"), CZPDEPOSIT_URL);
4388 free(id); free(date);
4389 return (err) ? -1 : 0;
4393 static void shi_convert_file_or_message_usage(const char *command) {
4394 oprintf(_(
4395 "Usage: %s [FILE [NAME]]\n"
4396 "Submit local FILE to authorized conversion under NAME. If NAME is missing,\n"
4397 "it will use FILE name. If FILE is missing, it will submit current message.\n"),
4398 command);
4399 oprintf(_(
4400 "\n"
4401 "If Czech POINT deposit accepts document, it will return document identifier\n"
4402 "that user is supposed to provide to officer at Czech POINT contact place.\n"
4403 "Currently only PDF 1.3 and higher version files and signed messages are\n"
4404 "accepted.\n"));
4405 oprintf(_("See <%s> for more details.\n"), CZPDEPOSIT_URL);
4409 static int shi_convert_file_or_message(int argc, const char **argv) {
4410 int fd = -1;
4411 struct isds_document document;
4412 int retval = 0;
4414 if (!argv || argc > 3) {
4415 shi_convert_file_or_message_usage((argv)?argv[0]:NULL);
4416 return -1;
4419 memset(&document, 0, sizeof(document));
4421 if (NULL == argv[1] || !*argv[1]) {
4422 /* Convert current message */
4423 if (!message) {
4424 fprintf(stderr, _("No message loaded\n"));
4425 return -1;
4427 if (!message->raw || !message->raw_length) {
4428 fprintf(stderr,
4429 _("Current message is missing raw representation\n"));
4430 return -1;
4432 document.dmFileDescr = astrcat(
4433 (NULL != message->envelope && NULL != message->envelope->dmID) ?
4434 message->envelope->dmID :
4435 "unknown",
4436 ".zfo");
4437 if (NULL == document.dmFileDescr) {
4438 printf(_("Could not build document name from message ID\n"));
4439 return -1;
4441 document.data = message->raw;
4442 document.data_length = message->raw_length;
4443 } else {
4444 /* Convert local file */
4445 if (argc == 3 && argv[2] && *argv[2])
4446 document.dmFileDescr = locale2utf8(argv[2]);
4447 else
4448 document.dmFileDescr = locale2utf8(argv[1]);
4449 if (!document.dmFileDescr) {
4450 printf(_("Could not convert document name to UTF-8\n"));
4451 return -1;
4454 printf(_("Loading document from file `%s'...\n"), argv[1]);
4455 if (mmap_file(argv[1], &fd, &document.data, &document.data_length)) {
4456 free(document.dmFileDescr);
4457 return -1;
4461 retval = do_convert(&document);
4463 if (0 <= fd) {
4464 munmap_file(fd, document.data, document.data_length);
4466 free(document.dmFileDescr);
4467 return retval;
4471 static void shi_convert_document_usage(const char *command) {
4472 oprintf(_(
4473 "Usage: %s NUMBER\n"
4474 "Submit message document with ordinal NUMBER to authorized conversion.\n"),
4475 command);
4476 oprintf(_(
4477 "\n"
4478 "If Czech POINT deposit accepts document, it will return document identifier\n"
4479 "that user is supposed to provide to officer at Czech POINT contact place.\n"
4480 "Currently only PDF 1.3 and higher version files and signed messages are\n"
4481 "accepted.\n"));
4482 oprintf(_("See <%s> for more details.\n"), CZPDEPOSIT_URL);
4486 static int shi_convert_document(int argc, const char **argv) {
4487 const struct isds_document *document;
4489 if (!argv || !argv[1] || !*argv[1]) {
4490 shi_convert_document_usage((argv)?argv[0]:NULL);
4491 return -1;
4494 document = locate_document_by_ordinal_string(argv[1]);
4495 if (!document) return -1;
4497 return do_convert(document);
4501 static void shi_resign_usage(const char *command) {
4502 oprintf(_(
4503 "Usage: %s [FILE]\n"
4504 "Send message or delivery data to re-sign it and to add current time stamp.\n"
4505 "If FILE is specified, message will be loaded from local file. Otherwise\n"
4506 "current message will be sent.\n"),
4507 command);
4508 oprintf(_(
4509 "\n"
4510 "Only signed messages or delivery data without time stamp are accepted by\n"
4511 "ISDS. Output re-signed message or delivery data will be loaded.\n"));
4515 static int shi_resign(int argc, const char **argv) {
4516 int fd = -1;
4517 void *data = NULL; /* Static */
4518 void *resigned_data = NULL; /* Dynamic, stored into message */
4519 size_t data_length = 0, resigned_data_length = 0;
4520 struct tm *valid_to = NULL; /* Dynamic */
4521 isds_error err;
4522 int error;
4524 if (!argv || argc > 3) {
4525 shi_resign_usage((argv)?argv[0]:NULL);
4526 return -1;
4529 if (NULL == argv[1] || !*argv[1]) {
4530 /* Use current message */
4531 if (!message) {
4532 fprintf(stderr, _("No message or delivery data loaded\n"));
4533 return -1;
4535 if (!message->raw || !message->raw_length) {
4536 fprintf(stderr, _("Current message or delivery data "
4537 "is missing raw representation\n"));
4538 return -1;
4540 data = message->raw;
4541 data_length = message->raw_length;
4542 } else {
4543 /* Use local file */
4544 printf(_("Loading message or delivery data from file `%s'...\n"),
4545 argv[1]);
4546 if (mmap_file(argv[1], &fd, &data, &data_length)) {
4547 return -1;
4551 printf(_("Re-signing...\n"));
4552 err = isds_resign_message(cisds, data, data_length,
4553 &resigned_data, &resigned_data_length, &valid_to);
4554 finish_isds_operation(cisds, err);
4556 if (0 <= fd) {
4557 munmap_file(fd, data, data_length);
4560 if (err) {
4561 return -1;
4564 print_header_tm(_("New time stamp expires"), valid_to);
4565 free(valid_to);
4567 error = do_load_anything(resigned_data, resigned_data_length, BUFFER_MOVE);
4568 if (error) free(resigned_data);
4570 return error;
4574 #if ENABLE_DEBUG
4575 static void shi_print_usage(const char *command) {
4576 oprintf(_(
4577 "Usage: %s STRING LENGTH\n"
4578 "Prints STRING into LENGTH columns. Negative LENGTH means not to cut\n"
4579 "overflowing string.\n"
4580 "This should be locale and terminal agnostic.\n"),
4581 command);
4585 static int shi_print(int argc, const char **argv) {
4586 long int width;
4588 if (!argv || !argv[1] || !argv[2]) {
4589 shi_print_usage((argv)?argv[0]:NULL);
4590 return -1;
4593 width = strtol(argv[2], NULL, 10);
4594 if (width < INT_MIN) {
4595 fprintf(stderr,
4596 _("Length argument must not lesser than %d.\n"), INT_MIN);
4597 return -1;
4599 if (width > INT_MAX) {
4600 fprintf(stderr,
4601 _("Length argument must not be greater than %d.\n"), INT_MAX);
4602 return -1;
4605 oprintf(_(">"));
4606 onprint(argv[1], width);
4607 oprintf(_("<\n"));
4609 return 0;
4613 static int shi_tokenize(int argc, const char **argv) {
4615 if (!argv) return 0;
4617 for (int i = 0; i < argc; i++) {
4618 oprintf(_(">%s<\n"), argv[i]);
4620 return 0;
4624 static int shi_quote(int argc, const char **argv) {
4625 char *escaped, *unescaped;
4627 if (!argv) return 0;
4629 oprintf(_("Original\tQuoted\tDequoted\n"));
4630 for (int i = 0; i < argc; i++) {
4631 escaped = shi_quote_filename((char *) argv[i], 0, NULL);
4632 unescaped = shi_dequote_filename((char *) argv[i], 0);
4633 oprintf(_(">%s<\t>%s<\t>%s<\n"), argv[i], escaped, unescaped);
4634 free(escaped);
4635 free(unescaped);
4637 return 0;
4641 static int shi_ask_yesno(int argc, const char **argv) {
4642 _Bool answer;
4644 answer = shi_ask_yes_no(argv[1], 1, batch_mode);
4645 oprintf(_("Answer is %s.\n"), (answer) ? _("Yes") : _("No"));
4647 return 0;
4651 #endif
4654 /* pclose(pipe), restore ouput to stdout, show error return code */
4655 int wait_for_shell(FILE **pipe) {
4656 int retval = 0;
4658 if (pipe && *pipe) {
4659 retval = pclose(*pipe);
4660 *pipe = NULL;
4661 output = stdout;
4663 if (retval == -1)
4664 fprintf(stderr, _("Exit code of shell command could not "
4665 "be determined\n"));
4666 else if (WIFEXITED(retval) && WEXITSTATUS(retval))
4667 printf(_("Shell command exited with code %d\n"),
4668 WEXITSTATUS(retval));
4669 else if (WIFSIGNALED(retval))
4670 printf(_("Shell command terminated by signal "
4671 "#%d\n"), WTERMSIG(retval));
4674 return retval;
4678 /* Interactive loop */
4679 void shi_loop(void) {
4680 char *command_line = NULL;
4681 char **command_argv = NULL;
4682 int command_argc;
4683 char *shell = NULL;
4684 struct command *command = NULL;
4685 FILE *pipe = NULL;
4686 int failed = 0;
4688 oprintf(_("Use `help' command to get list of available commands.\n"));
4690 select_completion(COMPL_COMMAND);
4691 set_prompt(NULL);
4693 while (1) {
4694 command_line = readline((prompt) ? prompt : _("shigofumi> "));
4695 /* Remember not parsable commands too to user be able to get back to
4696 * fix command */
4697 if (command_line && *command_line) {
4698 /* TODO: Omit blank lines */
4699 add_history(command_line);
4702 command_argv = tokenize(command_line, &command_argc, &shell);
4704 if (command_argv && command_argv[0]) {
4705 command = find_command(command_argv[0]);
4707 if (!command) {
4708 fprintf(stderr, _("Command not understood\n"));
4709 } else {
4710 failed = 0;
4711 if (shell) {
4712 fflush(stdout);
4713 pipe = popen(shell, "w");
4714 if (!pipe) {
4715 fprintf(stderr, _("Could not run shell command `%s':"
4716 " %s\n"), shell, strerror(errno));
4717 failed = 1;
4719 output = pipe;
4721 if (!failed) {
4722 command->function(command_argc,
4723 (const char **) command_argv);
4724 wait_for_shell(&pipe);
4729 argv_free(command_argv);
4730 zfree(shell);
4731 zfree(command_line);
4736 /* Non-interactive mode. Commands from @lines are processed until any command
4737 * lines remains or no error occurred. First failure terminates processing.
4738 * @lines is sequence of commands separated by '\n' or '\r'. The content is
4739 * modified during this call.
4740 * @return 0 if all command succeed, otherwise non-zero value
4742 int shi_batch(char *lines) {
4743 char *command_line;
4744 char **command_argv = NULL;
4745 int command_argc;
4746 char *shell = NULL;
4747 struct command *command = NULL;
4748 int retval = 0;
4749 FILE *pipe = NULL;
4750 int failed = 0;
4752 oprintf(_("Batch mode started.\n"));
4753 batch_mode = 1;
4754 select_completion(COMPL_COMMAND);
4756 while (!retval && (command_line = strtok(lines, "\n\r"))) {
4757 lines = NULL; /* strtok(3) requires it for subsequent calls */
4759 printf(_("Processing command: %s\n"), command_line);
4761 command_argv = tokenize(command_line, &command_argc, &shell);
4763 if (command_argv && command_argv[0]) {
4764 command = find_command(command_argv[0]);
4766 if (!command) {
4767 fprintf(stderr, _("Command not understood\n"));
4768 retval = -1;
4769 } else {
4770 failed = 0;
4771 if (shell) {
4772 fflush(stdout);
4773 pipe = popen(shell, "w");
4774 if (!pipe) {
4775 fprintf(stderr, _("Could not run shell command `%s':"
4776 " %s\n"), shell, strerror(errno));
4777 failed = 1;
4779 output = pipe;
4781 if (!failed) {
4782 retval = command->function(command_argc,
4783 (const char **) command_argv);
4784 if (wait_for_shell(&pipe)) retval = -1;
4789 argv_free(command_argv);
4790 zfree(shell);
4793 if (retval)
4794 fprintf(stderr, _("Command failed!\n"));
4795 return retval;
4799 #define COMMON_COMMANDS \
4800 { "accept", shi_accept_message, N_("accept commercial message"), \
4801 shi_accept_message_usage, ARGTYPE_MSGID }, \
4802 { "box", shi_box, N_("show current box details"), NULL, \
4803 ARGTYPE_NONE }, \
4804 { "boxlist", shi_boxlist, N_("get list of all boxes"), shi_boxlist_usage, \
4805 ARGTYPE_FILE }, \
4806 { "cache", shi_cache, N_("show cache details"), NULL, \
4807 ARGTYPE_NONE }, \
4808 { "cd", shi_chdir, N_("change working directory"), shi_chdir_usage, \
4809 ARGTYPE_FILE }, \
4810 { "commercialcredit", shi_commercialcredit, N_("get credit details"), \
4811 shi_commercialcredit_usage, ARGTYPE_BOXID }, \
4812 { "commercialreceiving", shi_commercialreceiving, \
4813 N_("manipulate commercial receiving box status"), \
4814 shi_commercialreceiving_usage, ARGTYPE_BOXID }, \
4815 { "commercialsending", shi_commercialsending, \
4816 N_("manipulate commercial sending box status"), \
4817 shi_commercialsending_usage, ARGTYPE_BOXID }, \
4818 { "compose", shi_compose, N_("compose a message"), shi_compose_usage, \
4819 ARGTYPE_FILE }, \
4820 { "convert", shi_convert_file_or_message, \
4821 N_("submit local document for authorized conversion"), \
4822 shi_convert_file_or_message_usage, ARGTYPE_FILE }, \
4823 { "copying", shi_copying, N_("show this program licence excerpt"), NULL, \
4824 ARGTYPE_NONE }, \
4825 { "debug", shi_debug, N_("set debugging"), shi_debug_usage, \
4826 ARGTYPE_FILE }, \
4827 { "delete", shi_delete_message, N_("delete message from storage"), \
4828 shi_delete_message_usage, ARGTYPE_MSGID }, \
4829 { "delivery", shi_delivery, N_("get message delivery details"), \
4830 shi_delivery_usage, ARGTYPE_MSGID }, \
4831 { "findbox", shi_find_box, N_("search for a box"), shi_find_box_usage, \
4832 ARGTYPE_BOXID }, \
4833 { "hash", shi_hash, N_("query ISDS for message hash"), \
4834 shi_hash_usage, ARGTYPE_MSGID }, \
4835 { "help", shi_help, N_("describe commands"), shi_help_usage, \
4836 ARGTYPE_COMMAND }, \
4837 { "load", shi_load_anything, \
4838 N_("load message or message delivery details from local file"), \
4839 shi_load_anything_usage, ARGTYPE_FILE }, \
4840 { "login", shi_login, N_("log into ISDS"), shi_login_usage, \
4841 ARGTYPE_FILE }, \
4842 { "lsi", shi_list_incoming, N_("list received messages"), \
4843 shi_list_incoming_usage, ARGTYPE_NONE }, \
4844 { "lso", shi_list_outgoing, N_("list sent messages"), \
4845 shi_list_outgoing_usage, ARGTYPE_NONE }, \
4846 { "msgi", shi_incoming_message, N_("get incoming message"), \
4847 shi_incoming_message_usage, ARGTYPE_MSGID }, \
4848 { "msgo", shi_outgoing_message, N_("get outgoing message"), \
4849 shi_outgoing_message_usage, ARGTYPE_MSGID }, \
4850 { "passwd", shi_passwd, N_("manipulate user password"), shi_passwd_usage, \
4851 ARGTYPE_NONE }, \
4852 { "pwd", shi_pwd, N_("print working directory"), NULL, ARGTYPE_NONE }, \
4853 { "quit", shi_quit, N_("exit shigofumi"), NULL, ARGTYPE_NONE }, \
4854 { "read", shi_read_message, N_("mark message as read"), \
4855 shi_read_message_usage, ARGTYPE_MSGID }, \
4856 { "resign", shi_resign, N_("re-sign message or delivery data"), \
4857 shi_resign_usage, ARGTYPE_FILE }, \
4858 { "set", shi_settings, N_("show settings"), NULL, ARGTYPE_NONE }, \
4859 { "sender", shi_message_sender, N_("get message sender"), \
4860 shi_message_sender_usage, ARGTYPE_MSGID }, \
4861 { "statbox", shi_stat_box, N_("get status of a box"), shi_stat_box_usage, \
4862 ARGTYPE_BOXID }, \
4863 { "user", shi_user, N_("show current user details"), NULL, \
4864 ARGTYPE_NONE }, \
4865 { "users", shi_users, N_("show box users"), shi_users_usage, \
4866 ARGTYPE_NONE }, \
4867 { "version", shi_version, N_("show version of this program"), NULL, \
4868 ARGTYPE_NONE}, \
4869 { NULL, NULL, NULL, NULL, ARGTYPE_NONE }
4871 struct command base_commands[] = {
4872 #if ENABLE_DEBUG
4873 { "askyesno", shi_ask_yesno, N_("demonstrate yes-no question"), NULL,
4874 ARGTYPE_NONE },
4875 { "quote", shi_quote, N_("demonstrate argument escaping"), NULL,
4876 ARGTYPE_FILE },
4877 { "print", shi_print, N_("print string into given width"),
4878 shi_print_usage, ARGTYPE_NONE },
4879 { "tokenize", shi_tokenize, N_("demonstrate arguments tokenization"), NULL,
4880 ARGTYPE_FILE },
4881 #endif
4882 COMMON_COMMANDS
4885 struct command message_commands[] = {
4886 { "authenticate", shi_authenticate, N_("check message authenticity"),
4887 NULL, ARGTYPE_NONE },
4888 { "cat", shi_cat_message, N_("show raw current message"),
4889 shi_cat_message_usage, ARGTYPE_NONE },
4890 { "catdoc", shi_cat_document, N_("show raw document"),
4891 shi_cat_document_usage, ARGTYPE_DOCID },
4892 { "convertdoc", shi_convert_document,
4893 N_("submit document of current message for authorized conversion"),
4894 shi_convert_document_usage, ARGTYPE_DOCID },
4895 { "dump", shi_dump_message, N_("dump current message structure"),
4896 NULL, ARGTYPE_NONE },
4897 { "opendoc", shi_open_document, N_("open document using external utility"),
4898 shi_open_document_usage, ARGTYPE_DOCID },
4899 { "savestamp", shi_save_stamp,
4900 N_("save time stamp of current message into local file"),
4901 shi_save_stamp_usage, ARGTYPE_FILE },
4902 { "savedoc", shi_save_document,
4903 N_("save document of current message into local file"),
4904 shi_save_document_usage, ARGTYPE_FILE },
4905 { "save", shi_save_message, N_("save current message into local file"),
4906 shi_save_message_usage, ARGTYPE_FILE },
4907 { "show", shi_show_message, N_("show current message"), NULL,
4908 ARGTYPE_NONE },
4909 { "verify", shi_verify, N_("verify current message hash"), NULL,
4910 ARGTYPE_NONE },
4911 COMMON_COMMANDS
4914 struct command list_commands[] = {
4915 { "show", shi_show_list, N_("show current message list or list item details"),
4916 shi_show_list_usage, ARGTYPE_MSGID },
4917 COMMON_COMMANDS
4920 #undef COMMON_COMMANDS
4923 static void main_version(void) {
4924 isds_init();
4925 show_version();
4926 isds_cleanup();
4927 printf("\n");
4928 shi_copying(0, NULL);
4932 static void main_usage(const char *command) {
4933 oprintf(_(
4934 "Usage: %s [OPTION...]\n"
4935 "Access ISDS, process local data box messages or delivery details, submit\n"
4936 "document to authorized conversion.\n"
4937 "\n"
4938 "Options:\n"
4939 " -c FILE use the FILE as configuration file instead of ~/%s\n"
4940 " -e COMMANDS execute COMMANDS (new line separated) and exit\n"
4941 " -V show version info and exit\n"
4943 (command) ? command : "shigofumi",
4944 CONFIG_FILE);
4948 int main(int argc, char **argv) {
4949 int opt;
4950 char *config_file = NULL;
4951 char *batch_commands = NULL;
4952 int retval = EXIT_SUCCESS;
4954 setlocale(LC_ALL, "");
4955 #if ENABLE_NLS
4956 /* Initialize gettext */
4957 bindtextdomain(PACKAGE, LOCALEDIR);
4958 textdomain(PACKAGE);
4959 #endif
4961 /* Default output */
4962 output = stdout;
4964 /* Parse arguments */
4965 optind = 0;
4966 while ((opt = getopt(argc, (char * const *)argv, "c:e:V")) != -1) {
4967 switch (opt) {
4968 case 'c':
4969 config_file = optarg;
4970 break;
4971 case 'e':
4972 batch_commands = optarg;
4973 break;
4974 case 'V':
4975 main_version();
4976 shi_exit(EXIT_SUCCESS);
4977 break;
4978 default:
4979 main_usage((argv[0]) ? basename(argv[0]): NULL);
4980 shi_exit(EXIT_FAILURE);
4985 if (shi_init(config_file)) {
4986 shi_exit(EXIT_FAILURE);
4989 /*shi_login(NULL);*/
4991 if (batch_commands) {
4992 if (shi_batch(batch_commands))
4993 retval = EXIT_FAILURE;
4994 } else {
4995 shi_loop();
4998 shi_exit(retval);