1 #define _XOPEN_SOURCE 600
6 #include <readline/readline.h>
7 #include <readline/history.h>
17 #include <sys/types.h>
23 #include "shigofumi.h"
24 #include "completion.h"
30 #define CONFIG_FILE ".shigofumirc"
31 #define CONFIG_SERVER "base_url"
32 #define CONFIG_USERNAME "username"
33 #define CONFIG_PASSWORD "password"
34 #define CONFIG_CERT_FORMAT "certificate_format"
35 #define CONFIG_CERT_PATH "certificate_path"
36 #define CONFIG_KEY_ENGINE "key_engine"
37 #define CONFIG_KEY_FORMAT "key_format"
38 #define CONFIG_KEY_PATH "key_path"
39 #define CONFIG_KEY_PASSWORD "key_password"
40 #define CONFIG_OTP_METHOD "otp_method"
41 #define CONFIG_OTP_CODE "otp_code"
42 #define CONFIG_DEBUGLEVEL "debug_level"
43 #define CONFIG_VERIFYSERVER "verify_server"
44 #define CONFIG_CAFILE "ca_file"
45 #define CONFIG_CADIRECTORY "ca_directory"
46 #define CONFIG_CLEAN_TEMPORARY_FILES "clean_temporary_files"
47 #define CONFIG_CONFIRM_SEND "confirm_send"
48 #define CONFIG_CRLFILE "crl_file"
49 #define CONFIG_TIMEOUT "timeout"
50 #define CONFIG_LOGFACILITIES "log_facilities"
51 #define CONFIG_LOGFILE "log_file"
52 #define CONFIG_LOGLEVEL "log_level"
53 #define CONFIG_MARKMESSAGEREAD "mark_message_read"
54 #define CONFIG_NORMALIZEMIMETYPE "normalize_mime_type"
55 #define CONFIG_OPENCOMMAND "open_command"
56 #define CONFIG_OVERWRITEFILES "overwrite_files"
57 #define CZPDEPOSIT_URL "https://www.czechpoint.cz/uschovna/"
63 cfg_opt_t configuration_syntax
[] = {
64 CFG_STR(CONFIG_SERVER
, NULL
, CFGF_NONE
),
65 CFG_STR(CONFIG_USERNAME
, NULL
, CFGF_NODEFAULT
),
66 CFG_STR(CONFIG_PASSWORD
, NULL
, CFGF_NODEFAULT
),
67 CFG_STR(CONFIG_CERT_FORMAT
, NULL
, CFGF_NODEFAULT
),
68 CFG_STR(CONFIG_CERT_PATH
, NULL
, CFGF_NODEFAULT
),
69 CFG_STR(CONFIG_KEY_ENGINE
, NULL
, CFGF_NODEFAULT
),
70 CFG_STR(CONFIG_KEY_FORMAT
, NULL
, CFGF_NODEFAULT
),
71 CFG_STR(CONFIG_KEY_PATH
, NULL
, CFGF_NODEFAULT
),
72 CFG_STR(CONFIG_KEY_PASSWORD
, NULL
, CFGF_NODEFAULT
),
73 CFG_STR(CONFIG_OTP_METHOD
, NULL
, CFGF_NODEFAULT
),
74 CFG_STR(CONFIG_OTP_CODE
, NULL
, CFGF_NODEFAULT
),
75 /*CFG_STR(CONFIG_DEBUGLEVEL, NULL, CFGF_NODEFAULT),*/
76 CFG_BOOL(CONFIG_VERIFYSERVER
, cfg_true
, CFGF_NONE
),
77 CFG_STR(CONFIG_CAFILE
, NULL
, CFGF_NODEFAULT
),
78 CFG_STR(CONFIG_CADIRECTORY
, NULL
, CFGF_NODEFAULT
),
79 CFG_BOOL(CONFIG_CLEAN_TEMPORARY_FILES
, cfg_true
, CFGF_NONE
),
80 CFG_STR(CONFIG_CRLFILE
, NULL
, CFGF_NODEFAULT
),
81 CFG_INT(CONFIG_TIMEOUT
, TIMEOUT
, CFGF_NONE
),
82 CFG_STR_LIST(CONFIG_LOGFACILITIES
, "{none}", CFGF_NONE
),
83 CFG_STR(CONFIG_LOGFILE
, NULL
, CFGF_NODEFAULT
),
84 CFG_INT(CONFIG_LOGLEVEL
, LOG_LEVEL
, CFGF_NONE
),
85 CFG_BOOL(CONFIG_CONFIRM_SEND
, cfg_true
, CFGF_NONE
),
86 CFG_BOOL(CONFIG_MARKMESSAGEREAD
, cfg_false
, CFGF_NONE
),
87 CFG_BOOL(CONFIG_NORMALIZEMIMETYPE
, cfg_true
, CFGF_NONE
),
88 CFG_STR_LIST(CONFIG_OPENCOMMAND
, "{xdg-open, %f}", CFGF_NONE
),
89 CFG_BOOL(CONFIG_OVERWRITEFILES
, cfg_true
, CFGF_NONE
),
100 struct command (*commands
)[] = NULL
;
104 struct isds_ctx
*cisds
= NULL
;
105 struct isds_list
*boxes
= NULL
;
106 struct isds_message
*message
= NULL
;
107 _Bool messages_are_outgoing
= 0;
108 struct isds_list
*messages
= NULL
;
109 unsigned long int total_messages
= 0;
110 struct isds_ctx
*czechpoint
= NULL
;
111 struct isds_list
*temporary_files
= NULL
;
113 /* Temporary log-in settings */
115 char *username
= NULL
;
116 char *password
= NULL
;
117 char *key_password
= NULL
;
118 char *otp_method
= NULL
;
119 char *otp_code
= NULL
;
120 char *pki_engine
= NULL
;
121 char *pki_certificate_path
= NULL
;
122 char *pki_certificate_format
= NULL
;
123 char *pki_key_path
= NULL
;
124 char *pki_key_format
= NULL
;
126 static void discard_credentials(void) {
134 zfree(pki_certificate_path
);
135 zfree(pki_certificate_format
);
137 zfree(pki_key_format
);
141 /* Remove temporary file */
142 void shi_unlink_temporary_file(void **data
) {
143 if (!data
|| !*data
) return;
145 const char *file
= (const char *)*data
;
151 /* Finish ISDS operation, report error, if the operation returned different
152 * code than @positive_code. */
153 static void finish_isds_operation_with_code(struct isds_ctx
*ctx
,
154 isds_error err
, isds_error positive_code
) {
155 shi_progressbar_finish();
156 if (err
!= positive_code
) {
157 if (isds_long_message(ctx
))
158 fprintf(stderr
, _("Error occurred: %s: %s\n"), isds_strerror(err
),
159 isds_long_message(ctx
));
161 fprintf(stderr
, _("Error occurred: %s\n"), isds_strerror(err
));
166 /* Finish ISDS operation, report error, if the operation did not returned
168 static void finish_isds_operation(struct isds_ctx
*ctx
, isds_error err
) {
169 finish_isds_operation_with_code(ctx
, err
, IE_SUCCESS
);
173 /* Do the cleanup and exit */
174 static void shi_exit(int exit_code
) {
176 discard_credentials();
177 isds_list_free(&boxes
);
178 isds_message_free(&message
);
179 isds_list_free(&messages
);
180 if (temporary_files
) {
181 oprintf(_("Removing temporary files...\n"));
182 isds_list_free(&temporary_files
);
187 oprintf(_("Logging out...\n"));
188 err
= isds_logout(cisds
);
189 finish_isds_operation(cisds
, err
);
190 if (err
) exit_code
= EXIT_FAILURE
;
191 isds_ctx_free(&cisds
);
193 isds_ctx_free(&czechpoint
);
197 cfg_free(configuration
);
206 /* Set prompt. if @format is NULL, switch to default prompt */
207 static void set_prompt(const char *format
, ...) {
211 va_start(ap
, format
);
212 shi_vasprintf(&buffer
, format
, ap
);
216 shi_asprintf(&prompt
, _("%s> "), buffer
);
225 prompt
= strdup(_("> "));
233 /* Convert name of PKI format into ISDS format type.
234 * Return -1 in case of invalid name */
235 static int string2pki_format(const char *name
, isds_pki_format
*format
) {
236 if (!name
|| !format
) { return -1; }
237 if (!strcasecmp(name
, "PEM")) {
238 *format
= PKI_FORMAT_PEM
;
239 } else if (!strcasecmp(name
, "DER")) {
240 *format
= PKI_FORMAT_DER
;
241 } else if (!strcasecmp(name
, "ENG")) {
242 *format
= PKI_FORMAT_ENG
;
250 /* Check for configutation option CONFIG_CERT_FORMAT.
251 * Return 0 in case of valid or undefined value. */
252 static int shi_validate_certificate_format(cfg_t
*configuration
, cfg_opt_t
*option
) {
253 isds_pki_format format
;
254 const char *value
= cfg_opt_getnstr(option
, 0);
256 if (NULL
!= value
&& string2pki_format(value
, &format
)) {
257 cfg_error(configuration
, _("Invalid certificate format: %s"), value
);
264 /* Check for configutation option CONFIG_KEY_FORMAT.
265 * Return 0 in case of valid or undefined value. */
266 static int shi_validate_key_format(cfg_t
*configuration
, cfg_opt_t
*option
) {
267 isds_pki_format format
;
268 const char *value
= cfg_opt_getnstr(option
, 0);
270 if (NULL
!= value
&& string2pki_format(value
, &format
)) {
271 cfg_error(configuration
, _("Invalid private key format: %s"), value
);
278 static int shi_load_configuration(const char *config_file
) {
279 char *config_name
= NULL
;
283 /* Get config file */
285 config_name
= (char *) config_file
;
287 if (-1 == shi_asprintf(&config_name
, "%s/%s", getenv("HOME"),
289 fprintf(stderr
, _("Could not build configuration file name\n"));
294 /* Parse configuration */
295 configuration
= cfg_init(configuration_syntax
, CFGF_NONE
);
296 cfg_set_validate_func(configuration
,
297 CONFIG_CERT_FORMAT
, shi_validate_certificate_format
);
298 cfg_set_validate_func(configuration
,
299 CONFIG_KEY_FORMAT
, shi_validate_key_format
);
301 ret
= cfg_parse(configuration
, config_name
);
304 if (ret
== CFG_FILE_ERROR
) {
306 _("Error while opening configuration file `%s': %s\n"),
307 config_name
, strerror(errno
));
308 if (NULL
== config_file
) {
309 /* Consider missing default configuration file as non-fatal. */
310 oprintf(_("Using default configuration\n"));
314 fprintf(stderr
, _("Error while parsing configuration file `%s'\n"),
319 if (config_name
!= config_file
) free(config_name
);
324 void logger(isds_log_facility facility
, isds_log_level level
,
325 const char *message
, int length
, void *data
) {
327 ssize_t written
, left
= length
;
330 fd
= *((int *) data
);
331 /*printf("\033[32mLOG(%02d,%02d): ", facility, level);
332 printf("%.*s", length, message);
336 written
= write(fd
, message
+ length
- left
, left
);
339 _("Could not save log message into log file: %s\n"
340 "Log message discarded!\n"),
351 /* Redirect ISDS log to file if @file is not NULL. */
352 static int do_log_to_file(const char *file
) {
354 logger_fd
= open_file_for_writing(file
, 0, 1);
355 if (logger_fd
== -1) {
356 fprintf(stderr
, _("Could not redirect ISDS log to file `%s'\n"),
360 isds_set_log_callback(logger
, &logger_fd
);
366 /* Add log facility based on its name. */
367 static int add_log_facility(isds_log_facility
*facilities
, const char *name
) {
368 if (!facilities
) return -1;
370 if (!strcmp(name
, "none")) *facilities
|= ILF_NONE
;
371 else if (!strcmp(name
, "http")) *facilities
|= ILF_HTTP
;
372 else if (!strcmp(name
, "soap")) *facilities
|= ILF_SOAP
;
373 else if (!strcmp(name
, "isds")) *facilities
|= ILF_ISDS
;
374 else if (!strcmp(name
, "file")) *facilities
|= ILF_FILE
;
375 else if (!strcmp(name
, "sec")) *facilities
|= ILF_SEC
;
376 else if (!strcmp(name
, "xml")) *facilities
|= ILF_XML
;
377 else if (!strcmp(name
, "all")) *facilities
|= ILF_ALL
;
379 fprintf(stderr
, _("%s: Unknown log facility\n"), name
);
387 /* Save log facility into confuse configuration */
388 static void save_log_facility(int level
) {
389 cfg_setlist(configuration
, CONFIG_LOGFACILITIES
, 0);
391 if (level
== ILF_ALL
) {
392 cfg_addlist(configuration
, CONFIG_LOGFACILITIES
, 1, "all");
395 if (level
== ILF_NONE
) {
396 cfg_addlist(configuration
, CONFIG_LOGFACILITIES
, 1, "none");
399 if (level
& ILF_HTTP
)
400 cfg_addlist(configuration
, CONFIG_LOGFACILITIES
, 1, "http");
401 if (level
& ILF_SOAP
)
402 cfg_addlist(configuration
, CONFIG_LOGFACILITIES
, 1, "soap");
403 if (level
& ILF_ISDS
)
404 cfg_addlist(configuration
, CONFIG_LOGFACILITIES
, 1, "isds");
405 if (level
& ILF_FILE
)
406 cfg_addlist(configuration
, CONFIG_LOGFACILITIES
, 1, "file");
408 cfg_addlist(configuration
, CONFIG_LOGFACILITIES
, 1, "sec");
410 cfg_addlist(configuration
, CONFIG_LOGFACILITIES
, 1, "xml");
414 /* Clamp long int to unsigned int */
415 static unsigned int normalize_timeout(long int raw
) {
417 oprintf(_("Configured network timeout is less then 0. "
421 if (raw
> UINT_MAX
) {
422 oprintf(_("Configured network timeout is greater then %1$u. "
423 "Clamped to %1$u.\n"), UINT_MAX
);
426 return (unsigned int) raw
;
430 /* Clamp long int to <0;100> */
431 static unsigned int normalize_log_level(long int raw
) {
433 oprintf(_("Configured log level is less then 0. Clamped to 0.\n"));
437 oprintf(_("Configured log level is greater then %1$u. "
438 "Clamped to %1$u.\n"), ILL_ALL
);
441 if (raw
> UINT_MAX
) {
442 oprintf(_("Configured log level is greater then %1$u. "
443 "Clamped to %1$u.\n"), UINT_MAX
);
446 return (unsigned int) raw
;
450 static int shi_init(const char *config_file
) {
453 unsigned int timeout
, log_level
;
454 isds_log_facility log_facility
= ILF_NONE
;
456 oprintf(_("This is Shigofumi, an ISDS client. "
457 "Have a nice e-government.\n"));
459 /* Do not permute arguments in getopt() */
460 if (setenv("POSIXLY_CORRECT", "", 1)) {
462 _("Could not set POSIXLY_CORRECT environment variable\n"));
466 /* Load configuration */
467 if (shi_load_configuration(config_file
))
469 timeout
= normalize_timeout(cfg_getint(configuration
, CONFIG_TIMEOUT
));
470 log_level
= normalize_log_level(cfg_getint(configuration
, CONFIG_LOGLEVEL
));
473 rl_readline_name
= "shigofumi";
474 rl_filename_quote_characters
= "\\ >";
475 rl_filename_quoting_function
= shi_quote_filename
;
476 rl_filename_dequoting_function
= shi_dequote_filename
;
477 rl_char_is_quoted_p
= shi_char_is_quoted
;
479 /* Initialize ISDS */
482 fprintf(stderr
, _("Could not initialize libisds library: %s\n"),
487 /* Set ISDS logging */
488 value
= cfg_getstr(configuration
, CONFIG_LOGFILE
);
489 if (do_log_to_file(value
))
491 for (int i
= 0; i
< cfg_size(configuration
, CONFIG_LOGFACILITIES
); i
++) {
492 if (add_log_facility(&log_facility
,
493 cfg_getnstr(configuration
, CONFIG_LOGFACILITIES
, i
)))
497 isds_set_logging(log_facility
, log_level
);
499 /* Set ISDS context up */
500 cisds
= isds_ctx_create();
502 fprintf(stderr
, _("Could not create ISDS context\n"));
505 err
= isds_set_timeout(cisds
, timeout
);
507 fprintf(stderr
, _("Could not set ISDS network timeout: %s\n"),
510 err
= isds_set_progress_callback(cisds
, shi_progressbar
, NULL
);
512 fprintf(stderr
, _("Could not register network progress bar: %s: %s\n"),
513 isds_strerror(err
), isds_long_message(cisds
));
515 err
= isds_set_opt(cisds
, IOPT_NORMALIZE_MIME_TYPE
,
516 cfg_getbool(configuration
, CONFIG_NORMALIZEMIMETYPE
));
519 cfg_getbool(configuration
, CONFIG_NORMALIZEMIMETYPE
) ?
520 _("Could not enable MIME type normalization: %s: %s\n") :
521 _("Could not disable MIME type normalization: %s: %s\n"),
522 isds_strerror(err
), isds_long_message(cisds
));
524 if (!cfg_getbool(configuration
, CONFIG_VERIFYSERVER
)) {
525 oprintf(_("Warning: Shigofumi disabled server identity verification "
526 "on user request!\n"));
527 err
= isds_set_opt(cisds
, IOPT_TLS_VERIFY_SERVER
, 0);
530 _("Could not disable server identity verification: "
532 isds_strerror(err
), isds_long_message(cisds
));
535 if ((value
= cfg_getstr(configuration
, CONFIG_CAFILE
))) {
536 err
= isds_set_opt(cisds
, IOPT_TLS_CA_FILE
, value
);
539 _("Could not set file with CA certificates: %s: %s: %s\n"),
540 value
, isds_strerror(err
), isds_long_message(cisds
));
543 if ((value
= cfg_getstr(configuration
, CONFIG_CADIRECTORY
))) {
544 err
= isds_set_opt(cisds
, IOPT_TLS_CA_DIRECTORY
, value
);
547 _("Could not set directory with CA certificates: "
549 value
, isds_strerror(err
), isds_long_message(cisds
));
552 if ((value
= cfg_getstr(configuration
, CONFIG_CRLFILE
))) {
553 err
= isds_set_opt(cisds
, IOPT_TLS_CRL_FILE
, value
);
555 fprintf(stderr
, _("Could not set file with CRL: %s: %s: %s\n"),
556 value
, isds_strerror(err
), isds_long_message(cisds
));
561 /* Set Czech POINT context up */
562 czechpoint
= isds_ctx_create();
564 fprintf(stderr
, _("Could not create Czech POINT context\n"));
567 err
= isds_set_timeout(czechpoint
, timeout
);
569 fprintf(stderr
, _("Could not set Czech POINT network timeout: %s\n"),
572 err
= isds_set_progress_callback(czechpoint
, shi_progressbar
, NULL
);
574 fprintf(stderr
, "Could not register network progress bar: %s: %s\n",
575 isds_strerror(err
), isds_long_message(cisds
));
582 static int shi_quit(int argc
, const char **argv
) {
583 shi_exit(EXIT_SUCCESS
);
589 static void shi_help_usage(const char *command
) {
591 "Usage: %s [COMMAND]\n"
592 "Show COMMAND manual or list of currently available commands.\n"
598 static int shi_help(int argc
, const char **argv
) {
599 size_t command_width
= 14;
603 fprintf(stderr
, _("No command is available\n"));
607 if (argc
== 2 && argv
[1] && *argv
[1]) {
608 /* Show usage for given command */
609 for (i
= 0; (*commands
)[i
].name
; i
++) {
610 if (!strcmp((*commands
)[i
].name
, argv
[1])) {
611 if ((*commands
)[i
].usage
)
612 (*commands
)[i
].usage((*commands
)[i
].name
);
613 else if ((*commands
)[i
].description
) {
614 ohprint((*commands
)[i
].name
, command_width
);
615 oprintf(" %s\n", _((*commands
)[i
].description
));
619 _("%s: %s: Command description not defined\n"),
624 fprintf(stderr
, _("%s: %s: No such command exists\n"), argv
[0], argv
[1]);
628 /* Or list all commands */
629 oprintf(_("Following commands are available:\n"));
630 for (i
= 0; (*commands
)[i
].name
; i
++) {
631 ohprint((*commands
)[i
].name
, command_width
);
632 oprintf(" %s\n", _((*commands
)[i
].description
));
639 static void show_version(void) {
640 char *libisds_version
= isds_version();
642 oprintf(_("This is Shigofumi version %s.\n"), PACKAGE_VERSION
);
648 confuse_version
, libisds_version
);
649 #if HAVE_DECL_MAGIC_VERSION
650 oprintf(_("libmagic: %d\n"), magic_version());
652 oprintf(_("libxml2: %s\n"
655 xmlParserVersion
, rl_library_version
);
656 free(libisds_version
);
660 static int shi_version(int argc
, const char **argv
) {
665 "It's a shigofumi. A letter delivered from the afterlife. (Fumika)\n"
666 "A message can not be delivered to dead person. (ISDS specification)\n"
667 "Virtual and real world. They can be compatible. (Program author)\n"
673 static int shi_copying(int argc
, const char **argv
) {
675 "This is Shigofumi, an ISDS client.\n"
676 "Copyright (C) 2010, 2011, 2012, 2013 Petr Pisar\n"
678 "This program is free software: you can redistribute it and/or modify\n"
679 "it under the terms of the GNU General Public License as published by\n"
680 "the Free Software Foundation, either version 3 of the License, or\n"
681 "(at your option) any later version.\n"
683 "This program is distributed in the hope that it will be useful,\n"
684 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
685 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
686 "GNU General Public License for more details.\n"
688 "You should have received a copy of the GNU General Public License\n"
689 "along with this program. If not, see <http://www.gnu.org/licenses/>.\n"
695 static int shi_cache(int argc
, const char **argv
) {
696 const struct isds_list
*item
;
700 for (item
= boxes
, i
= 0; item
; item
= item
->next
, i
++);
702 "Cached box list: %zu\n"),
708 "Cached message list:\n"
710 "\tMessages: %'lu\n"),
711 (messages_are_outgoing
) ? _("Outgoing") : _("Incoming"),
716 oprintf(_("Cached message: %s\n"),
717 (message
->envelope
&& message
->envelope
->dmID
) ?
718 message
->envelope
->dmID
: _("<Unknown ID>"));
725 static void shi_chdir_usage(const char *command
) {
727 "Usage: %s [DIRECTORY]\n"
728 "Change working directory to DIRECTORY.\n"
729 "If no DIRECTORY is supplied, HOME directory will be used.\n"),
734 static int shi_chdir(int argc
, const char **argv
) {
735 const char *directory
= NULL
;
737 if (!argv
|| argc
> 2) {
738 shi_chdir_usage((argv
) ? argv
[0] : NULL
);
742 if (argc
== 2 && argv
[1] && *argv
[1])
745 directory
= getenv("HOME");
747 oprintf("Environment variable HOME does not exist\n");
751 if (chdir(directory
)) {
752 oprintf(_("Could not change working directory: %s: %s\n"), directory
,
761 static int shi_pwd(int argc
, const char **argv
) {
762 char *buffer
= NULL
, *newbuffer
;
765 while (length
+= 1024) {
766 newbuffer
= realloc(buffer
, length
);
768 fprintf(stderr
, _("Error: Not enough memory\n"));
774 if (getcwd(buffer
, length
)) {
775 oprintf("%s\n", buffer
);
781 fprintf(stderr
, _("Error: Current directory string is too long\n"));
787 /* Deallocate *@destination and duplicate @new_value if non-NULL. In case of
788 * error, it prints error message and returns -1. Otherwise it returns 0. */
789 static int replace_string(char **destination
, const char *new_value
) {
790 if (destination
== NULL
) return -1;
792 if (new_value
!= NULL
) {
793 *destination
= strdup(new_value
);
794 if (*destination
== NULL
) {
795 fprintf(stderr
, _("Not enough memory\n"));
803 /* Convert name of OTP authentication method into ISDS method type.
804 * Return -1 in case of invalid name */
805 static int string2otp_method(const char *name
, isds_otp_method
*method
) {
806 if (!name
|| !method
) { return -1; }
807 if (!strcasecmp(name
, "HOTP")) {
809 } else if (!strcasecmp(name
, "TOTP")) {
819 * Return -1 in case of failure, 0 in case of success, +1 in case of partial
820 * success (e.g. TOTP preauthentication to obtain new code succeeded, but user
821 * is still not logged in because second phase is necessary). */
822 static int do_login(void) {
824 struct isds_pki_credentials pki
;
827 /* Build OTP structure */
828 if (NULL
!= otp_method
) {
829 if (string2otp_method(otp_method
, &otp
.method
)) {
830 fprintf(stderr
, _("Error: Invalid one-time password "
831 "authentication method `%s'\n"), otp_method
);
836 /* Announce base URL */
837 oprintf(_("ISDS base URL: %s\n"),
838 (server
== NULL
) ? _("<default>") : server
);
841 oprintf(_("Unattended mode detected. "
842 "Make sure credentials have been preset.\n"));
844 oprintf(_("You are going to insert credentials for your account.\n"
845 "Leave blank line to choose default value.\n"));
847 select_completion(COMPL_NONE
);
849 /* Ask for user name if not predefined */
850 if (NULL
== username
) {
851 shi_ask_for_string(&username
, _("Input ISDS user name: "),
852 cfg_getstr(configuration
, CONFIG_USERNAME
), batch_mode
);
855 /* Ask for password */
856 shi_ask_for_password(&password
, _("Input ISDS password: "),
857 cfg_getstr(configuration
, CONFIG_PASSWORD
), batch_mode
);
859 /* Ask for key password if PKI authentication requested */
860 if (NULL
!= pki_key_path
) {
861 shi_ask_for_password(&key_password
, _("Input private key password: "),
862 cfg_getstr(configuration
, CONFIG_KEY_PASSWORD
), batch_mode
);
865 /* Ask for OTP code if OTP authentication requested */
866 if (NULL
!= otp_method
) {
867 shi_ask_for_password(&otp_code
,
868 (otp
.method
== OTP_TIME
) ?
869 _("Input one-time code (empty to send new one): ") :
870 _("Input one-time code: "),
871 cfg_getstr(configuration
, CONFIG_OTP_CODE
), batch_mode
);
872 otp
.otp_code
= otp_code
;
875 select_completion(COMPL_COMMAND
);
878 /* Build PKI structure */
879 if (NULL
!= pki_certificate_path
|| NULL
!= pki_key_path
) {
880 pki
.engine
= pki_engine
;
881 if (NULL
== pki_certificate_format
) {
882 fprintf(stderr
, _("Error: No certficate format supplied\n"));
885 if (string2pki_format(pki_certificate_format
, &pki
.certificate_format
)) {
886 fprintf(stderr
, _("Error: Invalid certificate format `%s'\n"),
887 pki_certificate_format
);
890 if (NULL
== pki_key_format
) {
891 fprintf(stderr
, _("Error: No private key format supplied\n"));
894 if (string2pki_format(pki_key_format
, &pki
.key_format
)) {
895 fprintf(stderr
, _("Error: Invalid private key format `%s'\n"),
899 pki
.certificate
= pki_certificate_path
;
900 pki
.key
= pki_key_path
;
901 pki
.passphrase
= key_password
;
904 if (NULL
!= otp_method
&& OTP_TIME
== otp
.method
&& NULL
== otp_code
)
905 printf(_("Requesting one-time code from server for "
908 printf(_("Logging in...\n"));
909 err
= isds_login(cisds
, server
, username
, password
,
910 (NULL
!= pki_certificate_path
|| NULL
!= pki_key_path
) ?
912 (NULL
!= otp_method
) ? &otp
: NULL
);
913 if (NULL
!= otp_method
&& OTP_TIME
== otp
.method
&& NULL
== otp_code
)
914 finish_isds_operation_with_code(cisds
, err
, IE_PARTIAL_SUCCESS
);
916 finish_isds_operation(cisds
, err
);
918 if (IE_PARTIAL_SUCCESS
== err
) {
919 printf(_("OTP code has been sent by ISDS successfully.\n"
920 "Once you receive the code, retry log-in with "
924 printf(_("Log-in failed\n"));
928 oprintf(_("Logged in.\n"));
933 static struct isds_DbOwnerInfo
*do_box(void) {
935 struct isds_DbOwnerInfo
*box
= NULL
;
937 printf(_("Getting box details you are logged in...\n"));
938 err
= isds_GetOwnerInfoFromLogin(cisds
, &box
);
939 finish_isds_operation(cisds
, err
);
945 static int shi_box(int argc
, const char **argv
) {
946 struct isds_DbOwnerInfo
*box
= NULL
;
951 format_DbOwnerInfo(box
);
953 isds_DbOwnerInfo_free(&box
);
958 /* Get info about box with @id.
959 * @id is UTF-8 encoded
960 * Return NULL in case of error, otherwise box description that caller must
962 static struct isds_DbOwnerInfo
*stat_box(const char *id
) {
964 struct isds_DbOwnerInfo criteria
;
965 struct isds_list
*boxes
= NULL
, *item
;
966 struct isds_DbOwnerInfo
*box
= NULL
;
967 char *id_locale
= NULL
;
969 if (!id
|| !*id
) return NULL
;
971 id_locale
= utf82locale(id
);
972 memset(&criteria
, 0, sizeof(criteria
));
973 criteria
.dbID
= (char *) id
;
975 printf(_("Getting details about box with ID `%s'...\n"), id_locale
);
976 err
= isds_FindDataBox(cisds
, &criteria
, &boxes
);
977 finish_isds_operation(cisds
, err
);
980 for(item
= boxes
; item
; item
= item
->next
) {
981 if (!item
->data
) continue;
984 fprintf(stderr
, _("Error: More boxes match ID `%s'\n"), id_locale
);
988 box
= (struct isds_DbOwnerInfo
*) item
->data
;
995 isds_list_free(&boxes
);
1001 /* Obtain box ID value either from locale-encoded argument or from current box
1002 * the context is logged in.
1003 * @arg is locale-encoded box ID or NULL
1004 * @box_id outputs pointer to reallocated UTF-8 encoded box ID. Pass NULL if
1005 * you are not interreted in it. Will be NULLed on error.
1006 * @box_id_locale outputs pointer to reallocated locale-encoded box ID. Pass
1007 * NULL if you are not interrested in it. Will be NULLed on error.
1008 * @return 0 on sucess, -1 on error. */
1009 static int get_current_box_id(const char *arg
,
1010 char **box_id
, char **box_id_locale
) {
1011 struct isds_DbOwnerInfo
*box
= NULL
;
1013 if (NULL
!= box_id
) zfree(*box_id
);
1014 if (NULL
!= box_id_locale
) zfree(*box_id_locale
);
1016 if (NULL
== arg
|| '\0' == *arg
) {
1017 /* Get current box ID */
1019 if (NULL
== box
|| NULL
== box
->dbID
|| !*box
->dbID
) {
1020 isds_DbOwnerInfo_free(&box
);
1021 fprintf(stderr
, _("Could not get current box ID\n"));
1024 if (NULL
!= box_id
) {
1025 *box_id
= strdup(box
->dbID
);
1026 if (NULL
== *box_id
) {
1027 fprintf(stderr
, _("Not enough memory\n"));
1031 if (NULL
!= box_id_locale
) {
1032 *box_id_locale
= utf82locale(box
->dbID
);
1033 if (NULL
== *box_id_locale
) {
1034 fprintf(stderr
, _("Could not convert box ID to locale\n"));
1039 /* Box ID supplied as argument */
1040 if (NULL
!= box_id_locale
) {
1041 *box_id_locale
= strdup(arg
);
1042 if (NULL
== *box_id_locale
) {
1043 fprintf(stderr
, _("Not enough memory\n"));
1047 if (NULL
!= box_id
) {
1048 *box_id
= locale2utf8(arg
);
1049 if (NULL
== *box_id
) {
1050 fprintf(stderr
, _("Could not convert box ID `%s' to UTF-8\n"),
1060 if (NULL
!= box_id
) zfree(*box_id
);
1061 if (NULL
!= box_id_locale
) zfree(*box_id_locale
);
1066 static void shi_commercialcredit_usage(const char *command
) {
1068 "Usage: %s [OPTION...] [BOX_ID]\n"
1069 "Retrieve details and history of a credit available for sending commercial\n"
1070 "messages from a box with ID BOX_ID. Default is box you are logged in.\n"
1071 "Options require a date argument:\n"
1072 " -f list history from date inclusive (locale or full ISO 8601 date)\n"
1073 " -t list history to date inclusive (locale or full ISO 8601 date)\n"
1078 /* Retrieve details about credit for sending commercial messages */
1079 static int shi_commercialcredit(int argc
, const char **argv
) {
1081 struct tm
*from_date
= NULL
, *to_date
= NULL
;
1082 long int current_credit
;
1083 char *notification_email
= NULL
;
1084 struct isds_list
*history
= NULL
, *item
;
1086 char *box_id
= NULL
, *box_id_locale
= NULL
;
1091 while ((opt
= getopt(argc
, (char * const *)argv
, "f:ht:")) != -1) {
1095 from_date
= datestring2tm(optarg
);
1096 if (NULL
== from_date
) {
1097 fprintf(stderr
, _("Error: Could not parse date: %s\n"),
1104 shi_commercialcredit_usage((argv
)?argv
[0]:NULL
);
1108 to_date
= datestring2tm(optarg
);
1109 if (NULL
== to_date
) {
1110 fprintf(stderr
, _("Error: Could not parse date: %s\n"),
1117 shi_commercialcredit_usage((argv
)?argv
[0]:NULL
);
1122 if (optind
+ 1 < argc
) {
1123 fprintf(stderr
, _("Bad invocation\n"));
1124 shi_commercialcredit_usage((argv
)?argv
[0]:NULL
);
1129 if ((retval
= get_current_box_id(argv
[optind
], &box_id
, &box_id_locale
))) {
1133 printf(_("Querying `%s' box credit details...\n"),
1135 err
= isds_get_commercial_credit(cisds
, box_id
, from_date
, to_date
,
1136 ¤t_credit
, ¬ification_email
, &history
);
1137 finish_isds_operation(cisds
, err
);
1140 oprintf(_("Details of credit for sending commercial messages "
1141 "from box `%s':\n"), box_id_locale
);
1142 print_header_currency(_("Current credit"), ¤t_credit
);
1143 print_header_utf8(_("Notification e-mail"), notification_email
);
1145 if (NULL
!= history
) {
1146 oprintf(_("History of credit change events:\n"));
1147 print_header_tm(_("From"), from_date
);
1148 print_header_tm(_("To"), to_date
);
1149 for (item
= history
, ordinar
= 0; item
; item
=item
->next
) {
1150 if (!item
->data
) continue;
1152 oprintf(_("\n* Event #%d:\n"), ordinar
);
1153 format_credit_event(item
->data
);
1156 oprintf(_("No event exists.\n"));
1159 oprintf(_("Could not get details about a credit available for sending "
1160 "commercial messages\n"
1161 "from box `%s'.\n"),
1167 isds_list_free(&history
);
1168 free(notification_email
);
1172 free(box_id_locale
);
1177 static void shi_commercialreceiving_usage(const char *command
) {
1179 "Usage: %s [-0|-1] [BOX_ID]\n"
1180 "Manipulate commercial receiving box status.\n"
1181 " -O switch off receiving of commercial messages\n"
1182 " -1 switch on receiving of commercial messages\n"
1183 " BOX_ID affects box with ID BOX_ID; default is box you are logged in\n"
1184 "If no option is given, show current commercial receiving status.\n"),
1189 /* Manipulate commercial receiving box status */
1190 static int shi_commercialreceiving(int argc
, const char **argv
) {
1194 char *box_id
= NULL
, *box_id_locale
= NULL
;
1198 while ((opt
= getopt(argc
, (char * const *)argv
, "01")) != -1) {
1207 shi_commercialreceiving_usage((argv
)?argv
[0]:NULL
);
1211 if (optind
+ 1 < argc
) {
1212 fprintf(stderr
, _("Bad invocation\n"));
1213 shi_commercialreceiving_usage((argv
)?argv
[0]:NULL
);
1217 if ((retval
= get_current_box_id(argv
[optind
], &box_id
, &box_id_locale
))) {
1222 struct isds_DbOwnerInfo
*box
= stat_box(box_id
);
1224 fprintf(stderr
, _("Could not get details about box ID `%s'\n"),
1230 oprintf(_("Commercial receiving status of box `%s': "), box_id_locale
);
1231 if (!box
->dbOpenAddressing
)
1232 oprintf(_("Unknown\n"));
1233 else if (*box
->dbOpenAddressing
)
1234 oprintf(_("Positive\n"));
1236 oprintf(_("Negative\n"));
1237 isds_DbOwnerInfo_free(&box
);
1239 char *refnumber
= NULL
;
1241 _("Switching `%s' box commercial receiving on...\n"):
1242 _("Switching `%s' box commercial receiving off...\n"),
1244 err
= isds_switch_commercial_receiving(cisds
, box_id
, action
,
1246 finish_isds_operation(cisds
, err
);
1249 char *refnumber_locale
= utf82locale(refnumber
);
1250 oprintf(_("Commercial receiving status successfully changed. "
1251 "Assigned reference number: %s\n"),
1253 free(refnumber_locale
);
1255 oprintf(_("Commercial receiving status has not been changed.\n"));
1263 free(box_id_locale
);
1268 static void shi_commercialsending_usage(const char *command
) {
1270 "Usage: %s [BOX_ID]\n"
1271 "Retrieve permissions to send commercial messages from a box.\n"
1272 " BOX_ID query permissions for box with ID BOX_ID; default is box you\n"
1273 " are logged in\n"),
1278 /* Retrieve permissions to send commercial messages */
1279 static int shi_commercialsending(int argc
, const char **argv
) {
1281 struct isds_DbOwnerInfo
*box
= NULL
;
1282 struct isds_list
*permissions
= NULL
, *item
;
1284 char *box_id
= NULL
, *box_id_locale
= NULL
;
1289 while ((opt
= getopt(argc
, (char * const *)argv
, "h")) != -1) {
1292 shi_commercialsending_usage((argv
)?argv
[0]:NULL
);
1295 shi_commercialsending_usage((argv
)?argv
[0]:NULL
);
1299 if (optind
+ 1 < argc
) {
1300 fprintf(stderr
, _("Bad invocation\n"));
1301 shi_commercialsending_usage((argv
)?argv
[0]:NULL
);
1305 if ((retval
= get_current_box_id(argv
[optind
], &box_id
, &box_id_locale
))) {
1309 printf(_("Querying `%s' box commercial sending permissions...\n"),
1311 err
= isds_get_commercial_permissions(cisds
, box_id
, &permissions
);
1312 finish_isds_operation(cisds
, err
);
1315 oprintf(_("Permissions to send commercial messages from box `%s':\n"),
1317 for (item
= permissions
, ordinar
= 0; item
; item
=item
->next
) {
1318 if (!item
->data
) continue;
1320 oprintf(_("\n* Permission #%d:\n"), ordinar
);
1321 format_commercial_permission(item
->data
);
1324 oprintf(_("No permission exists.\n"));
1326 oprintf(_("Could not list permissions to send commercial messages "
1327 "from box `%s'.\n"), box_id_locale
);
1331 isds_list_free(&permissions
);
1333 free(box_id_locale
);
1334 isds_DbOwnerInfo_free(&box
);
1339 static int shi_user(int argc
, const char **argv
) {
1341 struct isds_DbUserInfo
*user
= NULL
;
1343 printf(_("Getting user details you are logged as...\n"));
1344 err
= isds_GetUserInfoFromLogin(cisds
, &user
);
1345 finish_isds_operation(cisds
, err
);
1348 format_DbUserInfo(user
);
1350 isds_DbUserInfo_free(&user
);
1355 static void shi_users_usage(const char *command
) {
1357 "Usage: %s BOX_ID\n"
1358 "Get list of users having access to box with BOX_ID.\n"),
1363 static int shi_users(int argc
, const char **argv
) {
1365 struct isds_list
*users
= NULL
, *item
;
1368 if (!argv
|| !argv
[1] || !*argv
[1]) {
1369 shi_users_usage((argv
)?argv
[0]:NULL
);
1373 printf(_("Getting users of box with ID `%s'...\n"), argv
[1]);
1374 err
= isds_GetDataBoxUsers(cisds
, argv
[1], &users
);
1375 finish_isds_operation(cisds
, err
);
1378 for (item
= users
, ordinar
= 0; item
; item
=item
->next
) {
1379 if (!item
->data
) continue;
1381 oprintf(_("\n* User #%d:\n"), ordinar
);
1382 format_DbUserInfo(item
->data
);
1385 oprintf(_("Empty list of users returned.\n"));
1387 isds_list_free(&users
);
1392 static int show_password_expiration(void) {
1394 struct timeval
*expiration
= NULL
;
1396 err
= isds_get_password_expiration(cisds
, &expiration
);
1397 finish_isds_operation(cisds
, err
);
1399 fprintf(stderr
, "Could not get password expiration time\n");
1403 print_header_timeval(_("Your password expires at"), expiration
);
1409 /* Change password in ISDS */
1410 static int do_passwd(void) {
1411 char *old_password
= NULL
;
1412 char *new_password
= NULL
;
1413 char *new_password2
= NULL
;
1414 struct isds_otp otp
;
1415 char *refnumber
= NULL
;
1416 isds_error err
= IE_ERROR
;
1419 if (replace_string(&otp_method
, cfg_getstr(configuration
, CONFIG_OTP_METHOD
)))
1421 /* Build OTP structure */
1422 if (NULL
!= otp_method
) {
1423 if (string2otp_method(otp_method
, &otp
.method
)) {
1424 fprintf(stderr
, _("Error: Invalid one-time password "
1425 "authentication method `%s'\n"), otp_method
);
1430 select_completion(COMPL_NONE
);
1433 "You are going to change your password. If you don't want to change your\n"
1434 "password, insert empty string or EOF.\n"
1436 "You will be asked for your current (old) password and then for new password.\n"
1437 "ISDS forces some criteria new password must fulfill. Current rules are:\n"
1438 "\tLength: minimal 8, maximal 32 characters\n"
1439 "\tMust contain at least: 1 upper case letter, 1 lower case letter, 1 digit\n"
1440 "\tAllowed alphabet: [a-z][A-Z][0-9][!#$%%&()*+,-.:=?@[]_{}|~]\n"
1441 "\tMust differ from last 255 passwords\n"
1442 "\tMust not contain user ID\n"
1443 "\tMust not contain sequence of three or more same characters\n"
1444 "\tMust not start with `qwert', `asdgf', or `12345'\n"
1445 "Finally, you must repeat your new password to avoid mistakes.\n"
1446 "After password change will be confirmed, you must log in again as password\n"
1447 "is transmitted to server on each request.\n"
1450 old_password
= ask_for_password(_("Old password: "));
1451 if (!old_password
|| *old_password
== '\0') {
1452 fprintf(stderr
, _("No password supplied\n"));
1456 /* Ask for OTP code if OTP authentication requested */
1458 if (NULL
!= otp_method
) {
1459 shi_ask_for_password(&otp_code
,
1460 (otp
.method
== OTP_TIME
) ?
1461 _("One-time code (empty to send new one): ") :
1462 _("One-time code: "),
1463 cfg_getstr(configuration
, CONFIG_OTP_CODE
), batch_mode
);
1464 otp
.otp_code
= otp_code
;
1468 if (NULL
== otp_method
|| NULL
!= otp_code
) {
1469 new_password
= ask_for_password(_("New password: "));
1470 if (!new_password
|| *new_password
== '\0') {
1471 fprintf(stderr
, _("No password supplied\n"));
1475 new_password2
= ask_for_password(_("Repeat new password: "));
1476 if (!new_password2
|| *new_password2
== '\0') {
1477 fprintf(stderr
, _("No password supplied\n"));
1481 if (strcmp(new_password
, new_password2
)) {
1482 fprintf(stderr
, _("New passwords differ\n"));
1487 if (NULL
!= otp_method
&& OTP_TIME
== otp
.method
&& NULL
== otp_code
)
1488 printf(_("Requesting one-time code from server for "
1489 "a password change...\n"));
1491 printf(_("Changing password...\n"));
1492 err
= isds_change_password(cisds
, old_password
, new_password
,
1493 (NULL
!= otp_method
) ? &otp
: NULL
, &refnumber
);
1494 if (NULL
!= otp_method
&& OTP_TIME
== otp
.method
&& NULL
== otp_code
)
1495 finish_isds_operation_with_code(cisds
, err
, IE_PARTIAL_SUCCESS
);
1497 finish_isds_operation(cisds
, err
);
1499 if (NULL
!= refnumber
) {
1500 char *refnumber_locale
= utf82locale(refnumber
);
1502 oprintf(_("Assigned reference number: %s\n"), refnumber_locale
);
1503 free(refnumber_locale
);
1505 if (IE_PARTIAL_SUCCESS
== err
) {
1506 oprintf(_("OTP code has been sent by ISDS successfully.\n"
1507 "Once you receive the code, retry changing password with "
1511 printf(_("Password change failed\n"));
1514 oprintf(_("Password HAS been successfully changed.\n"));
1520 oprintf(_("Password has NOT been changed!\n"));
1525 free(new_password2
);
1529 select_completion(COMPL_COMMAND
);
1532 "Remember, ISDS password has limited life time.\n"));
1537 static void shi_passwd_usage(const char *command
) {
1540 "Manipulate user password or change it if no option given.\n"
1543 " -S show password expiration time\n"
1548 static int shi_passwd(int argc
, const char **argv
) {
1552 while ((opt
= getopt(argc
, (char * const *)argv
, "S")) != -1) {
1555 return show_password_expiration();
1557 shi_passwd_usage(argv
[0]);
1561 if (optind
!= argc
|| argc
> 1) {
1562 fprintf(stderr
, _("Bad invocation\n"));
1563 shi_passwd_usage((argv
)?argv
[0]:NULL
);
1571 static void shi_login_usage(const char *command
) {
1573 "Usage: %s [OPTIONS] [USER_NAME]\n"
1574 "Attemp to log into ISDS server.\n"
1577 " -b URL ISDS server base URL\n"
1578 " -c IDENTIFIER user certificate\n"
1579 " -C FORMAT user certificate format\n"
1580 " -k IDENTIFIER user private key\n"
1581 " -K FORMAT user private key format\n"
1582 " -e IDENTIFIER cryptographic engine\n"
1583 " -o METHOD use one-time password authentication method\n"
1585 "Recognized certificate and key FORMATS are:\n"
1586 " PEM Base64 encoded serialization in local file\n"
1587 " DER binary serialization in local file\n"
1588 " ENG material is stored in cryptographic engine\n"
1589 "Identifiers of cryptographic engine, certificate, and private key are\n"
1590 "specific for underlying cryptographic library.\n"
1592 "Recognized one-time password methods are:\n"
1593 " HOTP HMAC-based OTP method\n"
1594 " TOTP time-based OTP method\n"
1596 "Values of omitted options are taken from configuration file.\n"
1601 static int shi_login(int argc
, const char **argv
) {
1605 discard_credentials();
1607 /* Load stored configuration */
1608 if (replace_string(&server
, cfg_getstr(configuration
, CONFIG_SERVER
)))
1610 if (replace_string(&otp_method
, cfg_getstr(configuration
, CONFIG_OTP_METHOD
)))
1612 if (replace_string(&pki_engine
,
1613 cfg_getstr(configuration
, CONFIG_KEY_ENGINE
)))
1615 if (replace_string(&pki_certificate_path
,
1616 cfg_getstr(configuration
, CONFIG_CERT_PATH
)))
1618 if (replace_string(&pki_certificate_format
,
1619 cfg_getstr(configuration
, CONFIG_CERT_FORMAT
)))
1621 if (replace_string(&pki_key_path
,
1622 cfg_getstr(configuration
, CONFIG_KEY_PATH
)))
1624 if (replace_string(&pki_key_format
,
1625 cfg_getstr(configuration
, CONFIG_KEY_FORMAT
)))
1628 /* Override configuration with positional arguments */
1630 while ((opt
= getopt(argc
, (char * const *)argv
, "b:c:C:k:K:e:o:")) != -1) {
1633 if (replace_string(&server
, optarg
)) return -1;
1636 if (replace_string(&pki_certificate_path
, optarg
)) return -1;
1639 if (replace_string(&pki_certificate_format
, optarg
)) return -1;
1642 if (replace_string(&pki_key_path
, optarg
)) return -1;
1645 if (replace_string(&pki_key_format
, optarg
)) return -1;
1648 if (replace_string(&pki_engine
, optarg
)) return -1;
1651 if (replace_string(&otp_method
, optarg
)) return -1;
1654 shi_login_usage(argv
[0]);
1658 if (optind
< argc
- 1) {
1659 fprintf(stderr
, _("Bad invocation\n"));
1660 shi_login_usage(argv
[0]);
1663 if (optind
== argc
- 1) {
1664 username
= strdup(argv
[optind
]);
1667 /* Proceed log-in */
1668 status
= do_login();
1669 if (status
< 0) return -1;
1671 /* If log-in passed, store configuration */
1672 cfg_setstr(configuration
, CONFIG_SERVER
, server
);
1673 cfg_setstr(configuration
, CONFIG_USERNAME
, username
);
1674 cfg_setstr(configuration
, CONFIG_PASSWORD
, password
);
1675 cfg_setstr(configuration
, CONFIG_KEY_PASSWORD
, key_password
);
1676 cfg_setstr(configuration
, CONFIG_OTP_METHOD
, otp_method
);
1677 cfg_setstr(configuration
, CONFIG_KEY_ENGINE
, pki_engine
);
1678 cfg_setstr(configuration
, CONFIG_CERT_PATH
, pki_certificate_path
);
1679 cfg_setstr(configuration
, CONFIG_CERT_FORMAT
, pki_certificate_format
);
1680 cfg_setstr(configuration
, CONFIG_KEY_PATH
, pki_key_path
);
1681 cfg_setstr(configuration
, CONFIG_KEY_FORMAT
, pki_key_format
);
1683 /* Get some details only if fully logged in */
1685 show_password_expiration();
1691 static void shi_debug_usage(const char *command
) {
1693 "Usage: %s -l LEVEL [-f FACILITY...] [{-e | -o FILE}]\n"
1694 "Debug FACILITIES on LEVEL.\n"
1696 "-l LEVEL set log level, valid interval <%d,%d>, default is %d\n"
1697 " %d is no logging, %d critical, %d errors,\n"
1698 " %d warnings, %d info, %d debug, %d all\n"
1699 "-f FACILITY debug only given facility, repeat this option to debug\n"
1700 " more facilities; valid values: none, http, soap, isds,\n"
1701 " file, sec, xml, all; default is none\n"
1702 "-e write debug log into stderr\n"
1703 "-o FILE append debug log to FILE\n"
1706 ILL_NONE
, ILL_ALL
, ILL_NONE
,
1707 ILL_NONE
, ILL_CRIT
, ILL_ERR
, ILL_WARNING
,
1708 ILL_INFO
, ILL_DEBUG
, ILL_ALL
);
1711 static int shi_debug(int argc
, const char **argv
) {
1713 int log_level
= ILL_NONE
;
1714 isds_log_facility log_facility
= ILF_NONE
;
1716 _Bool close_log
= 0;
1719 while ((opt
= getopt(argc
, (char * const *)argv
, "l:f:eo:")) != -1) {
1722 log_level
= normalize_log_level(atoi(optarg
));
1725 if (add_log_facility(&log_facility
, optarg
)) return -1;
1733 shi_debug_usage(argv
[0]);
1737 if (optind
== 1 || optind
!= argc
) {
1738 fprintf(stderr
, _("Bad invocation\n"));
1739 shi_debug_usage(argv
[0]);
1745 isds_set_log_callback(NULL
, NULL
);
1747 if (logger_fd
!= -1) {
1748 if (-1 == close(logger_fd
)) {
1749 fprintf(stderr
, _("Closing log file failed: %s\n"),
1754 cfg_setstr(configuration
, CONFIG_LOGFILE
, NULL
);
1756 if (do_log_to_file(file
))
1758 if (file
) cfg_setstr(configuration
, CONFIG_LOGFILE
, file
);
1760 /* Set log levels */
1761 isds_set_logging(log_facility
, log_level
);
1762 cfg_setint(configuration
, CONFIG_LOGLEVEL
, log_level
);
1763 save_log_facility(log_facility
);
1769 static void show_setting_str(const char *variable
, _Bool cenzore
) {
1770 if (!variable
) return;
1772 const char *value
= cfg_getstr(configuration
, variable
);
1776 oprintf(_("%s = <set>\n"), variable
);
1778 oprintf(_("%s = `%s'\n"), variable
, value
);
1780 oprintf(_("%s = <unset>\n"), variable
);
1785 static void show_setting_boolean(const char *variable
) {
1786 if (!variable
) return;
1788 _Bool value
= cfg_getbool(configuration
, variable
);
1791 oprintf(_("%s = <true>\n"), variable
);
1793 oprintf(_("%s = <false>\n"), variable
);
1798 static void show_setting_int(const char *variable
) {
1799 if (!variable
) return;
1801 long int value
= cfg_getint(configuration
, variable
);
1803 oprintf(_("%s = %ld\n"), variable
, value
);
1807 static void show_setting_strlist(const char *variable
) {
1808 if (!variable
) return;
1810 int length
= cfg_size(configuration
, variable
);
1813 oprintf(_("%s = <unset>\n"), variable
);
1815 oprintf(_("%s = {"), variable
);
1816 for (int i
= 0; i
< length
; i
++) {
1817 const char *value
= cfg_getnstr(configuration
, variable
, i
);
1819 oprintf(_("`%s', "), value
);
1821 oprintf(_("`%s'}\n"), value
);
1827 static int shi_settings(int argc
, const char **argv
) {
1829 int log_level = ILL_NONE;
1830 isds_log_facility log_facility = ILF_NONE;
1832 _Bool close_log = 0;*/
1836 while ((opt = getopt(argc, (char * const *)argv, "l:f:eo:")) != -1) {
1839 log_level = normalize_log_level(atoi(optarg));
1842 if (add_log_facility(&log_facility, optarg)) return -1;
1850 shi_debug_usage(argv[0]);
1854 if (optind == 1 || optind != argc) {
1855 printf(_("Bad invocation\n"));
1856 shi_debug_usage(argv[0]);
1861 oprintf(_("Current settings:\n"));
1863 show_setting_str(CONFIG_SERVER
, 0);
1864 show_setting_str(CONFIG_USERNAME
, 0);
1865 show_setting_str(CONFIG_PASSWORD
, 1),
1866 show_setting_str(CONFIG_CERT_FORMAT
, 0),
1867 show_setting_str(CONFIG_CERT_PATH
, 0),
1868 show_setting_str(CONFIG_KEY_ENGINE
, 0),
1869 show_setting_str(CONFIG_KEY_FORMAT
, 0),
1870 show_setting_str(CONFIG_KEY_PATH
, 0),
1871 show_setting_str(CONFIG_KEY_PASSWORD
, 1),
1872 show_setting_str(CONFIG_OTP_METHOD
, 0),
1873 show_setting_str(CONFIG_OTP_CODE
, 1),
1874 show_setting_boolean(CONFIG_VERIFYSERVER
);
1875 show_setting_str(CONFIG_CAFILE
, 0);
1876 show_setting_str(CONFIG_CADIRECTORY
, 0);
1877 show_setting_boolean(CONFIG_CLEAN_TEMPORARY_FILES
);
1878 show_setting_str(CONFIG_CRLFILE
, 0);
1879 show_setting_int(CONFIG_TIMEOUT
);
1880 show_setting_strlist(CONFIG_LOGFACILITIES
);
1881 show_setting_str(CONFIG_LOGFILE
, 0);
1882 show_setting_int(CONFIG_LOGLEVEL
);
1883 show_setting_boolean(CONFIG_CONFIRM_SEND
);
1884 show_setting_boolean(CONFIG_MARKMESSAGEREAD
);
1885 show_setting_boolean(CONFIG_NORMALIZEMIMETYPE
);
1886 show_setting_strlist(CONFIG_OPENCOMMAND
);
1887 show_setting_boolean(CONFIG_OVERWRITEFILES
);
1893 static void shi_find_box_usage(const char *command
) {
1895 "Usage: %s {OPTION... | BOX_ID}\n"
1896 "Get information about box with BOX_ID or boxes meeting other criteria.\n"
1897 "Each search option requires an argument:\n"
1898 " -t box type; accepted values:\n"
1899 " FO Private individual\n"
1900 " PFO Self-employed individual\n"
1901 " PFO_ADVOK Lawyer\n"
1902 " PFO_AUDITOR Statutory auditor\n"
1903 " PFO_DANPOR Tax advisor\n"
1904 " PFO_INSSPR Insolvency administrator\n"
1905 " PO Organisation\n"
1906 " PO_ZAK Organization based by law\n"
1907 " PO_REQ Organization based on request\n"
1908 " OVM Public authority\n"
1909 " OVM_EXEKUT Executor\n"
1910 " OVM_NOTAR Notary\n"
1911 " OVM_REQ Public authority based on request\n"
1912 " OVM_FO Private individual listed in the public authority\n"
1914 " OVM_PFO Self-employed individual listed in the public\n"
1915 " authority index\n"
1916 " OVM_PO Organisation listed in the public authority index\n"
1917 " -j identity number\n"
1919 "Person name options:\n"
1923 " -b last name at birth\n"
1924 " -s subject name\n"
1927 " -d birth date (locale or full ISO 8601 date)\n"
1929 " -y birth county\n"
1935 " -z number in street\n"
1936 " -Z number in municipality\n"
1943 " -p phone number\n"
1945 " -r registry code\n"
1946 " -a box status; accepted values:\n"
1947 " ACCESSIBLE Accessible\n"
1948 " TEMP_INACCESSIBLE Temporary inaccessible\n"
1949 " NOT_YET_ACCESSIBLE Not yet accessible\n"
1950 " PERM_INACCESSIBLE Permanently inaccessible\n"
1951 " REMOVED Deleted\n"
1952 " -o act as public authority; boolean values: 0 is false, 1 is true\n"
1953 " -k receive commercial messages; boolean values\n"
1955 "Not all option combinations are meaningful or allowed. For example box\n"
1956 "type is always required (except direct box ID query).\n"
1957 "ISDS can refuse to answer to much broad query. Not all boxes are searchable\n"
1964 /* Allow reassignment */
1965 #define FILL_OR_LEAVE(variable, locale) { \
1967 (variable) = locale2utf8(locale); \
1968 if (!(variable)) { \
1969 fprintf(stderr, _("Error: Not enough memory\n")); \
1975 #define CALLOC_OR_LEAVE(structure) { \
1976 if (!(structure)) { \
1977 (structure) = calloc(1, sizeof(*(structure))); \
1978 if (!(structure)) { \
1979 fprintf(stderr, _("Error: Not enough memory\n")); \
1986 #define FILL_BOOLEAN_OR_LEAVE(variable, locale) { \
1988 (variable) = malloc(sizeof(*(variable))); \
1989 if (!(variable)) { \
1990 fprintf(stderr, _("Error: Not enough memory\n")); \
1994 if (!strcmp((locale), "0")) *(variable) = 0; \
1995 else if (!strcmp((locale), "1")) *(variable) = 1; \
1997 fprintf(stderr, _("%s: %s: Unknown boolean value\n"), \
1998 argv[0], (locale)); \
2004 #define FILL_LONGINT_OR_LEAVE(variable, locale) { \
2005 if (!(locale) || !*(locale)) { \
2006 fprintf(stderr, _("%s: Empty integer value\n"), argv[0]); \
2012 (variable) = malloc(sizeof(*(variable))); \
2013 if (!(variable)) { \
2014 fprintf(stderr, _("Error: Not enough memory\n")); \
2018 (*variable) = strtol((locale), &endptr, 0); \
2020 fprintf(stderr, _("%s: %s: Invalid integer value\n"), \
2021 argv[0], (locale)); \
2027 #define FILL_ULONGINT_OR_LEAVE(variable, locale) { \
2029 if (!(locale) || !*(locale)) { \
2030 fprintf(stderr, _("%s: Empty integer value\n"), argv[0]); \
2036 (variable) = malloc(sizeof(*(variable))); \
2037 if (!(variable)) { \
2038 fprintf(stderr, _("Error: Not enough memory\n")); \
2042 value = strtol((locale), &endptr, 0); \
2044 fprintf(stderr, _("%s: %s: Invalid integer value\n"), \
2045 argv[0], (locale)); \
2050 fprintf(stderr, _("%s: %s: Negative integer value\n"), \
2051 argv[0], (locale)); \
2055 (*variable) = value; \
2058 static int shi_find_box(int argc
, const char **argv
) {
2061 struct isds_DbOwnerInfo
*criteria
= NULL
;
2062 struct isds_list
*item
;
2066 if (!argv
|| !argv
[1] || !*argv
[1]) {
2067 fprintf(stderr
, _("Error: No argument supplied\n"));
2068 shi_find_box_usage((argv
)?argv
[0]:NULL
);
2072 criteria
= calloc(1, sizeof(*criteria
));
2074 fprintf(stderr
, _("Error: Not enough memory\n"));
2081 while ((opt
= getopt(argc
, (char * const *)argv
, "t:j:s:"
2082 "f:m:l:b:s:" "d:w:y:c:" "W:S:z:Z:P:C:"
2083 "n:e:p:i:r:a:o:k:")) != -1) {
2086 criteria
->dbType
= malloc(sizeof(*criteria
->dbType
));
2087 if (!criteria
->dbType
) {
2088 fprintf(stderr
, _("Error: Not enough memory\n"));
2092 if (!string2isds_DbType(criteria
->dbType
, optarg
)) {
2093 fprintf(stderr
, _("%s: %s: Unknown box type\n"),
2101 FILL_OR_LEAVE(criteria
->ic
, optarg
);
2106 CALLOC_OR_LEAVE(criteria
->personName
);
2107 FILL_OR_LEAVE(criteria
->personName
->pnFirstName
, optarg
);
2110 CALLOC_OR_LEAVE(criteria
->personName
);
2111 FILL_OR_LEAVE(criteria
->personName
->pnMiddleName
, optarg
);
2114 CALLOC_OR_LEAVE(criteria
->personName
);
2115 FILL_OR_LEAVE(criteria
->personName
->pnLastName
, optarg
);
2118 CALLOC_OR_LEAVE(criteria
->personName
);
2119 FILL_OR_LEAVE(criteria
->personName
->pnLastNameAtBirth
, optarg
);
2122 FILL_OR_LEAVE(criteria
->firmName
, optarg
);
2127 CALLOC_OR_LEAVE(criteria
->birthInfo
);
2128 criteria
->birthInfo
->biDate
= datestring2tm(optarg
);
2129 if (!criteria
->birthInfo
->biDate
) {
2130 fprintf(stderr
, _("Error: Could not parse date: %s\n"),
2137 CALLOC_OR_LEAVE(criteria
->birthInfo
);
2138 FILL_OR_LEAVE(criteria
->birthInfo
->biCity
, optarg
);
2141 CALLOC_OR_LEAVE(criteria
->birthInfo
);
2142 FILL_OR_LEAVE(criteria
->birthInfo
->biCounty
, optarg
);
2145 CALLOC_OR_LEAVE(criteria
->birthInfo
);
2146 FILL_OR_LEAVE(criteria
->birthInfo
->biState
, optarg
);
2151 CALLOC_OR_LEAVE(criteria
->address
);
2152 FILL_OR_LEAVE(criteria
->address
->adCity
, optarg
);
2155 CALLOC_OR_LEAVE(criteria
->address
);
2156 FILL_OR_LEAVE(criteria
->address
->adStreet
, optarg
);
2159 CALLOC_OR_LEAVE(criteria
->address
);
2160 FILL_OR_LEAVE(criteria
->address
->adNumberInStreet
, optarg
);
2163 CALLOC_OR_LEAVE(criteria
->address
);
2164 FILL_OR_LEAVE(criteria
->address
->adNumberInMunicipality
,
2168 CALLOC_OR_LEAVE(criteria
->address
);
2169 FILL_OR_LEAVE(criteria
->address
->adZipCode
, optarg
);
2172 CALLOC_OR_LEAVE(criteria
->address
);
2173 FILL_OR_LEAVE(criteria
->address
->adState
, optarg
);
2178 FILL_OR_LEAVE(criteria
->nationality
, optarg
);
2181 FILL_OR_LEAVE(criteria
->email
, optarg
);
2184 FILL_OR_LEAVE(criteria
->telNumber
, optarg
);
2187 FILL_OR_LEAVE(criteria
->identifier
, optarg
);
2190 FILL_OR_LEAVE(criteria
->registryCode
, optarg
);
2193 criteria
->dbState
= malloc(sizeof(*criteria
->dbState
));
2194 if (!criteria
->dbState
) {
2195 fprintf(stderr
, _("Error: Not enough memory\n"));
2199 if (!strcmp(optarg
, "ACCESSIBLE"))
2200 *criteria
->dbState
= DBSTATE_ACCESSIBLE
;
2201 else if (!strcmp(optarg
, "TEMP_INACCESSIBLE"))
2202 *criteria
->dbState
= DBSTATE_TEMP_UNACCESSIBLE
;
2203 else if (!strcmp(optarg
, "NOT_YET_ACCESSIBLE"))
2204 *criteria
->dbState
= DBSTATE_NOT_YET_ACCESSIBLE
;
2205 else if (!strcmp(optarg
, "PERM_INACCESSIBLE"))
2206 *criteria
->dbState
= DBSTATE_PERM_UNACCESSIBLE
;
2207 else if (!strcmp(optarg
, "REMOVED"))
2208 *criteria
->dbState
= DBSTATE_REMOVED
;
2210 fprintf(stderr
, _("%s: %s: Unknown box status\n"),
2217 FILL_BOOLEAN_OR_LEAVE(criteria
->dbEffectiveOVM
, optarg
);
2220 FILL_BOOLEAN_OR_LEAVE(criteria
->dbOpenAddressing
, optarg
);
2224 shi_find_box_usage(argv
[0]);
2230 /* There must be an option and all of them must be recognized, if not only
2231 * BOX_ID supplied */
2232 if (argc
> 2 && optind
!= argc
) {
2233 fprintf(stderr
, _("Error: Superfluous argument\n"));
2234 shi_find_box_usage(argv
[0]);
2239 /* If only box ID is supplied use it */
2240 if (argc
== 2 && argv
[1] && *argv
[1]) {
2241 criteria
->dbID
= locale2utf8(argv
[1]);
2242 if (!criteria
->dbID
) {
2243 fprintf(stderr
, _("Error: Not enough memory\n"));
2249 printf(_("Searching boxes...\n"));
2250 err
= isds_FindDataBox(cisds
, criteria
, &boxes
);
2251 finish_isds_operation(cisds
, err
);
2254 for(item
= boxes
; item
; item
= item
->next
) {
2255 if (!item
->data
) continue;
2258 oprintf(_("\n* Result #%d:\n"), order
);
2259 format_DbOwnerInfo(item
->data
);
2263 isds_DbOwnerInfo_free(&criteria
);
2268 static void shi_search_box_usage(const char *command
) {
2270 "Usage: %s [OPTION...] QUERY...\n"
2271 "Get list of boxes matching a query string.\n"
2272 "Attribute options:\n"
2273 " -a search in addresses only\n"
2274 " -b search in box identifiers only\n"
2275 " -i search in organization identifiers only\n"
2277 "Box type options:\n"
2278 " -t TYPE restrict to a box type; accepted values:\n"
2279 " FO Private individual\n"
2280 " PFO Self-employed individual\n"
2281 " PFO_ADVOK Lawyer\n"
2282 " PFO_AUDITOR Statutory auditor\n"
2283 " PFO_DANPOR Tax advisor\n"
2284 " PFO_INSSPR Insolvency administrator\n"
2285 " PO Organization\n"
2286 " PO_ZAK Organization based by law\n"
2287 " PO_REQ Organization based on request\n"
2288 " OVM Public authority\n"
2289 " OVM_NOTAR Notary\n"
2290 " OVM_EXEKUT Executor\n"
2291 " OVM_REQ Public authority based on request\n"
2292 " OVM_FO Private individual listed in the public\n"
2293 " authority index\n"
2294 " OVM_PFO Self-employed individual listed in the public\n"
2295 " authority index\n"
2296 " OVM_PO Organisation listed in the public authority\n"
2299 "Pagination options:\n"
2300 " -p NUMBER request a page with this number (defaults to the first one)\n"
2301 " -s NUMBER request a page with this size\n"
2303 "The query arguments are concatenated by a space into one query string.\n"
2304 "If no attribute option is specified, the query string will be searched in\n"
2305 "all of the attributes (address, box ID, organization ID). The server splits\n"
2306 "the query string into words and normalize them according to complex rules\n"
2307 "(see ISDS specification for more details) before the search. Each word\n"
2308 "matches independently, but all of them must exist in a box attributes to\n"
2315 static int shi_search_box(int argc
, const char **argv
) {
2318 isds_fulltext_target target
= FULLTEXT_ALL
;
2319 isds_DbType type
= DBTYPE_SYSTEM
;
2320 unsigned long int *page_size
= NULL
;
2321 unsigned long int *page_number
= NULL
;
2322 char *query_locale
= NULL
;
2324 unsigned long int *total_matching_boxes
= NULL
;
2325 unsigned long int *current_page_beginning
= NULL
;
2326 unsigned long int *current_page_size
= NULL
;
2327 _Bool
*last_page
= NULL
;
2328 struct isds_list
*boxes
= NULL
;
2329 struct isds_list
*item
;
2330 unsigned long int order
= 0;
2333 if (!argv
|| !argv
[1] || !*argv
[1]) {
2334 fprintf(stderr
, _("Error: No argument supplied\n"));
2335 shi_search_box_usage((argv
)?argv
[0]:NULL
);
2341 while ((opt
= getopt(argc
, (char * const *)argv
, "abi" "t:" "p:s:"))
2345 if (target
!= FULLTEXT_ALL
) {
2346 fprintf(stderr
, _("%s: -%c: Another attribute option "
2347 "has already been specified\n"),
2352 target
= FULLTEXT_ADDRESS
;
2355 if (target
!= FULLTEXT_ALL
) {
2356 fprintf(stderr
, _("%s: -%c: Another attribute option "
2357 "has already been specified\n"),
2362 target
= FULLTEXT_BOX_ID
;
2365 if (target
!= FULLTEXT_ALL
) {
2366 fprintf(stderr
, _("%s: -%c: Another attribute option "
2367 "has already been specified\n"),
2372 target
= FULLTEXT_IC
;
2375 if (!string2isds_DbType(&type
, optarg
)) {
2376 fprintf(stderr
, _("%s: %s: Unknown box type\n"),
2384 FILL_ULONGINT_OR_LEAVE(page_number
, optarg
);
2388 FILL_ULONGINT_OR_LEAVE(page_size
, optarg
);
2392 shi_search_box_usage(argv
[0]);
2398 /* All options are optional, there must one non-options argument, the
2400 if (optind
>= argc
) {
2401 fprintf(stderr
, _("Error: Missing a query argument\n"));
2406 /* If only box ID is supplied use it */
2407 if (NULL
== argv
[optind
] || '\0' == *argv
[optind
]) {
2408 fprintf(stderr
, _("Error: The query string must be non-empty\n"));
2413 /* Concatenate arguments into one query string */
2414 for (int i
= optind
; NULL
!= argv
[i
]; i
++) {
2415 char *new_query_locale
;
2416 new_query_locale
= astrcat3(query_locale
, " ", argv
[i
]);
2417 if (NULL
== new_query_locale
) {
2418 fprintf(stderr
, _("Error: Not enough memory\n"));
2423 query_locale
= new_query_locale
;
2425 FILL_OR_LEAVE(query
, query_locale
);
2427 printf(_("Searching boxes...\n"));
2428 err
= isds_find_box_by_fulltext(cisds
,
2429 query
, &target
, &type
, page_size
, page_number
, 0,
2430 &total_matching_boxes
, ¤t_page_beginning
, ¤t_page_size
,
2431 &last_page
, &boxes
);
2432 finish_isds_operation(cisds
, err
);
2435 if (NULL
!= current_page_beginning
) {
2436 order
= *current_page_beginning
;
2438 print_header_ulongint(_("Total matching boxes"), total_matching_boxes
);
2439 print_header_ulongint(_("This page size"), current_page_size
);
2441 for(item
= boxes
; item
; item
= item
->next
) {
2442 if (!item
->data
) continue;
2445 oprintf(_("\n* Result #%lu:\n"), order
);
2446 format_isds_fulltext_result(item
->data
);
2449 if (NULL
!= last_page
) {
2453 oprintf(_("This is the last page.\n"));
2455 oprintf(_("Next pages exist.\n"));
2463 free(total_matching_boxes
);
2464 free(current_page_beginning
);
2465 free(current_page_size
);
2467 isds_list_free(&boxes
);
2472 static void shi_stat_box_usage(const char *command
) {
2474 "Usage: %s BOX_ID...\n"
2475 "Get status of box with BOX_ID. More boxes can be specified.\n"),
2480 /* Get boxes status */
2481 static int shi_stat_box(int argc
, const char **argv
) {
2486 if (!argv
|| !*argv
|| argc
< 2 || !argv
[1]) {
2487 fprintf(stderr
, _("Missing box ID\n"));
2488 shi_stat_box_usage((argv
[0])?argv
[0]:NULL
);
2492 for (int i
= 1; i
< argc
; i
++) {
2493 if (!argv
[i
] || !*argv
[i
]) continue;
2496 id
= locale2utf8(argv
[i
]);
2498 fprintf(stderr
, _("%s: Could not covert box ID to UTF-8\n"),
2503 printf(_("Getting status of box `%s'...\n"), argv
[i
]);
2504 err
= isds_CheckDataBox(cisds
, id
, &status
);
2505 finish_isds_operation(cisds
, err
);
2508 oprintf(_("Status of box `%s': %s\n"),
2509 argv
[i
], DbState2string(&status
));
2516 static void shi_boxlist_usage(const char *command
) {
2518 "Usage: %s LIST_TYPE FILE\n"
2519 "Save latest snapshot of list of boxes of type LIST_TYPE into FILE.\n"
2521 "Currently recognized LIST_TYPES are:\n"
2523 " UPG Effectively OVM boxes\n"
2524 " OVM OVM gross type boxes\n"
2525 " OPN Boxes allowing receiving commercial messages\n"
2527 "Not all types are available to all users. E.g. only `UPG' is available\n"
2528 "to regular users.\n"
2530 "The format of the list is comma separate list that is packed into\n"
2531 "ZIP archive. Name of the list file denotes time of snapshoting\n"
2532 "the list. The snapshot is created by ISDS once a day.\n"),
2537 /* Download list of boxes */
2538 static int shi_boxlist(int argc
, const char **argv
) {
2540 const char *type_locale
;
2542 void *buffer
= NULL
;
2543 size_t buffer_length
;
2546 if (!argv
|| !*argv
|| argc
< 3 || !argv
[1] || !argv
[2]) {
2547 fprintf(stderr
, _("Bad number of arguments\n"));
2548 shi_boxlist_usage((argv
)?argv
[0]:NULL
);
2551 type_locale
= argv
[1];
2553 type
= locale2utf8(type_locale
);
2555 fprintf(stderr
, _("%s: Could not covert list type to UTF-8\n"),
2560 printf(_("Getting `%s' list of boxes...\n"), type_locale
);
2561 err
= isds_get_box_list_archive(cisds
, type
, &buffer
, &buffer_length
);
2562 finish_isds_operation(cisds
, err
);
2568 retval
= save_data_to_file(argv
[2], -1, buffer
, buffer_length
,
2570 cfg_getbool(configuration
, CONFIG_OVERWRITEFILES
));
2577 static void shi_delivery_usage(const char *command
) {
2579 "Usage: %s [MESSAGE_ID]\n"
2580 "Get delivery data about a message.\n"
2581 "If MESSAGE_ID is defined, query for that message.\n"
2582 "Otherwise use current message.\n"),
2587 static int shi_delivery(int argc
, const char **argv
) {
2589 const char *id
= NULL
;
2590 struct isds_message
*delivery_info
= NULL
;
2592 if (!argv
|| argc
> 2) {
2593 shi_delivery_usage((argv
)?argv
[0]:NULL
);
2596 if (argc
== 2 && argv
[1] && *argv
[1]) {
2600 fprintf(stderr
, _("No message loaded\n"));
2603 if (!message
->envelope
|| !message
->envelope
->dmID
) {
2604 fprintf(stderr
, _("Current message is missing ID\n"));
2607 id
= message
->envelope
->dmID
;
2610 printf(_("Getting delivery info...\n"));
2611 err
= isds_get_signed_delivery_info(cisds
, id
, &delivery_info
);
2612 finish_isds_operation(cisds
, err
);
2616 isds_message_free(&message
);
2617 message
= delivery_info
;
2619 format_message(message
);
2621 if (message
->envelope
&& message
->envelope
->dmID
)
2622 set_prompt(_("%s %s"), argv
[0], message
->envelope
->dmID
);
2624 set_prompt("%s", argv
[0]);
2625 select_completion(COMPL_MSG
);
2630 static int shi_dump_message(int argc
, const char **argv
) {
2632 fprintf(stderr
, _("No message loaded\n"));
2636 print_message(message
);
2641 static void shi_hash_usage(const char *command
) {
2643 "Usage: %s [MESSAGE_ID]\n"
2644 "Retrieve message hash stored in ISDS.\n"
2645 "If MESSAGE_ID is defined, query for that message.\n"
2646 "Otherwise use current message.\n"),
2651 static int shi_hash(int argc
, const char **argv
) {
2653 const char *id
= NULL
;
2654 struct isds_hash
*hash
= NULL
;
2655 char *hash_string
= NULL
;
2657 if (!argv
|| argc
> 2) {
2658 shi_hash_usage((argv
)?argv
[0]:NULL
);
2661 if (argc
== 2 && argv
[1] && *argv
[1])
2665 fprintf(stderr
, _("No message loaded\n"));
2668 if (!message
->envelope
|| !message
->envelope
->dmID
) {
2669 fprintf(stderr
, _("Current message is missing ID\n"));
2672 id
= message
->envelope
->dmID
;
2675 printf(_("Getting message hash...\n"));
2676 err
= isds_download_message_hash(cisds
, id
, &hash
);
2677 finish_isds_operation(cisds
, err
);
2680 hash_string
= hash2string(hash
);
2681 oprintf(_("ISDS states message with `%s' ID has following hash:\n%s\n"),
2685 isds_hash_free(&hash
);
2690 static int shi_verify(int argc
, const char **argv
) {
2693 struct isds_hash
*retrieved_hash
= NULL
, *stored_hash
= NULL
;
2694 char *hash_string
= NULL
;
2698 fprintf(stderr
, _("No message loaded\n"));
2702 if (!message
->envelope
) {
2703 fprintf(stderr
, _("Current message is missing envelope\n"));
2706 stored_hash
= message
->envelope
->hash
;
2707 message
->envelope
->hash
= NULL
;
2709 if (message
->envelope
->dmID
) {
2710 /* Verify remote hash */
2711 oprintf(_("Remote hash check:\n"));
2713 printf(_("Getting message hash...\n"));
2714 err
= isds_download_message_hash(cisds
, message
->envelope
->dmID
,
2716 finish_isds_operation(cisds
, err
);
2718 if (retrieved_hash
) {
2719 hash_string
= hash2string(retrieved_hash
);
2720 ohprint(_("Retrieved:"), width
);
2721 oprintf("%s\n", hash_string
);
2725 if (retrieved_hash
&& message
->raw
) {
2726 err
= isds_compute_message_hash(cisds
, message
,
2727 retrieved_hash
->algorithm
);
2728 finish_isds_operation(cisds
, err
);
2731 hash_string
= hash2string(message
->envelope
->hash
);
2732 ohprint(_("Computed:"), width
);
2733 oprintf("%s\n", hash_string
);
2738 err
= isds_hash_cmp(retrieved_hash
, message
->envelope
->hash
);
2741 oprintf(_("Hashes match.\n")); break;
2743 oprintf(_("Hashes do not match.\n"));
2747 oprintf(_("Hashes could not be compared.\n"));
2752 free(retrieved_hash
);
2757 /* Verify stored hash */
2758 oprintf(_("Stored hash check:\n"));
2760 hash_string
= hash2string(stored_hash
);
2761 ohprint(_("Stored:"), width
);
2762 oprintf("%s\n", hash_string
);
2766 err
= isds_compute_message_hash(cisds
, message
,
2767 stored_hash
->algorithm
);
2768 finish_isds_operation(cisds
, err
);
2771 hash_string
= hash2string(message
->envelope
->hash
);
2772 ohprint(_("Computed:"), width
);
2773 oprintf("%s\n", hash_string
);
2778 err
= isds_hash_cmp(stored_hash
, message
->envelope
->hash
);
2781 oprintf(_("Hashes match.\n")); break;
2783 oprintf(_("Hashes do not match.\n"));
2787 oprintf(_("Hashes could not be compared.\n"));
2792 isds_hash_free(&message
->envelope
->hash
);
2795 message
->envelope
->hash
= stored_hash
;
2800 static int shi_authenticate(int argc
, const char **argv
) {
2805 fprintf(stderr
, _("No message loaded\n"));
2808 if (!message
->raw
|| message
->raw_length
== 0) {
2809 fprintf(stderr
, _("Current message is missing raw representation\n"));
2813 printf(_("Submitting message to authenticity check...\n"));
2814 err
= isds_authenticate_message(cisds
, message
->raw
, message
->raw_length
);
2815 finish_isds_operation(cisds
, (err
== IE_NOTUNIQ
) ? IE_SUCCESS
: err
);
2819 oprintf(_("Message originates in ISDS.\n")); break;
2821 oprintf(_("Message is unknown to ISDS or has been tampered.\n"));
2833 static void shi_accept_message_usage(const char *command
) {
2835 "Usage: %s [MESSAGE_ID...]\n"
2836 "Accept commercial message moving its state to received.\n"
2837 "If MESSAGE_ID is defined, accept that message. More messages can be specified.\n"
2838 "Otherwise accept all commercial incoming messages.\n"),
2843 static int shi_accept_message(int argc
, const char **argv
) {
2847 /* Process messages named in argv */
2848 for (int i
= 1; i
< argc
; i
++) {
2849 if (!argv
[i
] || !*argv
[i
]) continue;
2851 id
= locale2utf8(argv
[i
]);
2854 _("Error: Could not convert message ID to UTF-8: %s\n"),
2859 printf(_("Accepting message `%s'...\n"), argv
[i
]);
2860 err
= isds_mark_message_received(cisds
, id
);
2861 finish_isds_operation(cisds
, err
);
2867 oprintf(_("Message `%s' accepted\n"), argv
[i
]);
2872 /* TODO: list commercial not received messages and accept all of them
2875 _("Error: No message ID supplied. Accepting all commercial "
2876 "messages not implemented yet.\n"));
2884 static void shi_delete_message_usage(const char *command
) {
2886 "Usage: %s {-i|-o} MESSAGE_ID...\n"
2887 "Remove message from long term storage.\n"
2889 " -i Messages are incoming\n"
2890 " -o Messages are outoging\n"),
2895 static int shi_delete_message(int argc
, const char **argv
) {
2899 _Bool direction_specified
= 0;
2903 while ((opt
= getopt(argc
, (char * const *)argv
, "io")) != -1) {
2907 direction_specified
= 1;
2911 direction_specified
= 1;
2914 shi_delete_message_usage((argv
)?argv
[0]:NULL
);
2918 if (optind
>= argc
|| !argv
|| !argv
[optind
] || !*argv
[optind
]) {
2919 fprintf(stderr
, _("Bad invocation\n"));
2920 shi_delete_message_usage((argv
)?argv
[0]:NULL
);
2923 if (!direction_specified
) {
2924 fprintf(stderr
, _("Message direction has not been specified\n"));
2925 shi_delete_message_usage((argv
)?argv
[0]:NULL
);
2929 /* Process messages named in argv */
2930 for (int i
= optind
; i
< argc
; i
++) {
2931 if (!argv
[i
] || !*argv
[i
]) continue;
2933 id
= locale2utf8(argv
[i
]);
2936 _("Error: Could not convert message ID to UTF-8: %s\n"),
2941 printf(_("Deleting message `%s'...\n"), argv
[i
]);
2942 err
= isds_delete_message_from_storage(cisds
, id
, incoming
);
2943 finish_isds_operation(cisds
, err
);
2949 oprintf(_("Message `%s' deleted\n"), argv
[i
]);
2957 /* Convert message ID form locale to UTF-8 or in other direction. If both
2958 * strings are provided, UTF-8 will take precedence. The missing string is
2959 * automatically allocated (but not freed before). If UTF-8 version has been
2960 * provided, @stastic_utf8 will become 1, otherwise 0. You can pass the
2961 * strings and the flags to free_message_id() to free memory properly.*/
2962 static int convert_message_id(char **id_utf8
, char **id_locale
, _Bool
*static_utf8
) {
2963 if (!id_utf8
|| !id_locale
|| !static_utf8
) return -1;
2964 if (!*id_utf8
&& !*id_locale
) return -1;
2968 *id_locale
= utf82locale(*id_utf8
);
2971 *id_utf8
= locale2utf8(*id_locale
);
2974 _("Error: Could not convert message ID to UTF-8: %s\n"),
2984 /* Free message ID strings as were allocated by convert_message_id() */
2985 static int free_message_id(char **id_utf8
, char **id_locale
, _Bool static_utf8
) {
2986 if (!id_utf8
|| !id_locale
) return -1;
2987 if (static_utf8
) zfree(*id_locale
);
2988 else zfree(*id_utf8
);
2993 /* Return static UTF-8 encoded ID of current message. In case of error NULL. */
2994 static const char *get_current_message_id(void) {
2996 fprintf(stderr
, _("No message loaded\n"));
2999 if (!message
->envelope
) {
3000 fprintf(stderr
, _("Loaded message is missing envelope\n"));
3003 if (!message
->envelope
->dmID
|| !*message
->envelope
->dmID
) {
3004 fprintf(stderr
, _("Loaded message is missing ID\n"));
3007 return message
->envelope
->dmID
;
3011 static void shi_message_sender_usage(const char *command
) {
3013 "Usage: %s [MESSAGE_ID...]\n"
3014 "Get details about sender of a message.\n"
3015 "If MESSAGE_ID is defined, get sender of that message. More messages can be specified.\n"
3016 "Otherwise will get sender of current message, if any is loaded.\n"),
3021 /* Get details about sender of message with given ID. At least one form must
3023 * @message_id is UTF-8 string
3024 * @message_id_locale is string in locale encoding
3025 * @return 0 on success, -1 on failure */
3026 static int do_message_sender(const char *message_id
, const char *message_id_locale
) {
3027 isds_sender_type
*type
= NULL
;
3028 char *raw_type
= NULL
;
3033 if (convert_message_id((char **)&message_id
, (char **)&message_id_locale
,
3037 printf(_("Getting sender of message `%s'...\n"), message_id_locale
);
3038 err
= isds_get_message_sender(cisds
, message_id
, &type
, &raw_type
, &name
);
3039 finish_isds_operation(cisds
, err
);
3041 free_message_id((char **)&message_id
, (char **)&message_id_locale
,
3046 format_sender_info(message_id
, type
, raw_type
, name
);
3048 free_message_id((char **)&message_id
, (char **)&message_id_locale
,
3057 static int shi_message_sender(int argc
, const char **argv
) {
3059 return do_message_sender(get_current_message_id(), NULL
);
3062 for (int i
= 1; i
< argc
; i
++) {
3063 if (!argv
[i
] || !*argv
[i
]) continue;
3064 if (do_message_sender(NULL
, argv
[i
]))
3072 /* Mark message as read. At least one form of ID must be provided.
3073 * @id is UTF-8 encoded message ID
3074 * @id_locale is locale encoded message ID. @id takes preference. */
3075 static int do_read_message(const char *id
, const char *id_locale
) {
3079 if ((!id
|| !*id
) && (!id_locale
|| !*id_locale
)) return -1;
3081 if (convert_message_id((char **)&id
, (char **)&id_locale
, &static_id
)) return -1;
3083 printf(_("Marking message `%s' as read...\n"), id_locale
);
3084 err
= isds_mark_message_read(cisds
, id
);
3085 finish_isds_operation(cisds
, err
);
3088 oprintf(_("Message `%s' marked as read\n"), id_locale
);
3090 free_message_id((char **)&id
, (char **)&id_locale
, static_id
);
3092 return (err
) ? -1 : 0;
3096 static void shi_read_message_usage(const char *command
) {
3098 "Usage: %s [MESSAGE_ID...]\n"
3099 "Mark message as read moving its state to read.\n"
3101 "When new incoming message is download, its state is not changed on server.\n"
3102 "Client must mark such message as read explicitly. You can use this command\n"
3103 "to do so, if not done automatically at download time by your client.\n"
3105 "If MESSAGE_ID is defined, mark that message. More messages can be specified.\n"
3106 "Otherwise marks currently loaded message.\n"),
3111 static int shi_read_message(int argc
, const char **argv
) {
3113 return do_read_message(get_current_message_id(), NULL
);
3116 for (int i
= 1; i
< argc
; i
++) {
3117 if (!argv
[i
] || !*argv
[i
]) continue;
3118 if (do_read_message(NULL
, argv
[i
]))
3126 static void shi_cat_message_usage(const char *command
) {
3129 "Print unformated raw representation of current message.\n"
3131 "This is the same content you would get into file by `save' command.\n"
3133 "Be ware the binary stream can screw your terminal. No new line character\n"
3134 "will be appended to the end of the output.\n"),
3139 static int shi_cat_message(int argc
, const char **argv
) {
3141 fprintf(stderr
, _("No message loaded\n"));
3145 if (!message
->raw
|| !message
->raw_length
) {
3146 fprintf(stderr
, _("Current message is missing raw representation\n"));
3150 if (owrite(message
->raw
, message
->raw_length
) != message
->raw_length
) {
3151 fprintf(stderr
, _("Error while printing message content\n"));
3159 static int shi_show_message(int argc
, const char **argv
) {
3161 fprintf(stderr
, _("No message loaded\n"));
3165 format_message(message
);
3170 static void shi_incoming_message_usage(const char *command
) {
3172 "Usage: %s [-r] MESSAGE_ID\n"
3173 "Get incoming message with MESSAGE_ID.\n"
3175 " -r Mark mesage as read\n"),
3180 static int shi_incoming_message(int argc
, const char **argv
) {
3184 _Bool mark_as_read
= 0;
3187 while ((opt
= getopt(argc
, (char * const *)argv
, "r")) != -1) {
3193 shi_incoming_message_usage((argv
)?argv
[0]:NULL
);
3197 if (optind
+ 1 != argc
|| !argv
|| !argv
[optind
] || !*argv
[optind
]) {
3198 fprintf(stderr
, _("Bad invocation\n"));
3199 shi_incoming_message_usage((argv
)?argv
[0]:NULL
);
3204 printf(_("Getting incoming message...\n"));
3205 err
= isds_get_signed_received_message(cisds
, id
, &message
);
3206 finish_isds_operation(cisds
, err
);
3209 select_completion(COMPL_COMMAND
);
3213 format_message(message
);
3215 if (message
->envelope
&& message
->envelope
->dmID
)
3216 set_prompt(_("%s %s"), argv
[0], message
->envelope
->dmID
);
3218 set_prompt("%s", argv
[0]);
3219 select_completion(COMPL_MSG
);
3221 if (mark_as_read
|| cfg_getbool(configuration
, CONFIG_MARKMESSAGEREAD
)) {
3222 if (message
->envelope
&& message
->envelope
->dmMessageStatus
&&
3223 ! (*message
->envelope
->dmMessageStatus
& MESSAGESTATE_READ
))
3224 return do_read_message(id
, NULL
);
3230 static void shi_outgoing_message_usage(const char *command
) {
3232 "Usage: %s MESSAGE_ID\n"
3233 "Get outgoing message with MESSAGE_ID.\n"),
3238 static int shi_outgoing_message(int argc
, const char **argv
) {
3242 if (!argv
|| !argv
[1] || !*argv
[1]) {
3243 shi_outgoing_message_usage(argv
[0]);
3248 printf(_("Getting outgoing message...\n"));
3249 err
= isds_get_signed_sent_message(cisds
, id
, &message
);
3250 finish_isds_operation(cisds
, err
);
3253 select_completion(COMPL_COMMAND
);
3257 format_message(message
);
3258 if (message
->envelope
&& message
->envelope
->dmID
)
3259 set_prompt(_("%s %s"), argv
[0], message
->envelope
->dmID
);
3261 set_prompt("%s", argv
[0]);
3262 select_completion(COMPL_MSG
);
3267 /* Detect type (message or delivery data) and load it. And change completion
3268 * and show the data.
3269 * @buffer is memory with message or delivery data
3270 * @length is size of @buffer in bytes
3271 * @strategy defines how to fill global message variable
3272 * @return 0 for success, otherwise non-zero. */
3273 static int do_load_anything(const void *buffer
, size_t length
,
3274 isds_buffer_strategy strategy
) {
3275 isds_raw_type raw_type
;
3277 char *type_name
= NULL
;
3279 if (NULL
== buffer
|| 0 == length
) {
3283 printf(_("Detecting format...\n"));
3284 err
= isds_guess_raw_type(cisds
, &raw_type
, buffer
, length
);
3285 finish_isds_operation(cisds
, err
);
3288 if (err
== IE_NOTSUP
)
3289 fprintf(stderr
, _("Unknown format.\n"));
3291 fprintf(stderr
, _("Error while detecting format.\n"));
3294 case RAWTYPE_INCOMING_MESSAGE
:
3295 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
:
3296 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
:
3297 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
:
3298 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
3299 err
= isds_load_message(cisds
, raw_type
,
3300 buffer
, length
, &message
, strategy
);
3301 finish_isds_operation(cisds
, err
);
3302 type_name
= N_("message");
3305 case RAWTYPE_DELIVERYINFO
:
3306 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO
:
3307 case RAWTYPE_CMS_SIGNED_DELIVERYINFO
:
3308 err
= isds_load_delivery_info(cisds
, raw_type
,
3309 buffer
, length
, &message
, strategy
);
3310 finish_isds_operation(cisds
, err
);
3311 type_name
= N_("delivery");
3316 _("Unsupported format.\n"));
3323 select_completion(COMPL_COMMAND
);
3327 format_message(message
);
3329 if (message
->envelope
&& message
->envelope
->dmID
)
3330 set_prompt(_("%s %s"), _(type_name
), message
->envelope
->dmID
);
3332 set_prompt("%s", _(type_name
));
3333 select_completion(COMPL_MSG
);
3338 static void shi_load_anything_usage(const char *command
) {
3341 "Load message or message delivery details from local FILE.\n"),
3346 static int shi_load_anything(int argc
, const char **argv
) {
3348 void *buffer
= NULL
;
3352 if (!argv
|| !argv
[1] || !*argv
[1]) {
3353 shi_load_anything_usage((argv
)?argv
[0]:NULL
);
3357 printf(_("Loading file `%s'...\n"), argv
[1]);
3359 if (mmap_file(argv
[1], &fd
, &buffer
, &length
)) return -1;
3361 error
= do_load_anything(buffer
, length
, BUFFER_COPY
);
3363 munmap_file(fd
, buffer
, length
);
3369 static void shi_save_message_usage(const char *command
) {
3372 "Save message into local FILE.\n"),
3377 static const char *raw_type2mime(isds_raw_type raw_type
) {
3379 case RAWTYPE_INCOMING_MESSAGE
:
3380 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
:
3381 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
:
3382 case RAWTYPE_DELIVERYINFO
:
3383 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO
:
3386 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
:
3387 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
3388 case RAWTYPE_CMS_SIGNED_DELIVERYINFO
:
3389 return "application/pkcs7-mime";
3397 static int shi_save_message(int argc
, const char **argv
) {
3398 _Bool overwrite
= cfg_getbool(configuration
, CONFIG_OVERWRITEFILES
);
3400 if (!argv
|| !argv
[1] || !*argv
[1]) {
3401 shi_save_message_usage(argv
[0]);
3406 fprintf(stderr
, _("No message loaded\n"));
3409 if (!message
->raw
|| message
->raw_length
== 0) {
3410 fprintf(stderr
, _("Loaded message is missing raw representation\n"));
3414 return save_data_to_file(argv
[1], -1, message
->raw
, message
->raw_length
,
3415 raw_type2mime(message
->raw_type
), overwrite
);
3419 /* Return document of current message identified by ordinal number expressed
3420 * as string. In case of error return NULL. */
3421 static const struct isds_document
*locate_document_by_ordinal_string(
3422 const char *number
) {
3423 const struct isds_list
*item
;
3424 const struct isds_document
*document
= NULL
;
3427 if (!number
) return NULL
;
3429 ordinar
= atoi(number
);
3431 fprintf(stderr
, _("%s: Document number must be positive number\n"),
3437 fprintf(stderr
, _("No message loaded\n"));
3442 for (item
= message
->documents
, i
= 0; item
; item
= item
->next
) {
3443 if (!item
->data
) continue;
3444 if (++i
== ordinar
) {
3445 document
= (const struct isds_document
*) item
->data
;
3450 fprintf(stderr
, _("Message does not contain document #%d\n"), ordinar
);
3458 static void shi_cat_document_usage(const char *command
) {
3460 "Usage: %s NUMBER\n"
3461 "Print document selected with ordinal NUMBER.\n"),
3465 static int shi_cat_document(int argc
, const char **argv
) {
3466 const struct isds_document
*document
;
3468 if (!argv
|| !argv
[1] || !*argv
[1] || argc
> 3) {
3469 shi_cat_document_usage(argv
[0]);
3473 document
= locate_document_by_ordinal_string(argv
[1]);
3474 if (!document
) return -1;
3476 if (document
->is_xml
) {
3477 xmlBufferPtr buffer
= NULL
;
3480 if (serialize_xml_to_buffer(&buffer
, document
->xml_node_list
))
3483 written
= owrite(buffer
->content
, buffer
->use
);
3484 xmlBufferFree(buffer
);
3485 if (written
!= buffer
->use
) {
3486 fprintf(stderr
, _("Error while printing document content\n"));
3490 if (!document
->data
|| !document
->data_length
) {
3491 fprintf(stderr
, _("Document is missing raw representation\n"));
3495 if (owrite(document
->data
, document
->data_length
) != document
->data_length
) {
3496 fprintf(stderr
, _("Error while printing document content\n"));
3505 static void shi_save_document_usage(const char *command
) {
3507 "Usage: %s NUMBER [DESTINATION]\n"
3508 "Save document having ordinal NUMBER within current message into local file.\n"
3509 "If DESTINATION is file (or does not exist yet), document will be saved into\n"
3511 "If DESTINATION is existing directory, file name equaled to document name\n"
3512 "will be saved into DESTINATION.\n"
3513 "If DESTINATION is missing, document name will be used as file name and\n"
3514 "saved into working directory.\n"
3515 "Be aware that document name does not embed malicious characters (slashes).\n"
3517 "If the document is a binary stream, image of the document will be copied\n"
3518 "into a file. If the document is a XML document, the XML tree will be serialized\n"
3519 "into a file. If XML document stands for one element or one text node, the node\n"
3520 "(and its children recursively) will be serialized. If XML document compounds\n"
3521 "more nodes or a comment or a processing instruction, parent node from ISDS name\n"
3522 "space will be used to ensure output serialized XML well-formness.\n"
3528 static int shi_save_document(int argc
, const char **argv
) {
3529 const struct isds_document
*document
;
3530 const char *dirname
= NULL
;
3531 char *filename
= NULL
, *path
= NULL
;
3533 _Bool overwrite
= cfg_getbool(configuration
, CONFIG_OVERWRITEFILES
);
3535 if (!argv
|| !argv
[1] || !*argv
[1] || argc
> 3) {
3536 shi_save_document_usage(argv
[0]);
3540 document
= locate_document_by_ordinal_string(argv
[1]);
3541 if (!document
) return -1;
3543 /* Select directory and file name */
3544 if (argv
[2] && *argv
[2]) {
3545 if (!is_directory(argv
[2])) {
3548 filename
= strdup(argv
[2]);
3550 fprintf(stderr
, _("Not enough memory\n"));
3555 if (!filename
&& document
->dmFileDescr
&& &document
->dmFileDescr
) {
3556 filename
= utf82locale(document
->dmFileDescr
);
3558 fprintf(stderr
, _("Not enough memory\n"));
3564 _("File name neither supplied, nor document name exists\n"
3565 "Please, supply one.\n"));
3571 path
= astrcat3(dirname
, "/", filename
);
3578 fprintf(stderr
, _("Not enough memory\n"));
3583 if (document
->is_xml
)
3584 retval
= save_xml_to_file(path
, -1, document
->xml_node_list
,
3585 document
->dmMimeType
, overwrite
);
3587 retval
= save_data_to_file(path
, -1, document
->data
,
3588 document
->data_length
, document
->dmMimeType
, overwrite
);
3594 /* Execute program specified as NULL terminated array of arguments. argv[0] is
3595 * subject of PATH search variable look-up. The program is executed directly,
3596 * it's not a shell command. */
3597 static int execute_system_command(char *const argv
[]) {
3600 if (!argv
|| !argv
[0]) return -1;
3604 /* Could not fork */
3605 fprintf(stderr
, _("Could not fork\n"));
3607 } else if (pid
== 0) {
3609 execvp(argv
[0], argv
);
3610 fprintf(stderr
, _("Could not execute:"));
3611 for (char *const *arg
= argv
; *arg
; arg
++)
3612 fprintf(stderr
, " %s", *arg
);
3613 fprintf(stderr
, _(": %s\n"), strerror(errno
));
3616 /* Wait for the command */
3619 if (-1 == waitpid(pid
, &retval
, 0)) {
3620 fprintf(stderr
, _("Could not wait for executed command\n"));
3625 fprintf(stderr
, _("Exit code of command could not "
3626 "be determined\n"));
3627 else if (WIFEXITED(retval
) && WEXITSTATUS(retval
))
3628 printf(_("Command exited with code %d\n"),
3629 WEXITSTATUS(retval
));
3630 else if (WIFSIGNALED(retval
))
3631 printf(_("Command terminated by signal "
3632 "#%d\n"), WTERMSIG(retval
));
3638 /* Run editor to create new text document */
3639 static int edit_new_textual_document(struct isds_document
*document
) {
3640 char filename
[14] = "shiXXXXXX.txt";
3642 char *command
[] = { getenv("VISUAL"), filename
, NULL
};
3644 struct stat file_before
, file_after
;
3647 fprintf(stderr
, _("Editing is forbidden in batch mode.\n"));
3651 if (NULL
== document
) return -1;
3652 if (NULL
== command
[0]) command
[0] = getenv("EDITOR");
3653 if (NULL
== command
[0]) {
3655 _("Neither environment variable VISUAL nor EDITOR are set.\n"));
3659 /* Create temporary file for the document */
3660 fd
= create_new_file(filename
, 4);
3664 if (fstat(fd
, &file_before
)) {
3666 _("Could not retrieve modification time for `%s': %s\n"),
3667 filename
, strerror(errno
));
3672 /* Open the file with $EDITOR */
3673 if ((retval
= execute_system_command(command
))) {
3674 fprintf(stderr
, _("Editor failed.\n"));
3679 /* Compare modification times */
3680 /* XXX: fstat(2) does return updated st_mtime. Bug in Linux 3.7.1? */
3681 if (stat(filename
, &file_after
)) {
3683 _("Could not retrieve modification time for `%s': %s\n"),
3684 filename
, strerror(errno
));
3688 if (file_before
.st_mtime
== file_after
.st_mtime
) {
3689 fprintf(stderr
, _("Edited document has not been changed.\n"));
3695 if (load_data_from_file(filename
, &document
->data
,
3696 &document
->data_length
, NULL
)) {
3702 if (NULL
== document
->dmMimeType
)
3703 FILL_OR_LEAVE(document
->dmMimeType
, "text/plain");
3704 /* XXX: POSIX basename() modifies argument */
3705 if (NULL
== document
->dmFileDescr
)
3706 FILL_OR_LEAVE(document
->dmFileDescr
, basename(filename
));
3709 /* Remove the file */
3710 unlink_file(filename
);
3716 /* Append @suffix into @buffer with @size bytes prealocated at position @at.
3717 * Trailing '\0' of @suffix is not carried.
3718 * @buffer can be reallocated if @size is not suffient to fill @suffix
3719 * @size is original @buffer size, can change if @buffer would be reallocated
3720 * @at position where append @suffic to. Outputs new end after appending
3721 * @suffix is NULL rerminated string to append
3722 * @return 0 if success, -1 otherwise. Caller is resposible for freeing
3724 static int append_string_at(char **buffer
, size_t *size
, char **at
,
3725 const char *suffix
) {
3727 if (!buffer
|| !*buffer
|| !size
|| !at
|| !*at
) return -1;
3728 if (!suffix
) return 0;
3731 if (*at
- *buffer
+ 1 >= *size
) {
3732 /* End of buffer, grow it */
3733 if (*size
< 8) *size
= 8;
3735 char *new_buffer
= realloc(*buffer
, *size
);
3736 if (!new_buffer
) return -1;
3737 *at
= *at
- *buffer
+ new_buffer
;
3738 *buffer
= new_buffer
;
3741 /* Copy a character */
3742 *((*at
)++) = *(suffix
++);
3749 static char *expand_command_arg(const char *format
, const char *file
,
3751 char *buffer
= NULL
;
3753 const char *format_cursor
;
3754 char *buffer_cursor
;
3756 if (!format
) return NULL
;
3758 for (format_cursor
= format
, buffer_cursor
= buffer
; ; format_cursor
++) {
3759 if (buffer_cursor
- buffer
+ 1 >= size
) {
3760 /* End of buffer, grow it */
3761 if (size
< 8) size
= 8;
3763 char *new_buffer
= realloc(buffer
, size
);
3764 if (!new_buffer
) goto error
;
3765 buffer_cursor
= buffer_cursor
- buffer
+ new_buffer
;
3766 buffer
= new_buffer
;
3769 if (*format_cursor
== '%') {
3771 switch (*(format_cursor
+1)) {
3774 fprintf(stderr
, _("Could not expand `%%f' because "
3775 "file name did not exist.\n"));
3779 if (append_string_at(&buffer
, &size
, &buffer_cursor
, file
))
3786 fprintf(stderr
, _("Could not expand `%%t' because "
3787 "file type did not exist.\n"));
3791 if (append_string_at(&buffer
, &size
, &buffer_cursor
, type
))
3801 /* Copy plain character */
3802 *(buffer_cursor
++) = *format_cursor
;
3804 if (!*format_cursor
) break;
3810 fprintf(stderr
, _("Error: Not enough memory\n"));
3816 /* Expand open_command configuration option by substiting %f and %t with file
3817 * name and file type.
3818 * @file is locale encoded file name
3819 * @type is UTF-8 encoded MIME type
3820 * @return heap allocated arrary of arguments or NULL if error occurs.
3821 * Arguments are coded in locale. */
3822 static char **expand_open_command(const char *file
, const char *type
) {
3823 char **command
= NULL
;
3825 char *type_locale
= NULL
;
3827 length
= cfg_size(configuration
, CONFIG_OPENCOMMAND
);
3829 fprintf(stderr
, _("%s not set\n"), CONFIG_OPENCOMMAND
);
3833 command
= malloc((length
+ 1) * sizeof(*command
));
3835 fprintf(stderr
, _("Error: Not enough memory\n"));
3840 type_locale
= utf82locale(type
);
3842 printf(_("Could not convert document MIME type to locale "
3849 for (int i
= 0; i
< length
; i
++) {
3850 const char *value
= cfg_getnstr(configuration
, CONFIG_OPENCOMMAND
, i
);
3851 command
[i
] = expand_command_arg(value
, file
, type_locale
);
3853 fprintf(stderr
, _("Error: Not enough memory\n"));
3854 for (int j
= 0; j
< i
; j
++) free(command
[j
]);
3860 command
[length
] = NULL
;
3867 /* Register temporary @file for removal at shigofumi exit.
3868 * This is done by destructor call-back in isds_list_free(). */
3869 static int register_temporary_file(const char *file
) {
3870 struct isds_list
*temporary_file
= NULL
;
3872 if (!file
) return 0;
3874 temporary_file
= malloc(sizeof(*temporary_file
));
3875 if (!temporary_file
) {
3876 fprintf(stderr
, _("Error: Not enough memory\n"));
3880 temporary_file
->data
= (void *)strdup(file
);
3881 if (!temporary_file
) {
3882 fprintf(stderr
, _("Error: Not enough memory\n"));
3883 free(temporary_file
);
3887 temporary_file
->destructor
= shi_unlink_temporary_file
;
3889 if (temporary_files
)
3890 temporary_file
->next
= temporary_files
;
3892 temporary_file
->next
= NULL
;
3893 temporary_files
= temporary_file
;
3899 static void shi_open_document_usage(const char *command
) {
3901 "Usage: %s NUMBER\n"
3902 "Save document having ordinal NUMBER within current message into temporal\n"
3903 "local file, open the file by xdg-open utility and then remove the file.\n"
3909 static int shi_open_document(int argc
, const char **argv
) {
3910 const struct isds_document
*document
;
3911 char filename
[10] = "shiXXXXXX";
3913 char **command
= NULL
;
3916 if (!argv
|| !argv
[1] || !*argv
[1] || argc
> 3) {
3917 shi_open_document_usage(argv
[0]);
3921 document
= locate_document_by_ordinal_string(argv
[1]);
3922 if (!document
) return -1;
3924 /* Create temporary file for the document */
3925 fd
= create_new_file(filename
, 0);
3931 if (document
->is_xml
)
3932 retval
= save_xml_to_file(filename
, fd
, document
->xml_node_list
,
3933 document
->dmMimeType
, 0);
3935 retval
= save_data_to_file(filename
, fd
, document
->data
,
3936 document
->data_length
, document
->dmMimeType
, 0);
3938 /* Open the file with external utility */
3940 /* Construct command arguments to execute */
3941 command
= expand_open_command(filename
, document
->dmMimeType
);
3946 /* XXX: Do not use system(3) as we cannot escape uknown shell */
3947 retval
= execute_system_command(command
);
3948 for (char **arg
= command
; *arg
; arg
++) free(*arg
);
3953 /* Remove the file */
3954 /* XXX: We do not know when external program opens the file. We cannot
3955 * remove it immediately. Register the filename and remove all temporary
3956 * files at exit of shigofumi if requested by configuration. */
3957 if (cfg_getbool(configuration
, CONFIG_CLEAN_TEMPORARY_FILES
)) {
3958 if (register_temporary_file(filename
))
3959 fprintf(stderr
, _("Warning: Temporary file `%s' could not been "
3960 "registered for later removal. Remove the file by "
3961 "hand, please.\n"), filename
);
3967 static void shi_compose_usage(const char *command
) {
3969 "Usage: %s OPTION...\n"
3970 "Compose and send a message to recipient defined by his box ID.\n"
3971 "Each option requires an argument (if not stated otherwise):\n"
3972 " -s * message subject\n"
3974 "Recipient options:\n"
3975 " -b * recipient box ID\n"
3976 " -U organisation unit name\n"
3977 " -N organisation unit number\n"
3978 " -P to hands of given person\n"
3980 "Sender organisation structure options:\n"
3981 " -I publish user's identity\n"
3985 "Message identifier options:\n"
3986 " -r sender reference number\n"
3987 " -f sender file ID\n"
3988 " -R recipient reference number\n"
3989 " -F recipient file ID\n"
3991 "Legal title options:\n"
3992 " -y year act has been issued\n"
3993 " -a ordinal number of act in a year\n"
3994 " -e section of the act\n"
3995 " -o paragraph of the act\n"
3996 " -i point of the paragraph of the act\n"
3998 "Delivery options:\n"
3999 " -p personal delivery required\n"
4000 " -t allow substitutable delivery\n"
4001 " -A non-OVM sender acts as public authority\n"
4002 " -C commercial type; accepted values:\n"
4003 " K Commercial message paid by sender or sponsor\n"
4004 " I Initiatory commercial message offering to pay response\n"
4005 " O Commercial response paid by recipient\n"
4006 " V Public message paid by government\n"
4007 " Missing option defaults to K or O (see `commercialsending' command)\n"
4008 " if sending to non-OVM recipient with enabled commercial receiving,\n"
4009 " otherwise it defaults to V.\n"
4011 "Document options:\n"
4012 " -d * read document from local file. If `-' is specified,\n"
4013 " run text editor.\n"
4014 " -D document name (defaults to base local file name)\n"
4015 " -x transport subset of the document as a XML.\n"
4016 " Argument is XPath expression specifying desired node set\n"
4017 " -m override MIME type (guessed on -d)\n"
4018 " -g document ID (must be unique per message)\n"
4019 " -G reference to other document using its ID\n"
4020 " -c document is digital signature of other document (NO argument\n"
4023 "Options marked with asterisk are mandatory, other are optional. Another soft\n"
4024 "dependencies can emerge upon using specific option. They are not mandated by\n"
4025 "ISDS currently, but client library or this program can force them to assure\n"
4026 "semantically complete message. Following soft dependencies are recommended:\n"
4027 " -y <=> -a act number and year must be used at the same time\n"
4028 " -i => -o act point requires act paragraph\n"
4029 " -o => -e act paragraph requires act section\n"
4030 " -e => -a act section requires act number\n"
4031 " -G => -g document with referenced ID must exist\n"
4032 " -c => -G signature must refer to signed document\n"
4033 " -c first document cannot be signature\n"
4034 " -C I => -r sender reference number allows responder to reply to this message\n"
4035 " -C O -> -R recipient reference number must match sender reference number of\n"
4036 " initiatory message\n"
4038 "More documents can be attached to a message by repeating `-d' option.\n"
4039 "Document order will be preserved. Other document options affect immediately\n"
4040 "preceding `-d' document only. E.g. `-d /tmp/foo.pdf -m application/pdf\n"
4041 "-d /tmp/bar.txt -m text/plain' attaches first PDF file, then textual file.\n"
4043 "The same applies to recipient options that must start with box ID (-b).\n"
4044 "If more recipients specified, each of them will get a copy of composed\n"
4045 "message. ISDS will assign message identifier to each copy in turn.\n"
4051 static int shi_compose(int argc
, const char **argv
) {
4056 struct isds_message
*message
= NULL
;
4057 struct isds_envelope
*envelope
= NULL
;
4058 struct isds_list
*documents
= NULL
;
4059 struct isds_document
*document
= NULL
;
4060 struct isds_list
*copies
= NULL
, *copy_item
= NULL
;
4061 struct isds_message_copy
*copy
= NULL
;
4062 char *message_id_locale
= NULL
, *recipient_id_locale
= NULL
,
4063 *dmStatus_locale
= NULL
;
4065 if (!argv
|| !argv
[1] || !*argv
[1]) {
4066 fprintf(stderr
, _("Error: No argument supplied\n"));
4067 shi_compose_usage((argv
)?argv
[0]:NULL
);
4071 message
= calloc(1, sizeof(*message
));
4073 fprintf(stderr
, _("Error: Not enough memory\n"));
4077 envelope
= calloc(1, sizeof(*envelope
));
4079 fprintf(stderr
, _("Error: Not enough memory\n"));
4083 message
->envelope
= envelope
;
4087 while ((opt
= getopt(argc
, (char * const *)argv
, "s:" "b:U:N:P:" "I:u:n:"
4088 "r:f:R:F:" "y:a:e:o:i:" "p:t:A:C:" "d:D:x:m:g:G:c"
4092 FILL_OR_LEAVE(envelope
->dmAnnotation
, optarg
);
4095 /* Recipient options */
4099 /* First recipient */
4100 CALLOC_OR_LEAVE(copies
);
4101 copies
->destructor
=
4102 (void(*)(void **)) isds_message_copy_free
;
4105 /* Next recipient */
4106 CALLOC_OR_LEAVE(copy_item
->next
);
4107 copy_item
->next
->destructor
=
4108 (void(*)(void **)) isds_message_copy_free
;
4109 copy_item
= copy_item
->next
;
4111 CALLOC_OR_LEAVE(copy
);
4112 copy_item
->data
= copy
;
4114 /* Copy recipient box ID */
4115 FILL_OR_LEAVE(copy
->dbIDRecipient
, optarg
);
4120 _("Error: %s: Recipient box ID (-b) must precede "
4121 "recipient organisation unit name (-%c)\n"),
4126 FILL_OR_LEAVE(copy
->dmRecipientOrgUnit
, optarg
);
4131 _("Error: %s: Recipient box ID (-b) must precede "
4132 "recipient organisation unit number (-%c)\n"),
4137 FILL_LONGINT_OR_LEAVE(copy
->dmRecipientOrgUnitNum
, optarg
);
4142 _("Error: %s: Recipient box ID (-b) must precede "
4143 "to-hands option (-%c)\n"), optarg
, opt
);
4147 FILL_OR_LEAVE(copy
->dmToHands
, optarg
);
4150 /* Sender organisation structure options */
4152 FILL_BOOLEAN_OR_LEAVE(envelope
->dmPublishOwnID
, optarg
);
4155 FILL_OR_LEAVE(envelope
->dmSenderOrgUnit
, optarg
);
4158 FILL_LONGINT_OR_LEAVE(envelope
->dmSenderOrgUnitNum
, optarg
);
4161 /* Message identifier options */
4163 FILL_OR_LEAVE(envelope
->dmSenderRefNumber
, optarg
);
4166 FILL_OR_LEAVE(envelope
->dmSenderIdent
, optarg
);
4169 FILL_OR_LEAVE(envelope
->dmRecipientRefNumber
, optarg
);
4172 FILL_OR_LEAVE(envelope
->dmRecipientIdent
, optarg
);
4175 /* Legal title options */
4177 FILL_LONGINT_OR_LEAVE(envelope
->dmLegalTitleYear
, optarg
);
4180 FILL_LONGINT_OR_LEAVE(envelope
->dmLegalTitleLaw
, optarg
);
4183 FILL_OR_LEAVE(envelope
->dmLegalTitleSect
, optarg
);
4186 FILL_OR_LEAVE(envelope
->dmLegalTitlePar
, optarg
);
4189 FILL_OR_LEAVE(envelope
->dmLegalTitlePoint
, optarg
);
4192 /* Delivery options */
4194 FILL_BOOLEAN_OR_LEAVE(envelope
->dmPersonalDelivery
, optarg
);
4197 FILL_BOOLEAN_OR_LEAVE(envelope
->dmAllowSubstDelivery
, optarg
);
4200 FILL_BOOLEAN_OR_LEAVE(envelope
->dmOVM
, optarg
);
4203 FILL_OR_LEAVE(envelope
->dmType
, optarg
);
4206 /* Document options */
4210 /* First document */
4211 CALLOC_OR_LEAVE(message
->documents
);
4212 message
->documents
->destructor
=
4213 (void(*)(void **)) isds_document_free
;
4214 documents
= message
->documents
;
4215 CALLOC_OR_LEAVE(document
);
4216 documents
->data
= document
;
4217 document
->dmFileMetaType
= FILEMETATYPE_MAIN
;
4220 CALLOC_OR_LEAVE(documents
->next
);
4221 documents
->next
->destructor
=
4222 (void(*)(void **)) isds_document_free
;
4223 documents
= documents
->next
;
4224 CALLOC_OR_LEAVE(document
);
4225 documents
->data
= document
;
4226 document
->dmFileMetaType
= FILEMETATYPE_ENCLOSURE
;
4229 if (strcmp(optarg
, "-")) {
4230 /* Load file if specified. Keep editing new file after
4231 * processing all arguments. */
4232 if (load_data_from_file(optarg
, &document
->data
,
4233 &document
->data_length
, &document
->dmMimeType
)) {
4237 /* XXX: POSIX basename() modifies argument */
4238 FILL_OR_LEAVE(document
->dmFileDescr
, basename(optarg
));
4244 _("Error: %s: Document file (-d) must precede "
4245 "document name (-%c)\n"), optarg
, opt
);
4249 FILL_OR_LEAVE(document
->dmFileDescr
, optarg
);
4254 _("Error: %s: Document file (-d) must precede "
4255 "XPath expression (-%c)\n"), optarg
, opt
);
4259 /* Load XML node list */
4260 char *xpath_expr
= NULL
;
4261 FILL_OR_LEAVE(xpath_expr
, optarg
);
4262 retval
= load_xml_subtree_from_memory(
4263 document
->data
, document
->data_length
,
4264 &document
->xml_node_list
, xpath_expr
);
4269 /* Switch document type to XML */
4270 document
->is_xml
= 1;
4271 zfree(document
->data
);
4272 document
->data_length
= 0;
4273 documents
->destructor
=
4274 (void(*)(void **)) free_document_with_xml_node_list
;
4279 _("Error: %s: Document file (-d) must precede "
4280 "MIME type (-%c)\n"), optarg
, opt
);
4284 FILL_OR_LEAVE(document
->dmMimeType
, optarg
);
4289 _("Error: %s: Document file (-d) must precede "
4290 "document ID (-%c)\n"), optarg
, opt
);
4294 FILL_OR_LEAVE(document
->dmFileGuid
, optarg
);
4299 _("Error: %s: Document file (-d) must precede "
4300 "document reference (-%c)\n"), optarg
, opt
);
4304 FILL_OR_LEAVE(document
->dmUpFileGuid
, optarg
);
4309 _("Error: Document file (-d) must precede "
4310 "document signature type (-%c)\n"), opt
);
4314 document
->dmFileMetaType
= FILEMETATYPE_SIGNATURE
;
4318 shi_compose_usage(argv
[0]);
4324 /* All options must be recognized */
4325 if (optind
!= argc
) {
4326 fprintf(stderr
, _("Error: Superfluous argument\n"));
4327 shi_compose_usage(argv
[0]);
4333 fprintf(stderr
, _("Error: No recipient box ID specified\n"));
4334 shi_compose_usage(argv
[0]);
4339 /* TODO: Check Legal Title soft dependencies */
4341 /* Compose missing documents */
4342 for (i
= 1, documents
= message
->documents
; NULL
!= documents
;
4343 documents
= documents
->next
, i
++) {
4344 document
= documents
->data
;
4345 if (NULL
== document
->data
) {
4346 printf(_("Editing document #%u...\n"), i
);
4347 if (edit_new_textual_document(document
)) {
4348 fprintf(stderr
, _("Composition aborted.\n"));
4356 oprintf(_("Following message has been composed:\n"));
4357 format_message(message
);
4358 oprintf(_("Following recipients have been specified:\n"));
4359 format_copies(copies
);
4360 /* TODO: correction */
4361 if (cfg_getbool(configuration
, CONFIG_CONFIRM_SEND
)) {
4362 if (!shi_ask_yes_no(_("Send composed message?"), 1, batch_mode
)) {
4363 fprintf(stderr
, _("Composition aborted.\n"));
4369 /* Send a message */
4370 printf(_("Sending message...\n"));
4371 err
= isds_send_message_to_multiple_recipients(cisds
, message
, copies
);
4372 finish_isds_operation(cisds
, err
);
4373 if (err
&& err
!= IE_PARTIAL_SUCCESS
) {
4378 /* Show results for each copy */
4379 for (copy_item
= copies
; copy_item
; copy_item
= copy_item
->next
) {
4380 if (!copy_item
->data
) continue;
4381 copy
= (struct isds_message_copy
*) copy_item
->data
;
4382 recipient_id_locale
= utf82locale(copy
->dbIDRecipient
);
4386 if (copy
->dmStatus
) dmStatus_locale
= utf82locale(copy
->dmStatus
);
4387 if (dmStatus_locale
)
4388 oprintf(_("%s: Failed: %s: %s\n"),
4389 recipient_id_locale
,
4390 isds_strerror(copy
->error
), dmStatus_locale
);
4392 oprintf(_("%s: Failed: %s\n"), recipient_id_locale
,
4393 isds_strerror(copy
->error
));
4394 zfree(dmStatus_locale
);
4396 message_id_locale
= utf82locale(copy
->dmID
);
4397 oprintf(_("%s: Succeeded. Assigned message ID: %s\n"),
4398 recipient_id_locale
, message_id_locale
);
4399 free(message_id_locale
);
4402 free(recipient_id_locale
);
4406 isds_message_free(&message
);
4407 isds_list_free(&copies
);
4412 #undef FILL_LONGINT_OR_LEAVE
4413 #undef FILL_BOOLEAN_OR_LEAVE
4414 #undef CALLOC_OR_LEAVE
4415 #undef FILL_OR_LEAVE
4418 static void shi_save_stamp_usage(const char *command
) {
4421 "Save message time stamp into local FILE.\n"),
4426 static int shi_save_stamp(int argc
, const char **argv
) {
4427 _Bool overwrite
= cfg_getbool(configuration
, CONFIG_OVERWRITEFILES
);
4429 if (!argv
|| !argv
[1] || !*argv
[1]) {
4430 shi_save_message_usage(argv
[0]);
4435 fprintf(stderr
, _("No message loaded\n"));
4438 if (!message
->envelope
|| !message
->envelope
->timestamp
||
4439 message
->envelope
->timestamp_length
== 0) {
4440 fprintf(stderr
, _("Loaded message is missing time stamp\n"));
4444 return save_data_to_file(argv
[1], -1,
4445 message
->envelope
->timestamp
, message
->envelope
->timestamp_length
,
4446 "application/timestamp-reply", overwrite
);
4450 /* Return message of current message list identified by message ID expressed
4451 * as string. In case of error return NULL. */
4452 static const struct isds_message
*locate_message_by_id(const char *id_locale
) {
4454 const struct isds_list
*item
;
4455 const struct isds_message
*message
= NULL
;
4457 if (NULL
== id_locale
) return NULL
;
4459 id
= locale2utf8(id_locale
);
4461 fprintf(stderr
, _("Could not convert message ID `%s' to UTF-8\n"),
4466 if (NULL
== messages
) {
4467 fprintf(stderr
, _("No message list loaded\n"));
4472 for (item
= messages
; NULL
!= item
; item
= item
->next
) {
4473 if (NULL
== item
->data
) continue;
4474 message
= (const struct isds_message
*) item
->data
;
4475 if (NULL
== message
->envelope
|| NULL
== message
->envelope
->dmID
)
4477 if (!strcmp(id
, message
->envelope
->dmID
)) {
4482 fprintf(stderr
, _("List does not contain message `%s'\n"), id_locale
);
4490 static void shi_show_list_usage(const char *command
) {
4492 "Usage: %s [MESSAGE_ID]\n"
4493 "If no MESSAGE_ID is given, show current message list.\n"
4494 "If MESSAGE_ID is specified, show details about message with the MESSAGE_ID\n"
4495 "on the current message list.\n"),
4500 static int shi_show_list(int argc
, const char **argv
) {
4501 if (NULL
== messages
) {
4502 fprintf(stderr
, _("No message list loaded\n"));
4506 if (argc
> 2 || (2 == argc
&& NULL
== argv
[1])) {
4507 shi_show_list_usage((argv
[0] == NULL
) ? NULL
: argv
[0]);
4512 /* Show details about one message */
4513 const struct isds_message
*message
= locate_message_by_id(argv
[1]);
4514 if (NULL
== message
) return -1;
4515 format_message(message
);
4520 oprintf((messages_are_outgoing
) ?
4521 ngettext("You have %'lu outgoing message\n",
4522 "You have %'lu outgoing messages\n", total_messages
) :
4523 ngettext("You have %'lu incoming message\n",
4524 "You have %'lu incoming messages\n", total_messages
),
4526 print_message_list(messages
, messages_are_outgoing
);
4531 static void describe_message_listing_flags(void) {
4533 "The third column displays flags describing status of a message:\n"
4534 " First character is a commercial type of the message:\n"
4535 " P public non-commercial message\n"
4536 " C commercial message\n"
4537 " I commercial message offering a paid response\n"
4538 " i like I, but the offer has expired\n"
4539 " R commerical message as a reply to I\n"
4540 " The second character is a delivery status of the message:\n"
4541 " > message has been sent into the system\n"
4542 " t message has been time-stamped by the system\n"
4543 " I message contained an infected document\n"
4544 " N message has been delivered ordinaly\n"
4545 " n message has been delivered substitutingly\n"
4546 " O message has been accepted by the recipient\n"
4547 " \" \" message has been read\n"
4548 " ! message could not been delivered\n"
4549 " D message content has been deleted\n"
4550 " S message has been stored in the long term storage\n"
4551 " ? unrecognized state\n"));
4555 static void shi_list_incoming_usage(const char *command
) {
4558 "List messages received into your box.\n"
4561 describe_message_listing_flags();
4565 static int shi_list_incoming(int argc
, const char **argv
) {
4566 unsigned long int count
= 0;
4569 printf(_("Listing incoming messages...\n"));
4570 err
= isds_get_list_of_received_messages(cisds
, NULL
, NULL
, NULL
,
4571 MESSAGESTATE_ANY
, 0, &count
, &messages
);
4572 finish_isds_operation(cisds
, err
);
4573 total_messages
= count
;
4576 select_completion(COMPL_COMMAND
);
4579 messages_are_outgoing
= 0;
4581 shi_show_list(0, NULL
);
4583 set_prompt(_("%s %'lu"), argv
[0], total_messages
);
4584 select_completion(COMPL_LIST
);
4589 static void shi_list_outgoing_usage(const char *command
) {
4592 "List messages sent from your box.\n"
4595 describe_message_listing_flags();
4599 static int shi_list_outgoing(int argc
, const char **argv
) {
4600 unsigned long int count
= 0;
4603 printf(_("Listing outgoing messages...\n"));
4604 err
= isds_get_list_of_sent_messages(cisds
, NULL
, NULL
, NULL
,
4605 MESSAGESTATE_ANY
, 0, &count
, &messages
);
4606 finish_isds_operation(cisds
, err
);
4607 total_messages
= count
;
4610 select_completion(COMPL_COMMAND
);
4613 messages_are_outgoing
= 1;
4615 shi_show_list(0, NULL
);
4617 set_prompt(_("%s %'lu"), argv
[0], total_messages
);
4618 select_completion(COMPL_LIST
);
4623 /* Submit document for conversion and print assigned identifier */
4624 static int do_convert(const struct isds_document
*document
) {
4627 struct tm
*date
= NULL
;
4629 if (!document
) return -1;
4631 printf(_("Submitting document for authorized conversion...\n"));
4633 err
= czp_convert_document(czechpoint
, document
, &id
, &date
);
4634 finish_isds_operation(czechpoint
, err
);
4637 char *name_locale
= utf82locale(document
->dmFileDescr
);
4638 char *date_string
= tm2string(date
);
4639 char *id_locale
= utf82locale(id
);
4641 "Document submitted for authorized conversion successfully under name\n"
4643 "Submit identifier assigned by Czech POINT deposit is `%s'.\n"),
4644 name_locale
, date_string
, id_locale
);
4648 oprintf(_("Be ware that submitted document has restricted lifetime "
4650 oprintf(_("See <%s> for more details.\n"), CZPDEPOSIT_URL
);
4653 free(id
); free(date
);
4654 return (err
) ? -1 : 0;
4658 static void shi_convert_file_or_message_usage(const char *command
) {
4660 "Usage: %s [FILE [NAME]]\n"
4661 "Submit local FILE to authorized conversion under NAME. If NAME is missing,\n"
4662 "it will use FILE name. If FILE is missing, it will submit current message.\n"),
4666 "If Czech POINT deposit accepts document, it will return document identifier\n"
4667 "that user is supposed to provide to officer at Czech POINT contact place.\n"
4668 "Currently only PDF 1.3 and higher version files and signed messages are\n"
4670 oprintf(_("See <%s> for more details.\n"), CZPDEPOSIT_URL
);
4674 static int shi_convert_file_or_message(int argc
, const char **argv
) {
4676 struct isds_document document
;
4679 if (!argv
|| argc
> 3) {
4680 shi_convert_file_or_message_usage((argv
)?argv
[0]:NULL
);
4684 memset(&document
, 0, sizeof(document
));
4686 if (NULL
== argv
[1] || !*argv
[1]) {
4687 /* Convert current message */
4689 fprintf(stderr
, _("No message loaded\n"));
4692 if (!message
->raw
|| !message
->raw_length
) {
4694 _("Current message is missing raw representation\n"));
4697 document
.dmFileDescr
= astrcat(
4698 (NULL
!= message
->envelope
&& NULL
!= message
->envelope
->dmID
) ?
4699 message
->envelope
->dmID
:
4702 if (NULL
== document
.dmFileDescr
) {
4703 printf(_("Could not build document name from message ID\n"));
4706 document
.data
= message
->raw
;
4707 document
.data_length
= message
->raw_length
;
4709 /* Convert local file */
4710 if (argc
== 3 && argv
[2] && *argv
[2])
4711 document
.dmFileDescr
= locale2utf8(argv
[2]);
4713 document
.dmFileDescr
= locale2utf8(argv
[1]);
4714 if (!document
.dmFileDescr
) {
4715 printf(_("Could not convert document name to UTF-8\n"));
4719 printf(_("Loading document from file `%s'...\n"), argv
[1]);
4720 if (mmap_file(argv
[1], &fd
, &document
.data
, &document
.data_length
)) {
4721 free(document
.dmFileDescr
);
4726 retval
= do_convert(&document
);
4729 munmap_file(fd
, document
.data
, document
.data_length
);
4731 free(document
.dmFileDescr
);
4736 static void shi_convert_document_usage(const char *command
) {
4738 "Usage: %s NUMBER\n"
4739 "Submit message document with ordinal NUMBER to authorized conversion.\n"),
4743 "If Czech POINT deposit accepts document, it will return document identifier\n"
4744 "that user is supposed to provide to officer at Czech POINT contact place.\n"
4745 "Currently only PDF 1.3 and higher version files and signed messages are\n"
4747 oprintf(_("See <%s> for more details.\n"), CZPDEPOSIT_URL
);
4751 static int shi_convert_document(int argc
, const char **argv
) {
4752 const struct isds_document
*document
;
4754 if (!argv
|| !argv
[1] || !*argv
[1]) {
4755 shi_convert_document_usage((argv
)?argv
[0]:NULL
);
4759 document
= locate_document_by_ordinal_string(argv
[1]);
4760 if (!document
) return -1;
4762 return do_convert(document
);
4766 static void shi_resign_usage(const char *command
) {
4768 "Usage: %s [FILE]\n"
4769 "Send message or delivery data to re-sign it and to add current time stamp.\n"
4770 "If FILE is specified, message will be loaded from local file. Otherwise\n"
4771 "current message will be sent.\n"),
4775 "Only signed messages or delivery data without time stamp are accepted by\n"
4776 "ISDS. Output re-signed message or delivery data will be loaded.\n"));
4780 static int shi_resign(int argc
, const char **argv
) {
4782 void *data
= NULL
; /* Static */
4783 void *resigned_data
= NULL
; /* Dynamic, stored into message */
4784 size_t data_length
= 0, resigned_data_length
= 0;
4785 struct tm
*valid_to
= NULL
; /* Dynamic */
4789 if (!argv
|| argc
> 3) {
4790 shi_resign_usage((argv
)?argv
[0]:NULL
);
4794 if (NULL
== argv
[1] || !*argv
[1]) {
4795 /* Use current message */
4797 fprintf(stderr
, _("No message or delivery data loaded\n"));
4800 if (!message
->raw
|| !message
->raw_length
) {
4801 fprintf(stderr
, _("Current message or delivery data "
4802 "is missing raw representation\n"));
4805 data
= message
->raw
;
4806 data_length
= message
->raw_length
;
4808 /* Use local file */
4809 printf(_("Loading message or delivery data from file `%s'...\n"),
4811 if (mmap_file(argv
[1], &fd
, &data
, &data_length
)) {
4816 printf(_("Re-signing...\n"));
4817 err
= isds_resign_message(cisds
, data
, data_length
,
4818 &resigned_data
, &resigned_data_length
, &valid_to
);
4819 finish_isds_operation(cisds
, err
);
4822 munmap_file(fd
, data
, data_length
);
4829 print_header_tm(_("New time stamp expires"), valid_to
);
4832 error
= do_load_anything(resigned_data
, resigned_data_length
, BUFFER_MOVE
);
4833 if (error
) free(resigned_data
);
4840 static void shi_print_usage(const char *command
) {
4842 "Usage: %s STRING LENGTH\n"
4843 "Prints STRING into LENGTH columns. Negative LENGTH means not to cut\n"
4844 "overflowing string.\n"
4845 "This should be locale and terminal agnostic.\n"),
4850 static int shi_print(int argc
, const char **argv
) {
4853 if (!argv
|| !argv
[1] || !argv
[2]) {
4854 shi_print_usage((argv
)?argv
[0]:NULL
);
4858 width
= strtol(argv
[2], NULL
, 10);
4859 if (width
< INT_MIN
) {
4861 _("Length argument must not lesser than %d.\n"), INT_MIN
);
4864 if (width
> INT_MAX
) {
4866 _("Length argument must not be greater than %d.\n"), INT_MAX
);
4871 onprint(argv
[1], width
);
4878 static int shi_tokenize(int argc
, const char **argv
) {
4880 if (!argv
) return 0;
4882 for (int i
= 0; i
< argc
; i
++) {
4883 oprintf(_(">%s<\n"), argv
[i
]);
4889 static int shi_quote(int argc
, const char **argv
) {
4890 char *escaped
, *unescaped
;
4892 if (!argv
) return 0;
4894 oprintf(_("Original\tQuoted\tDequoted\n"));
4895 for (int i
= 0; i
< argc
; i
++) {
4896 escaped
= shi_quote_filename((char *) argv
[i
], 0, NULL
);
4897 unescaped
= shi_dequote_filename((char *) argv
[i
], 0);
4898 oprintf(_(">%s<\t>%s<\t>%s<\n"), argv
[i
], escaped
, unescaped
);
4906 static int shi_ask_yesno(int argc
, const char **argv
) {
4909 answer
= shi_ask_yes_no(argv
[1], 1, batch_mode
);
4910 oprintf(_("Answer is %s.\n"), (answer
) ? _("Yes") : _("No"));
4919 /* pclose(pipe), restore ouput to stdout, show error return code */
4920 int wait_for_shell(FILE **pipe
) {
4923 if (pipe
&& *pipe
) {
4924 retval
= pclose(*pipe
);
4929 fprintf(stderr
, _("Exit code of shell command could not "
4930 "be determined\n"));
4931 else if (WIFEXITED(retval
) && WEXITSTATUS(retval
))
4932 printf(_("Shell command exited with code %d\n"),
4933 WEXITSTATUS(retval
));
4934 else if (WIFSIGNALED(retval
))
4935 printf(_("Shell command terminated by signal "
4936 "#%d\n"), WTERMSIG(retval
));
4943 /* Interactive loop */
4944 void shi_loop(void) {
4945 char *command_line
= NULL
;
4946 char **command_argv
= NULL
;
4949 struct command
*command
= NULL
;
4953 oprintf(_("Use `help' command to get list of available commands.\n"));
4955 select_completion(COMPL_COMMAND
);
4959 command_line
= readline((prompt
) ? prompt
: _("shigofumi> "));
4960 /* Remember not parsable commands too to user be able to get back to
4962 if (command_line
&& *command_line
) {
4963 /* TODO: Omit blank lines */
4964 add_history(command_line
);
4967 command_argv
= tokenize(command_line
, &command_argc
, &shell
);
4969 if (command_argv
&& command_argv
[0]) {
4970 command
= find_command(command_argv
[0]);
4973 fprintf(stderr
, _("Command not understood\n"));
4978 pipe
= popen(shell
, "w");
4980 fprintf(stderr
, _("Could not run shell command `%s':"
4981 " %s\n"), shell
, strerror(errno
));
4987 command
->function(command_argc
,
4988 (const char **) command_argv
);
4989 wait_for_shell(&pipe
);
4994 argv_free(command_argv
);
4996 zfree(command_line
);
5001 /* Non-interactive mode. Commands from @lines are processed until any command
5002 * lines remains or no error occurred. First failure terminates processing.
5003 * @lines is sequence of commands separated by '\n' or '\r'. The content is
5004 * modified during this call.
5005 * @return 0 if all command succeed, otherwise non-zero value
5007 int shi_batch(char *lines
) {
5009 char **command_argv
= NULL
;
5012 struct command
*command
= NULL
;
5017 oprintf(_("Batch mode started.\n"));
5019 select_completion(COMPL_COMMAND
);
5021 while (!retval
&& (command_line
= strtok(lines
, "\n\r"))) {
5022 lines
= NULL
; /* strtok(3) requires it for subsequent calls */
5024 printf(_("Processing command: %s\n"), command_line
);
5026 command_argv
= tokenize(command_line
, &command_argc
, &shell
);
5028 if (command_argv
&& command_argv
[0]) {
5029 command
= find_command(command_argv
[0]);
5032 fprintf(stderr
, _("Command not understood\n"));
5038 pipe
= popen(shell
, "w");
5040 fprintf(stderr
, _("Could not run shell command `%s':"
5041 " %s\n"), shell
, strerror(errno
));
5047 retval
= command
->function(command_argc
,
5048 (const char **) command_argv
);
5049 if (wait_for_shell(&pipe
)) retval
= -1;
5054 argv_free(command_argv
);
5059 fprintf(stderr
, _("Command failed!\n"));
5064 #define COMMON_COMMANDS \
5065 { "accept", shi_accept_message, N_("accept commercial message"), \
5066 shi_accept_message_usage, ARGTYPE_MSGID }, \
5067 { "box", shi_box, N_("show current box details"), NULL, \
5069 { "boxlist", shi_boxlist, N_("get list of all boxes"), shi_boxlist_usage, \
5071 { "cache", shi_cache, N_("show cache details"), NULL, \
5073 { "cd", shi_chdir, N_("change working directory"), shi_chdir_usage, \
5075 { "commercialcredit", shi_commercialcredit, N_("get credit details"), \
5076 shi_commercialcredit_usage, ARGTYPE_BOXID }, \
5077 { "commercialreceiving", shi_commercialreceiving, \
5078 N_("manipulate commercial receiving box status"), \
5079 shi_commercialreceiving_usage, ARGTYPE_BOXID }, \
5080 { "commercialsending", shi_commercialsending, \
5081 N_("manipulate commercial sending box status"), \
5082 shi_commercialsending_usage, ARGTYPE_BOXID }, \
5083 { "compose", shi_compose, N_("compose a message"), shi_compose_usage, \
5085 { "convert", shi_convert_file_or_message, \
5086 N_("submit local document for authorized conversion"), \
5087 shi_convert_file_or_message_usage, ARGTYPE_FILE }, \
5088 { "copying", shi_copying, N_("show this program licence excerpt"), NULL, \
5090 { "debug", shi_debug, N_("set debugging"), shi_debug_usage, \
5092 { "delete", shi_delete_message, N_("delete message from storage"), \
5093 shi_delete_message_usage, ARGTYPE_MSGID }, \
5094 { "delivery", shi_delivery, N_("get message delivery details"), \
5095 shi_delivery_usage, ARGTYPE_MSGID }, \
5096 { "findbox", shi_find_box, N_("search for a box by attributes"), \
5097 shi_find_box_usage, ARGTYPE_BOXID }, \
5098 { "hash", shi_hash, N_("query ISDS for message hash"), \
5099 shi_hash_usage, ARGTYPE_MSGID }, \
5100 { "help", shi_help, N_("describe commands"), shi_help_usage, \
5101 ARGTYPE_COMMAND }, \
5102 { "load", shi_load_anything, \
5103 N_("load message or message delivery details from local file"), \
5104 shi_load_anything_usage, ARGTYPE_FILE }, \
5105 { "login", shi_login, N_("log into ISDS"), shi_login_usage, \
5107 { "lsi", shi_list_incoming, N_("list received messages"), \
5108 shi_list_incoming_usage, ARGTYPE_NONE }, \
5109 { "lso", shi_list_outgoing, N_("list sent messages"), \
5110 shi_list_outgoing_usage, ARGTYPE_NONE }, \
5111 { "msgi", shi_incoming_message, N_("get incoming message"), \
5112 shi_incoming_message_usage, ARGTYPE_MSGID }, \
5113 { "msgo", shi_outgoing_message, N_("get outgoing message"), \
5114 shi_outgoing_message_usage, ARGTYPE_MSGID }, \
5115 { "passwd", shi_passwd, N_("manipulate user password"), shi_passwd_usage, \
5117 { "pwd", shi_pwd, N_("print working directory"), NULL, ARGTYPE_NONE }, \
5118 { "quit", shi_quit, N_("exit shigofumi"), NULL, ARGTYPE_NONE }, \
5119 { "read", shi_read_message, N_("mark message as read"), \
5120 shi_read_message_usage, ARGTYPE_MSGID }, \
5121 { "resign", shi_resign, N_("re-sign message or delivery data"), \
5122 shi_resign_usage, ARGTYPE_FILE }, \
5123 { "searchbox", shi_search_box, N_("search for a box by a full-text"), \
5124 shi_search_box_usage, ARGTYPE_BOXID }, \
5125 { "set", shi_settings, N_("show settings"), NULL, ARGTYPE_NONE }, \
5126 { "sender", shi_message_sender, N_("get message sender"), \
5127 shi_message_sender_usage, ARGTYPE_MSGID }, \
5128 { "statbox", shi_stat_box, N_("get status of a box"), shi_stat_box_usage, \
5130 { "user", shi_user, N_("show current user details"), NULL, \
5132 { "users", shi_users, N_("show box users"), shi_users_usage, \
5134 { "version", shi_version, N_("show version of this program"), NULL, \
5136 { NULL, NULL, NULL, NULL, ARGTYPE_NONE }
5138 struct command base_commands
[] = {
5140 { "askyesno", shi_ask_yesno
, N_("demonstrate yes-no question"), NULL
,
5142 { "quote", shi_quote
, N_("demonstrate argument escaping"), NULL
,
5144 { "print", shi_print
, N_("print string into given width"),
5145 shi_print_usage
, ARGTYPE_NONE
},
5146 { "tokenize", shi_tokenize
, N_("demonstrate arguments tokenization"), NULL
,
5152 struct command message_commands
[] = {
5153 { "authenticate", shi_authenticate
, N_("check message authenticity"),
5154 NULL
, ARGTYPE_NONE
},
5155 { "cat", shi_cat_message
, N_("show raw current message"),
5156 shi_cat_message_usage
, ARGTYPE_NONE
},
5157 { "catdoc", shi_cat_document
, N_("show raw document"),
5158 shi_cat_document_usage
, ARGTYPE_DOCID
},
5159 { "convertdoc", shi_convert_document
,
5160 N_("submit document of current message for authorized conversion"),
5161 shi_convert_document_usage
, ARGTYPE_DOCID
},
5162 { "dump", shi_dump_message
, N_("dump current message structure"),
5163 NULL
, ARGTYPE_NONE
},
5164 { "opendoc", shi_open_document
, N_("open document using external utility"),
5165 shi_open_document_usage
, ARGTYPE_DOCID
},
5166 { "savestamp", shi_save_stamp
,
5167 N_("save time stamp of current message into local file"),
5168 shi_save_stamp_usage
, ARGTYPE_FILE
},
5169 { "savedoc", shi_save_document
,
5170 N_("save document of current message into local file"),
5171 shi_save_document_usage
, ARGTYPE_FILE
},
5172 { "save", shi_save_message
, N_("save current message into local file"),
5173 shi_save_message_usage
, ARGTYPE_FILE
},
5174 { "show", shi_show_message
, N_("show current message"), NULL
,
5176 { "verify", shi_verify
, N_("verify current message hash"), NULL
,
5181 struct command list_commands
[] = {
5182 { "show", shi_show_list
, N_("show current message list or list item details"),
5183 shi_show_list_usage
, ARGTYPE_MSGID
},
5187 #undef COMMON_COMMANDS
5190 static void main_version(void) {
5195 shi_copying(0, NULL
);
5199 static void main_usage(const char *command
) {
5201 "Usage: %s [OPTION...]\n"
5202 "Access ISDS, process local data box messages or delivery details, submit\n"
5203 "document to authorized conversion.\n"
5206 " -c FILE use the FILE as configuration file instead of ~/%s\n"
5207 " -e COMMANDS execute COMMANDS (new line separated) and exit\n"
5208 " -V show version info and exit\n"
5210 (command
) ? command
: "shigofumi",
5215 int main(int argc
, char **argv
) {
5217 char *config_file
= NULL
;
5218 char *batch_commands
= NULL
;
5219 int retval
= EXIT_SUCCESS
;
5221 setlocale(LC_ALL
, "");
5223 /* Initialize gettext */
5224 bindtextdomain(PACKAGE
, LOCALEDIR
);
5225 textdomain(PACKAGE
);
5228 /* Default output */
5231 /* Parse arguments */
5233 while ((opt
= getopt(argc
, (char * const *)argv
, "c:e:V")) != -1) {
5236 config_file
= optarg
;
5239 batch_commands
= optarg
;
5243 shi_exit(EXIT_SUCCESS
);
5246 main_usage((argv
[0]) ? basename(argv
[0]): NULL
);
5247 shi_exit(EXIT_FAILURE
);
5252 if (shi_init(config_file
)) {
5253 shi_exit(EXIT_FAILURE
);
5256 /*shi_login(NULL);*/
5258 if (batch_commands
) {
5259 if (shi_batch(batch_commands
))
5260 retval
= EXIT_FAILURE
;