1 #define _XOPEN_SOURCE 600
6 #include <readline/readline.h>
7 #include <readline/history.h>
16 #include <sys/types.h>
20 #include "shigofumi.h"
21 #include "completition.h"
27 #define CONFIG_FILE ".shigofumirc"
28 #define CONFIG_SERVER "base_url"
29 #define CONFIG_USERNAME "username"
30 #define CONFIG_PASSWORD "password"
31 #define CONFIG_CERT_FORMAT "certificate_format"
32 #define CONFIG_CERT_PATH "certificate_path"
33 #define CONFIG_KEY_ENGINE "key_engine"
34 #define CONFIG_KEY_FORMAT "key_format"
35 #define CONFIG_KEY_PATH "key_path"
36 #define CONFIG_KEY_PASSWORD "key_password"
37 #define CONFIG_OTP_METHOD "otp_method"
38 #define CONFIG_OTP_CODE "otp_code"
39 #define CONFIG_DEBUGLEVEL "debug_level"
40 #define CONFIG_VERIFYSERVER "verify_server"
41 #define CONFIG_CAFILE "ca_file"
42 #define CONFIG_CADIRECTORY "ca_directory"
43 #define CONFIG_CLEAN_TEMPORARY_FILES "clean_temporary_files"
44 #define CONFIG_CRLFILE "crl_file"
45 #define CONFIG_TIMEOUT "timeout"
46 #define CONFIG_LOGFACILITIES "log_facilities"
47 #define CONFIG_LOGFILE "log_file"
48 #define CONFIG_LOGLEVEL "log_level"
49 #define CONFIG_MARKMESSAGEREAD "mark_message_read"
50 #define CONFIG_NORMALIZEMIMETYPE "normalize_mime_type"
51 #define CONFIG_OPENCOMMAND "open_command"
52 #define CONFIG_OVERWRITEFILES "overwrite_files"
53 #define CZPDEPOSIT_URL "https://www.czechpoint.cz/uschovna/"
59 cfg_opt_t configuration_syntax
[] = {
60 CFG_STR(CONFIG_SERVER
, NULL
, CFGF_NONE
),
61 CFG_STR(CONFIG_USERNAME
, NULL
, CFGF_NODEFAULT
),
62 CFG_STR(CONFIG_PASSWORD
, NULL
, CFGF_NODEFAULT
),
63 CFG_STR(CONFIG_CERT_FORMAT
, NULL
, CFGF_NODEFAULT
),
64 CFG_STR(CONFIG_CERT_PATH
, NULL
, CFGF_NODEFAULT
),
65 CFG_STR(CONFIG_KEY_ENGINE
, NULL
, CFGF_NODEFAULT
),
66 CFG_STR(CONFIG_KEY_FORMAT
, NULL
, CFGF_NODEFAULT
),
67 CFG_STR(CONFIG_KEY_PATH
, NULL
, CFGF_NODEFAULT
),
68 CFG_STR(CONFIG_KEY_PASSWORD
, NULL
, CFGF_NODEFAULT
),
69 CFG_STR(CONFIG_OTP_METHOD
, NULL
, CFGF_NODEFAULT
),
70 CFG_STR(CONFIG_OTP_CODE
, NULL
, CFGF_NODEFAULT
),
71 /*CFG_STR(CONFIG_DEBUGLEVEL, NULL, CFGF_NODEFAULT),*/
72 CFG_BOOL(CONFIG_VERIFYSERVER
, cfg_true
, CFGF_NONE
),
73 CFG_STR(CONFIG_CAFILE
, NULL
, CFGF_NODEFAULT
),
74 CFG_STR(CONFIG_CADIRECTORY
, NULL
, CFGF_NODEFAULT
),
75 CFG_BOOL(CONFIG_CLEAN_TEMPORARY_FILES
, cfg_true
, CFGF_NONE
),
76 CFG_STR(CONFIG_CRLFILE
, NULL
, CFGF_NODEFAULT
),
77 CFG_INT(CONFIG_TIMEOUT
, TIMEOUT
, CFGF_NONE
),
78 CFG_STR_LIST(CONFIG_LOGFACILITIES
, "{none}", CFGF_NONE
),
79 CFG_STR(CONFIG_LOGFILE
, NULL
, CFGF_NODEFAULT
),
80 CFG_INT(CONFIG_LOGLEVEL
, LOG_LEVEL
, CFGF_NONE
),
81 CFG_BOOL(CONFIG_MARKMESSAGEREAD
, cfg_false
, CFGF_NONE
),
82 CFG_BOOL(CONFIG_NORMALIZEMIMETYPE
, cfg_true
, CFGF_NONE
),
83 CFG_STR_LIST(CONFIG_OPENCOMMAND
, "{xdg-open, %f}", CFGF_NONE
),
84 CFG_BOOL(CONFIG_OVERWRITEFILES
, cfg_true
, CFGF_NONE
),
95 struct command (*commands
)[] = NULL
;
99 struct isds_ctx
*cisds
= NULL
;
100 struct isds_list
*boxes
= NULL
;
101 struct isds_message
*message
= NULL
;
102 _Bool messages_are_outgoing
= 0;
103 struct isds_list
*messages
= NULL
;
104 unsigned long int total_messages
= 0;
105 struct isds_ctx
*czechpoint
= NULL
;
106 struct isds_list
*temporary_files
= NULL
;
108 /* Temporary log-in settings */
110 char *username
= NULL
;
111 char *password
= NULL
;
112 char *key_password
= NULL
;
113 char *otp_method
= NULL
;
114 char *otp_code
= NULL
;
115 char *pki_engine
= NULL
;
116 char *pki_certificate_path
= NULL
;
117 char *pki_certificate_format
= NULL
;
118 char *pki_key_path
= NULL
;
119 char *pki_key_format
= NULL
;
121 static void discard_credentials(void) {
129 zfree(pki_certificate_path
);
130 zfree(pki_certificate_format
);
132 zfree(pki_key_format
);
136 /* Remove temporary file */
137 void shi_unlink_temporary_file(void **data
) {
138 if (!data
|| !*data
) return;
140 const char *file
= (const char *)*data
;
141 if (-1 == remove(file
)) {
142 fprintf(stderr
, _("Could not remove temporary file `%s': %s\n"),
143 file
, strerror(errno
));
149 /* Finish ISDS operation, report error, if the operation returned different
150 * code than @positive_code. */
151 static void finish_isds_operation_with_code(struct isds_ctx
*ctx
,
152 isds_error err
, isds_error positive_code
) {
153 shi_progressbar_finish();
154 if (err
!= positive_code
) {
155 if (isds_long_message(ctx
))
156 fprintf(stderr
, _("Error occurred: %s: %s\n"), isds_strerror(err
),
157 isds_long_message(ctx
));
159 fprintf(stderr
, _("Error occurred: %s\n"), isds_strerror(err
));
164 /* Finish ISDS operation, report error, if the operation did not returned
166 static void finish_isds_operation(struct isds_ctx
*ctx
, isds_error err
) {
167 finish_isds_operation_with_code(ctx
, err
, IE_SUCCESS
);
171 /* Do the cleanup and exit */
172 static void shi_exit(int exit_code
) {
174 discard_credentials();
175 isds_list_free(&boxes
);
176 isds_message_free(&message
);
177 isds_list_free(&messages
);
178 if (temporary_files
) {
179 oprintf(_("Removing temporary files...\n"));
180 isds_list_free(&temporary_files
);
185 oprintf(_("Logging out...\n"));
186 err
= isds_logout(cisds
);
187 finish_isds_operation(cisds
, err
);
188 if (err
) exit_code
= EXIT_FAILURE
;
189 isds_ctx_free(&cisds
);
191 isds_ctx_free(&czechpoint
);
195 cfg_free(configuration
);
204 /* Set prompt. if @format is NULL, switch to default prompt */
205 static void set_prompt(const char *format
, ...) {
209 va_start(ap
, format
);
210 shi_vasprintf(&buffer
, format
, ap
);
214 shi_asprintf(&prompt
, _("%s> "), buffer
);
223 prompt
= strdup(_("> "));
231 static int shi_load_configuration(const char *config_file
) {
232 char *config_name
= NULL
;
235 /* Get config file */
237 config_name
= (char *) config_file
;
239 if (-1 == shi_asprintf(&config_name
, "%s/%s", getenv("HOME"),
241 fprintf(stderr
, _("Could not build configuration file name\n"));
246 /* Parse configuration */
247 configuration
= cfg_init(configuration_syntax
, CFGF_NONE
);
248 ret
= cfg_parse(configuration
, config_name
);
250 if (ret
== CFG_FILE_ERROR
) {
252 _("Error while opening configuration file `%s': %s\n"),
253 config_name
, strerror(errno
));
255 fprintf(stderr
, _("Error while parsing configuration file `%s'\n"),
258 oprintf(_("Using default configuration\n"));
261 if (config_name
!= config_file
) free(config_name
);
266 void logger(isds_log_facility facility
, isds_log_level level
,
267 const char *message
, int length
, void *data
) {
269 ssize_t written
, left
= length
;
272 fd
= *((int *) data
);
273 /*printf("\033[32mLOG(%02d,%02d): ", facility, level);
274 printf("%.*s", length, message);
278 written
= write(fd
, message
+ length
- left
, left
);
281 _("Could not save log message into log file: %s\n"
282 "Log message discarded!\n"),
293 /* Redirect ISDS log to file if @file is not NULL. */
294 static int do_log_to_file(const char *file
) {
296 logger_fd
= open_file_for_writing(file
, 0, 1);
297 if (logger_fd
== -1) {
298 fprintf(stderr
, _("Could not redirect ISDS log to file `%s'\n"),
302 isds_set_log_callback(logger
, &logger_fd
);
308 /* Add log facility based on its name. */
309 static int add_log_facility(isds_log_facility
*facilities
, const char *name
) {
310 if (!facilities
) return -1;
312 if (!strcmp(name
, "none")) *facilities
|= ILF_NONE
;
313 else if (!strcmp(name
, "http")) *facilities
|= ILF_HTTP
;
314 else if (!strcmp(name
, "soap")) *facilities
|= ILF_SOAP
;
315 else if (!strcmp(name
, "isds")) *facilities
|= ILF_ISDS
;
316 else if (!strcmp(name
, "file")) *facilities
|= ILF_FILE
;
317 else if (!strcmp(name
, "sec")) *facilities
|= ILF_SEC
;
318 else if (!strcmp(name
, "xml")) *facilities
|= ILF_XML
;
319 else if (!strcmp(name
, "all")) *facilities
|= ILF_ALL
;
321 fprintf(stderr
, _("%s: Unknown log facility\n"), name
);
329 /* Save log facility into confuse configuration */
330 static void save_log_facility(int level
) {
331 cfg_setlist(configuration
, CONFIG_LOGFACILITIES
, 0);
333 if (level
== ILF_ALL
) {
334 cfg_addlist(configuration
, CONFIG_LOGFACILITIES
, 1, "all");
337 if (level
== ILF_NONE
) {
338 cfg_addlist(configuration
, CONFIG_LOGFACILITIES
, 1, "none");
341 if (level
& ILF_HTTP
)
342 cfg_addlist(configuration
, CONFIG_LOGFACILITIES
, 1, "http");
343 if (level
& ILF_SOAP
)
344 cfg_addlist(configuration
, CONFIG_LOGFACILITIES
, 1, "soap");
345 if (level
& ILF_ISDS
)
346 cfg_addlist(configuration
, CONFIG_LOGFACILITIES
, 1, "isds");
347 if (level
& ILF_FILE
)
348 cfg_addlist(configuration
, CONFIG_LOGFACILITIES
, 1, "file");
350 cfg_addlist(configuration
, CONFIG_LOGFACILITIES
, 1, "sec");
352 cfg_addlist(configuration
, CONFIG_LOGFACILITIES
, 1, "xml");
356 /* Clamp long int to unsigned int */
357 static unsigned int normalize_timeout(long int raw
) {
359 oprintf(_("Configured network timeout is less then 0. "
363 if (raw
> UINT_MAX
) {
364 oprintf(_("Configured network timeout is greater then %1$u. "
365 "Clamped to %1$u.\n"), UINT_MAX
);
368 return (unsigned int) raw
;
372 /* Clamp long int to <0;100> */
373 static unsigned int normalize_log_level(long int raw
) {
375 oprintf(_("Configured log level is less then 0. Clamped to 0.\n"));
379 oprintf(_("Configured log level is greater then %1$u. "
380 "Clamped to %1$u.\n"), ILL_ALL
);
383 if (raw
> UINT_MAX
) {
384 oprintf(_("Configured log level is greater then %1$u. "
385 "Clamped to %1$u.\n"), UINT_MAX
);
388 return (unsigned int) raw
;
392 static int shi_init(const char *config_file
) {
395 unsigned int timeout
, log_level
;
396 isds_log_facility log_facility
= ILF_NONE
;
398 oprintf(_("This is Shigofumi, an ISDS client. "
399 "Have a nice e-government.\n"));
401 /* Do not permute arguments in getopt() */
402 if (setenv("POSIXLY_CORRECT", "", 1)) {
404 _("Could not set POSIXLY_CORRECT environment variable\n"));
408 /* Load configuration */
409 if (shi_load_configuration(config_file
))
411 timeout
= normalize_timeout(cfg_getint(configuration
, CONFIG_TIMEOUT
));
412 log_level
= normalize_log_level(cfg_getint(configuration
, CONFIG_LOGLEVEL
));
415 rl_readline_name
= "shigofumi";
416 rl_filename_quote_characters
= "\\ >";
417 rl_filename_quoting_function
= shi_quote_filename
;
418 rl_filename_dequoting_function
= shi_dequote_filename
;
419 rl_char_is_quoted_p
= shi_char_is_quoted
;
421 /* Initialize ISDS */
424 fprintf(stderr
, _("Could not initialize libisds library: %s\n"),
429 /* Set ISDS logging */
430 value
= cfg_getstr(configuration
, CONFIG_LOGFILE
);
431 if (do_log_to_file(value
))
433 for (int i
= 0; i
< cfg_size(configuration
, CONFIG_LOGFACILITIES
); i
++) {
434 if (add_log_facility(&log_facility
,
435 cfg_getnstr(configuration
, CONFIG_LOGFACILITIES
, i
)))
439 isds_set_logging(log_facility
, log_level
);
441 /* Set ISDS context up */
442 cisds
= isds_ctx_create();
444 fprintf(stderr
, _("Could not create ISDS context\n"));
447 err
= isds_set_timeout(cisds
, timeout
);
449 fprintf(stderr
, _("Could not set ISDS network timeout: %s\n"),
452 err
= isds_set_progress_callback(cisds
, shi_progressbar
, NULL
);
454 fprintf(stderr
, _("Could not register network progress bar: %s: %s\n"),
455 isds_strerror(err
), isds_long_message(cisds
));
457 err
= isds_set_opt(cisds
, IOPT_NORMALIZE_MIME_TYPE
,
458 cfg_getbool(configuration
, CONFIG_NORMALIZEMIMETYPE
));
461 cfg_getbool(configuration
, CONFIG_NORMALIZEMIMETYPE
) ?
462 _("Could not enable MIME type normalization: %s: %s\n") :
463 _("Could not disable MIME type normalization: %s: %s\n"),
464 isds_strerror(err
), isds_long_message(cisds
));
466 if (!cfg_getbool(configuration
, CONFIG_VERIFYSERVER
)) {
467 oprintf(_("Warning: Shigofumi disabled server identity verification "
468 "on user request!\n"));
469 err
= isds_set_opt(cisds
, IOPT_TLS_VERIFY_SERVER
, 0);
472 _("Could not disable server identity verification: "
474 isds_strerror(err
), isds_long_message(cisds
));
477 if ((value
= cfg_getstr(configuration
, CONFIG_CAFILE
))) {
478 err
= isds_set_opt(cisds
, IOPT_TLS_CA_FILE
, value
);
481 _("Could not set file with CA certificates: %s: %s: %s\n"),
482 value
, isds_strerror(err
), isds_long_message(cisds
));
485 if ((value
= cfg_getstr(configuration
, CONFIG_CADIRECTORY
))) {
486 err
= isds_set_opt(cisds
, IOPT_TLS_CA_DIRECTORY
, value
);
489 _("Could not set directory with CA certificates: "
491 value
, isds_strerror(err
), isds_long_message(cisds
));
494 if ((value
= cfg_getstr(configuration
, CONFIG_CRLFILE
))) {
495 err
= isds_set_opt(cisds
, IOPT_TLS_CRL_FILE
, value
);
497 fprintf(stderr
, _("Could not set file with CRL: %s: %s: %s\n"),
498 value
, isds_strerror(err
), isds_long_message(cisds
));
503 /* Set Czech POINT context up */
504 czechpoint
= isds_ctx_create();
506 fprintf(stderr
, _("Could not create Czech POINT context\n"));
509 err
= isds_set_timeout(czechpoint
, timeout
);
511 fprintf(stderr
, _("Could not set Czech POINT network timeout: %s\n"),
514 err
= isds_set_progress_callback(czechpoint
, shi_progressbar
, NULL
);
516 fprintf(stderr
, "Could not register network progress bar: %s: %s\n",
517 isds_strerror(err
), isds_long_message(cisds
));
524 static int shi_quit(int argc
, const char **argv
) {
525 shi_exit(EXIT_SUCCESS
);
531 static void shi_help_usage(const char *command
) {
533 "Usage: %s [COMMAND]\n"
534 "Show COMMAND manual or list of currently available commands.\n"
540 static int shi_help(int argc
, const char **argv
) {
541 size_t command_width
= 14;
545 fprintf(stderr
, _("No command is available\n"));
549 if (argc
== 2 && argv
[1] && *argv
[1]) {
550 /* Show usage for given command */
551 for (i
= 0; (*commands
)[i
].name
; i
++) {
552 if (!strcmp((*commands
)[i
].name
, argv
[1])) {
553 if ((*commands
)[i
].usage
)
554 (*commands
)[i
].usage((*commands
)[i
].name
);
555 else if ((*commands
)[i
].description
) {
556 ohprint((*commands
)[i
].name
, command_width
);
557 oprintf(" %s\n", _((*commands
)[i
].description
));
561 _("%s: %s: Command description not defined\n"),
566 fprintf(stderr
, _("%s: %s: No such command exists\n"), argv
[0], argv
[1]);
570 /* Or list all commands */
571 oprintf(_("Following commands are available:\n"));
572 for (i
= 0; (*commands
)[i
].name
; i
++) {
573 ohprint((*commands
)[i
].name
, command_width
);
574 oprintf(" %s\n", _((*commands
)[i
].description
));
581 static void show_version(void) {
582 char *libisds_version
= isds_version();
584 oprintf(_("This is Shigofumi version %s.\n"), PACKAGE_VERSION
);
590 rl_library_version
, libisds_version
);
591 free(libisds_version
);
595 static int shi_version(int argc
, const char **argv
) {
600 "It's a shigofumi. A letter delivered from the afterlife. (Fumika)\n"
601 "A message can not be delivered to dead person. (ISDS specification)\n"
602 "Virtual and real world. They can be compatible. (Program author)\n"
608 static int shi_copying(int argc
, const char **argv
) {
610 "This is Shigofumi, an ISDS client.\n"
611 "Copyright (C) 2010, 2011, 2012 Petr Pisar\n"
613 "This program is free software: you can redistribute it and/or modify\n"
614 "it under the terms of the GNU General Public License as published by\n"
615 "the Free Software Foundation, either version 3 of the License, or\n"
616 "(at your option) any later version.\n"
618 "This program is distributed in the hope that it will be useful,\n"
619 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
620 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
621 "GNU General Public License for more details.\n"
623 "You should have received a copy of the GNU General Public License\n"
624 "along with this program. If not, see <http://www.gnu.org/licenses/>.\n"
630 static int shi_cache(int argc
, const char **argv
) {
631 const struct isds_list
*item
;
635 for (item
= boxes
, i
= 0; item
; item
= item
->next
, i
++);
637 "Cached box list: %zu\n"),
643 "Cached message list:\n"
645 "\tMessages: %'lu\n"),
646 (messages_are_outgoing
) ? _("Outgoing") : _("Incoming"),
651 oprintf(_("Cached message: %s\n"),
652 (message
->envelope
&& message
->envelope
->dmID
) ?
653 message
->envelope
->dmID
: _("<Unknown ID>"));
660 static void shi_chdir_usage(const char *command
) {
662 "Usage: %s [DIRECTORY]\n"
663 "Change working directory to DIRECTORY.\n"
664 "If no DIRECTORY is supplied, HOME directory will be used.\n"),
669 static int shi_chdir(int argc
, const char **argv
) {
670 const char *directory
= NULL
;
672 if (!argv
|| argc
> 2) {
673 shi_chdir_usage((argv
) ? argv
[0] : NULL
);
677 if (argc
== 2 && argv
[1] && *argv
[1])
680 directory
= getenv("HOME");
682 oprintf("Environment variable HOME does not exist\n");
686 if (chdir(directory
)) {
687 oprintf(_("Could not change working directory: %s: %s\n"), directory
,
696 static int shi_pwd(int argc
, const char **argv
) {
697 char *buffer
= NULL
, *newbuffer
;
700 while (length
+= 1024) {
701 newbuffer
= realloc(buffer
, length
);
703 fprintf(stderr
, _("Error: Not enough memory\n"));
709 if (getcwd(buffer
, length
)) {
710 oprintf("%s\n", buffer
);
716 fprintf(stderr
, _("Error: Current directory string is too long\n"));
722 /* Deallocate *@destination and duplicate @new_value if non-NULL. In case of
723 * error, it prints error message and returns -1. Otherwise it returns 0. */
724 static int replace_string(char **destination
, const char *new_value
) {
725 if (destination
== NULL
) return -1;
727 if (new_value
!= NULL
) {
728 *destination
= strdup(new_value
);
729 if (*destination
== NULL
) {
730 fprintf(stderr
, _("Not enough memory\n"));
738 /* Convert name of PKI format into ISDS format type.
739 * Return -1 in case of invalid name */
740 static int string2pki_format(const char *name
, isds_pki_format
*format
) {
741 if (!name
|| !format
) { return -1; }
742 if (!strcasecmp(name
, "PEM")) {
743 *format
= PKI_FORMAT_PEM
;
744 } else if (!strcasecmp(name
, "DER")) {
745 *format
= PKI_FORMAT_DER
;
746 } else if (!strcasecmp(name
, "ENG")) {
747 *format
= PKI_FORMAT_ENG
;
755 /* Convert name of OTP authentication method into ISDS method type.
756 * Return -1 in case of invalid name */
757 static int string2otp_method(const char *name
, isds_otp_method
*method
) {
758 if (!name
|| !method
) { return -1; }
759 if (!strcasecmp(name
, "HOTP")) {
761 } else if (!strcasecmp(name
, "TOTP")) {
771 * Return -1 in case of failure, 0 in case of success, +1 in case of partial
772 * success (e.g. TOTP preauthentication to obtain new code succeeded, but user
773 * is still not logged in because second phase is necessary). */
774 static int do_login(void) {
776 struct isds_pki_credentials pki
;
779 /* Build OTP structure */
780 if (NULL
!= otp_method
) {
781 if (string2otp_method(otp_method
, &otp
.method
)) {
782 fprintf(stderr
, _("Error: Invalid one-time password "
783 "authentication method `%s'\n"), otp_method
);
788 /* Announce base URL */
789 oprintf(_("ISDS base URL: %s\n"),
790 (server
== NULL
) ? _("<default>") : server
);
793 oprintf(_("Unattended mode detected. "
794 "Make sure credentials have been preset.\n"));
796 oprintf(_("You are going to insert credentials for your account.\n"
797 "Leave blank line to choose default value.\n"));
799 select_completition(COMPL_NONE
);
801 /* Ask for user name if not predefined */
802 if (NULL
== username
) {
803 shi_ask_for_string(&username
, _("Input ISDS user name: "),
804 cfg_getstr(configuration
, CONFIG_USERNAME
), batch_mode
);
807 /* Ask for password */
808 shi_ask_for_password(&password
, _("Input ISDS password: "),
809 cfg_getstr(configuration
, CONFIG_PASSWORD
), batch_mode
);
811 /* Ask for key password if PKI authentication requested */
812 if (pki_certificate_path
) {
813 shi_ask_for_password(&key_password
, _("Input private key password: "),
814 cfg_getstr(configuration
, CONFIG_KEY_PASSWORD
), batch_mode
);
817 /* Ask for OTP code if OTP authentication requested */
818 if (NULL
!= otp_method
) {
819 shi_ask_for_password(&otp_code
,
820 (otp
.method
== OTP_TIME
) ?
821 _("Input one-time code (empty to send new one): ") :
822 _("Input one-time code: "),
823 cfg_getstr(configuration
, CONFIG_OTP_CODE
), batch_mode
);
824 otp
.otp_code
= otp_code
;
827 select_completition(COMPL_COMMAND
);
830 /* Build PKI structure */
831 if (pki_certificate_path
) {
832 pki
.engine
= pki_engine
;
833 if (NULL
== pki_certificate_format
) {
834 fprintf(stderr
, _("Error: No certficate format supplied\n"));
837 if (string2pki_format(pki_certificate_format
, &pki
.certificate_format
)) {
838 fprintf(stderr
, _("Error: Invalid certificate format `%s'\n"),
839 pki_certificate_format
);
842 if (NULL
== pki_key_format
) {
843 fprintf(stderr
, _("Error: No private key format supplied\n"));
846 if (string2pki_format(pki_key_format
, &pki
.key_format
)) {
847 fprintf(stderr
, _("Error: Invalid private key format `%s'\n"),
851 pki
.certificate
= pki_certificate_path
;
852 pki
.key
= pki_key_path
;
853 pki
.passphrase
= key_password
;
856 if (NULL
!= otp_method
&& OTP_TIME
== otp
.method
&& NULL
== otp_code
)
857 printf(_("Requesting one-time code from server for "
860 printf(_("Logging in...\n"));
861 err
= isds_login(cisds
, server
, username
, password
,
862 (NULL
!= pki_certificate_path
) ? &pki
: NULL
,
863 (NULL
!= otp_method
) ? &otp
: NULL
);
864 if (NULL
!= otp_method
&& OTP_TIME
== otp
.method
&& NULL
== otp_code
)
865 finish_isds_operation_with_code(cisds
, err
, IE_PARTIAL_SUCCESS
);
867 finish_isds_operation(cisds
, err
);
869 if (IE_PARTIAL_SUCCESS
== err
) {
870 printf(_("OTP code has been sent by ISDS successfully.\n"
871 "Once you receive the code, retry log-in with "
875 printf(_("Log-in failed\n"));
879 oprintf(_("Logged in.\n"));
884 static struct isds_DbOwnerInfo
*do_box(void) {
886 struct isds_DbOwnerInfo
*box
= NULL
;
888 printf(_("Getting box details you are logged in...\n"));
889 err
= isds_GetOwnerInfoFromLogin(cisds
, &box
);
890 finish_isds_operation(cisds
, err
);
896 static int shi_box(int argc
, const char **argv
) {
897 struct isds_DbOwnerInfo
*box
= NULL
;
902 format_DbOwnerInfo(box
);
904 isds_DbOwnerInfo_free(&box
);
909 /* Get info about box with @id.
910 * @id is UTF-8 encoded
911 * Return NULL in case of error, otherwise box description that caller must
913 static struct isds_DbOwnerInfo
*stat_box(const char *id
) {
915 struct isds_DbOwnerInfo criteria
;
916 struct isds_list
*boxes
= NULL
, *item
;
917 struct isds_DbOwnerInfo
*box
= NULL
;
918 char *id_locale
= NULL
;
920 if (!id
|| !*id
) return NULL
;
922 id_locale
= utf82locale(id
);
923 memset(&criteria
, 0, sizeof(criteria
));
924 criteria
.dbID
= (char *) id
;
926 printf(_("Getting details about box with ID `%s'...\n"), id_locale
);
927 err
= isds_FindDataBox(cisds
, &criteria
, &boxes
);
928 finish_isds_operation(cisds
, err
);
931 for(item
= boxes
; item
; item
= item
->next
) {
932 if (!item
->data
) continue;
935 fprintf(stderr
, _("Error: More boxes match ID `%s'\n"), id_locale
);
939 box
= (struct isds_DbOwnerInfo
*) item
->data
;
946 isds_list_free(&boxes
);
952 static void shi_commercialreceiving_usage(const char *command
) {
954 "Usage: %s [-0|-1] [BOX_ID]\n"
955 "Manipulate commercial receiving box status.\n"
956 " -O switch off receiving of commercial messages\n"
957 " -1 switch on receiving of commercial messages\n"
958 " BOX_ID affects box with ID BOX_ID; default is box you are logged in\n"
959 "If no option is given, show current commercial receiving status.\n"),
964 /* Manipulate commercial receiving box status */
965 static int shi_commercialreceiving(int argc
, const char **argv
) {
967 struct isds_DbOwnerInfo
*box
= NULL
;
970 char *box_id
= NULL
, *box_id_locale
= NULL
;
975 while ((opt
= getopt(argc
, (char * const *)argv
, "01")) != -1) {
984 shi_commercialreceiving_usage((argv
)?argv
[0]:NULL
);
988 if (optind
+ 1 < argc
) {
989 fprintf(stderr
, _("Bad invocation\n"));
990 shi_commercialreceiving_usage((argv
)?argv
[0]:NULL
);
994 if (!argv
[optind
] || !*argv
[optind
]) {
995 /* Get current box ID */
997 if (!box
|| !box
->dbID
|| !*box
->dbID
) {
998 isds_DbOwnerInfo_free(&box
);
999 fprintf(stderr
, _("Could not get current box ID\n"));
1002 box_id
= box
->dbID
; static_box_id
= 1;
1003 box_id_locale
= utf82locale(box_id
);
1005 /* Box ID supplied as argument */
1006 box_id_locale
= (char *) argv
[optind
];
1007 box_id
= locale2utf8(box_id_locale
); static_box_id
= 0;
1009 fprintf(stderr
, _("Could not convert box ID `%s' to UTF-8\n"),
1016 if (!box
) box
= stat_box(box_id
);
1018 fprintf(stderr
, _("Could not get details about box ID `%s'\n"),
1024 oprintf(_("Commercial receiving status of box `%s': "), box_id_locale
);
1025 if (!box
->dbOpenAddressing
)
1026 oprintf(_("Unknown\n"));
1027 else if (*box
->dbOpenAddressing
)
1028 oprintf(_("Positive\n"));
1030 oprintf(_("Negative\n"));
1032 char *refnumber
= NULL
;
1034 _("Switching `%s' box commercial receiving on...\n"):
1035 _("Switching `%s' box commercial receiving off...\n"),
1037 err
= isds_switch_commercial_receiving(cisds
, box_id
, action
,
1039 finish_isds_operation(cisds
, err
);
1042 char *refnumber_locale
= utf82locale(refnumber
);
1043 oprintf(_("Commercial receiving status successfully changed. "
1044 "Assigned reference number: %s\n"),
1046 free(refnumber_locale
);
1048 oprintf(_("Commercial receiving status has not been changed.\n"));
1055 if (!static_box_id
) free(box_id
);
1056 else free(box_id_locale
);
1057 isds_DbOwnerInfo_free(&box
);
1062 static void shi_commercialsending_usage(const char *command
) {
1064 "Usage: %s [BOX_ID]\n"
1065 "Retrieve permissions to send commercial messages from a box.\n"
1066 " BOX_ID query permissions for box with ID BOX_ID; default is box you\n"
1067 " are logged in\n"),
1072 /* Retrieve permissions to send commercial messages */
1073 static int shi_commercialsending(int argc
, const char **argv
) {
1075 struct isds_DbOwnerInfo
*box
= NULL
;
1076 struct isds_list
*permissions
= NULL
, *item
;
1078 char *box_id
= NULL
, *box_id_locale
= NULL
;
1079 _Bool static_box_id
;
1084 while ((opt
= getopt(argc
, (char * const *)argv
, "h")) != -1) {
1087 shi_commercialsending_usage((argv
)?argv
[0]:NULL
);
1090 shi_commercialsending_usage((argv
)?argv
[0]:NULL
);
1094 if (optind
+ 1 < argc
) {
1095 fprintf(stderr
, _("Bad invocation\n"));
1096 shi_commercialsending_usage((argv
)?argv
[0]:NULL
);
1100 if (!argv
[optind
] || !*argv
[optind
]) {
1101 /* Get current box ID */
1103 if (!box
|| !box
->dbID
|| !*box
->dbID
) {
1104 isds_DbOwnerInfo_free(&box
);
1105 fprintf(stderr
, _("Could not get current box ID\n"));
1108 box_id
= box
->dbID
; static_box_id
= 1;
1109 box_id_locale
= utf82locale(box_id
);
1111 /* Box ID supplied as argument */
1112 box_id_locale
= (char *) argv
[optind
];
1113 box_id
= locale2utf8(box_id_locale
); static_box_id
= 0;
1115 fprintf(stderr
, _("Could not convert box ID `%s' to UTF-8\n"),
1121 printf(_("Querying `%s' box commercial sending permissions...\n"),
1123 err
= isds_get_commercial_permissions(cisds
, box_id
, &permissions
);
1124 finish_isds_operation(cisds
, err
);
1127 oprintf(_("Permissions to send commercial messages from box `%s':\n"),
1129 for (item
= permissions
, ordinar
= 0; item
; item
=item
->next
) {
1130 if (!item
->data
) continue;
1132 oprintf(_("\n* Permission #%d:\n"), ordinar
);
1133 format_commercial_permission(item
->data
);
1136 oprintf(_("No permission exists.\n"));
1138 oprintf(_("Could not list permissions to send commercial messages "
1139 "from box `%s'.\n"), box_id_locale
);
1143 isds_list_free(&permissions
);
1144 if (!static_box_id
) free(box_id
);
1145 else free(box_id_locale
);
1146 isds_DbOwnerInfo_free(&box
);
1151 static int shi_user(int argc
, const char **argv
) {
1153 struct isds_DbUserInfo
*user
= NULL
;
1155 printf(_("Getting user details you are logged as...\n"));
1156 err
= isds_GetUserInfoFromLogin(cisds
, &user
);
1157 finish_isds_operation(cisds
, err
);
1160 format_DbUserInfo(user
);
1162 isds_DbUserInfo_free(&user
);
1167 static void shi_users_usage(const char *command
) {
1169 "Usage: %s BOX_ID\n"
1170 "Get list of users having access to box with BOX_ID.\n"),
1175 static int shi_users(int argc
, const char **argv
) {
1177 struct isds_list
*users
= NULL
, *item
;
1180 if (!argv
|| !argv
[1] || !*argv
[1]) {
1181 shi_users_usage((argv
)?argv
[0]:NULL
);
1185 printf(_("Getting users of box with ID `%s'...\n"), argv
[1]);
1186 err
= isds_GetDataBoxUsers(cisds
, argv
[1], &users
);
1187 finish_isds_operation(cisds
, err
);
1190 for (item
= users
, ordinar
= 0; item
; item
=item
->next
) {
1191 if (!item
->data
) continue;
1193 oprintf(_("\n* User #%d:\n"), ordinar
);
1194 format_DbUserInfo(item
->data
);
1197 oprintf(_("Empty list of users returned.\n"));
1199 isds_list_free(&users
);
1204 static int show_password_expiration(void) {
1206 struct timeval
*expiration
= NULL
;
1208 err
= isds_get_password_expiration(cisds
, &expiration
);
1209 finish_isds_operation(cisds
, err
);
1211 fprintf(stderr
, "Could not get password expiration time\n");
1215 print_header_timeval(_("Your password expires at"), expiration
);
1221 /* Change password in ISDS */
1222 static int do_passwd(void) {
1223 char *old_password
= NULL
;
1224 char *new_password
= NULL
;
1225 char *new_password2
= NULL
;
1226 struct isds_otp otp
;
1227 char *refnumber
= NULL
;
1228 isds_error err
= IE_ERROR
;
1231 if (replace_string(&otp_method
, cfg_getstr(configuration
, CONFIG_OTP_METHOD
)))
1233 /* Build OTP structure */
1234 if (NULL
!= otp_method
) {
1235 if (string2otp_method(otp_method
, &otp
.method
)) {
1236 fprintf(stderr
, _("Error: Invalid one-time password "
1237 "authentication method `%s'\n"), otp_method
);
1242 select_completition(COMPL_NONE
);
1245 "You are going to change your password. If you don't want to change your\n"
1246 "password, insert empty string or EOF.\n"
1248 "You will be asked for your current (old) password and then for new password.\n"
1249 "ISDS forces some criteria new password must fulfill. Current rules are:\n"
1250 "\tLength: minimal 8, maximal 32 characters\n"
1251 "\tMust contain at least: 1 upper case letter, 1 lower case letter, 1 digit\n"
1252 "\tAllowed alphabet: [a-z][A-Z][0-9][!#$%%&()*+,-.:=?@[]_{}|~]\n"
1253 "\tMust differ from last 255 passwords\n"
1254 "\tMust not contain user ID\n"
1255 "\tMust not contain sequence of three or more same characters\n"
1256 "\tMust not start with `qwert', `asdgf', or `12345'\n"
1257 "Finally, you must repeat your new password to avoid mistakes.\n"
1258 "After password change will be confirmed, you must log in again as password\n"
1259 "is transmitted to server on each request.\n"
1262 old_password
= ask_for_password(_("Old password: "));
1263 if (!old_password
|| *old_password
== '\0') {
1264 fprintf(stderr
, _("No password supplied\n"));
1268 /* Ask for OTP code if OTP authentication requested */
1270 if (NULL
!= otp_method
) {
1271 shi_ask_for_password(&otp_code
,
1272 (otp
.method
== OTP_TIME
) ?
1273 _("One-time code (empty to send new one): ") :
1274 _("One-time code: "),
1275 cfg_getstr(configuration
, CONFIG_OTP_CODE
), batch_mode
);
1276 otp
.otp_code
= otp_code
;
1280 if (NULL
== otp_method
|| NULL
!= otp_code
) {
1281 new_password
= ask_for_password(_("New password: "));
1282 if (!new_password
|| *new_password
== '\0') {
1283 fprintf(stderr
, _("No password supplied\n"));
1287 new_password2
= ask_for_password(_("Repeat new password: "));
1288 if (!new_password2
|| new_password2
== '\0') {
1289 fprintf(stderr
, _("No password supplied\n"));
1293 if (strcmp(new_password
, new_password2
)) {
1294 fprintf(stderr
, _("New passwords differ\n"));
1299 if (NULL
!= otp_method
&& OTP_TIME
== otp
.method
&& NULL
== otp_code
)
1300 printf(_("Requesting one-time code from server for "
1301 "a password change...\n"));
1303 printf(_("Changing password...\n"));
1304 err
= isds_change_password(cisds
, old_password
, new_password
,
1305 (NULL
!= otp_method
) ? &otp
: NULL
, &refnumber
);
1306 if (NULL
!= otp_method
&& OTP_TIME
== otp
.method
&& NULL
== otp_code
)
1307 finish_isds_operation_with_code(cisds
, err
, IE_PARTIAL_SUCCESS
);
1309 finish_isds_operation(cisds
, err
);
1311 if (NULL
!= refnumber
) {
1312 char *refnumber_locale
= utf82locale(refnumber
);
1314 oprintf(_("Assigned reference number: %s\n"), refnumber_locale
);
1315 free(refnumber_locale
);
1317 if (IE_PARTIAL_SUCCESS
== err
) {
1318 oprintf(_("OTP code has been sent by ISDS successfully.\n"
1319 "Once you receive the code, retry changing password with "
1323 printf(_("Password change failed\n"));
1326 oprintf(_("Password HAS been successfully changed.\n"));
1332 oprintf(_("Password has NOT been changed!\n"));
1337 free(new_password2
);
1341 select_completition(COMPL_COMMAND
);
1344 "Remember, ISDS password has limited life time.\n"));
1349 static void shi_passwd_usage(const char *command
) {
1352 "Manipulate user password or change it if no option given.\n"
1355 " -S show password expiration time\n"
1360 static int shi_passwd(int argc
, const char **argv
) {
1364 while ((opt
= getopt(argc
, (char * const *)argv
, "S")) != -1) {
1367 return show_password_expiration();
1369 shi_passwd_usage(argv
[0]);
1373 if (optind
!= argc
|| argc
> 1) {
1374 fprintf(stderr
, _("Bad invocation\n"));
1375 shi_passwd_usage((argv
)?argv
[0]:NULL
);
1383 static void shi_login_usage(const char *command
) {
1385 "Usage: %s [OPTIONS] [USER_NAME]\n"
1386 "Attemp to log into ISDS server.\n"
1389 " -b URL ISDS server base URL\n"
1390 " -c IDENTIFIER user certificate\n"
1391 " -C FORMAT user certificate format\n"
1392 " -k IDENTIFIER user private key\n"
1393 " -K FORMAT user private key format\n"
1394 " -e IDENTIFIER cryptographic engine\n"
1395 " -o METHOD use one-time password authentication method\n"
1397 "Recognized certificate and key FORMATS are:\n"
1398 " PEM Base64 encoded serialization in local file\n"
1399 " DER binary serialization in local file\n"
1400 " ENG material is stored in cryptographic engine\n"
1401 "Identifiers of cryptographic engine, certificate, and private key are\n"
1402 "specific for underlying cryptographic library.\n"
1404 "Recognized one-time password methods are:\n"
1405 " HOTP HMAC-based OTP method\n"
1406 " TOTP time-based OTP method\n"
1408 "Values of omitted options are taken from configuration file.\n"
1413 static int shi_login(int argc
, const char **argv
) {
1417 discard_credentials();
1419 /* Load stored configuration */
1420 if (replace_string(&server
, cfg_getstr(configuration
, CONFIG_SERVER
)))
1422 if (replace_string(&otp_method
, cfg_getstr(configuration
, CONFIG_OTP_METHOD
)))
1424 if (replace_string(&pki_engine
,
1425 cfg_getstr(configuration
, CONFIG_KEY_ENGINE
)))
1427 if (replace_string(&pki_certificate_path
,
1428 cfg_getstr(configuration
, CONFIG_CERT_PATH
)))
1430 if (replace_string(&pki_certificate_format
,
1431 cfg_getstr(configuration
, CONFIG_CERT_FORMAT
)))
1433 if (replace_string(&pki_key_path
,
1434 cfg_getstr(configuration
, CONFIG_KEY_PATH
)))
1436 if (replace_string(&pki_key_format
,
1437 cfg_getstr(configuration
, CONFIG_KEY_FORMAT
)))
1440 /* Override configuration with positional arguments */
1442 while ((opt
= getopt(argc
, (char * const *)argv
, "b:c:C:k:K:e:o:")) != -1) {
1445 if (replace_string(&server
, optarg
)) return -1;
1448 if (replace_string(&pki_certificate_path
, optarg
)) return -1;
1451 if (replace_string(&pki_certificate_format
, optarg
)) return -1;
1454 if (replace_string(&pki_key_path
, optarg
)) return -1;
1457 if (replace_string(&pki_key_format
, optarg
)) return -1;
1460 if (replace_string(&pki_engine
, optarg
)) return -1;
1463 if (replace_string(&otp_method
, optarg
)) return -1;
1466 shi_login_usage(argv
[0]);
1470 if (optind
< argc
- 1) {
1471 fprintf(stderr
, _("Bad invocation\n"));
1472 shi_login_usage(argv
[0]);
1475 if (optind
== argc
- 1) {
1476 username
= strdup(argv
[optind
]);
1479 /* Proceed log-in */
1480 status
= do_login();
1481 if (status
< 0) return -1;
1483 /* If log-in passed, store configuration */
1484 cfg_setstr(configuration
, CONFIG_SERVER
, server
);
1485 cfg_setstr(configuration
, CONFIG_USERNAME
, username
);
1486 cfg_setstr(configuration
, CONFIG_PASSWORD
, password
);
1487 cfg_setstr(configuration
, CONFIG_KEY_PASSWORD
, key_password
);
1488 cfg_setstr(configuration
, CONFIG_OTP_METHOD
, otp_method
);
1489 cfg_setstr(configuration
, CONFIG_KEY_ENGINE
, pki_engine
);
1490 cfg_setstr(configuration
, CONFIG_CERT_PATH
, pki_certificate_path
);
1491 cfg_setstr(configuration
, CONFIG_CERT_FORMAT
, pki_certificate_format
);
1492 cfg_setstr(configuration
, CONFIG_KEY_PATH
, pki_key_path
);
1493 cfg_setstr(configuration
, CONFIG_KEY_FORMAT
, pki_key_format
);
1495 /* Get some details only if fully logged in */
1497 show_password_expiration();
1503 static void shi_debug_usage(const char *command
) {
1505 "Usage: %s -l LEVEL [-f FACILITY...] [{-e | -o FILE}]\n"
1506 "Debug FACILITIES on LEVEL.\n"
1508 "-l LEVEL set log level, valid interval <%d,%d>, default is %d\n"
1509 " %d is no logging, %d critical, %d errors,\n"
1510 " %d warnings, %d info, %d debug, %d all\n"
1511 "-f FACILITY debug only given facility, repeat this option to debug\n"
1512 " more facilities; valid values: none, http, soap, isds,\n"
1513 " file, sec, xml, all; default is none\n"
1514 "-e write debug log into stderr\n"
1515 "-o FILE append debug log to FILE\n"
1518 ILL_NONE
, ILL_ALL
, ILL_NONE
,
1519 ILL_NONE
, ILL_CRIT
, ILL_ERR
, ILL_WARNING
,
1520 ILL_INFO
, ILL_DEBUG
, ILL_ALL
);
1523 static int shi_debug(int argc
, const char **argv
) {
1525 int log_level
= ILL_NONE
;
1526 isds_log_facility log_facility
= ILF_NONE
;
1528 _Bool close_log
= 0;
1531 while ((opt
= getopt(argc
, (char * const *)argv
, "l:f:eo:")) != -1) {
1534 log_level
= normalize_log_level(atoi(optarg
));
1537 if (add_log_facility(&log_facility
, optarg
)) return -1;
1545 shi_debug_usage(argv
[0]);
1549 if (optind
== 1 || optind
!= argc
) {
1550 fprintf(stderr
, _("Bad invocation\n"));
1551 shi_debug_usage(argv
[0]);
1557 isds_set_log_callback(NULL
, NULL
);
1559 if (logger_fd
!= -1) {
1560 if (-1 == close(logger_fd
)) {
1561 fprintf(stderr
, _("Closing log file failed: %s\n"),
1566 cfg_setstr(configuration
, CONFIG_LOGFILE
, NULL
);
1568 if (do_log_to_file(file
))
1570 if (file
) cfg_setstr(configuration
, CONFIG_LOGFILE
, file
);
1572 /* Set log levels */
1573 isds_set_logging(log_facility
, log_level
);
1574 cfg_setint(configuration
, CONFIG_LOGLEVEL
, log_level
);
1575 save_log_facility(log_facility
);
1581 static void show_setting_str(const char *variable
, _Bool cenzore
) {
1582 if (!variable
) return;
1584 const char *value
= cfg_getstr(configuration
, variable
);
1588 oprintf(_("%s = <set>\n"), variable
);
1590 oprintf(_("%s = `%s'\n"), variable
, value
);
1592 oprintf(_("%s = <unset>\n"), variable
);
1597 static void show_setting_boolean(const char *variable
) {
1598 if (!variable
) return;
1600 _Bool value
= cfg_getbool(configuration
, variable
);
1603 oprintf(_("%s = <true>\n"), variable
);
1605 oprintf(_("%s = <false>\n"), variable
);
1610 static void show_setting_int(const char *variable
) {
1611 if (!variable
) return;
1613 long int value
= cfg_getint(configuration
, variable
);
1615 oprintf(_("%s = %ld\n"), variable
, value
);
1619 static void show_setting_strlist(const char *variable
) {
1620 if (!variable
) return;
1622 int length
= cfg_size(configuration
, variable
);
1625 oprintf(_("%s = <unset>\n"), variable
);
1627 oprintf(_("%s = {"), variable
);
1628 for (int i
= 0; i
< length
; i
++) {
1629 const char *value
= cfg_getnstr(configuration
, variable
, i
);
1631 oprintf(_("`%s', "), value
);
1633 oprintf(_("`%s'}\n"), value
);
1639 static int shi_settings(int argc
, const char **argv
) {
1641 int log_level = ILL_NONE;
1642 isds_log_facility log_facility = ILF_NONE;
1644 _Bool close_log = 0;*/
1648 while ((opt = getopt(argc, (char * const *)argv, "l:f:eo:")) != -1) {
1651 log_level = normalize_log_level(atoi(optarg));
1654 if (add_log_facility(&log_facility, optarg)) return -1;
1662 shi_debug_usage(argv[0]);
1666 if (optind == 1 || optind != argc) {
1667 printf(_("Bad invocation\n"));
1668 shi_debug_usage(argv[0]);
1673 oprintf(_("Current settings:\n"));
1675 show_setting_str(CONFIG_SERVER
, 0);
1676 show_setting_str(CONFIG_USERNAME
, 0);
1677 show_setting_str(CONFIG_PASSWORD
, 1),
1678 show_setting_str(CONFIG_CERT_FORMAT
, 0),
1679 show_setting_str(CONFIG_CERT_PATH
, 0),
1680 show_setting_str(CONFIG_KEY_ENGINE
, 0),
1681 show_setting_str(CONFIG_KEY_FORMAT
, 0),
1682 show_setting_str(CONFIG_KEY_PATH
, 0),
1683 show_setting_str(CONFIG_KEY_PASSWORD
, 1),
1684 show_setting_str(CONFIG_OTP_METHOD
, 0),
1685 show_setting_str(CONFIG_OTP_CODE
, 1),
1686 show_setting_boolean(CONFIG_VERIFYSERVER
);
1687 show_setting_str(CONFIG_CAFILE
, 0);
1688 show_setting_str(CONFIG_CADIRECTORY
, 0);
1689 show_setting_boolean(CONFIG_CLEAN_TEMPORARY_FILES
);
1690 show_setting_str(CONFIG_CRLFILE
, 0);
1691 show_setting_int(CONFIG_TIMEOUT
);
1692 show_setting_strlist(CONFIG_LOGFACILITIES
);
1693 show_setting_str(CONFIG_LOGFILE
, 0);
1694 show_setting_int(CONFIG_LOGLEVEL
);
1695 show_setting_boolean(CONFIG_MARKMESSAGEREAD
);
1696 show_setting_boolean(CONFIG_NORMALIZEMIMETYPE
);
1697 show_setting_strlist(CONFIG_OPENCOMMAND
);
1698 show_setting_boolean(CONFIG_OVERWRITEFILES
);
1704 static void shi_find_box_usage(const char *command
) {
1706 "Usage: %s {OPTION... | BOX_ID}\n"
1707 "Get information about box with BOX_ID or boxes meeting other criteria.\n"
1708 "Each search option requires an argument:\n"
1709 " -t box type; accepted values:\n"
1710 " FO Private individual\n"
1711 " PFO Self-employed individual\n"
1712 " PFO_ADVOK Lawyer\n"
1713 " PFO_DANPOR Tax advisor\n"
1714 " PFO_INSSPR Insolvency administrator\n"
1715 " PO Organisation\n"
1716 " PO_ZAK Organization based by law\n"
1717 " PO_REQ Organization based on request\n"
1718 " OVM Public authority\n"
1719 " OVM_NOTAR Notary\n"
1720 " OVM_EXEKUT Executor\n"
1721 " OVM_REQ Public authority based on request\n"
1722 " -j identity number\n"
1724 "Person name options:\n"
1728 " -b last name at birth\n"
1729 " -s subject name\n"
1732 " -d birth date (locale or full ISO 8601 date)\n"
1734 " -y birth county\n"
1740 " -z number in street\n"
1741 " -Z number in municipality\n"
1748 " -p phone number\n"
1750 " -r registry code\n"
1751 " -a box status; accepted values:\n"
1752 " ACCESSIBLE Accessible\n"
1753 " TEMP_INACCESSIBLE Temporary inaccessible\n"
1754 " NOT_YET_ACCESSIBLE Not yet accessible\n"
1755 " PERM_INACCESSIBLE Permanently inaccessible\n"
1756 " REMOVED Deleted\n"
1757 " -o act as public authority; boolean values: 0 is false, 1 is true\n"
1758 " -k receive commercial messages; boolean values\n"
1760 "Not all option combinations are meaningful or allowed. For example box\n"
1761 "type is always required (except direct box ID query).\n"
1762 "ISDS can refuse to answer to much broad query. Not all boxes are searchable\n"
1769 /* Allow reassignment */
1770 #define FILL_OR_LEAVE(variable, locale) { \
1772 (variable) = locale2utf8(locale); \
1773 if (!(variable)) { \
1774 fprintf(stderr, _("Error: Not enough memory\n")); \
1780 #define CALLOC_OR_LEAVE(structure) { \
1781 if (!(structure)) { \
1782 (structure) = calloc(1, sizeof(*(structure))); \
1783 if (!(structure)) { \
1784 fprintf(stderr, _("Error: Not enough memory\n")); \
1791 #define FILL_BOOLEAN_OR_LEAVE(variable, locale) { \
1793 (variable) = malloc(sizeof(*(variable))); \
1794 if (!(variable)) { \
1795 fprintf(stderr, _("Error: Not enough memory\n")); \
1799 if (!strcmp((locale), "0")) *(variable) = 0; \
1800 else if (!strcmp((locale), "1")) *(variable) = 1; \
1802 fprintf(stderr, _("%s: %s: Unknown boolean value\n"), \
1803 argv[0], (locale)); \
1809 #define FILL_LONGINT_OR_LEAVE(variable, locale) { \
1810 if (!(locale) || !*(locale)) { \
1811 fprintf(stderr, _("%s: Empty integer value\n"), argv[0]); \
1817 (variable) = malloc(sizeof(*(variable))); \
1818 if (!(variable)) { \
1819 fprintf(stderr, _("Error: Not enough memory\n")); \
1823 (*variable) = strtol((locale), &endptr, 0); \
1825 fprintf(stderr, _("%s: %s: Invalid integer value\n"), \
1826 argv[0], (locale)); \
1832 static int shi_find_box(int argc
, const char **argv
) {
1835 struct isds_DbOwnerInfo
*criteria
= NULL
;
1836 struct isds_list
*item
;
1840 if (!argv
|| !argv
[1] || !*argv
[1]) {
1841 fprintf(stderr
, _("Error: No argument supplied\n"));
1842 shi_find_box_usage((argv
)?argv
[0]:NULL
);
1846 criteria
= calloc(1, sizeof(*criteria
));
1848 fprintf(stderr
, _("Error: Not enough memory\n"));
1855 while ((opt
= getopt(argc
, (char * const *)argv
, "t:j:s:"
1856 "f:m:l:b:s:" "d:w:y:c:" "W:S:z:Z:P:C:"
1857 "n:e:p:i:r:a:o:k:")) != -1) {
1860 criteria
->dbType
= malloc(sizeof(*criteria
->dbType
));
1861 if (!criteria
->dbType
) {
1862 fprintf(stderr
, _("Error: Not enough memory\n"));
1866 if (!strcmp(optarg
, "FO"))
1867 *criteria
->dbType
= DBTYPE_FO
;
1868 else if (!strcmp(optarg
, "PFO"))
1869 *criteria
->dbType
= DBTYPE_PFO
;
1870 else if (!strcmp(optarg
, "PFO_ADVOK"))
1871 *criteria
->dbType
= DBTYPE_PFO_ADVOK
;
1872 else if (!strcmp(optarg
, "PFO_DANPOR"))
1873 *criteria
->dbType
= DBTYPE_PFO_DANPOR
;
1874 else if (!strcmp(optarg
, "PFO_INSSPR"))
1875 *criteria
->dbType
= DBTYPE_PFO_INSSPR
;
1876 else if (!strcmp(optarg
, "PO"))
1877 *criteria
->dbType
= DBTYPE_PO
;
1878 else if (!strcmp(optarg
, "PO_ZAK"))
1879 *criteria
->dbType
= DBTYPE_PO_ZAK
;
1880 else if (!strcmp(optarg
, "PO_REQ"))
1881 *criteria
->dbType
= DBTYPE_PO_REQ
;
1882 else if (!strcmp(optarg
, "OVM"))
1883 *criteria
->dbType
= DBTYPE_OVM
;
1884 else if (!strcmp(optarg
, "OVM_NOTAR"))
1885 *criteria
->dbType
= DBTYPE_OVM_NOTAR
;
1886 else if (!strcmp(optarg
, "OVM_EXEKUT"))
1887 *criteria
->dbType
= DBTYPE_OVM_EXEKUT
;
1888 else if (!strcmp(optarg
, "OVM_REQ"))
1889 *criteria
->dbType
= DBTYPE_OVM_REQ
;
1891 fprintf(stderr
, _("%s: %s: Unknown box type\n"),
1899 FILL_OR_LEAVE(criteria
->ic
, optarg
);
1904 CALLOC_OR_LEAVE(criteria
->personName
);
1905 FILL_OR_LEAVE(criteria
->personName
->pnFirstName
, optarg
);
1908 CALLOC_OR_LEAVE(criteria
->personName
);
1909 FILL_OR_LEAVE(criteria
->personName
->pnMiddleName
, optarg
);
1912 CALLOC_OR_LEAVE(criteria
->personName
);
1913 FILL_OR_LEAVE(criteria
->personName
->pnLastName
, optarg
);
1916 CALLOC_OR_LEAVE(criteria
->personName
);
1917 FILL_OR_LEAVE(criteria
->personName
->pnLastNameAtBirth
, optarg
);
1920 FILL_OR_LEAVE(criteria
->firmName
, optarg
);
1925 CALLOC_OR_LEAVE(criteria
->birthInfo
);
1926 criteria
->birthInfo
->biDate
= datestring2tm(optarg
);
1927 if (!criteria
->birthInfo
->biDate
) {
1928 fprintf(stderr
, _("Error: Could not parse date: %s\n"),
1935 CALLOC_OR_LEAVE(criteria
->birthInfo
);
1936 FILL_OR_LEAVE(criteria
->birthInfo
->biCity
, optarg
);
1939 CALLOC_OR_LEAVE(criteria
->birthInfo
);
1940 FILL_OR_LEAVE(criteria
->birthInfo
->biCounty
, optarg
);
1943 CALLOC_OR_LEAVE(criteria
->birthInfo
);
1944 FILL_OR_LEAVE(criteria
->birthInfo
->biState
, optarg
);
1949 CALLOC_OR_LEAVE(criteria
->address
);
1950 FILL_OR_LEAVE(criteria
->address
->adCity
, optarg
);
1953 CALLOC_OR_LEAVE(criteria
->address
);
1954 FILL_OR_LEAVE(criteria
->address
->adStreet
, optarg
);
1957 CALLOC_OR_LEAVE(criteria
->address
);
1958 FILL_OR_LEAVE(criteria
->address
->adNumberInStreet
, optarg
);
1961 CALLOC_OR_LEAVE(criteria
->address
);
1962 FILL_OR_LEAVE(criteria
->address
->adNumberInMunicipality
,
1966 CALLOC_OR_LEAVE(criteria
->address
);
1967 FILL_OR_LEAVE(criteria
->address
->adZipCode
, optarg
);
1970 CALLOC_OR_LEAVE(criteria
->address
);
1971 FILL_OR_LEAVE(criteria
->address
->adState
, optarg
);
1976 FILL_OR_LEAVE(criteria
->nationality
, optarg
);
1979 FILL_OR_LEAVE(criteria
->email
, optarg
);
1982 FILL_OR_LEAVE(criteria
->telNumber
, optarg
);
1985 FILL_OR_LEAVE(criteria
->identifier
, optarg
);
1988 FILL_OR_LEAVE(criteria
->registryCode
, optarg
);
1991 criteria
->dbState
= malloc(sizeof(*criteria
->dbState
));
1992 if (!criteria
->dbState
) {
1993 fprintf(stderr
, _("Error: Not enough memory\n"));
1997 if (!strcmp(optarg
, "ACCESSIBLE"))
1998 *criteria
->dbState
= DBSTATE_ACCESSIBLE
;
1999 else if (!strcmp(optarg
, "TEMP_INACCESSIBLE"))
2000 *criteria
->dbState
= DBSTATE_TEMP_UNACCESSIBLE
;
2001 else if (!strcmp(optarg
, "NOT_YET_ACCESSIBLE"))
2002 *criteria
->dbState
= DBSTATE_NOT_YET_ACCESSIBLE
;
2003 else if (!strcmp(optarg
, "PERM_INACCESSIBLE"))
2004 *criteria
->dbState
= DBSTATE_PERM_UNACCESSIBLE
;
2005 else if (!strcmp(optarg
, "REMOVED"))
2006 *criteria
->dbState
= DBSTATE_REMOVED
;
2008 fprintf(stderr
, _("%s: %s: Unknown box status\n"),
2015 FILL_BOOLEAN_OR_LEAVE(criteria
->dbEffectiveOVM
, optarg
);
2018 FILL_BOOLEAN_OR_LEAVE(criteria
->dbOpenAddressing
, optarg
);
2022 shi_find_box_usage(argv
[0]);
2028 /* There must be an option and all of them must be recognized, if not only
2029 * BOX_ID supplied */
2030 if (argc
> 2 && optind
!= argc
) {
2031 fprintf(stderr
, _("Error: Superfluous argument\n"));
2032 shi_find_box_usage(argv
[0]);
2037 /* If only box ID is supplied use it */
2038 if (argc
== 2 && argv
[1] && *argv
[1]) {
2039 criteria
->dbID
= locale2utf8(argv
[1]);
2040 if (!criteria
->dbID
) {
2041 fprintf(stderr
, _("Error: Not enough memory\n"));
2047 printf(_("Searching boxes...\n"));
2048 err
= isds_FindDataBox(cisds
, criteria
, &boxes
);
2049 finish_isds_operation(cisds
, err
);
2052 for(item
= boxes
; item
; item
= item
->next
) {
2053 if (!item
->data
) continue;
2056 oprintf(_("\n* Result #%d:\n"), order
);
2057 format_DbOwnerInfo(item
->data
);
2061 isds_DbOwnerInfo_free(&criteria
);
2066 static void shi_stat_box_usage(const char *command
) {
2068 "Usage: %s BOX_ID...\n"
2069 "Get status of box with BOX_ID. More boxes can be specified.\n"),
2074 /* Get boxes status */
2075 static int shi_stat_box(int argc
, const char **argv
) {
2080 if (!argv
|| !*argv
|| argc
< 2 || !argv
[1]) {
2081 fprintf(stderr
, _("Missing box ID\n"));
2082 shi_stat_box_usage((argv
[0])?argv
[0]:NULL
);
2086 for (int i
= 1; i
< argc
; i
++) {
2087 if (!argv
[i
] || !*argv
[i
]) continue;
2090 id
= locale2utf8(argv
[i
]);
2092 fprintf(stderr
, _("%s: Could not covert box ID to UTF-8\n"),
2097 printf(_("Getting status of box `%s'...\n"), argv
[i
]);
2098 err
= isds_CheckDataBox(cisds
, id
, &status
);
2099 finish_isds_operation(cisds
, err
);
2102 oprintf(_("Status of box `%s': %s\n"),
2103 argv
[i
], DbState2string(&status
));
2110 static void shi_boxlist_usage(const char *command
) {
2112 "Usage: %s LIST_TYPE FILE\n"
2113 "Save latest snapshot of list of boxes of type LIST_TYPE into FILE.\n"
2115 "Currently recognized LIST_TYPES are:\n"
2117 " UPG Effectively OVM boxes\n"
2118 " OVM OVM gross type boxes\n"
2119 " OPN Boxes allowing receiving commercial messages\n"
2121 "Not all types are available to all users. E.g. only `UPG' is available\n"
2122 "to regular users.\n"
2124 "The format of the list is comma separate list that is packed into\n"
2125 "ZIP archive. Name of the list file denotes time of snapshoting\n"
2126 "the list. The snapshot is created by ISDS once a day.\n"),
2131 /* Download list of boxes */
2132 static int shi_boxlist(int argc
, const char **argv
) {
2134 const char *type_locale
;
2136 void *buffer
= NULL
;
2137 size_t buffer_length
;
2140 if (!argv
|| !*argv
|| argc
< 3 || !argv
[1] || !argv
[2]) {
2141 fprintf(stderr
, _("Bad number of arguments\n"));
2142 shi_boxlist_usage((argv
)?argv
[0]:NULL
);
2145 type_locale
= argv
[1];
2147 type
= locale2utf8(type_locale
);
2149 fprintf(stderr
, _("%s: Could not covert list type to UTF-8\n"),
2154 printf(_("Getting `%s' list of boxes...\n"), type_locale
);
2155 err
= isds_get_box_list_archive(cisds
, type
, &buffer
, &buffer_length
);
2156 finish_isds_operation(cisds
, err
);
2162 retval
= save_data_to_file(argv
[2], -1, buffer
, buffer_length
,
2164 cfg_getbool(configuration
, CONFIG_OVERWRITEFILES
));
2171 static void shi_compose_usage(const char *command
) {
2173 "Usage: %s OPTION...\n"
2174 "Compose and send a message to recipient defined by his box ID.\n"
2175 "Each option requires an argument (if not stated otherwise):\n"
2176 " -s * message subject\n"
2178 "Recipient options:\n"
2179 " -b * recipient box ID\n"
2180 " -U organisation unit name\n"
2181 " -N organisation unit number\n"
2182 " -P to hands of given person\n"
2184 "Sender organisation structure options:\n"
2185 " -I publish user's identity (NO argument allowed)\n"
2189 "Message identifier options:\n"
2190 " -r sender reference number\n"
2191 " -f sender file ID\n"
2192 " -R recipient reference number\n"
2193 " -F recipient file ID\n"
2195 "Legal title options:\n"
2196 " -y year act has been issued\n"
2197 " -a ordinal number of act in a year\n"
2198 " -e section of the act\n"
2199 " -o paragraph of the act\n"
2200 " -i point of the paragraph of the act\n"
2202 "Delivery options:\n"
2203 " -p personal delivery required\n"
2204 " -t allow substitutable delivery\n"
2205 " -A non-OVM sender acts as public authority\n"
2206 " -C commercial type; accepted values:\n"
2207 " K Commercial message paid by sender or sponsor\n"
2208 " I Initiatory commercial message offering to pay response\n"
2209 " O Commercial response paid by recipient\n"
2210 " V Public message paid by government\n"
2211 " Missing option defaults to K or O (see `commercialsending' command)\n"
2212 " if sending to non-OVM recipient with enabled commercial receiving,\n"
2213 " otherwise it defaults to V.\n"
2215 "Document options:\n"
2216 " -d * read document from local file\n"
2217 " -D document name (defaults to base local file name)\n"
2218 " -x transport subset of the document as a XML.\n"
2219 " Argument is XPath expression specifying desired node set\n"
2220 " -m override MIME type (guessed on -d)\n"
2221 " -g document ID (must be unique per message)\n"
2222 " -G reference to other document using its ID\n"
2223 " -c document is digital signature of other document (NO argument\n"
2226 "Options marked with asterisk are mandatory, other are optional. Another soft\n"
2227 "dependencies can emerge upon using specific option. They are not mandated by\n"
2228 "ISDS currently, but client library or this program can force them to assure\n"
2229 "semantically complete message. Following soft dependencies are recommended:\n"
2230 " -y <=> -a act number and year must be used at the same time\n"
2231 " -i => -o act point requires act paragraph\n"
2232 " -o => -e act paragraph requires act section\n"
2233 " -e => -a act section requires act number\n"
2234 " -G => -g document with referenced ID must exist\n"
2235 " -c => -G signature must refer to signed document\n"
2236 " -c first document cannot be signature\n"
2237 " -C I => -r sender reference number allows responder to reply to this message\n"
2238 " -C O -> -R recipient reference number must match sender reference number of\n"
2239 " initiatory message\n"
2241 "More documents can be attached to a message by repeating `-d' option.\n"
2242 "Document order will be preserved. Other document options affect immediately\n"
2243 "preceding `-d' document only. E.g. `-d /tmp/foo.pdf -m application/pdf\n"
2244 "-d /tmp/bar.txt -m text/plain' attaches first PDF file, then textual file.\n"
2246 "The same applies to recipient options that must start with box ID (-b).\n"
2247 "If more recipients specified, each of them will get a copy of composed\n"
2248 "message. ISDS will assign message identifier to each copy in turn.\n"
2254 static int shi_compose(int argc
, const char **argv
) {
2258 struct isds_message
*message
= NULL
;
2259 struct isds_envelope
*envelope
= NULL
;
2260 struct isds_list
*documents
= NULL
;
2261 struct isds_document
*document
= NULL
;
2262 struct isds_list
*copies
= NULL
, *copy_item
= NULL
;
2263 struct isds_message_copy
*copy
= NULL
;
2264 char *message_id_locale
= NULL
, *recipient_id_locale
= NULL
,
2265 *dmStatus_locale
= NULL
;
2267 if (!argv
|| !argv
[1] || !*argv
[1]) {
2268 fprintf(stderr
, _("Error: No argument supplied\n"));
2269 shi_compose_usage((argv
)?argv
[0]:NULL
);
2273 message
= calloc(1, sizeof(*message
));
2275 fprintf(stderr
, _("Error: Not enough memory\n"));
2279 envelope
= calloc(1, sizeof(*envelope
));
2281 fprintf(stderr
, _("Error: Not enough memory\n"));
2285 message
->envelope
= envelope
;
2289 while ((opt
= getopt(argc
, (char * const *)argv
, "s:" "b:U:N:P:" "Iu:n:"
2290 "r:f:R:F:" "y:a:e:o:i:" "p:t:A:C:" "d:D:x:m:g:G:c"
2294 FILL_OR_LEAVE(envelope
->dmAnnotation
, optarg
);
2297 /* Recipient options */
2301 /* First recipient */
2302 CALLOC_OR_LEAVE(copies
);
2303 copies
->destructor
=
2304 (void(*)(void **)) isds_message_copy_free
;
2307 /* Next recipient */
2308 CALLOC_OR_LEAVE(copy_item
->next
);
2309 copy_item
->next
->destructor
=
2310 (void(*)(void **)) isds_message_copy_free
;
2311 copy_item
= copy_item
->next
;
2313 CALLOC_OR_LEAVE(copy
);
2314 copy_item
->data
= copy
;
2316 /* Copy recipient box ID */
2317 FILL_OR_LEAVE(copy
->dbIDRecipient
, optarg
);
2322 _("Error: %s: Recipient box ID (-b) must precede "
2323 "recipient organisation unit name (-%c)\n"),
2328 FILL_OR_LEAVE(copy
->dmRecipientOrgUnit
, optarg
);
2333 _("Error: %s: Recipient box ID (-b) must precede "
2334 "recipient organisation unit number (-%c)\n"),
2339 FILL_LONGINT_OR_LEAVE(copy
->dmRecipientOrgUnitNum
, optarg
);
2344 _("Error: %s: Recipient box ID (-b) must precede "
2345 "to-hands option (-%c)\n"), optarg
, opt
);
2349 FILL_OR_LEAVE(copy
->dmToHands
, optarg
);
2352 /* Sender organisation structure options */
2354 FILL_BOOLEAN_OR_LEAVE(envelope
->dmPublishOwnID
, optarg
);
2356 FILL_OR_LEAVE(envelope
->dmSenderOrgUnit
, optarg
);
2359 FILL_LONGINT_OR_LEAVE(envelope
->dmSenderOrgUnitNum
, optarg
);
2362 /* Message identifier options */
2364 FILL_OR_LEAVE(envelope
->dmSenderRefNumber
, optarg
);
2367 FILL_OR_LEAVE(envelope
->dmSenderIdent
, optarg
);
2370 FILL_OR_LEAVE(envelope
->dmRecipientRefNumber
, optarg
);
2373 FILL_OR_LEAVE(envelope
->dmRecipientIdent
, optarg
);
2376 /* Legal title options */
2378 FILL_LONGINT_OR_LEAVE(envelope
->dmLegalTitleYear
, optarg
);
2381 FILL_LONGINT_OR_LEAVE(envelope
->dmLegalTitleLaw
, optarg
);
2384 FILL_OR_LEAVE(envelope
->dmLegalTitleSect
, optarg
);
2387 FILL_OR_LEAVE(envelope
->dmLegalTitlePar
, optarg
);
2390 FILL_OR_LEAVE(envelope
->dmLegalTitlePoint
, optarg
);
2393 /* Delivery options */
2395 FILL_BOOLEAN_OR_LEAVE(envelope
->dmPersonalDelivery
, optarg
);
2398 FILL_BOOLEAN_OR_LEAVE(envelope
->dmAllowSubstDelivery
, optarg
);
2401 FILL_BOOLEAN_OR_LEAVE(envelope
->dmOVM
, optarg
);
2404 FILL_OR_LEAVE(envelope
->dmType
, optarg
);
2407 /* Document options */
2411 /* First document */
2412 CALLOC_OR_LEAVE(message
->documents
);
2413 message
->documents
->destructor
=
2414 (void(*)(void **)) isds_document_free
;
2415 documents
= message
->documents
;
2416 CALLOC_OR_LEAVE(document
);
2417 documents
->data
= document
;
2418 document
->dmFileMetaType
= FILEMETATYPE_MAIN
;
2421 CALLOC_OR_LEAVE(documents
->next
);
2422 documents
->next
->destructor
=
2423 (void(*)(void **)) isds_document_free
;
2424 documents
= documents
->next
;
2425 CALLOC_OR_LEAVE(document
);
2426 documents
->data
= document
;
2427 document
->dmFileMetaType
= FILEMETATYPE_ENCLOSURE
;
2430 if (load_data_from_file(optarg
, &document
->data
,
2431 &document
->data_length
, &document
->dmMimeType
)) {
2435 /* XXX: POSIX basename() modifies argument */
2436 FILL_OR_LEAVE(document
->dmFileDescr
, basename(optarg
));
2441 _("Error: %s: Document file (-d) must precede "
2442 "document name (-%c)\n"), optarg
, opt
);
2446 FILL_OR_LEAVE(document
->dmFileDescr
, optarg
);
2451 _("Error: %s: Document file (-d) must precede "
2452 "XPath expression (-%c)\n"), optarg
, opt
);
2456 /* Load XML node list */
2457 char *xpath_expr
= NULL
;
2458 FILL_OR_LEAVE(xpath_expr
, optarg
);
2459 retval
= load_xml_subtree_from_memory(
2460 document
->data
, document
->data_length
,
2461 &document
->xml_node_list
, xpath_expr
);
2466 /* Switch document type to XML */
2467 document
->is_xml
= 1;
2468 zfree(document
->data
);
2469 document
->data_length
= 0;
2470 documents
->destructor
=
2471 (void(*)(void **)) free_document_with_xml_node_list
;
2476 _("Error: %s: Document file (-d) must precede "
2477 "MIME type (-%c)\n"), optarg
, opt
);
2481 FILL_OR_LEAVE(document
->dmMimeType
, optarg
);
2486 _("Error: %s: Document file (-d) must precede "
2487 "document ID (-%c)\n"), optarg
, opt
);
2491 FILL_OR_LEAVE(document
->dmFileGuid
, optarg
);
2496 _("Error: %s: Document file (-d) must precede "
2497 "document reference (-%c)\n"), optarg
, opt
);
2501 FILL_OR_LEAVE(document
->dmUpFileGuid
, optarg
);
2506 _("Error: Document file (-d) must precede "
2507 "document signature type (-%c)\n"), opt
);
2511 document
->dmFileMetaType
= FILEMETATYPE_SIGNATURE
;
2515 shi_compose_usage(argv
[0]);
2521 /* All options must be recognized */
2522 if (optind
!= argc
) {
2523 fprintf(stderr
, _("Error: Superfluous argument\n"));
2524 shi_compose_usage(argv
[0]);
2530 fprintf(stderr
, _("Error: No recipient box ID specified\n"));
2531 shi_compose_usage(argv
[0]);
2536 /* TODO: Check Legal Title soft dependencies */
2539 oprintf(_("Following message has been composed:\n"));
2540 format_message(message
);
2541 oprintf(_("Following recipients have been specified:\n"));
2542 format_copies(copies
);
2543 /* TODO: Confirmation, correction */
2545 /* Send a message */
2546 printf(_("Sending message...\n"));
2547 err
= isds_send_message_to_multiple_recipients(cisds
, message
, copies
);
2548 finish_isds_operation(cisds
, err
);
2549 if (err
&& err
!= IE_PARTIAL_SUCCESS
) {
2554 /* Show results for each copy */
2555 for (copy_item
= copies
; copy_item
; copy_item
= copy_item
->next
) {
2556 if (!copy_item
->data
) continue;
2557 copy
= (struct isds_message_copy
*) copy_item
->data
;
2558 recipient_id_locale
= utf82locale(copy
->dbIDRecipient
);
2562 if (copy
->dmStatus
) dmStatus_locale
= utf82locale(copy
->dmStatus
);
2563 if (dmStatus_locale
)
2564 oprintf(_("%s: Failed: %s: %s\n"),
2565 recipient_id_locale
,
2566 isds_strerror(copy
->error
), dmStatus_locale
);
2568 oprintf(_("%s: Failed: %s\n"), recipient_id_locale
,
2569 isds_strerror(copy
->error
));
2570 zfree(dmStatus_locale
);
2572 message_id_locale
= utf82locale(copy
->dmID
);
2573 oprintf(_("%s: Succeeded. Assigned message ID: %s\n"),
2574 recipient_id_locale
, message_id_locale
);
2575 free(message_id_locale
);
2578 free(recipient_id_locale
);
2582 isds_message_free(&message
);
2583 isds_list_free(&copies
);
2587 #undef FILL_LONGINT_OR_LEAVE
2588 #undef FILL_BOOLEAN_OR_LEAVE
2589 #undef CALLOC_OR_LEAVE
2590 #undef FILL_OR_LEAVE
2593 static void shi_delivery_usage(const char *command
) {
2595 "Usage: %s MESSAGE_ID\n"
2596 "Get delivery data about message with MESSAGE_ID.\n"),
2601 static int shi_delivery(int argc
, const char **argv
) {
2605 if (!argv
|| !argv
[1] || !*argv
[1]) {
2606 shi_delivery_usage(argv
[0]);
2611 printf(_("Getting delivery info...\n"));
2612 err
= isds_get_signed_delivery_info(cisds
, id
, &message
);
2613 finish_isds_operation(cisds
, err
);
2616 select_completition(COMPL_COMMAND
);
2620 format_message(message
);
2622 if (message
->envelope
&& message
->envelope
->dmID
)
2623 set_prompt(_("%s %s"), argv
[0], message
->envelope
->dmID
);
2625 set_prompt("%s", argv
[0]);
2626 select_completition(COMPL_MSG
);
2631 static int shi_dump_message(int argc
, const char **argv
) {
2633 fprintf(stderr
, _("No message loaded\n"));
2637 print_message(message
);
2642 static void shi_hash_usage(const char *command
) {
2644 "Usage: %s [MESSAGE_ID]\n"
2645 "Retrieve message hash stored in ISDS.\n"
2646 "If MESSAGE_ID is defined, query for that message.\n"
2647 "Otherwise use current message.\n"),
2652 static int shi_hash(int argc
, const char **argv
) {
2654 const char *id
= NULL
;
2655 struct isds_hash
*hash
= NULL
;
2656 char *hash_string
= NULL
;
2658 if (!argv
|| argc
> 2) {
2659 shi_hash_usage((argv
)?argv
[0]:NULL
);
2662 if (argc
== 2 && argv
[1] && *argv
[1])
2666 fprintf(stderr
, _("No message loaded\n"));
2669 if (!message
->envelope
|| !message
->envelope
->dmID
) {
2670 fprintf(stderr
, _("Current message is missing ID\n"));
2673 id
= message
->envelope
->dmID
;
2676 printf(_("Getting message hash...\n"));
2677 err
= isds_download_message_hash(cisds
, id
, &hash
);
2678 finish_isds_operation(cisds
, err
);
2681 hash_string
= hash2string(hash
);
2682 oprintf(_("ISDS states message with `%s' ID has following hash:\n%s\n"),
2686 isds_hash_free(&hash
);
2691 static int shi_verify(int argc
, const char **argv
) {
2694 struct isds_hash
*retrieved_hash
= NULL
, *stored_hash
= NULL
;
2695 char *hash_string
= NULL
;
2699 fprintf(stderr
, _("No message loaded\n"));
2703 if (!message
->envelope
) {
2704 fprintf(stderr
, _("Current message is missing envelope\n"));
2707 stored_hash
= message
->envelope
->hash
;
2708 message
->envelope
->hash
= NULL
;
2710 if (message
->envelope
->dmID
) {
2711 /* Verify remote hash */
2712 oprintf(_("Remote hash check:\n"));
2714 printf(_("Getting message hash...\n"));
2715 err
= isds_download_message_hash(cisds
, message
->envelope
->dmID
,
2717 finish_isds_operation(cisds
, err
);
2719 if (retrieved_hash
) {
2720 hash_string
= hash2string(retrieved_hash
);
2721 ohprint(_("Retrieved:"), width
);
2722 oprintf("%s\n", hash_string
);
2726 if (retrieved_hash
&& message
->raw
) {
2727 err
= isds_compute_message_hash(cisds
, message
,
2728 retrieved_hash
->algorithm
);
2729 finish_isds_operation(cisds
, err
);
2732 hash_string
= hash2string(message
->envelope
->hash
);
2733 ohprint(_("Computed:"), width
);
2734 oprintf("%s\n", hash_string
);
2739 err
= isds_hash_cmp(retrieved_hash
, message
->envelope
->hash
);
2742 oprintf(_("Hashes match.\n")); break;
2744 oprintf(_("Hashes do not match.\n"));
2748 oprintf(_("Hashes could not be compared.\n"));
2753 free(retrieved_hash
);
2758 /* Verify stored hash */
2759 oprintf(_("Stored hash check:\n"));
2761 hash_string
= hash2string(stored_hash
);
2762 ohprint(_("Stored:"), width
);
2763 oprintf("%s\n", hash_string
);
2767 err
= isds_compute_message_hash(cisds
, message
,
2768 stored_hash
->algorithm
);
2769 finish_isds_operation(cisds
, err
);
2772 hash_string
= hash2string(message
->envelope
->hash
);
2773 ohprint(_("Computed:"), width
);
2774 oprintf("%s\n", hash_string
);
2779 err
= isds_hash_cmp(stored_hash
, message
->envelope
->hash
);
2782 oprintf(_("Hashes match.\n")); break;
2784 oprintf(_("Hashes do not match.\n"));
2788 oprintf(_("Hashes could not be compared.\n"));
2793 isds_hash_free(&message
->envelope
->hash
);
2796 message
->envelope
->hash
= stored_hash
;
2801 static int shi_authenticate(int argc
, const char **argv
) {
2806 fprintf(stderr
, _("No message loaded\n"));
2809 if (!message
->raw
|| message
->raw_length
== 0) {
2810 fprintf(stderr
, _("Current message is missing raw representation\n"));
2814 printf(_("Submitting message to authenticity check...\n"));
2815 err
= isds_authenticate_message(cisds
, message
->raw
, message
->raw_length
);
2816 finish_isds_operation(cisds
, (err
== IE_NOTUNIQ
) ? IE_SUCCESS
: err
);
2820 oprintf(_("Message originates in ISDS.\n")); break;
2822 oprintf(_("Message is unknown to ISDS or has been tampered.\n"));
2834 static void shi_accept_message_usage(const char *command
) {
2836 "Usage: %s [MESSAGE_ID...]\n"
2837 "Accept commercial message moving its state to received.\n"
2838 "If MESSAGE_ID is defined, accept that message. More messages can be specified.\n"
2839 "Otherwise accept all commercial incoming messages.\n"),
2844 static int shi_accept_message(int argc
, const char **argv
) {
2848 /* Process messages named in argv */
2849 for (int i
= 1; i
< argc
; i
++) {
2850 if (!argv
[i
] || !*argv
[i
]) continue;
2852 id
= locale2utf8(argv
[i
]);
2855 _("Error: Could not convert message ID to UTF-8: %s\n"),
2860 printf(_("Accepting message `%s'...\n"), argv
[i
]);
2861 err
= isds_mark_message_received(cisds
, id
);
2862 finish_isds_operation(cisds
, err
);
2868 oprintf(_("Message `%s' accepted\n"), argv
[i
]);
2873 /* TODO: list commercial not received messages and accept all of them
2876 _("Error: No message ID supplied. Accepting all commercial "
2877 "messages not implemented yet.\n"));
2885 static void shi_delete_message_usage(const char *command
) {
2887 "Usage: %s {-i|-o} MESSAGE_ID...\n"
2888 "Remove message from long term storage.\n"
2890 " -i Messages are incoming\n"
2891 " -o Messages are outoging\n"),
2896 static int shi_delete_message(int argc
, const char **argv
) {
2900 _Bool direction_specified
= 0;
2904 while ((opt
= getopt(argc
, (char * const *)argv
, "io")) != -1) {
2908 direction_specified
= 1;
2912 direction_specified
= 1;
2915 shi_delete_message_usage((argv
)?argv
[0]:NULL
);
2919 if (optind
>= argc
|| !argv
|| !argv
[optind
] || !*argv
[optind
]) {
2920 fprintf(stderr
, _("Bad invocation\n"));
2921 shi_delete_message_usage((argv
)?argv
[0]:NULL
);
2924 if (!direction_specified
) {
2925 fprintf(stderr
, _("Message direction has not been specified\n"));
2926 shi_delete_message_usage((argv
)?argv
[0]:NULL
);
2930 /* Process messages named in argv */
2931 for (int i
= optind
; i
< argc
; i
++) {
2932 if (!argv
[i
] || !*argv
[i
]) continue;
2934 id
= locale2utf8(argv
[i
]);
2937 _("Error: Could not convert message ID to UTF-8: %s\n"),
2942 printf(_("Deleting message `%s'...\n"), argv
[i
]);
2943 err
= isds_delete_message_from_storage(cisds
, id
, incoming
);
2944 finish_isds_operation(cisds
, err
);
2950 oprintf(_("Message `%s' deleted\n"), argv
[i
]);
2958 /* Convert message ID form locale to UTF-8 or in other direction. If both
2959 * strings are provided, UTF-8 will take precedence. The missing string is
2960 * automatically allocated (but not freed before). If UTF-8 version has been
2961 * provided, @stastic_utf8 will become 1, otherwise 0. You can pass the
2962 * strings and the flags to free_message_id() to free memory properly.*/
2963 static int convert_message_id(char **id_utf8
, char **id_locale
, _Bool
*static_utf8
) {
2964 if (!id_utf8
|| !id_locale
|| !static_utf8
) return -1;
2965 if (!*id_utf8
&& !*id_locale
) return -1;
2969 *id_locale
= utf82locale(*id_utf8
);
2972 *id_utf8
= locale2utf8(*id_locale
);
2975 _("Error: Could not convert message ID to UTF-8: %s\n"),
2985 /* Free message ID strings as were allocated by convert_message_id() */
2986 static int free_message_id(char **id_utf8
, char **id_locale
, _Bool static_utf8
) {
2987 if (!id_utf8
|| !id_locale
) return -1;
2988 if (static_utf8
) zfree(*id_locale
);
2989 else zfree(*id_utf8
);
2994 /* Return static UTF-8 encoded ID of current message. In case of error NULL. */
2995 static const char *get_current_message_id(void) {
2997 fprintf(stderr
, _("No message loaded\n"));
3000 if (!message
->envelope
) {
3001 fprintf(stderr
, _("Loaded message is missing envelope\n"));
3004 if (!message
->envelope
->dmID
|| !*message
->envelope
->dmID
) {
3005 fprintf(stderr
, _("Loaded message is missing ID\n"));
3008 return message
->envelope
->dmID
;
3012 static void shi_message_sender_usage(const char *command
) {
3014 "Usage: %s [MESSAGE_ID...]\n"
3015 "Get details about sender of a message.\n"
3016 "If MESSAGE_ID is defined, get sender of that message. More messages can be specified.\n"
3017 "Otherwise will get sender of current message, if any is loaded.\n"),
3022 /* Get details about sender of message with given ID. At least one form must
3024 * @message_id is UTF-8 string
3025 * @message_id_locale is string in locale encoding
3026 * @return 0 on success, -1 on failure */
3027 static int do_message_sender(const char *message_id
, const char *message_id_locale
) {
3028 isds_sender_type
*type
= NULL
;
3029 char *raw_type
= NULL
;
3034 if (convert_message_id((char **)&message_id
, (char **)&message_id_locale
,
3038 printf(_("Getting sender of message `%s'...\n"), message_id_locale
);
3039 err
= isds_get_message_sender(cisds
, message_id
, &type
, &raw_type
, &name
);
3040 finish_isds_operation(cisds
, err
);
3042 free_message_id((char **)&message_id
, (char **)&message_id_locale
,
3047 format_sender_info(message_id
, type
, raw_type
, name
);
3049 free_message_id((char **)&message_id
, (char **)&message_id_locale
,
3058 static int shi_message_sender(int argc
, const char **argv
) {
3060 return do_message_sender(get_current_message_id(), NULL
);
3063 for (int i
= 1; i
< argc
; i
++) {
3064 if (!argv
[i
] || !*argv
[i
]) continue;
3065 if (do_message_sender(NULL
, argv
[i
]))
3073 /* Mark message as read. At least one form of ID must be provided.
3074 * @id is UTF-8 encoded message ID
3075 * @id_locale is locale encoded message ID. @id takes preference. */
3076 static int do_read_message(const char *id
, const char *id_locale
) {
3080 if ((!id
|| !*id
) && (!id_locale
|| !*id_locale
)) return -1;
3082 if (convert_message_id((char **)&id
, (char **)&id_locale
, &static_id
)) return -1;
3084 printf(_("Marking message `%s' as read...\n"), id_locale
);
3085 err
= isds_mark_message_read(cisds
, id
);
3086 finish_isds_operation(cisds
, err
);
3089 oprintf(_("Message `%s' marked as read\n"), id_locale
);
3091 free_message_id((char **)&id
, (char **)&id_locale
, static_id
);
3093 return (err
) ? -1 : 0;
3097 static void shi_read_message_usage(const char *command
) {
3099 "Usage: %s [MESSAGE_ID...]\n"
3100 "Mark message as read moving its state to read.\n"
3102 "When new incoming message is download, its state is not changed on server.\n"
3103 "Client must mark such message as read explicitly. You can use this command\n"
3104 "to do so, if not done automatically at download time by your client.\n"
3106 "If MESSAGE_ID is defined, mark that message. More messages can be specified.\n"
3107 "Otherwise marks currently loaded message.\n"),
3112 static int shi_read_message(int argc
, const char **argv
) {
3114 return do_read_message(get_current_message_id(), NULL
);
3117 for (int i
= 1; i
< argc
; i
++) {
3118 if (!argv
[i
] || !*argv
[i
]) continue;
3119 if (do_read_message(NULL
, argv
[i
]))
3127 static void shi_cat_message_usage(const char *command
) {
3130 "Print unformated raw representation of current message.\n"
3132 "This is the same content you would get into file by `save' command.\n"
3134 "Be ware the binary stream can screw your terminal. No new line character\n"
3135 "will be appended to the end of the output.\n"),
3140 static int shi_cat_message(int argc
, const char **argv
) {
3142 fprintf(stderr
, _("No message loaded\n"));
3146 if (!message
->raw
|| !message
->raw_length
) {
3147 fprintf(stderr
, _("Current message is missing raw representation\n"));
3151 if (owrite(message
->raw
, message
->raw_length
) != message
->raw_length
) {
3152 fprintf(stderr
, _("Error while printing message content\n"));
3160 static int shi_show_message(int argc
, const char **argv
) {
3162 fprintf(stderr
, _("No message loaded\n"));
3166 format_message(message
);
3171 static void shi_incoming_message_usage(const char *command
) {
3173 "Usage: %s [-r] MESSAGE_ID\n"
3174 "Get incoming message with MESSAGE_ID.\n"
3176 " -r Mark mesage as read\n"),
3181 static int shi_incoming_message(int argc
, const char **argv
) {
3185 _Bool mark_as_read
= 0;
3188 while ((opt
= getopt(argc
, (char * const *)argv
, "r")) != -1) {
3194 shi_incoming_message_usage((argv
)?argv
[0]:NULL
);
3198 if (optind
+ 1 != argc
|| !argv
|| !argv
[optind
] || !*argv
[optind
]) {
3199 fprintf(stderr
, _("Bad invocation\n"));
3200 shi_incoming_message_usage((argv
)?argv
[0]:NULL
);
3205 printf(_("Getting incoming message...\n"));
3206 err
= isds_get_signed_received_message(cisds
, id
, &message
);
3207 finish_isds_operation(cisds
, err
);
3210 select_completition(COMPL_COMMAND
);
3214 format_message(message
);
3216 if (message
->envelope
&& message
->envelope
->dmID
)
3217 set_prompt(_("%s %s"), argv
[0], message
->envelope
->dmID
);
3219 set_prompt("%s", argv
[0]);
3220 select_completition(COMPL_MSG
);
3222 if (mark_as_read
|| cfg_getbool(configuration
, CONFIG_MARKMESSAGEREAD
)) {
3223 if (message
->envelope
&& message
->envelope
->dmMessageStatus
&&
3224 ! (*message
->envelope
->dmMessageStatus
& MESSAGESTATE_READ
))
3225 return do_read_message(id
, NULL
);
3231 static void shi_outgoing_message_usage(const char *command
) {
3233 "Usage: %s MESSAGE_ID\n"
3234 "Get outgoing message with MESSAGE_ID.\n"),
3239 static int shi_outgoing_message(int argc
, const char **argv
) {
3243 if (!argv
|| !argv
[1] || !*argv
[1]) {
3244 shi_outgoing_message_usage(argv
[0]);
3249 printf(_("Getting outgoing message...\n"));
3250 err
= isds_get_signed_sent_message(cisds
, id
, &message
);
3251 finish_isds_operation(cisds
, err
);
3254 select_completition(COMPL_COMMAND
);
3258 format_message(message
);
3259 if (message
->envelope
&& message
->envelope
->dmID
)
3260 set_prompt(_("%s %s"), argv
[0], message
->envelope
->dmID
);
3262 set_prompt("%s", argv
[0]);
3263 select_completition(COMPL_MSG
);
3268 /* Detect type (message or delivery data) and load it. And change completion
3269 * and show the data.
3270 * @buffer is memory with message or delivery data
3271 * @length is size of @buffer in bytes
3272 * @strategy defines how to fill global message variable
3273 * @return 0 for success, otherwise non-zero. */
3274 static int do_load_anything(const void *buffer
, size_t length
,
3275 isds_buffer_strategy strategy
) {
3276 isds_raw_type raw_type
;
3278 char *type_name
= NULL
;
3280 if (NULL
== buffer
|| 0 == length
) {
3284 printf(_("Detecting format...\n"));
3285 err
= isds_guess_raw_type(cisds
, &raw_type
, buffer
, length
);
3286 finish_isds_operation(cisds
, err
);
3289 if (err
== IE_NOTSUP
)
3290 fprintf(stderr
, _("Unknown format.\n"));
3292 fprintf(stderr
, _("Error while detecting format.\n"));
3295 case RAWTYPE_INCOMING_MESSAGE
:
3296 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
:
3297 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
:
3298 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
:
3299 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
3300 err
= isds_load_message(cisds
, raw_type
,
3301 buffer
, length
, &message
, strategy
);
3302 finish_isds_operation(cisds
, err
);
3303 type_name
= N_("message");
3306 case RAWTYPE_DELIVERYINFO
:
3307 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO
:
3308 case RAWTYPE_CMS_SIGNED_DELIVERYINFO
:
3309 err
= isds_load_delivery_info(cisds
, raw_type
,
3310 buffer
, length
, &message
, strategy
);
3311 finish_isds_operation(cisds
, err
);
3312 type_name
= N_("delivery");
3317 _("Unsupported format.\n"));
3324 select_completition(COMPL_COMMAND
);
3328 format_message(message
);
3330 if (message
->envelope
&& message
->envelope
->dmID
)
3331 set_prompt(_("%s %s"), _(type_name
), message
->envelope
->dmID
);
3333 set_prompt("%s", _(type_name
));
3334 select_completition(COMPL_MSG
);
3339 static void shi_load_anything_usage(const char *command
) {
3342 "Load message or message delivery details from local FILE.\n"),
3347 static int shi_load_anything(int argc
, const char **argv
) {
3349 void *buffer
= NULL
;
3353 if (!argv
|| !argv
[1] || !*argv
[1]) {
3354 shi_load_anything_usage((argv
)?argv
[0]:NULL
);
3358 printf(_("Loading file `%s'...\n"), argv
[1]);
3360 if (mmap_file(argv
[1], &fd
, &buffer
, &length
)) return -1;
3362 error
= do_load_anything(buffer
, length
, BUFFER_COPY
);
3364 munmap_file(fd
, buffer
, length
);
3370 static void shi_save_message_usage(const char *command
) {
3373 "Save message into local FILE.\n"),
3378 static const char *raw_type2mime(isds_raw_type raw_type
) {
3380 case RAWTYPE_INCOMING_MESSAGE
:
3381 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
:
3382 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
:
3383 case RAWTYPE_DELIVERYINFO
:
3384 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO
:
3387 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
:
3388 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
3389 case RAWTYPE_CMS_SIGNED_DELIVERYINFO
:
3390 return "application/pkcs7-mime";
3398 static int shi_save_message(int argc
, const char **argv
) {
3399 _Bool overwrite
= cfg_getbool(configuration
, CONFIG_OVERWRITEFILES
);
3401 if (!argv
|| !argv
[1] || !*argv
[1]) {
3402 shi_save_message_usage(argv
[0]);
3407 fprintf(stderr
, _("No message loaded\n"));
3410 if (!message
->raw
|| message
->raw_length
== 0) {
3411 fprintf(stderr
, _("Loaded message is missing raw representation\n"));
3415 return save_data_to_file(argv
[1], -1, message
->raw
, message
->raw_length
,
3416 raw_type2mime(message
->raw_type
), overwrite
);
3420 /* Return document of current message identified by ordinal number expressed
3421 * as string. In case of error return NULL. */
3422 static const struct isds_document
*locate_document_by_ordinal_string(
3423 const char *number
) {
3424 const struct isds_list
*item
;
3425 const struct isds_document
*document
= NULL
;
3428 if (!number
) return NULL
;
3430 ordinar
= atoi(number
);
3432 fprintf(stderr
, _("%s: Document number must be positive number\n"),
3438 fprintf(stderr
, _("No message loaded\n"));
3443 for (item
= message
->documents
, i
= 0; item
; item
= item
->next
) {
3444 if (!item
->data
) continue;
3445 if (++i
== ordinar
) {
3446 document
= (const struct isds_document
*) item
->data
;
3451 fprintf(stderr
, _("Message does not contain document #%d\n"), ordinar
);
3459 static void shi_cat_document_usage(const char *command
) {
3461 "Usage: %s NUMBER\n"
3462 "Print document selected with ordinal NUMBER.\n"),
3466 static int shi_cat_document(int argc
, const char **argv
) {
3467 const struct isds_document
*document
;
3469 if (!argv
|| !argv
[1] || !*argv
[1] || argc
> 3) {
3470 shi_cat_document_usage(argv
[0]);
3474 document
= locate_document_by_ordinal_string(argv
[1]);
3475 if (!document
) return -1;
3477 if (document
->is_xml
) {
3478 xmlBufferPtr buffer
= NULL
;
3481 if (serialize_xml_to_buffer(&buffer
, document
->xml_node_list
))
3484 written
= owrite(buffer
->content
, buffer
->use
);
3485 xmlBufferFree(buffer
);
3486 if (written
!= buffer
->use
) {
3487 fprintf(stderr
, _("Error while printing document content\n"));
3491 if (!document
->data
|| !document
->data_length
) {
3492 fprintf(stderr
, _("Document is missing raw representation\n"));
3496 if (owrite(document
->data
, document
->data_length
) != document
->data_length
) {
3497 fprintf(stderr
, _("Error while printing document content\n"));
3506 static void shi_save_document_usage(const char *command
) {
3508 "Usage: %s NUMBER [DESTINATION]\n"
3509 "Save document having ordinal NUMBER within current message into local file.\n"
3510 "If DESTINATION is file (or does not exist yet), document will be saved into\n"
3512 "If DESTINATION is existing directory, file name equaled to document name\n"
3513 "will be saved into DESTINATION.\n"
3514 "If DESTINATION is missing, document name will be used as file name and\n"
3515 "saved into working directory.\n"
3516 "Be aware that document name does not embed malicious characters (slashes).\n"
3518 "If the document is a binary stream, image of the document will be copied\n"
3519 "into a file. If the document is a XML document, the XML tree will be serialized\n"
3520 "into a file. If XML document stands for one element or one text node, the node\n"
3521 "(and its children recursively) will be serialized. If XML document compounds\n"
3522 "more nodes or a comment or a processing instruction, parent node from ISDS name\n"
3523 "space will be used to ensure output serialized XML well-formness.\n"
3529 static int shi_save_document(int argc
, const char **argv
) {
3530 const struct isds_document
*document
;
3531 const char *dirname
= NULL
;
3532 char *filename
= NULL
, *path
= NULL
;
3534 _Bool overwrite
= cfg_getbool(configuration
, CONFIG_OVERWRITEFILES
);
3536 if (!argv
|| !argv
[1] || !*argv
[1] || argc
> 3) {
3537 shi_save_document_usage(argv
[0]);
3541 document
= locate_document_by_ordinal_string(argv
[1]);
3542 if (!document
) return -1;
3544 /* Select directory and file name */
3545 if (argv
[2] && *argv
[2]) {
3546 if (!is_directory(argv
[2])) {
3549 filename
= strdup(argv
[2]);
3551 fprintf(stderr
, _("Not enough memory\n"));
3556 if (!filename
&& document
->dmFileDescr
&& &document
->dmFileDescr
) {
3557 filename
= utf82locale(document
->dmFileDescr
);
3559 fprintf(stderr
, _("Not enough memory\n"));
3565 _("File name neither supplied, nor document name exists\n"
3566 "Please, supply one.\n"));
3572 path
= astrcat3(dirname
, "/", filename
);
3579 fprintf(stderr
, _("Not enough memory\n"));
3584 if (document
->is_xml
)
3585 retval
= save_xml_to_file(path
, -1, document
->xml_node_list
,
3586 document
->dmMimeType
, overwrite
);
3588 retval
= save_data_to_file(path
, -1, document
->data
,
3589 document
->data_length
, document
->dmMimeType
, overwrite
);
3595 /* Execute program specified as NULL terminated array of arguments. argv[0] is
3596 * subject of PATH search variable look-up. The program is executed directly,
3597 * it's not a shell command. */
3598 static int execute_system_command(char *const argv
[]) {
3601 if (!argv
|| !argv
[0]) return -1;
3605 /* Could not fork */
3606 fprintf(stderr
, _("Could not fork\n"));
3608 } else if (pid
== 0) {
3610 execvp(argv
[0], argv
);
3611 fprintf(stderr
, _("Could not execute:"));
3612 for (char *const *arg
= argv
; *arg
; arg
++)
3613 fprintf(stderr
, " %s", *arg
);
3614 fprintf(stderr
, _(": %s\n"), strerror(errno
));
3617 /* Wait for the command */
3620 if (-1 == waitpid(pid
, &retval
, 0)) {
3621 fprintf(stderr
, _("Could not wait for executed command\n"));
3626 fprintf(stderr
, _("Exit code of command could not "
3627 "be determined\n"));
3628 else if (WIFEXITED(retval
) && WEXITSTATUS(retval
))
3629 printf(_("Command exited with code %d\n"),
3630 WEXITSTATUS(retval
));
3631 else if (WIFSIGNALED(retval
))
3632 printf(_("Command terminated by signal "
3633 "#%d\n"), WTERMSIG(retval
));
3639 /* Append @suffix into @buffer with @size bytes prealocated at position @at.
3640 * Trailing '\0' of @suffix is not carried.
3641 * @buffer can be reallocated if @size is not suffient to fill @suffix
3642 * @size is original @buffer size, can change if @buffer would be reallocated
3643 * @at position where append @suffic to. Outputs new end after appending
3644 * @suffix is NULL rerminated string to append
3645 * @return 0 if success, -1 otherwise. Caller is resposible for freeing
3647 static int append_string_at(char **buffer
, size_t *size
, char **at
,
3648 const char *suffix
) {
3650 if (!buffer
|| !*buffer
|| !size
|| !at
|| !*at
) return -1;
3651 if (!suffix
) return 0;
3654 if (*at
- *buffer
+ 1 >= *size
) {
3655 /* End of buffer, grow it */
3656 if (*size
< 8) *size
= 8;
3658 char *new_buffer
= realloc(*buffer
, *size
);
3659 if (!new_buffer
) return -1;
3660 *at
= *at
- *buffer
+ new_buffer
;
3661 *buffer
= new_buffer
;
3664 /* Copy a character */
3665 *((*at
)++) = *(suffix
++);
3672 static char *expand_command_arg(const char *format
, const char *file
,
3674 char *buffer
= NULL
;
3676 const char *format_cursor
;
3677 char *buffer_cursor
;
3679 if (!format
) return NULL
;
3681 for (format_cursor
= format
, buffer_cursor
= buffer
; ; format_cursor
++) {
3682 if (buffer_cursor
- buffer
+ 1 >= size
) {
3683 /* End of buffer, grow it */
3684 if (size
< 8) size
= 8;
3686 char *new_buffer
= realloc(buffer
, size
);
3687 if (!new_buffer
) goto error
;
3688 buffer_cursor
= buffer_cursor
- buffer
+ new_buffer
;
3689 buffer
= new_buffer
;
3692 if (*format_cursor
== '%') {
3694 switch (*(format_cursor
+1)) {
3697 fprintf(stderr
, _("Could not expand `%%f' because "
3698 "file name did not exist.\n"));
3702 if (append_string_at(&buffer
, &size
, &buffer_cursor
, file
))
3709 fprintf(stderr
, _("Could not expand `%%t' because "
3710 "file type did not exist.\n"));
3714 if (append_string_at(&buffer
, &size
, &buffer_cursor
, type
))
3724 /* Copy plain character */
3725 *(buffer_cursor
++) = *format_cursor
;
3727 if (!*format_cursor
) break;
3733 fprintf(stderr
, _("Error: Not enough memory\n"));
3739 /* Expand open_command configuration option by substiting %f and %t with file
3740 * name and file type.
3741 * @file is locale encoded file name
3742 * @type is UTF-8 encoded MIME type
3743 * @return heap allocated arrary of arguments or NULL if error occurs.
3744 * Arguments are coded in locale. */
3745 static char **expand_open_command(const char *file
, const char *type
) {
3746 char **command
= NULL
;
3748 char *type_locale
= NULL
;
3750 length
= cfg_size(configuration
, CONFIG_OPENCOMMAND
);
3752 fprintf(stderr
, _("%s not set\n"), CONFIG_OPENCOMMAND
);
3756 command
= malloc((length
+ 1) * sizeof(*command
));
3758 fprintf(stderr
, _("Error: Not enough memory\n"));
3763 type_locale
= utf82locale(type
);
3765 printf(_("Could not convert document MIME type to locale "
3772 for (int i
= 0; i
< length
; i
++) {
3773 const char *value
= cfg_getnstr(configuration
, CONFIG_OPENCOMMAND
, i
);
3774 command
[i
] = expand_command_arg(value
, file
, type_locale
);
3776 fprintf(stderr
, _("Error: Not enough memory\n"));
3777 for (int j
= 0; j
< i
; j
++) free(command
[j
]);
3783 command
[length
] = NULL
;
3790 /* Register temporary @file for removal at shigofumi exit.
3791 * This is done by destructor call-back in isds_list_free(). */
3792 static int register_temporary_file(const char *file
) {
3793 struct isds_list
*temporary_file
= NULL
;
3795 if (!file
) return 0;
3797 temporary_file
= malloc(sizeof(*temporary_file
));
3798 if (!temporary_file
) {
3799 fprintf(stderr
, _("Error: Not enough memory\n"));
3803 temporary_file
->data
= (void *)strdup(file
);
3804 if (!temporary_file
) {
3805 fprintf(stderr
, _("Error: Not enough memory\n"));
3806 free(temporary_file
);
3810 temporary_file
->destructor
= shi_unlink_temporary_file
;
3812 if (temporary_files
)
3813 temporary_file
->next
= temporary_files
;
3815 temporary_file
->next
= NULL
;
3816 temporary_files
= temporary_file
;
3822 static void shi_open_document_usage(const char *command
) {
3824 "Usage: %s NUMBER\n"
3825 "Save document having ordinal NUMBER within current message into temporal\n"
3826 "local file, open the file by xdg-open utility and then remove the file.\n"
3832 static int shi_open_document(int argc
, const char **argv
) {
3833 const struct isds_document
*document
;
3834 char filename
[10] = "shiXXXXXX";
3836 char **command
= NULL
;
3839 if (!argv
|| !argv
[1] || !*argv
[1] || argc
> 3) {
3840 shi_open_document_usage(argv
[0]);
3844 document
= locate_document_by_ordinal_string(argv
[1]);
3845 if (!document
) return -1;
3847 /* Create temporary file for the document */
3848 /* TODO: mkstemp() works in CWD. Implement something working in $TMP. */
3849 fd
= mkstemp(filename
);
3851 fprintf(stderr
, _("Could not create temporary file: %s\n"),
3857 if (document
->is_xml
)
3858 retval
= save_xml_to_file(filename
, fd
, document
->xml_node_list
,
3859 document
->dmMimeType
, 0);
3861 retval
= save_data_to_file(filename
, fd
, document
->data
,
3862 document
->data_length
, document
->dmMimeType
, 0);
3864 /* Open the file with external utility */
3866 /* Construct command arguments to execute */
3867 command
= expand_open_command(filename
, document
->dmMimeType
);
3872 /* XXX: Do not use system(3) as we cannot escape uknown shell */
3873 retval
= execute_system_command(command
);
3874 for (char **arg
= command
; *arg
; arg
++) free(*arg
);
3879 /* Remove the file */
3880 /* XXX: We do not know when external program opens the file. We cannot
3881 * remove it immediately. Register the filename and remove all temporary
3882 * files at exit of shigofumi if requested by configuration. */
3883 if (cfg_getbool(configuration
, CONFIG_CLEAN_TEMPORARY_FILES
)) {
3884 if (register_temporary_file(filename
))
3885 fprintf(stderr
, _("Warning: Temporary file `%s' could not been "
3886 "registered for later removal. Remove the file by "
3887 "hand, please.\n"), filename
);
3893 static void shi_save_stamp_usage(const char *command
) {
3896 "Save message time stamp into local FILE.\n"),
3901 static int shi_save_stamp(int argc
, const char **argv
) {
3902 _Bool overwrite
= cfg_getbool(configuration
, CONFIG_OVERWRITEFILES
);
3904 if (!argv
|| !argv
[1] || !*argv
[1]) {
3905 shi_save_message_usage(argv
[0]);
3910 fprintf(stderr
, _("No message loaded\n"));
3913 if (!message
->envelope
|| !message
->envelope
->timestamp
||
3914 message
->envelope
->timestamp_length
== 0) {
3915 fprintf(stderr
, _("Loaded message is missing time stamp\n"));
3919 return save_data_to_file(argv
[1], -1,
3920 message
->envelope
->timestamp
, message
->envelope
->timestamp_length
,
3921 "application/timestamp-reply", overwrite
);
3925 static int shi_show_list(int argc
, const char **argv
) {
3927 fprintf(stderr
, _("No message list loaded\n"));
3931 oprintf((messages_are_outgoing
) ?
3932 ngettext("You have %'lu outgoing message\n",
3933 "You have %'lu outgoing messages\n", total_messages
) :
3934 ngettext("You have %'lu incoming message\n",
3935 "You have %'lu incoming messages\n", total_messages
),
3937 print_message_list(messages
, messages_are_outgoing
);
3942 static int shi_list_incoming(int argc
, const char **argv
) {
3945 printf(_("Listing incoming messages...\n"));
3946 err
= isds_get_list_of_received_messages(cisds
, NULL
, NULL
, NULL
,
3947 MESSAGESTATE_ANY
, 0, &total_messages
, &messages
);
3948 finish_isds_operation(cisds
, err
);
3951 select_completition(COMPL_COMMAND
);
3954 messages_are_outgoing
= 0;
3956 shi_show_list(0, NULL
);
3958 set_prompt(_("%s %'lu"), argv
[0], total_messages
);
3959 select_completition(COMPL_LIST
);
3964 static int shi_list_outgoing(int argc
, const char **argv
) {
3967 printf(_("Listing outgoing messages...\n"));
3968 err
= isds_get_list_of_sent_messages(cisds
, NULL
, NULL
, NULL
,
3969 MESSAGESTATE_ANY
, 0, &total_messages
, &messages
);
3970 finish_isds_operation(cisds
, err
);
3973 select_completition(COMPL_COMMAND
);
3976 messages_are_outgoing
= 1;
3978 shi_show_list(0, NULL
);
3980 set_prompt(_("%s %'lu"), argv
[0], total_messages
);
3981 select_completition(COMPL_LIST
);
3986 /* Submit document for conversion and print assigned identifier */
3987 static int do_convert(const struct isds_document
*document
) {
3990 struct tm
*date
= NULL
;
3992 if (!document
) return -1;
3994 printf(_("Submitting document for authorized conversion...\n"));
3996 err
= czp_convert_document(czechpoint
, document
, &id
, &date
);
3997 finish_isds_operation(czechpoint
, err
);
4000 char *name_locale
= utf82locale(document
->dmFileDescr
);
4001 char *date_string
= tm2string(date
);
4002 char *id_locale
= utf82locale(id
);
4004 "Document submitted for authorized conversion successfully under name\n"
4006 "Submit identifier assigned by Czech POINT deposit is `%s'.\n"),
4007 name_locale
, date_string
, id_locale
);
4011 oprintf(_("Be ware that submitted document has restricted lifetime "
4013 oprintf(_("See <%s> for more details.\n"), CZPDEPOSIT_URL
);
4016 free(id
); free(date
);
4017 return (err
) ? -1 : 0;
4021 static void shi_convert_file_or_message_usage(const char *command
) {
4023 "Usage: %s [FILE [NAME]]\n"
4024 "Submit local FILE to authorized conversion under NAME. If NAME is missing,\n"
4025 "it will use FILE name. If FILE is missing, it will submit current message.\n"),
4029 "If Czech POINT deposit accepts document, it will return document identifier\n"
4030 "that user is supposed to provide to officer at Czech POINT contact place.\n"
4031 "Currently only PDF 1.3 and higher version files and signed messages are\n"
4033 oprintf(_("See <%s> for more details.\n"), CZPDEPOSIT_URL
);
4037 static int shi_convert_file_or_message(int argc
, const char **argv
) {
4039 struct isds_document document
;
4042 if (!argv
|| argc
> 3) {
4043 shi_convert_file_or_message_usage((argv
)?argv
[0]:NULL
);
4047 memset(&document
, 0, sizeof(document
));
4049 if (NULL
== argv
[1] || !*argv
[1]) {
4050 /* Convert current message */
4052 fprintf(stderr
, _("No message loaded\n"));
4055 if (!message
->raw
|| !message
->raw_length
) {
4057 _("Current message is missing raw representation\n"));
4060 document
.dmFileDescr
= astrcat(
4061 (NULL
!= message
->envelope
&& NULL
!= message
->envelope
->dmID
) ?
4062 message
->envelope
->dmID
:
4065 if (NULL
== document
.dmFileDescr
) {
4066 printf(_("Could not build document name from message ID\n"));
4069 document
.data
= message
->raw
;
4070 document
.data_length
= message
->raw_length
;
4072 /* Convert local file */
4073 if (argc
== 3 && argv
[2] && *argv
[2])
4074 document
.dmFileDescr
= locale2utf8(argv
[2]);
4076 document
.dmFileDescr
= locale2utf8(argv
[1]);
4077 if (!document
.dmFileDescr
) {
4078 printf(_("Could not convert document name to UTF-8\n"));
4082 printf(_("Loading document from file `%s'...\n"), argv
[1]);
4083 if (mmap_file(argv
[1], &fd
, &document
.data
, &document
.data_length
)) {
4084 free(document
.dmFileDescr
);
4089 retval
= do_convert(&document
);
4092 munmap_file(fd
, document
.data
, document
.data_length
);
4094 free(document
.dmFileDescr
);
4099 static void shi_convert_document_usage(const char *command
) {
4101 "Usage: %s NUMBER\n"
4102 "Submit message document with ordinal NUMBER to authorized conversion.\n"),
4106 "If Czech POINT deposit accepts document, it will return document identifier\n"
4107 "that user is supposed to provide to officer at Czech POINT contact place.\n"
4108 "Currently only PDF 1.3 and higher version files and signed messages are\n"
4110 oprintf(_("See <%s> for more details.\n"), CZPDEPOSIT_URL
);
4114 static int shi_convert_document(int argc
, const char **argv
) {
4115 const struct isds_document
*document
;
4117 if (!argv
|| !argv
[1] || !*argv
[1]) {
4118 shi_convert_document_usage((argv
)?argv
[0]:NULL
);
4122 document
= locate_document_by_ordinal_string(argv
[1]);
4123 if (!document
) return -1;
4125 return do_convert(document
);
4129 static void shi_resign_usage(const char *command
) {
4131 "Usage: %s [FILE]\n"
4132 "Send message or delivery data to re-sign it and to add current time stamp.\n"
4133 "If FILE is specified, message will be loaded from local file. Otherwise\n"
4134 "current message will be sent.\n"),
4138 "Only signed messages or delivery data without time stamp are accepted by\n"
4139 "ISDS. Output re-signed message or delivery data will be loaded.\n"));
4143 static int shi_resign(int argc
, const char **argv
) {
4145 void *data
= NULL
; /* Static */
4146 void *resigned_data
= NULL
; /* Dynamic, stored into message */
4147 size_t data_length
= 0, resigned_data_length
= 0;
4148 struct tm
*valid_to
= NULL
; /* Dynamic */
4151 if (!argv
|| argc
> 3) {
4152 shi_resign_usage((argv
)?argv
[0]:NULL
);
4156 if (NULL
== argv
[1] || !*argv
[1]) {
4157 /* Use current message */
4159 fprintf(stderr
, _("No message or delivery data loaded\n"));
4162 if (!message
->raw
|| !message
->raw_length
) {
4163 fprintf(stderr
, _("Current message or delivery data "
4164 "is missing raw representation\n"));
4167 data
= message
->raw
;
4168 data_length
= message
->raw_length
;
4170 /* Use local file */
4171 printf(_("Loading message or delivery data from file `%s'...\n"),
4173 if (mmap_file(argv
[1], &fd
, &data
, &data_length
)) {
4178 printf(_("Re-signing...\n"));
4179 err
= isds_resign_message(cisds
, data
, data_length
,
4180 &resigned_data
, &resigned_data_length
, &valid_to
);
4181 finish_isds_operation(cisds
, err
);
4184 munmap_file(fd
, data
, data_length
);
4191 print_header_tm(_("New time stamp expires"), valid_to
);
4194 return do_load_anything(resigned_data
, resigned_data_length
, BUFFER_MOVE
);
4199 static void shi_print_usage(const char *command
) {
4201 "Usage: %s STRING LENGTH\n"
4202 "Prints STRING into LENGTH columns. Negative LENGTH means not to cut\n"
4203 "overflowing string.\n"
4204 "This should be locale and terminal agnostic.\n"),
4209 static int shi_print(int argc
, const char **argv
) {
4212 if (!argv
|| !argv
[1] || !argv
[2]) {
4213 shi_print_usage((argv
)?argv
[0]:NULL
);
4217 width
= strtol(argv
[2], NULL
, 10);
4218 if (width
< INT_MIN
) {
4220 _("Length argument must not lesser than %d.\n"), INT_MIN
);
4223 if (width
> INT_MAX
) {
4225 _("Length argument must not be greater than %d.\n"), INT_MAX
);
4230 onprint(argv
[1], width
);
4237 static int shi_tokenize(int argc
, const char **argv
) {
4239 if (!argv
) return 0;
4241 for (int i
= 0; i
< argc
; i
++) {
4242 oprintf(_(">%s<\n"), argv
[i
]);
4248 static int shi_quote(int argc
, const char **argv
) {
4249 char *escaped
, *unescaped
;
4251 if (!argv
) return 0;
4253 oprintf(_("Original\tQuoted\tDequoted\n"));
4254 for (int i
= 0; i
< argc
; i
++) {
4255 escaped
= shi_quote_filename((char *) argv
[i
], 0, NULL
);
4256 unescaped
= shi_dequote_filename((char *) argv
[i
], 0);
4257 oprintf(_(">%s<\t>%s<\t>%s<\n"), argv
[i
], escaped
, unescaped
);
4266 /* pclose(pipe), restore ouput to stdout, show error return code */
4267 int wait_for_shell(FILE **pipe
) {
4270 if (pipe
&& *pipe
) {
4271 retval
= pclose(*pipe
);
4276 fprintf(stderr
, _("Exit code of shell command could not "
4277 "be determined\n"));
4278 else if (WIFEXITED(retval
) && WEXITSTATUS(retval
))
4279 printf(_("Shell command exited with code %d\n"),
4280 WEXITSTATUS(retval
));
4281 else if (WIFSIGNALED(retval
))
4282 printf(_("Shell command terminated by signal "
4283 "#%d\n"), WTERMSIG(retval
));
4290 /* Interactive loop */
4291 void shi_loop(void) {
4292 char *command_line
= NULL
;
4293 char **command_argv
= NULL
;
4296 struct command
*command
= NULL
;
4300 oprintf(_("Use `help' command to get list of available commands.\n"));
4302 select_completition(COMPL_COMMAND
);
4306 command_line
= readline((prompt
) ? prompt
: _("shigofumi> "));
4307 /* Remember not parsable commands too to user be able to get back to
4309 if (command_line
&& *command_line
) {
4310 /* TODO: Omit blank lines */
4311 add_history(command_line
);
4314 command_argv
= tokenize(command_line
, &command_argc
, &shell
);
4316 if (command_argv
&& command_argv
[0]) {
4317 command
= find_command(command_argv
[0]);
4320 fprintf(stderr
, _("Command not understood\n"));
4325 pipe
= popen(shell
, "w");
4327 fprintf(stderr
, _("Could not run shell command `%s':"
4328 " %s\n"), shell
, strerror(errno
));
4334 command
->function(command_argc
,
4335 (const char **) command_argv
);
4336 wait_for_shell(&pipe
);
4341 argv_free(command_argv
);
4343 zfree(command_line
);
4348 /* Non-interactive mode. Commands from @lines are processed until any command
4349 * lines remains or no error occurred. First failure terminates processing.
4350 * @lines is sequence of commands separated by '\n' or '\r'. The content is
4351 * modified during this call.
4352 * @return 0 if all command succeed, otherwise non-zero value
4354 int shi_batch(char *lines
) {
4356 char **command_argv
= NULL
;
4359 struct command
*command
= NULL
;
4364 oprintf(_("Batch mode started.\n"));
4366 select_completition(COMPL_COMMAND
);
4368 while (!retval
&& (command_line
= strtok(lines
, "\n\r"))) {
4369 lines
= NULL
; /* strtok(3) requires it for subsequent calls */
4371 printf(_("Processing command: %s\n"), command_line
);
4373 command_argv
= tokenize(command_line
, &command_argc
, &shell
);
4375 if (command_argv
&& command_argv
[0]) {
4376 command
= find_command(command_argv
[0]);
4379 fprintf(stderr
, _("Command not understood\n"));
4385 pipe
= popen(shell
, "w");
4387 fprintf(stderr
, _("Could not run shell command `%s':"
4388 " %s\n"), shell
, strerror(errno
));
4394 retval
= command
->function(command_argc
,
4395 (const char **) command_argv
);
4396 if (wait_for_shell(&pipe
)) retval
= -1;
4401 argv_free(command_argv
);
4406 fprintf(stderr
, _("Command failed!\n"));
4411 #define COMMON_COMMANDS \
4412 { "accept", shi_accept_message, N_("accept commercial message"), \
4413 shi_accept_message_usage, ARGTYPE_MSGID }, \
4414 { "box", shi_box, N_("show current box details"), NULL, \
4416 { "boxlist", shi_boxlist, N_("get list of all boxes"), shi_boxlist_usage, \
4418 { "cache", shi_cache, N_("show cache details"), NULL, \
4420 { "cd", shi_chdir, N_("change working directory"), shi_chdir_usage, \
4422 { "commercialreceiving", shi_commercialreceiving, \
4423 N_("manipulate commercial receiving box status"), \
4424 shi_commercialreceiving_usage, ARGTYPE_BOXID }, \
4425 { "commercialsending", shi_commercialsending, \
4426 N_("manipulate commercial sending box status"), \
4427 shi_commercialsending_usage, ARGTYPE_BOXID }, \
4428 { "compose", shi_compose, N_("compose a message"), shi_compose_usage, \
4430 { "convert", shi_convert_file_or_message, \
4431 N_("submit local document for authorized conversion"), \
4432 shi_convert_file_or_message_usage, ARGTYPE_FILE }, \
4433 { "copying", shi_copying, N_("show this program licence excerpt"), NULL, \
4435 { "debug", shi_debug, N_("set debugging"), shi_debug_usage, \
4437 { "delete", shi_delete_message, N_("delete message from storage"), \
4438 shi_delete_message_usage, ARGTYPE_MSGID }, \
4439 { "delivery", shi_delivery, N_("get message delivery details"), \
4440 shi_delivery_usage, ARGTYPE_MSGID }, \
4441 { "findbox", shi_find_box, N_("search for a box"), shi_find_box_usage, \
4443 { "hash", shi_hash, N_("query ISDS for message hash"), \
4444 shi_hash_usage, ARGTYPE_MSGID }, \
4445 { "help", shi_help, N_("describe commands"), shi_help_usage, \
4446 ARGTYPE_COMMAND }, \
4447 { "load", shi_load_anything, \
4448 N_("load message or message delivery details from local file"), \
4449 shi_load_anything_usage, ARGTYPE_FILE }, \
4450 { "login", shi_login, N_("log into ISDS"), shi_login_usage, \
4452 { "lsi", shi_list_incoming, N_("list received messages"), NULL, \
4454 { "lso", shi_list_outgoing, N_("list sent messages"), NULL, \
4456 { "msgi", shi_incoming_message, N_("get incoming message"), \
4457 shi_incoming_message_usage, ARGTYPE_MSGID }, \
4458 { "msgo", shi_outgoing_message, N_("get outgoing message"), \
4459 shi_outgoing_message_usage, ARGTYPE_MSGID }, \
4460 { "passwd", shi_passwd, N_("manipulate user password"), shi_passwd_usage, \
4462 { "pwd", shi_pwd, N_("print working directory"), NULL, ARGTYPE_NONE }, \
4463 { "quit", shi_quit, N_("exit shigofumi"), NULL, ARGTYPE_NONE }, \
4464 { "read", shi_read_message, N_("mark message as read"), \
4465 shi_read_message_usage, ARGTYPE_MSGID }, \
4466 { "resign", shi_resign, N_("re-sign message or delivery data"), \
4467 shi_resign_usage, ARGTYPE_FILE }, \
4468 { "set", shi_settings, N_("show settings"), NULL, ARGTYPE_NONE }, \
4469 { "sender", shi_message_sender, N_("get message sender"), \
4470 shi_message_sender_usage, ARGTYPE_MSGID }, \
4471 { "statbox", shi_stat_box, N_("get status of a box"), shi_stat_box_usage, \
4473 { "user", shi_user, N_("show current user details"), NULL, \
4475 { "users", shi_users, N_("show box users"), shi_users_usage, \
4477 { "version", shi_version, N_("show version of this program"), NULL, \
4479 { NULL, NULL, NULL, NULL, ARGTYPE_NONE }
4481 struct command base_commands
[] = {
4483 { "quote", shi_quote
, N_("demonstrate argument escaping"), NULL
,
4485 { "print", shi_print
, N_("print string into given width"),
4486 shi_print_usage
, ARGTYPE_NONE
},
4487 { "tokenize", shi_tokenize
, N_("demonstrate arguments tokenization"), NULL
,
4493 struct command message_commands
[] = {
4494 { "authenticate", shi_authenticate
, N_("check message authenticity"),
4495 NULL
, ARGTYPE_NONE
},
4496 { "cat", shi_cat_message
, N_("show raw current message"),
4497 shi_cat_message_usage
, ARGTYPE_NONE
},
4498 { "catdoc", shi_cat_document
, N_("show raw document"),
4499 shi_cat_document_usage
, ARGTYPE_DOCID
},
4500 { "convertdoc", shi_convert_document
,
4501 N_("submit document of current message for authorized conversion"),
4502 shi_convert_document_usage
, ARGTYPE_DOCID
},
4503 { "dump", shi_dump_message
, N_("dump current message structure"),
4504 NULL
, ARGTYPE_NONE
},
4505 { "opendoc", shi_open_document
, N_("open document using external utility"),
4506 shi_open_document_usage
, ARGTYPE_DOCID
},
4507 { "savestamp", shi_save_stamp
,
4508 N_("save time stamp of current message into local file"),
4509 shi_save_stamp_usage
, ARGTYPE_FILE
},
4510 { "savedoc", shi_save_document
,
4511 N_("save document of current message into local file"),
4512 shi_save_document_usage
, ARGTYPE_FILE
},
4513 { "save", shi_save_message
, N_("save current message into local file"),
4514 shi_save_message_usage
, ARGTYPE_FILE
},
4515 { "show", shi_show_message
, N_("show current message"), NULL
,
4517 { "verify", shi_verify
, N_("verify current message hash"), NULL
,
4522 struct command list_commands
[] = {
4523 { "show", shi_show_list
, N_("show current message list"), NULL
,
4528 #undef COMMON_COMMANDS
4531 static void main_version(void) {
4536 shi_copying(0, NULL
);
4540 static void main_usage(const char *command
) {
4542 "Usage: %s [OPTION...]\n"
4543 "Access ISDS, process local data box messages or delivery details, submit\n"
4544 "document to authorized conversion.\n"
4547 " -c FILE use the FILE as configuration file instead of ~/%s\n"
4548 " -e COMMANDS execute COMMANDS (new line separated) and exit\n"
4549 " -V show version info and exit\n"
4551 (command
) ? command
: "shigofumi",
4556 int main(int argc
, char **argv
) {
4558 char *config_file
= NULL
;
4559 char *batch_commands
= NULL
;
4560 int retval
= EXIT_SUCCESS
;
4562 setlocale(LC_ALL
, "");
4564 /* Initialize gettext */
4565 bindtextdomain(PACKAGE
, LOCALEDIR
);
4566 textdomain(PACKAGE
);
4569 /* Default output */
4572 /* Parse arguments */
4574 while ((opt
= getopt(argc
, (char * const *)argv
, "c:e:V")) != -1) {
4577 config_file
= optarg
;
4580 batch_commands
= optarg
;
4584 shi_exit(EXIT_SUCCESS
);
4587 main_usage((argv
[0]) ? basename(argv
[0]): NULL
);
4588 shi_exit(EXIT_FAILURE
);
4593 if (shi_init(config_file
)) {
4594 shi_exit(EXIT_FAILURE
);
4597 /*shi_login(NULL);*/
4599 if (batch_commands
) {
4600 if (shi_batch(batch_commands
))
4601 retval
= EXIT_FAILURE
;