1 #define _XOPEN_SOURCE 600
6 #include <readline/readline.h>
7 #include <readline/history.h>
17 #include <sys/types.h>
22 #include "shigofumi.h"
23 #include "completition.h"
29 #define CONFIG_FILE ".shigofumirc"
30 #define CONFIG_SERVER "base_url"
31 #define CONFIG_USERNAME "username"
32 #define CONFIG_PASSWORD "password"
33 #define CONFIG_CERT_FORMAT "certificate_format"
34 #define CONFIG_CERT_PATH "certificate_path"
35 #define CONFIG_KEY_ENGINE "key_engine"
36 #define CONFIG_KEY_FORMAT "key_format"
37 #define CONFIG_KEY_PATH "key_path"
38 #define CONFIG_KEY_PASSWORD "key_password"
39 #define CONFIG_OTP_METHOD "otp_method"
40 #define CONFIG_OTP_CODE "otp_code"
41 #define CONFIG_DEBUGLEVEL "debug_level"
42 #define CONFIG_VERIFYSERVER "verify_server"
43 #define CONFIG_CAFILE "ca_file"
44 #define CONFIG_CADIRECTORY "ca_directory"
45 #define CONFIG_CLEAN_TEMPORARY_FILES "clean_temporary_files"
46 #define CONFIG_CRLFILE "crl_file"
47 #define CONFIG_TIMEOUT "timeout"
48 #define CONFIG_LOGFACILITIES "log_facilities"
49 #define CONFIG_LOGFILE "log_file"
50 #define CONFIG_LOGLEVEL "log_level"
51 #define CONFIG_MARKMESSAGEREAD "mark_message_read"
52 #define CONFIG_NORMALIZEMIMETYPE "normalize_mime_type"
53 #define CONFIG_OPENCOMMAND "open_command"
54 #define CONFIG_OVERWRITEFILES "overwrite_files"
55 #define CZPDEPOSIT_URL "https://www.czechpoint.cz/uschovna/"
61 cfg_opt_t configuration_syntax
[] = {
62 CFG_STR(CONFIG_SERVER
, NULL
, CFGF_NONE
),
63 CFG_STR(CONFIG_USERNAME
, NULL
, CFGF_NODEFAULT
),
64 CFG_STR(CONFIG_PASSWORD
, NULL
, CFGF_NODEFAULT
),
65 CFG_STR(CONFIG_CERT_FORMAT
, NULL
, CFGF_NODEFAULT
),
66 CFG_STR(CONFIG_CERT_PATH
, NULL
, CFGF_NODEFAULT
),
67 CFG_STR(CONFIG_KEY_ENGINE
, NULL
, CFGF_NODEFAULT
),
68 CFG_STR(CONFIG_KEY_FORMAT
, NULL
, CFGF_NODEFAULT
),
69 CFG_STR(CONFIG_KEY_PATH
, NULL
, CFGF_NODEFAULT
),
70 CFG_STR(CONFIG_KEY_PASSWORD
, NULL
, CFGF_NODEFAULT
),
71 CFG_STR(CONFIG_OTP_METHOD
, NULL
, CFGF_NODEFAULT
),
72 CFG_STR(CONFIG_OTP_CODE
, NULL
, CFGF_NODEFAULT
),
73 /*CFG_STR(CONFIG_DEBUGLEVEL, NULL, CFGF_NODEFAULT),*/
74 CFG_BOOL(CONFIG_VERIFYSERVER
, cfg_true
, CFGF_NONE
),
75 CFG_STR(CONFIG_CAFILE
, NULL
, CFGF_NODEFAULT
),
76 CFG_STR(CONFIG_CADIRECTORY
, NULL
, CFGF_NODEFAULT
),
77 CFG_BOOL(CONFIG_CLEAN_TEMPORARY_FILES
, cfg_true
, CFGF_NONE
),
78 CFG_STR(CONFIG_CRLFILE
, NULL
, CFGF_NODEFAULT
),
79 CFG_INT(CONFIG_TIMEOUT
, TIMEOUT
, CFGF_NONE
),
80 CFG_STR_LIST(CONFIG_LOGFACILITIES
, "{none}", CFGF_NONE
),
81 CFG_STR(CONFIG_LOGFILE
, NULL
, CFGF_NODEFAULT
),
82 CFG_INT(CONFIG_LOGLEVEL
, LOG_LEVEL
, CFGF_NONE
),
83 CFG_BOOL(CONFIG_MARKMESSAGEREAD
, cfg_false
, CFGF_NONE
),
84 CFG_BOOL(CONFIG_NORMALIZEMIMETYPE
, cfg_true
, CFGF_NONE
),
85 CFG_STR_LIST(CONFIG_OPENCOMMAND
, "{xdg-open, %f}", CFGF_NONE
),
86 CFG_BOOL(CONFIG_OVERWRITEFILES
, cfg_true
, CFGF_NONE
),
97 struct command (*commands
)[] = NULL
;
101 struct isds_ctx
*cisds
= NULL
;
102 struct isds_list
*boxes
= NULL
;
103 struct isds_message
*message
= NULL
;
104 _Bool messages_are_outgoing
= 0;
105 struct isds_list
*messages
= NULL
;
106 unsigned long int total_messages
= 0;
107 struct isds_ctx
*czechpoint
= NULL
;
108 struct isds_list
*temporary_files
= NULL
;
110 /* Temporary log-in settings */
112 char *username
= NULL
;
113 char *password
= NULL
;
114 char *key_password
= NULL
;
115 char *otp_method
= NULL
;
116 char *otp_code
= NULL
;
117 char *pki_engine
= NULL
;
118 char *pki_certificate_path
= NULL
;
119 char *pki_certificate_format
= NULL
;
120 char *pki_key_path
= NULL
;
121 char *pki_key_format
= NULL
;
123 static void discard_credentials(void) {
131 zfree(pki_certificate_path
);
132 zfree(pki_certificate_format
);
134 zfree(pki_key_format
);
138 /* Remove temporary file */
139 void shi_unlink_temporary_file(void **data
) {
140 if (!data
|| !*data
) return;
142 const char *file
= (const char *)*data
;
148 /* Finish ISDS operation, report error, if the operation returned different
149 * code than @positive_code. */
150 static void finish_isds_operation_with_code(struct isds_ctx
*ctx
,
151 isds_error err
, isds_error positive_code
) {
152 shi_progressbar_finish();
153 if (err
!= positive_code
) {
154 if (isds_long_message(ctx
))
155 fprintf(stderr
, _("Error occurred: %s: %s\n"), isds_strerror(err
),
156 isds_long_message(ctx
));
158 fprintf(stderr
, _("Error occurred: %s\n"), isds_strerror(err
));
163 /* Finish ISDS operation, report error, if the operation did not returned
165 static void finish_isds_operation(struct isds_ctx
*ctx
, isds_error err
) {
166 finish_isds_operation_with_code(ctx
, err
, IE_SUCCESS
);
170 /* Do the cleanup and exit */
171 static void shi_exit(int exit_code
) {
173 discard_credentials();
174 isds_list_free(&boxes
);
175 isds_message_free(&message
);
176 isds_list_free(&messages
);
177 if (temporary_files
) {
178 oprintf(_("Removing temporary files...\n"));
179 isds_list_free(&temporary_files
);
184 oprintf(_("Logging out...\n"));
185 err
= isds_logout(cisds
);
186 finish_isds_operation(cisds
, err
);
187 if (err
) exit_code
= EXIT_FAILURE
;
188 isds_ctx_free(&cisds
);
190 isds_ctx_free(&czechpoint
);
194 cfg_free(configuration
);
203 /* Set prompt. if @format is NULL, switch to default prompt */
204 static void set_prompt(const char *format
, ...) {
208 va_start(ap
, format
);
209 shi_vasprintf(&buffer
, format
, ap
);
213 shi_asprintf(&prompt
, _("%s> "), buffer
);
222 prompt
= strdup(_("> "));
230 static int shi_load_configuration(const char *config_file
) {
231 char *config_name
= NULL
;
234 /* Get config file */
236 config_name
= (char *) config_file
;
238 if (-1 == shi_asprintf(&config_name
, "%s/%s", getenv("HOME"),
240 fprintf(stderr
, _("Could not build configuration file name\n"));
245 /* Parse configuration */
246 configuration
= cfg_init(configuration_syntax
, CFGF_NONE
);
247 ret
= cfg_parse(configuration
, config_name
);
249 if (ret
== CFG_FILE_ERROR
) {
251 _("Error while opening configuration file `%s': %s\n"),
252 config_name
, strerror(errno
));
254 fprintf(stderr
, _("Error while parsing configuration file `%s'\n"),
257 oprintf(_("Using default configuration\n"));
260 if (config_name
!= config_file
) free(config_name
);
265 void logger(isds_log_facility facility
, isds_log_level level
,
266 const char *message
, int length
, void *data
) {
268 ssize_t written
, left
= length
;
271 fd
= *((int *) data
);
272 /*printf("\033[32mLOG(%02d,%02d): ", facility, level);
273 printf("%.*s", length, message);
277 written
= write(fd
, message
+ length
- left
, left
);
280 _("Could not save log message into log file: %s\n"
281 "Log message discarded!\n"),
292 /* Redirect ISDS log to file if @file is not NULL. */
293 static int do_log_to_file(const char *file
) {
295 logger_fd
= open_file_for_writing(file
, 0, 1);
296 if (logger_fd
== -1) {
297 fprintf(stderr
, _("Could not redirect ISDS log to file `%s'\n"),
301 isds_set_log_callback(logger
, &logger_fd
);
307 /* Add log facility based on its name. */
308 static int add_log_facility(isds_log_facility
*facilities
, const char *name
) {
309 if (!facilities
) return -1;
311 if (!strcmp(name
, "none")) *facilities
|= ILF_NONE
;
312 else if (!strcmp(name
, "http")) *facilities
|= ILF_HTTP
;
313 else if (!strcmp(name
, "soap")) *facilities
|= ILF_SOAP
;
314 else if (!strcmp(name
, "isds")) *facilities
|= ILF_ISDS
;
315 else if (!strcmp(name
, "file")) *facilities
|= ILF_FILE
;
316 else if (!strcmp(name
, "sec")) *facilities
|= ILF_SEC
;
317 else if (!strcmp(name
, "xml")) *facilities
|= ILF_XML
;
318 else if (!strcmp(name
, "all")) *facilities
|= ILF_ALL
;
320 fprintf(stderr
, _("%s: Unknown log facility\n"), name
);
328 /* Save log facility into confuse configuration */
329 static void save_log_facility(int level
) {
330 cfg_setlist(configuration
, CONFIG_LOGFACILITIES
, 0);
332 if (level
== ILF_ALL
) {
333 cfg_addlist(configuration
, CONFIG_LOGFACILITIES
, 1, "all");
336 if (level
== ILF_NONE
) {
337 cfg_addlist(configuration
, CONFIG_LOGFACILITIES
, 1, "none");
340 if (level
& ILF_HTTP
)
341 cfg_addlist(configuration
, CONFIG_LOGFACILITIES
, 1, "http");
342 if (level
& ILF_SOAP
)
343 cfg_addlist(configuration
, CONFIG_LOGFACILITIES
, 1, "soap");
344 if (level
& ILF_ISDS
)
345 cfg_addlist(configuration
, CONFIG_LOGFACILITIES
, 1, "isds");
346 if (level
& ILF_FILE
)
347 cfg_addlist(configuration
, CONFIG_LOGFACILITIES
, 1, "file");
349 cfg_addlist(configuration
, CONFIG_LOGFACILITIES
, 1, "sec");
351 cfg_addlist(configuration
, CONFIG_LOGFACILITIES
, 1, "xml");
355 /* Clamp long int to unsigned int */
356 static unsigned int normalize_timeout(long int raw
) {
358 oprintf(_("Configured network timeout is less then 0. "
362 if (raw
> UINT_MAX
) {
363 oprintf(_("Configured network timeout is greater then %1$u. "
364 "Clamped to %1$u.\n"), UINT_MAX
);
367 return (unsigned int) raw
;
371 /* Clamp long int to <0;100> */
372 static unsigned int normalize_log_level(long int raw
) {
374 oprintf(_("Configured log level is less then 0. Clamped to 0.\n"));
378 oprintf(_("Configured log level is greater then %1$u. "
379 "Clamped to %1$u.\n"), ILL_ALL
);
382 if (raw
> UINT_MAX
) {
383 oprintf(_("Configured log level is greater then %1$u. "
384 "Clamped to %1$u.\n"), UINT_MAX
);
387 return (unsigned int) raw
;
391 static int shi_init(const char *config_file
) {
394 unsigned int timeout
, log_level
;
395 isds_log_facility log_facility
= ILF_NONE
;
397 oprintf(_("This is Shigofumi, an ISDS client. "
398 "Have a nice e-government.\n"));
400 /* Do not permute arguments in getopt() */
401 if (setenv("POSIXLY_CORRECT", "", 1)) {
403 _("Could not set POSIXLY_CORRECT environment variable\n"));
407 /* Load configuration */
408 if (shi_load_configuration(config_file
))
410 timeout
= normalize_timeout(cfg_getint(configuration
, CONFIG_TIMEOUT
));
411 log_level
= normalize_log_level(cfg_getint(configuration
, CONFIG_LOGLEVEL
));
414 rl_readline_name
= "shigofumi";
415 rl_filename_quote_characters
= "\\ >";
416 rl_filename_quoting_function
= shi_quote_filename
;
417 rl_filename_dequoting_function
= shi_dequote_filename
;
418 rl_char_is_quoted_p
= shi_char_is_quoted
;
420 /* Initialize ISDS */
423 fprintf(stderr
, _("Could not initialize libisds library: %s\n"),
428 /* Set ISDS logging */
429 value
= cfg_getstr(configuration
, CONFIG_LOGFILE
);
430 if (do_log_to_file(value
))
432 for (int i
= 0; i
< cfg_size(configuration
, CONFIG_LOGFACILITIES
); i
++) {
433 if (add_log_facility(&log_facility
,
434 cfg_getnstr(configuration
, CONFIG_LOGFACILITIES
, i
)))
438 isds_set_logging(log_facility
, log_level
);
440 /* Set ISDS context up */
441 cisds
= isds_ctx_create();
443 fprintf(stderr
, _("Could not create ISDS context\n"));
446 err
= isds_set_timeout(cisds
, timeout
);
448 fprintf(stderr
, _("Could not set ISDS network timeout: %s\n"),
451 err
= isds_set_progress_callback(cisds
, shi_progressbar
, NULL
);
453 fprintf(stderr
, _("Could not register network progress bar: %s: %s\n"),
454 isds_strerror(err
), isds_long_message(cisds
));
456 err
= isds_set_opt(cisds
, IOPT_NORMALIZE_MIME_TYPE
,
457 cfg_getbool(configuration
, CONFIG_NORMALIZEMIMETYPE
));
460 cfg_getbool(configuration
, CONFIG_NORMALIZEMIMETYPE
) ?
461 _("Could not enable MIME type normalization: %s: %s\n") :
462 _("Could not disable MIME type normalization: %s: %s\n"),
463 isds_strerror(err
), isds_long_message(cisds
));
465 if (!cfg_getbool(configuration
, CONFIG_VERIFYSERVER
)) {
466 oprintf(_("Warning: Shigofumi disabled server identity verification "
467 "on user request!\n"));
468 err
= isds_set_opt(cisds
, IOPT_TLS_VERIFY_SERVER
, 0);
471 _("Could not disable server identity verification: "
473 isds_strerror(err
), isds_long_message(cisds
));
476 if ((value
= cfg_getstr(configuration
, CONFIG_CAFILE
))) {
477 err
= isds_set_opt(cisds
, IOPT_TLS_CA_FILE
, value
);
480 _("Could not set file with CA certificates: %s: %s: %s\n"),
481 value
, isds_strerror(err
), isds_long_message(cisds
));
484 if ((value
= cfg_getstr(configuration
, CONFIG_CADIRECTORY
))) {
485 err
= isds_set_opt(cisds
, IOPT_TLS_CA_DIRECTORY
, value
);
488 _("Could not set directory with CA certificates: "
490 value
, isds_strerror(err
), isds_long_message(cisds
));
493 if ((value
= cfg_getstr(configuration
, CONFIG_CRLFILE
))) {
494 err
= isds_set_opt(cisds
, IOPT_TLS_CRL_FILE
, value
);
496 fprintf(stderr
, _("Could not set file with CRL: %s: %s: %s\n"),
497 value
, isds_strerror(err
), isds_long_message(cisds
));
502 /* Set Czech POINT context up */
503 czechpoint
= isds_ctx_create();
505 fprintf(stderr
, _("Could not create Czech POINT context\n"));
508 err
= isds_set_timeout(czechpoint
, timeout
);
510 fprintf(stderr
, _("Could not set Czech POINT network timeout: %s\n"),
513 err
= isds_set_progress_callback(czechpoint
, shi_progressbar
, NULL
);
515 fprintf(stderr
, "Could not register network progress bar: %s: %s\n",
516 isds_strerror(err
), isds_long_message(cisds
));
523 static int shi_quit(int argc
, const char **argv
) {
524 shi_exit(EXIT_SUCCESS
);
530 static void shi_help_usage(const char *command
) {
532 "Usage: %s [COMMAND]\n"
533 "Show COMMAND manual or list of currently available commands.\n"
539 static int shi_help(int argc
, const char **argv
) {
540 size_t command_width
= 14;
544 fprintf(stderr
, _("No command is available\n"));
548 if (argc
== 2 && argv
[1] && *argv
[1]) {
549 /* Show usage for given command */
550 for (i
= 0; (*commands
)[i
].name
; i
++) {
551 if (!strcmp((*commands
)[i
].name
, argv
[1])) {
552 if ((*commands
)[i
].usage
)
553 (*commands
)[i
].usage((*commands
)[i
].name
);
554 else if ((*commands
)[i
].description
) {
555 ohprint((*commands
)[i
].name
, command_width
);
556 oprintf(" %s\n", _((*commands
)[i
].description
));
560 _("%s: %s: Command description not defined\n"),
565 fprintf(stderr
, _("%s: %s: No such command exists\n"), argv
[0], argv
[1]);
569 /* Or list all commands */
570 oprintf(_("Following commands are available:\n"));
571 for (i
= 0; (*commands
)[i
].name
; i
++) {
572 ohprint((*commands
)[i
].name
, command_width
);
573 oprintf(" %s\n", _((*commands
)[i
].description
));
580 static void show_version(void) {
581 char *libisds_version
= isds_version();
583 oprintf(_("This is Shigofumi version %s.\n"), PACKAGE_VERSION
);
589 rl_library_version
, libisds_version
);
590 free(libisds_version
);
594 static int shi_version(int argc
, const char **argv
) {
599 "It's a shigofumi. A letter delivered from the afterlife. (Fumika)\n"
600 "A message can not be delivered to dead person. (ISDS specification)\n"
601 "Virtual and real world. They can be compatible. (Program author)\n"
607 static int shi_copying(int argc
, const char **argv
) {
609 "This is Shigofumi, an ISDS client.\n"
610 "Copyright (C) 2010, 2011, 2012 Petr Pisar\n"
612 "This program is free software: you can redistribute it and/or modify\n"
613 "it under the terms of the GNU General Public License as published by\n"
614 "the Free Software Foundation, either version 3 of the License, or\n"
615 "(at your option) any later version.\n"
617 "This program is distributed in the hope that it will be useful,\n"
618 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
619 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
620 "GNU General Public License for more details.\n"
622 "You should have received a copy of the GNU General Public License\n"
623 "along with this program. If not, see <http://www.gnu.org/licenses/>.\n"
629 static int shi_cache(int argc
, const char **argv
) {
630 const struct isds_list
*item
;
634 for (item
= boxes
, i
= 0; item
; item
= item
->next
, i
++);
636 "Cached box list: %zu\n"),
642 "Cached message list:\n"
644 "\tMessages: %'lu\n"),
645 (messages_are_outgoing
) ? _("Outgoing") : _("Incoming"),
650 oprintf(_("Cached message: %s\n"),
651 (message
->envelope
&& message
->envelope
->dmID
) ?
652 message
->envelope
->dmID
: _("<Unknown ID>"));
659 static void shi_chdir_usage(const char *command
) {
661 "Usage: %s [DIRECTORY]\n"
662 "Change working directory to DIRECTORY.\n"
663 "If no DIRECTORY is supplied, HOME directory will be used.\n"),
668 static int shi_chdir(int argc
, const char **argv
) {
669 const char *directory
= NULL
;
671 if (!argv
|| argc
> 2) {
672 shi_chdir_usage((argv
) ? argv
[0] : NULL
);
676 if (argc
== 2 && argv
[1] && *argv
[1])
679 directory
= getenv("HOME");
681 oprintf("Environment variable HOME does not exist\n");
685 if (chdir(directory
)) {
686 oprintf(_("Could not change working directory: %s: %s\n"), directory
,
695 static int shi_pwd(int argc
, const char **argv
) {
696 char *buffer
= NULL
, *newbuffer
;
699 while (length
+= 1024) {
700 newbuffer
= realloc(buffer
, length
);
702 fprintf(stderr
, _("Error: Not enough memory\n"));
708 if (getcwd(buffer
, length
)) {
709 oprintf("%s\n", buffer
);
715 fprintf(stderr
, _("Error: Current directory string is too long\n"));
721 /* Deallocate *@destination and duplicate @new_value if non-NULL. In case of
722 * error, it prints error message and returns -1. Otherwise it returns 0. */
723 static int replace_string(char **destination
, const char *new_value
) {
724 if (destination
== NULL
) return -1;
726 if (new_value
!= NULL
) {
727 *destination
= strdup(new_value
);
728 if (*destination
== NULL
) {
729 fprintf(stderr
, _("Not enough memory\n"));
737 /* Convert name of PKI format into ISDS format type.
738 * Return -1 in case of invalid name */
739 static int string2pki_format(const char *name
, isds_pki_format
*format
) {
740 if (!name
|| !format
) { return -1; }
741 if (!strcasecmp(name
, "PEM")) {
742 *format
= PKI_FORMAT_PEM
;
743 } else if (!strcasecmp(name
, "DER")) {
744 *format
= PKI_FORMAT_DER
;
745 } else if (!strcasecmp(name
, "ENG")) {
746 *format
= PKI_FORMAT_ENG
;
754 /* Convert name of OTP authentication method into ISDS method type.
755 * Return -1 in case of invalid name */
756 static int string2otp_method(const char *name
, isds_otp_method
*method
) {
757 if (!name
|| !method
) { return -1; }
758 if (!strcasecmp(name
, "HOTP")) {
760 } else if (!strcasecmp(name
, "TOTP")) {
770 * Return -1 in case of failure, 0 in case of success, +1 in case of partial
771 * success (e.g. TOTP preauthentication to obtain new code succeeded, but user
772 * is still not logged in because second phase is necessary). */
773 static int do_login(void) {
775 struct isds_pki_credentials pki
;
778 /* Build OTP structure */
779 if (NULL
!= otp_method
) {
780 if (string2otp_method(otp_method
, &otp
.method
)) {
781 fprintf(stderr
, _("Error: Invalid one-time password "
782 "authentication method `%s'\n"), otp_method
);
787 /* Announce base URL */
788 oprintf(_("ISDS base URL: %s\n"),
789 (server
== NULL
) ? _("<default>") : server
);
792 oprintf(_("Unattended mode detected. "
793 "Make sure credentials have been preset.\n"));
795 oprintf(_("You are going to insert credentials for your account.\n"
796 "Leave blank line to choose default value.\n"));
798 select_completition(COMPL_NONE
);
800 /* Ask for user name if not predefined */
801 if (NULL
== username
) {
802 shi_ask_for_string(&username
, _("Input ISDS user name: "),
803 cfg_getstr(configuration
, CONFIG_USERNAME
), batch_mode
);
806 /* Ask for password */
807 shi_ask_for_password(&password
, _("Input ISDS password: "),
808 cfg_getstr(configuration
, CONFIG_PASSWORD
), batch_mode
);
810 /* Ask for key password if PKI authentication requested */
811 if (pki_certificate_path
) {
812 shi_ask_for_password(&key_password
, _("Input private key password: "),
813 cfg_getstr(configuration
, CONFIG_KEY_PASSWORD
), batch_mode
);
816 /* Ask for OTP code if OTP authentication requested */
817 if (NULL
!= otp_method
) {
818 shi_ask_for_password(&otp_code
,
819 (otp
.method
== OTP_TIME
) ?
820 _("Input one-time code (empty to send new one): ") :
821 _("Input one-time code: "),
822 cfg_getstr(configuration
, CONFIG_OTP_CODE
), batch_mode
);
823 otp
.otp_code
= otp_code
;
826 select_completition(COMPL_COMMAND
);
829 /* Build PKI structure */
830 if (pki_certificate_path
) {
831 pki
.engine
= pki_engine
;
832 if (NULL
== pki_certificate_format
) {
833 fprintf(stderr
, _("Error: No certficate format supplied\n"));
836 if (string2pki_format(pki_certificate_format
, &pki
.certificate_format
)) {
837 fprintf(stderr
, _("Error: Invalid certificate format `%s'\n"),
838 pki_certificate_format
);
841 if (NULL
== pki_key_format
) {
842 fprintf(stderr
, _("Error: No private key format supplied\n"));
845 if (string2pki_format(pki_key_format
, &pki
.key_format
)) {
846 fprintf(stderr
, _("Error: Invalid private key format `%s'\n"),
850 pki
.certificate
= pki_certificate_path
;
851 pki
.key
= pki_key_path
;
852 pki
.passphrase
= key_password
;
855 if (NULL
!= otp_method
&& OTP_TIME
== otp
.method
&& NULL
== otp_code
)
856 printf(_("Requesting one-time code from server for "
859 printf(_("Logging in...\n"));
860 err
= isds_login(cisds
, server
, username
, password
,
861 (NULL
!= pki_certificate_path
) ? &pki
: NULL
,
862 (NULL
!= otp_method
) ? &otp
: NULL
);
863 if (NULL
!= otp_method
&& OTP_TIME
== otp
.method
&& NULL
== otp_code
)
864 finish_isds_operation_with_code(cisds
, err
, IE_PARTIAL_SUCCESS
);
866 finish_isds_operation(cisds
, err
);
868 if (IE_PARTIAL_SUCCESS
== err
) {
869 printf(_("OTP code has been sent by ISDS successfully.\n"
870 "Once you receive the code, retry log-in with "
874 printf(_("Log-in failed\n"));
878 oprintf(_("Logged in.\n"));
883 static struct isds_DbOwnerInfo
*do_box(void) {
885 struct isds_DbOwnerInfo
*box
= NULL
;
887 printf(_("Getting box details you are logged in...\n"));
888 err
= isds_GetOwnerInfoFromLogin(cisds
, &box
);
889 finish_isds_operation(cisds
, err
);
895 static int shi_box(int argc
, const char **argv
) {
896 struct isds_DbOwnerInfo
*box
= NULL
;
901 format_DbOwnerInfo(box
);
903 isds_DbOwnerInfo_free(&box
);
908 /* Get info about box with @id.
909 * @id is UTF-8 encoded
910 * Return NULL in case of error, otherwise box description that caller must
912 static struct isds_DbOwnerInfo
*stat_box(const char *id
) {
914 struct isds_DbOwnerInfo criteria
;
915 struct isds_list
*boxes
= NULL
, *item
;
916 struct isds_DbOwnerInfo
*box
= NULL
;
917 char *id_locale
= NULL
;
919 if (!id
|| !*id
) return NULL
;
921 id_locale
= utf82locale(id
);
922 memset(&criteria
, 0, sizeof(criteria
));
923 criteria
.dbID
= (char *) id
;
925 printf(_("Getting details about box with ID `%s'...\n"), id_locale
);
926 err
= isds_FindDataBox(cisds
, &criteria
, &boxes
);
927 finish_isds_operation(cisds
, err
);
930 for(item
= boxes
; item
; item
= item
->next
) {
931 if (!item
->data
) continue;
934 fprintf(stderr
, _("Error: More boxes match ID `%s'\n"), id_locale
);
938 box
= (struct isds_DbOwnerInfo
*) item
->data
;
945 isds_list_free(&boxes
);
951 static void shi_commercialreceiving_usage(const char *command
) {
953 "Usage: %s [-0|-1] [BOX_ID]\n"
954 "Manipulate commercial receiving box status.\n"
955 " -O switch off receiving of commercial messages\n"
956 " -1 switch on receiving of commercial messages\n"
957 " BOX_ID affects box with ID BOX_ID; default is box you are logged in\n"
958 "If no option is given, show current commercial receiving status.\n"),
963 /* Manipulate commercial receiving box status */
964 static int shi_commercialreceiving(int argc
, const char **argv
) {
966 struct isds_DbOwnerInfo
*box
= NULL
;
969 char *box_id
= NULL
, *box_id_locale
= NULL
;
974 while ((opt
= getopt(argc
, (char * const *)argv
, "01")) != -1) {
983 shi_commercialreceiving_usage((argv
)?argv
[0]:NULL
);
987 if (optind
+ 1 < argc
) {
988 fprintf(stderr
, _("Bad invocation\n"));
989 shi_commercialreceiving_usage((argv
)?argv
[0]:NULL
);
993 if (!argv
[optind
] || !*argv
[optind
]) {
994 /* Get current box ID */
996 if (!box
|| !box
->dbID
|| !*box
->dbID
) {
997 isds_DbOwnerInfo_free(&box
);
998 fprintf(stderr
, _("Could not get current box ID\n"));
1001 box_id
= box
->dbID
; static_box_id
= 1;
1002 box_id_locale
= utf82locale(box_id
);
1004 /* Box ID supplied as argument */
1005 box_id_locale
= (char *) argv
[optind
];
1006 box_id
= locale2utf8(box_id_locale
); static_box_id
= 0;
1008 fprintf(stderr
, _("Could not convert box ID `%s' to UTF-8\n"),
1015 if (!box
) box
= stat_box(box_id
);
1017 fprintf(stderr
, _("Could not get details about box ID `%s'\n"),
1023 oprintf(_("Commercial receiving status of box `%s': "), box_id_locale
);
1024 if (!box
->dbOpenAddressing
)
1025 oprintf(_("Unknown\n"));
1026 else if (*box
->dbOpenAddressing
)
1027 oprintf(_("Positive\n"));
1029 oprintf(_("Negative\n"));
1031 char *refnumber
= NULL
;
1033 _("Switching `%s' box commercial receiving on...\n"):
1034 _("Switching `%s' box commercial receiving off...\n"),
1036 err
= isds_switch_commercial_receiving(cisds
, box_id
, action
,
1038 finish_isds_operation(cisds
, err
);
1041 char *refnumber_locale
= utf82locale(refnumber
);
1042 oprintf(_("Commercial receiving status successfully changed. "
1043 "Assigned reference number: %s\n"),
1045 free(refnumber_locale
);
1047 oprintf(_("Commercial receiving status has not been changed.\n"));
1054 if (!static_box_id
) free(box_id
);
1055 else free(box_id_locale
);
1056 isds_DbOwnerInfo_free(&box
);
1061 static void shi_commercialsending_usage(const char *command
) {
1063 "Usage: %s [BOX_ID]\n"
1064 "Retrieve permissions to send commercial messages from a box.\n"
1065 " BOX_ID query permissions for box with ID BOX_ID; default is box you\n"
1066 " are logged in\n"),
1071 /* Retrieve permissions to send commercial messages */
1072 static int shi_commercialsending(int argc
, const char **argv
) {
1074 struct isds_DbOwnerInfo
*box
= NULL
;
1075 struct isds_list
*permissions
= NULL
, *item
;
1077 char *box_id
= NULL
, *box_id_locale
= NULL
;
1078 _Bool static_box_id
;
1083 while ((opt
= getopt(argc
, (char * const *)argv
, "h")) != -1) {
1086 shi_commercialsending_usage((argv
)?argv
[0]:NULL
);
1089 shi_commercialsending_usage((argv
)?argv
[0]:NULL
);
1093 if (optind
+ 1 < argc
) {
1094 fprintf(stderr
, _("Bad invocation\n"));
1095 shi_commercialsending_usage((argv
)?argv
[0]:NULL
);
1099 if (!argv
[optind
] || !*argv
[optind
]) {
1100 /* Get current box ID */
1102 if (!box
|| !box
->dbID
|| !*box
->dbID
) {
1103 isds_DbOwnerInfo_free(&box
);
1104 fprintf(stderr
, _("Could not get current box ID\n"));
1107 box_id
= box
->dbID
; static_box_id
= 1;
1108 box_id_locale
= utf82locale(box_id
);
1110 /* Box ID supplied as argument */
1111 box_id_locale
= (char *) argv
[optind
];
1112 box_id
= locale2utf8(box_id_locale
); static_box_id
= 0;
1114 fprintf(stderr
, _("Could not convert box ID `%s' to UTF-8\n"),
1120 printf(_("Querying `%s' box commercial sending permissions...\n"),
1122 err
= isds_get_commercial_permissions(cisds
, box_id
, &permissions
);
1123 finish_isds_operation(cisds
, err
);
1126 oprintf(_("Permissions to send commercial messages from box `%s':\n"),
1128 for (item
= permissions
, ordinar
= 0; item
; item
=item
->next
) {
1129 if (!item
->data
) continue;
1131 oprintf(_("\n* Permission #%d:\n"), ordinar
);
1132 format_commercial_permission(item
->data
);
1135 oprintf(_("No permission exists.\n"));
1137 oprintf(_("Could not list permissions to send commercial messages "
1138 "from box `%s'.\n"), box_id_locale
);
1142 isds_list_free(&permissions
);
1143 if (!static_box_id
) free(box_id
);
1144 else free(box_id_locale
);
1145 isds_DbOwnerInfo_free(&box
);
1150 static int shi_user(int argc
, const char **argv
) {
1152 struct isds_DbUserInfo
*user
= NULL
;
1154 printf(_("Getting user details you are logged as...\n"));
1155 err
= isds_GetUserInfoFromLogin(cisds
, &user
);
1156 finish_isds_operation(cisds
, err
);
1159 format_DbUserInfo(user
);
1161 isds_DbUserInfo_free(&user
);
1166 static void shi_users_usage(const char *command
) {
1168 "Usage: %s BOX_ID\n"
1169 "Get list of users having access to box with BOX_ID.\n"),
1174 static int shi_users(int argc
, const char **argv
) {
1176 struct isds_list
*users
= NULL
, *item
;
1179 if (!argv
|| !argv
[1] || !*argv
[1]) {
1180 shi_users_usage((argv
)?argv
[0]:NULL
);
1184 printf(_("Getting users of box with ID `%s'...\n"), argv
[1]);
1185 err
= isds_GetDataBoxUsers(cisds
, argv
[1], &users
);
1186 finish_isds_operation(cisds
, err
);
1189 for (item
= users
, ordinar
= 0; item
; item
=item
->next
) {
1190 if (!item
->data
) continue;
1192 oprintf(_("\n* User #%d:\n"), ordinar
);
1193 format_DbUserInfo(item
->data
);
1196 oprintf(_("Empty list of users returned.\n"));
1198 isds_list_free(&users
);
1203 static int show_password_expiration(void) {
1205 struct timeval
*expiration
= NULL
;
1207 err
= isds_get_password_expiration(cisds
, &expiration
);
1208 finish_isds_operation(cisds
, err
);
1210 fprintf(stderr
, "Could not get password expiration time\n");
1214 print_header_timeval(_("Your password expires at"), expiration
);
1220 /* Change password in ISDS */
1221 static int do_passwd(void) {
1222 char *old_password
= NULL
;
1223 char *new_password
= NULL
;
1224 char *new_password2
= NULL
;
1225 struct isds_otp otp
;
1226 char *refnumber
= NULL
;
1227 isds_error err
= IE_ERROR
;
1230 if (replace_string(&otp_method
, cfg_getstr(configuration
, CONFIG_OTP_METHOD
)))
1232 /* Build OTP structure */
1233 if (NULL
!= otp_method
) {
1234 if (string2otp_method(otp_method
, &otp
.method
)) {
1235 fprintf(stderr
, _("Error: Invalid one-time password "
1236 "authentication method `%s'\n"), otp_method
);
1241 select_completition(COMPL_NONE
);
1244 "You are going to change your password. If you don't want to change your\n"
1245 "password, insert empty string or EOF.\n"
1247 "You will be asked for your current (old) password and then for new password.\n"
1248 "ISDS forces some criteria new password must fulfill. Current rules are:\n"
1249 "\tLength: minimal 8, maximal 32 characters\n"
1250 "\tMust contain at least: 1 upper case letter, 1 lower case letter, 1 digit\n"
1251 "\tAllowed alphabet: [a-z][A-Z][0-9][!#$%%&()*+,-.:=?@[]_{}|~]\n"
1252 "\tMust differ from last 255 passwords\n"
1253 "\tMust not contain user ID\n"
1254 "\tMust not contain sequence of three or more same characters\n"
1255 "\tMust not start with `qwert', `asdgf', or `12345'\n"
1256 "Finally, you must repeat your new password to avoid mistakes.\n"
1257 "After password change will be confirmed, you must log in again as password\n"
1258 "is transmitted to server on each request.\n"
1261 old_password
= ask_for_password(_("Old password: "));
1262 if (!old_password
|| *old_password
== '\0') {
1263 fprintf(stderr
, _("No password supplied\n"));
1267 /* Ask for OTP code if OTP authentication requested */
1269 if (NULL
!= otp_method
) {
1270 shi_ask_for_password(&otp_code
,
1271 (otp
.method
== OTP_TIME
) ?
1272 _("One-time code (empty to send new one): ") :
1273 _("One-time code: "),
1274 cfg_getstr(configuration
, CONFIG_OTP_CODE
), batch_mode
);
1275 otp
.otp_code
= otp_code
;
1279 if (NULL
== otp_method
|| NULL
!= otp_code
) {
1280 new_password
= ask_for_password(_("New password: "));
1281 if (!new_password
|| *new_password
== '\0') {
1282 fprintf(stderr
, _("No password supplied\n"));
1286 new_password2
= ask_for_password(_("Repeat new password: "));
1287 if (!new_password2
|| new_password2
== '\0') {
1288 fprintf(stderr
, _("No password supplied\n"));
1292 if (strcmp(new_password
, new_password2
)) {
1293 fprintf(stderr
, _("New passwords differ\n"));
1298 if (NULL
!= otp_method
&& OTP_TIME
== otp
.method
&& NULL
== otp_code
)
1299 printf(_("Requesting one-time code from server for "
1300 "a password change...\n"));
1302 printf(_("Changing password...\n"));
1303 err
= isds_change_password(cisds
, old_password
, new_password
,
1304 (NULL
!= otp_method
) ? &otp
: NULL
, &refnumber
);
1305 if (NULL
!= otp_method
&& OTP_TIME
== otp
.method
&& NULL
== otp_code
)
1306 finish_isds_operation_with_code(cisds
, err
, IE_PARTIAL_SUCCESS
);
1308 finish_isds_operation(cisds
, err
);
1310 if (NULL
!= refnumber
) {
1311 char *refnumber_locale
= utf82locale(refnumber
);
1313 oprintf(_("Assigned reference number: %s\n"), refnumber_locale
);
1314 free(refnumber_locale
);
1316 if (IE_PARTIAL_SUCCESS
== err
) {
1317 oprintf(_("OTP code has been sent by ISDS successfully.\n"
1318 "Once you receive the code, retry changing password with "
1322 printf(_("Password change failed\n"));
1325 oprintf(_("Password HAS been successfully changed.\n"));
1331 oprintf(_("Password has NOT been changed!\n"));
1336 free(new_password2
);
1340 select_completition(COMPL_COMMAND
);
1343 "Remember, ISDS password has limited life time.\n"));
1348 static void shi_passwd_usage(const char *command
) {
1351 "Manipulate user password or change it if no option given.\n"
1354 " -S show password expiration time\n"
1359 static int shi_passwd(int argc
, const char **argv
) {
1363 while ((opt
= getopt(argc
, (char * const *)argv
, "S")) != -1) {
1366 return show_password_expiration();
1368 shi_passwd_usage(argv
[0]);
1372 if (optind
!= argc
|| argc
> 1) {
1373 fprintf(stderr
, _("Bad invocation\n"));
1374 shi_passwd_usage((argv
)?argv
[0]:NULL
);
1382 static void shi_login_usage(const char *command
) {
1384 "Usage: %s [OPTIONS] [USER_NAME]\n"
1385 "Attemp to log into ISDS server.\n"
1388 " -b URL ISDS server base URL\n"
1389 " -c IDENTIFIER user certificate\n"
1390 " -C FORMAT user certificate format\n"
1391 " -k IDENTIFIER user private key\n"
1392 " -K FORMAT user private key format\n"
1393 " -e IDENTIFIER cryptographic engine\n"
1394 " -o METHOD use one-time password authentication method\n"
1396 "Recognized certificate and key FORMATS are:\n"
1397 " PEM Base64 encoded serialization in local file\n"
1398 " DER binary serialization in local file\n"
1399 " ENG material is stored in cryptographic engine\n"
1400 "Identifiers of cryptographic engine, certificate, and private key are\n"
1401 "specific for underlying cryptographic library.\n"
1403 "Recognized one-time password methods are:\n"
1404 " HOTP HMAC-based OTP method\n"
1405 " TOTP time-based OTP method\n"
1407 "Values of omitted options are taken from configuration file.\n"
1412 static int shi_login(int argc
, const char **argv
) {
1416 discard_credentials();
1418 /* Load stored configuration */
1419 if (replace_string(&server
, cfg_getstr(configuration
, CONFIG_SERVER
)))
1421 if (replace_string(&otp_method
, cfg_getstr(configuration
, CONFIG_OTP_METHOD
)))
1423 if (replace_string(&pki_engine
,
1424 cfg_getstr(configuration
, CONFIG_KEY_ENGINE
)))
1426 if (replace_string(&pki_certificate_path
,
1427 cfg_getstr(configuration
, CONFIG_CERT_PATH
)))
1429 if (replace_string(&pki_certificate_format
,
1430 cfg_getstr(configuration
, CONFIG_CERT_FORMAT
)))
1432 if (replace_string(&pki_key_path
,
1433 cfg_getstr(configuration
, CONFIG_KEY_PATH
)))
1435 if (replace_string(&pki_key_format
,
1436 cfg_getstr(configuration
, CONFIG_KEY_FORMAT
)))
1439 /* Override configuration with positional arguments */
1441 while ((opt
= getopt(argc
, (char * const *)argv
, "b:c:C:k:K:e:o:")) != -1) {
1444 if (replace_string(&server
, optarg
)) return -1;
1447 if (replace_string(&pki_certificate_path
, optarg
)) return -1;
1450 if (replace_string(&pki_certificate_format
, optarg
)) return -1;
1453 if (replace_string(&pki_key_path
, optarg
)) return -1;
1456 if (replace_string(&pki_key_format
, optarg
)) return -1;
1459 if (replace_string(&pki_engine
, optarg
)) return -1;
1462 if (replace_string(&otp_method
, optarg
)) return -1;
1465 shi_login_usage(argv
[0]);
1469 if (optind
< argc
- 1) {
1470 fprintf(stderr
, _("Bad invocation\n"));
1471 shi_login_usage(argv
[0]);
1474 if (optind
== argc
- 1) {
1475 username
= strdup(argv
[optind
]);
1478 /* Proceed log-in */
1479 status
= do_login();
1480 if (status
< 0) return -1;
1482 /* If log-in passed, store configuration */
1483 cfg_setstr(configuration
, CONFIG_SERVER
, server
);
1484 cfg_setstr(configuration
, CONFIG_USERNAME
, username
);
1485 cfg_setstr(configuration
, CONFIG_PASSWORD
, password
);
1486 cfg_setstr(configuration
, CONFIG_KEY_PASSWORD
, key_password
);
1487 cfg_setstr(configuration
, CONFIG_OTP_METHOD
, otp_method
);
1488 cfg_setstr(configuration
, CONFIG_KEY_ENGINE
, pki_engine
);
1489 cfg_setstr(configuration
, CONFIG_CERT_PATH
, pki_certificate_path
);
1490 cfg_setstr(configuration
, CONFIG_CERT_FORMAT
, pki_certificate_format
);
1491 cfg_setstr(configuration
, CONFIG_KEY_PATH
, pki_key_path
);
1492 cfg_setstr(configuration
, CONFIG_KEY_FORMAT
, pki_key_format
);
1494 /* Get some details only if fully logged in */
1496 show_password_expiration();
1502 static void shi_debug_usage(const char *command
) {
1504 "Usage: %s -l LEVEL [-f FACILITY...] [{-e | -o FILE}]\n"
1505 "Debug FACILITIES on LEVEL.\n"
1507 "-l LEVEL set log level, valid interval <%d,%d>, default is %d\n"
1508 " %d is no logging, %d critical, %d errors,\n"
1509 " %d warnings, %d info, %d debug, %d all\n"
1510 "-f FACILITY debug only given facility, repeat this option to debug\n"
1511 " more facilities; valid values: none, http, soap, isds,\n"
1512 " file, sec, xml, all; default is none\n"
1513 "-e write debug log into stderr\n"
1514 "-o FILE append debug log to FILE\n"
1517 ILL_NONE
, ILL_ALL
, ILL_NONE
,
1518 ILL_NONE
, ILL_CRIT
, ILL_ERR
, ILL_WARNING
,
1519 ILL_INFO
, ILL_DEBUG
, ILL_ALL
);
1522 static int shi_debug(int argc
, const char **argv
) {
1524 int log_level
= ILL_NONE
;
1525 isds_log_facility log_facility
= ILF_NONE
;
1527 _Bool close_log
= 0;
1530 while ((opt
= getopt(argc
, (char * const *)argv
, "l:f:eo:")) != -1) {
1533 log_level
= normalize_log_level(atoi(optarg
));
1536 if (add_log_facility(&log_facility
, optarg
)) return -1;
1544 shi_debug_usage(argv
[0]);
1548 if (optind
== 1 || optind
!= argc
) {
1549 fprintf(stderr
, _("Bad invocation\n"));
1550 shi_debug_usage(argv
[0]);
1556 isds_set_log_callback(NULL
, NULL
);
1558 if (logger_fd
!= -1) {
1559 if (-1 == close(logger_fd
)) {
1560 fprintf(stderr
, _("Closing log file failed: %s\n"),
1565 cfg_setstr(configuration
, CONFIG_LOGFILE
, NULL
);
1567 if (do_log_to_file(file
))
1569 if (file
) cfg_setstr(configuration
, CONFIG_LOGFILE
, file
);
1571 /* Set log levels */
1572 isds_set_logging(log_facility
, log_level
);
1573 cfg_setint(configuration
, CONFIG_LOGLEVEL
, log_level
);
1574 save_log_facility(log_facility
);
1580 static void show_setting_str(const char *variable
, _Bool cenzore
) {
1581 if (!variable
) return;
1583 const char *value
= cfg_getstr(configuration
, variable
);
1587 oprintf(_("%s = <set>\n"), variable
);
1589 oprintf(_("%s = `%s'\n"), variable
, value
);
1591 oprintf(_("%s = <unset>\n"), variable
);
1596 static void show_setting_boolean(const char *variable
) {
1597 if (!variable
) return;
1599 _Bool value
= cfg_getbool(configuration
, variable
);
1602 oprintf(_("%s = <true>\n"), variable
);
1604 oprintf(_("%s = <false>\n"), variable
);
1609 static void show_setting_int(const char *variable
) {
1610 if (!variable
) return;
1612 long int value
= cfg_getint(configuration
, variable
);
1614 oprintf(_("%s = %ld\n"), variable
, value
);
1618 static void show_setting_strlist(const char *variable
) {
1619 if (!variable
) return;
1621 int length
= cfg_size(configuration
, variable
);
1624 oprintf(_("%s = <unset>\n"), variable
);
1626 oprintf(_("%s = {"), variable
);
1627 for (int i
= 0; i
< length
; i
++) {
1628 const char *value
= cfg_getnstr(configuration
, variable
, i
);
1630 oprintf(_("`%s', "), value
);
1632 oprintf(_("`%s'}\n"), value
);
1638 static int shi_settings(int argc
, const char **argv
) {
1640 int log_level = ILL_NONE;
1641 isds_log_facility log_facility = ILF_NONE;
1643 _Bool close_log = 0;*/
1647 while ((opt = getopt(argc, (char * const *)argv, "l:f:eo:")) != -1) {
1650 log_level = normalize_log_level(atoi(optarg));
1653 if (add_log_facility(&log_facility, optarg)) return -1;
1661 shi_debug_usage(argv[0]);
1665 if (optind == 1 || optind != argc) {
1666 printf(_("Bad invocation\n"));
1667 shi_debug_usage(argv[0]);
1672 oprintf(_("Current settings:\n"));
1674 show_setting_str(CONFIG_SERVER
, 0);
1675 show_setting_str(CONFIG_USERNAME
, 0);
1676 show_setting_str(CONFIG_PASSWORD
, 1),
1677 show_setting_str(CONFIG_CERT_FORMAT
, 0),
1678 show_setting_str(CONFIG_CERT_PATH
, 0),
1679 show_setting_str(CONFIG_KEY_ENGINE
, 0),
1680 show_setting_str(CONFIG_KEY_FORMAT
, 0),
1681 show_setting_str(CONFIG_KEY_PATH
, 0),
1682 show_setting_str(CONFIG_KEY_PASSWORD
, 1),
1683 show_setting_str(CONFIG_OTP_METHOD
, 0),
1684 show_setting_str(CONFIG_OTP_CODE
, 1),
1685 show_setting_boolean(CONFIG_VERIFYSERVER
);
1686 show_setting_str(CONFIG_CAFILE
, 0);
1687 show_setting_str(CONFIG_CADIRECTORY
, 0);
1688 show_setting_boolean(CONFIG_CLEAN_TEMPORARY_FILES
);
1689 show_setting_str(CONFIG_CRLFILE
, 0);
1690 show_setting_int(CONFIG_TIMEOUT
);
1691 show_setting_strlist(CONFIG_LOGFACILITIES
);
1692 show_setting_str(CONFIG_LOGFILE
, 0);
1693 show_setting_int(CONFIG_LOGLEVEL
);
1694 show_setting_boolean(CONFIG_MARKMESSAGEREAD
);
1695 show_setting_boolean(CONFIG_NORMALIZEMIMETYPE
);
1696 show_setting_strlist(CONFIG_OPENCOMMAND
);
1697 show_setting_boolean(CONFIG_OVERWRITEFILES
);
1703 static void shi_find_box_usage(const char *command
) {
1705 "Usage: %s {OPTION... | BOX_ID}\n"
1706 "Get information about box with BOX_ID or boxes meeting other criteria.\n"
1707 "Each search option requires an argument:\n"
1708 " -t box type; accepted values:\n"
1709 " FO Private individual\n"
1710 " PFO Self-employed individual\n"
1711 " PFO_ADVOK Lawyer\n"
1712 " PFO_DANPOR Tax advisor\n"
1713 " PFO_INSSPR Insolvency administrator\n"
1714 " PO Organisation\n"
1715 " PO_ZAK Organization based by law\n"
1716 " PO_REQ Organization based on request\n"
1717 " OVM Public authority\n"
1718 " OVM_NOTAR Notary\n"
1719 " OVM_EXEKUT Executor\n"
1720 " OVM_REQ Public authority based on request\n"
1721 " -j identity number\n"
1723 "Person name options:\n"
1727 " -b last name at birth\n"
1728 " -s subject name\n"
1731 " -d birth date (locale or full ISO 8601 date)\n"
1733 " -y birth county\n"
1739 " -z number in street\n"
1740 " -Z number in municipality\n"
1747 " -p phone number\n"
1749 " -r registry code\n"
1750 " -a box status; accepted values:\n"
1751 " ACCESSIBLE Accessible\n"
1752 " TEMP_INACCESSIBLE Temporary inaccessible\n"
1753 " NOT_YET_ACCESSIBLE Not yet accessible\n"
1754 " PERM_INACCESSIBLE Permanently inaccessible\n"
1755 " REMOVED Deleted\n"
1756 " -o act as public authority; boolean values: 0 is false, 1 is true\n"
1757 " -k receive commercial messages; boolean values\n"
1759 "Not all option combinations are meaningful or allowed. For example box\n"
1760 "type is always required (except direct box ID query).\n"
1761 "ISDS can refuse to answer to much broad query. Not all boxes are searchable\n"
1768 /* Allow reassignment */
1769 #define FILL_OR_LEAVE(variable, locale) { \
1771 (variable) = locale2utf8(locale); \
1772 if (!(variable)) { \
1773 fprintf(stderr, _("Error: Not enough memory\n")); \
1779 #define CALLOC_OR_LEAVE(structure) { \
1780 if (!(structure)) { \
1781 (structure) = calloc(1, sizeof(*(structure))); \
1782 if (!(structure)) { \
1783 fprintf(stderr, _("Error: Not enough memory\n")); \
1790 #define FILL_BOOLEAN_OR_LEAVE(variable, locale) { \
1792 (variable) = malloc(sizeof(*(variable))); \
1793 if (!(variable)) { \
1794 fprintf(stderr, _("Error: Not enough memory\n")); \
1798 if (!strcmp((locale), "0")) *(variable) = 0; \
1799 else if (!strcmp((locale), "1")) *(variable) = 1; \
1801 fprintf(stderr, _("%s: %s: Unknown boolean value\n"), \
1802 argv[0], (locale)); \
1808 #define FILL_LONGINT_OR_LEAVE(variable, locale) { \
1809 if (!(locale) || !*(locale)) { \
1810 fprintf(stderr, _("%s: Empty integer value\n"), argv[0]); \
1816 (variable) = malloc(sizeof(*(variable))); \
1817 if (!(variable)) { \
1818 fprintf(stderr, _("Error: Not enough memory\n")); \
1822 (*variable) = strtol((locale), &endptr, 0); \
1824 fprintf(stderr, _("%s: %s: Invalid integer value\n"), \
1825 argv[0], (locale)); \
1831 static int shi_find_box(int argc
, const char **argv
) {
1834 struct isds_DbOwnerInfo
*criteria
= NULL
;
1835 struct isds_list
*item
;
1839 if (!argv
|| !argv
[1] || !*argv
[1]) {
1840 fprintf(stderr
, _("Error: No argument supplied\n"));
1841 shi_find_box_usage((argv
)?argv
[0]:NULL
);
1845 criteria
= calloc(1, sizeof(*criteria
));
1847 fprintf(stderr
, _("Error: Not enough memory\n"));
1854 while ((opt
= getopt(argc
, (char * const *)argv
, "t:j:s:"
1855 "f:m:l:b:s:" "d:w:y:c:" "W:S:z:Z:P:C:"
1856 "n:e:p:i:r:a:o:k:")) != -1) {
1859 criteria
->dbType
= malloc(sizeof(*criteria
->dbType
));
1860 if (!criteria
->dbType
) {
1861 fprintf(stderr
, _("Error: Not enough memory\n"));
1865 if (!strcmp(optarg
, "FO"))
1866 *criteria
->dbType
= DBTYPE_FO
;
1867 else if (!strcmp(optarg
, "PFO"))
1868 *criteria
->dbType
= DBTYPE_PFO
;
1869 else if (!strcmp(optarg
, "PFO_ADVOK"))
1870 *criteria
->dbType
= DBTYPE_PFO_ADVOK
;
1871 else if (!strcmp(optarg
, "PFO_DANPOR"))
1872 *criteria
->dbType
= DBTYPE_PFO_DANPOR
;
1873 else if (!strcmp(optarg
, "PFO_INSSPR"))
1874 *criteria
->dbType
= DBTYPE_PFO_INSSPR
;
1875 else if (!strcmp(optarg
, "PO"))
1876 *criteria
->dbType
= DBTYPE_PO
;
1877 else if (!strcmp(optarg
, "PO_ZAK"))
1878 *criteria
->dbType
= DBTYPE_PO_ZAK
;
1879 else if (!strcmp(optarg
, "PO_REQ"))
1880 *criteria
->dbType
= DBTYPE_PO_REQ
;
1881 else if (!strcmp(optarg
, "OVM"))
1882 *criteria
->dbType
= DBTYPE_OVM
;
1883 else if (!strcmp(optarg
, "OVM_NOTAR"))
1884 *criteria
->dbType
= DBTYPE_OVM_NOTAR
;
1885 else if (!strcmp(optarg
, "OVM_EXEKUT"))
1886 *criteria
->dbType
= DBTYPE_OVM_EXEKUT
;
1887 else if (!strcmp(optarg
, "OVM_REQ"))
1888 *criteria
->dbType
= DBTYPE_OVM_REQ
;
1890 fprintf(stderr
, _("%s: %s: Unknown box type\n"),
1898 FILL_OR_LEAVE(criteria
->ic
, optarg
);
1903 CALLOC_OR_LEAVE(criteria
->personName
);
1904 FILL_OR_LEAVE(criteria
->personName
->pnFirstName
, optarg
);
1907 CALLOC_OR_LEAVE(criteria
->personName
);
1908 FILL_OR_LEAVE(criteria
->personName
->pnMiddleName
, optarg
);
1911 CALLOC_OR_LEAVE(criteria
->personName
);
1912 FILL_OR_LEAVE(criteria
->personName
->pnLastName
, optarg
);
1915 CALLOC_OR_LEAVE(criteria
->personName
);
1916 FILL_OR_LEAVE(criteria
->personName
->pnLastNameAtBirth
, optarg
);
1919 FILL_OR_LEAVE(criteria
->firmName
, optarg
);
1924 CALLOC_OR_LEAVE(criteria
->birthInfo
);
1925 criteria
->birthInfo
->biDate
= datestring2tm(optarg
);
1926 if (!criteria
->birthInfo
->biDate
) {
1927 fprintf(stderr
, _("Error: Could not parse date: %s\n"),
1934 CALLOC_OR_LEAVE(criteria
->birthInfo
);
1935 FILL_OR_LEAVE(criteria
->birthInfo
->biCity
, optarg
);
1938 CALLOC_OR_LEAVE(criteria
->birthInfo
);
1939 FILL_OR_LEAVE(criteria
->birthInfo
->biCounty
, optarg
);
1942 CALLOC_OR_LEAVE(criteria
->birthInfo
);
1943 FILL_OR_LEAVE(criteria
->birthInfo
->biState
, optarg
);
1948 CALLOC_OR_LEAVE(criteria
->address
);
1949 FILL_OR_LEAVE(criteria
->address
->adCity
, optarg
);
1952 CALLOC_OR_LEAVE(criteria
->address
);
1953 FILL_OR_LEAVE(criteria
->address
->adStreet
, optarg
);
1956 CALLOC_OR_LEAVE(criteria
->address
);
1957 FILL_OR_LEAVE(criteria
->address
->adNumberInStreet
, optarg
);
1960 CALLOC_OR_LEAVE(criteria
->address
);
1961 FILL_OR_LEAVE(criteria
->address
->adNumberInMunicipality
,
1965 CALLOC_OR_LEAVE(criteria
->address
);
1966 FILL_OR_LEAVE(criteria
->address
->adZipCode
, optarg
);
1969 CALLOC_OR_LEAVE(criteria
->address
);
1970 FILL_OR_LEAVE(criteria
->address
->adState
, optarg
);
1975 FILL_OR_LEAVE(criteria
->nationality
, optarg
);
1978 FILL_OR_LEAVE(criteria
->email
, optarg
);
1981 FILL_OR_LEAVE(criteria
->telNumber
, optarg
);
1984 FILL_OR_LEAVE(criteria
->identifier
, optarg
);
1987 FILL_OR_LEAVE(criteria
->registryCode
, optarg
);
1990 criteria
->dbState
= malloc(sizeof(*criteria
->dbState
));
1991 if (!criteria
->dbState
) {
1992 fprintf(stderr
, _("Error: Not enough memory\n"));
1996 if (!strcmp(optarg
, "ACCESSIBLE"))
1997 *criteria
->dbState
= DBSTATE_ACCESSIBLE
;
1998 else if (!strcmp(optarg
, "TEMP_INACCESSIBLE"))
1999 *criteria
->dbState
= DBSTATE_TEMP_UNACCESSIBLE
;
2000 else if (!strcmp(optarg
, "NOT_YET_ACCESSIBLE"))
2001 *criteria
->dbState
= DBSTATE_NOT_YET_ACCESSIBLE
;
2002 else if (!strcmp(optarg
, "PERM_INACCESSIBLE"))
2003 *criteria
->dbState
= DBSTATE_PERM_UNACCESSIBLE
;
2004 else if (!strcmp(optarg
, "REMOVED"))
2005 *criteria
->dbState
= DBSTATE_REMOVED
;
2007 fprintf(stderr
, _("%s: %s: Unknown box status\n"),
2014 FILL_BOOLEAN_OR_LEAVE(criteria
->dbEffectiveOVM
, optarg
);
2017 FILL_BOOLEAN_OR_LEAVE(criteria
->dbOpenAddressing
, optarg
);
2021 shi_find_box_usage(argv
[0]);
2027 /* There must be an option and all of them must be recognized, if not only
2028 * BOX_ID supplied */
2029 if (argc
> 2 && optind
!= argc
) {
2030 fprintf(stderr
, _("Error: Superfluous argument\n"));
2031 shi_find_box_usage(argv
[0]);
2036 /* If only box ID is supplied use it */
2037 if (argc
== 2 && argv
[1] && *argv
[1]) {
2038 criteria
->dbID
= locale2utf8(argv
[1]);
2039 if (!criteria
->dbID
) {
2040 fprintf(stderr
, _("Error: Not enough memory\n"));
2046 printf(_("Searching boxes...\n"));
2047 err
= isds_FindDataBox(cisds
, criteria
, &boxes
);
2048 finish_isds_operation(cisds
, err
);
2051 for(item
= boxes
; item
; item
= item
->next
) {
2052 if (!item
->data
) continue;
2055 oprintf(_("\n* Result #%d:\n"), order
);
2056 format_DbOwnerInfo(item
->data
);
2060 isds_DbOwnerInfo_free(&criteria
);
2065 static void shi_stat_box_usage(const char *command
) {
2067 "Usage: %s BOX_ID...\n"
2068 "Get status of box with BOX_ID. More boxes can be specified.\n"),
2073 /* Get boxes status */
2074 static int shi_stat_box(int argc
, const char **argv
) {
2079 if (!argv
|| !*argv
|| argc
< 2 || !argv
[1]) {
2080 fprintf(stderr
, _("Missing box ID\n"));
2081 shi_stat_box_usage((argv
[0])?argv
[0]:NULL
);
2085 for (int i
= 1; i
< argc
; i
++) {
2086 if (!argv
[i
] || !*argv
[i
]) continue;
2089 id
= locale2utf8(argv
[i
]);
2091 fprintf(stderr
, _("%s: Could not covert box ID to UTF-8\n"),
2096 printf(_("Getting status of box `%s'...\n"), argv
[i
]);
2097 err
= isds_CheckDataBox(cisds
, id
, &status
);
2098 finish_isds_operation(cisds
, err
);
2101 oprintf(_("Status of box `%s': %s\n"),
2102 argv
[i
], DbState2string(&status
));
2109 static void shi_boxlist_usage(const char *command
) {
2111 "Usage: %s LIST_TYPE FILE\n"
2112 "Save latest snapshot of list of boxes of type LIST_TYPE into FILE.\n"
2114 "Currently recognized LIST_TYPES are:\n"
2116 " UPG Effectively OVM boxes\n"
2117 " OVM OVM gross type boxes\n"
2118 " OPN Boxes allowing receiving commercial messages\n"
2120 "Not all types are available to all users. E.g. only `UPG' is available\n"
2121 "to regular users.\n"
2123 "The format of the list is comma separate list that is packed into\n"
2124 "ZIP archive. Name of the list file denotes time of snapshoting\n"
2125 "the list. The snapshot is created by ISDS once a day.\n"),
2130 /* Download list of boxes */
2131 static int shi_boxlist(int argc
, const char **argv
) {
2133 const char *type_locale
;
2135 void *buffer
= NULL
;
2136 size_t buffer_length
;
2139 if (!argv
|| !*argv
|| argc
< 3 || !argv
[1] || !argv
[2]) {
2140 fprintf(stderr
, _("Bad number of arguments\n"));
2141 shi_boxlist_usage((argv
)?argv
[0]:NULL
);
2144 type_locale
= argv
[1];
2146 type
= locale2utf8(type_locale
);
2148 fprintf(stderr
, _("%s: Could not covert list type to UTF-8\n"),
2153 printf(_("Getting `%s' list of boxes...\n"), type_locale
);
2154 err
= isds_get_box_list_archive(cisds
, type
, &buffer
, &buffer_length
);
2155 finish_isds_operation(cisds
, err
);
2161 retval
= save_data_to_file(argv
[2], -1, buffer
, buffer_length
,
2163 cfg_getbool(configuration
, CONFIG_OVERWRITEFILES
));
2170 static void shi_delivery_usage(const char *command
) {
2172 "Usage: %s MESSAGE_ID\n"
2173 "Get delivery data about message with MESSAGE_ID.\n"),
2178 static int shi_delivery(int argc
, const char **argv
) {
2182 if (!argv
|| !argv
[1] || !*argv
[1]) {
2183 shi_delivery_usage(argv
[0]);
2188 printf(_("Getting delivery info...\n"));
2189 err
= isds_get_signed_delivery_info(cisds
, id
, &message
);
2190 finish_isds_operation(cisds
, err
);
2193 select_completition(COMPL_COMMAND
);
2197 format_message(message
);
2199 if (message
->envelope
&& message
->envelope
->dmID
)
2200 set_prompt(_("%s %s"), argv
[0], message
->envelope
->dmID
);
2202 set_prompt("%s", argv
[0]);
2203 select_completition(COMPL_MSG
);
2208 static int shi_dump_message(int argc
, const char **argv
) {
2210 fprintf(stderr
, _("No message loaded\n"));
2214 print_message(message
);
2219 static void shi_hash_usage(const char *command
) {
2221 "Usage: %s [MESSAGE_ID]\n"
2222 "Retrieve message hash stored in ISDS.\n"
2223 "If MESSAGE_ID is defined, query for that message.\n"
2224 "Otherwise use current message.\n"),
2229 static int shi_hash(int argc
, const char **argv
) {
2231 const char *id
= NULL
;
2232 struct isds_hash
*hash
= NULL
;
2233 char *hash_string
= NULL
;
2235 if (!argv
|| argc
> 2) {
2236 shi_hash_usage((argv
)?argv
[0]:NULL
);
2239 if (argc
== 2 && argv
[1] && *argv
[1])
2243 fprintf(stderr
, _("No message loaded\n"));
2246 if (!message
->envelope
|| !message
->envelope
->dmID
) {
2247 fprintf(stderr
, _("Current message is missing ID\n"));
2250 id
= message
->envelope
->dmID
;
2253 printf(_("Getting message hash...\n"));
2254 err
= isds_download_message_hash(cisds
, id
, &hash
);
2255 finish_isds_operation(cisds
, err
);
2258 hash_string
= hash2string(hash
);
2259 oprintf(_("ISDS states message with `%s' ID has following hash:\n%s\n"),
2263 isds_hash_free(&hash
);
2268 static int shi_verify(int argc
, const char **argv
) {
2271 struct isds_hash
*retrieved_hash
= NULL
, *stored_hash
= NULL
;
2272 char *hash_string
= NULL
;
2276 fprintf(stderr
, _("No message loaded\n"));
2280 if (!message
->envelope
) {
2281 fprintf(stderr
, _("Current message is missing envelope\n"));
2284 stored_hash
= message
->envelope
->hash
;
2285 message
->envelope
->hash
= NULL
;
2287 if (message
->envelope
->dmID
) {
2288 /* Verify remote hash */
2289 oprintf(_("Remote hash check:\n"));
2291 printf(_("Getting message hash...\n"));
2292 err
= isds_download_message_hash(cisds
, message
->envelope
->dmID
,
2294 finish_isds_operation(cisds
, err
);
2296 if (retrieved_hash
) {
2297 hash_string
= hash2string(retrieved_hash
);
2298 ohprint(_("Retrieved:"), width
);
2299 oprintf("%s\n", hash_string
);
2303 if (retrieved_hash
&& message
->raw
) {
2304 err
= isds_compute_message_hash(cisds
, message
,
2305 retrieved_hash
->algorithm
);
2306 finish_isds_operation(cisds
, err
);
2309 hash_string
= hash2string(message
->envelope
->hash
);
2310 ohprint(_("Computed:"), width
);
2311 oprintf("%s\n", hash_string
);
2316 err
= isds_hash_cmp(retrieved_hash
, message
->envelope
->hash
);
2319 oprintf(_("Hashes match.\n")); break;
2321 oprintf(_("Hashes do not match.\n"));
2325 oprintf(_("Hashes could not be compared.\n"));
2330 free(retrieved_hash
);
2335 /* Verify stored hash */
2336 oprintf(_("Stored hash check:\n"));
2338 hash_string
= hash2string(stored_hash
);
2339 ohprint(_("Stored:"), width
);
2340 oprintf("%s\n", hash_string
);
2344 err
= isds_compute_message_hash(cisds
, message
,
2345 stored_hash
->algorithm
);
2346 finish_isds_operation(cisds
, err
);
2349 hash_string
= hash2string(message
->envelope
->hash
);
2350 ohprint(_("Computed:"), width
);
2351 oprintf("%s\n", hash_string
);
2356 err
= isds_hash_cmp(stored_hash
, message
->envelope
->hash
);
2359 oprintf(_("Hashes match.\n")); break;
2361 oprintf(_("Hashes do not match.\n"));
2365 oprintf(_("Hashes could not be compared.\n"));
2370 isds_hash_free(&message
->envelope
->hash
);
2373 message
->envelope
->hash
= stored_hash
;
2378 static int shi_authenticate(int argc
, const char **argv
) {
2383 fprintf(stderr
, _("No message loaded\n"));
2386 if (!message
->raw
|| message
->raw_length
== 0) {
2387 fprintf(stderr
, _("Current message is missing raw representation\n"));
2391 printf(_("Submitting message to authenticity check...\n"));
2392 err
= isds_authenticate_message(cisds
, message
->raw
, message
->raw_length
);
2393 finish_isds_operation(cisds
, (err
== IE_NOTUNIQ
) ? IE_SUCCESS
: err
);
2397 oprintf(_("Message originates in ISDS.\n")); break;
2399 oprintf(_("Message is unknown to ISDS or has been tampered.\n"));
2411 static void shi_accept_message_usage(const char *command
) {
2413 "Usage: %s [MESSAGE_ID...]\n"
2414 "Accept commercial message moving its state to received.\n"
2415 "If MESSAGE_ID is defined, accept that message. More messages can be specified.\n"
2416 "Otherwise accept all commercial incoming messages.\n"),
2421 static int shi_accept_message(int argc
, const char **argv
) {
2425 /* Process messages named in argv */
2426 for (int i
= 1; i
< argc
; i
++) {
2427 if (!argv
[i
] || !*argv
[i
]) continue;
2429 id
= locale2utf8(argv
[i
]);
2432 _("Error: Could not convert message ID to UTF-8: %s\n"),
2437 printf(_("Accepting message `%s'...\n"), argv
[i
]);
2438 err
= isds_mark_message_received(cisds
, id
);
2439 finish_isds_operation(cisds
, err
);
2445 oprintf(_("Message `%s' accepted\n"), argv
[i
]);
2450 /* TODO: list commercial not received messages and accept all of them
2453 _("Error: No message ID supplied. Accepting all commercial "
2454 "messages not implemented yet.\n"));
2462 static void shi_delete_message_usage(const char *command
) {
2464 "Usage: %s {-i|-o} MESSAGE_ID...\n"
2465 "Remove message from long term storage.\n"
2467 " -i Messages are incoming\n"
2468 " -o Messages are outoging\n"),
2473 static int shi_delete_message(int argc
, const char **argv
) {
2477 _Bool direction_specified
= 0;
2481 while ((opt
= getopt(argc
, (char * const *)argv
, "io")) != -1) {
2485 direction_specified
= 1;
2489 direction_specified
= 1;
2492 shi_delete_message_usage((argv
)?argv
[0]:NULL
);
2496 if (optind
>= argc
|| !argv
|| !argv
[optind
] || !*argv
[optind
]) {
2497 fprintf(stderr
, _("Bad invocation\n"));
2498 shi_delete_message_usage((argv
)?argv
[0]:NULL
);
2501 if (!direction_specified
) {
2502 fprintf(stderr
, _("Message direction has not been specified\n"));
2503 shi_delete_message_usage((argv
)?argv
[0]:NULL
);
2507 /* Process messages named in argv */
2508 for (int i
= optind
; i
< argc
; i
++) {
2509 if (!argv
[i
] || !*argv
[i
]) continue;
2511 id
= locale2utf8(argv
[i
]);
2514 _("Error: Could not convert message ID to UTF-8: %s\n"),
2519 printf(_("Deleting message `%s'...\n"), argv
[i
]);
2520 err
= isds_delete_message_from_storage(cisds
, id
, incoming
);
2521 finish_isds_operation(cisds
, err
);
2527 oprintf(_("Message `%s' deleted\n"), argv
[i
]);
2535 /* Convert message ID form locale to UTF-8 or in other direction. If both
2536 * strings are provided, UTF-8 will take precedence. The missing string is
2537 * automatically allocated (but not freed before). If UTF-8 version has been
2538 * provided, @stastic_utf8 will become 1, otherwise 0. You can pass the
2539 * strings and the flags to free_message_id() to free memory properly.*/
2540 static int convert_message_id(char **id_utf8
, char **id_locale
, _Bool
*static_utf8
) {
2541 if (!id_utf8
|| !id_locale
|| !static_utf8
) return -1;
2542 if (!*id_utf8
&& !*id_locale
) return -1;
2546 *id_locale
= utf82locale(*id_utf8
);
2549 *id_utf8
= locale2utf8(*id_locale
);
2552 _("Error: Could not convert message ID to UTF-8: %s\n"),
2562 /* Free message ID strings as were allocated by convert_message_id() */
2563 static int free_message_id(char **id_utf8
, char **id_locale
, _Bool static_utf8
) {
2564 if (!id_utf8
|| !id_locale
) return -1;
2565 if (static_utf8
) zfree(*id_locale
);
2566 else zfree(*id_utf8
);
2571 /* Return static UTF-8 encoded ID of current message. In case of error NULL. */
2572 static const char *get_current_message_id(void) {
2574 fprintf(stderr
, _("No message loaded\n"));
2577 if (!message
->envelope
) {
2578 fprintf(stderr
, _("Loaded message is missing envelope\n"));
2581 if (!message
->envelope
->dmID
|| !*message
->envelope
->dmID
) {
2582 fprintf(stderr
, _("Loaded message is missing ID\n"));
2585 return message
->envelope
->dmID
;
2589 static void shi_message_sender_usage(const char *command
) {
2591 "Usage: %s [MESSAGE_ID...]\n"
2592 "Get details about sender of a message.\n"
2593 "If MESSAGE_ID is defined, get sender of that message. More messages can be specified.\n"
2594 "Otherwise will get sender of current message, if any is loaded.\n"),
2599 /* Get details about sender of message with given ID. At least one form must
2601 * @message_id is UTF-8 string
2602 * @message_id_locale is string in locale encoding
2603 * @return 0 on success, -1 on failure */
2604 static int do_message_sender(const char *message_id
, const char *message_id_locale
) {
2605 isds_sender_type
*type
= NULL
;
2606 char *raw_type
= NULL
;
2611 if (convert_message_id((char **)&message_id
, (char **)&message_id_locale
,
2615 printf(_("Getting sender of message `%s'...\n"), message_id_locale
);
2616 err
= isds_get_message_sender(cisds
, message_id
, &type
, &raw_type
, &name
);
2617 finish_isds_operation(cisds
, err
);
2619 free_message_id((char **)&message_id
, (char **)&message_id_locale
,
2624 format_sender_info(message_id
, type
, raw_type
, name
);
2626 free_message_id((char **)&message_id
, (char **)&message_id_locale
,
2635 static int shi_message_sender(int argc
, const char **argv
) {
2637 return do_message_sender(get_current_message_id(), NULL
);
2640 for (int i
= 1; i
< argc
; i
++) {
2641 if (!argv
[i
] || !*argv
[i
]) continue;
2642 if (do_message_sender(NULL
, argv
[i
]))
2650 /* Mark message as read. At least one form of ID must be provided.
2651 * @id is UTF-8 encoded message ID
2652 * @id_locale is locale encoded message ID. @id takes preference. */
2653 static int do_read_message(const char *id
, const char *id_locale
) {
2657 if ((!id
|| !*id
) && (!id_locale
|| !*id_locale
)) return -1;
2659 if (convert_message_id((char **)&id
, (char **)&id_locale
, &static_id
)) return -1;
2661 printf(_("Marking message `%s' as read...\n"), id_locale
);
2662 err
= isds_mark_message_read(cisds
, id
);
2663 finish_isds_operation(cisds
, err
);
2666 oprintf(_("Message `%s' marked as read\n"), id_locale
);
2668 free_message_id((char **)&id
, (char **)&id_locale
, static_id
);
2670 return (err
) ? -1 : 0;
2674 static void shi_read_message_usage(const char *command
) {
2676 "Usage: %s [MESSAGE_ID...]\n"
2677 "Mark message as read moving its state to read.\n"
2679 "When new incoming message is download, its state is not changed on server.\n"
2680 "Client must mark such message as read explicitly. You can use this command\n"
2681 "to do so, if not done automatically at download time by your client.\n"
2683 "If MESSAGE_ID is defined, mark that message. More messages can be specified.\n"
2684 "Otherwise marks currently loaded message.\n"),
2689 static int shi_read_message(int argc
, const char **argv
) {
2691 return do_read_message(get_current_message_id(), NULL
);
2694 for (int i
= 1; i
< argc
; i
++) {
2695 if (!argv
[i
] || !*argv
[i
]) continue;
2696 if (do_read_message(NULL
, argv
[i
]))
2704 static void shi_cat_message_usage(const char *command
) {
2707 "Print unformated raw representation of current message.\n"
2709 "This is the same content you would get into file by `save' command.\n"
2711 "Be ware the binary stream can screw your terminal. No new line character\n"
2712 "will be appended to the end of the output.\n"),
2717 static int shi_cat_message(int argc
, const char **argv
) {
2719 fprintf(stderr
, _("No message loaded\n"));
2723 if (!message
->raw
|| !message
->raw_length
) {
2724 fprintf(stderr
, _("Current message is missing raw representation\n"));
2728 if (owrite(message
->raw
, message
->raw_length
) != message
->raw_length
) {
2729 fprintf(stderr
, _("Error while printing message content\n"));
2737 static int shi_show_message(int argc
, const char **argv
) {
2739 fprintf(stderr
, _("No message loaded\n"));
2743 format_message(message
);
2748 static void shi_incoming_message_usage(const char *command
) {
2750 "Usage: %s [-r] MESSAGE_ID\n"
2751 "Get incoming message with MESSAGE_ID.\n"
2753 " -r Mark mesage as read\n"),
2758 static int shi_incoming_message(int argc
, const char **argv
) {
2762 _Bool mark_as_read
= 0;
2765 while ((opt
= getopt(argc
, (char * const *)argv
, "r")) != -1) {
2771 shi_incoming_message_usage((argv
)?argv
[0]:NULL
);
2775 if (optind
+ 1 != argc
|| !argv
|| !argv
[optind
] || !*argv
[optind
]) {
2776 fprintf(stderr
, _("Bad invocation\n"));
2777 shi_incoming_message_usage((argv
)?argv
[0]:NULL
);
2782 printf(_("Getting incoming message...\n"));
2783 err
= isds_get_signed_received_message(cisds
, id
, &message
);
2784 finish_isds_operation(cisds
, err
);
2787 select_completition(COMPL_COMMAND
);
2791 format_message(message
);
2793 if (message
->envelope
&& message
->envelope
->dmID
)
2794 set_prompt(_("%s %s"), argv
[0], message
->envelope
->dmID
);
2796 set_prompt("%s", argv
[0]);
2797 select_completition(COMPL_MSG
);
2799 if (mark_as_read
|| cfg_getbool(configuration
, CONFIG_MARKMESSAGEREAD
)) {
2800 if (message
->envelope
&& message
->envelope
->dmMessageStatus
&&
2801 ! (*message
->envelope
->dmMessageStatus
& MESSAGESTATE_READ
))
2802 return do_read_message(id
, NULL
);
2808 static void shi_outgoing_message_usage(const char *command
) {
2810 "Usage: %s MESSAGE_ID\n"
2811 "Get outgoing message with MESSAGE_ID.\n"),
2816 static int shi_outgoing_message(int argc
, const char **argv
) {
2820 if (!argv
|| !argv
[1] || !*argv
[1]) {
2821 shi_outgoing_message_usage(argv
[0]);
2826 printf(_("Getting outgoing message...\n"));
2827 err
= isds_get_signed_sent_message(cisds
, id
, &message
);
2828 finish_isds_operation(cisds
, err
);
2831 select_completition(COMPL_COMMAND
);
2835 format_message(message
);
2836 if (message
->envelope
&& message
->envelope
->dmID
)
2837 set_prompt(_("%s %s"), argv
[0], message
->envelope
->dmID
);
2839 set_prompt("%s", argv
[0]);
2840 select_completition(COMPL_MSG
);
2845 /* Detect type (message or delivery data) and load it. And change completion
2846 * and show the data.
2847 * @buffer is memory with message or delivery data
2848 * @length is size of @buffer in bytes
2849 * @strategy defines how to fill global message variable
2850 * @return 0 for success, otherwise non-zero. */
2851 static int do_load_anything(const void *buffer
, size_t length
,
2852 isds_buffer_strategy strategy
) {
2853 isds_raw_type raw_type
;
2855 char *type_name
= NULL
;
2857 if (NULL
== buffer
|| 0 == length
) {
2861 printf(_("Detecting format...\n"));
2862 err
= isds_guess_raw_type(cisds
, &raw_type
, buffer
, length
);
2863 finish_isds_operation(cisds
, err
);
2866 if (err
== IE_NOTSUP
)
2867 fprintf(stderr
, _("Unknown format.\n"));
2869 fprintf(stderr
, _("Error while detecting format.\n"));
2872 case RAWTYPE_INCOMING_MESSAGE
:
2873 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
:
2874 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
:
2875 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
:
2876 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
2877 err
= isds_load_message(cisds
, raw_type
,
2878 buffer
, length
, &message
, strategy
);
2879 finish_isds_operation(cisds
, err
);
2880 type_name
= N_("message");
2883 case RAWTYPE_DELIVERYINFO
:
2884 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO
:
2885 case RAWTYPE_CMS_SIGNED_DELIVERYINFO
:
2886 err
= isds_load_delivery_info(cisds
, raw_type
,
2887 buffer
, length
, &message
, strategy
);
2888 finish_isds_operation(cisds
, err
);
2889 type_name
= N_("delivery");
2894 _("Unsupported format.\n"));
2901 select_completition(COMPL_COMMAND
);
2905 format_message(message
);
2907 if (message
->envelope
&& message
->envelope
->dmID
)
2908 set_prompt(_("%s %s"), _(type_name
), message
->envelope
->dmID
);
2910 set_prompt("%s", _(type_name
));
2911 select_completition(COMPL_MSG
);
2916 static void shi_load_anything_usage(const char *command
) {
2919 "Load message or message delivery details from local FILE.\n"),
2924 static int shi_load_anything(int argc
, const char **argv
) {
2926 void *buffer
= NULL
;
2930 if (!argv
|| !argv
[1] || !*argv
[1]) {
2931 shi_load_anything_usage((argv
)?argv
[0]:NULL
);
2935 printf(_("Loading file `%s'...\n"), argv
[1]);
2937 if (mmap_file(argv
[1], &fd
, &buffer
, &length
)) return -1;
2939 error
= do_load_anything(buffer
, length
, BUFFER_COPY
);
2941 munmap_file(fd
, buffer
, length
);
2947 static void shi_save_message_usage(const char *command
) {
2950 "Save message into local FILE.\n"),
2955 static const char *raw_type2mime(isds_raw_type raw_type
) {
2957 case RAWTYPE_INCOMING_MESSAGE
:
2958 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
:
2959 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
:
2960 case RAWTYPE_DELIVERYINFO
:
2961 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO
:
2964 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
:
2965 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
2966 case RAWTYPE_CMS_SIGNED_DELIVERYINFO
:
2967 return "application/pkcs7-mime";
2975 static int shi_save_message(int argc
, const char **argv
) {
2976 _Bool overwrite
= cfg_getbool(configuration
, CONFIG_OVERWRITEFILES
);
2978 if (!argv
|| !argv
[1] || !*argv
[1]) {
2979 shi_save_message_usage(argv
[0]);
2984 fprintf(stderr
, _("No message loaded\n"));
2987 if (!message
->raw
|| message
->raw_length
== 0) {
2988 fprintf(stderr
, _("Loaded message is missing raw representation\n"));
2992 return save_data_to_file(argv
[1], -1, message
->raw
, message
->raw_length
,
2993 raw_type2mime(message
->raw_type
), overwrite
);
2997 /* Return document of current message identified by ordinal number expressed
2998 * as string. In case of error return NULL. */
2999 static const struct isds_document
*locate_document_by_ordinal_string(
3000 const char *number
) {
3001 const struct isds_list
*item
;
3002 const struct isds_document
*document
= NULL
;
3005 if (!number
) return NULL
;
3007 ordinar
= atoi(number
);
3009 fprintf(stderr
, _("%s: Document number must be positive number\n"),
3015 fprintf(stderr
, _("No message loaded\n"));
3020 for (item
= message
->documents
, i
= 0; item
; item
= item
->next
) {
3021 if (!item
->data
) continue;
3022 if (++i
== ordinar
) {
3023 document
= (const struct isds_document
*) item
->data
;
3028 fprintf(stderr
, _("Message does not contain document #%d\n"), ordinar
);
3036 static void shi_cat_document_usage(const char *command
) {
3038 "Usage: %s NUMBER\n"
3039 "Print document selected with ordinal NUMBER.\n"),
3043 static int shi_cat_document(int argc
, const char **argv
) {
3044 const struct isds_document
*document
;
3046 if (!argv
|| !argv
[1] || !*argv
[1] || argc
> 3) {
3047 shi_cat_document_usage(argv
[0]);
3051 document
= locate_document_by_ordinal_string(argv
[1]);
3052 if (!document
) return -1;
3054 if (document
->is_xml
) {
3055 xmlBufferPtr buffer
= NULL
;
3058 if (serialize_xml_to_buffer(&buffer
, document
->xml_node_list
))
3061 written
= owrite(buffer
->content
, buffer
->use
);
3062 xmlBufferFree(buffer
);
3063 if (written
!= buffer
->use
) {
3064 fprintf(stderr
, _("Error while printing document content\n"));
3068 if (!document
->data
|| !document
->data_length
) {
3069 fprintf(stderr
, _("Document is missing raw representation\n"));
3073 if (owrite(document
->data
, document
->data_length
) != document
->data_length
) {
3074 fprintf(stderr
, _("Error while printing document content\n"));
3083 static void shi_save_document_usage(const char *command
) {
3085 "Usage: %s NUMBER [DESTINATION]\n"
3086 "Save document having ordinal NUMBER within current message into local file.\n"
3087 "If DESTINATION is file (or does not exist yet), document will be saved into\n"
3089 "If DESTINATION is existing directory, file name equaled to document name\n"
3090 "will be saved into DESTINATION.\n"
3091 "If DESTINATION is missing, document name will be used as file name and\n"
3092 "saved into working directory.\n"
3093 "Be aware that document name does not embed malicious characters (slashes).\n"
3095 "If the document is a binary stream, image of the document will be copied\n"
3096 "into a file. If the document is a XML document, the XML tree will be serialized\n"
3097 "into a file. If XML document stands for one element or one text node, the node\n"
3098 "(and its children recursively) will be serialized. If XML document compounds\n"
3099 "more nodes or a comment or a processing instruction, parent node from ISDS name\n"
3100 "space will be used to ensure output serialized XML well-formness.\n"
3106 static int shi_save_document(int argc
, const char **argv
) {
3107 const struct isds_document
*document
;
3108 const char *dirname
= NULL
;
3109 char *filename
= NULL
, *path
= NULL
;
3111 _Bool overwrite
= cfg_getbool(configuration
, CONFIG_OVERWRITEFILES
);
3113 if (!argv
|| !argv
[1] || !*argv
[1] || argc
> 3) {
3114 shi_save_document_usage(argv
[0]);
3118 document
= locate_document_by_ordinal_string(argv
[1]);
3119 if (!document
) return -1;
3121 /* Select directory and file name */
3122 if (argv
[2] && *argv
[2]) {
3123 if (!is_directory(argv
[2])) {
3126 filename
= strdup(argv
[2]);
3128 fprintf(stderr
, _("Not enough memory\n"));
3133 if (!filename
&& document
->dmFileDescr
&& &document
->dmFileDescr
) {
3134 filename
= utf82locale(document
->dmFileDescr
);
3136 fprintf(stderr
, _("Not enough memory\n"));
3142 _("File name neither supplied, nor document name exists\n"
3143 "Please, supply one.\n"));
3149 path
= astrcat3(dirname
, "/", filename
);
3156 fprintf(stderr
, _("Not enough memory\n"));
3161 if (document
->is_xml
)
3162 retval
= save_xml_to_file(path
, -1, document
->xml_node_list
,
3163 document
->dmMimeType
, overwrite
);
3165 retval
= save_data_to_file(path
, -1, document
->data
,
3166 document
->data_length
, document
->dmMimeType
, overwrite
);
3172 /* Execute program specified as NULL terminated array of arguments. argv[0] is
3173 * subject of PATH search variable look-up. The program is executed directly,
3174 * it's not a shell command. */
3175 static int execute_system_command(char *const argv
[]) {
3178 if (!argv
|| !argv
[0]) return -1;
3182 /* Could not fork */
3183 fprintf(stderr
, _("Could not fork\n"));
3185 } else if (pid
== 0) {
3187 execvp(argv
[0], argv
);
3188 fprintf(stderr
, _("Could not execute:"));
3189 for (char *const *arg
= argv
; *arg
; arg
++)
3190 fprintf(stderr
, " %s", *arg
);
3191 fprintf(stderr
, _(": %s\n"), strerror(errno
));
3194 /* Wait for the command */
3197 if (-1 == waitpid(pid
, &retval
, 0)) {
3198 fprintf(stderr
, _("Could not wait for executed command\n"));
3203 fprintf(stderr
, _("Exit code of command could not "
3204 "be determined\n"));
3205 else if (WIFEXITED(retval
) && WEXITSTATUS(retval
))
3206 printf(_("Command exited with code %d\n"),
3207 WEXITSTATUS(retval
));
3208 else if (WIFSIGNALED(retval
))
3209 printf(_("Command terminated by signal "
3210 "#%d\n"), WTERMSIG(retval
));
3216 /* Run editor to create new text document */
3217 static int edit_new_textual_document(struct isds_document
*document
) {
3218 char filename
[14] = "shiXXXXXX.txt";
3220 char *command
[] = { getenv("VISUAL"), filename
, NULL
};
3222 struct stat file_before
, file_after
;
3225 fprintf(stderr
, _("Editing is forbidden in batch mode.\n"));
3229 if (NULL
== document
) return -1;
3230 if (NULL
== command
[0]) command
[0] = getenv("EDITOR");
3231 if (NULL
== command
[0]) {
3233 _("Neither environment variable VISUAL nor EDITOR are set.\n"));
3237 /* Create temporary file for the document */
3238 fd
= create_new_file(filename
, 4);
3242 if (fstat(fd
, &file_before
)) {
3244 _("Could not retrieve modification time for `%s': %s\n"),
3245 filename
, strerror(errno
));
3250 /* Open the file with $EDITOR */
3251 if ((retval
= execute_system_command(command
))) {
3252 fprintf(stderr
, _("Editor failed.\n"));
3257 /* Compare modification times */
3258 /* XXX: fstat(2) does return updated st_mtime. Bug in Linux 3.7.1? */
3259 if (stat(filename
, &file_after
)) {
3261 _("Could not retrieve modification time for `%s': %s\n"),
3262 filename
, strerror(errno
));
3266 if (file_before
.st_mtime
== file_after
.st_mtime
) {
3267 fprintf(stderr
, _("Edited document has not been changed.\n"));
3273 if (load_data_from_file(filename
, &document
->data
,
3274 &document
->data_length
, NULL
)) {
3280 if (NULL
== document
->dmMimeType
)
3281 FILL_OR_LEAVE(document
->dmMimeType
, "text/plain");
3282 /* XXX: POSIX basename() modifies argument */
3283 if (NULL
== document
->dmFileDescr
)
3284 FILL_OR_LEAVE(document
->dmFileDescr
, basename(filename
));
3287 /* Remove the file */
3288 unlink_file(filename
);
3294 /* Append @suffix into @buffer with @size bytes prealocated at position @at.
3295 * Trailing '\0' of @suffix is not carried.
3296 * @buffer can be reallocated if @size is not suffient to fill @suffix
3297 * @size is original @buffer size, can change if @buffer would be reallocated
3298 * @at position where append @suffic to. Outputs new end after appending
3299 * @suffix is NULL rerminated string to append
3300 * @return 0 if success, -1 otherwise. Caller is resposible for freeing
3302 static int append_string_at(char **buffer
, size_t *size
, char **at
,
3303 const char *suffix
) {
3305 if (!buffer
|| !*buffer
|| !size
|| !at
|| !*at
) return -1;
3306 if (!suffix
) return 0;
3309 if (*at
- *buffer
+ 1 >= *size
) {
3310 /* End of buffer, grow it */
3311 if (*size
< 8) *size
= 8;
3313 char *new_buffer
= realloc(*buffer
, *size
);
3314 if (!new_buffer
) return -1;
3315 *at
= *at
- *buffer
+ new_buffer
;
3316 *buffer
= new_buffer
;
3319 /* Copy a character */
3320 *((*at
)++) = *(suffix
++);
3327 static char *expand_command_arg(const char *format
, const char *file
,
3329 char *buffer
= NULL
;
3331 const char *format_cursor
;
3332 char *buffer_cursor
;
3334 if (!format
) return NULL
;
3336 for (format_cursor
= format
, buffer_cursor
= buffer
; ; format_cursor
++) {
3337 if (buffer_cursor
- buffer
+ 1 >= size
) {
3338 /* End of buffer, grow it */
3339 if (size
< 8) size
= 8;
3341 char *new_buffer
= realloc(buffer
, size
);
3342 if (!new_buffer
) goto error
;
3343 buffer_cursor
= buffer_cursor
- buffer
+ new_buffer
;
3344 buffer
= new_buffer
;
3347 if (*format_cursor
== '%') {
3349 switch (*(format_cursor
+1)) {
3352 fprintf(stderr
, _("Could not expand `%%f' because "
3353 "file name did not exist.\n"));
3357 if (append_string_at(&buffer
, &size
, &buffer_cursor
, file
))
3364 fprintf(stderr
, _("Could not expand `%%t' because "
3365 "file type did not exist.\n"));
3369 if (append_string_at(&buffer
, &size
, &buffer_cursor
, type
))
3379 /* Copy plain character */
3380 *(buffer_cursor
++) = *format_cursor
;
3382 if (!*format_cursor
) break;
3388 fprintf(stderr
, _("Error: Not enough memory\n"));
3394 /* Expand open_command configuration option by substiting %f and %t with file
3395 * name and file type.
3396 * @file is locale encoded file name
3397 * @type is UTF-8 encoded MIME type
3398 * @return heap allocated arrary of arguments or NULL if error occurs.
3399 * Arguments are coded in locale. */
3400 static char **expand_open_command(const char *file
, const char *type
) {
3401 char **command
= NULL
;
3403 char *type_locale
= NULL
;
3405 length
= cfg_size(configuration
, CONFIG_OPENCOMMAND
);
3407 fprintf(stderr
, _("%s not set\n"), CONFIG_OPENCOMMAND
);
3411 command
= malloc((length
+ 1) * sizeof(*command
));
3413 fprintf(stderr
, _("Error: Not enough memory\n"));
3418 type_locale
= utf82locale(type
);
3420 printf(_("Could not convert document MIME type to locale "
3427 for (int i
= 0; i
< length
; i
++) {
3428 const char *value
= cfg_getnstr(configuration
, CONFIG_OPENCOMMAND
, i
);
3429 command
[i
] = expand_command_arg(value
, file
, type_locale
);
3431 fprintf(stderr
, _("Error: Not enough memory\n"));
3432 for (int j
= 0; j
< i
; j
++) free(command
[j
]);
3438 command
[length
] = NULL
;
3445 /* Register temporary @file for removal at shigofumi exit.
3446 * This is done by destructor call-back in isds_list_free(). */
3447 static int register_temporary_file(const char *file
) {
3448 struct isds_list
*temporary_file
= NULL
;
3450 if (!file
) return 0;
3452 temporary_file
= malloc(sizeof(*temporary_file
));
3453 if (!temporary_file
) {
3454 fprintf(stderr
, _("Error: Not enough memory\n"));
3458 temporary_file
->data
= (void *)strdup(file
);
3459 if (!temporary_file
) {
3460 fprintf(stderr
, _("Error: Not enough memory\n"));
3461 free(temporary_file
);
3465 temporary_file
->destructor
= shi_unlink_temporary_file
;
3467 if (temporary_files
)
3468 temporary_file
->next
= temporary_files
;
3470 temporary_file
->next
= NULL
;
3471 temporary_files
= temporary_file
;
3477 static void shi_open_document_usage(const char *command
) {
3479 "Usage: %s NUMBER\n"
3480 "Save document having ordinal NUMBER within current message into temporal\n"
3481 "local file, open the file by xdg-open utility and then remove the file.\n"
3487 static int shi_open_document(int argc
, const char **argv
) {
3488 const struct isds_document
*document
;
3489 char filename
[10] = "shiXXXXXX";
3491 char **command
= NULL
;
3494 if (!argv
|| !argv
[1] || !*argv
[1] || argc
> 3) {
3495 shi_open_document_usage(argv
[0]);
3499 document
= locate_document_by_ordinal_string(argv
[1]);
3500 if (!document
) return -1;
3502 /* Create temporary file for the document */
3503 fd
= create_new_file(filename
, 0);
3509 if (document
->is_xml
)
3510 retval
= save_xml_to_file(filename
, fd
, document
->xml_node_list
,
3511 document
->dmMimeType
, 0);
3513 retval
= save_data_to_file(filename
, fd
, document
->data
,
3514 document
->data_length
, document
->dmMimeType
, 0);
3516 /* Open the file with external utility */
3518 /* Construct command arguments to execute */
3519 command
= expand_open_command(filename
, document
->dmMimeType
);
3524 /* XXX: Do not use system(3) as we cannot escape uknown shell */
3525 retval
= execute_system_command(command
);
3526 for (char **arg
= command
; *arg
; arg
++) free(*arg
);
3531 /* Remove the file */
3532 /* XXX: We do not know when external program opens the file. We cannot
3533 * remove it immediately. Register the filename and remove all temporary
3534 * files at exit of shigofumi if requested by configuration. */
3535 if (cfg_getbool(configuration
, CONFIG_CLEAN_TEMPORARY_FILES
)) {
3536 if (register_temporary_file(filename
))
3537 fprintf(stderr
, _("Warning: Temporary file `%s' could not been "
3538 "registered for later removal. Remove the file by "
3539 "hand, please.\n"), filename
);
3545 static void shi_compose_usage(const char *command
) {
3547 "Usage: %s OPTION...\n"
3548 "Compose and send a message to recipient defined by his box ID.\n"
3549 "Each option requires an argument (if not stated otherwise):\n"
3550 " -s * message subject\n"
3552 "Recipient options:\n"
3553 " -b * recipient box ID\n"
3554 " -U organisation unit name\n"
3555 " -N organisation unit number\n"
3556 " -P to hands of given person\n"
3558 "Sender organisation structure options:\n"
3559 " -I publish user's identity (NO argument allowed)\n"
3563 "Message identifier options:\n"
3564 " -r sender reference number\n"
3565 " -f sender file ID\n"
3566 " -R recipient reference number\n"
3567 " -F recipient file ID\n"
3569 "Legal title options:\n"
3570 " -y year act has been issued\n"
3571 " -a ordinal number of act in a year\n"
3572 " -e section of the act\n"
3573 " -o paragraph of the act\n"
3574 " -i point of the paragraph of the act\n"
3576 "Delivery options:\n"
3577 " -p personal delivery required\n"
3578 " -t allow substitutable delivery\n"
3579 " -A non-OVM sender acts as public authority\n"
3580 " -C commercial type; accepted values:\n"
3581 " K Commercial message paid by sender or sponsor\n"
3582 " I Initiatory commercial message offering to pay response\n"
3583 " O Commercial response paid by recipient\n"
3584 " V Public message paid by government\n"
3585 " Missing option defaults to K or O (see `commercialsending' command)\n"
3586 " if sending to non-OVM recipient with enabled commercial receiving,\n"
3587 " otherwise it defaults to V.\n"
3589 "Document options:\n"
3590 " -d * read document from local file. If `-' is specified,\n"
3591 " run text editor.\n"
3592 " -D document name (defaults to base local file name)\n"
3593 " -x transport subset of the document as a XML.\n"
3594 " Argument is XPath expression specifying desired node set\n"
3595 " -m override MIME type (guessed on -d)\n"
3596 " -g document ID (must be unique per message)\n"
3597 " -G reference to other document using its ID\n"
3598 " -c document is digital signature of other document (NO argument\n"
3601 "Options marked with asterisk are mandatory, other are optional. Another soft\n"
3602 "dependencies can emerge upon using specific option. They are not mandated by\n"
3603 "ISDS currently, but client library or this program can force them to assure\n"
3604 "semantically complete message. Following soft dependencies are recommended:\n"
3605 " -y <=> -a act number and year must be used at the same time\n"
3606 " -i => -o act point requires act paragraph\n"
3607 " -o => -e act paragraph requires act section\n"
3608 " -e => -a act section requires act number\n"
3609 " -G => -g document with referenced ID must exist\n"
3610 " -c => -G signature must refer to signed document\n"
3611 " -c first document cannot be signature\n"
3612 " -C I => -r sender reference number allows responder to reply to this message\n"
3613 " -C O -> -R recipient reference number must match sender reference number of\n"
3614 " initiatory message\n"
3616 "More documents can be attached to a message by repeating `-d' option.\n"
3617 "Document order will be preserved. Other document options affect immediately\n"
3618 "preceding `-d' document only. E.g. `-d /tmp/foo.pdf -m application/pdf\n"
3619 "-d /tmp/bar.txt -m text/plain' attaches first PDF file, then textual file.\n"
3621 "The same applies to recipient options that must start with box ID (-b).\n"
3622 "If more recipients specified, each of them will get a copy of composed\n"
3623 "message. ISDS will assign message identifier to each copy in turn.\n"
3629 static int shi_compose(int argc
, const char **argv
) {
3634 struct isds_message
*message
= NULL
;
3635 struct isds_envelope
*envelope
= NULL
;
3636 struct isds_list
*documents
= NULL
;
3637 struct isds_document
*document
= NULL
;
3638 struct isds_list
*copies
= NULL
, *copy_item
= NULL
;
3639 struct isds_message_copy
*copy
= NULL
;
3640 char *message_id_locale
= NULL
, *recipient_id_locale
= NULL
,
3641 *dmStatus_locale
= NULL
;
3643 if (!argv
|| !argv
[1] || !*argv
[1]) {
3644 fprintf(stderr
, _("Error: No argument supplied\n"));
3645 shi_compose_usage((argv
)?argv
[0]:NULL
);
3649 message
= calloc(1, sizeof(*message
));
3651 fprintf(stderr
, _("Error: Not enough memory\n"));
3655 envelope
= calloc(1, sizeof(*envelope
));
3657 fprintf(stderr
, _("Error: Not enough memory\n"));
3661 message
->envelope
= envelope
;
3665 while ((opt
= getopt(argc
, (char * const *)argv
, "s:" "b:U:N:P:" "Iu:n:"
3666 "r:f:R:F:" "y:a:e:o:i:" "p:t:A:C:" "d:D:x:m:g:G:c"
3670 FILL_OR_LEAVE(envelope
->dmAnnotation
, optarg
);
3673 /* Recipient options */
3677 /* First recipient */
3678 CALLOC_OR_LEAVE(copies
);
3679 copies
->destructor
=
3680 (void(*)(void **)) isds_message_copy_free
;
3683 /* Next recipient */
3684 CALLOC_OR_LEAVE(copy_item
->next
);
3685 copy_item
->next
->destructor
=
3686 (void(*)(void **)) isds_message_copy_free
;
3687 copy_item
= copy_item
->next
;
3689 CALLOC_OR_LEAVE(copy
);
3690 copy_item
->data
= copy
;
3692 /* Copy recipient box ID */
3693 FILL_OR_LEAVE(copy
->dbIDRecipient
, optarg
);
3698 _("Error: %s: Recipient box ID (-b) must precede "
3699 "recipient organisation unit name (-%c)\n"),
3704 FILL_OR_LEAVE(copy
->dmRecipientOrgUnit
, optarg
);
3709 _("Error: %s: Recipient box ID (-b) must precede "
3710 "recipient organisation unit number (-%c)\n"),
3715 FILL_LONGINT_OR_LEAVE(copy
->dmRecipientOrgUnitNum
, optarg
);
3720 _("Error: %s: Recipient box ID (-b) must precede "
3721 "to-hands option (-%c)\n"), optarg
, opt
);
3725 FILL_OR_LEAVE(copy
->dmToHands
, optarg
);
3728 /* Sender organisation structure options */
3730 FILL_BOOLEAN_OR_LEAVE(envelope
->dmPublishOwnID
, optarg
);
3732 FILL_OR_LEAVE(envelope
->dmSenderOrgUnit
, optarg
);
3735 FILL_LONGINT_OR_LEAVE(envelope
->dmSenderOrgUnitNum
, optarg
);
3738 /* Message identifier options */
3740 FILL_OR_LEAVE(envelope
->dmSenderRefNumber
, optarg
);
3743 FILL_OR_LEAVE(envelope
->dmSenderIdent
, optarg
);
3746 FILL_OR_LEAVE(envelope
->dmRecipientRefNumber
, optarg
);
3749 FILL_OR_LEAVE(envelope
->dmRecipientIdent
, optarg
);
3752 /* Legal title options */
3754 FILL_LONGINT_OR_LEAVE(envelope
->dmLegalTitleYear
, optarg
);
3757 FILL_LONGINT_OR_LEAVE(envelope
->dmLegalTitleLaw
, optarg
);
3760 FILL_OR_LEAVE(envelope
->dmLegalTitleSect
, optarg
);
3763 FILL_OR_LEAVE(envelope
->dmLegalTitlePar
, optarg
);
3766 FILL_OR_LEAVE(envelope
->dmLegalTitlePoint
, optarg
);
3769 /* Delivery options */
3771 FILL_BOOLEAN_OR_LEAVE(envelope
->dmPersonalDelivery
, optarg
);
3774 FILL_BOOLEAN_OR_LEAVE(envelope
->dmAllowSubstDelivery
, optarg
);
3777 FILL_BOOLEAN_OR_LEAVE(envelope
->dmOVM
, optarg
);
3780 FILL_OR_LEAVE(envelope
->dmType
, optarg
);
3783 /* Document options */
3787 /* First document */
3788 CALLOC_OR_LEAVE(message
->documents
);
3789 message
->documents
->destructor
=
3790 (void(*)(void **)) isds_document_free
;
3791 documents
= message
->documents
;
3792 CALLOC_OR_LEAVE(document
);
3793 documents
->data
= document
;
3794 document
->dmFileMetaType
= FILEMETATYPE_MAIN
;
3797 CALLOC_OR_LEAVE(documents
->next
);
3798 documents
->next
->destructor
=
3799 (void(*)(void **)) isds_document_free
;
3800 documents
= documents
->next
;
3801 CALLOC_OR_LEAVE(document
);
3802 documents
->data
= document
;
3803 document
->dmFileMetaType
= FILEMETATYPE_ENCLOSURE
;
3806 if (strcmp(optarg
, "-")) {
3807 /* Load file if specified. Keep editing new file after
3808 * processing all arguments. */
3809 if (load_data_from_file(optarg
, &document
->data
,
3810 &document
->data_length
, &document
->dmMimeType
)) {
3814 /* XXX: POSIX basename() modifies argument */
3815 FILL_OR_LEAVE(document
->dmFileDescr
, basename(optarg
));
3821 _("Error: %s: Document file (-d) must precede "
3822 "document name (-%c)\n"), optarg
, opt
);
3826 FILL_OR_LEAVE(document
->dmFileDescr
, optarg
);
3831 _("Error: %s: Document file (-d) must precede "
3832 "XPath expression (-%c)\n"), optarg
, opt
);
3836 /* Load XML node list */
3837 char *xpath_expr
= NULL
;
3838 FILL_OR_LEAVE(xpath_expr
, optarg
);
3839 retval
= load_xml_subtree_from_memory(
3840 document
->data
, document
->data_length
,
3841 &document
->xml_node_list
, xpath_expr
);
3846 /* Switch document type to XML */
3847 document
->is_xml
= 1;
3848 zfree(document
->data
);
3849 document
->data_length
= 0;
3850 documents
->destructor
=
3851 (void(*)(void **)) free_document_with_xml_node_list
;
3856 _("Error: %s: Document file (-d) must precede "
3857 "MIME type (-%c)\n"), optarg
, opt
);
3861 FILL_OR_LEAVE(document
->dmMimeType
, optarg
);
3866 _("Error: %s: Document file (-d) must precede "
3867 "document ID (-%c)\n"), optarg
, opt
);
3871 FILL_OR_LEAVE(document
->dmFileGuid
, optarg
);
3876 _("Error: %s: Document file (-d) must precede "
3877 "document reference (-%c)\n"), optarg
, opt
);
3881 FILL_OR_LEAVE(document
->dmUpFileGuid
, optarg
);
3886 _("Error: Document file (-d) must precede "
3887 "document signature type (-%c)\n"), opt
);
3891 document
->dmFileMetaType
= FILEMETATYPE_SIGNATURE
;
3895 shi_compose_usage(argv
[0]);
3901 /* All options must be recognized */
3902 if (optind
!= argc
) {
3903 fprintf(stderr
, _("Error: Superfluous argument\n"));
3904 shi_compose_usage(argv
[0]);
3910 fprintf(stderr
, _("Error: No recipient box ID specified\n"));
3911 shi_compose_usage(argv
[0]);
3916 /* TODO: Check Legal Title soft dependencies */
3918 /* Compose missing documents */
3919 for (i
= 1, documents
= message
->documents
; NULL
!= documents
;
3920 documents
= documents
->next
, i
++) {
3921 document
= documents
->data
;
3922 if (NULL
== document
->data
) {
3923 printf(_("Editing document #%u...\n"), i
);
3924 if (edit_new_textual_document(document
)) {
3925 fprintf(stderr
, _("Composition aborted.\n"));
3933 oprintf(_("Following message has been composed:\n"));
3934 format_message(message
);
3935 oprintf(_("Following recipients have been specified:\n"));
3936 format_copies(copies
);
3937 /* TODO: Confirmation, correction */
3939 /* Send a message */
3940 printf(_("Sending message...\n"));
3941 err
= isds_send_message_to_multiple_recipients(cisds
, message
, copies
);
3942 finish_isds_operation(cisds
, err
);
3943 if (err
&& err
!= IE_PARTIAL_SUCCESS
) {
3948 /* Show results for each copy */
3949 for (copy_item
= copies
; copy_item
; copy_item
= copy_item
->next
) {
3950 if (!copy_item
->data
) continue;
3951 copy
= (struct isds_message_copy
*) copy_item
->data
;
3952 recipient_id_locale
= utf82locale(copy
->dbIDRecipient
);
3956 if (copy
->dmStatus
) dmStatus_locale
= utf82locale(copy
->dmStatus
);
3957 if (dmStatus_locale
)
3958 oprintf(_("%s: Failed: %s: %s\n"),
3959 recipient_id_locale
,
3960 isds_strerror(copy
->error
), dmStatus_locale
);
3962 oprintf(_("%s: Failed: %s\n"), recipient_id_locale
,
3963 isds_strerror(copy
->error
));
3964 zfree(dmStatus_locale
);
3966 message_id_locale
= utf82locale(copy
->dmID
);
3967 oprintf(_("%s: Succeeded. Assigned message ID: %s\n"),
3968 recipient_id_locale
, message_id_locale
);
3969 free(message_id_locale
);
3972 free(recipient_id_locale
);
3976 isds_message_free(&message
);
3977 isds_list_free(&copies
);
3982 #undef FILL_LONGINT_OR_LEAVE
3983 #undef FILL_BOOLEAN_OR_LEAVE
3984 #undef CALLOC_OR_LEAVE
3985 #undef FILL_OR_LEAVE
3988 static void shi_save_stamp_usage(const char *command
) {
3991 "Save message time stamp into local FILE.\n"),
3996 static int shi_save_stamp(int argc
, const char **argv
) {
3997 _Bool overwrite
= cfg_getbool(configuration
, CONFIG_OVERWRITEFILES
);
3999 if (!argv
|| !argv
[1] || !*argv
[1]) {
4000 shi_save_message_usage(argv
[0]);
4005 fprintf(stderr
, _("No message loaded\n"));
4008 if (!message
->envelope
|| !message
->envelope
->timestamp
||
4009 message
->envelope
->timestamp_length
== 0) {
4010 fprintf(stderr
, _("Loaded message is missing time stamp\n"));
4014 return save_data_to_file(argv
[1], -1,
4015 message
->envelope
->timestamp
, message
->envelope
->timestamp_length
,
4016 "application/timestamp-reply", overwrite
);
4020 static int shi_show_list(int argc
, const char **argv
) {
4022 fprintf(stderr
, _("No message list loaded\n"));
4026 oprintf((messages_are_outgoing
) ?
4027 ngettext("You have %'lu outgoing message\n",
4028 "You have %'lu outgoing messages\n", total_messages
) :
4029 ngettext("You have %'lu incoming message\n",
4030 "You have %'lu incoming messages\n", total_messages
),
4032 print_message_list(messages
, messages_are_outgoing
);
4037 static int shi_list_incoming(int argc
, const char **argv
) {
4040 printf(_("Listing incoming messages...\n"));
4041 err
= isds_get_list_of_received_messages(cisds
, NULL
, NULL
, NULL
,
4042 MESSAGESTATE_ANY
, 0, &total_messages
, &messages
);
4043 finish_isds_operation(cisds
, err
);
4046 select_completition(COMPL_COMMAND
);
4049 messages_are_outgoing
= 0;
4051 shi_show_list(0, NULL
);
4053 set_prompt(_("%s %'lu"), argv
[0], total_messages
);
4054 select_completition(COMPL_LIST
);
4059 static int shi_list_outgoing(int argc
, const char **argv
) {
4062 printf(_("Listing outgoing messages...\n"));
4063 err
= isds_get_list_of_sent_messages(cisds
, NULL
, NULL
, NULL
,
4064 MESSAGESTATE_ANY
, 0, &total_messages
, &messages
);
4065 finish_isds_operation(cisds
, err
);
4068 select_completition(COMPL_COMMAND
);
4071 messages_are_outgoing
= 1;
4073 shi_show_list(0, NULL
);
4075 set_prompt(_("%s %'lu"), argv
[0], total_messages
);
4076 select_completition(COMPL_LIST
);
4081 /* Submit document for conversion and print assigned identifier */
4082 static int do_convert(const struct isds_document
*document
) {
4085 struct tm
*date
= NULL
;
4087 if (!document
) return -1;
4089 printf(_("Submitting document for authorized conversion...\n"));
4091 err
= czp_convert_document(czechpoint
, document
, &id
, &date
);
4092 finish_isds_operation(czechpoint
, err
);
4095 char *name_locale
= utf82locale(document
->dmFileDescr
);
4096 char *date_string
= tm2string(date
);
4097 char *id_locale
= utf82locale(id
);
4099 "Document submitted for authorized conversion successfully under name\n"
4101 "Submit identifier assigned by Czech POINT deposit is `%s'.\n"),
4102 name_locale
, date_string
, id_locale
);
4106 oprintf(_("Be ware that submitted document has restricted lifetime "
4108 oprintf(_("See <%s> for more details.\n"), CZPDEPOSIT_URL
);
4111 free(id
); free(date
);
4112 return (err
) ? -1 : 0;
4116 static void shi_convert_file_or_message_usage(const char *command
) {
4118 "Usage: %s [FILE [NAME]]\n"
4119 "Submit local FILE to authorized conversion under NAME. If NAME is missing,\n"
4120 "it will use FILE name. If FILE is missing, it will submit current message.\n"),
4124 "If Czech POINT deposit accepts document, it will return document identifier\n"
4125 "that user is supposed to provide to officer at Czech POINT contact place.\n"
4126 "Currently only PDF 1.3 and higher version files and signed messages are\n"
4128 oprintf(_("See <%s> for more details.\n"), CZPDEPOSIT_URL
);
4132 static int shi_convert_file_or_message(int argc
, const char **argv
) {
4134 struct isds_document document
;
4137 if (!argv
|| argc
> 3) {
4138 shi_convert_file_or_message_usage((argv
)?argv
[0]:NULL
);
4142 memset(&document
, 0, sizeof(document
));
4144 if (NULL
== argv
[1] || !*argv
[1]) {
4145 /* Convert current message */
4147 fprintf(stderr
, _("No message loaded\n"));
4150 if (!message
->raw
|| !message
->raw_length
) {
4152 _("Current message is missing raw representation\n"));
4155 document
.dmFileDescr
= astrcat(
4156 (NULL
!= message
->envelope
&& NULL
!= message
->envelope
->dmID
) ?
4157 message
->envelope
->dmID
:
4160 if (NULL
== document
.dmFileDescr
) {
4161 printf(_("Could not build document name from message ID\n"));
4164 document
.data
= message
->raw
;
4165 document
.data_length
= message
->raw_length
;
4167 /* Convert local file */
4168 if (argc
== 3 && argv
[2] && *argv
[2])
4169 document
.dmFileDescr
= locale2utf8(argv
[2]);
4171 document
.dmFileDescr
= locale2utf8(argv
[1]);
4172 if (!document
.dmFileDescr
) {
4173 printf(_("Could not convert document name to UTF-8\n"));
4177 printf(_("Loading document from file `%s'...\n"), argv
[1]);
4178 if (mmap_file(argv
[1], &fd
, &document
.data
, &document
.data_length
)) {
4179 free(document
.dmFileDescr
);
4184 retval
= do_convert(&document
);
4187 munmap_file(fd
, document
.data
, document
.data_length
);
4189 free(document
.dmFileDescr
);
4194 static void shi_convert_document_usage(const char *command
) {
4196 "Usage: %s NUMBER\n"
4197 "Submit message document with ordinal NUMBER to authorized conversion.\n"),
4201 "If Czech POINT deposit accepts document, it will return document identifier\n"
4202 "that user is supposed to provide to officer at Czech POINT contact place.\n"
4203 "Currently only PDF 1.3 and higher version files and signed messages are\n"
4205 oprintf(_("See <%s> for more details.\n"), CZPDEPOSIT_URL
);
4209 static int shi_convert_document(int argc
, const char **argv
) {
4210 const struct isds_document
*document
;
4212 if (!argv
|| !argv
[1] || !*argv
[1]) {
4213 shi_convert_document_usage((argv
)?argv
[0]:NULL
);
4217 document
= locate_document_by_ordinal_string(argv
[1]);
4218 if (!document
) return -1;
4220 return do_convert(document
);
4224 static void shi_resign_usage(const char *command
) {
4226 "Usage: %s [FILE]\n"
4227 "Send message or delivery data to re-sign it and to add current time stamp.\n"
4228 "If FILE is specified, message will be loaded from local file. Otherwise\n"
4229 "current message will be sent.\n"),
4233 "Only signed messages or delivery data without time stamp are accepted by\n"
4234 "ISDS. Output re-signed message or delivery data will be loaded.\n"));
4238 static int shi_resign(int argc
, const char **argv
) {
4240 void *data
= NULL
; /* Static */
4241 void *resigned_data
= NULL
; /* Dynamic, stored into message */
4242 size_t data_length
= 0, resigned_data_length
= 0;
4243 struct tm
*valid_to
= NULL
; /* Dynamic */
4247 if (!argv
|| argc
> 3) {
4248 shi_resign_usage((argv
)?argv
[0]:NULL
);
4252 if (NULL
== argv
[1] || !*argv
[1]) {
4253 /* Use current message */
4255 fprintf(stderr
, _("No message or delivery data loaded\n"));
4258 if (!message
->raw
|| !message
->raw_length
) {
4259 fprintf(stderr
, _("Current message or delivery data "
4260 "is missing raw representation\n"));
4263 data
= message
->raw
;
4264 data_length
= message
->raw_length
;
4266 /* Use local file */
4267 printf(_("Loading message or delivery data from file `%s'...\n"),
4269 if (mmap_file(argv
[1], &fd
, &data
, &data_length
)) {
4274 printf(_("Re-signing...\n"));
4275 err
= isds_resign_message(cisds
, data
, data_length
,
4276 &resigned_data
, &resigned_data_length
, &valid_to
);
4277 finish_isds_operation(cisds
, err
);
4280 munmap_file(fd
, data
, data_length
);
4287 print_header_tm(_("New time stamp expires"), valid_to
);
4290 error
= do_load_anything(resigned_data
, resigned_data_length
, BUFFER_MOVE
);
4291 if (error
) free(resigned_data
);
4298 static void shi_print_usage(const char *command
) {
4300 "Usage: %s STRING LENGTH\n"
4301 "Prints STRING into LENGTH columns. Negative LENGTH means not to cut\n"
4302 "overflowing string.\n"
4303 "This should be locale and terminal agnostic.\n"),
4308 static int shi_print(int argc
, const char **argv
) {
4311 if (!argv
|| !argv
[1] || !argv
[2]) {
4312 shi_print_usage((argv
)?argv
[0]:NULL
);
4316 width
= strtol(argv
[2], NULL
, 10);
4317 if (width
< INT_MIN
) {
4319 _("Length argument must not lesser than %d.\n"), INT_MIN
);
4322 if (width
> INT_MAX
) {
4324 _("Length argument must not be greater than %d.\n"), INT_MAX
);
4329 onprint(argv
[1], width
);
4336 static int shi_tokenize(int argc
, const char **argv
) {
4338 if (!argv
) return 0;
4340 for (int i
= 0; i
< argc
; i
++) {
4341 oprintf(_(">%s<\n"), argv
[i
]);
4347 static int shi_quote(int argc
, const char **argv
) {
4348 char *escaped
, *unescaped
;
4350 if (!argv
) return 0;
4352 oprintf(_("Original\tQuoted\tDequoted\n"));
4353 for (int i
= 0; i
< argc
; i
++) {
4354 escaped
= shi_quote_filename((char *) argv
[i
], 0, NULL
);
4355 unescaped
= shi_dequote_filename((char *) argv
[i
], 0);
4356 oprintf(_(">%s<\t>%s<\t>%s<\n"), argv
[i
], escaped
, unescaped
);
4365 /* pclose(pipe), restore ouput to stdout, show error return code */
4366 int wait_for_shell(FILE **pipe
) {
4369 if (pipe
&& *pipe
) {
4370 retval
= pclose(*pipe
);
4375 fprintf(stderr
, _("Exit code of shell command could not "
4376 "be determined\n"));
4377 else if (WIFEXITED(retval
) && WEXITSTATUS(retval
))
4378 printf(_("Shell command exited with code %d\n"),
4379 WEXITSTATUS(retval
));
4380 else if (WIFSIGNALED(retval
))
4381 printf(_("Shell command terminated by signal "
4382 "#%d\n"), WTERMSIG(retval
));
4389 /* Interactive loop */
4390 void shi_loop(void) {
4391 char *command_line
= NULL
;
4392 char **command_argv
= NULL
;
4395 struct command
*command
= NULL
;
4399 oprintf(_("Use `help' command to get list of available commands.\n"));
4401 select_completition(COMPL_COMMAND
);
4405 command_line
= readline((prompt
) ? prompt
: _("shigofumi> "));
4406 /* Remember not parsable commands too to user be able to get back to
4408 if (command_line
&& *command_line
) {
4409 /* TODO: Omit blank lines */
4410 add_history(command_line
);
4413 command_argv
= tokenize(command_line
, &command_argc
, &shell
);
4415 if (command_argv
&& command_argv
[0]) {
4416 command
= find_command(command_argv
[0]);
4419 fprintf(stderr
, _("Command not understood\n"));
4424 pipe
= popen(shell
, "w");
4426 fprintf(stderr
, _("Could not run shell command `%s':"
4427 " %s\n"), shell
, strerror(errno
));
4433 command
->function(command_argc
,
4434 (const char **) command_argv
);
4435 wait_for_shell(&pipe
);
4440 argv_free(command_argv
);
4442 zfree(command_line
);
4447 /* Non-interactive mode. Commands from @lines are processed until any command
4448 * lines remains or no error occurred. First failure terminates processing.
4449 * @lines is sequence of commands separated by '\n' or '\r'. The content is
4450 * modified during this call.
4451 * @return 0 if all command succeed, otherwise non-zero value
4453 int shi_batch(char *lines
) {
4455 char **command_argv
= NULL
;
4458 struct command
*command
= NULL
;
4463 oprintf(_("Batch mode started.\n"));
4465 select_completition(COMPL_COMMAND
);
4467 while (!retval
&& (command_line
= strtok(lines
, "\n\r"))) {
4468 lines
= NULL
; /* strtok(3) requires it for subsequent calls */
4470 printf(_("Processing command: %s\n"), command_line
);
4472 command_argv
= tokenize(command_line
, &command_argc
, &shell
);
4474 if (command_argv
&& command_argv
[0]) {
4475 command
= find_command(command_argv
[0]);
4478 fprintf(stderr
, _("Command not understood\n"));
4484 pipe
= popen(shell
, "w");
4486 fprintf(stderr
, _("Could not run shell command `%s':"
4487 " %s\n"), shell
, strerror(errno
));
4493 retval
= command
->function(command_argc
,
4494 (const char **) command_argv
);
4495 if (wait_for_shell(&pipe
)) retval
= -1;
4500 argv_free(command_argv
);
4505 fprintf(stderr
, _("Command failed!\n"));
4510 #define COMMON_COMMANDS \
4511 { "accept", shi_accept_message, N_("accept commercial message"), \
4512 shi_accept_message_usage, ARGTYPE_MSGID }, \
4513 { "box", shi_box, N_("show current box details"), NULL, \
4515 { "boxlist", shi_boxlist, N_("get list of all boxes"), shi_boxlist_usage, \
4517 { "cache", shi_cache, N_("show cache details"), NULL, \
4519 { "cd", shi_chdir, N_("change working directory"), shi_chdir_usage, \
4521 { "commercialreceiving", shi_commercialreceiving, \
4522 N_("manipulate commercial receiving box status"), \
4523 shi_commercialreceiving_usage, ARGTYPE_BOXID }, \
4524 { "commercialsending", shi_commercialsending, \
4525 N_("manipulate commercial sending box status"), \
4526 shi_commercialsending_usage, ARGTYPE_BOXID }, \
4527 { "compose", shi_compose, N_("compose a message"), shi_compose_usage, \
4529 { "convert", shi_convert_file_or_message, \
4530 N_("submit local document for authorized conversion"), \
4531 shi_convert_file_or_message_usage, ARGTYPE_FILE }, \
4532 { "copying", shi_copying, N_("show this program licence excerpt"), NULL, \
4534 { "debug", shi_debug, N_("set debugging"), shi_debug_usage, \
4536 { "delete", shi_delete_message, N_("delete message from storage"), \
4537 shi_delete_message_usage, ARGTYPE_MSGID }, \
4538 { "delivery", shi_delivery, N_("get message delivery details"), \
4539 shi_delivery_usage, ARGTYPE_MSGID }, \
4540 { "findbox", shi_find_box, N_("search for a box"), shi_find_box_usage, \
4542 { "hash", shi_hash, N_("query ISDS for message hash"), \
4543 shi_hash_usage, ARGTYPE_MSGID }, \
4544 { "help", shi_help, N_("describe commands"), shi_help_usage, \
4545 ARGTYPE_COMMAND }, \
4546 { "load", shi_load_anything, \
4547 N_("load message or message delivery details from local file"), \
4548 shi_load_anything_usage, ARGTYPE_FILE }, \
4549 { "login", shi_login, N_("log into ISDS"), shi_login_usage, \
4551 { "lsi", shi_list_incoming, N_("list received messages"), NULL, \
4553 { "lso", shi_list_outgoing, N_("list sent messages"), NULL, \
4555 { "msgi", shi_incoming_message, N_("get incoming message"), \
4556 shi_incoming_message_usage, ARGTYPE_MSGID }, \
4557 { "msgo", shi_outgoing_message, N_("get outgoing message"), \
4558 shi_outgoing_message_usage, ARGTYPE_MSGID }, \
4559 { "passwd", shi_passwd, N_("manipulate user password"), shi_passwd_usage, \
4561 { "pwd", shi_pwd, N_("print working directory"), NULL, ARGTYPE_NONE }, \
4562 { "quit", shi_quit, N_("exit shigofumi"), NULL, ARGTYPE_NONE }, \
4563 { "read", shi_read_message, N_("mark message as read"), \
4564 shi_read_message_usage, ARGTYPE_MSGID }, \
4565 { "resign", shi_resign, N_("re-sign message or delivery data"), \
4566 shi_resign_usage, ARGTYPE_FILE }, \
4567 { "set", shi_settings, N_("show settings"), NULL, ARGTYPE_NONE }, \
4568 { "sender", shi_message_sender, N_("get message sender"), \
4569 shi_message_sender_usage, ARGTYPE_MSGID }, \
4570 { "statbox", shi_stat_box, N_("get status of a box"), shi_stat_box_usage, \
4572 { "user", shi_user, N_("show current user details"), NULL, \
4574 { "users", shi_users, N_("show box users"), shi_users_usage, \
4576 { "version", shi_version, N_("show version of this program"), NULL, \
4578 { NULL, NULL, NULL, NULL, ARGTYPE_NONE }
4580 struct command base_commands
[] = {
4582 { "quote", shi_quote
, N_("demonstrate argument escaping"), NULL
,
4584 { "print", shi_print
, N_("print string into given width"),
4585 shi_print_usage
, ARGTYPE_NONE
},
4586 { "tokenize", shi_tokenize
, N_("demonstrate arguments tokenization"), NULL
,
4592 struct command message_commands
[] = {
4593 { "authenticate", shi_authenticate
, N_("check message authenticity"),
4594 NULL
, ARGTYPE_NONE
},
4595 { "cat", shi_cat_message
, N_("show raw current message"),
4596 shi_cat_message_usage
, ARGTYPE_NONE
},
4597 { "catdoc", shi_cat_document
, N_("show raw document"),
4598 shi_cat_document_usage
, ARGTYPE_DOCID
},
4599 { "convertdoc", shi_convert_document
,
4600 N_("submit document of current message for authorized conversion"),
4601 shi_convert_document_usage
, ARGTYPE_DOCID
},
4602 { "dump", shi_dump_message
, N_("dump current message structure"),
4603 NULL
, ARGTYPE_NONE
},
4604 { "opendoc", shi_open_document
, N_("open document using external utility"),
4605 shi_open_document_usage
, ARGTYPE_DOCID
},
4606 { "savestamp", shi_save_stamp
,
4607 N_("save time stamp of current message into local file"),
4608 shi_save_stamp_usage
, ARGTYPE_FILE
},
4609 { "savedoc", shi_save_document
,
4610 N_("save document of current message into local file"),
4611 shi_save_document_usage
, ARGTYPE_FILE
},
4612 { "save", shi_save_message
, N_("save current message into local file"),
4613 shi_save_message_usage
, ARGTYPE_FILE
},
4614 { "show", shi_show_message
, N_("show current message"), NULL
,
4616 { "verify", shi_verify
, N_("verify current message hash"), NULL
,
4621 struct command list_commands
[] = {
4622 { "show", shi_show_list
, N_("show current message list"), NULL
,
4627 #undef COMMON_COMMANDS
4630 static void main_version(void) {
4635 shi_copying(0, NULL
);
4639 static void main_usage(const char *command
) {
4641 "Usage: %s [OPTION...]\n"
4642 "Access ISDS, process local data box messages or delivery details, submit\n"
4643 "document to authorized conversion.\n"
4646 " -c FILE use the FILE as configuration file instead of ~/%s\n"
4647 " -e COMMANDS execute COMMANDS (new line separated) and exit\n"
4648 " -V show version info and exit\n"
4650 (command
) ? command
: "shigofumi",
4655 int main(int argc
, char **argv
) {
4657 char *config_file
= NULL
;
4658 char *batch_commands
= NULL
;
4659 int retval
= EXIT_SUCCESS
;
4661 setlocale(LC_ALL
, "");
4663 /* Initialize gettext */
4664 bindtextdomain(PACKAGE
, LOCALEDIR
);
4665 textdomain(PACKAGE
);
4668 /* Default output */
4671 /* Parse arguments */
4673 while ((opt
= getopt(argc
, (char * const *)argv
, "c:e:V")) != -1) {
4676 config_file
= optarg
;
4679 batch_commands
= optarg
;
4683 shi_exit(EXIT_SUCCESS
);
4686 main_usage((argv
[0]) ? basename(argv
[0]): NULL
);
4687 shi_exit(EXIT_FAILURE
);
4692 if (shi_init(config_file
)) {
4693 shi_exit(EXIT_FAILURE
);
4696 /*shi_login(NULL);*/
4698 if (batch_commands
) {
4699 if (shi_batch(batch_commands
))
4700 retval
= EXIT_FAILURE
;