Print struct tm has header
[shigofumi.git] / src / shigofumi.c
blobe7deac5bebb2ebb82189edf45f70b571d1b5375f
1 #define _XOPEN_SOURCE 600
2 #include <stdlib.h>
3 #include <stdio.h>
4 #include <isds.h>
5 #include <string.h>
6 #include <readline/readline.h>
7 #include <readline/history.h>
8 #include <locale.h>
9 #include <unistd.h>
10 #include <string.h>
11 #include <stdint.h>
12 #include <confuse.h>
13 #include <errno.h>
14 #include <libgen.h>
15 #include <limits.h>
16 #include <sys/types.h>
17 #include <sys/wait.h>
18 #include <strings.h>
20 #include "shigofumi.h"
21 #include "completition.h"
22 #include "data.h"
23 #include "io.h"
24 #include "ui.h"
25 #include "utils.h"
27 #define CONFIG_FILE ".shigofumirc"
28 #define CONFIG_SERVER "base_url"
29 #define CONFIG_USERNAME "username"
30 #define CONFIG_PASSWORD "password"
31 #define CONFIG_CERT_FORMAT "certificate_format"
32 #define CONFIG_CERT_PATH "certificate_path"
33 #define CONFIG_KEY_ENGINE "key_engine"
34 #define CONFIG_KEY_FORMAT "key_format"
35 #define CONFIG_KEY_PATH "key_path"
36 #define CONFIG_KEY_PASSWORD "key_password"
37 #define CONFIG_OTP_METHOD "otp_method"
38 #define CONFIG_OTP_CODE "otp_code"
39 #define CONFIG_DEBUGLEVEL "debug_level"
40 #define CONFIG_VERIFYSERVER "verify_server"
41 #define CONFIG_CAFILE "ca_file"
42 #define CONFIG_CADIRECTORY "ca_directory"
43 #define CONFIG_CLEAN_TEMPORARY_FILES "clean_temporary_files"
44 #define CONFIG_CRLFILE "crl_file"
45 #define CONFIG_TIMEOUT "timeout"
46 #define CONFIG_LOGFACILITIES "log_facilities"
47 #define CONFIG_LOGFILE "log_file"
48 #define CONFIG_LOGLEVEL "log_level"
49 #define CONFIG_MARKMESSAGEREAD "mark_message_read"
50 #define CONFIG_NORMALIZEMIMETYPE "normalize_mime_type"
51 #define CONFIG_OPENCOMMAND "open_command"
52 #define CONFIG_OVERWRITEFILES "overwrite_files"
53 #define CZPDEPOSIT_URL "https://www.czechpoint.cz/uschovna/"
55 #define TIMEOUT 10000
56 #define LOG_LEVEL 20
58 /* Configuration */
59 cfg_opt_t configuration_syntax[] = {
60 CFG_STR(CONFIG_SERVER, NULL, CFGF_NONE),
61 CFG_STR(CONFIG_USERNAME, NULL, CFGF_NODEFAULT),
62 CFG_STR(CONFIG_PASSWORD, NULL, CFGF_NODEFAULT),
63 CFG_STR(CONFIG_CERT_FORMAT, NULL, CFGF_NODEFAULT),
64 CFG_STR(CONFIG_CERT_PATH, NULL, CFGF_NODEFAULT),
65 CFG_STR(CONFIG_KEY_ENGINE, NULL, CFGF_NODEFAULT),
66 CFG_STR(CONFIG_KEY_FORMAT, NULL, CFGF_NODEFAULT),
67 CFG_STR(CONFIG_KEY_PATH, NULL, CFGF_NODEFAULT),
68 CFG_STR(CONFIG_KEY_PASSWORD, NULL, CFGF_NODEFAULT),
69 CFG_STR(CONFIG_OTP_METHOD, NULL, CFGF_NODEFAULT),
70 CFG_STR(CONFIG_OTP_CODE, NULL, CFGF_NODEFAULT),
71 /*CFG_STR(CONFIG_DEBUGLEVEL, NULL, CFGF_NODEFAULT),*/
72 CFG_BOOL(CONFIG_VERIFYSERVER, cfg_true, CFGF_NONE),
73 CFG_STR(CONFIG_CAFILE, NULL, CFGF_NODEFAULT),
74 CFG_STR(CONFIG_CADIRECTORY, NULL, CFGF_NODEFAULT),
75 CFG_BOOL(CONFIG_CLEAN_TEMPORARY_FILES, cfg_true, CFGF_NONE),
76 CFG_STR(CONFIG_CRLFILE, NULL, CFGF_NODEFAULT),
77 CFG_INT(CONFIG_TIMEOUT, TIMEOUT, CFGF_NONE),
78 CFG_STR_LIST(CONFIG_LOGFACILITIES, "{none}", CFGF_NONE),
79 CFG_STR(CONFIG_LOGFILE, NULL, CFGF_NODEFAULT),
80 CFG_INT(CONFIG_LOGLEVEL, LOG_LEVEL, CFGF_NONE),
81 CFG_BOOL(CONFIG_MARKMESSAGEREAD, cfg_false, CFGF_NONE),
82 CFG_BOOL(CONFIG_NORMALIZEMIMETYPE, cfg_true, CFGF_NONE),
83 CFG_STR_LIST(CONFIG_OPENCOMMAND, "{xdg-open, %f}", CFGF_NONE),
84 CFG_BOOL(CONFIG_OVERWRITEFILES, cfg_true, CFGF_NONE),
85 CFG_END()
87 cfg_t *configuration;
89 /* Logger */
90 int logger_fd = -1;
92 /* UI */
93 _Bool batch_mode = 0;
94 char *prompt = NULL;
95 struct command (*commands)[] = NULL;
96 FILE *output = NULL;
98 /* Data */
99 struct isds_ctx *cisds = NULL;
100 struct isds_list *boxes = NULL;
101 struct isds_message *message = NULL;
102 _Bool messages_are_outgoing = 0;
103 struct isds_list *messages = NULL;
104 unsigned long int total_messages = 0;
105 struct isds_ctx *czechpoint = NULL;
106 struct isds_list *temporary_files = NULL;
108 /* Temporary log-in settings */
109 char *server = NULL;
110 char *username = NULL;
111 char *password = NULL;
112 char *key_password = NULL;
113 char *otp_method = NULL;
114 char *otp_code = NULL;
115 char *pki_engine = NULL;
116 char *pki_certificate_path = NULL;
117 char *pki_certificate_format = NULL;
118 char *pki_key_path = NULL;
119 char *pki_key_format = NULL;
121 static void discard_credentials(void) {
122 zfree(server);
123 zfree(username);
124 zfree(password);
125 zfree(key_password);
126 zfree(otp_method);
127 zfree(otp_code);
128 zfree(pki_engine);
129 zfree(pki_certificate_path);
130 zfree(pki_certificate_format);
131 zfree(pki_key_path);
132 zfree(pki_key_format);
136 /* Remove temporary file */
137 void shi_unlink_temporary_file(void **data) {
138 if (!data || !*data) return;
140 const char *file = (const char *)*data;
141 if (-1 == remove(file)) {
142 fprintf(stderr, _("Could not remove temporary file `%s': %s\n"),
143 file, strerror(errno));
145 zfree(*data);
149 /* Finish ISDS operation, report error, if the operation returned different
150 * code than @positive_code. */
151 static void finish_isds_operation_with_code(struct isds_ctx *ctx,
152 isds_error err, isds_error positive_code) {
153 shi_progressbar_finish();
154 if (err != positive_code) {
155 if (isds_long_message(ctx))
156 fprintf(stderr, _("Error occurred: %s: %s\n"), isds_strerror(err),
157 isds_long_message(ctx));
158 else
159 fprintf(stderr, _("Error occurred: %s\n"), isds_strerror(err));
164 /* Finish ISDS operation, report error, if the operation did not returned
165 * IE_SUCCESS. */
166 static void finish_isds_operation(struct isds_ctx *ctx, isds_error err) {
167 finish_isds_operation_with_code(ctx, err, IE_SUCCESS);
171 /* Do the cleanup and exit */
172 static void shi_exit(int exit_code) {
173 /* Data */
174 discard_credentials();
175 isds_list_free(&boxes);
176 isds_message_free(&message);
177 isds_list_free(&messages);
178 if (temporary_files) {
179 oprintf(_("Removing temporary files...\n"));
180 isds_list_free(&temporary_files);
183 if (cisds) {
184 isds_error err;
185 oprintf(_("Logging out...\n"));
186 err = isds_logout(cisds);
187 finish_isds_operation(cisds, err);
188 if (err) exit_code = EXIT_FAILURE;
189 isds_ctx_free(&cisds);
191 isds_ctx_free(&czechpoint);
192 isds_cleanup();
194 /* Configuration */
195 cfg_free(configuration);
197 /* UI */
198 free(prompt);
199 free(commands);
201 exit(exit_code);
204 /* Set prompt. if @format is NULL, switch to default prompt */
205 static void set_prompt(const char *format, ...) {
206 char *buffer = NULL;
207 va_list ap;
208 if (format) {
209 va_start(ap, format);
210 shi_vasprintf(&buffer, format, ap);
211 va_end(ap);
213 if (buffer) {
214 shi_asprintf(&prompt, _("%s> "), buffer);
215 if (prompt) {
216 free(buffer);
217 return;
221 free(buffer);
222 free(prompt);
223 prompt = strdup(_("> "));
224 return;
227 zfree(prompt);
231 static int shi_load_configuration(const char *config_file) {
232 char *config_name = NULL;
233 int ret;
235 /* Get config file */
236 if (config_file) {
237 config_name = (char *) config_file;
238 } else {
239 if (-1 == shi_asprintf(&config_name, "%s/%s", getenv("HOME"),
240 CONFIG_FILE)) {
241 fprintf(stderr, _("Could not build configuration file name\n"));
242 return -1;
246 /* Parse configuration */
247 configuration = cfg_init(configuration_syntax, CFGF_NONE);
248 ret = cfg_parse(configuration, config_name);
249 if (ret) {
250 if (ret == CFG_FILE_ERROR) {
251 fprintf(stderr,
252 _("Error while opening configuration file `%s': %s\n"),
253 config_name, strerror(errno));
254 } else {
255 fprintf(stderr, _("Error while parsing configuration file `%s'\n"),
256 config_name);
258 oprintf(_("Using default configuration\n"));
261 if (config_name != config_file) free(config_name);
262 return 0;
266 void logger(isds_log_facility facility, isds_log_level level,
267 const char *message, int length, void *data) {
268 int fd;
269 ssize_t written, left = length;
271 if (!data) return;
272 fd = *((int *) data);
273 /*printf("\033[32mLOG(%02d,%02d): ", facility, level);
274 printf("%.*s", length, message);
275 printf("\033[m");*/
277 while (left) {
278 written = write(fd, message + length - left, left);
279 if (written == -1) {
280 fprintf(stderr,
281 _("Could not save log message into log file: %s\n"
282 "Log message discarded!\n"),
283 strerror(errno));
284 /*close(fd);
285 fd = -1;*/
286 return;
288 left-=written;
293 /* Redirect ISDS log to file if @file is not NULL. */
294 static int do_log_to_file(const char *file) {
295 if (file && *file) {
296 logger_fd = open_file_for_writing(file, 0, 1);
297 if (logger_fd == -1) {
298 fprintf(stderr, _("Could not redirect ISDS log to file `%s'\n"),
299 file);
300 return -1;
302 isds_set_log_callback(logger, &logger_fd);
304 return 0;
308 /* Add log facility based on its name. */
309 static int add_log_facility(isds_log_facility *facilities, const char *name) {
310 if (!facilities) return -1;
312 if (!strcmp(name, "none")) *facilities |= ILF_NONE;
313 else if (!strcmp(name, "http")) *facilities |= ILF_HTTP;
314 else if (!strcmp(name, "soap")) *facilities |= ILF_SOAP;
315 else if (!strcmp(name, "isds")) *facilities |= ILF_ISDS;
316 else if (!strcmp(name, "file")) *facilities |= ILF_FILE;
317 else if (!strcmp(name, "sec")) *facilities |= ILF_SEC;
318 else if (!strcmp(name, "xml")) *facilities |= ILF_XML;
319 else if (!strcmp(name, "all")) *facilities |= ILF_ALL;
320 else {
321 fprintf(stderr, _("%s: Unknown log facility\n"), name);
322 return -1;
325 return 0;
329 /* Save log facility into confuse configuration */
330 static void save_log_facility(int level) {
331 cfg_setlist(configuration, CONFIG_LOGFACILITIES, 0);
333 if (level == ILF_ALL) {
334 cfg_addlist(configuration, CONFIG_LOGFACILITIES, 1, "all");
335 return;
337 if (level == ILF_NONE) {
338 cfg_addlist(configuration, CONFIG_LOGFACILITIES, 1, "none");
339 return;
341 if (level & ILF_HTTP)
342 cfg_addlist(configuration, CONFIG_LOGFACILITIES, 1, "http");
343 if (level & ILF_SOAP)
344 cfg_addlist(configuration, CONFIG_LOGFACILITIES, 1, "soap");
345 if (level & ILF_ISDS)
346 cfg_addlist(configuration, CONFIG_LOGFACILITIES, 1, "isds");
347 if (level & ILF_FILE)
348 cfg_addlist(configuration, CONFIG_LOGFACILITIES, 1, "file");
349 if (level & ILF_SEC)
350 cfg_addlist(configuration, CONFIG_LOGFACILITIES, 1, "sec");
351 if (level & ILF_XML)
352 cfg_addlist(configuration, CONFIG_LOGFACILITIES, 1, "xml");
356 /* Clamp long int to unsigned int */
357 static unsigned int normalize_timeout(long int raw) {
358 if (raw < 0) {
359 oprintf(_("Configured network timeout is less then 0. "
360 "Clamped to 0.\n"));
361 return 0;
363 if (raw > UINT_MAX ) {
364 oprintf(_("Configured network timeout is greater then %1$u. "
365 "Clamped to %1$u.\n"), UINT_MAX);
366 return UINT_MAX;
368 return (unsigned int) raw;
372 /* Clamp long int to <0;100> */
373 static unsigned int normalize_log_level(long int raw) {
374 if (raw < 0) {
375 oprintf(_("Configured log level is less then 0. Clamped to 0.\n"));
376 return 0;
378 if (raw > ILL_ALL) {
379 oprintf(_("Configured log level is greater then %1$u. "
380 "Clamped to %1$u.\n"), ILL_ALL);
381 return ILL_ALL;
383 if (raw > UINT_MAX ) {
384 oprintf(_("Configured log level is greater then %1$u. "
385 "Clamped to %1$u.\n"), UINT_MAX);
386 return UINT_MAX;
388 return (unsigned int) raw;
392 static int shi_init(const char *config_file) {
393 isds_error err;
394 char *value;
395 unsigned int timeout, log_level;
396 isds_log_facility log_facility = ILF_NONE;
398 oprintf(_("This is Shigofumi, an ISDS client. "
399 "Have a nice e-government.\n"));
401 /* Do not permute arguments in getopt() */
402 if (setenv("POSIXLY_CORRECT", "", 1)) {
403 fprintf(stderr,
404 _("Could not set POSIXLY_CORRECT environment variable\n"));
405 return -1;
408 /* Load configuration */
409 if (shi_load_configuration(config_file))
410 return -1;
411 timeout = normalize_timeout(cfg_getint(configuration, CONFIG_TIMEOUT));
412 log_level = normalize_log_level(cfg_getint(configuration, CONFIG_LOGLEVEL));
414 /* Init readline */
415 rl_readline_name = "shigofumi";
416 rl_filename_quote_characters = "\\ >";
417 rl_filename_quoting_function = shi_quote_filename;
418 rl_filename_dequoting_function = shi_dequote_filename;
419 rl_char_is_quoted_p = shi_char_is_quoted;
421 /* Initialize ISDS */
422 err = isds_init();
423 if (err) {
424 fprintf(stderr, _("Could not initialize libisds library: %s\n"),
425 isds_strerror(err));
426 return -1;
429 /* Set ISDS logging */
430 value = cfg_getstr(configuration, CONFIG_LOGFILE);
431 if (do_log_to_file(value))
432 return -1;
433 for (int i = 0; i < cfg_size(configuration, CONFIG_LOGFACILITIES); i++) {
434 if (add_log_facility(&log_facility,
435 cfg_getnstr(configuration, CONFIG_LOGFACILITIES, i)))
436 return -1;
439 isds_set_logging(log_facility, log_level);
441 /* Set ISDS context up */
442 cisds = isds_ctx_create();
443 if (!cisds) {
444 fprintf(stderr, _("Could not create ISDS context\n"));
445 return -1;
447 err = isds_set_timeout(cisds, timeout);
448 if (err) {
449 fprintf(stderr, _("Could not set ISDS network timeout: %s\n"),
450 isds_strerror(err));
452 err = isds_set_progress_callback(cisds, shi_progressbar, NULL);
453 if (err) {
454 fprintf(stderr, _("Could not register network progress bar: %s: %s\n"),
455 isds_strerror(err), isds_long_message(cisds));
457 err = isds_set_opt(cisds, IOPT_NORMALIZE_MIME_TYPE,
458 cfg_getbool(configuration, CONFIG_NORMALIZEMIMETYPE));
459 if (err) {
460 fprintf(stderr,
461 cfg_getbool(configuration, CONFIG_NORMALIZEMIMETYPE) ?
462 _("Could not enable MIME type normalization: %s: %s\n") :
463 _("Could not disable MIME type normalization: %s: %s\n"),
464 isds_strerror(err), isds_long_message(cisds));
466 if (!cfg_getbool(configuration, CONFIG_VERIFYSERVER)) {
467 oprintf(_("Warning: Shigofumi disabled server identity verification "
468 "on user request!\n"));
469 err = isds_set_opt(cisds, IOPT_TLS_VERIFY_SERVER, 0);
470 if (err) {
471 fprintf(stderr,
472 _("Could not disable server identity verification: "
473 "%s: %s\n"),
474 isds_strerror(err), isds_long_message(cisds));
477 if ((value = cfg_getstr(configuration, CONFIG_CAFILE))) {
478 err = isds_set_opt(cisds, IOPT_TLS_CA_FILE, value);
479 if (err) {
480 fprintf(stderr,
481 _("Could not set file with CA certificates: %s: %s: %s\n"),
482 value, isds_strerror(err), isds_long_message(cisds));
485 if ((value = cfg_getstr(configuration, CONFIG_CADIRECTORY))) {
486 err = isds_set_opt(cisds, IOPT_TLS_CA_DIRECTORY, value);
487 if (err) {
488 fprintf(stderr,
489 _("Could not set directory with CA certificates: "
490 "%s: %s: %s\n"),
491 value, isds_strerror(err), isds_long_message(cisds));
494 if ((value = cfg_getstr(configuration, CONFIG_CRLFILE))) {
495 err = isds_set_opt(cisds, IOPT_TLS_CRL_FILE, value);
496 if (err) {
497 fprintf(stderr, _("Could not set file with CRL: %s: %s: %s\n"),
498 value, isds_strerror(err), isds_long_message(cisds));
503 /* Set Czech POINT context up */
504 czechpoint = isds_ctx_create();
505 if (!czechpoint) {
506 fprintf(stderr, _("Could not create Czech POINT context\n"));
507 return -1;
509 err = isds_set_timeout(czechpoint, timeout);
510 if (err) {
511 fprintf(stderr, _("Could not set Czech POINT network timeout: %s\n"),
512 isds_strerror(err));
514 err = isds_set_progress_callback(czechpoint, shi_progressbar, NULL);
515 if (err) {
516 fprintf(stderr, "Could not register network progress bar: %s: %s\n",
517 isds_strerror(err), isds_long_message(cisds));
520 return 0;
524 static int shi_quit(int argc, const char **argv) {
525 shi_exit(EXIT_SUCCESS);
526 return 0;
531 static void shi_help_usage(const char *command) {
532 oprintf(_(
533 "Usage: %s [COMMAND]\n"
534 "Show COMMAND manual or list of currently available commands.\n"
536 command);
540 static int shi_help(int argc, const char **argv) {
541 size_t command_width = 14;
542 int i;
544 if (!commands) {
545 fprintf(stderr, _("No command is available\n"));
546 return -1;
549 if (argc == 2 && argv[1] && *argv[1]) {
550 /* Show usage for given command */
551 for (i = 0; (*commands)[i].name; i++) {
552 if (!strcmp((*commands)[i].name, argv[1])) {
553 if ((*commands)[i].usage)
554 (*commands)[i].usage((*commands)[i].name);
555 else if ((*commands)[i].description) {
556 ohprint((*commands)[i].name, command_width);
557 oprintf(" %s\n", _((*commands)[i].description));
559 else
560 fprintf(stderr,
561 _("%s: %s: Command description not defined\n"),
562 argv[0], argv[1]);
563 return 0;
566 fprintf(stderr, _("%s: %s: No such command exists\n"), argv[0], argv[1]);
567 return -1;
570 /* Or list all commands */
571 oprintf(_("Following commands are available:\n"));
572 for (i = 0; (*commands)[i].name; i++) {
573 ohprint((*commands)[i].name, command_width);
574 oprintf(" %s\n", _((*commands)[i].description));
577 return 0;
581 static void show_version(void) {
582 char *libisds_version = isds_version();
584 oprintf(_("This is Shigofumi version %s.\n"), PACKAGE_VERSION);
585 oprintf(_("\n"
586 "Used libraries\n"
587 "Readline: %s\n"
588 "libisds: %s\n"
590 rl_library_version, libisds_version);
591 free(libisds_version);
595 static int shi_version(int argc, const char **argv) {
596 show_version();
597 oprintf(_(
598 "\n"
599 "-----\n"
600 "It's a shigofumi. A letter delivered from the afterlife. (Fumika)\n"
601 "A message can not be delivered to dead person. (ISDS specification)\n"
602 "Virtual and real world. They can be compatible. (Program author)\n"
604 return 0;
608 static int shi_copying(int argc, const char **argv) {
609 oprintf(_(
610 "This is Shigofumi, an ISDS client.\n"
611 "Copyright (C) 2010, 2011, 2012 Petr Pisar\n"
612 "\n"
613 "This program is free software: you can redistribute it and/or modify\n"
614 "it under the terms of the GNU General Public License as published by\n"
615 "the Free Software Foundation, either version 3 of the License, or\n"
616 "(at your option) any later version.\n"
617 "\n"
618 "This program is distributed in the hope that it will be useful,\n"
619 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
620 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
621 "GNU General Public License for more details.\n"
622 "\n"
623 "You should have received a copy of the GNU General Public License\n"
624 "along with this program. If not, see <http://www.gnu.org/licenses/>.\n"
626 return 0;
630 static int shi_cache(int argc, const char **argv) {
631 const struct isds_list *item;
632 size_t i;
634 if (boxes) {
635 for (item = boxes, i = 0; item; item = item->next, i++);
636 oprintf(_(
637 "Cached box list: %zu\n"),
641 if (messages) {
642 oprintf(_(
643 "Cached message list:\n"
644 "\tDirection: %s\n"
645 "\tMessages: %'lu\n"),
646 (messages_are_outgoing) ? _("Outgoing") : _("Incoming"),
647 total_messages);
650 if (message) {
651 oprintf(_("Cached message: %s\n"),
652 (message->envelope && message->envelope->dmID) ?
653 message->envelope->dmID : _("<Unknown ID>"));
656 return 0;
660 static void shi_chdir_usage(const char *command) {
661 oprintf(_(
662 "Usage: %s [DIRECTORY]\n"
663 "Change working directory to DIRECTORY.\n"
664 "If no DIRECTORY is supplied, HOME directory will be used.\n"),
665 command);
669 static int shi_chdir(int argc, const char **argv) {
670 const char *directory = NULL;
672 if (!argv || argc > 2) {
673 shi_chdir_usage((argv) ? argv[0] : NULL);
674 return -1;
677 if (argc == 2 && argv[1] && *argv[1])
678 directory = argv[1];
679 else {
680 directory = getenv("HOME");
681 if (!directory) {
682 oprintf("Environment variable HOME does not exist\n");
683 return -1;
686 if (chdir(directory)) {
687 oprintf(_("Could not change working directory: %s: %s\n"), directory,
688 strerror(errno));
689 return -1;
692 return 0;
696 static int shi_pwd(int argc, const char **argv) {
697 char *buffer = NULL, *newbuffer;
698 size_t length = 0;
700 while (length += 1024) {
701 newbuffer = realloc(buffer, length);
702 if (!newbuffer) {
703 fprintf(stderr, _("Error: Not enough memory\n"));
704 free(buffer);
705 return -1;
707 buffer = newbuffer;
709 if (getcwd(buffer, length)) {
710 oprintf("%s\n", buffer);
711 free(buffer);
712 return 0;
716 fprintf(stderr, _("Error: Current directory string is too long\n"));
717 free(buffer);
718 return -1;
722 /* Deallocate *@destination and duplicate @new_value if non-NULL. In case of
723 * error, it prints error message and returns -1. Otherwise it returns 0. */
724 static int replace_string(char **destination, const char *new_value) {
725 if (destination == NULL) return -1;
726 zfree(*destination);
727 if (new_value != NULL) {
728 *destination = strdup(new_value);
729 if (*destination == NULL) {
730 fprintf(stderr, _("Not enough memory\n"));
731 return -1;
734 return 0;
738 /* Convert name of PKI format into ISDS format type.
739 * Return -1 in case of invalid name */
740 static int string2pki_format(const char *name, isds_pki_format *format) {
741 if (!name || !format) { return -1; }
742 if (!strcasecmp(name, "PEM")) {
743 *format = PKI_FORMAT_PEM;
744 } else if (!strcasecmp(name, "DER")) {
745 *format = PKI_FORMAT_DER;
746 } else if (!strcasecmp(name, "ENG")) {
747 *format = PKI_FORMAT_ENG;
748 } else {
749 return -1;
751 return 0;
755 /* Convert name of OTP authentication method into ISDS method type.
756 * Return -1 in case of invalid name */
757 static int string2otp_method(const char *name, isds_otp_method *method) {
758 if (!name || !method) { return -1; }
759 if (!strcasecmp(name, "HOTP")) {
760 *method = OTP_HMAC;
761 } else if (!strcasecmp(name, "TOTP")) {
762 *method = OTP_TIME;
763 } else {
764 return -1;
766 return 0;
770 /* Log-in to ISDS.
771 * Return -1 in case of failure, 0 in case of success, +1 in case of partial
772 * success (e.g. TOTP preauthentication to obtain new code succeeded, but user
773 * is still not logged in because second phase is necessary). */
774 static int do_login(void) {
775 isds_error err;
776 struct isds_pki_credentials pki;
777 struct isds_otp otp;
779 /* Build OTP structure */
780 if (NULL != otp_method) {
781 if (string2otp_method(otp_method, &otp.method)) {
782 fprintf(stderr, _("Error: Invalid one-time password "
783 "authentication method `%s'\n"), otp_method);
784 return -1;
788 /* Announce base URL */
789 oprintf(_("ISDS base URL: %s\n"),
790 (server == NULL) ? _("<default>") : server);
792 if (batch_mode) {
793 oprintf(_("Unattended mode detected. "
794 "Make sure credentials have been preset.\n"));
795 } else {
796 oprintf(_("You are going to insert credentials for your account.\n"
797 "Leave blank line to choose default value.\n"));
799 select_completition(COMPL_NONE);
801 /* Ask for user name if not predefined */
802 if (NULL == username) {
803 shi_ask_for_string(&username, _("Input ISDS user name: "),
804 cfg_getstr(configuration, CONFIG_USERNAME), batch_mode);
807 /* Ask for password */
808 shi_ask_for_password(&password, _("Input ISDS password: "),
809 cfg_getstr(configuration, CONFIG_PASSWORD), batch_mode);
811 /* Ask for key password if PKI authentication requested */
812 if (pki_certificate_path) {
813 shi_ask_for_password(&key_password, _("Input private key password: "),
814 cfg_getstr(configuration, CONFIG_KEY_PASSWORD), batch_mode);
817 /* Ask for OTP code if OTP authentication requested */
818 if (NULL != otp_method) {
819 shi_ask_for_password(&otp_code,
820 (otp.method == OTP_TIME) ?
821 _("Input one-time code (empty to send new one): ") :
822 _("Input one-time code: "),
823 cfg_getstr(configuration, CONFIG_OTP_CODE), batch_mode);
824 otp.otp_code = otp_code;
827 select_completition(COMPL_COMMAND);
828 set_prompt(NULL);
830 /* Build PKI structure */
831 if (pki_certificate_path) {
832 pki.engine = pki_engine;
833 if (NULL == pki_certificate_format) {
834 fprintf(stderr, _("Error: No certficate format supplied\n"));
835 return -1;
837 if (string2pki_format(pki_certificate_format, &pki.certificate_format)) {
838 fprintf(stderr, _("Error: Invalid certificate format `%s'\n"),
839 pki_certificate_format);
840 return -1;
842 if (NULL == pki_key_format) {
843 fprintf(stderr, _("Error: No private key format supplied\n"));
844 return -1;
846 if (string2pki_format(pki_key_format, &pki.key_format)) {
847 fprintf(stderr, _("Error: Invalid private key format `%s'\n"),
848 pki_key_format);
849 return -1;
851 pki.certificate = pki_certificate_path;
852 pki.key = pki_key_path;
853 pki.passphrase = key_password;
856 if (NULL != otp_method && OTP_TIME == otp.method && NULL == otp_code)
857 printf(_("Requesting one-time code from server for "
858 "a login...\n"));
859 else
860 printf(_("Logging in...\n"));
861 err = isds_login(cisds, server, username, password,
862 (NULL != pki_certificate_path) ? &pki : NULL,
863 (NULL != otp_method) ? &otp : NULL);
864 if (NULL != otp_method && OTP_TIME == otp.method && NULL == otp_code)
865 finish_isds_operation_with_code(cisds, err, IE_PARTIAL_SUCCESS);
866 else
867 finish_isds_operation(cisds, err);
869 if (IE_PARTIAL_SUCCESS == err) {
870 printf(_("OTP code has been sent by ISDS successfully.\n"
871 "Once you receive the code, retry log-in with "
872 "the code.\n"));
873 return 1;
874 } else if (err) {
875 printf(_("Log-in failed\n"));
876 return -1;
879 oprintf(_("Logged in.\n"));
880 return 0;
884 static struct isds_DbOwnerInfo *do_box(void) {
885 isds_error err;
886 struct isds_DbOwnerInfo *box = NULL;
888 printf(_("Getting box details you are logged in...\n"));
889 err = isds_GetOwnerInfoFromLogin(cisds, &box);
890 finish_isds_operation(cisds, err);
892 return box;
896 static int shi_box(int argc, const char **argv) {
897 struct isds_DbOwnerInfo *box = NULL;
899 box = do_box();
900 if (!box) return -1;
902 format_DbOwnerInfo(box);
904 isds_DbOwnerInfo_free(&box);
905 return 0;
909 /* Get info about box with @id.
910 * @id is UTF-8 encoded
911 * Return NULL in case of error, otherwise box description that caller must
912 * free. */
913 static struct isds_DbOwnerInfo *stat_box(const char *id) {
914 isds_error err;
915 struct isds_DbOwnerInfo criteria;
916 struct isds_list *boxes = NULL, *item;
917 struct isds_DbOwnerInfo *box = NULL;
918 char *id_locale = NULL;
920 if (!id || !*id) return NULL;
922 id_locale = utf82locale(id);
923 memset(&criteria, 0, sizeof(criteria));
924 criteria.dbID = (char *) id;
926 printf(_("Getting details about box with ID `%s'...\n"), id_locale);
927 err = isds_FindDataBox(cisds, &criteria, &boxes);
928 finish_isds_operation(cisds, err);
929 if (err) goto leave;
931 for(item = boxes; item; item = item->next) {
932 if (!item->data) continue;
934 if (item->next) {
935 fprintf(stderr, _("Error: More boxes match ID `%s'\n"), id_locale);
936 goto leave;
939 box = (struct isds_DbOwnerInfo *) item->data;
940 item->data = NULL;
941 break;
944 leave:
945 free(id_locale);
946 isds_list_free(&boxes);
948 return box;
952 static void shi_commercialreceiving_usage(const char *command) {
953 oprintf(_(
954 "Usage: %s [-0|-1] [BOX_ID]\n"
955 "Manipulate commercial receiving box status.\n"
956 " -O switch off receiving of commercial messages\n"
957 " -1 switch on receiving of commercial messages\n"
958 " BOX_ID affects box with ID BOX_ID; default is box you are logged in\n"
959 "If no option is given, show current commercial receiving status.\n"),
960 command);
964 /* Manipulate commercial receiving box status */
965 static int shi_commercialreceiving(int argc, const char **argv) {
966 isds_error err;
967 struct isds_DbOwnerInfo *box = NULL;
968 int opt;
969 int action = -1;
970 char *box_id = NULL, *box_id_locale = NULL;
971 _Bool static_box_id;
972 int retval = 0;
974 optind = 0;
975 while ((opt = getopt(argc, (char * const *)argv, "01")) != -1) {
976 switch (opt) {
977 case '0':
978 action = 0;
979 break;
980 case '1':
981 action = 1;
982 break;
983 default:
984 shi_commercialreceiving_usage((argv)?argv[0]:NULL);
985 return -1;
988 if (optind + 1 < argc) {
989 fprintf(stderr, _("Bad invocation\n"));
990 shi_commercialreceiving_usage((argv)?argv[0]:NULL);
991 return -1;
994 if (!argv[optind] || !*argv[optind]) {
995 /* Get current box ID */
996 box = do_box();
997 if (!box || !box->dbID || !*box->dbID) {
998 isds_DbOwnerInfo_free(&box);
999 fprintf(stderr, _("Could not get current box ID\n"));
1000 return -1;
1002 box_id = box->dbID; static_box_id = 1;
1003 box_id_locale = utf82locale(box_id);
1004 } else {
1005 /* Box ID supplied as argument */
1006 box_id_locale = (char *) argv[optind];
1007 box_id = locale2utf8(box_id_locale); static_box_id = 0;
1008 if (!box_id) {
1009 fprintf(stderr, _("Could not convert box ID `%s' to UTF-8\n"),
1010 box_id_locale);
1011 return -1;
1015 if (action == -1) {
1016 if (!box) box = stat_box(box_id);
1017 if (!box) {
1018 fprintf(stderr, _("Could not get details about box ID `%s'\n"),
1019 box_id_locale);
1020 retval = -1;
1021 goto leave;
1024 oprintf(_("Commercial receiving status of box `%s': "), box_id_locale);
1025 if (!box->dbOpenAddressing)
1026 oprintf(_("Unknown\n"));
1027 else if (*box->dbOpenAddressing)
1028 oprintf(_("Positive\n"));
1029 else
1030 oprintf(_("Negative\n"));
1031 } else {
1032 char *refnumber = NULL;
1033 printf((action) ?
1034 _("Switching `%s' box commercial receiving on...\n"):
1035 _("Switching `%s' box commercial receiving off...\n"),
1036 box_id_locale);
1037 err = isds_switch_commercial_receiving(cisds, box_id, action,
1038 NULL, &refnumber);
1039 finish_isds_operation(cisds, err);
1041 if (!err) {
1042 char *refnumber_locale = utf82locale(refnumber);
1043 oprintf(_("Commercial receiving status successfully changed. "
1044 "Assigned reference number: %s\n"),
1045 refnumber_locale);
1046 free(refnumber_locale);
1047 } else {
1048 oprintf(_("Commercial receiving status has not been changed.\n"));
1049 retval = -1;
1051 free(refnumber);
1054 leave:
1055 if (!static_box_id) free(box_id);
1056 else free(box_id_locale);
1057 isds_DbOwnerInfo_free(&box);
1058 return retval;
1062 static void shi_commercialsending_usage(const char *command) {
1063 oprintf(_(
1064 "Usage: %s [BOX_ID]\n"
1065 "Retrieve permissions to send commercial messages from a box.\n"
1066 " BOX_ID query permissions for box with ID BOX_ID; default is box you\n"
1067 " are logged in\n"),
1068 command);
1072 /* Retrieve permissions to send commercial messages */
1073 static int shi_commercialsending(int argc, const char **argv) {
1074 isds_error err;
1075 struct isds_DbOwnerInfo *box = NULL;
1076 struct isds_list *permissions = NULL, *item;
1077 int opt;
1078 char *box_id = NULL, *box_id_locale = NULL;
1079 _Bool static_box_id;
1080 int ordinar;
1081 int retval = 0;
1083 optind = 0;
1084 while ((opt = getopt(argc, (char * const *)argv, "h")) != -1) {
1085 switch (opt) {
1086 case 'h':
1087 shi_commercialsending_usage((argv)?argv[0]:NULL);
1088 return 0;
1089 default:
1090 shi_commercialsending_usage((argv)?argv[0]:NULL);
1091 return -1;
1094 if (optind + 1 < argc) {
1095 fprintf(stderr, _("Bad invocation\n"));
1096 shi_commercialsending_usage((argv)?argv[0]:NULL);
1097 return -1;
1100 if (!argv[optind] || !*argv[optind]) {
1101 /* Get current box ID */
1102 box = do_box();
1103 if (!box || !box->dbID || !*box->dbID) {
1104 isds_DbOwnerInfo_free(&box);
1105 fprintf(stderr, _("Could not get current box ID\n"));
1106 return -1;
1108 box_id = box->dbID; static_box_id = 1;
1109 box_id_locale = utf82locale(box_id);
1110 } else {
1111 /* Box ID supplied as argument */
1112 box_id_locale = (char *) argv[optind];
1113 box_id = locale2utf8(box_id_locale); static_box_id = 0;
1114 if (!box_id) {
1115 fprintf(stderr, _("Could not convert box ID `%s' to UTF-8\n"),
1116 box_id_locale);
1117 return -1;
1121 printf(_("Querying `%s' box commercial sending permissions...\n"),
1122 box_id_locale);
1123 err = isds_get_commercial_permissions(cisds, box_id, &permissions);
1124 finish_isds_operation(cisds, err);
1126 if (!err) {
1127 oprintf(_("Permissions to send commercial messages from box `%s':\n"),
1128 box_id_locale);
1129 for (item = permissions, ordinar = 0; item; item=item->next) {
1130 if (!item->data) continue;
1131 ordinar++;
1132 oprintf(_("\n* Permission #%d:\n"), ordinar);
1133 format_commercial_permission(item->data);
1135 if (ordinar == 0)
1136 oprintf(_("No permission exists.\n"));
1137 } else {
1138 oprintf(_("Could not list permissions to send commercial messages "
1139 "from box `%s'.\n"), box_id_locale);
1140 retval = -1;
1143 isds_list_free(&permissions);
1144 if (!static_box_id) free(box_id);
1145 else free(box_id_locale);
1146 isds_DbOwnerInfo_free(&box);
1147 return retval;
1151 static int shi_user(int argc, const char **argv) {
1152 isds_error err;
1153 struct isds_DbUserInfo *user = NULL;
1155 printf(_("Getting user details you are logged as...\n"));
1156 err = isds_GetUserInfoFromLogin(cisds, &user);
1157 finish_isds_operation(cisds, err);
1158 if (err) return -1;
1160 format_DbUserInfo(user);
1162 isds_DbUserInfo_free(&user);
1163 return 0;
1167 static void shi_users_usage(const char *command) {
1168 oprintf(_(
1169 "Usage: %s BOX_ID\n"
1170 "Get list of users having access to box with BOX_ID.\n"),
1171 command);
1175 static int shi_users(int argc, const char **argv) {
1176 isds_error err;
1177 struct isds_list *users = NULL, *item;
1178 int ordinar;
1180 if (!argv || !argv[1] || !*argv[1]) {
1181 shi_users_usage((argv)?argv[0]:NULL);
1182 return -1;
1185 printf(_("Getting users of box with ID `%s'...\n"), argv[1]);
1186 err = isds_GetDataBoxUsers(cisds, argv[1], &users);
1187 finish_isds_operation(cisds, err);
1188 if (err) return -1;
1190 for (item = users, ordinar = 0; item; item=item->next) {
1191 if (!item->data) continue;
1192 ordinar++;
1193 oprintf(_("\n* User #%d:\n"), ordinar);
1194 format_DbUserInfo(item->data);
1196 if (ordinar == 0)
1197 oprintf(_("Empty list of users returned.\n"));
1199 isds_list_free(&users);
1200 return 0;
1204 static int show_password_expiration(void) {
1205 isds_error err;
1206 struct timeval *expiration = NULL;
1208 err = isds_get_password_expiration(cisds, &expiration);
1209 finish_isds_operation(cisds, err);
1210 if (err) {
1211 fprintf(stderr, "Could not get password expiration time\n");
1212 return -1;
1215 print_header_timeval(_("Your password expires at"), expiration);
1216 free(expiration);
1217 return 0;
1221 /* Change password in ISDS */
1222 static int do_passwd(void) {
1223 char *old_password = NULL;
1224 char *new_password = NULL;
1225 char *new_password2 = NULL;
1226 struct isds_otp otp;
1227 char *refnumber = NULL;
1228 isds_error err = IE_ERROR;
1229 int retval = 0;
1231 if (replace_string(&otp_method, cfg_getstr(configuration, CONFIG_OTP_METHOD)))
1232 return -1;
1233 /* Build OTP structure */
1234 if (NULL != otp_method) {
1235 if (string2otp_method(otp_method, &otp.method)) {
1236 fprintf(stderr, _("Error: Invalid one-time password "
1237 "authentication method `%s'\n"), otp_method);
1238 return -1;
1242 select_completition(COMPL_NONE);
1244 oprintf(_(
1245 "You are going to change your password. If you don't want to change your\n"
1246 "password, insert empty string or EOF.\n"
1247 "\n"
1248 "You will be asked for your current (old) password and then for new password.\n"
1249 "ISDS forces some criteria new password must fulfill. Current rules are:\n"
1250 "\tLength: minimal 8, maximal 32 characters\n"
1251 "\tMust contain at least: 1 upper case letter, 1 lower case letter, 1 digit\n"
1252 "\tAllowed alphabet: [a-z][A-Z][0-9][!#$%%&()*+,-.:=?@[]_{}|~]\n"
1253 "\tMust differ from last 255 passwords\n"
1254 "\tMust not contain user ID\n"
1255 "\tMust not contain sequence of three or more same characters\n"
1256 "\tMust not start with `qwert', `asdgf', or `12345'\n"
1257 "Finally, you must repeat your new password to avoid mistakes.\n"
1258 "After password change will be confirmed, you must log in again as password\n"
1259 "is transmitted to server on each request.\n"
1260 "\n"));
1262 old_password = ask_for_password(_("Old password: "));
1263 if (!old_password || *old_password == '\0') {
1264 fprintf(stderr, _("No password supplied\n"));
1265 goto error;
1268 /* Ask for OTP code if OTP authentication requested */
1269 zfree(otp_code);
1270 if (NULL != otp_method) {
1271 shi_ask_for_password(&otp_code,
1272 (otp.method == OTP_TIME) ?
1273 _("One-time code (empty to send new one): ") :
1274 _("One-time code: "),
1275 cfg_getstr(configuration, CONFIG_OTP_CODE), batch_mode);
1276 otp.otp_code = otp_code;
1280 if (NULL == otp_method || NULL != otp_code) {
1281 new_password = ask_for_password(_("New password: "));
1282 if (!new_password || *new_password == '\0') {
1283 fprintf(stderr, _("No password supplied\n"));
1284 goto error;
1287 new_password2 = ask_for_password(_("Repeat new password: "));
1288 if (!new_password2 || new_password2 == '\0') {
1289 fprintf(stderr, _("No password supplied\n"));
1290 goto error;
1293 if (strcmp(new_password, new_password2)) {
1294 fprintf(stderr, _("New passwords differ\n"));
1295 goto error;
1299 if (NULL != otp_method && OTP_TIME == otp.method && NULL == otp_code)
1300 printf(_("Requesting one-time code from server for "
1301 "a password change...\n"));
1302 else
1303 printf(_("Changing password...\n"));
1304 err = isds_change_password(cisds, old_password, new_password,
1305 (NULL != otp_method) ? &otp : NULL, &refnumber);
1306 if (NULL != otp_method && OTP_TIME == otp.method && NULL == otp_code)
1307 finish_isds_operation_with_code(cisds, err, IE_PARTIAL_SUCCESS);
1308 else
1309 finish_isds_operation(cisds, err);
1311 if (NULL != refnumber) {
1312 char *refnumber_locale = utf82locale(refnumber);
1313 free(refnumber);
1314 oprintf(_("Assigned reference number: %s\n"), refnumber_locale);
1315 free(refnumber_locale);
1317 if (IE_PARTIAL_SUCCESS == err) {
1318 oprintf(_("OTP code has been sent by ISDS successfully.\n"
1319 "Once you receive the code, retry changing password with "
1320 "the code.\n"));
1321 goto leave;
1322 } else if (err) {
1323 printf(_("Password change failed\n"));
1324 goto error;
1325 } else {
1326 oprintf(_("Password HAS been successfully changed.\n"));
1327 goto leave;
1330 error:
1331 retval = -1;
1332 oprintf(_("Password has NOT been changed!\n"));
1334 leave:
1335 free(old_password);
1336 free(new_password);
1337 free(new_password2);
1338 zfree(otp_code);
1340 set_prompt(NULL);
1341 select_completition(COMPL_COMMAND);
1343 oprintf(_("\n"
1344 "Remember, ISDS password has limited life time.\n"));
1345 return retval;
1349 static void shi_passwd_usage(const char *command) {
1350 oprintf(_(
1351 "Usage: %s [-S]\n"
1352 "Manipulate user password or change it if no option given.\n"
1353 "\n"
1354 "Options:\n"
1355 " -S show password expiration time\n"
1356 ), command);
1360 static int shi_passwd(int argc, const char **argv) {
1361 int opt;
1363 optind = 0;
1364 while ((opt = getopt(argc, (char * const *)argv, "S")) != -1) {
1365 switch (opt) {
1366 case 'S':
1367 return show_password_expiration();
1368 default:
1369 shi_passwd_usage(argv[0]);
1370 return -1;
1373 if (optind != argc || argc > 1) {
1374 fprintf(stderr, _("Bad invocation\n"));
1375 shi_passwd_usage((argv)?argv[0]:NULL);
1376 return -1;
1379 return do_passwd();
1383 static void shi_login_usage(const char *command) {
1384 oprintf(_(
1385 "Usage: %s [OPTIONS] [USER_NAME]\n"
1386 "Attemp to log into ISDS server.\n"
1387 "\n"
1388 "Options:\n"
1389 " -b URL ISDS server base URL\n"
1390 " -c IDENTIFIER user certificate\n"
1391 " -C FORMAT user certificate format\n"
1392 " -k IDENTIFIER user private key\n"
1393 " -K FORMAT user private key format\n"
1394 " -e IDENTIFIER cryptographic engine\n"
1395 " -o METHOD use one-time password authentication method\n"
1396 "\n"
1397 "Recognized certificate and key FORMATS are:\n"
1398 " PEM Base64 encoded serialization in local file\n"
1399 " DER binary serialization in local file\n"
1400 " ENG material is stored in cryptographic engine\n"
1401 "Identifiers of cryptographic engine, certificate, and private key are\n"
1402 "specific for underlying cryptographic library.\n"
1403 "\n"
1404 "Recognized one-time password methods are:\n"
1405 " HOTP HMAC-based OTP method\n"
1406 " TOTP time-based OTP method\n"
1407 "\n"
1408 "Values of omitted options are taken from configuration file.\n"
1409 ), command);
1413 static int shi_login(int argc, const char **argv) {
1414 int opt;
1415 int status;
1417 discard_credentials();
1419 /* Load stored configuration */
1420 if (replace_string(&server, cfg_getstr(configuration, CONFIG_SERVER)))
1421 return -1;
1422 if (replace_string(&otp_method, cfg_getstr(configuration, CONFIG_OTP_METHOD)))
1423 return -1;
1424 if (replace_string(&pki_engine,
1425 cfg_getstr(configuration, CONFIG_KEY_ENGINE)))
1426 return -1;
1427 if (replace_string(&pki_certificate_path,
1428 cfg_getstr(configuration, CONFIG_CERT_PATH)))
1429 return -1;
1430 if (replace_string(&pki_certificate_format,
1431 cfg_getstr(configuration, CONFIG_CERT_FORMAT)))
1432 return -1;
1433 if (replace_string(&pki_key_path,
1434 cfg_getstr(configuration, CONFIG_KEY_PATH)))
1435 return -1;
1436 if (replace_string(&pki_key_format,
1437 cfg_getstr(configuration, CONFIG_KEY_FORMAT)))
1438 return -1;
1440 /* Override configuration with positional arguments */
1441 optind = 0;
1442 while ((opt = getopt(argc, (char * const *)argv, "b:c:C:k:K:e:o:")) != -1) {
1443 switch (opt) {
1444 case 'b':
1445 if (replace_string(&server, optarg)) return -1;
1446 break;
1447 case 'c':
1448 if (replace_string(&pki_certificate_path, optarg)) return -1;
1449 break;
1450 case 'C':
1451 if (replace_string(&pki_certificate_format, optarg)) return -1;
1452 break;
1453 case 'k':
1454 if (replace_string(&pki_key_path, optarg)) return -1;
1455 break;
1456 case 'K':
1457 if (replace_string(&pki_key_format, optarg)) return -1;
1458 break;
1459 case 'e':
1460 if (replace_string(&pki_engine, optarg)) return -1;
1461 break;
1462 case 'o':
1463 if (replace_string(&otp_method, optarg)) return -1;
1464 break;
1465 default:
1466 shi_login_usage(argv[0]);
1467 return -1;
1470 if (optind < argc - 1) {
1471 fprintf(stderr, _("Bad invocation\n"));
1472 shi_login_usage(argv[0]);
1473 return -1;
1475 if (optind == argc - 1) {
1476 username = strdup(argv[optind]);
1479 /* Proceed log-in */
1480 status = do_login();
1481 if (status < 0) return -1;
1483 /* If log-in passed, store configuration */
1484 cfg_setstr(configuration, CONFIG_SERVER, server);
1485 cfg_setstr(configuration, CONFIG_USERNAME, username);
1486 cfg_setstr(configuration, CONFIG_PASSWORD, password);
1487 cfg_setstr(configuration, CONFIG_KEY_PASSWORD, key_password);
1488 cfg_setstr(configuration, CONFIG_OTP_METHOD, otp_method);
1489 cfg_setstr(configuration, CONFIG_KEY_ENGINE, pki_engine);
1490 cfg_setstr(configuration, CONFIG_CERT_PATH, pki_certificate_path);
1491 cfg_setstr(configuration, CONFIG_CERT_FORMAT, pki_certificate_format);
1492 cfg_setstr(configuration, CONFIG_KEY_PATH, pki_key_path);
1493 cfg_setstr(configuration, CONFIG_KEY_FORMAT, pki_key_format);
1495 /* Get some details only if fully logged in */
1496 if (0 == status) {
1497 show_password_expiration();
1499 return 0;
1503 static void shi_debug_usage(const char *command) {
1504 oprintf(_(
1505 "Usage: %s -l LEVEL [-f FACILITY...] [{-e | -o FILE}]\n"
1506 "Debug FACILITIES on LEVEL.\n"
1507 "\n"
1508 "-l LEVEL set log level, valid interval <%d,%d>, default is %d\n"
1509 " %d is no logging, %d critical, %d errors,\n"
1510 " %d warnings, %d info, %d debug, %d all\n"
1511 "-f FACILITY debug only given facility, repeat this option to debug\n"
1512 " more facilities; valid values: none, http, soap, isds,\n"
1513 " file, sec, xml, all; default is none\n"
1514 "-e write debug log into stderr\n"
1515 "-o FILE append debug log to FILE\n"
1517 command,
1518 ILL_NONE, ILL_ALL, ILL_NONE,
1519 ILL_NONE, ILL_CRIT, ILL_ERR, ILL_WARNING,
1520 ILL_INFO, ILL_DEBUG, ILL_ALL);
1523 static int shi_debug(int argc, const char **argv) {
1524 int opt;
1525 int log_level = ILL_NONE;
1526 isds_log_facility log_facility = ILF_NONE;
1527 char *file = NULL;
1528 _Bool close_log = 0;
1530 optind = 0;
1531 while ((opt = getopt(argc, (char * const *)argv, "l:f:eo:")) != -1) {
1532 switch (opt) {
1533 case 'l':
1534 log_level = normalize_log_level(atoi(optarg));
1535 break;
1536 case 'f':
1537 if (add_log_facility(&log_facility, optarg)) return -1;
1538 break;
1539 case 'e':
1540 close_log = 1;
1541 case 'o':
1542 file = optarg;
1543 break;
1544 default:
1545 shi_debug_usage(argv[0]);
1546 return -1;
1549 if (optind == 1 || optind != argc) {
1550 fprintf(stderr, _("Bad invocation\n"));
1551 shi_debug_usage(argv[0]);
1552 return -1;
1555 /* Redirect log */
1556 if (close_log) {
1557 isds_set_log_callback(NULL, NULL);
1559 if (logger_fd != -1) {
1560 if (-1 == close(logger_fd)) {
1561 fprintf(stderr, _("Closing log file failed: %s\n"),
1562 strerror(errno));
1563 return -1;
1566 cfg_setstr(configuration, CONFIG_LOGFILE, NULL);
1568 if (do_log_to_file(file))
1569 return -1;
1570 if (file) cfg_setstr(configuration, CONFIG_LOGFILE, file);
1572 /* Set log levels */
1573 isds_set_logging(log_facility, log_level);
1574 cfg_setint(configuration, CONFIG_LOGLEVEL, log_level);
1575 save_log_facility(log_facility);
1577 return 0;
1581 static void show_setting_str(const char *variable, _Bool cenzore) {
1582 if (!variable) return;
1584 const char *value = cfg_getstr(configuration, variable);
1586 if (value) {
1587 if (cenzore)
1588 oprintf(_("%s = <set>\n"), variable);
1589 else
1590 oprintf(_("%s = `%s'\n"), variable, value);
1591 } else {
1592 oprintf(_("%s = <unset>\n"), variable);
1597 static void show_setting_boolean(const char *variable) {
1598 if (!variable) return;
1600 _Bool value = cfg_getbool(configuration, variable);
1602 if (value) {
1603 oprintf(_("%s = <true>\n"), variable);
1604 } else {
1605 oprintf(_("%s = <false>\n"), variable);
1610 static void show_setting_int(const char *variable) {
1611 if (!variable) return;
1613 long int value = cfg_getint(configuration, variable);
1615 oprintf(_("%s = %ld\n"), variable, value);
1619 static void show_setting_strlist(const char *variable) {
1620 if (!variable) return;
1622 int length = cfg_size(configuration, variable);
1624 if (length <= 0) {
1625 oprintf(_("%s = <unset>\n"), variable);
1626 } else {
1627 oprintf(_("%s = {"), variable);
1628 for (int i = 0; i < length; i++) {
1629 const char *value = cfg_getnstr(configuration, variable, i);
1630 if (i < length - 1)
1631 oprintf(_("`%s', "), value);
1632 else
1633 oprintf(_("`%s'}\n"), value);
1639 static int shi_settings(int argc, const char **argv) {
1640 /*int opt;
1641 int log_level = ILL_NONE;
1642 isds_log_facility log_facility = ILF_NONE;
1643 char *file = NULL;
1644 _Bool close_log = 0;*/
1647 optind = 0;
1648 while ((opt = getopt(argc, (char * const *)argv, "l:f:eo:")) != -1) {
1649 switch (opt) {
1650 case 'l':
1651 log_level = normalize_log_level(atoi(optarg));
1652 break;
1653 case 'f':
1654 if (add_log_facility(&log_facility, optarg)) return -1;
1655 break;
1656 case 'e':
1657 close_log = 1;
1658 case 'o':
1659 file = optarg;
1660 break;
1661 default:
1662 shi_debug_usage(argv[0]);
1663 return -1;
1666 if (optind == 1 || optind != argc) {
1667 printf(_("Bad invocation\n"));
1668 shi_debug_usage(argv[0]);
1669 return -1;
1673 oprintf(_("Current settings:\n"));
1675 show_setting_str(CONFIG_SERVER, 0);
1676 show_setting_str(CONFIG_USERNAME, 0);
1677 show_setting_str(CONFIG_PASSWORD, 1),
1678 show_setting_str(CONFIG_CERT_FORMAT, 0),
1679 show_setting_str(CONFIG_CERT_PATH, 0),
1680 show_setting_str(CONFIG_KEY_ENGINE, 0),
1681 show_setting_str(CONFIG_KEY_FORMAT, 0),
1682 show_setting_str(CONFIG_KEY_PATH, 0),
1683 show_setting_str(CONFIG_KEY_PASSWORD, 1),
1684 show_setting_str(CONFIG_OTP_METHOD, 0),
1685 show_setting_str(CONFIG_OTP_CODE, 1),
1686 show_setting_boolean(CONFIG_VERIFYSERVER);
1687 show_setting_str(CONFIG_CAFILE, 0);
1688 show_setting_str(CONFIG_CADIRECTORY, 0);
1689 show_setting_boolean(CONFIG_CLEAN_TEMPORARY_FILES);
1690 show_setting_str(CONFIG_CRLFILE, 0);
1691 show_setting_int(CONFIG_TIMEOUT);
1692 show_setting_strlist(CONFIG_LOGFACILITIES);
1693 show_setting_str(CONFIG_LOGFILE, 0);
1694 show_setting_int(CONFIG_LOGLEVEL);
1695 show_setting_boolean(CONFIG_MARKMESSAGEREAD);
1696 show_setting_boolean(CONFIG_NORMALIZEMIMETYPE);
1697 show_setting_strlist(CONFIG_OPENCOMMAND);
1698 show_setting_boolean(CONFIG_OVERWRITEFILES);
1700 return 0;
1704 static void shi_find_box_usage(const char *command) {
1705 oprintf(_(
1706 "Usage: %s {OPTION... | BOX_ID}\n"
1707 "Get information about box with BOX_ID or boxes meeting other criteria.\n"
1708 "Each search option requires an argument:\n"
1709 " -t box type; accepted values:\n"
1710 " FO Private individual\n"
1711 " PFO Self-employed individual\n"
1712 " PFO_ADVOK Lawyer\n"
1713 " PFO_DANPOR Tax advisor\n"
1714 " PFO_INSSPR Insolvency administrator\n"
1715 " PO Organisation\n"
1716 " PO_ZAK Organization based by law\n"
1717 " PO_REQ Organization based on request\n"
1718 " OVM Public authority\n"
1719 " OVM_NOTAR Notary\n"
1720 " OVM_EXEKUT Executor\n"
1721 " OVM_REQ Public authority based on request\n"
1722 " -j identity number\n"
1723 "\n"
1724 "Person name options:\n"
1725 " -f first name\n"
1726 " -m middle name\n"
1727 " -l last name\n"
1728 " -b last name at birth\n"
1729 " -s subject name\n"
1730 "\n"
1731 "Birth options:\n"
1732 " -d birth date (locale or full ISO 8601 date)\n"
1733 " -w birth city\n"
1734 " -y birth county\n"
1735 " -c birth state\n"
1736 "\n"
1737 "Address:\n"
1738 " -W city\n"
1739 " -S street\n"
1740 " -z number in street\n"
1741 " -Z number in municipality\n"
1742 " -P ZIP code\n"
1743 " -C state\n"
1744 "\n"
1745 "Other options:\n"
1746 " -n nationality\n"
1747 " -e e-mail\n"
1748 " -p phone number\n"
1749 " -i identifier\n"
1750 " -r registry code\n"
1751 " -a box status; accepted values:\n"
1752 " ACCESSIBLE Accessible\n"
1753 " TEMP_INACCESSIBLE Temporary inaccessible\n"
1754 " NOT_YET_ACCESSIBLE Not yet accessible\n"
1755 " PERM_INACCESSIBLE Permanently inaccessible\n"
1756 " REMOVED Deleted\n"
1757 " -o act as public authority; boolean values: 0 is false, 1 is true\n"
1758 " -k receive commercial messages; boolean values\n"
1759 "\n"
1760 "Not all option combinations are meaningful or allowed. For example box\n"
1761 "type is always required (except direct box ID query).\n"
1762 "ISDS can refuse to answer to much broad query. Not all boxes are searchable\n"
1763 "by every user.\n"
1765 command);
1769 /* Allow reassignment */
1770 #define FILL_OR_LEAVE(variable, locale) { \
1771 zfree(variable); \
1772 (variable) = locale2utf8(locale); \
1773 if (!(variable)) { \
1774 fprintf(stderr, _("Error: Not enough memory\n")); \
1775 retval = -1; \
1776 goto leave; \
1780 #define CALLOC_OR_LEAVE(structure) { \
1781 if (!(structure)) { \
1782 (structure) = calloc(1, sizeof(*(structure))); \
1783 if (!(structure)) { \
1784 fprintf(stderr, _("Error: Not enough memory\n")); \
1785 retval = -1; \
1786 goto leave; \
1791 #define FILL_BOOLEAN_OR_LEAVE(variable, locale) { \
1792 zfree(variable); \
1793 (variable) = malloc(sizeof(*(variable))); \
1794 if (!(variable)) { \
1795 fprintf(stderr, _("Error: Not enough memory\n")); \
1796 retval = -1; \
1797 goto leave; \
1799 if (!strcmp((locale), "0")) *(variable) = 0; \
1800 else if (!strcmp((locale), "1")) *(variable) = 1; \
1801 else { \
1802 fprintf(stderr, _("%s: %s: Unknown boolean value\n"), \
1803 argv[0], (locale)); \
1804 retval = -1; \
1805 goto leave; \
1809 #define FILL_LONGINT_OR_LEAVE(variable, locale) { \
1810 if (!(locale) || !*(locale)) { \
1811 fprintf(stderr, _("%s: Empty integer value\n"), argv[0]); \
1812 retval = -1; \
1813 goto leave; \
1815 char *endptr; \
1816 zfree(variable); \
1817 (variable) = malloc(sizeof(*(variable))); \
1818 if (!(variable)) { \
1819 fprintf(stderr, _("Error: Not enough memory\n")); \
1820 retval = -1; \
1821 goto leave; \
1823 (*variable) = strtol((locale), &endptr, 0); \
1824 if (*endptr) { \
1825 fprintf(stderr, _("%s: %s: Invalid integer value\n"), \
1826 argv[0], (locale)); \
1827 retval = -1; \
1828 goto leave; \
1832 static int shi_find_box(int argc, const char **argv) {
1833 int opt;
1834 isds_error err;
1835 struct isds_DbOwnerInfo *criteria = NULL;
1836 struct isds_list *item;
1837 int order = 0;
1838 int retval = 0;
1840 if (!argv || !argv[1] || !*argv[1]) {
1841 fprintf(stderr, _("Error: No argument supplied\n"));
1842 shi_find_box_usage((argv)?argv[0]:NULL);
1843 return -1;
1846 criteria = calloc(1, sizeof(*criteria));
1847 if (!criteria) {
1848 fprintf(stderr, _("Error: Not enough memory\n"));
1849 retval = -1;
1850 goto leave;
1853 /* Parse options */
1854 optind = 0;
1855 while ((opt = getopt(argc, (char * const *)argv, "t:j:s:"
1856 "f:m:l:b:s:" "d:w:y:c:" "W:S:z:Z:P:C:"
1857 "n:e:p:i:r:a:o:k:")) != -1) {
1858 switch (opt) {
1859 case 't':
1860 criteria->dbType = malloc(sizeof(*criteria->dbType));
1861 if (!criteria->dbType) {
1862 fprintf(stderr, _("Error: Not enough memory\n"));
1863 retval = -1;
1864 goto leave;
1866 if (!strcmp(optarg, "FO"))
1867 *criteria->dbType = DBTYPE_FO;
1868 else if (!strcmp(optarg, "PFO"))
1869 *criteria->dbType = DBTYPE_PFO;
1870 else if (!strcmp(optarg, "PFO_ADVOK"))
1871 *criteria->dbType = DBTYPE_PFO_ADVOK;
1872 else if (!strcmp(optarg, "PFO_DANPOR"))
1873 *criteria->dbType = DBTYPE_PFO_DANPOR;
1874 else if (!strcmp(optarg, "PFO_INSSPR"))
1875 *criteria->dbType = DBTYPE_PFO_INSSPR;
1876 else if (!strcmp(optarg, "PO"))
1877 *criteria->dbType = DBTYPE_PO;
1878 else if (!strcmp(optarg, "PO_ZAK"))
1879 *criteria->dbType = DBTYPE_PO_ZAK;
1880 else if (!strcmp(optarg, "PO_REQ"))
1881 *criteria->dbType = DBTYPE_PO_REQ;
1882 else if (!strcmp(optarg, "OVM"))
1883 *criteria->dbType = DBTYPE_OVM;
1884 else if (!strcmp(optarg, "OVM_NOTAR"))
1885 *criteria->dbType = DBTYPE_OVM_NOTAR;
1886 else if (!strcmp(optarg, "OVM_EXEKUT"))
1887 *criteria->dbType = DBTYPE_OVM_EXEKUT;
1888 else if (!strcmp(optarg, "OVM_REQ"))
1889 *criteria->dbType = DBTYPE_OVM_REQ;
1890 else {
1891 fprintf(stderr, _("%s: %s: Unknown box type\n"),
1892 argv[0], optarg);
1893 retval = -1;
1894 goto leave;
1896 break;
1898 case 'j':
1899 FILL_OR_LEAVE(criteria->ic, optarg);
1900 break;
1902 /* Person name */
1903 case 'f':
1904 CALLOC_OR_LEAVE(criteria->personName);
1905 FILL_OR_LEAVE(criteria->personName->pnFirstName, optarg);
1906 break;
1907 case 'm':
1908 CALLOC_OR_LEAVE(criteria->personName);
1909 FILL_OR_LEAVE(criteria->personName->pnMiddleName, optarg);
1910 break;
1911 case 'l':
1912 CALLOC_OR_LEAVE(criteria->personName);
1913 FILL_OR_LEAVE(criteria->personName->pnLastName, optarg);
1914 break;
1915 case 'b':
1916 CALLOC_OR_LEAVE(criteria->personName);
1917 FILL_OR_LEAVE(criteria->personName->pnLastNameAtBirth, optarg);
1918 break;
1919 case 's':
1920 FILL_OR_LEAVE(criteria->firmName, optarg);
1921 break;
1923 /* Birth */
1924 case 'd':
1925 CALLOC_OR_LEAVE(criteria->birthInfo);
1926 criteria->birthInfo->biDate = datestring2tm(optarg);
1927 if (!criteria->birthInfo->biDate) {
1928 fprintf(stderr, _("Error: Could not parse date: %s\n"),
1929 optarg);
1930 retval = -1;
1931 goto leave;
1933 break;
1934 case 'w':
1935 CALLOC_OR_LEAVE(criteria->birthInfo);
1936 FILL_OR_LEAVE(criteria->birthInfo->biCity, optarg);
1937 break;
1938 case 'y':
1939 CALLOC_OR_LEAVE(criteria->birthInfo);
1940 FILL_OR_LEAVE(criteria->birthInfo->biCounty, optarg);
1941 break;
1942 case 'c':
1943 CALLOC_OR_LEAVE(criteria->birthInfo);
1944 FILL_OR_LEAVE(criteria->birthInfo->biState, optarg);
1945 break;
1947 /* Address */
1948 case 'W':
1949 CALLOC_OR_LEAVE(criteria->address);
1950 FILL_OR_LEAVE(criteria->address->adCity, optarg);
1951 break;
1952 case 'S':
1953 CALLOC_OR_LEAVE(criteria->address);
1954 FILL_OR_LEAVE(criteria->address->adStreet, optarg);
1955 break;
1956 case 'z':
1957 CALLOC_OR_LEAVE(criteria->address);
1958 FILL_OR_LEAVE(criteria->address->adNumberInStreet, optarg);
1959 break;
1960 case 'Z':
1961 CALLOC_OR_LEAVE(criteria->address);
1962 FILL_OR_LEAVE(criteria->address->adNumberInMunicipality,
1963 optarg);
1964 break;
1965 case 'P':
1966 CALLOC_OR_LEAVE(criteria->address);
1967 FILL_OR_LEAVE(criteria->address->adZipCode, optarg);
1968 break;
1969 case 'C':
1970 CALLOC_OR_LEAVE(criteria->address);
1971 FILL_OR_LEAVE(criteria->address->adState, optarg);
1972 break;
1974 /* Other options */
1975 case 'n':
1976 FILL_OR_LEAVE(criteria->nationality, optarg);
1977 break;
1978 case 'e':
1979 FILL_OR_LEAVE(criteria->email, optarg);
1980 break;
1981 case 'p':
1982 FILL_OR_LEAVE(criteria->telNumber, optarg);
1983 break;
1984 case 'i':
1985 FILL_OR_LEAVE(criteria->identifier, optarg);
1986 break;
1987 case 'r':
1988 FILL_OR_LEAVE(criteria->registryCode, optarg);
1989 break;
1990 case 'a':
1991 criteria->dbState = malloc(sizeof(*criteria->dbState));
1992 if (!criteria->dbState) {
1993 fprintf(stderr, _("Error: Not enough memory\n"));
1994 retval = -1;
1995 goto leave;
1997 if (!strcmp(optarg, "ACCESSIBLE"))
1998 *criteria->dbState = DBSTATE_ACCESSIBLE;
1999 else if (!strcmp(optarg, "TEMP_INACCESSIBLE"))
2000 *criteria->dbState = DBSTATE_TEMP_UNACCESSIBLE;
2001 else if (!strcmp(optarg, "NOT_YET_ACCESSIBLE"))
2002 *criteria->dbState = DBSTATE_NOT_YET_ACCESSIBLE;
2003 else if (!strcmp(optarg, "PERM_INACCESSIBLE"))
2004 *criteria->dbState = DBSTATE_PERM_UNACCESSIBLE;
2005 else if (!strcmp(optarg, "REMOVED"))
2006 *criteria->dbState = DBSTATE_REMOVED;
2007 else {
2008 fprintf(stderr, _("%s: %s: Unknown box status\n"),
2009 argv[0], optarg);
2010 retval = -1;
2011 goto leave;
2013 break;
2014 case 'o':
2015 FILL_BOOLEAN_OR_LEAVE(criteria->dbEffectiveOVM, optarg);
2016 break;
2017 case 'k':
2018 FILL_BOOLEAN_OR_LEAVE(criteria->dbOpenAddressing, optarg);
2019 break;
2021 default:
2022 shi_find_box_usage(argv[0]);
2023 retval = -1;
2024 goto leave;
2028 /* There must be an option and all of them must be recognized, if not only
2029 * BOX_ID supplied */
2030 if (argc > 2 && optind != argc) {
2031 fprintf(stderr, _("Error: Superfluous argument\n"));
2032 shi_find_box_usage(argv[0]);
2033 retval = -1;
2034 goto leave;
2037 /* If only box ID is supplied use it */
2038 if (argc == 2 && argv[1] && *argv[1]) {
2039 criteria->dbID = locale2utf8(argv[1]);
2040 if (!criteria->dbID) {
2041 fprintf(stderr, _("Error: Not enough memory\n"));
2042 retval = -1;
2043 goto leave;
2047 printf(_("Searching boxes...\n"));
2048 err = isds_FindDataBox(cisds, criteria, &boxes);
2049 finish_isds_operation(cisds, err);
2050 if (err) return -1;
2052 for(item = boxes; item; item = item->next) {
2053 if (!item->data) continue;
2054 order++;
2056 oprintf(_("\n* Result #%d:\n"), order);
2057 format_DbOwnerInfo(item->data);
2060 leave:
2061 isds_DbOwnerInfo_free(&criteria);
2062 return retval;
2066 static void shi_stat_box_usage(const char *command) {
2067 oprintf(_(
2068 "Usage: %s BOX_ID...\n"
2069 "Get status of box with BOX_ID. More boxes can be specified.\n"),
2070 command);
2074 /* Get boxes status */
2075 static int shi_stat_box(int argc, const char **argv) {
2076 isds_error err;
2077 char *id = NULL;
2078 long int status;
2080 if (!argv || !*argv || argc < 2 || !argv[1]) {
2081 fprintf(stderr, _("Missing box ID\n"));
2082 shi_stat_box_usage((argv[0])?argv[0]:NULL);
2083 return -1;
2086 for (int i = 1; i < argc; i++) {
2087 if (!argv[i] || !*argv[i]) continue;
2089 free(id);
2090 id = locale2utf8(argv[i]);
2091 if (!id) {
2092 fprintf(stderr, _("%s: Could not covert box ID to UTF-8\n"),
2093 argv[i]);
2094 return -1;
2097 printf(_("Getting status of box `%s'...\n"), argv[i]);
2098 err = isds_CheckDataBox(cisds, id, &status);
2099 finish_isds_operation(cisds, err);
2100 if (err) return -1;
2102 oprintf(_("Status of box `%s': %s\n"),
2103 argv[i], DbState2string(&status));
2106 return 0;
2110 static void shi_boxlist_usage(const char *command) {
2111 oprintf(_(
2112 "Usage: %s LIST_TYPE FILE\n"
2113 "Save latest snapshot of list of boxes of type LIST_TYPE into FILE.\n"
2114 "\n"
2115 "Currently recognized LIST_TYPES are:\n"
2116 " ALL All boxes\n"
2117 " UPG Effectively OVM boxes\n"
2118 " OVM OVM gross type boxes\n"
2119 " OPN Boxes allowing receiving commercial messages\n"
2120 "\n"
2121 "Not all types are available to all users. E.g. only `UPG' is available\n"
2122 "to regular users.\n"
2123 "\n"
2124 "The format of the list is comma separate list that is packed into\n"
2125 "ZIP archive. Name of the list file denotes time of snapshoting\n"
2126 "the list. The snapshot is created by ISDS once a day.\n"),
2127 command);
2131 /* Download list of boxes */
2132 static int shi_boxlist(int argc, const char **argv) {
2133 isds_error err;
2134 const char *type_locale;
2135 char *type = NULL;
2136 void *buffer = NULL;
2137 size_t buffer_length;
2138 int retval;
2140 if (!argv || !*argv || argc < 3 || !argv[1] || !argv[2]) {
2141 fprintf(stderr, _("Bad number of arguments\n"));
2142 shi_boxlist_usage((argv)?argv[0]:NULL);
2143 return -1;
2145 type_locale = argv[1];
2147 type = locale2utf8(type_locale);
2148 if (!type) {
2149 fprintf(stderr, _("%s: Could not covert list type to UTF-8\n"),
2150 type_locale);
2151 return -1;
2154 printf(_("Getting `%s' list of boxes...\n"), type_locale);
2155 err = isds_get_box_list_archive(cisds, type, &buffer, &buffer_length);
2156 finish_isds_operation(cisds, err);
2157 free(type);
2158 if (err) {
2159 return -1;
2162 retval = save_data_to_file(argv[2], -1, buffer, buffer_length,
2163 "application/zip",
2164 cfg_getbool(configuration, CONFIG_OVERWRITEFILES));
2166 free(buffer);
2167 return retval;
2171 static void shi_compose_usage(const char *command) {
2172 oprintf(_(
2173 "Usage: %s OPTION...\n"
2174 "Compose and send a message to recipient defined by his box ID.\n"
2175 "Each option requires an argument (if not stated otherwise):\n"
2176 " -s * message subject\n"
2177 "\n"
2178 "Recipient options:\n"
2179 " -b * recipient box ID\n"
2180 " -U organisation unit name\n"
2181 " -N organisation unit number\n"
2182 " -P to hands of given person\n"
2183 "\n"
2184 "Sender organisation structure options:\n"
2185 " -I publish user's identity (NO argument allowed)\n"
2186 " -u unit name\n"
2187 " -n unit number\n"
2188 "\n"
2189 "Message identifier options:\n"
2190 " -r sender reference number\n"
2191 " -f sender file ID\n"
2192 " -R recipient reference number\n"
2193 " -F recipient file ID\n"
2194 "\n"
2195 "Legal title options:\n"
2196 " -y year act has been issued\n"
2197 " -a ordinal number of act in a year\n"
2198 " -e section of the act\n"
2199 " -o paragraph of the act\n"
2200 " -i point of the paragraph of the act\n"
2201 "\n"
2202 "Delivery options:\n"
2203 " -p personal delivery required\n"
2204 " -t allow substitutable delivery\n"
2205 " -A non-OVM sender acts as public authority\n"
2206 " -C commercial type; accepted values:\n"
2207 " K Commercial message paid by sender or sponsor\n"
2208 " I Initiatory commercial message offering to pay response\n"
2209 " O Commercial response paid by recipient\n"
2210 " V Public message paid by government\n"
2211 " Missing option defaults to K or O (see `commercialsending' command)\n"
2212 " if sending to non-OVM recipient with enabled commercial receiving,\n"
2213 " otherwise it defaults to V.\n"
2214 "\n"
2215 "Document options:\n"
2216 " -d * read document from local file\n"
2217 " -D document name (defaults to base local file name)\n"
2218 " -x transport subset of the document as a XML.\n"
2219 " Argument is XPath expression specifying desired node set\n"
2220 " -m override MIME type (guessed on -d)\n"
2221 " -g document ID (must be unique per message)\n"
2222 " -G reference to other document using its ID\n"
2223 " -c document is digital signature of other document (NO argument\n"
2224 " allowed)\n"
2225 "\n"
2226 "Options marked with asterisk are mandatory, other are optional. Another soft\n"
2227 "dependencies can emerge upon using specific option. They are not mandated by\n"
2228 "ISDS currently, but client library or this program can force them to assure\n"
2229 "semantically complete message. Following soft dependencies are recommended:\n"
2230 " -y <=> -a act number and year must be used at the same time\n"
2231 " -i => -o act point requires act paragraph\n"
2232 " -o => -e act paragraph requires act section\n"
2233 " -e => -a act section requires act number\n"
2234 " -G => -g document with referenced ID must exist\n"
2235 " -c => -G signature must refer to signed document\n"
2236 " -c first document cannot be signature\n"
2237 " -C I => -r sender reference number allows responder to reply to this message\n"
2238 " -C O -> -R recipient reference number must match sender reference number of\n"
2239 " initiatory message\n"
2240 "\n"
2241 "More documents can be attached to a message by repeating `-d' option.\n"
2242 "Document order will be preserved. Other document options affect immediately\n"
2243 "preceding `-d' document only. E.g. `-d /tmp/foo.pdf -m application/pdf\n"
2244 "-d /tmp/bar.txt -m text/plain' attaches first PDF file, then textual file.\n"
2245 "\n"
2246 "The same applies to recipient options that must start with box ID (-b).\n"
2247 "If more recipients specified, each of them will get a copy of composed\n"
2248 "message. ISDS will assign message identifier to each copy in turn.\n"
2250 command);
2254 static int shi_compose(int argc, const char **argv) {
2255 int opt;
2256 isds_error err;
2257 int retval = 0;
2258 struct isds_message *message = NULL;
2259 struct isds_envelope *envelope = NULL;
2260 struct isds_list *documents = NULL;
2261 struct isds_document *document = NULL;
2262 struct isds_list *copies = NULL, *copy_item = NULL;
2263 struct isds_message_copy *copy = NULL;
2264 char *message_id_locale = NULL, *recipient_id_locale = NULL,
2265 *dmStatus_locale = NULL;
2267 if (!argv || !argv[1] || !*argv[1]) {
2268 fprintf(stderr, _("Error: No argument supplied\n"));
2269 shi_compose_usage((argv)?argv[0]:NULL);
2270 return -1;
2273 message = calloc(1, sizeof(*message));
2274 if (!message) {
2275 fprintf(stderr, _("Error: Not enough memory\n"));
2276 retval = -1;
2277 goto leave;
2279 envelope = calloc(1, sizeof(*envelope));
2280 if (!envelope) {
2281 fprintf(stderr, _("Error: Not enough memory\n"));
2282 retval = -1;
2283 goto leave;
2285 message->envelope = envelope;
2287 /* Parse options */
2288 optind = 0;
2289 while ((opt = getopt(argc, (char * const *)argv, "s:" "b:U:N:P:" "Iu:n:"
2290 "r:f:R:F:" "y:a:e:o:i:" "p:t:A:C:" "d:D:x:m:g:G:c"
2291 )) != -1) {
2292 switch (opt) {
2293 case 's':
2294 FILL_OR_LEAVE(envelope->dmAnnotation, optarg);
2295 break;
2297 /* Recipient options */
2298 case 'b':
2299 copy = NULL;
2300 if (!copies) {
2301 /* First recipient */
2302 CALLOC_OR_LEAVE(copies);
2303 copies->destructor =
2304 (void(*)(void **)) isds_message_copy_free;
2305 copy_item = copies;
2306 } else {
2307 /* Next recipient */
2308 CALLOC_OR_LEAVE(copy_item->next);
2309 copy_item->next->destructor =
2310 (void(*)(void **)) isds_message_copy_free;
2311 copy_item = copy_item->next;
2313 CALLOC_OR_LEAVE(copy);
2314 copy_item->data = copy;
2316 /* Copy recipient box ID */
2317 FILL_OR_LEAVE(copy->dbIDRecipient, optarg);
2318 break;
2319 case 'U':
2320 if (!copy) {
2321 fprintf(stderr,
2322 _("Error: %s: Recipient box ID (-b) must precede "
2323 "recipient organisation unit name (-%c)\n"),
2324 optarg, opt);
2325 retval = -1;
2326 goto leave;
2328 FILL_OR_LEAVE(copy->dmRecipientOrgUnit, optarg);
2329 break;
2330 case 'N':
2331 if (!copy) {
2332 fprintf(stderr,
2333 _("Error: %s: Recipient box ID (-b) must precede "
2334 "recipient organisation unit number (-%c)\n"),
2335 optarg, opt);
2336 retval = -1;
2337 goto leave;
2339 FILL_LONGINT_OR_LEAVE(copy->dmRecipientOrgUnitNum, optarg);
2340 break;
2341 case 'P':
2342 if (!copy) {
2343 fprintf(stderr,
2344 _("Error: %s: Recipient box ID (-b) must precede "
2345 "to-hands option (-%c)\n"), optarg, opt);
2346 retval = -1;
2347 goto leave;
2349 FILL_OR_LEAVE(copy->dmToHands, optarg);
2350 break;
2352 /* Sender organisation structure options */
2353 case 'I':
2354 FILL_BOOLEAN_OR_LEAVE(envelope->dmPublishOwnID, optarg);
2355 case 'u':
2356 FILL_OR_LEAVE(envelope->dmSenderOrgUnit, optarg);
2357 break;
2358 case 'n':
2359 FILL_LONGINT_OR_LEAVE(envelope->dmSenderOrgUnitNum, optarg);
2360 break;
2362 /* Message identifier options */
2363 case 'r':
2364 FILL_OR_LEAVE(envelope->dmSenderRefNumber, optarg);
2365 break;
2366 case 'f':
2367 FILL_OR_LEAVE(envelope->dmSenderIdent, optarg);
2368 break;
2369 case 'R':
2370 FILL_OR_LEAVE(envelope->dmRecipientRefNumber, optarg);
2371 break;
2372 case 'F':
2373 FILL_OR_LEAVE(envelope->dmRecipientIdent, optarg);
2374 break;
2376 /* Legal title options */
2377 case 'y':
2378 FILL_LONGINT_OR_LEAVE(envelope->dmLegalTitleYear, optarg);
2379 break;
2380 case 'a':
2381 FILL_LONGINT_OR_LEAVE(envelope->dmLegalTitleLaw, optarg);
2382 break;
2383 case 'e':
2384 FILL_OR_LEAVE(envelope->dmLegalTitleSect, optarg);
2385 break;
2386 case 'o':
2387 FILL_OR_LEAVE(envelope->dmLegalTitlePar, optarg);
2388 break;
2389 case 'i':
2390 FILL_OR_LEAVE(envelope->dmLegalTitlePoint, optarg);
2391 break;
2393 /* Delivery options */
2394 case 'p':
2395 FILL_BOOLEAN_OR_LEAVE(envelope->dmPersonalDelivery, optarg);
2396 break;
2397 case 't':
2398 FILL_BOOLEAN_OR_LEAVE(envelope->dmAllowSubstDelivery, optarg);
2399 break;
2400 case 'A':
2401 FILL_BOOLEAN_OR_LEAVE(envelope->dmOVM, optarg);
2402 break;
2403 case 'C':
2404 FILL_OR_LEAVE(envelope->dmType, optarg);
2405 break;
2407 /* Document options */
2408 case 'd':
2409 document = NULL;
2410 if (!documents) {
2411 /* First document */
2412 CALLOC_OR_LEAVE(message->documents);
2413 message->documents->destructor =
2414 (void(*)(void **)) isds_document_free;
2415 documents = message->documents;
2416 CALLOC_OR_LEAVE(document);
2417 documents->data = document;
2418 document->dmFileMetaType = FILEMETATYPE_MAIN;
2419 } else {
2420 /* Next document */
2421 CALLOC_OR_LEAVE(documents->next);
2422 documents->next->destructor =
2423 (void(*)(void **)) isds_document_free;
2424 documents = documents->next;
2425 CALLOC_OR_LEAVE(document);
2426 documents->data = document;
2427 document->dmFileMetaType = FILEMETATYPE_ENCLOSURE;
2430 if (load_data_from_file(optarg, &document->data,
2431 &document->data_length, &document->dmMimeType)) {
2432 retval = -1;
2433 goto leave;
2435 /* XXX: POSIX basename() modifies argument */
2436 FILL_OR_LEAVE(document->dmFileDescr, basename(optarg));
2437 break;
2438 case 'D':
2439 if (!document) {
2440 fprintf(stderr,
2441 _("Error: %s: Document file (-d) must precede "
2442 "document name (-%c)\n"), optarg, opt);
2443 retval = -1;
2444 goto leave;
2446 FILL_OR_LEAVE(document->dmFileDescr, optarg);
2447 break;
2448 case 'x':
2449 if (!document) {
2450 fprintf(stderr,
2451 _("Error: %s: Document file (-d) must precede "
2452 "XPath expression (-%c)\n"), optarg, opt);
2453 retval = -1;
2454 goto leave;
2456 /* Load XML node list */
2457 char *xpath_expr = NULL;
2458 FILL_OR_LEAVE(xpath_expr, optarg);
2459 retval = load_xml_subtree_from_memory(
2460 document->data, document->data_length,
2461 &document->xml_node_list, xpath_expr);
2462 if (retval) {
2463 free(xpath_expr);
2464 goto leave;
2466 /* Switch document type to XML */
2467 document->is_xml = 1;
2468 zfree(document->data);
2469 document->data_length = 0;
2470 documents->destructor =
2471 (void(*)(void **)) free_document_with_xml_node_list;
2472 break;
2473 case 'm':
2474 if (!document) {
2475 fprintf(stderr,
2476 _("Error: %s: Document file (-d) must precede "
2477 "MIME type (-%c)\n"), optarg, opt);
2478 retval = -1;
2479 goto leave;
2481 FILL_OR_LEAVE(document->dmMimeType, optarg);
2482 break;
2483 case 'g':
2484 if (!document) {
2485 fprintf(stderr,
2486 _("Error: %s: Document file (-d) must precede "
2487 "document ID (-%c)\n"), optarg, opt);
2488 retval = -1;
2489 goto leave;
2491 FILL_OR_LEAVE(document->dmFileGuid, optarg);
2492 break;
2493 case 'G':
2494 if (!document) {
2495 fprintf(stderr,
2496 _("Error: %s: Document file (-d) must precede "
2497 "document reference (-%c)\n"), optarg, opt);
2498 retval = -1;
2499 goto leave;
2501 FILL_OR_LEAVE(document->dmUpFileGuid, optarg);
2502 break;
2503 case 'c':
2504 if (!document) {
2505 fprintf(stderr,
2506 _("Error: Document file (-d) must precede "
2507 "document signature type (-%c)\n"), opt);
2508 retval = -1;
2509 goto leave;
2511 document->dmFileMetaType = FILEMETATYPE_SIGNATURE;
2512 break;
2514 default:
2515 shi_compose_usage(argv[0]);
2516 retval = -1;
2517 goto leave;
2521 /* All options must be recognized */
2522 if (optind != argc) {
2523 fprintf(stderr, _("Error: Superfluous argument\n"));
2524 shi_compose_usage(argv[0]);
2525 retval = -1;
2526 goto leave;
2529 if (!copies) {
2530 fprintf(stderr, _("Error: No recipient box ID specified\n"));
2531 shi_compose_usage(argv[0]);
2532 retval = -1;
2533 goto leave;
2536 /* TODO: Check Legal Title soft dependencies */
2538 /* Preview */
2539 oprintf(_("Following message has been composed:\n"));
2540 format_message(message);
2541 oprintf(_("Following recipients have been specified:\n"));
2542 format_copies(copies);
2543 /* TODO: Confirmation, correction */
2545 /* Send a message */
2546 printf(_("Sending message...\n"));
2547 err = isds_send_message_to_multiple_recipients(cisds, message, copies);
2548 finish_isds_operation(cisds, err);
2549 if (err && err != IE_PARTIAL_SUCCESS) {
2550 retval = -1;
2551 goto leave;
2554 /* Show results for each copy */
2555 for (copy_item = copies; copy_item; copy_item = copy_item->next) {
2556 if (!copy_item->data) continue;
2557 copy = (struct isds_message_copy *) copy_item->data;
2558 recipient_id_locale = utf82locale(copy->dbIDRecipient);
2560 if (copy->error) {
2561 retval = -1;
2562 if (copy->dmStatus) dmStatus_locale = utf82locale(copy->dmStatus);
2563 if (dmStatus_locale)
2564 oprintf(_("%s: Failed: %s: %s\n"),
2565 recipient_id_locale,
2566 isds_strerror(copy->error), dmStatus_locale);
2567 else
2568 oprintf(_("%s: Failed: %s\n"), recipient_id_locale,
2569 isds_strerror(copy->error));
2570 zfree(dmStatus_locale);
2571 } else {
2572 message_id_locale = utf82locale(copy->dmID);
2573 oprintf(_("%s: Succeeded. Assigned message ID: %s\n"),
2574 recipient_id_locale, message_id_locale);
2575 free(message_id_locale);
2578 free(recipient_id_locale);
2581 leave:
2582 isds_message_free(&message);
2583 isds_list_free(&copies);
2584 return retval;
2587 #undef FILL_LONGINT_OR_LEAVE
2588 #undef FILL_BOOLEAN_OR_LEAVE
2589 #undef CALLOC_OR_LEAVE
2590 #undef FILL_OR_LEAVE
2593 static void shi_delivery_usage(const char *command) {
2594 oprintf(_(
2595 "Usage: %s MESSAGE_ID\n"
2596 "Get delivery data about message with MESSAGE_ID.\n"),
2597 command);
2601 static int shi_delivery(int argc, const char **argv) {
2602 isds_error err;
2603 const char *id;
2605 if (!argv || !argv[1] || !*argv[1]) {
2606 shi_delivery_usage(argv[0]);
2607 return -1;
2609 id = argv[1];
2611 printf(_("Getting delivery info...\n"));
2612 err = isds_get_signed_delivery_info(cisds, id, &message);
2613 finish_isds_operation(cisds, err);
2614 if (err) {
2615 set_prompt(NULL);
2616 select_completition(COMPL_COMMAND);
2617 return -1;
2620 format_message(message);
2622 if (message->envelope && message->envelope->dmID)
2623 set_prompt(_("%s %s"), argv[0], message->envelope->dmID);
2624 else
2625 set_prompt("%s", argv[0]);
2626 select_completition(COMPL_MSG);
2627 return 0;
2631 static int shi_dump_message(int argc, const char **argv) {
2632 if (!message) {
2633 fprintf(stderr, _("No message loaded\n"));
2634 return -1;
2637 print_message(message);
2638 return 0;
2642 static void shi_hash_usage(const char *command) {
2643 oprintf(_(
2644 "Usage: %s [MESSAGE_ID]\n"
2645 "Retrieve message hash stored in ISDS.\n"
2646 "If MESSAGE_ID is defined, query for that message.\n"
2647 "Otherwise use current message.\n"),
2648 command);
2652 static int shi_hash(int argc, const char **argv) {
2653 isds_error err;
2654 const char *id = NULL;
2655 struct isds_hash *hash = NULL;
2656 char *hash_string = NULL;
2658 if (!argv || argc > 2) {
2659 shi_hash_usage((argv)?argv[0]:NULL);
2660 return -1;
2662 if (argc == 2 && argv[1] && *argv[1])
2663 id = argv[1];
2664 else {
2665 if (!message) {
2666 fprintf(stderr, _("No message loaded\n"));
2667 return -1;
2669 if (!message->envelope || !message->envelope->dmID) {
2670 fprintf(stderr, _("Current message is missing ID\n"));
2671 return -1;
2673 id = message->envelope->dmID;
2676 printf(_("Getting message hash...\n"));
2677 err = isds_download_message_hash(cisds, id, &hash);
2678 finish_isds_operation(cisds, err);
2679 if (err) return -1;
2681 hash_string = hash2string(hash);
2682 oprintf(_("ISDS states message with `%s' ID has following hash:\n%s\n"),
2683 id, hash_string);
2685 free(hash_string);
2686 isds_hash_free(&hash);
2687 return 0;
2691 static int shi_verify(int argc, const char **argv) {
2692 isds_error err;
2693 int retval = 0;
2694 struct isds_hash *retrieved_hash = NULL, *stored_hash = NULL;
2695 char *hash_string = NULL;
2696 size_t width = 4;
2698 if (!message) {
2699 fprintf(stderr, _("No message loaded\n"));
2700 return -1;
2703 if (!message->envelope) {
2704 fprintf(stderr, _("Current message is missing envelope\n"));
2705 return -1;
2707 stored_hash = message->envelope->hash;
2708 message->envelope->hash = NULL;
2710 if (message->envelope->dmID) {
2711 /* Verify remote hash */
2712 oprintf(_("Remote hash check:\n"));
2714 printf(_("Getting message hash...\n"));
2715 err = isds_download_message_hash(cisds, message->envelope->dmID,
2716 &retrieved_hash);
2717 finish_isds_operation(cisds, err);
2719 if (retrieved_hash) {
2720 hash_string = hash2string(retrieved_hash);
2721 ohprint(_("Retrieved:"), width);
2722 oprintf("%s\n", hash_string);
2723 zfree(hash_string);
2726 if (retrieved_hash && message->raw) {
2727 err = isds_compute_message_hash(cisds, message,
2728 retrieved_hash->algorithm);
2729 finish_isds_operation(cisds, err);
2731 if (!err) {
2732 hash_string = hash2string(message->envelope->hash);
2733 ohprint(_("Computed:"), width);
2734 oprintf("%s\n", hash_string);
2735 zfree(hash_string);
2739 err = isds_hash_cmp(retrieved_hash, message->envelope->hash);
2740 switch (err) {
2741 case IE_SUCCESS:
2742 oprintf(_("Hashes match.\n")); break;
2743 case IE_NOTUNIQ:
2744 oprintf(_("Hashes do not match.\n"));
2745 retval = -1;
2746 break;
2747 default:
2748 oprintf(_("Hashes could not be compared.\n"));
2749 retval = -1;
2750 break;
2753 free(retrieved_hash);
2757 if (stored_hash) {
2758 /* Verify stored hash */
2759 oprintf(_("Stored hash check:\n"));
2761 hash_string = hash2string(stored_hash);
2762 ohprint(_("Stored:"), width);
2763 oprintf("%s\n", hash_string);
2764 zfree(hash_string);
2766 if (message->raw) {
2767 err = isds_compute_message_hash(cisds, message,
2768 stored_hash->algorithm);
2769 finish_isds_operation(cisds, err);
2771 if (!err) {
2772 hash_string = hash2string(message->envelope->hash);
2773 ohprint(_("Computed:"), width);
2774 oprintf("%s\n", hash_string);
2775 zfree(hash_string);
2779 err = isds_hash_cmp(stored_hash, message->envelope->hash);
2780 switch (err) {
2781 case IE_SUCCESS:
2782 oprintf(_("Hashes match.\n")); break;
2783 case IE_NOTUNIQ:
2784 oprintf(_("Hashes do not match.\n"));
2785 retval = -1;
2786 break;
2787 default:
2788 oprintf(_("Hashes could not be compared.\n"));
2789 retval = -1;
2790 break;
2793 isds_hash_free(&message->envelope->hash);
2796 message->envelope->hash = stored_hash;
2797 return retval;
2801 static int shi_authenticate(int argc, const char **argv) {
2802 isds_error err;
2803 int retval = 0;
2805 if (!message) {
2806 fprintf(stderr, _("No message loaded\n"));
2807 return -1;
2809 if (!message->raw || message->raw_length == 0) {
2810 fprintf(stderr, _("Current message is missing raw representation\n"));
2811 return -1;
2814 printf(_("Submitting message to authenticity check...\n"));
2815 err = isds_authenticate_message(cisds, message->raw, message->raw_length);
2816 finish_isds_operation(cisds, (err == IE_NOTUNIQ) ? IE_SUCCESS : err);
2818 switch (err) {
2819 case IE_SUCCESS:
2820 oprintf(_("Message originates in ISDS.\n")); break;
2821 case IE_NOTUNIQ:
2822 oprintf(_("Message is unknown to ISDS or has been tampered.\n"));
2823 retval = -1;
2824 break;
2825 default:
2826 retval = -1;
2827 break;
2830 return retval;
2834 static void shi_accept_message_usage(const char *command) {
2835 oprintf(_(
2836 "Usage: %s [MESSAGE_ID...]\n"
2837 "Accept commercial message moving its state to received.\n"
2838 "If MESSAGE_ID is defined, accept that message. More messages can be specified.\n"
2839 "Otherwise accept all commercial incoming messages.\n"),
2840 command);
2844 static int shi_accept_message(int argc, const char **argv) {
2845 isds_error err;
2846 char *id = NULL;
2848 /* Process messages named in argv */
2849 for (int i = 1; i < argc; i++) {
2850 if (!argv[i] || !*argv[i]) continue;
2852 id = locale2utf8(argv[i]);
2853 if (!id) {
2854 fprintf(stderr,
2855 _("Error: Could not convert message ID to UTF-8: %s\n"),
2856 argv[i]);
2857 return -1;
2860 printf(_("Accepting message `%s'...\n"), argv[i]);
2861 err = isds_mark_message_received(cisds, id);
2862 finish_isds_operation(cisds, err);
2863 if (err) {
2864 free(id);
2865 return -1;
2868 oprintf(_("Message `%s' accepted\n"), argv[i]);
2869 free(id);
2872 if (argc < 2) {
2873 /* TODO: list commercial not received messages and accept all of them
2874 * */
2875 fprintf(stderr,
2876 _("Error: No message ID supplied. Accepting all commercial "
2877 "messages not implemented yet.\n"));
2878 return -1;
2881 return 0;
2885 static void shi_delete_message_usage(const char *command) {
2886 oprintf(_(
2887 "Usage: %s {-i|-o} MESSAGE_ID...\n"
2888 "Remove message from long term storage.\n"
2889 "Options:\n"
2890 " -i Messages are incoming\n"
2891 " -o Messages are outoging\n"),
2892 command);
2896 static int shi_delete_message(int argc, const char **argv) {
2897 isds_error err;
2898 char *id = NULL;
2899 _Bool incoming = 0;
2900 _Bool direction_specified = 0;
2901 int opt;
2903 optind = 0;
2904 while ((opt = getopt(argc, (char * const *)argv, "io")) != -1) {
2905 switch (opt) {
2906 case 'i':
2907 incoming = 1;
2908 direction_specified = 1;
2909 break;
2910 case 'o':
2911 incoming = 0;
2912 direction_specified = 1;
2913 break;
2914 default:
2915 shi_delete_message_usage((argv)?argv[0]:NULL);
2916 return -1;
2919 if (optind >= argc || !argv || !argv[optind] || !*argv[optind]) {
2920 fprintf(stderr, _("Bad invocation\n"));
2921 shi_delete_message_usage((argv)?argv[0]:NULL);
2922 return -1;
2924 if (!direction_specified) {
2925 fprintf(stderr, _("Message direction has not been specified\n"));
2926 shi_delete_message_usage((argv)?argv[0]:NULL);
2927 return -1;
2930 /* Process messages named in argv */
2931 for (int i = optind; i < argc; i++) {
2932 if (!argv[i] || !*argv[i]) continue;
2934 id = locale2utf8(argv[i]);
2935 if (!id) {
2936 fprintf(stderr,
2937 _("Error: Could not convert message ID to UTF-8: %s\n"),
2938 argv[i]);
2939 return -1;
2942 printf(_("Deleting message `%s'...\n"), argv[i]);
2943 err = isds_delete_message_from_storage(cisds, id, incoming);
2944 finish_isds_operation(cisds, err);
2945 if (err) {
2946 free(id);
2947 return -1;
2950 oprintf(_("Message `%s' deleted\n"), argv[i]);
2951 free(id);
2954 return 0;
2958 /* Convert message ID form locale to UTF-8 or in other direction. If both
2959 * strings are provided, UTF-8 will take precedence. The missing string is
2960 * automatically allocated (but not freed before). If UTF-8 version has been
2961 * provided, @stastic_utf8 will become 1, otherwise 0. You can pass the
2962 * strings and the flags to free_message_id() to free memory properly.*/
2963 static int convert_message_id(char **id_utf8, char **id_locale, _Bool *static_utf8) {
2964 if (!id_utf8 || !id_locale || !static_utf8) return -1;
2965 if (!*id_utf8 && !*id_locale) return -1;
2967 if (*id_utf8) {
2968 *static_utf8 = 1;
2969 *id_locale = utf82locale(*id_utf8);
2970 } else {
2971 *static_utf8 = 0;
2972 *id_utf8 = locale2utf8(*id_locale);
2973 if (!*id_utf8) {
2974 fprintf(stderr,
2975 _("Error: Could not convert message ID to UTF-8: %s\n"),
2976 *id_locale);
2977 return -1;
2981 return 0;
2985 /* Free message ID strings as were allocated by convert_message_id() */
2986 static int free_message_id(char **id_utf8, char **id_locale, _Bool static_utf8) {
2987 if (!id_utf8 || !id_locale) return -1;
2988 if (static_utf8) zfree(*id_locale);
2989 else zfree(*id_utf8);
2990 return 0;
2994 /* Return static UTF-8 encoded ID of current message. In case of error NULL. */
2995 static const char *get_current_message_id(void) {
2996 if (!message) {
2997 fprintf(stderr, _("No message loaded\n"));
2998 return NULL;
3000 if (!message->envelope) {
3001 fprintf(stderr, _("Loaded message is missing envelope\n"));
3002 return NULL;
3004 if (!message->envelope->dmID || !*message->envelope->dmID) {
3005 fprintf(stderr, _("Loaded message is missing ID\n"));
3006 return NULL;
3008 return message->envelope->dmID;
3012 static void shi_message_sender_usage(const char *command) {
3013 oprintf(_(
3014 "Usage: %s [MESSAGE_ID...]\n"
3015 "Get details about sender of a message.\n"
3016 "If MESSAGE_ID is defined, get sender of that message. More messages can be specified.\n"
3017 "Otherwise will get sender of current message, if any is loaded.\n"),
3018 command);
3022 /* Get details about sender of message with given ID. At least one form must
3023 * be specified.
3024 * @message_id is UTF-8 string
3025 * @message_id_locale is string in locale encoding
3026 * @return 0 on success, -1 on failure */
3027 static int do_message_sender(const char *message_id, const char *message_id_locale) {
3028 isds_sender_type *type = NULL;
3029 char *raw_type = NULL;
3030 char *name = NULL;
3031 isds_error err;
3032 _Bool static_id;
3034 if (convert_message_id((char **)&message_id, (char **)&message_id_locale,
3035 &static_id))
3036 return -1;
3038 printf(_("Getting sender of message `%s'...\n"), message_id_locale);
3039 err = isds_get_message_sender(cisds, message_id, &type, &raw_type, &name);
3040 finish_isds_operation(cisds, err);
3041 if (err) {
3042 free_message_id((char **)&message_id, (char **)&message_id_locale,
3043 static_id);
3044 return -1;
3047 format_sender_info(message_id, type, raw_type, name);
3049 free_message_id((char **)&message_id, (char **)&message_id_locale,
3050 static_id);
3051 zfree(type);
3052 zfree(raw_type);
3053 zfree(name);
3054 return 0;
3058 static int shi_message_sender(int argc, const char **argv) {
3059 if (argc < 2) {
3060 return do_message_sender(get_current_message_id(), NULL);
3063 for (int i = 1; i < argc; i++) {
3064 if (!argv[i] || !*argv[i]) continue;
3065 if (do_message_sender(NULL, argv[i]))
3066 return -1;
3069 return 0;
3073 /* Mark message as read. At least one form of ID must be provided.
3074 * @id is UTF-8 encoded message ID
3075 * @id_locale is locale encoded message ID. @id takes preference. */
3076 static int do_read_message(const char *id, const char *id_locale) {
3077 _Bool static_id;
3078 isds_error err;
3080 if ((!id || !*id) && (!id_locale || !*id_locale)) return -1;
3082 if (convert_message_id((char **)&id, (char **)&id_locale, &static_id)) return -1;
3084 printf(_("Marking message `%s' as read...\n"), id_locale);
3085 err = isds_mark_message_read(cisds, id);
3086 finish_isds_operation(cisds, err);
3088 if (!err)
3089 oprintf(_("Message `%s' marked as read\n"), id_locale);
3091 free_message_id((char **)&id, (char **)&id_locale, static_id);
3093 return (err) ? -1 : 0;
3097 static void shi_read_message_usage(const char *command) {
3098 oprintf(_(
3099 "Usage: %s [MESSAGE_ID...]\n"
3100 "Mark message as read moving its state to read.\n"
3101 "\n"
3102 "When new incoming message is download, its state is not changed on server.\n"
3103 "Client must mark such message as read explicitly. You can use this command\n"
3104 "to do so, if not done automatically at download time by your client.\n"
3105 "\n"
3106 "If MESSAGE_ID is defined, mark that message. More messages can be specified.\n"
3107 "Otherwise marks currently loaded message.\n"),
3108 command);
3112 static int shi_read_message(int argc, const char **argv) {
3113 if (argc < 2) {
3114 return do_read_message(get_current_message_id(), NULL);
3117 for (int i = 1; i < argc; i++) {
3118 if (!argv[i] || !*argv[i]) continue;
3119 if (do_read_message(NULL, argv[i]))
3120 return -1;
3123 return 0;
3127 static void shi_cat_message_usage(const char *command) {
3128 oprintf(_(
3129 "Usage: %s\n"
3130 "Print unformated raw representation of current message.\n"
3131 "\n"
3132 "This is the same content you would get into file by `save' command.\n"
3133 "\n"
3134 "Be ware the binary stream can screw your terminal. No new line character\n"
3135 "will be appended to the end of the output.\n"),
3136 command);
3140 static int shi_cat_message(int argc, const char **argv) {
3141 if (!message) {
3142 fprintf(stderr, _("No message loaded\n"));
3143 return -1;
3146 if (!message->raw || !message->raw_length) {
3147 fprintf(stderr, _("Current message is missing raw representation\n"));
3148 return -1;
3151 if (owrite(message->raw, message->raw_length) != message->raw_length) {
3152 fprintf(stderr, _("Error while printing message content\n"));
3153 return -1;
3156 return 0;
3160 static int shi_show_message(int argc, const char **argv) {
3161 if (!message) {
3162 fprintf(stderr, _("No message loaded\n"));
3163 return -1;
3166 format_message(message);
3167 return 0;
3171 static void shi_incoming_message_usage(const char *command) {
3172 oprintf(_(
3173 "Usage: %s [-r] MESSAGE_ID\n"
3174 "Get incoming message with MESSAGE_ID.\n"
3175 "Options:\n"
3176 " -r Mark mesage as read\n"),
3177 command);
3181 static int shi_incoming_message(int argc, const char **argv) {
3182 isds_error err;
3183 const char *id;
3184 int opt;
3185 _Bool mark_as_read = 0;
3187 optind = 0;
3188 while ((opt = getopt(argc, (char * const *)argv, "r")) != -1) {
3189 switch (opt) {
3190 case 'r':
3191 mark_as_read = 1;
3192 break;
3193 default:
3194 shi_incoming_message_usage((argv)?argv[0]:NULL);
3195 return -1;
3198 if (optind + 1 != argc || !argv || !argv[optind] || !*argv[optind]) {
3199 fprintf(stderr, _("Bad invocation\n"));
3200 shi_incoming_message_usage((argv)?argv[0]:NULL);
3201 return -1;
3203 id = argv[optind];
3205 printf(_("Getting incoming message...\n"));
3206 err = isds_get_signed_received_message(cisds, id, &message);
3207 finish_isds_operation(cisds, err);
3208 if (err) {
3209 set_prompt(NULL);
3210 select_completition(COMPL_COMMAND);
3211 return -1;
3214 format_message(message);
3216 if (message->envelope && message->envelope->dmID)
3217 set_prompt(_("%s %s"), argv[0], message->envelope->dmID);
3218 else
3219 set_prompt("%s", argv[0]);
3220 select_completition(COMPL_MSG);
3222 if (mark_as_read || cfg_getbool(configuration, CONFIG_MARKMESSAGEREAD)) {
3223 if (message->envelope && message->envelope->dmMessageStatus &&
3224 ! (*message->envelope->dmMessageStatus & MESSAGESTATE_READ))
3225 return do_read_message(id, NULL);
3227 return 0;
3231 static void shi_outgoing_message_usage(const char *command) {
3232 oprintf(_(
3233 "Usage: %s MESSAGE_ID\n"
3234 "Get outgoing message with MESSAGE_ID.\n"),
3235 command);
3239 static int shi_outgoing_message(int argc, const char **argv) {
3240 isds_error err;
3241 const char *id;
3243 if (!argv || !argv[1] || !*argv[1]) {
3244 shi_outgoing_message_usage(argv[0]);
3245 return -1;
3247 id = argv[1];
3249 printf(_("Getting outgoing message...\n"));
3250 err = isds_get_signed_sent_message(cisds, id, &message);
3251 finish_isds_operation(cisds, err);
3252 if (err) {
3253 set_prompt(NULL);
3254 select_completition(COMPL_COMMAND);
3255 return -1;
3258 format_message(message);
3259 if (message->envelope && message->envelope->dmID)
3260 set_prompt(_("%s %s"), argv[0], message->envelope->dmID);
3261 else
3262 set_prompt("%s", argv[0]);
3263 select_completition(COMPL_MSG);
3264 return 0;
3268 /* Detect type (message or delivery data) and load it. And change completion
3269 * and show the data.
3270 * @buffer is memory with message or delivery data
3271 * @length is size of @buffer in bytes
3272 * @strategy defines how to fill global message variable
3273 * @return 0 for success, otherwise non-zero. */
3274 static int do_load_anything(const void *buffer, size_t length,
3275 isds_buffer_strategy strategy) {
3276 isds_raw_type raw_type;
3277 isds_error err;
3278 char *type_name = NULL;
3280 if (NULL == buffer || 0 == length) {
3281 return -1;
3284 printf(_("Detecting format...\n"));
3285 err = isds_guess_raw_type(cisds, &raw_type, buffer, length);
3286 finish_isds_operation(cisds, err);
3288 if (err) {
3289 if (err == IE_NOTSUP)
3290 fprintf(stderr, _("Unknown format.\n"));
3291 else
3292 fprintf(stderr, _("Error while detecting format.\n"));
3293 } else {
3294 switch (raw_type) {
3295 case RAWTYPE_INCOMING_MESSAGE:
3296 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
3297 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
3298 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
3299 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
3300 err = isds_load_message(cisds, raw_type,
3301 buffer, length, &message, strategy);
3302 finish_isds_operation(cisds, err);
3303 type_name = N_("message");
3304 break;
3306 case RAWTYPE_DELIVERYINFO:
3307 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO:
3308 case RAWTYPE_CMS_SIGNED_DELIVERYINFO:
3309 err = isds_load_delivery_info(cisds, raw_type,
3310 buffer, length, &message, strategy);
3311 finish_isds_operation(cisds, err);
3312 type_name = N_("delivery");
3313 break;
3315 default:
3316 fprintf(stderr,
3317 _("Unsupported format.\n"));
3318 err = IE_NOTSUP;
3322 if (err) {
3323 set_prompt(NULL);
3324 select_completition(COMPL_COMMAND);
3325 return -1;
3328 format_message(message);
3330 if (message->envelope && message->envelope->dmID)
3331 set_prompt(_("%s %s"), _(type_name), message->envelope->dmID);
3332 else
3333 set_prompt("%s", _(type_name));
3334 select_completition(COMPL_MSG);
3335 return 0;
3339 static void shi_load_anything_usage(const char *command) {
3340 oprintf(_(
3341 "Usage: %s FILE\n"
3342 "Load message or message delivery details from local FILE.\n"),
3343 command);
3347 static int shi_load_anything(int argc, const char **argv) {
3348 int fd;
3349 void *buffer = NULL;
3350 size_t length;
3351 int error;
3353 if (!argv || !argv[1] || !*argv[1]) {
3354 shi_load_anything_usage((argv)?argv[0]:NULL);
3355 return -1;
3358 printf(_("Loading file `%s'...\n"), argv[1]);
3360 if (mmap_file(argv[1], &fd, &buffer, &length)) return -1;
3362 error = do_load_anything(buffer, length, BUFFER_COPY);
3364 munmap_file(fd, buffer, length);
3366 return error;
3370 static void shi_save_message_usage(const char *command) {
3371 oprintf(_(
3372 "Usage: %s FILE\n"
3373 "Save message into local FILE.\n"),
3374 command);
3378 static const char *raw_type2mime(isds_raw_type raw_type) {
3379 switch (raw_type) {
3380 case RAWTYPE_INCOMING_MESSAGE:
3381 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
3382 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
3383 case RAWTYPE_DELIVERYINFO:
3384 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO:
3385 return "text/xml";
3387 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
3388 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
3389 case RAWTYPE_CMS_SIGNED_DELIVERYINFO:
3390 return "application/pkcs7-mime";
3392 default:
3393 return NULL;
3398 static int shi_save_message(int argc, const char **argv) {
3399 _Bool overwrite = cfg_getbool(configuration, CONFIG_OVERWRITEFILES);
3401 if (!argv || !argv[1] || !*argv[1]) {
3402 shi_save_message_usage(argv[0]);
3403 return -1;
3406 if (!message) {
3407 fprintf(stderr, _("No message loaded\n"));
3408 return -1;
3410 if (!message->raw || message->raw_length == 0) {
3411 fprintf(stderr, _("Loaded message is missing raw representation\n"));
3412 return -1;
3415 return save_data_to_file(argv[1], -1, message->raw, message->raw_length,
3416 raw_type2mime(message->raw_type), overwrite);
3420 /* Return document of current message identified by ordinal number expressed
3421 * as string. In case of error return NULL. */
3422 static const struct isds_document *locate_document_by_ordinal_string(
3423 const char *number) {
3424 const struct isds_list *item;
3425 const struct isds_document *document = NULL;
3426 int ordinar, i;
3428 if (!number) return NULL;
3430 ordinar = atoi(number);
3431 if (ordinar <= 0) {
3432 fprintf(stderr, _("%s: Document number must be positive number\n"),
3433 number);
3434 return NULL;
3437 if (!message) {
3438 fprintf(stderr, _("No message loaded\n"));
3439 return NULL;
3442 /* Find document */
3443 for (item = message->documents, i = 0; item; item = item->next) {
3444 if (!item->data) continue;
3445 if (++i == ordinar) {
3446 document = (const struct isds_document *) item->data;
3447 break;
3450 if (i != ordinar) {
3451 fprintf(stderr, _("Message does not contain document #%d\n"), ordinar);
3452 return NULL;
3455 return document;
3459 static void shi_cat_document_usage(const char *command) {
3460 oprintf(_(
3461 "Usage: %s NUMBER\n"
3462 "Print document selected with ordinal NUMBER.\n"),
3463 command);
3466 static int shi_cat_document(int argc, const char **argv) {
3467 const struct isds_document *document;
3469 if (!argv || !argv[1] || !*argv[1] || argc > 3) {
3470 shi_cat_document_usage(argv[0]);
3471 return -1;
3474 document = locate_document_by_ordinal_string(argv[1]);
3475 if (!document) return -1;
3477 if (document->is_xml) {
3478 xmlBufferPtr buffer = NULL;
3479 size_t written;
3481 if (serialize_xml_to_buffer(&buffer, document->xml_node_list))
3482 return -1;
3484 written = owrite(buffer->content, buffer->use);
3485 xmlBufferFree(buffer);
3486 if (written != buffer->use) {
3487 fprintf(stderr, _("Error while printing document content\n"));
3488 return -1;
3490 } else {
3491 if (!document->data || !document->data_length) {
3492 fprintf(stderr, _("Document is missing raw representation\n"));
3493 return -1;
3496 if (owrite(document->data, document->data_length) != document->data_length) {
3497 fprintf(stderr, _("Error while printing document content\n"));
3498 return -1;
3502 return 0;
3506 static void shi_save_document_usage(const char *command) {
3507 oprintf(_(
3508 "Usage: %s NUMBER [DESTINATION]\n"
3509 "Save document having ordinal NUMBER within current message into local file.\n"
3510 "If DESTINATION is file (or does not exist yet), document will be saved into\n"
3511 "this file.\n"
3512 "If DESTINATION is existing directory, file name equaled to document name\n"
3513 "will be saved into DESTINATION.\n"
3514 "If DESTINATION is missing, document name will be used as file name and\n"
3515 "saved into working directory.\n"
3516 "Be aware that document name does not embed malicious characters (slashes).\n"
3517 "\n"
3518 "If the document is a binary stream, image of the document will be copied\n"
3519 "into a file. If the document is a XML document, the XML tree will be serialized\n"
3520 "into a file. If XML document stands for one element or one text node, the node\n"
3521 "(and its children recursively) will be serialized. If XML document compounds\n"
3522 "more nodes or a comment or a processing instruction, parent node from ISDS name\n"
3523 "space will be used to ensure output serialized XML well-formness.\n"
3525 command);
3529 static int shi_save_document(int argc, const char **argv) {
3530 const struct isds_document *document;
3531 const char *dirname = NULL;
3532 char *filename = NULL, *path = NULL;
3533 int retval = 0;
3534 _Bool overwrite = cfg_getbool(configuration, CONFIG_OVERWRITEFILES);
3536 if (!argv || !argv[1] || !*argv[1] || argc > 3) {
3537 shi_save_document_usage(argv[0]);
3538 return -1;
3541 document = locate_document_by_ordinal_string(argv[1]);
3542 if (!document) return -1;
3544 /* Select directory and file name */
3545 if (argv[2] && *argv[2]) {
3546 if (!is_directory(argv[2])) {
3547 dirname = argv[2];
3548 } else {
3549 filename = strdup(argv[2]);
3550 if (!filename) {
3551 fprintf(stderr, _("Not enough memory\n"));
3552 return -1;
3556 if (!filename && document->dmFileDescr && &document->dmFileDescr) {
3557 filename = utf82locale(document->dmFileDescr);
3558 if (!filename) {
3559 fprintf(stderr, _("Not enough memory\n"));
3560 return -1;
3563 if (!filename) {
3564 fprintf(stderr,
3565 _("File name neither supplied, nor document name exists\n"
3566 "Please, supply one.\n"));
3567 return -1;
3570 /* Build path */
3571 if (dirname) {
3572 path = astrcat3(dirname, "/", filename);
3573 zfree(filename);
3574 } else {
3575 path = filename;
3576 filename = NULL;
3578 if (!path) {
3579 fprintf(stderr, _("Not enough memory\n"));
3580 return -1;
3583 /* Save document */
3584 if (document->is_xml)
3585 retval = save_xml_to_file(path, -1, document->xml_node_list,
3586 document->dmMimeType, overwrite);
3587 else
3588 retval = save_data_to_file(path, -1, document->data,
3589 document->data_length, document->dmMimeType, overwrite);
3590 free(path);
3591 return retval;
3595 /* Execute program specified as NULL terminated array of arguments. argv[0] is
3596 * subject of PATH search variable look-up. The program is executed directly,
3597 * it's not a shell command. */
3598 static int execute_system_command(char *const argv[]) {
3599 pid_t pid;
3601 if (!argv || !argv[0]) return -1;
3603 pid = fork();
3604 if (pid == -1) {
3605 /* Could not fork */
3606 fprintf(stderr, _("Could not fork\n"));
3607 return -1;
3608 } else if (pid == 0) {
3609 /* Child */
3610 execvp(argv[0], argv);
3611 fprintf(stderr, _("Could not execute:"));
3612 for (char *const *arg = argv; *arg; arg++)
3613 fprintf(stderr, " %s", *arg);
3614 fprintf(stderr, _(": %s\n"), strerror(errno));
3615 exit(EXIT_FAILURE);
3616 } else {
3617 /* Wait for the command */
3618 int retval;
3620 if (-1 == waitpid(pid, &retval, 0)) {
3621 fprintf(stderr, _("Could not wait for executed command\n"));
3622 return -1;
3625 if (retval == -1)
3626 fprintf(stderr, _("Exit code of command could not "
3627 "be determined\n"));
3628 else if (WIFEXITED(retval) && WEXITSTATUS(retval))
3629 printf(_("Command exited with code %d\n"),
3630 WEXITSTATUS(retval));
3631 else if (WIFSIGNALED(retval))
3632 printf(_("Command terminated by signal "
3633 "#%d\n"), WTERMSIG(retval));
3634 return retval;
3639 /* Append @suffix into @buffer with @size bytes prealocated at position @at.
3640 * Trailing '\0' of @suffix is not carried.
3641 * @buffer can be reallocated if @size is not suffient to fill @suffix
3642 * @size is original @buffer size, can change if @buffer would be reallocated
3643 * @at position where append @suffic to. Outputs new end after appending
3644 * @suffix is NULL rerminated string to append
3645 * @return 0 if success, -1 otherwise. Caller is resposible for freeing
3646 * @buffer.*/
3647 static int append_string_at(char **buffer, size_t *size, char **at,
3648 const char *suffix) {
3650 if (!buffer || !*buffer || !size || !at || !*at) return -1;
3651 if (!suffix) return 0;
3653 while (*suffix) {
3654 if (*at - *buffer + 1 >= *size) {
3655 /* End of buffer, grow it */
3656 if (*size < 8) *size = 8;
3657 *size *= 2;
3658 char *new_buffer = realloc(*buffer, *size);
3659 if (!new_buffer) return -1;
3660 *at = *at - *buffer + new_buffer;
3661 *buffer = new_buffer;
3664 /* Copy a character */
3665 *((*at)++) = *(suffix++);
3668 return 0;
3672 static char *expand_command_arg(const char *format, const char *file,
3673 const char *type) {
3674 char *buffer = NULL;
3675 size_t size = 0;
3676 const char *format_cursor;
3677 char *buffer_cursor;
3679 if (!format) return NULL;
3681 for (format_cursor = format, buffer_cursor = buffer; ; format_cursor++) {
3682 if (buffer_cursor - buffer + 1 >= size) {
3683 /* End of buffer, grow it */
3684 if (size < 8) size = 8;
3685 size *= 2;
3686 char *new_buffer = realloc(buffer, size);
3687 if (!new_buffer) goto error;
3688 buffer_cursor = buffer_cursor - buffer + new_buffer;
3689 buffer = new_buffer;
3692 if (*format_cursor == '%') {
3693 /* Escape */
3694 switch (*(format_cursor+1)) {
3695 case 'f':
3696 if (!file) {
3697 fprintf(stderr, _("Could not expand `%%f' because "
3698 "file name did not exist.\n"));
3699 free(buffer);
3700 return NULL;
3702 if (append_string_at(&buffer, &size, &buffer_cursor, file))
3703 goto error;
3705 format_cursor++;
3706 continue;
3707 case 't':
3708 if (!file) {
3709 fprintf(stderr, _("Could not expand `%%t' because "
3710 "file type did not exist.\n"));
3711 free(buffer);
3712 return NULL;
3714 if (append_string_at(&buffer, &size, &buffer_cursor, type))
3715 goto error;
3717 format_cursor++;
3718 continue;
3719 case '%':
3720 format_cursor++;
3724 /* Copy plain character */
3725 *(buffer_cursor++) = *format_cursor;
3727 if (!*format_cursor) break;
3730 return buffer;
3732 error:
3733 fprintf(stderr, _("Error: Not enough memory\n"));
3734 free(buffer);
3735 return NULL;
3739 /* Expand open_command configuration option by substiting %f and %t with file
3740 * name and file type.
3741 * @file is locale encoded file name
3742 * @type is UTF-8 encoded MIME type
3743 * @return heap allocated arrary of arguments or NULL if error occurs.
3744 * Arguments are coded in locale. */
3745 static char **expand_open_command(const char *file, const char *type) {
3746 char **command = NULL;
3747 int length;
3748 char *type_locale = NULL;
3750 length = cfg_size(configuration, CONFIG_OPENCOMMAND);
3751 if (length <= 0) {
3752 fprintf(stderr, _("%s not set\n"), CONFIG_OPENCOMMAND);
3753 return NULL;
3756 command = malloc((length + 1) * sizeof(*command));
3757 if (!command) {
3758 fprintf(stderr, _("Error: Not enough memory\n"));
3759 return NULL;
3762 if (type) {
3763 type_locale = utf82locale(type);
3764 if (!type_locale) {
3765 printf(_("Could not convert document MIME type to locale "
3766 "encoding\n"));
3767 free(command);
3768 return NULL;
3772 for (int i = 0; i < length; i++) {
3773 const char *value = cfg_getnstr(configuration, CONFIG_OPENCOMMAND, i);
3774 command[i] = expand_command_arg(value, file, type_locale);
3775 if (!command[i]) {
3776 fprintf(stderr, _("Error: Not enough memory\n"));
3777 for (int j = 0; j < i; j++) free(command[j]);
3778 free(command);
3779 free(type_locale);
3780 return NULL;
3783 command[length] = NULL;
3785 free(type_locale);
3786 return command;
3790 /* Register temporary @file for removal at shigofumi exit.
3791 * This is done by destructor call-back in isds_list_free(). */
3792 static int register_temporary_file(const char *file) {
3793 struct isds_list *temporary_file = NULL;
3795 if (!file) return 0;
3797 temporary_file = malloc(sizeof(*temporary_file));
3798 if (!temporary_file) {
3799 fprintf(stderr, _("Error: Not enough memory\n"));
3800 return -1;
3803 temporary_file->data = (void *)strdup(file);
3804 if (!temporary_file) {
3805 fprintf(stderr, _("Error: Not enough memory\n"));
3806 free(temporary_file);
3807 return -1;
3810 temporary_file->destructor = shi_unlink_temporary_file;
3812 if (temporary_files)
3813 temporary_file->next = temporary_files;
3814 else
3815 temporary_file->next = NULL;
3816 temporary_files = temporary_file;
3818 return 0;
3822 static void shi_open_document_usage(const char *command) {
3823 oprintf(_(
3824 "Usage: %s NUMBER\n"
3825 "Save document having ordinal NUMBER within current message into temporal\n"
3826 "local file, open the file by xdg-open utility and then remove the file.\n"
3828 command);
3832 static int shi_open_document(int argc, const char **argv) {
3833 const struct isds_document *document;
3834 char filename[10] = "shiXXXXXX";
3835 int fd;
3836 char **command = NULL;
3837 int retval = 0;
3839 if (!argv || !argv[1] || !*argv[1] || argc > 3) {
3840 shi_open_document_usage(argv[0]);
3841 return -1;
3844 document = locate_document_by_ordinal_string(argv[1]);
3845 if (!document) return -1;
3847 /* Create temporary file for the document */
3848 /* TODO: mkstemp() works in CWD. Implement something working in $TMP. */
3849 fd = mkstemp(filename);
3850 if (fd == -1) {
3851 fprintf(stderr, _("Could not create temporary file: %s\n"),
3852 strerror(errno));
3853 return -1;
3856 /* Save document */
3857 if (document->is_xml)
3858 retval = save_xml_to_file(filename, fd, document->xml_node_list,
3859 document->dmMimeType, 0);
3860 else
3861 retval = save_data_to_file(filename, fd, document->data,
3862 document->data_length, document->dmMimeType, 0);
3864 /* Open the file with external utility */
3865 if (!retval) {
3866 /* Construct command arguments to execute */
3867 command = expand_open_command(filename, document->dmMimeType);
3869 if (!command)
3870 retval = -1;
3871 else {
3872 /* XXX: Do not use system(3) as we cannot escape uknown shell */
3873 retval = execute_system_command(command);
3874 for (char **arg = command; *arg; arg++) free(*arg);
3875 free(command);
3879 /* Remove the file */
3880 /* XXX: We do not know when external program opens the file. We cannot
3881 * remove it immediately. Register the filename and remove all temporary
3882 * files at exit of shigofumi if requested by configuration. */
3883 if (cfg_getbool(configuration, CONFIG_CLEAN_TEMPORARY_FILES)) {
3884 if (register_temporary_file(filename))
3885 fprintf(stderr, _("Warning: Temporary file `%s' could not been "
3886 "registered for later removal. Remove the file by "
3887 "hand, please.\n"), filename);
3889 return retval;
3893 static void shi_save_stamp_usage(const char *command) {
3894 oprintf(_(
3895 "Usage: %s FILE\n"
3896 "Save message time stamp into local FILE.\n"),
3897 command);
3901 static int shi_save_stamp(int argc, const char **argv) {
3902 _Bool overwrite = cfg_getbool(configuration, CONFIG_OVERWRITEFILES);
3904 if (!argv || !argv[1] || !*argv[1]) {
3905 shi_save_message_usage(argv[0]);
3906 return -1;
3909 if (!message) {
3910 fprintf(stderr, _("No message loaded\n"));
3911 return -1;
3913 if (!message->envelope || !message->envelope->timestamp||
3914 message->envelope->timestamp_length == 0) {
3915 fprintf(stderr, _("Loaded message is missing time stamp\n"));
3916 return -1;
3919 return save_data_to_file(argv[1], -1,
3920 message->envelope->timestamp, message->envelope->timestamp_length,
3921 "application/timestamp-reply", overwrite);
3925 static int shi_show_list(int argc, const char **argv) {
3926 if (!messages) {
3927 fprintf(stderr, _("No message list loaded\n"));
3928 return -1;
3931 oprintf((messages_are_outgoing) ?
3932 ngettext("You have %'lu outgoing message\n",
3933 "You have %'lu outgoing messages\n", total_messages) :
3934 ngettext("You have %'lu incoming message\n",
3935 "You have %'lu incoming messages\n", total_messages),
3936 total_messages);
3937 print_message_list(messages, messages_are_outgoing);
3938 return 0;
3942 static int shi_list_incoming(int argc, const char **argv) {
3943 isds_error err;
3945 printf(_("Listing incoming messages...\n"));
3946 err = isds_get_list_of_received_messages(cisds, NULL, NULL, NULL,
3947 MESSAGESTATE_ANY, 0, &total_messages, &messages);
3948 finish_isds_operation(cisds, err);
3949 if (err) {
3950 set_prompt(NULL);
3951 select_completition(COMPL_COMMAND);
3952 return -1;
3954 messages_are_outgoing = 0;
3956 shi_show_list(0, NULL);
3958 set_prompt(_("%s %'lu"), argv[0], total_messages);
3959 select_completition(COMPL_LIST);
3960 return 0;
3964 static int shi_list_outgoing(int argc, const char **argv) {
3965 isds_error err;
3967 printf(_("Listing outgoing messages...\n"));
3968 err = isds_get_list_of_sent_messages(cisds, NULL, NULL, NULL,
3969 MESSAGESTATE_ANY, 0, &total_messages, &messages);
3970 finish_isds_operation(cisds, err);
3971 if (err) {
3972 set_prompt(NULL);
3973 select_completition(COMPL_COMMAND);
3974 return -1;
3976 messages_are_outgoing = 1;
3978 shi_show_list(0, NULL);
3980 set_prompt(_("%s %'lu"), argv[0], total_messages);
3981 select_completition(COMPL_LIST);
3982 return 0;
3986 /* Submit document for conversion and print assigned identifier */
3987 static int do_convert(const struct isds_document *document) {
3988 isds_error err;
3989 char *id = NULL;
3990 struct tm *date = NULL;
3992 if (!document) return -1;
3994 printf(_("Submitting document for authorized conversion...\n"));
3996 err = czp_convert_document(czechpoint, document, &id, &date);
3997 finish_isds_operation(czechpoint, err);
3999 if (!err) {
4000 char *name_locale = utf82locale(document->dmFileDescr);
4001 char *date_string = tm2string(date);
4002 char *id_locale = utf82locale(id);
4003 oprintf(_(
4004 "Document submitted for authorized conversion successfully under name\n"
4005 "`%s' on %s.\n"
4006 "Submit identifier assigned by Czech POINT deposit is `%s'.\n"),
4007 name_locale, date_string, id_locale);
4008 free(name_locale);
4009 free(date_string);
4010 free(id_locale);
4011 oprintf(_("Be ware that submitted document has restricted lifetime "
4012 "(30 days).\n"));
4013 oprintf(_("See <%s> for more details.\n"), CZPDEPOSIT_URL);
4016 free(id); free(date);
4017 return (err) ? -1 : 0;
4021 static void shi_convert_file_or_message_usage(const char *command) {
4022 oprintf(_(
4023 "Usage: %s [FILE [NAME]]\n"
4024 "Submit local FILE to authorized conversion under NAME. If NAME is missing,\n"
4025 "it will use FILE name. If FILE is missing, it will submit current message.\n"),
4026 command);
4027 oprintf(_(
4028 "\n"
4029 "If Czech POINT deposit accepts document, it will return document identifier\n"
4030 "that user is supposed to provide to officer at Czech POINT contact place.\n"
4031 "Currently only PDF 1.3 and higher version files and signed messages are\n"
4032 "accepted.\n"));
4033 oprintf(_("See <%s> for more details.\n"), CZPDEPOSIT_URL);
4037 static int shi_convert_file_or_message(int argc, const char **argv) {
4038 int fd = -1;
4039 struct isds_document document;
4040 int retval = 0;
4042 if (!argv || argc > 3) {
4043 shi_convert_file_or_message_usage((argv)?argv[0]:NULL);
4044 return -1;
4047 memset(&document, 0, sizeof(document));
4049 if (NULL == argv[1] || !*argv[1]) {
4050 /* Convert current message */
4051 if (!message) {
4052 fprintf(stderr, _("No message loaded\n"));
4053 return -1;
4055 if (!message->raw || !message->raw_length) {
4056 fprintf(stderr,
4057 _("Current message is missing raw representation\n"));
4058 return -1;
4060 document.dmFileDescr = astrcat(
4061 (NULL != message->envelope && NULL != message->envelope->dmID) ?
4062 message->envelope->dmID :
4063 "unknown",
4064 ".zfo");
4065 if (NULL == document.dmFileDescr) {
4066 printf(_("Could not build document name from message ID\n"));
4067 return -1;
4069 document.data = message->raw;
4070 document.data_length = message->raw_length;
4071 } else {
4072 /* Convert local file */
4073 if (argc == 3 && argv[2] && *argv[2])
4074 document.dmFileDescr = locale2utf8(argv[2]);
4075 else
4076 document.dmFileDescr = locale2utf8(argv[1]);
4077 if (!document.dmFileDescr) {
4078 printf(_("Could not convert document name to UTF-8\n"));
4079 return -1;
4082 printf(_("Loading document from file `%s'...\n"), argv[1]);
4083 if (mmap_file(argv[1], &fd, &document.data, &document.data_length)) {
4084 free(document.dmFileDescr);
4085 return -1;
4089 retval = do_convert(&document);
4091 if (0 <= fd) {
4092 munmap_file(fd, document.data, document.data_length);
4094 free(document.dmFileDescr);
4095 return retval;
4099 static void shi_convert_document_usage(const char *command) {
4100 oprintf(_(
4101 "Usage: %s NUMBER\n"
4102 "Submit message document with ordinal NUMBER to authorized conversion.\n"),
4103 command);
4104 oprintf(_(
4105 "\n"
4106 "If Czech POINT deposit accepts document, it will return document identifier\n"
4107 "that user is supposed to provide to officer at Czech POINT contact place.\n"
4108 "Currently only PDF 1.3 and higher version files and signed messages are\n"
4109 "accepted.\n"));
4110 oprintf(_("See <%s> for more details.\n"), CZPDEPOSIT_URL);
4114 static int shi_convert_document(int argc, const char **argv) {
4115 const struct isds_document *document;
4117 if (!argv || !argv[1] || !*argv[1]) {
4118 shi_convert_document_usage((argv)?argv[0]:NULL);
4119 return -1;
4122 document = locate_document_by_ordinal_string(argv[1]);
4123 if (!document) return -1;
4125 return do_convert(document);
4129 static void shi_resign_usage(const char *command) {
4130 oprintf(_(
4131 "Usage: %s [FILE]\n"
4132 "Send message or delivery data to re-sign it and to add current time stamp.\n"
4133 "If FILE is specified, message will be loaded from local file. Otherwise\n"
4134 "current message will be sent.\n"),
4135 command);
4136 oprintf(_(
4137 "\n"
4138 "Only signed messages or delivery data without time stamp are accepted by\n"
4139 "ISDS. Output re-signed message or delivery data will be loaded.\n"));
4143 static int shi_resign(int argc, const char **argv) {
4144 int fd = -1;
4145 void *data = NULL; /* Static */
4146 void *resigned_data = NULL; /* Dynamic, stored into message */
4147 size_t data_length = 0, resigned_data_length = 0;
4148 struct tm *valid_to = NULL; /* Dynamic */
4149 isds_error err;
4151 if (!argv || argc > 3) {
4152 shi_resign_usage((argv)?argv[0]:NULL);
4153 return -1;
4156 if (NULL == argv[1] || !*argv[1]) {
4157 /* Use current message */
4158 if (!message) {
4159 fprintf(stderr, _("No message or delivery data loaded\n"));
4160 return -1;
4162 if (!message->raw || !message->raw_length) {
4163 fprintf(stderr, _("Current message or delivery data "
4164 "is missing raw representation\n"));
4165 return -1;
4167 data = message->raw;
4168 data_length = message->raw_length;
4169 } else {
4170 /* Use local file */
4171 printf(_("Loading message or delivery data from file `%s'...\n"),
4172 argv[1]);
4173 if (mmap_file(argv[1], &fd, &data, &data_length)) {
4174 return -1;
4178 printf(_("Re-signing...\n"));
4179 err = isds_resign_message(cisds, data, data_length,
4180 &resigned_data, &resigned_data_length, &valid_to);
4181 finish_isds_operation(cisds, err);
4183 if (0 <= fd) {
4184 munmap_file(fd, data, data_length);
4187 if (err) {
4188 return -1;
4191 print_header_tm(_("New time stamp expires"), valid_to);
4192 free(valid_to);
4194 return do_load_anything(resigned_data, resigned_data_length, BUFFER_MOVE);
4198 #if ENABLE_DEBUG
4199 static void shi_print_usage(const char *command) {
4200 oprintf(_(
4201 "Usage: %s STRING LENGTH\n"
4202 "Prints STRING into LENGTH columns. Negative LENGTH means not to cut\n"
4203 "overflowing string.\n"
4204 "This should be locale and terminal agnostic.\n"),
4205 command);
4209 static int shi_print(int argc, const char **argv) {
4210 long int width;
4212 if (!argv || !argv[1] || !argv[2]) {
4213 shi_print_usage((argv)?argv[0]:NULL);
4214 return -1;
4217 width = strtol(argv[2], NULL, 10);
4218 if (width < INT_MIN) {
4219 fprintf(stderr,
4220 _("Length argument must not lesser than %d.\n"), INT_MIN);
4221 return -1;
4223 if (width > INT_MAX) {
4224 fprintf(stderr,
4225 _("Length argument must not be greater than %d.\n"), INT_MAX);
4226 return -1;
4229 oprintf(_(">"));
4230 onprint(argv[1], width);
4231 oprintf(_("<\n"));
4233 return 0;
4237 static int shi_tokenize(int argc, const char **argv) {
4239 if (!argv) return 0;
4241 for (int i = 0; i < argc; i++) {
4242 oprintf(_(">%s<\n"), argv[i]);
4244 return 0;
4248 static int shi_quote(int argc, const char **argv) {
4249 char *escaped, *unescaped;
4251 if (!argv) return 0;
4253 oprintf(_("Original\tQuoted\tDequoted\n"));
4254 for (int i = 0; i < argc; i++) {
4255 escaped = shi_quote_filename((char *) argv[i], 0, NULL);
4256 unescaped = shi_dequote_filename((char *) argv[i], 0);
4257 oprintf(_(">%s<\t>%s<\t>%s<\n"), argv[i], escaped, unescaped);
4258 free(escaped);
4259 free(unescaped);
4261 return 0;
4263 #endif
4266 /* pclose(pipe), restore ouput to stdout, show error return code */
4267 int wait_for_shell(FILE **pipe) {
4268 int retval = 0;
4270 if (pipe && *pipe) {
4271 retval = pclose(*pipe);
4272 *pipe = NULL;
4273 output = stdout;
4275 if (retval == -1)
4276 fprintf(stderr, _("Exit code of shell command could not "
4277 "be determined\n"));
4278 else if (WIFEXITED(retval) && WEXITSTATUS(retval))
4279 printf(_("Shell command exited with code %d\n"),
4280 WEXITSTATUS(retval));
4281 else if (WIFSIGNALED(retval))
4282 printf(_("Shell command terminated by signal "
4283 "#%d\n"), WTERMSIG(retval));
4286 return retval;
4290 /* Interactive loop */
4291 void shi_loop(void) {
4292 char *command_line = NULL;
4293 char **command_argv = NULL;
4294 int command_argc;
4295 char *shell = NULL;
4296 struct command *command = NULL;
4297 FILE *pipe = NULL;
4298 int failed = 0;
4300 oprintf(_("Use `help' command to get list of available commands.\n"));
4302 select_completition(COMPL_COMMAND);
4303 set_prompt(NULL);
4305 while (1) {
4306 command_line = readline((prompt) ? prompt : _("shigofumi> "));
4307 /* Remember not parsable commands too to user be able to get back to
4308 * fix command */
4309 if (command_line && *command_line) {
4310 /* TODO: Omit blank lines */
4311 add_history(command_line);
4314 command_argv = tokenize(command_line, &command_argc, &shell);
4316 if (command_argv && command_argv[0]) {
4317 command = find_command(command_argv[0]);
4319 if (!command) {
4320 fprintf(stderr, _("Command not understood\n"));
4321 } else {
4322 failed = 0;
4323 if (shell) {
4324 fflush(stdout);
4325 pipe = popen(shell, "w");
4326 if (!pipe) {
4327 fprintf(stderr, _("Could not run shell command `%s':"
4328 " %s\n"), shell, strerror(errno));
4329 failed = 1;
4331 output = pipe;
4333 if (!failed) {
4334 command->function(command_argc,
4335 (const char **) command_argv);
4336 wait_for_shell(&pipe);
4341 argv_free(command_argv);
4342 zfree(shell);
4343 zfree(command_line);
4348 /* Non-interactive mode. Commands from @lines are processed until any command
4349 * lines remains or no error occurred. First failure terminates processing.
4350 * @lines is sequence of commands separated by '\n' or '\r'. The content is
4351 * modified during this call.
4352 * @return 0 if all command succeed, otherwise non-zero value
4354 int shi_batch(char *lines) {
4355 char *command_line;
4356 char **command_argv = NULL;
4357 int command_argc;
4358 char *shell = NULL;
4359 struct command *command = NULL;
4360 int retval = 0;
4361 FILE *pipe = NULL;
4362 int failed = 0;
4364 oprintf(_("Batch mode started.\n"));
4365 batch_mode = 1;
4366 select_completition(COMPL_COMMAND);
4368 while (!retval && (command_line = strtok(lines, "\n\r"))) {
4369 lines = NULL; /* strtok(3) requires it for subsequent calls */
4371 printf(_("Processing command: %s\n"), command_line);
4373 command_argv = tokenize(command_line, &command_argc, &shell);
4375 if (command_argv && command_argv[0]) {
4376 command = find_command(command_argv[0]);
4378 if (!command) {
4379 fprintf(stderr, _("Command not understood\n"));
4380 retval = -1;
4381 } else {
4382 failed = 0;
4383 if (shell) {
4384 fflush(stdout);
4385 pipe = popen(shell, "w");
4386 if (!pipe) {
4387 fprintf(stderr, _("Could not run shell command `%s':"
4388 " %s\n"), shell, strerror(errno));
4389 failed = 1;
4391 output = pipe;
4393 if (!failed) {
4394 retval = command->function(command_argc,
4395 (const char **) command_argv);
4396 if (wait_for_shell(&pipe)) retval = -1;
4401 argv_free(command_argv);
4402 zfree(shell);
4405 if (retval)
4406 fprintf(stderr, _("Command failed!\n"));
4407 return retval;
4411 #define COMMON_COMMANDS \
4412 { "accept", shi_accept_message, N_("accept commercial message"), \
4413 shi_accept_message_usage, ARGTYPE_MSGID }, \
4414 { "box", shi_box, N_("show current box details"), NULL, \
4415 ARGTYPE_NONE }, \
4416 { "boxlist", shi_boxlist, N_("get list of all boxes"), shi_boxlist_usage, \
4417 ARGTYPE_FILE }, \
4418 { "cache", shi_cache, N_("show cache details"), NULL, \
4419 ARGTYPE_NONE }, \
4420 { "cd", shi_chdir, N_("change working directory"), shi_chdir_usage, \
4421 ARGTYPE_FILE }, \
4422 { "commercialreceiving", shi_commercialreceiving, \
4423 N_("manipulate commercial receiving box status"), \
4424 shi_commercialreceiving_usage, ARGTYPE_BOXID }, \
4425 { "commercialsending", shi_commercialsending, \
4426 N_("manipulate commercial sending box status"), \
4427 shi_commercialsending_usage, ARGTYPE_BOXID }, \
4428 { "compose", shi_compose, N_("compose a message"), shi_compose_usage, \
4429 ARGTYPE_FILE }, \
4430 { "convert", shi_convert_file_or_message, \
4431 N_("submit local document for authorized conversion"), \
4432 shi_convert_file_or_message_usage, ARGTYPE_FILE }, \
4433 { "copying", shi_copying, N_("show this program licence excerpt"), NULL, \
4434 ARGTYPE_NONE }, \
4435 { "debug", shi_debug, N_("set debugging"), shi_debug_usage, \
4436 ARGTYPE_FILE }, \
4437 { "delete", shi_delete_message, N_("delete message from storage"), \
4438 shi_delete_message_usage, ARGTYPE_MSGID }, \
4439 { "delivery", shi_delivery, N_("get message delivery details"), \
4440 shi_delivery_usage, ARGTYPE_MSGID }, \
4441 { "findbox", shi_find_box, N_("search for a box"), shi_find_box_usage, \
4442 ARGTYPE_BOXID }, \
4443 { "hash", shi_hash, N_("query ISDS for message hash"), \
4444 shi_hash_usage, ARGTYPE_MSGID }, \
4445 { "help", shi_help, N_("describe commands"), shi_help_usage, \
4446 ARGTYPE_COMMAND }, \
4447 { "load", shi_load_anything, \
4448 N_("load message or message delivery details from local file"), \
4449 shi_load_anything_usage, ARGTYPE_FILE }, \
4450 { "login", shi_login, N_("log into ISDS"), shi_login_usage, \
4451 ARGTYPE_FILE }, \
4452 { "lsi", shi_list_incoming, N_("list received messages"), NULL, \
4453 ARGTYPE_NONE }, \
4454 { "lso", shi_list_outgoing, N_("list sent messages"), NULL, \
4455 ARGTYPE_NONE }, \
4456 { "msgi", shi_incoming_message, N_("get incoming message"), \
4457 shi_incoming_message_usage, ARGTYPE_MSGID }, \
4458 { "msgo", shi_outgoing_message, N_("get outgoing message"), \
4459 shi_outgoing_message_usage, ARGTYPE_MSGID }, \
4460 { "passwd", shi_passwd, N_("manipulate user password"), shi_passwd_usage, \
4461 ARGTYPE_NONE }, \
4462 { "pwd", shi_pwd, N_("print working directory"), NULL, ARGTYPE_NONE }, \
4463 { "quit", shi_quit, N_("exit shigofumi"), NULL, ARGTYPE_NONE }, \
4464 { "read", shi_read_message, N_("mark message as read"), \
4465 shi_read_message_usage, ARGTYPE_MSGID }, \
4466 { "resign", shi_resign, N_("re-sign message or delivery data"), \
4467 shi_resign_usage, ARGTYPE_FILE }, \
4468 { "set", shi_settings, N_("show settings"), NULL, ARGTYPE_NONE }, \
4469 { "sender", shi_message_sender, N_("get message sender"), \
4470 shi_message_sender_usage, ARGTYPE_MSGID }, \
4471 { "statbox", shi_stat_box, N_("get status of a box"), shi_stat_box_usage, \
4472 ARGTYPE_BOXID }, \
4473 { "user", shi_user, N_("show current user details"), NULL, \
4474 ARGTYPE_NONE }, \
4475 { "users", shi_users, N_("show box users"), shi_users_usage, \
4476 ARGTYPE_NONE }, \
4477 { "version", shi_version, N_("show version of this program"), NULL, \
4478 ARGTYPE_NONE}, \
4479 { NULL, NULL, NULL, NULL, ARGTYPE_NONE }
4481 struct command base_commands[] = {
4482 #if ENABLE_DEBUG
4483 { "quote", shi_quote, N_("demonstrate argument escaping"), NULL,
4484 ARGTYPE_FILE },
4485 { "print", shi_print, N_("print string into given width"),
4486 shi_print_usage, ARGTYPE_NONE },
4487 { "tokenize", shi_tokenize, N_("demonstrate arguments tokenization"), NULL,
4488 ARGTYPE_FILE },
4489 #endif
4490 COMMON_COMMANDS
4493 struct command message_commands[] = {
4494 { "authenticate", shi_authenticate, N_("check message authenticity"),
4495 NULL, ARGTYPE_NONE },
4496 { "cat", shi_cat_message, N_("show raw current message"),
4497 shi_cat_message_usage, ARGTYPE_NONE },
4498 { "catdoc", shi_cat_document, N_("show raw document"),
4499 shi_cat_document_usage, ARGTYPE_DOCID },
4500 { "convertdoc", shi_convert_document,
4501 N_("submit document of current message for authorized conversion"),
4502 shi_convert_document_usage, ARGTYPE_DOCID },
4503 { "dump", shi_dump_message, N_("dump current message structure"),
4504 NULL, ARGTYPE_NONE },
4505 { "opendoc", shi_open_document, N_("open document using external utility"),
4506 shi_open_document_usage, ARGTYPE_DOCID },
4507 { "savestamp", shi_save_stamp,
4508 N_("save time stamp of current message into local file"),
4509 shi_save_stamp_usage, ARGTYPE_FILE },
4510 { "savedoc", shi_save_document,
4511 N_("save document of current message into local file"),
4512 shi_save_document_usage, ARGTYPE_FILE },
4513 { "save", shi_save_message, N_("save current message into local file"),
4514 shi_save_message_usage, ARGTYPE_FILE },
4515 { "show", shi_show_message, N_("show current message"), NULL,
4516 ARGTYPE_NONE },
4517 { "verify", shi_verify, N_("verify current message hash"), NULL,
4518 ARGTYPE_NONE },
4519 COMMON_COMMANDS
4522 struct command list_commands[] = {
4523 { "show", shi_show_list, N_("show current message list"), NULL,
4524 ARGTYPE_NONE },
4525 COMMON_COMMANDS
4528 #undef COMMON_COMMANDS
4531 static void main_version(void) {
4532 isds_init();
4533 show_version();
4534 isds_cleanup();
4535 printf("\n");
4536 shi_copying(0, NULL);
4540 static void main_usage(const char *command) {
4541 oprintf(_(
4542 "Usage: %s [OPTION...]\n"
4543 "Access ISDS, process local data box messages or delivery details, submit\n"
4544 "document to authorized conversion.\n"
4545 "\n"
4546 "Options:\n"
4547 " -c FILE use the FILE as configuration file instead of ~/%s\n"
4548 " -e COMMANDS execute COMMANDS (new line separated) and exit\n"
4549 " -V show version info and exit\n"
4551 (command) ? command : "shigofumi",
4552 CONFIG_FILE);
4556 int main(int argc, char **argv) {
4557 int opt;
4558 char *config_file = NULL;
4559 char *batch_commands = NULL;
4560 int retval = EXIT_SUCCESS;
4562 setlocale(LC_ALL, "");
4563 #if ENABLE_NLS
4564 /* Initialize gettext */
4565 bindtextdomain(PACKAGE, LOCALEDIR);
4566 textdomain(PACKAGE);
4567 #endif
4569 /* Default output */
4570 output = stdout;
4572 /* Parse arguments */
4573 optind = 0;
4574 while ((opt = getopt(argc, (char * const *)argv, "c:e:V")) != -1) {
4575 switch (opt) {
4576 case 'c':
4577 config_file = optarg;
4578 break;
4579 case 'e':
4580 batch_commands = optarg;
4581 break;
4582 case 'V':
4583 main_version();
4584 shi_exit(EXIT_SUCCESS);
4585 break;
4586 default:
4587 main_usage((argv[0]) ? basename(argv[0]): NULL);
4588 shi_exit(EXIT_FAILURE);
4593 if (shi_init(config_file)) {
4594 shi_exit(EXIT_FAILURE);
4597 /*shi_login(NULL);*/
4599 if (batch_commands) {
4600 if (shi_batch(batch_commands))
4601 retval = EXIT_FAILURE;
4602 } else {
4603 shi_loop();
4606 shi_exit(retval);