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 "completion.h"
29 #define CONFIG_FILE ".shigofumirc"
30 #define CONFIG_SERVER "base_url"
31 #define CONFIG_USERNAME "username"
32 #define CONFIG_PASSWORD "password"
33 #define CONFIG_CERT_FORMAT "certificate_format"
34 #define CONFIG_CERT_PATH "certificate_path"
35 #define CONFIG_KEY_ENGINE "key_engine"
36 #define CONFIG_KEY_FORMAT "key_format"
37 #define CONFIG_KEY_PATH "key_path"
38 #define CONFIG_KEY_PASSWORD "key_password"
39 #define CONFIG_OTP_METHOD "otp_method"
40 #define CONFIG_OTP_CODE "otp_code"
41 #define CONFIG_DEBUGLEVEL "debug_level"
42 #define CONFIG_VERIFYSERVER "verify_server"
43 #define CONFIG_CAFILE "ca_file"
44 #define CONFIG_CADIRECTORY "ca_directory"
45 #define CONFIG_CLEAN_TEMPORARY_FILES "clean_temporary_files"
46 #define CONFIG_CONFIRM_SEND "confirm_send"
47 #define CONFIG_CRLFILE "crl_file"
48 #define CONFIG_TIMEOUT "timeout"
49 #define CONFIG_LOGFACILITIES "log_facilities"
50 #define CONFIG_LOGFILE "log_file"
51 #define CONFIG_LOGLEVEL "log_level"
52 #define CONFIG_MARKMESSAGEREAD "mark_message_read"
53 #define CONFIG_NORMALIZEMIMETYPE "normalize_mime_type"
54 #define CONFIG_OPENCOMMAND "open_command"
55 #define CONFIG_OVERWRITEFILES "overwrite_files"
56 #define CZPDEPOSIT_URL "https://www.czechpoint.cz/uschovna/"
62 cfg_opt_t configuration_syntax
[] = {
63 CFG_STR(CONFIG_SERVER
, NULL
, CFGF_NONE
),
64 CFG_STR(CONFIG_USERNAME
, NULL
, CFGF_NODEFAULT
),
65 CFG_STR(CONFIG_PASSWORD
, NULL
, CFGF_NODEFAULT
),
66 CFG_STR(CONFIG_CERT_FORMAT
, NULL
, CFGF_NODEFAULT
),
67 CFG_STR(CONFIG_CERT_PATH
, NULL
, CFGF_NODEFAULT
),
68 CFG_STR(CONFIG_KEY_ENGINE
, NULL
, CFGF_NODEFAULT
),
69 CFG_STR(CONFIG_KEY_FORMAT
, NULL
, CFGF_NODEFAULT
),
70 CFG_STR(CONFIG_KEY_PATH
, NULL
, CFGF_NODEFAULT
),
71 CFG_STR(CONFIG_KEY_PASSWORD
, NULL
, CFGF_NODEFAULT
),
72 CFG_STR(CONFIG_OTP_METHOD
, NULL
, CFGF_NODEFAULT
),
73 CFG_STR(CONFIG_OTP_CODE
, NULL
, CFGF_NODEFAULT
),
74 /*CFG_STR(CONFIG_DEBUGLEVEL, NULL, CFGF_NODEFAULT),*/
75 CFG_BOOL(CONFIG_VERIFYSERVER
, cfg_true
, CFGF_NONE
),
76 CFG_STR(CONFIG_CAFILE
, NULL
, CFGF_NODEFAULT
),
77 CFG_STR(CONFIG_CADIRECTORY
, NULL
, CFGF_NODEFAULT
),
78 CFG_BOOL(CONFIG_CLEAN_TEMPORARY_FILES
, cfg_true
, CFGF_NONE
),
79 CFG_STR(CONFIG_CRLFILE
, NULL
, CFGF_NODEFAULT
),
80 CFG_INT(CONFIG_TIMEOUT
, TIMEOUT
, CFGF_NONE
),
81 CFG_STR_LIST(CONFIG_LOGFACILITIES
, "{none}", CFGF_NONE
),
82 CFG_STR(CONFIG_LOGFILE
, NULL
, CFGF_NODEFAULT
),
83 CFG_INT(CONFIG_LOGLEVEL
, LOG_LEVEL
, CFGF_NONE
),
84 CFG_BOOL(CONFIG_CONFIRM_SEND
, cfg_true
, CFGF_NONE
),
85 CFG_BOOL(CONFIG_MARKMESSAGEREAD
, cfg_false
, CFGF_NONE
),
86 CFG_BOOL(CONFIG_NORMALIZEMIMETYPE
, cfg_true
, CFGF_NONE
),
87 CFG_STR_LIST(CONFIG_OPENCOMMAND
, "{xdg-open, %f}", CFGF_NONE
),
88 CFG_BOOL(CONFIG_OVERWRITEFILES
, cfg_true
, CFGF_NONE
),
99 struct command (*commands
)[] = NULL
;
103 struct isds_ctx
*cisds
= NULL
;
104 struct isds_list
*boxes
= NULL
;
105 struct isds_message
*message
= NULL
;
106 _Bool messages_are_outgoing
= 0;
107 struct isds_list
*messages
= NULL
;
108 unsigned long int total_messages
= 0;
109 struct isds_ctx
*czechpoint
= NULL
;
110 struct isds_list
*temporary_files
= NULL
;
112 /* Temporary log-in settings */
114 char *username
= NULL
;
115 char *password
= NULL
;
116 char *key_password
= NULL
;
117 char *otp_method
= NULL
;
118 char *otp_code
= NULL
;
119 char *pki_engine
= NULL
;
120 char *pki_certificate_path
= NULL
;
121 char *pki_certificate_format
= NULL
;
122 char *pki_key_path
= NULL
;
123 char *pki_key_format
= NULL
;
125 static void discard_credentials(void) {
133 zfree(pki_certificate_path
);
134 zfree(pki_certificate_format
);
136 zfree(pki_key_format
);
140 /* Remove temporary file */
141 void shi_unlink_temporary_file(void **data
) {
142 if (!data
|| !*data
) return;
144 const char *file
= (const char *)*data
;
150 /* Finish ISDS operation, report error, if the operation returned different
151 * code than @positive_code. */
152 static void finish_isds_operation_with_code(struct isds_ctx
*ctx
,
153 isds_error err
, isds_error positive_code
) {
154 shi_progressbar_finish();
155 if (err
!= positive_code
) {
156 if (isds_long_message(ctx
))
157 fprintf(stderr
, _("Error occurred: %s: %s\n"), isds_strerror(err
),
158 isds_long_message(ctx
));
160 fprintf(stderr
, _("Error occurred: %s\n"), isds_strerror(err
));
165 /* Finish ISDS operation, report error, if the operation did not returned
167 static void finish_isds_operation(struct isds_ctx
*ctx
, isds_error err
) {
168 finish_isds_operation_with_code(ctx
, err
, IE_SUCCESS
);
172 /* Do the cleanup and exit */
173 static void shi_exit(int exit_code
) {
175 discard_credentials();
176 isds_list_free(&boxes
);
177 isds_message_free(&message
);
178 isds_list_free(&messages
);
179 if (temporary_files
) {
180 oprintf(_("Removing temporary files...\n"));
181 isds_list_free(&temporary_files
);
186 oprintf(_("Logging out...\n"));
187 err
= isds_logout(cisds
);
188 finish_isds_operation(cisds
, err
);
189 if (err
) exit_code
= EXIT_FAILURE
;
190 isds_ctx_free(&cisds
);
192 isds_ctx_free(&czechpoint
);
196 cfg_free(configuration
);
205 /* Set prompt. if @format is NULL, switch to default prompt */
206 static void set_prompt(const char *format
, ...) {
210 va_start(ap
, format
);
211 shi_vasprintf(&buffer
, format
, ap
);
215 shi_asprintf(&prompt
, _("%s> "), buffer
);
224 prompt
= strdup(_("> "));
232 static int shi_load_configuration(const char *config_file
) {
233 char *config_name
= NULL
;
236 /* Get config file */
238 config_name
= (char *) config_file
;
240 if (-1 == shi_asprintf(&config_name
, "%s/%s", getenv("HOME"),
242 fprintf(stderr
, _("Could not build configuration file name\n"));
247 /* Parse configuration */
248 configuration
= cfg_init(configuration_syntax
, CFGF_NONE
);
249 ret
= cfg_parse(configuration
, config_name
);
251 if (ret
== CFG_FILE_ERROR
) {
253 _("Error while opening configuration file `%s': %s\n"),
254 config_name
, strerror(errno
));
256 fprintf(stderr
, _("Error while parsing configuration file `%s'\n"),
259 oprintf(_("Using default configuration\n"));
262 if (config_name
!= config_file
) free(config_name
);
267 void logger(isds_log_facility facility
, isds_log_level level
,
268 const char *message
, int length
, void *data
) {
270 ssize_t written
, left
= length
;
273 fd
= *((int *) data
);
274 /*printf("\033[32mLOG(%02d,%02d): ", facility, level);
275 printf("%.*s", length, message);
279 written
= write(fd
, message
+ length
- left
, left
);
282 _("Could not save log message into log file: %s\n"
283 "Log message discarded!\n"),
294 /* Redirect ISDS log to file if @file is not NULL. */
295 static int do_log_to_file(const char *file
) {
297 logger_fd
= open_file_for_writing(file
, 0, 1);
298 if (logger_fd
== -1) {
299 fprintf(stderr
, _("Could not redirect ISDS log to file `%s'\n"),
303 isds_set_log_callback(logger
, &logger_fd
);
309 /* Add log facility based on its name. */
310 static int add_log_facility(isds_log_facility
*facilities
, const char *name
) {
311 if (!facilities
) return -1;
313 if (!strcmp(name
, "none")) *facilities
|= ILF_NONE
;
314 else if (!strcmp(name
, "http")) *facilities
|= ILF_HTTP
;
315 else if (!strcmp(name
, "soap")) *facilities
|= ILF_SOAP
;
316 else if (!strcmp(name
, "isds")) *facilities
|= ILF_ISDS
;
317 else if (!strcmp(name
, "file")) *facilities
|= ILF_FILE
;
318 else if (!strcmp(name
, "sec")) *facilities
|= ILF_SEC
;
319 else if (!strcmp(name
, "xml")) *facilities
|= ILF_XML
;
320 else if (!strcmp(name
, "all")) *facilities
|= ILF_ALL
;
322 fprintf(stderr
, _("%s: Unknown log facility\n"), name
);
330 /* Save log facility into confuse configuration */
331 static void save_log_facility(int level
) {
332 cfg_setlist(configuration
, CONFIG_LOGFACILITIES
, 0);
334 if (level
== ILF_ALL
) {
335 cfg_addlist(configuration
, CONFIG_LOGFACILITIES
, 1, "all");
338 if (level
== ILF_NONE
) {
339 cfg_addlist(configuration
, CONFIG_LOGFACILITIES
, 1, "none");
342 if (level
& ILF_HTTP
)
343 cfg_addlist(configuration
, CONFIG_LOGFACILITIES
, 1, "http");
344 if (level
& ILF_SOAP
)
345 cfg_addlist(configuration
, CONFIG_LOGFACILITIES
, 1, "soap");
346 if (level
& ILF_ISDS
)
347 cfg_addlist(configuration
, CONFIG_LOGFACILITIES
, 1, "isds");
348 if (level
& ILF_FILE
)
349 cfg_addlist(configuration
, CONFIG_LOGFACILITIES
, 1, "file");
351 cfg_addlist(configuration
, CONFIG_LOGFACILITIES
, 1, "sec");
353 cfg_addlist(configuration
, CONFIG_LOGFACILITIES
, 1, "xml");
357 /* Clamp long int to unsigned int */
358 static unsigned int normalize_timeout(long int raw
) {
360 oprintf(_("Configured network timeout is less then 0. "
364 if (raw
> UINT_MAX
) {
365 oprintf(_("Configured network timeout is greater then %1$u. "
366 "Clamped to %1$u.\n"), UINT_MAX
);
369 return (unsigned int) raw
;
373 /* Clamp long int to <0;100> */
374 static unsigned int normalize_log_level(long int raw
) {
376 oprintf(_("Configured log level is less then 0. Clamped to 0.\n"));
380 oprintf(_("Configured log level is greater then %1$u. "
381 "Clamped to %1$u.\n"), ILL_ALL
);
384 if (raw
> UINT_MAX
) {
385 oprintf(_("Configured log level is greater then %1$u. "
386 "Clamped to %1$u.\n"), UINT_MAX
);
389 return (unsigned int) raw
;
393 static int shi_init(const char *config_file
) {
396 unsigned int timeout
, log_level
;
397 isds_log_facility log_facility
= ILF_NONE
;
399 oprintf(_("This is Shigofumi, an ISDS client. "
400 "Have a nice e-government.\n"));
402 /* Do not permute arguments in getopt() */
403 if (setenv("POSIXLY_CORRECT", "", 1)) {
405 _("Could not set POSIXLY_CORRECT environment variable\n"));
409 /* Load configuration */
410 if (shi_load_configuration(config_file
))
412 timeout
= normalize_timeout(cfg_getint(configuration
, CONFIG_TIMEOUT
));
413 log_level
= normalize_log_level(cfg_getint(configuration
, CONFIG_LOGLEVEL
));
416 rl_readline_name
= "shigofumi";
417 rl_filename_quote_characters
= "\\ >";
418 rl_filename_quoting_function
= shi_quote_filename
;
419 rl_filename_dequoting_function
= shi_dequote_filename
;
420 rl_char_is_quoted_p
= shi_char_is_quoted
;
422 /* Initialize ISDS */
425 fprintf(stderr
, _("Could not initialize libisds library: %s\n"),
430 /* Set ISDS logging */
431 value
= cfg_getstr(configuration
, CONFIG_LOGFILE
);
432 if (do_log_to_file(value
))
434 for (int i
= 0; i
< cfg_size(configuration
, CONFIG_LOGFACILITIES
); i
++) {
435 if (add_log_facility(&log_facility
,
436 cfg_getnstr(configuration
, CONFIG_LOGFACILITIES
, i
)))
440 isds_set_logging(log_facility
, log_level
);
442 /* Set ISDS context up */
443 cisds
= isds_ctx_create();
445 fprintf(stderr
, _("Could not create ISDS context\n"));
448 err
= isds_set_timeout(cisds
, timeout
);
450 fprintf(stderr
, _("Could not set ISDS network timeout: %s\n"),
453 err
= isds_set_progress_callback(cisds
, shi_progressbar
, NULL
);
455 fprintf(stderr
, _("Could not register network progress bar: %s: %s\n"),
456 isds_strerror(err
), isds_long_message(cisds
));
458 err
= isds_set_opt(cisds
, IOPT_NORMALIZE_MIME_TYPE
,
459 cfg_getbool(configuration
, CONFIG_NORMALIZEMIMETYPE
));
462 cfg_getbool(configuration
, CONFIG_NORMALIZEMIMETYPE
) ?
463 _("Could not enable MIME type normalization: %s: %s\n") :
464 _("Could not disable MIME type normalization: %s: %s\n"),
465 isds_strerror(err
), isds_long_message(cisds
));
467 if (!cfg_getbool(configuration
, CONFIG_VERIFYSERVER
)) {
468 oprintf(_("Warning: Shigofumi disabled server identity verification "
469 "on user request!\n"));
470 err
= isds_set_opt(cisds
, IOPT_TLS_VERIFY_SERVER
, 0);
473 _("Could not disable server identity verification: "
475 isds_strerror(err
), isds_long_message(cisds
));
478 if ((value
= cfg_getstr(configuration
, CONFIG_CAFILE
))) {
479 err
= isds_set_opt(cisds
, IOPT_TLS_CA_FILE
, value
);
482 _("Could not set file with CA certificates: %s: %s: %s\n"),
483 value
, isds_strerror(err
), isds_long_message(cisds
));
486 if ((value
= cfg_getstr(configuration
, CONFIG_CADIRECTORY
))) {
487 err
= isds_set_opt(cisds
, IOPT_TLS_CA_DIRECTORY
, value
);
490 _("Could not set directory with CA certificates: "
492 value
, isds_strerror(err
), isds_long_message(cisds
));
495 if ((value
= cfg_getstr(configuration
, CONFIG_CRLFILE
))) {
496 err
= isds_set_opt(cisds
, IOPT_TLS_CRL_FILE
, value
);
498 fprintf(stderr
, _("Could not set file with CRL: %s: %s: %s\n"),
499 value
, isds_strerror(err
), isds_long_message(cisds
));
504 /* Set Czech POINT context up */
505 czechpoint
= isds_ctx_create();
507 fprintf(stderr
, _("Could not create Czech POINT context\n"));
510 err
= isds_set_timeout(czechpoint
, timeout
);
512 fprintf(stderr
, _("Could not set Czech POINT network timeout: %s\n"),
515 err
= isds_set_progress_callback(czechpoint
, shi_progressbar
, NULL
);
517 fprintf(stderr
, "Could not register network progress bar: %s: %s\n",
518 isds_strerror(err
), isds_long_message(cisds
));
525 static int shi_quit(int argc
, const char **argv
) {
526 shi_exit(EXIT_SUCCESS
);
532 static void shi_help_usage(const char *command
) {
534 "Usage: %s [COMMAND]\n"
535 "Show COMMAND manual or list of currently available commands.\n"
541 static int shi_help(int argc
, const char **argv
) {
542 size_t command_width
= 14;
546 fprintf(stderr
, _("No command is available\n"));
550 if (argc
== 2 && argv
[1] && *argv
[1]) {
551 /* Show usage for given command */
552 for (i
= 0; (*commands
)[i
].name
; i
++) {
553 if (!strcmp((*commands
)[i
].name
, argv
[1])) {
554 if ((*commands
)[i
].usage
)
555 (*commands
)[i
].usage((*commands
)[i
].name
);
556 else if ((*commands
)[i
].description
) {
557 ohprint((*commands
)[i
].name
, command_width
);
558 oprintf(" %s\n", _((*commands
)[i
].description
));
562 _("%s: %s: Command description not defined\n"),
567 fprintf(stderr
, _("%s: %s: No such command exists\n"), argv
[0], argv
[1]);
571 /* Or list all commands */
572 oprintf(_("Following commands are available:\n"));
573 for (i
= 0; (*commands
)[i
].name
; i
++) {
574 ohprint((*commands
)[i
].name
, command_width
);
575 oprintf(" %s\n", _((*commands
)[i
].description
));
582 static void show_version(void) {
583 char *libisds_version
= isds_version();
585 oprintf(_("This is Shigofumi version %s.\n"), PACKAGE_VERSION
);
593 confuse_version
, libisds_version
, xmlParserVersion
,
595 free(libisds_version
);
599 static int shi_version(int argc
, const char **argv
) {
604 "It's a shigofumi. A letter delivered from the afterlife. (Fumika)\n"
605 "A message can not be delivered to dead person. (ISDS specification)\n"
606 "Virtual and real world. They can be compatible. (Program author)\n"
612 static int shi_copying(int argc
, const char **argv
) {
614 "This is Shigofumi, an ISDS client.\n"
615 "Copyright (C) 2010, 2011, 2012, 2013 Petr Pisar\n"
617 "This program is free software: you can redistribute it and/or modify\n"
618 "it under the terms of the GNU General Public License as published by\n"
619 "the Free Software Foundation, either version 3 of the License, or\n"
620 "(at your option) any later version.\n"
622 "This program is distributed in the hope that it will be useful,\n"
623 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
624 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
625 "GNU General Public License for more details.\n"
627 "You should have received a copy of the GNU General Public License\n"
628 "along with this program. If not, see <http://www.gnu.org/licenses/>.\n"
634 static int shi_cache(int argc
, const char **argv
) {
635 const struct isds_list
*item
;
639 for (item
= boxes
, i
= 0; item
; item
= item
->next
, i
++);
641 "Cached box list: %zu\n"),
647 "Cached message list:\n"
649 "\tMessages: %'lu\n"),
650 (messages_are_outgoing
) ? _("Outgoing") : _("Incoming"),
655 oprintf(_("Cached message: %s\n"),
656 (message
->envelope
&& message
->envelope
->dmID
) ?
657 message
->envelope
->dmID
: _("<Unknown ID>"));
664 static void shi_chdir_usage(const char *command
) {
666 "Usage: %s [DIRECTORY]\n"
667 "Change working directory to DIRECTORY.\n"
668 "If no DIRECTORY is supplied, HOME directory will be used.\n"),
673 static int shi_chdir(int argc
, const char **argv
) {
674 const char *directory
= NULL
;
676 if (!argv
|| argc
> 2) {
677 shi_chdir_usage((argv
) ? argv
[0] : NULL
);
681 if (argc
== 2 && argv
[1] && *argv
[1])
684 directory
= getenv("HOME");
686 oprintf("Environment variable HOME does not exist\n");
690 if (chdir(directory
)) {
691 oprintf(_("Could not change working directory: %s: %s\n"), directory
,
700 static int shi_pwd(int argc
, const char **argv
) {
701 char *buffer
= NULL
, *newbuffer
;
704 while (length
+= 1024) {
705 newbuffer
= realloc(buffer
, length
);
707 fprintf(stderr
, _("Error: Not enough memory\n"));
713 if (getcwd(buffer
, length
)) {
714 oprintf("%s\n", buffer
);
720 fprintf(stderr
, _("Error: Current directory string is too long\n"));
726 /* Deallocate *@destination and duplicate @new_value if non-NULL. In case of
727 * error, it prints error message and returns -1. Otherwise it returns 0. */
728 static int replace_string(char **destination
, const char *new_value
) {
729 if (destination
== NULL
) return -1;
731 if (new_value
!= NULL
) {
732 *destination
= strdup(new_value
);
733 if (*destination
== NULL
) {
734 fprintf(stderr
, _("Not enough memory\n"));
742 /* Convert name of PKI format into ISDS format type.
743 * Return -1 in case of invalid name */
744 static int string2pki_format(const char *name
, isds_pki_format
*format
) {
745 if (!name
|| !format
) { return -1; }
746 if (!strcasecmp(name
, "PEM")) {
747 *format
= PKI_FORMAT_PEM
;
748 } else if (!strcasecmp(name
, "DER")) {
749 *format
= PKI_FORMAT_DER
;
750 } else if (!strcasecmp(name
, "ENG")) {
751 *format
= PKI_FORMAT_ENG
;
759 /* Convert name of OTP authentication method into ISDS method type.
760 * Return -1 in case of invalid name */
761 static int string2otp_method(const char *name
, isds_otp_method
*method
) {
762 if (!name
|| !method
) { return -1; }
763 if (!strcasecmp(name
, "HOTP")) {
765 } else if (!strcasecmp(name
, "TOTP")) {
775 * Return -1 in case of failure, 0 in case of success, +1 in case of partial
776 * success (e.g. TOTP preauthentication to obtain new code succeeded, but user
777 * is still not logged in because second phase is necessary). */
778 static int do_login(void) {
780 struct isds_pki_credentials pki
;
783 /* Build OTP structure */
784 if (NULL
!= otp_method
) {
785 if (string2otp_method(otp_method
, &otp
.method
)) {
786 fprintf(stderr
, _("Error: Invalid one-time password "
787 "authentication method `%s'\n"), otp_method
);
792 /* Announce base URL */
793 oprintf(_("ISDS base URL: %s\n"),
794 (server
== NULL
) ? _("<default>") : server
);
797 oprintf(_("Unattended mode detected. "
798 "Make sure credentials have been preset.\n"));
800 oprintf(_("You are going to insert credentials for your account.\n"
801 "Leave blank line to choose default value.\n"));
803 select_completion(COMPL_NONE
);
805 /* Ask for user name if not predefined */
806 if (NULL
== username
) {
807 shi_ask_for_string(&username
, _("Input ISDS user name: "),
808 cfg_getstr(configuration
, CONFIG_USERNAME
), batch_mode
);
811 /* Ask for password */
812 shi_ask_for_password(&password
, _("Input ISDS password: "),
813 cfg_getstr(configuration
, CONFIG_PASSWORD
), batch_mode
);
815 /* Ask for key password if PKI authentication requested */
816 if (NULL
!= pki_key_path
) {
817 shi_ask_for_password(&key_password
, _("Input private key password: "),
818 cfg_getstr(configuration
, CONFIG_KEY_PASSWORD
), batch_mode
);
821 /* Ask for OTP code if OTP authentication requested */
822 if (NULL
!= otp_method
) {
823 shi_ask_for_password(&otp_code
,
824 (otp
.method
== OTP_TIME
) ?
825 _("Input one-time code (empty to send new one): ") :
826 _("Input one-time code: "),
827 cfg_getstr(configuration
, CONFIG_OTP_CODE
), batch_mode
);
828 otp
.otp_code
= otp_code
;
831 select_completion(COMPL_COMMAND
);
834 /* Build PKI structure */
835 if (NULL
!= pki_certificate_path
|| NULL
!= pki_key_path
) {
836 pki
.engine
= pki_engine
;
837 if (NULL
== pki_certificate_format
) {
838 fprintf(stderr
, _("Error: No certficate format supplied\n"));
841 if (string2pki_format(pki_certificate_format
, &pki
.certificate_format
)) {
842 fprintf(stderr
, _("Error: Invalid certificate format `%s'\n"),
843 pki_certificate_format
);
846 if (NULL
== pki_key_format
) {
847 fprintf(stderr
, _("Error: No private key format supplied\n"));
850 if (string2pki_format(pki_key_format
, &pki
.key_format
)) {
851 fprintf(stderr
, _("Error: Invalid private key format `%s'\n"),
855 pki
.certificate
= pki_certificate_path
;
856 pki
.key
= pki_key_path
;
857 pki
.passphrase
= key_password
;
860 if (NULL
!= otp_method
&& OTP_TIME
== otp
.method
&& NULL
== otp_code
)
861 printf(_("Requesting one-time code from server for "
864 printf(_("Logging in...\n"));
865 err
= isds_login(cisds
, server
, username
, password
,
866 (NULL
!= pki_certificate_path
|| NULL
!= pki_key_path
) ?
868 (NULL
!= otp_method
) ? &otp
: NULL
);
869 if (NULL
!= otp_method
&& OTP_TIME
== otp
.method
&& NULL
== otp_code
)
870 finish_isds_operation_with_code(cisds
, err
, IE_PARTIAL_SUCCESS
);
872 finish_isds_operation(cisds
, err
);
874 if (IE_PARTIAL_SUCCESS
== err
) {
875 printf(_("OTP code has been sent by ISDS successfully.\n"
876 "Once you receive the code, retry log-in with "
880 printf(_("Log-in failed\n"));
884 oprintf(_("Logged in.\n"));
889 static struct isds_DbOwnerInfo
*do_box(void) {
891 struct isds_DbOwnerInfo
*box
= NULL
;
893 printf(_("Getting box details you are logged in...\n"));
894 err
= isds_GetOwnerInfoFromLogin(cisds
, &box
);
895 finish_isds_operation(cisds
, err
);
901 static int shi_box(int argc
, const char **argv
) {
902 struct isds_DbOwnerInfo
*box
= NULL
;
907 format_DbOwnerInfo(box
);
909 isds_DbOwnerInfo_free(&box
);
914 /* Get info about box with @id.
915 * @id is UTF-8 encoded
916 * Return NULL in case of error, otherwise box description that caller must
918 static struct isds_DbOwnerInfo
*stat_box(const char *id
) {
920 struct isds_DbOwnerInfo criteria
;
921 struct isds_list
*boxes
= NULL
, *item
;
922 struct isds_DbOwnerInfo
*box
= NULL
;
923 char *id_locale
= NULL
;
925 if (!id
|| !*id
) return NULL
;
927 id_locale
= utf82locale(id
);
928 memset(&criteria
, 0, sizeof(criteria
));
929 criteria
.dbID
= (char *) id
;
931 printf(_("Getting details about box with ID `%s'...\n"), id_locale
);
932 err
= isds_FindDataBox(cisds
, &criteria
, &boxes
);
933 finish_isds_operation(cisds
, err
);
936 for(item
= boxes
; item
; item
= item
->next
) {
937 if (!item
->data
) continue;
940 fprintf(stderr
, _("Error: More boxes match ID `%s'\n"), id_locale
);
944 box
= (struct isds_DbOwnerInfo
*) item
->data
;
951 isds_list_free(&boxes
);
957 /* Obtain box ID value either from locale-encoded argument or from current box
958 * the context is logged in.
959 * @arg is locale-encoded box ID or NULL
960 * @box_id outputs pointer to reallocated UTF-8 encoded box ID. Pass NULL if
961 * you are not interreted in it. Will be NULLed on error.
962 * @box_id_locale outputs pointer to reallocated locale-encoded box ID. Pass
963 * NULL if you are not interrested in it. Will be NULLed on error.
964 * @return 0 on sucess, -1 on error. */
965 static int get_current_box_id(const char *arg
,
966 char **box_id
, char **box_id_locale
) {
967 struct isds_DbOwnerInfo
*box
= NULL
;
969 if (NULL
!= box_id
) zfree(*box_id
);
970 if (NULL
!= box_id_locale
) zfree(*box_id_locale
);
972 if (NULL
== arg
|| '\0' == *arg
) {
973 /* Get current box ID */
975 if (NULL
== box
|| NULL
== box
->dbID
|| !*box
->dbID
) {
976 isds_DbOwnerInfo_free(&box
);
977 fprintf(stderr
, _("Could not get current box ID\n"));
980 if (NULL
!= box_id
) {
981 *box_id
= strdup(box
->dbID
);
982 if (NULL
== *box_id
) {
983 fprintf(stderr
, _("Not enough memory\n"));
987 if (NULL
!= box_id_locale
) {
988 *box_id_locale
= utf82locale(box
->dbID
);
989 if (NULL
== *box_id_locale
) {
990 fprintf(stderr
, _("Could not convert box ID to locale\n"));
995 /* Box ID supplied as argument */
996 if (NULL
!= box_id_locale
) {
997 *box_id_locale
= strdup(arg
);
998 if (NULL
== *box_id_locale
) {
999 fprintf(stderr
, _("Not enough memory\n"));
1003 if (NULL
!= box_id
) {
1004 *box_id
= locale2utf8(arg
);
1005 if (NULL
== *box_id
) {
1006 fprintf(stderr
, _("Could not convert box ID `%s' to UTF-8\n"),
1016 if (NULL
!= box_id
) zfree(*box_id
);
1017 if (NULL
!= box_id_locale
) zfree(*box_id_locale
);
1022 static void shi_commercialcredit_usage(const char *command
) {
1024 "Usage: %s [OPTION...] [BOX_ID]\n"
1025 "Retrieve details and history of a credit available for sending commercial\n"
1026 "messages from a box with ID BOX_ID. Default is box you are logged in.\n"
1027 "Options require a date argument:\n"
1028 " -f list history from date inclusive (locale or full ISO 8601 date)\n"
1029 " -t list history to date inclusive (locale or full ISO 8601 date)\n"
1034 /* Retrieve details about credit for sending commercial messages */
1035 static int shi_commercialcredit(int argc
, const char **argv
) {
1037 struct tm
*from_date
= NULL
, *to_date
= NULL
;
1038 long int current_credit
;
1039 char *notification_email
= NULL
;
1040 struct isds_list
*history
= NULL
, *item
;
1042 char *box_id
= NULL
, *box_id_locale
= NULL
;
1047 while ((opt
= getopt(argc
, (char * const *)argv
, "f:ht:")) != -1) {
1051 from_date
= datestring2tm(optarg
);
1052 if (NULL
== from_date
) {
1053 fprintf(stderr
, _("Error: Could not parse date: %s\n"),
1060 shi_commercialcredit_usage((argv
)?argv
[0]:NULL
);
1064 to_date
= datestring2tm(optarg
);
1065 if (NULL
== to_date
) {
1066 fprintf(stderr
, _("Error: Could not parse date: %s\n"),
1073 shi_commercialcredit_usage((argv
)?argv
[0]:NULL
);
1078 if (optind
+ 1 < argc
) {
1079 fprintf(stderr
, _("Bad invocation\n"));
1080 shi_commercialcredit_usage((argv
)?argv
[0]:NULL
);
1085 if ((retval
= get_current_box_id(argv
[optind
], &box_id
, &box_id_locale
))) {
1089 printf(_("Querying `%s' box credit details...\n"),
1091 err
= isds_get_commercial_credit(cisds
, box_id
, from_date
, to_date
,
1092 ¤t_credit
, ¬ification_email
, &history
);
1093 finish_isds_operation(cisds
, err
);
1096 oprintf(_("Details of credit for sending commercial messages "
1097 "from box `%s':\n"), box_id_locale
);
1098 print_header_currency(_("Current credit"), ¤t_credit
);
1099 print_header_utf8(_("Notification e-mail"), notification_email
);
1101 if (NULL
!= history
) {
1102 oprintf(_("History of credit change events:\n"));
1103 print_header_tm(_("From"), from_date
);
1104 print_header_tm(_("To"), to_date
);
1105 for (item
= history
, ordinar
= 0; item
; item
=item
->next
) {
1106 if (!item
->data
) continue;
1108 oprintf(_("\n* Event #%d:\n"), ordinar
);
1109 format_credit_event(item
->data
);
1112 oprintf(_("No event exists.\n"));
1115 oprintf(_("Could not get details about a credit available for sending "
1116 "commercial messages\n"
1117 "from box `%s'.\n"),
1123 isds_list_free(&history
);
1124 free(notification_email
);
1128 free(box_id_locale
);
1133 static void shi_commercialreceiving_usage(const char *command
) {
1135 "Usage: %s [-0|-1] [BOX_ID]\n"
1136 "Manipulate commercial receiving box status.\n"
1137 " -O switch off receiving of commercial messages\n"
1138 " -1 switch on receiving of commercial messages\n"
1139 " BOX_ID affects box with ID BOX_ID; default is box you are logged in\n"
1140 "If no option is given, show current commercial receiving status.\n"),
1145 /* Manipulate commercial receiving box status */
1146 static int shi_commercialreceiving(int argc
, const char **argv
) {
1150 char *box_id
= NULL
, *box_id_locale
= NULL
;
1154 while ((opt
= getopt(argc
, (char * const *)argv
, "01")) != -1) {
1163 shi_commercialreceiving_usage((argv
)?argv
[0]:NULL
);
1167 if (optind
+ 1 < argc
) {
1168 fprintf(stderr
, _("Bad invocation\n"));
1169 shi_commercialreceiving_usage((argv
)?argv
[0]:NULL
);
1173 if ((retval
= get_current_box_id(argv
[optind
], &box_id
, &box_id_locale
))) {
1178 struct isds_DbOwnerInfo
*box
= stat_box(box_id
);
1180 fprintf(stderr
, _("Could not get details about box ID `%s'\n"),
1186 oprintf(_("Commercial receiving status of box `%s': "), box_id_locale
);
1187 if (!box
->dbOpenAddressing
)
1188 oprintf(_("Unknown\n"));
1189 else if (*box
->dbOpenAddressing
)
1190 oprintf(_("Positive\n"));
1192 oprintf(_("Negative\n"));
1193 isds_DbOwnerInfo_free(&box
);
1195 char *refnumber
= NULL
;
1197 _("Switching `%s' box commercial receiving on...\n"):
1198 _("Switching `%s' box commercial receiving off...\n"),
1200 err
= isds_switch_commercial_receiving(cisds
, box_id
, action
,
1202 finish_isds_operation(cisds
, err
);
1205 char *refnumber_locale
= utf82locale(refnumber
);
1206 oprintf(_("Commercial receiving status successfully changed. "
1207 "Assigned reference number: %s\n"),
1209 free(refnumber_locale
);
1211 oprintf(_("Commercial receiving status has not been changed.\n"));
1219 free(box_id_locale
);
1224 static void shi_commercialsending_usage(const char *command
) {
1226 "Usage: %s [BOX_ID]\n"
1227 "Retrieve permissions to send commercial messages from a box.\n"
1228 " BOX_ID query permissions for box with ID BOX_ID; default is box you\n"
1229 " are logged in\n"),
1234 /* Retrieve permissions to send commercial messages */
1235 static int shi_commercialsending(int argc
, const char **argv
) {
1237 struct isds_DbOwnerInfo
*box
= NULL
;
1238 struct isds_list
*permissions
= NULL
, *item
;
1240 char *box_id
= NULL
, *box_id_locale
= NULL
;
1245 while ((opt
= getopt(argc
, (char * const *)argv
, "h")) != -1) {
1248 shi_commercialsending_usage((argv
)?argv
[0]:NULL
);
1251 shi_commercialsending_usage((argv
)?argv
[0]:NULL
);
1255 if (optind
+ 1 < argc
) {
1256 fprintf(stderr
, _("Bad invocation\n"));
1257 shi_commercialsending_usage((argv
)?argv
[0]:NULL
);
1261 if ((retval
= get_current_box_id(argv
[optind
], &box_id
, &box_id_locale
))) {
1265 printf(_("Querying `%s' box commercial sending permissions...\n"),
1267 err
= isds_get_commercial_permissions(cisds
, box_id
, &permissions
);
1268 finish_isds_operation(cisds
, err
);
1271 oprintf(_("Permissions to send commercial messages from box `%s':\n"),
1273 for (item
= permissions
, ordinar
= 0; item
; item
=item
->next
) {
1274 if (!item
->data
) continue;
1276 oprintf(_("\n* Permission #%d:\n"), ordinar
);
1277 format_commercial_permission(item
->data
);
1280 oprintf(_("No permission exists.\n"));
1282 oprintf(_("Could not list permissions to send commercial messages "
1283 "from box `%s'.\n"), box_id_locale
);
1287 isds_list_free(&permissions
);
1289 free(box_id_locale
);
1290 isds_DbOwnerInfo_free(&box
);
1295 static int shi_user(int argc
, const char **argv
) {
1297 struct isds_DbUserInfo
*user
= NULL
;
1299 printf(_("Getting user details you are logged as...\n"));
1300 err
= isds_GetUserInfoFromLogin(cisds
, &user
);
1301 finish_isds_operation(cisds
, err
);
1304 format_DbUserInfo(user
);
1306 isds_DbUserInfo_free(&user
);
1311 static void shi_users_usage(const char *command
) {
1313 "Usage: %s BOX_ID\n"
1314 "Get list of users having access to box with BOX_ID.\n"),
1319 static int shi_users(int argc
, const char **argv
) {
1321 struct isds_list
*users
= NULL
, *item
;
1324 if (!argv
|| !argv
[1] || !*argv
[1]) {
1325 shi_users_usage((argv
)?argv
[0]:NULL
);
1329 printf(_("Getting users of box with ID `%s'...\n"), argv
[1]);
1330 err
= isds_GetDataBoxUsers(cisds
, argv
[1], &users
);
1331 finish_isds_operation(cisds
, err
);
1334 for (item
= users
, ordinar
= 0; item
; item
=item
->next
) {
1335 if (!item
->data
) continue;
1337 oprintf(_("\n* User #%d:\n"), ordinar
);
1338 format_DbUserInfo(item
->data
);
1341 oprintf(_("Empty list of users returned.\n"));
1343 isds_list_free(&users
);
1348 static int show_password_expiration(void) {
1350 struct timeval
*expiration
= NULL
;
1352 err
= isds_get_password_expiration(cisds
, &expiration
);
1353 finish_isds_operation(cisds
, err
);
1355 fprintf(stderr
, "Could not get password expiration time\n");
1359 print_header_timeval(_("Your password expires at"), expiration
);
1365 /* Change password in ISDS */
1366 static int do_passwd(void) {
1367 char *old_password
= NULL
;
1368 char *new_password
= NULL
;
1369 char *new_password2
= NULL
;
1370 struct isds_otp otp
;
1371 char *refnumber
= NULL
;
1372 isds_error err
= IE_ERROR
;
1375 if (replace_string(&otp_method
, cfg_getstr(configuration
, CONFIG_OTP_METHOD
)))
1377 /* Build OTP structure */
1378 if (NULL
!= otp_method
) {
1379 if (string2otp_method(otp_method
, &otp
.method
)) {
1380 fprintf(stderr
, _("Error: Invalid one-time password "
1381 "authentication method `%s'\n"), otp_method
);
1386 select_completion(COMPL_NONE
);
1389 "You are going to change your password. If you don't want to change your\n"
1390 "password, insert empty string or EOF.\n"
1392 "You will be asked for your current (old) password and then for new password.\n"
1393 "ISDS forces some criteria new password must fulfill. Current rules are:\n"
1394 "\tLength: minimal 8, maximal 32 characters\n"
1395 "\tMust contain at least: 1 upper case letter, 1 lower case letter, 1 digit\n"
1396 "\tAllowed alphabet: [a-z][A-Z][0-9][!#$%%&()*+,-.:=?@[]_{}|~]\n"
1397 "\tMust differ from last 255 passwords\n"
1398 "\tMust not contain user ID\n"
1399 "\tMust not contain sequence of three or more same characters\n"
1400 "\tMust not start with `qwert', `asdgf', or `12345'\n"
1401 "Finally, you must repeat your new password to avoid mistakes.\n"
1402 "After password change will be confirmed, you must log in again as password\n"
1403 "is transmitted to server on each request.\n"
1406 old_password
= ask_for_password(_("Old password: "));
1407 if (!old_password
|| *old_password
== '\0') {
1408 fprintf(stderr
, _("No password supplied\n"));
1412 /* Ask for OTP code if OTP authentication requested */
1414 if (NULL
!= otp_method
) {
1415 shi_ask_for_password(&otp_code
,
1416 (otp
.method
== OTP_TIME
) ?
1417 _("One-time code (empty to send new one): ") :
1418 _("One-time code: "),
1419 cfg_getstr(configuration
, CONFIG_OTP_CODE
), batch_mode
);
1420 otp
.otp_code
= otp_code
;
1424 if (NULL
== otp_method
|| NULL
!= otp_code
) {
1425 new_password
= ask_for_password(_("New password: "));
1426 if (!new_password
|| *new_password
== '\0') {
1427 fprintf(stderr
, _("No password supplied\n"));
1431 new_password2
= ask_for_password(_("Repeat new password: "));
1432 if (!new_password2
|| new_password2
== '\0') {
1433 fprintf(stderr
, _("No password supplied\n"));
1437 if (strcmp(new_password
, new_password2
)) {
1438 fprintf(stderr
, _("New passwords differ\n"));
1443 if (NULL
!= otp_method
&& OTP_TIME
== otp
.method
&& NULL
== otp_code
)
1444 printf(_("Requesting one-time code from server for "
1445 "a password change...\n"));
1447 printf(_("Changing password...\n"));
1448 err
= isds_change_password(cisds
, old_password
, new_password
,
1449 (NULL
!= otp_method
) ? &otp
: NULL
, &refnumber
);
1450 if (NULL
!= otp_method
&& OTP_TIME
== otp
.method
&& NULL
== otp_code
)
1451 finish_isds_operation_with_code(cisds
, err
, IE_PARTIAL_SUCCESS
);
1453 finish_isds_operation(cisds
, err
);
1455 if (NULL
!= refnumber
) {
1456 char *refnumber_locale
= utf82locale(refnumber
);
1458 oprintf(_("Assigned reference number: %s\n"), refnumber_locale
);
1459 free(refnumber_locale
);
1461 if (IE_PARTIAL_SUCCESS
== err
) {
1462 oprintf(_("OTP code has been sent by ISDS successfully.\n"
1463 "Once you receive the code, retry changing password with "
1467 printf(_("Password change failed\n"));
1470 oprintf(_("Password HAS been successfully changed.\n"));
1476 oprintf(_("Password has NOT been changed!\n"));
1481 free(new_password2
);
1485 select_completion(COMPL_COMMAND
);
1488 "Remember, ISDS password has limited life time.\n"));
1493 static void shi_passwd_usage(const char *command
) {
1496 "Manipulate user password or change it if no option given.\n"
1499 " -S show password expiration time\n"
1504 static int shi_passwd(int argc
, const char **argv
) {
1508 while ((opt
= getopt(argc
, (char * const *)argv
, "S")) != -1) {
1511 return show_password_expiration();
1513 shi_passwd_usage(argv
[0]);
1517 if (optind
!= argc
|| argc
> 1) {
1518 fprintf(stderr
, _("Bad invocation\n"));
1519 shi_passwd_usage((argv
)?argv
[0]:NULL
);
1527 static void shi_login_usage(const char *command
) {
1529 "Usage: %s [OPTIONS] [USER_NAME]\n"
1530 "Attemp to log into ISDS server.\n"
1533 " -b URL ISDS server base URL\n"
1534 " -c IDENTIFIER user certificate\n"
1535 " -C FORMAT user certificate format\n"
1536 " -k IDENTIFIER user private key\n"
1537 " -K FORMAT user private key format\n"
1538 " -e IDENTIFIER cryptographic engine\n"
1539 " -o METHOD use one-time password authentication method\n"
1541 "Recognized certificate and key FORMATS are:\n"
1542 " PEM Base64 encoded serialization in local file\n"
1543 " DER binary serialization in local file\n"
1544 " ENG material is stored in cryptographic engine\n"
1545 "Identifiers of cryptographic engine, certificate, and private key are\n"
1546 "specific for underlying cryptographic library.\n"
1548 "Recognized one-time password methods are:\n"
1549 " HOTP HMAC-based OTP method\n"
1550 " TOTP time-based OTP method\n"
1552 "Values of omitted options are taken from configuration file.\n"
1557 static int shi_login(int argc
, const char **argv
) {
1561 discard_credentials();
1563 /* Load stored configuration */
1564 if (replace_string(&server
, cfg_getstr(configuration
, CONFIG_SERVER
)))
1566 if (replace_string(&otp_method
, cfg_getstr(configuration
, CONFIG_OTP_METHOD
)))
1568 if (replace_string(&pki_engine
,
1569 cfg_getstr(configuration
, CONFIG_KEY_ENGINE
)))
1571 if (replace_string(&pki_certificate_path
,
1572 cfg_getstr(configuration
, CONFIG_CERT_PATH
)))
1574 if (replace_string(&pki_certificate_format
,
1575 cfg_getstr(configuration
, CONFIG_CERT_FORMAT
)))
1577 if (replace_string(&pki_key_path
,
1578 cfg_getstr(configuration
, CONFIG_KEY_PATH
)))
1580 if (replace_string(&pki_key_format
,
1581 cfg_getstr(configuration
, CONFIG_KEY_FORMAT
)))
1584 /* Override configuration with positional arguments */
1586 while ((opt
= getopt(argc
, (char * const *)argv
, "b:c:C:k:K:e:o:")) != -1) {
1589 if (replace_string(&server
, optarg
)) return -1;
1592 if (replace_string(&pki_certificate_path
, optarg
)) return -1;
1595 if (replace_string(&pki_certificate_format
, optarg
)) return -1;
1598 if (replace_string(&pki_key_path
, optarg
)) return -1;
1601 if (replace_string(&pki_key_format
, optarg
)) return -1;
1604 if (replace_string(&pki_engine
, optarg
)) return -1;
1607 if (replace_string(&otp_method
, optarg
)) return -1;
1610 shi_login_usage(argv
[0]);
1614 if (optind
< argc
- 1) {
1615 fprintf(stderr
, _("Bad invocation\n"));
1616 shi_login_usage(argv
[0]);
1619 if (optind
== argc
- 1) {
1620 username
= strdup(argv
[optind
]);
1623 /* Proceed log-in */
1624 status
= do_login();
1625 if (status
< 0) return -1;
1627 /* If log-in passed, store configuration */
1628 cfg_setstr(configuration
, CONFIG_SERVER
, server
);
1629 cfg_setstr(configuration
, CONFIG_USERNAME
, username
);
1630 cfg_setstr(configuration
, CONFIG_PASSWORD
, password
);
1631 cfg_setstr(configuration
, CONFIG_KEY_PASSWORD
, key_password
);
1632 cfg_setstr(configuration
, CONFIG_OTP_METHOD
, otp_method
);
1633 cfg_setstr(configuration
, CONFIG_KEY_ENGINE
, pki_engine
);
1634 cfg_setstr(configuration
, CONFIG_CERT_PATH
, pki_certificate_path
);
1635 cfg_setstr(configuration
, CONFIG_CERT_FORMAT
, pki_certificate_format
);
1636 cfg_setstr(configuration
, CONFIG_KEY_PATH
, pki_key_path
);
1637 cfg_setstr(configuration
, CONFIG_KEY_FORMAT
, pki_key_format
);
1639 /* Get some details only if fully logged in */
1641 show_password_expiration();
1647 static void shi_debug_usage(const char *command
) {
1649 "Usage: %s -l LEVEL [-f FACILITY...] [{-e | -o FILE}]\n"
1650 "Debug FACILITIES on LEVEL.\n"
1652 "-l LEVEL set log level, valid interval <%d,%d>, default is %d\n"
1653 " %d is no logging, %d critical, %d errors,\n"
1654 " %d warnings, %d info, %d debug, %d all\n"
1655 "-f FACILITY debug only given facility, repeat this option to debug\n"
1656 " more facilities; valid values: none, http, soap, isds,\n"
1657 " file, sec, xml, all; default is none\n"
1658 "-e write debug log into stderr\n"
1659 "-o FILE append debug log to FILE\n"
1662 ILL_NONE
, ILL_ALL
, ILL_NONE
,
1663 ILL_NONE
, ILL_CRIT
, ILL_ERR
, ILL_WARNING
,
1664 ILL_INFO
, ILL_DEBUG
, ILL_ALL
);
1667 static int shi_debug(int argc
, const char **argv
) {
1669 int log_level
= ILL_NONE
;
1670 isds_log_facility log_facility
= ILF_NONE
;
1672 _Bool close_log
= 0;
1675 while ((opt
= getopt(argc
, (char * const *)argv
, "l:f:eo:")) != -1) {
1678 log_level
= normalize_log_level(atoi(optarg
));
1681 if (add_log_facility(&log_facility
, optarg
)) return -1;
1689 shi_debug_usage(argv
[0]);
1693 if (optind
== 1 || optind
!= argc
) {
1694 fprintf(stderr
, _("Bad invocation\n"));
1695 shi_debug_usage(argv
[0]);
1701 isds_set_log_callback(NULL
, NULL
);
1703 if (logger_fd
!= -1) {
1704 if (-1 == close(logger_fd
)) {
1705 fprintf(stderr
, _("Closing log file failed: %s\n"),
1710 cfg_setstr(configuration
, CONFIG_LOGFILE
, NULL
);
1712 if (do_log_to_file(file
))
1714 if (file
) cfg_setstr(configuration
, CONFIG_LOGFILE
, file
);
1716 /* Set log levels */
1717 isds_set_logging(log_facility
, log_level
);
1718 cfg_setint(configuration
, CONFIG_LOGLEVEL
, log_level
);
1719 save_log_facility(log_facility
);
1725 static void show_setting_str(const char *variable
, _Bool cenzore
) {
1726 if (!variable
) return;
1728 const char *value
= cfg_getstr(configuration
, variable
);
1732 oprintf(_("%s = <set>\n"), variable
);
1734 oprintf(_("%s = `%s'\n"), variable
, value
);
1736 oprintf(_("%s = <unset>\n"), variable
);
1741 static void show_setting_boolean(const char *variable
) {
1742 if (!variable
) return;
1744 _Bool value
= cfg_getbool(configuration
, variable
);
1747 oprintf(_("%s = <true>\n"), variable
);
1749 oprintf(_("%s = <false>\n"), variable
);
1754 static void show_setting_int(const char *variable
) {
1755 if (!variable
) return;
1757 long int value
= cfg_getint(configuration
, variable
);
1759 oprintf(_("%s = %ld\n"), variable
, value
);
1763 static void show_setting_strlist(const char *variable
) {
1764 if (!variable
) return;
1766 int length
= cfg_size(configuration
, variable
);
1769 oprintf(_("%s = <unset>\n"), variable
);
1771 oprintf(_("%s = {"), variable
);
1772 for (int i
= 0; i
< length
; i
++) {
1773 const char *value
= cfg_getnstr(configuration
, variable
, i
);
1775 oprintf(_("`%s', "), value
);
1777 oprintf(_("`%s'}\n"), value
);
1783 static int shi_settings(int argc
, const char **argv
) {
1785 int log_level = ILL_NONE;
1786 isds_log_facility log_facility = ILF_NONE;
1788 _Bool close_log = 0;*/
1792 while ((opt = getopt(argc, (char * const *)argv, "l:f:eo:")) != -1) {
1795 log_level = normalize_log_level(atoi(optarg));
1798 if (add_log_facility(&log_facility, optarg)) return -1;
1806 shi_debug_usage(argv[0]);
1810 if (optind == 1 || optind != argc) {
1811 printf(_("Bad invocation\n"));
1812 shi_debug_usage(argv[0]);
1817 oprintf(_("Current settings:\n"));
1819 show_setting_str(CONFIG_SERVER
, 0);
1820 show_setting_str(CONFIG_USERNAME
, 0);
1821 show_setting_str(CONFIG_PASSWORD
, 1),
1822 show_setting_str(CONFIG_CERT_FORMAT
, 0),
1823 show_setting_str(CONFIG_CERT_PATH
, 0),
1824 show_setting_str(CONFIG_KEY_ENGINE
, 0),
1825 show_setting_str(CONFIG_KEY_FORMAT
, 0),
1826 show_setting_str(CONFIG_KEY_PATH
, 0),
1827 show_setting_str(CONFIG_KEY_PASSWORD
, 1),
1828 show_setting_str(CONFIG_OTP_METHOD
, 0),
1829 show_setting_str(CONFIG_OTP_CODE
, 1),
1830 show_setting_boolean(CONFIG_VERIFYSERVER
);
1831 show_setting_str(CONFIG_CAFILE
, 0);
1832 show_setting_str(CONFIG_CADIRECTORY
, 0);
1833 show_setting_boolean(CONFIG_CLEAN_TEMPORARY_FILES
);
1834 show_setting_str(CONFIG_CRLFILE
, 0);
1835 show_setting_int(CONFIG_TIMEOUT
);
1836 show_setting_strlist(CONFIG_LOGFACILITIES
);
1837 show_setting_str(CONFIG_LOGFILE
, 0);
1838 show_setting_int(CONFIG_LOGLEVEL
);
1839 show_setting_boolean(CONFIG_CONFIRM_SEND
);
1840 show_setting_boolean(CONFIG_MARKMESSAGEREAD
);
1841 show_setting_boolean(CONFIG_NORMALIZEMIMETYPE
);
1842 show_setting_strlist(CONFIG_OPENCOMMAND
);
1843 show_setting_boolean(CONFIG_OVERWRITEFILES
);
1849 static void shi_find_box_usage(const char *command
) {
1851 "Usage: %s {OPTION... | BOX_ID}\n"
1852 "Get information about box with BOX_ID or boxes meeting other criteria.\n"
1853 "Each search option requires an argument:\n"
1854 " -t box type; accepted values:\n"
1855 " FO Private individual\n"
1856 " PFO Self-employed individual\n"
1857 " PFO_ADVOK Lawyer\n"
1858 " PFO_DANPOR Tax advisor\n"
1859 " PFO_INSSPR Insolvency administrator\n"
1860 " PO Organisation\n"
1861 " PO_ZAK Organization based by law\n"
1862 " PO_REQ Organization based on request\n"
1863 " OVM Public authority\n"
1864 " OVM_NOTAR Notary\n"
1865 " OVM_EXEKUT Executor\n"
1866 " OVM_REQ Public authority based on request\n"
1867 " -j identity number\n"
1869 "Person name options:\n"
1873 " -b last name at birth\n"
1874 " -s subject name\n"
1877 " -d birth date (locale or full ISO 8601 date)\n"
1879 " -y birth county\n"
1885 " -z number in street\n"
1886 " -Z number in municipality\n"
1893 " -p phone number\n"
1895 " -r registry code\n"
1896 " -a box status; accepted values:\n"
1897 " ACCESSIBLE Accessible\n"
1898 " TEMP_INACCESSIBLE Temporary inaccessible\n"
1899 " NOT_YET_ACCESSIBLE Not yet accessible\n"
1900 " PERM_INACCESSIBLE Permanently inaccessible\n"
1901 " REMOVED Deleted\n"
1902 " -o act as public authority; boolean values: 0 is false, 1 is true\n"
1903 " -k receive commercial messages; boolean values\n"
1905 "Not all option combinations are meaningful or allowed. For example box\n"
1906 "type is always required (except direct box ID query).\n"
1907 "ISDS can refuse to answer to much broad query. Not all boxes are searchable\n"
1914 /* Allow reassignment */
1915 #define FILL_OR_LEAVE(variable, locale) { \
1917 (variable) = locale2utf8(locale); \
1918 if (!(variable)) { \
1919 fprintf(stderr, _("Error: Not enough memory\n")); \
1925 #define CALLOC_OR_LEAVE(structure) { \
1926 if (!(structure)) { \
1927 (structure) = calloc(1, sizeof(*(structure))); \
1928 if (!(structure)) { \
1929 fprintf(stderr, _("Error: Not enough memory\n")); \
1936 #define FILL_BOOLEAN_OR_LEAVE(variable, locale) { \
1938 (variable) = malloc(sizeof(*(variable))); \
1939 if (!(variable)) { \
1940 fprintf(stderr, _("Error: Not enough memory\n")); \
1944 if (!strcmp((locale), "0")) *(variable) = 0; \
1945 else if (!strcmp((locale), "1")) *(variable) = 1; \
1947 fprintf(stderr, _("%s: %s: Unknown boolean value\n"), \
1948 argv[0], (locale)); \
1954 #define FILL_LONGINT_OR_LEAVE(variable, locale) { \
1955 if (!(locale) || !*(locale)) { \
1956 fprintf(stderr, _("%s: Empty integer value\n"), argv[0]); \
1962 (variable) = malloc(sizeof(*(variable))); \
1963 if (!(variable)) { \
1964 fprintf(stderr, _("Error: Not enough memory\n")); \
1968 (*variable) = strtol((locale), &endptr, 0); \
1970 fprintf(stderr, _("%s: %s: Invalid integer value\n"), \
1971 argv[0], (locale)); \
1977 static int shi_find_box(int argc
, const char **argv
) {
1980 struct isds_DbOwnerInfo
*criteria
= NULL
;
1981 struct isds_list
*item
;
1985 if (!argv
|| !argv
[1] || !*argv
[1]) {
1986 fprintf(stderr
, _("Error: No argument supplied\n"));
1987 shi_find_box_usage((argv
)?argv
[0]:NULL
);
1991 criteria
= calloc(1, sizeof(*criteria
));
1993 fprintf(stderr
, _("Error: Not enough memory\n"));
2000 while ((opt
= getopt(argc
, (char * const *)argv
, "t:j:s:"
2001 "f:m:l:b:s:" "d:w:y:c:" "W:S:z:Z:P:C:"
2002 "n:e:p:i:r:a:o:k:")) != -1) {
2005 criteria
->dbType
= malloc(sizeof(*criteria
->dbType
));
2006 if (!criteria
->dbType
) {
2007 fprintf(stderr
, _("Error: Not enough memory\n"));
2011 if (!strcmp(optarg
, "FO"))
2012 *criteria
->dbType
= DBTYPE_FO
;
2013 else if (!strcmp(optarg
, "PFO"))
2014 *criteria
->dbType
= DBTYPE_PFO
;
2015 else if (!strcmp(optarg
, "PFO_ADVOK"))
2016 *criteria
->dbType
= DBTYPE_PFO_ADVOK
;
2017 else if (!strcmp(optarg
, "PFO_DANPOR"))
2018 *criteria
->dbType
= DBTYPE_PFO_DANPOR
;
2019 else if (!strcmp(optarg
, "PFO_INSSPR"))
2020 *criteria
->dbType
= DBTYPE_PFO_INSSPR
;
2021 else if (!strcmp(optarg
, "PO"))
2022 *criteria
->dbType
= DBTYPE_PO
;
2023 else if (!strcmp(optarg
, "PO_ZAK"))
2024 *criteria
->dbType
= DBTYPE_PO_ZAK
;
2025 else if (!strcmp(optarg
, "PO_REQ"))
2026 *criteria
->dbType
= DBTYPE_PO_REQ
;
2027 else if (!strcmp(optarg
, "OVM"))
2028 *criteria
->dbType
= DBTYPE_OVM
;
2029 else if (!strcmp(optarg
, "OVM_NOTAR"))
2030 *criteria
->dbType
= DBTYPE_OVM_NOTAR
;
2031 else if (!strcmp(optarg
, "OVM_EXEKUT"))
2032 *criteria
->dbType
= DBTYPE_OVM_EXEKUT
;
2033 else if (!strcmp(optarg
, "OVM_REQ"))
2034 *criteria
->dbType
= DBTYPE_OVM_REQ
;
2036 fprintf(stderr
, _("%s: %s: Unknown box type\n"),
2044 FILL_OR_LEAVE(criteria
->ic
, optarg
);
2049 CALLOC_OR_LEAVE(criteria
->personName
);
2050 FILL_OR_LEAVE(criteria
->personName
->pnFirstName
, optarg
);
2053 CALLOC_OR_LEAVE(criteria
->personName
);
2054 FILL_OR_LEAVE(criteria
->personName
->pnMiddleName
, optarg
);
2057 CALLOC_OR_LEAVE(criteria
->personName
);
2058 FILL_OR_LEAVE(criteria
->personName
->pnLastName
, optarg
);
2061 CALLOC_OR_LEAVE(criteria
->personName
);
2062 FILL_OR_LEAVE(criteria
->personName
->pnLastNameAtBirth
, optarg
);
2065 FILL_OR_LEAVE(criteria
->firmName
, optarg
);
2070 CALLOC_OR_LEAVE(criteria
->birthInfo
);
2071 criteria
->birthInfo
->biDate
= datestring2tm(optarg
);
2072 if (!criteria
->birthInfo
->biDate
) {
2073 fprintf(stderr
, _("Error: Could not parse date: %s\n"),
2080 CALLOC_OR_LEAVE(criteria
->birthInfo
);
2081 FILL_OR_LEAVE(criteria
->birthInfo
->biCity
, optarg
);
2084 CALLOC_OR_LEAVE(criteria
->birthInfo
);
2085 FILL_OR_LEAVE(criteria
->birthInfo
->biCounty
, optarg
);
2088 CALLOC_OR_LEAVE(criteria
->birthInfo
);
2089 FILL_OR_LEAVE(criteria
->birthInfo
->biState
, optarg
);
2094 CALLOC_OR_LEAVE(criteria
->address
);
2095 FILL_OR_LEAVE(criteria
->address
->adCity
, optarg
);
2098 CALLOC_OR_LEAVE(criteria
->address
);
2099 FILL_OR_LEAVE(criteria
->address
->adStreet
, optarg
);
2102 CALLOC_OR_LEAVE(criteria
->address
);
2103 FILL_OR_LEAVE(criteria
->address
->adNumberInStreet
, optarg
);
2106 CALLOC_OR_LEAVE(criteria
->address
);
2107 FILL_OR_LEAVE(criteria
->address
->adNumberInMunicipality
,
2111 CALLOC_OR_LEAVE(criteria
->address
);
2112 FILL_OR_LEAVE(criteria
->address
->adZipCode
, optarg
);
2115 CALLOC_OR_LEAVE(criteria
->address
);
2116 FILL_OR_LEAVE(criteria
->address
->adState
, optarg
);
2121 FILL_OR_LEAVE(criteria
->nationality
, optarg
);
2124 FILL_OR_LEAVE(criteria
->email
, optarg
);
2127 FILL_OR_LEAVE(criteria
->telNumber
, optarg
);
2130 FILL_OR_LEAVE(criteria
->identifier
, optarg
);
2133 FILL_OR_LEAVE(criteria
->registryCode
, optarg
);
2136 criteria
->dbState
= malloc(sizeof(*criteria
->dbState
));
2137 if (!criteria
->dbState
) {
2138 fprintf(stderr
, _("Error: Not enough memory\n"));
2142 if (!strcmp(optarg
, "ACCESSIBLE"))
2143 *criteria
->dbState
= DBSTATE_ACCESSIBLE
;
2144 else if (!strcmp(optarg
, "TEMP_INACCESSIBLE"))
2145 *criteria
->dbState
= DBSTATE_TEMP_UNACCESSIBLE
;
2146 else if (!strcmp(optarg
, "NOT_YET_ACCESSIBLE"))
2147 *criteria
->dbState
= DBSTATE_NOT_YET_ACCESSIBLE
;
2148 else if (!strcmp(optarg
, "PERM_INACCESSIBLE"))
2149 *criteria
->dbState
= DBSTATE_PERM_UNACCESSIBLE
;
2150 else if (!strcmp(optarg
, "REMOVED"))
2151 *criteria
->dbState
= DBSTATE_REMOVED
;
2153 fprintf(stderr
, _("%s: %s: Unknown box status\n"),
2160 FILL_BOOLEAN_OR_LEAVE(criteria
->dbEffectiveOVM
, optarg
);
2163 FILL_BOOLEAN_OR_LEAVE(criteria
->dbOpenAddressing
, optarg
);
2167 shi_find_box_usage(argv
[0]);
2173 /* There must be an option and all of them must be recognized, if not only
2174 * BOX_ID supplied */
2175 if (argc
> 2 && optind
!= argc
) {
2176 fprintf(stderr
, _("Error: Superfluous argument\n"));
2177 shi_find_box_usage(argv
[0]);
2182 /* If only box ID is supplied use it */
2183 if (argc
== 2 && argv
[1] && *argv
[1]) {
2184 criteria
->dbID
= locale2utf8(argv
[1]);
2185 if (!criteria
->dbID
) {
2186 fprintf(stderr
, _("Error: Not enough memory\n"));
2192 printf(_("Searching boxes...\n"));
2193 err
= isds_FindDataBox(cisds
, criteria
, &boxes
);
2194 finish_isds_operation(cisds
, err
);
2197 for(item
= boxes
; item
; item
= item
->next
) {
2198 if (!item
->data
) continue;
2201 oprintf(_("\n* Result #%d:\n"), order
);
2202 format_DbOwnerInfo(item
->data
);
2206 isds_DbOwnerInfo_free(&criteria
);
2211 static void shi_stat_box_usage(const char *command
) {
2213 "Usage: %s BOX_ID...\n"
2214 "Get status of box with BOX_ID. More boxes can be specified.\n"),
2219 /* Get boxes status */
2220 static int shi_stat_box(int argc
, const char **argv
) {
2225 if (!argv
|| !*argv
|| argc
< 2 || !argv
[1]) {
2226 fprintf(stderr
, _("Missing box ID\n"));
2227 shi_stat_box_usage((argv
[0])?argv
[0]:NULL
);
2231 for (int i
= 1; i
< argc
; i
++) {
2232 if (!argv
[i
] || !*argv
[i
]) continue;
2235 id
= locale2utf8(argv
[i
]);
2237 fprintf(stderr
, _("%s: Could not covert box ID to UTF-8\n"),
2242 printf(_("Getting status of box `%s'...\n"), argv
[i
]);
2243 err
= isds_CheckDataBox(cisds
, id
, &status
);
2244 finish_isds_operation(cisds
, err
);
2247 oprintf(_("Status of box `%s': %s\n"),
2248 argv
[i
], DbState2string(&status
));
2255 static void shi_boxlist_usage(const char *command
) {
2257 "Usage: %s LIST_TYPE FILE\n"
2258 "Save latest snapshot of list of boxes of type LIST_TYPE into FILE.\n"
2260 "Currently recognized LIST_TYPES are:\n"
2262 " UPG Effectively OVM boxes\n"
2263 " OVM OVM gross type boxes\n"
2264 " OPN Boxes allowing receiving commercial messages\n"
2266 "Not all types are available to all users. E.g. only `UPG' is available\n"
2267 "to regular users.\n"
2269 "The format of the list is comma separate list that is packed into\n"
2270 "ZIP archive. Name of the list file denotes time of snapshoting\n"
2271 "the list. The snapshot is created by ISDS once a day.\n"),
2276 /* Download list of boxes */
2277 static int shi_boxlist(int argc
, const char **argv
) {
2279 const char *type_locale
;
2281 void *buffer
= NULL
;
2282 size_t buffer_length
;
2285 if (!argv
|| !*argv
|| argc
< 3 || !argv
[1] || !argv
[2]) {
2286 fprintf(stderr
, _("Bad number of arguments\n"));
2287 shi_boxlist_usage((argv
)?argv
[0]:NULL
);
2290 type_locale
= argv
[1];
2292 type
= locale2utf8(type_locale
);
2294 fprintf(stderr
, _("%s: Could not covert list type to UTF-8\n"),
2299 printf(_("Getting `%s' list of boxes...\n"), type_locale
);
2300 err
= isds_get_box_list_archive(cisds
, type
, &buffer
, &buffer_length
);
2301 finish_isds_operation(cisds
, err
);
2307 retval
= save_data_to_file(argv
[2], -1, buffer
, buffer_length
,
2309 cfg_getbool(configuration
, CONFIG_OVERWRITEFILES
));
2316 static void shi_delivery_usage(const char *command
) {
2318 "Usage: %s [MESSAGE_ID]\n"
2319 "Get delivery data about a message.\n"
2320 "If MESSAGE_ID is defined, query for that message.\n"
2321 "Otherwise use current message.\n"),
2326 static int shi_delivery(int argc
, const char **argv
) {
2328 const char *id
= NULL
;
2329 struct isds_message
*delivery_info
= NULL
;
2331 if (!argv
|| argc
> 2) {
2332 shi_delivery_usage((argv
)?argv
[0]:NULL
);
2335 if (argc
== 2 && argv
[1] && *argv
[1]) {
2339 fprintf(stderr
, _("No message loaded\n"));
2342 if (!message
->envelope
|| !message
->envelope
->dmID
) {
2343 fprintf(stderr
, _("Current message is missing ID\n"));
2346 id
= message
->envelope
->dmID
;
2349 printf(_("Getting delivery info...\n"));
2350 err
= isds_get_signed_delivery_info(cisds
, id
, &delivery_info
);
2351 finish_isds_operation(cisds
, err
);
2355 isds_message_free(&message
);
2356 message
= delivery_info
;
2358 format_message(message
);
2360 if (message
->envelope
&& message
->envelope
->dmID
)
2361 set_prompt(_("%s %s"), argv
[0], message
->envelope
->dmID
);
2363 set_prompt("%s", argv
[0]);
2364 select_completion(COMPL_MSG
);
2369 static int shi_dump_message(int argc
, const char **argv
) {
2371 fprintf(stderr
, _("No message loaded\n"));
2375 print_message(message
);
2380 static void shi_hash_usage(const char *command
) {
2382 "Usage: %s [MESSAGE_ID]\n"
2383 "Retrieve message hash stored in ISDS.\n"
2384 "If MESSAGE_ID is defined, query for that message.\n"
2385 "Otherwise use current message.\n"),
2390 static int shi_hash(int argc
, const char **argv
) {
2392 const char *id
= NULL
;
2393 struct isds_hash
*hash
= NULL
;
2394 char *hash_string
= NULL
;
2396 if (!argv
|| argc
> 2) {
2397 shi_hash_usage((argv
)?argv
[0]:NULL
);
2400 if (argc
== 2 && argv
[1] && *argv
[1])
2404 fprintf(stderr
, _("No message loaded\n"));
2407 if (!message
->envelope
|| !message
->envelope
->dmID
) {
2408 fprintf(stderr
, _("Current message is missing ID\n"));
2411 id
= message
->envelope
->dmID
;
2414 printf(_("Getting message hash...\n"));
2415 err
= isds_download_message_hash(cisds
, id
, &hash
);
2416 finish_isds_operation(cisds
, err
);
2419 hash_string
= hash2string(hash
);
2420 oprintf(_("ISDS states message with `%s' ID has following hash:\n%s\n"),
2424 isds_hash_free(&hash
);
2429 static int shi_verify(int argc
, const char **argv
) {
2432 struct isds_hash
*retrieved_hash
= NULL
, *stored_hash
= NULL
;
2433 char *hash_string
= NULL
;
2437 fprintf(stderr
, _("No message loaded\n"));
2441 if (!message
->envelope
) {
2442 fprintf(stderr
, _("Current message is missing envelope\n"));
2445 stored_hash
= message
->envelope
->hash
;
2446 message
->envelope
->hash
= NULL
;
2448 if (message
->envelope
->dmID
) {
2449 /* Verify remote hash */
2450 oprintf(_("Remote hash check:\n"));
2452 printf(_("Getting message hash...\n"));
2453 err
= isds_download_message_hash(cisds
, message
->envelope
->dmID
,
2455 finish_isds_operation(cisds
, err
);
2457 if (retrieved_hash
) {
2458 hash_string
= hash2string(retrieved_hash
);
2459 ohprint(_("Retrieved:"), width
);
2460 oprintf("%s\n", hash_string
);
2464 if (retrieved_hash
&& message
->raw
) {
2465 err
= isds_compute_message_hash(cisds
, message
,
2466 retrieved_hash
->algorithm
);
2467 finish_isds_operation(cisds
, err
);
2470 hash_string
= hash2string(message
->envelope
->hash
);
2471 ohprint(_("Computed:"), width
);
2472 oprintf("%s\n", hash_string
);
2477 err
= isds_hash_cmp(retrieved_hash
, message
->envelope
->hash
);
2480 oprintf(_("Hashes match.\n")); break;
2482 oprintf(_("Hashes do not match.\n"));
2486 oprintf(_("Hashes could not be compared.\n"));
2491 free(retrieved_hash
);
2496 /* Verify stored hash */
2497 oprintf(_("Stored hash check:\n"));
2499 hash_string
= hash2string(stored_hash
);
2500 ohprint(_("Stored:"), width
);
2501 oprintf("%s\n", hash_string
);
2505 err
= isds_compute_message_hash(cisds
, message
,
2506 stored_hash
->algorithm
);
2507 finish_isds_operation(cisds
, err
);
2510 hash_string
= hash2string(message
->envelope
->hash
);
2511 ohprint(_("Computed:"), width
);
2512 oprintf("%s\n", hash_string
);
2517 err
= isds_hash_cmp(stored_hash
, message
->envelope
->hash
);
2520 oprintf(_("Hashes match.\n")); break;
2522 oprintf(_("Hashes do not match.\n"));
2526 oprintf(_("Hashes could not be compared.\n"));
2531 isds_hash_free(&message
->envelope
->hash
);
2534 message
->envelope
->hash
= stored_hash
;
2539 static int shi_authenticate(int argc
, const char **argv
) {
2544 fprintf(stderr
, _("No message loaded\n"));
2547 if (!message
->raw
|| message
->raw_length
== 0) {
2548 fprintf(stderr
, _("Current message is missing raw representation\n"));
2552 printf(_("Submitting message to authenticity check...\n"));
2553 err
= isds_authenticate_message(cisds
, message
->raw
, message
->raw_length
);
2554 finish_isds_operation(cisds
, (err
== IE_NOTUNIQ
) ? IE_SUCCESS
: err
);
2558 oprintf(_("Message originates in ISDS.\n")); break;
2560 oprintf(_("Message is unknown to ISDS or has been tampered.\n"));
2572 static void shi_accept_message_usage(const char *command
) {
2574 "Usage: %s [MESSAGE_ID...]\n"
2575 "Accept commercial message moving its state to received.\n"
2576 "If MESSAGE_ID is defined, accept that message. More messages can be specified.\n"
2577 "Otherwise accept all commercial incoming messages.\n"),
2582 static int shi_accept_message(int argc
, const char **argv
) {
2586 /* Process messages named in argv */
2587 for (int i
= 1; i
< argc
; i
++) {
2588 if (!argv
[i
] || !*argv
[i
]) continue;
2590 id
= locale2utf8(argv
[i
]);
2593 _("Error: Could not convert message ID to UTF-8: %s\n"),
2598 printf(_("Accepting message `%s'...\n"), argv
[i
]);
2599 err
= isds_mark_message_received(cisds
, id
);
2600 finish_isds_operation(cisds
, err
);
2606 oprintf(_("Message `%s' accepted\n"), argv
[i
]);
2611 /* TODO: list commercial not received messages and accept all of them
2614 _("Error: No message ID supplied. Accepting all commercial "
2615 "messages not implemented yet.\n"));
2623 static void shi_delete_message_usage(const char *command
) {
2625 "Usage: %s {-i|-o} MESSAGE_ID...\n"
2626 "Remove message from long term storage.\n"
2628 " -i Messages are incoming\n"
2629 " -o Messages are outoging\n"),
2634 static int shi_delete_message(int argc
, const char **argv
) {
2638 _Bool direction_specified
= 0;
2642 while ((opt
= getopt(argc
, (char * const *)argv
, "io")) != -1) {
2646 direction_specified
= 1;
2650 direction_specified
= 1;
2653 shi_delete_message_usage((argv
)?argv
[0]:NULL
);
2657 if (optind
>= argc
|| !argv
|| !argv
[optind
] || !*argv
[optind
]) {
2658 fprintf(stderr
, _("Bad invocation\n"));
2659 shi_delete_message_usage((argv
)?argv
[0]:NULL
);
2662 if (!direction_specified
) {
2663 fprintf(stderr
, _("Message direction has not been specified\n"));
2664 shi_delete_message_usage((argv
)?argv
[0]:NULL
);
2668 /* Process messages named in argv */
2669 for (int i
= optind
; i
< argc
; i
++) {
2670 if (!argv
[i
] || !*argv
[i
]) continue;
2672 id
= locale2utf8(argv
[i
]);
2675 _("Error: Could not convert message ID to UTF-8: %s\n"),
2680 printf(_("Deleting message `%s'...\n"), argv
[i
]);
2681 err
= isds_delete_message_from_storage(cisds
, id
, incoming
);
2682 finish_isds_operation(cisds
, err
);
2688 oprintf(_("Message `%s' deleted\n"), argv
[i
]);
2696 /* Convert message ID form locale to UTF-8 or in other direction. If both
2697 * strings are provided, UTF-8 will take precedence. The missing string is
2698 * automatically allocated (but not freed before). If UTF-8 version has been
2699 * provided, @stastic_utf8 will become 1, otherwise 0. You can pass the
2700 * strings and the flags to free_message_id() to free memory properly.*/
2701 static int convert_message_id(char **id_utf8
, char **id_locale
, _Bool
*static_utf8
) {
2702 if (!id_utf8
|| !id_locale
|| !static_utf8
) return -1;
2703 if (!*id_utf8
&& !*id_locale
) return -1;
2707 *id_locale
= utf82locale(*id_utf8
);
2710 *id_utf8
= locale2utf8(*id_locale
);
2713 _("Error: Could not convert message ID to UTF-8: %s\n"),
2723 /* Free message ID strings as were allocated by convert_message_id() */
2724 static int free_message_id(char **id_utf8
, char **id_locale
, _Bool static_utf8
) {
2725 if (!id_utf8
|| !id_locale
) return -1;
2726 if (static_utf8
) zfree(*id_locale
);
2727 else zfree(*id_utf8
);
2732 /* Return static UTF-8 encoded ID of current message. In case of error NULL. */
2733 static const char *get_current_message_id(void) {
2735 fprintf(stderr
, _("No message loaded\n"));
2738 if (!message
->envelope
) {
2739 fprintf(stderr
, _("Loaded message is missing envelope\n"));
2742 if (!message
->envelope
->dmID
|| !*message
->envelope
->dmID
) {
2743 fprintf(stderr
, _("Loaded message is missing ID\n"));
2746 return message
->envelope
->dmID
;
2750 static void shi_message_sender_usage(const char *command
) {
2752 "Usage: %s [MESSAGE_ID...]\n"
2753 "Get details about sender of a message.\n"
2754 "If MESSAGE_ID is defined, get sender of that message. More messages can be specified.\n"
2755 "Otherwise will get sender of current message, if any is loaded.\n"),
2760 /* Get details about sender of message with given ID. At least one form must
2762 * @message_id is UTF-8 string
2763 * @message_id_locale is string in locale encoding
2764 * @return 0 on success, -1 on failure */
2765 static int do_message_sender(const char *message_id
, const char *message_id_locale
) {
2766 isds_sender_type
*type
= NULL
;
2767 char *raw_type
= NULL
;
2772 if (convert_message_id((char **)&message_id
, (char **)&message_id_locale
,
2776 printf(_("Getting sender of message `%s'...\n"), message_id_locale
);
2777 err
= isds_get_message_sender(cisds
, message_id
, &type
, &raw_type
, &name
);
2778 finish_isds_operation(cisds
, err
);
2780 free_message_id((char **)&message_id
, (char **)&message_id_locale
,
2785 format_sender_info(message_id
, type
, raw_type
, name
);
2787 free_message_id((char **)&message_id
, (char **)&message_id_locale
,
2796 static int shi_message_sender(int argc
, const char **argv
) {
2798 return do_message_sender(get_current_message_id(), NULL
);
2801 for (int i
= 1; i
< argc
; i
++) {
2802 if (!argv
[i
] || !*argv
[i
]) continue;
2803 if (do_message_sender(NULL
, argv
[i
]))
2811 /* Mark message as read. At least one form of ID must be provided.
2812 * @id is UTF-8 encoded message ID
2813 * @id_locale is locale encoded message ID. @id takes preference. */
2814 static int do_read_message(const char *id
, const char *id_locale
) {
2818 if ((!id
|| !*id
) && (!id_locale
|| !*id_locale
)) return -1;
2820 if (convert_message_id((char **)&id
, (char **)&id_locale
, &static_id
)) return -1;
2822 printf(_("Marking message `%s' as read...\n"), id_locale
);
2823 err
= isds_mark_message_read(cisds
, id
);
2824 finish_isds_operation(cisds
, err
);
2827 oprintf(_("Message `%s' marked as read\n"), id_locale
);
2829 free_message_id((char **)&id
, (char **)&id_locale
, static_id
);
2831 return (err
) ? -1 : 0;
2835 static void shi_read_message_usage(const char *command
) {
2837 "Usage: %s [MESSAGE_ID...]\n"
2838 "Mark message as read moving its state to read.\n"
2840 "When new incoming message is download, its state is not changed on server.\n"
2841 "Client must mark such message as read explicitly. You can use this command\n"
2842 "to do so, if not done automatically at download time by your client.\n"
2844 "If MESSAGE_ID is defined, mark that message. More messages can be specified.\n"
2845 "Otherwise marks currently loaded message.\n"),
2850 static int shi_read_message(int argc
, const char **argv
) {
2852 return do_read_message(get_current_message_id(), NULL
);
2855 for (int i
= 1; i
< argc
; i
++) {
2856 if (!argv
[i
] || !*argv
[i
]) continue;
2857 if (do_read_message(NULL
, argv
[i
]))
2865 static void shi_cat_message_usage(const char *command
) {
2868 "Print unformated raw representation of current message.\n"
2870 "This is the same content you would get into file by `save' command.\n"
2872 "Be ware the binary stream can screw your terminal. No new line character\n"
2873 "will be appended to the end of the output.\n"),
2878 static int shi_cat_message(int argc
, const char **argv
) {
2880 fprintf(stderr
, _("No message loaded\n"));
2884 if (!message
->raw
|| !message
->raw_length
) {
2885 fprintf(stderr
, _("Current message is missing raw representation\n"));
2889 if (owrite(message
->raw
, message
->raw_length
) != message
->raw_length
) {
2890 fprintf(stderr
, _("Error while printing message content\n"));
2898 static int shi_show_message(int argc
, const char **argv
) {
2900 fprintf(stderr
, _("No message loaded\n"));
2904 format_message(message
);
2909 static void shi_incoming_message_usage(const char *command
) {
2911 "Usage: %s [-r] MESSAGE_ID\n"
2912 "Get incoming message with MESSAGE_ID.\n"
2914 " -r Mark mesage as read\n"),
2919 static int shi_incoming_message(int argc
, const char **argv
) {
2923 _Bool mark_as_read
= 0;
2926 while ((opt
= getopt(argc
, (char * const *)argv
, "r")) != -1) {
2932 shi_incoming_message_usage((argv
)?argv
[0]:NULL
);
2936 if (optind
+ 1 != argc
|| !argv
|| !argv
[optind
] || !*argv
[optind
]) {
2937 fprintf(stderr
, _("Bad invocation\n"));
2938 shi_incoming_message_usage((argv
)?argv
[0]:NULL
);
2943 printf(_("Getting incoming message...\n"));
2944 err
= isds_get_signed_received_message(cisds
, id
, &message
);
2945 finish_isds_operation(cisds
, err
);
2948 select_completion(COMPL_COMMAND
);
2952 format_message(message
);
2954 if (message
->envelope
&& message
->envelope
->dmID
)
2955 set_prompt(_("%s %s"), argv
[0], message
->envelope
->dmID
);
2957 set_prompt("%s", argv
[0]);
2958 select_completion(COMPL_MSG
);
2960 if (mark_as_read
|| cfg_getbool(configuration
, CONFIG_MARKMESSAGEREAD
)) {
2961 if (message
->envelope
&& message
->envelope
->dmMessageStatus
&&
2962 ! (*message
->envelope
->dmMessageStatus
& MESSAGESTATE_READ
))
2963 return do_read_message(id
, NULL
);
2969 static void shi_outgoing_message_usage(const char *command
) {
2971 "Usage: %s MESSAGE_ID\n"
2972 "Get outgoing message with MESSAGE_ID.\n"),
2977 static int shi_outgoing_message(int argc
, const char **argv
) {
2981 if (!argv
|| !argv
[1] || !*argv
[1]) {
2982 shi_outgoing_message_usage(argv
[0]);
2987 printf(_("Getting outgoing message...\n"));
2988 err
= isds_get_signed_sent_message(cisds
, id
, &message
);
2989 finish_isds_operation(cisds
, err
);
2992 select_completion(COMPL_COMMAND
);
2996 format_message(message
);
2997 if (message
->envelope
&& message
->envelope
->dmID
)
2998 set_prompt(_("%s %s"), argv
[0], message
->envelope
->dmID
);
3000 set_prompt("%s", argv
[0]);
3001 select_completion(COMPL_MSG
);
3006 /* Detect type (message or delivery data) and load it. And change completion
3007 * and show the data.
3008 * @buffer is memory with message or delivery data
3009 * @length is size of @buffer in bytes
3010 * @strategy defines how to fill global message variable
3011 * @return 0 for success, otherwise non-zero. */
3012 static int do_load_anything(const void *buffer
, size_t length
,
3013 isds_buffer_strategy strategy
) {
3014 isds_raw_type raw_type
;
3016 char *type_name
= NULL
;
3018 if (NULL
== buffer
|| 0 == length
) {
3022 printf(_("Detecting format...\n"));
3023 err
= isds_guess_raw_type(cisds
, &raw_type
, buffer
, length
);
3024 finish_isds_operation(cisds
, err
);
3027 if (err
== IE_NOTSUP
)
3028 fprintf(stderr
, _("Unknown format.\n"));
3030 fprintf(stderr
, _("Error while detecting format.\n"));
3033 case RAWTYPE_INCOMING_MESSAGE
:
3034 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
:
3035 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
:
3036 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
:
3037 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
3038 err
= isds_load_message(cisds
, raw_type
,
3039 buffer
, length
, &message
, strategy
);
3040 finish_isds_operation(cisds
, err
);
3041 type_name
= N_("message");
3044 case RAWTYPE_DELIVERYINFO
:
3045 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO
:
3046 case RAWTYPE_CMS_SIGNED_DELIVERYINFO
:
3047 err
= isds_load_delivery_info(cisds
, raw_type
,
3048 buffer
, length
, &message
, strategy
);
3049 finish_isds_operation(cisds
, err
);
3050 type_name
= N_("delivery");
3055 _("Unsupported format.\n"));
3062 select_completion(COMPL_COMMAND
);
3066 format_message(message
);
3068 if (message
->envelope
&& message
->envelope
->dmID
)
3069 set_prompt(_("%s %s"), _(type_name
), message
->envelope
->dmID
);
3071 set_prompt("%s", _(type_name
));
3072 select_completion(COMPL_MSG
);
3077 static void shi_load_anything_usage(const char *command
) {
3080 "Load message or message delivery details from local FILE.\n"),
3085 static int shi_load_anything(int argc
, const char **argv
) {
3087 void *buffer
= NULL
;
3091 if (!argv
|| !argv
[1] || !*argv
[1]) {
3092 shi_load_anything_usage((argv
)?argv
[0]:NULL
);
3096 printf(_("Loading file `%s'...\n"), argv
[1]);
3098 if (mmap_file(argv
[1], &fd
, &buffer
, &length
)) return -1;
3100 error
= do_load_anything(buffer
, length
, BUFFER_COPY
);
3102 munmap_file(fd
, buffer
, length
);
3108 static void shi_save_message_usage(const char *command
) {
3111 "Save message into local FILE.\n"),
3116 static const char *raw_type2mime(isds_raw_type raw_type
) {
3118 case RAWTYPE_INCOMING_MESSAGE
:
3119 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
:
3120 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
:
3121 case RAWTYPE_DELIVERYINFO
:
3122 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO
:
3125 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
:
3126 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
3127 case RAWTYPE_CMS_SIGNED_DELIVERYINFO
:
3128 return "application/pkcs7-mime";
3136 static int shi_save_message(int argc
, const char **argv
) {
3137 _Bool overwrite
= cfg_getbool(configuration
, CONFIG_OVERWRITEFILES
);
3139 if (!argv
|| !argv
[1] || !*argv
[1]) {
3140 shi_save_message_usage(argv
[0]);
3145 fprintf(stderr
, _("No message loaded\n"));
3148 if (!message
->raw
|| message
->raw_length
== 0) {
3149 fprintf(stderr
, _("Loaded message is missing raw representation\n"));
3153 return save_data_to_file(argv
[1], -1, message
->raw
, message
->raw_length
,
3154 raw_type2mime(message
->raw_type
), overwrite
);
3158 /* Return document of current message identified by ordinal number expressed
3159 * as string. In case of error return NULL. */
3160 static const struct isds_document
*locate_document_by_ordinal_string(
3161 const char *number
) {
3162 const struct isds_list
*item
;
3163 const struct isds_document
*document
= NULL
;
3166 if (!number
) return NULL
;
3168 ordinar
= atoi(number
);
3170 fprintf(stderr
, _("%s: Document number must be positive number\n"),
3176 fprintf(stderr
, _("No message loaded\n"));
3181 for (item
= message
->documents
, i
= 0; item
; item
= item
->next
) {
3182 if (!item
->data
) continue;
3183 if (++i
== ordinar
) {
3184 document
= (const struct isds_document
*) item
->data
;
3189 fprintf(stderr
, _("Message does not contain document #%d\n"), ordinar
);
3197 static void shi_cat_document_usage(const char *command
) {
3199 "Usage: %s NUMBER\n"
3200 "Print document selected with ordinal NUMBER.\n"),
3204 static int shi_cat_document(int argc
, const char **argv
) {
3205 const struct isds_document
*document
;
3207 if (!argv
|| !argv
[1] || !*argv
[1] || argc
> 3) {
3208 shi_cat_document_usage(argv
[0]);
3212 document
= locate_document_by_ordinal_string(argv
[1]);
3213 if (!document
) return -1;
3215 if (document
->is_xml
) {
3216 xmlBufferPtr buffer
= NULL
;
3219 if (serialize_xml_to_buffer(&buffer
, document
->xml_node_list
))
3222 written
= owrite(buffer
->content
, buffer
->use
);
3223 xmlBufferFree(buffer
);
3224 if (written
!= buffer
->use
) {
3225 fprintf(stderr
, _("Error while printing document content\n"));
3229 if (!document
->data
|| !document
->data_length
) {
3230 fprintf(stderr
, _("Document is missing raw representation\n"));
3234 if (owrite(document
->data
, document
->data_length
) != document
->data_length
) {
3235 fprintf(stderr
, _("Error while printing document content\n"));
3244 static void shi_save_document_usage(const char *command
) {
3246 "Usage: %s NUMBER [DESTINATION]\n"
3247 "Save document having ordinal NUMBER within current message into local file.\n"
3248 "If DESTINATION is file (or does not exist yet), document will be saved into\n"
3250 "If DESTINATION is existing directory, file name equaled to document name\n"
3251 "will be saved into DESTINATION.\n"
3252 "If DESTINATION is missing, document name will be used as file name and\n"
3253 "saved into working directory.\n"
3254 "Be aware that document name does not embed malicious characters (slashes).\n"
3256 "If the document is a binary stream, image of the document will be copied\n"
3257 "into a file. If the document is a XML document, the XML tree will be serialized\n"
3258 "into a file. If XML document stands for one element or one text node, the node\n"
3259 "(and its children recursively) will be serialized. If XML document compounds\n"
3260 "more nodes or a comment or a processing instruction, parent node from ISDS name\n"
3261 "space will be used to ensure output serialized XML well-formness.\n"
3267 static int shi_save_document(int argc
, const char **argv
) {
3268 const struct isds_document
*document
;
3269 const char *dirname
= NULL
;
3270 char *filename
= NULL
, *path
= NULL
;
3272 _Bool overwrite
= cfg_getbool(configuration
, CONFIG_OVERWRITEFILES
);
3274 if (!argv
|| !argv
[1] || !*argv
[1] || argc
> 3) {
3275 shi_save_document_usage(argv
[0]);
3279 document
= locate_document_by_ordinal_string(argv
[1]);
3280 if (!document
) return -1;
3282 /* Select directory and file name */
3283 if (argv
[2] && *argv
[2]) {
3284 if (!is_directory(argv
[2])) {
3287 filename
= strdup(argv
[2]);
3289 fprintf(stderr
, _("Not enough memory\n"));
3294 if (!filename
&& document
->dmFileDescr
&& &document
->dmFileDescr
) {
3295 filename
= utf82locale(document
->dmFileDescr
);
3297 fprintf(stderr
, _("Not enough memory\n"));
3303 _("File name neither supplied, nor document name exists\n"
3304 "Please, supply one.\n"));
3310 path
= astrcat3(dirname
, "/", filename
);
3317 fprintf(stderr
, _("Not enough memory\n"));
3322 if (document
->is_xml
)
3323 retval
= save_xml_to_file(path
, -1, document
->xml_node_list
,
3324 document
->dmMimeType
, overwrite
);
3326 retval
= save_data_to_file(path
, -1, document
->data
,
3327 document
->data_length
, document
->dmMimeType
, overwrite
);
3333 /* Execute program specified as NULL terminated array of arguments. argv[0] is
3334 * subject of PATH search variable look-up. The program is executed directly,
3335 * it's not a shell command. */
3336 static int execute_system_command(char *const argv
[]) {
3339 if (!argv
|| !argv
[0]) return -1;
3343 /* Could not fork */
3344 fprintf(stderr
, _("Could not fork\n"));
3346 } else if (pid
== 0) {
3348 execvp(argv
[0], argv
);
3349 fprintf(stderr
, _("Could not execute:"));
3350 for (char *const *arg
= argv
; *arg
; arg
++)
3351 fprintf(stderr
, " %s", *arg
);
3352 fprintf(stderr
, _(": %s\n"), strerror(errno
));
3355 /* Wait for the command */
3358 if (-1 == waitpid(pid
, &retval
, 0)) {
3359 fprintf(stderr
, _("Could not wait for executed command\n"));
3364 fprintf(stderr
, _("Exit code of command could not "
3365 "be determined\n"));
3366 else if (WIFEXITED(retval
) && WEXITSTATUS(retval
))
3367 printf(_("Command exited with code %d\n"),
3368 WEXITSTATUS(retval
));
3369 else if (WIFSIGNALED(retval
))
3370 printf(_("Command terminated by signal "
3371 "#%d\n"), WTERMSIG(retval
));
3377 /* Run editor to create new text document */
3378 static int edit_new_textual_document(struct isds_document
*document
) {
3379 char filename
[14] = "shiXXXXXX.txt";
3381 char *command
[] = { getenv("VISUAL"), filename
, NULL
};
3383 struct stat file_before
, file_after
;
3386 fprintf(stderr
, _("Editing is forbidden in batch mode.\n"));
3390 if (NULL
== document
) return -1;
3391 if (NULL
== command
[0]) command
[0] = getenv("EDITOR");
3392 if (NULL
== command
[0]) {
3394 _("Neither environment variable VISUAL nor EDITOR are set.\n"));
3398 /* Create temporary file for the document */
3399 fd
= create_new_file(filename
, 4);
3403 if (fstat(fd
, &file_before
)) {
3405 _("Could not retrieve modification time for `%s': %s\n"),
3406 filename
, strerror(errno
));
3411 /* Open the file with $EDITOR */
3412 if ((retval
= execute_system_command(command
))) {
3413 fprintf(stderr
, _("Editor failed.\n"));
3418 /* Compare modification times */
3419 /* XXX: fstat(2) does return updated st_mtime. Bug in Linux 3.7.1? */
3420 if (stat(filename
, &file_after
)) {
3422 _("Could not retrieve modification time for `%s': %s\n"),
3423 filename
, strerror(errno
));
3427 if (file_before
.st_mtime
== file_after
.st_mtime
) {
3428 fprintf(stderr
, _("Edited document has not been changed.\n"));
3434 if (load_data_from_file(filename
, &document
->data
,
3435 &document
->data_length
, NULL
)) {
3441 if (NULL
== document
->dmMimeType
)
3442 FILL_OR_LEAVE(document
->dmMimeType
, "text/plain");
3443 /* XXX: POSIX basename() modifies argument */
3444 if (NULL
== document
->dmFileDescr
)
3445 FILL_OR_LEAVE(document
->dmFileDescr
, basename(filename
));
3448 /* Remove the file */
3449 unlink_file(filename
);
3455 /* Append @suffix into @buffer with @size bytes prealocated at position @at.
3456 * Trailing '\0' of @suffix is not carried.
3457 * @buffer can be reallocated if @size is not suffient to fill @suffix
3458 * @size is original @buffer size, can change if @buffer would be reallocated
3459 * @at position where append @suffic to. Outputs new end after appending
3460 * @suffix is NULL rerminated string to append
3461 * @return 0 if success, -1 otherwise. Caller is resposible for freeing
3463 static int append_string_at(char **buffer
, size_t *size
, char **at
,
3464 const char *suffix
) {
3466 if (!buffer
|| !*buffer
|| !size
|| !at
|| !*at
) return -1;
3467 if (!suffix
) return 0;
3470 if (*at
- *buffer
+ 1 >= *size
) {
3471 /* End of buffer, grow it */
3472 if (*size
< 8) *size
= 8;
3474 char *new_buffer
= realloc(*buffer
, *size
);
3475 if (!new_buffer
) return -1;
3476 *at
= *at
- *buffer
+ new_buffer
;
3477 *buffer
= new_buffer
;
3480 /* Copy a character */
3481 *((*at
)++) = *(suffix
++);
3488 static char *expand_command_arg(const char *format
, const char *file
,
3490 char *buffer
= NULL
;
3492 const char *format_cursor
;
3493 char *buffer_cursor
;
3495 if (!format
) return NULL
;
3497 for (format_cursor
= format
, buffer_cursor
= buffer
; ; format_cursor
++) {
3498 if (buffer_cursor
- buffer
+ 1 >= size
) {
3499 /* End of buffer, grow it */
3500 if (size
< 8) size
= 8;
3502 char *new_buffer
= realloc(buffer
, size
);
3503 if (!new_buffer
) goto error
;
3504 buffer_cursor
= buffer_cursor
- buffer
+ new_buffer
;
3505 buffer
= new_buffer
;
3508 if (*format_cursor
== '%') {
3510 switch (*(format_cursor
+1)) {
3513 fprintf(stderr
, _("Could not expand `%%f' because "
3514 "file name did not exist.\n"));
3518 if (append_string_at(&buffer
, &size
, &buffer_cursor
, file
))
3525 fprintf(stderr
, _("Could not expand `%%t' because "
3526 "file type did not exist.\n"));
3530 if (append_string_at(&buffer
, &size
, &buffer_cursor
, type
))
3540 /* Copy plain character */
3541 *(buffer_cursor
++) = *format_cursor
;
3543 if (!*format_cursor
) break;
3549 fprintf(stderr
, _("Error: Not enough memory\n"));
3555 /* Expand open_command configuration option by substiting %f and %t with file
3556 * name and file type.
3557 * @file is locale encoded file name
3558 * @type is UTF-8 encoded MIME type
3559 * @return heap allocated arrary of arguments or NULL if error occurs.
3560 * Arguments are coded in locale. */
3561 static char **expand_open_command(const char *file
, const char *type
) {
3562 char **command
= NULL
;
3564 char *type_locale
= NULL
;
3566 length
= cfg_size(configuration
, CONFIG_OPENCOMMAND
);
3568 fprintf(stderr
, _("%s not set\n"), CONFIG_OPENCOMMAND
);
3572 command
= malloc((length
+ 1) * sizeof(*command
));
3574 fprintf(stderr
, _("Error: Not enough memory\n"));
3579 type_locale
= utf82locale(type
);
3581 printf(_("Could not convert document MIME type to locale "
3588 for (int i
= 0; i
< length
; i
++) {
3589 const char *value
= cfg_getnstr(configuration
, CONFIG_OPENCOMMAND
, i
);
3590 command
[i
] = expand_command_arg(value
, file
, type_locale
);
3592 fprintf(stderr
, _("Error: Not enough memory\n"));
3593 for (int j
= 0; j
< i
; j
++) free(command
[j
]);
3599 command
[length
] = NULL
;
3606 /* Register temporary @file for removal at shigofumi exit.
3607 * This is done by destructor call-back in isds_list_free(). */
3608 static int register_temporary_file(const char *file
) {
3609 struct isds_list
*temporary_file
= NULL
;
3611 if (!file
) return 0;
3613 temporary_file
= malloc(sizeof(*temporary_file
));
3614 if (!temporary_file
) {
3615 fprintf(stderr
, _("Error: Not enough memory\n"));
3619 temporary_file
->data
= (void *)strdup(file
);
3620 if (!temporary_file
) {
3621 fprintf(stderr
, _("Error: Not enough memory\n"));
3622 free(temporary_file
);
3626 temporary_file
->destructor
= shi_unlink_temporary_file
;
3628 if (temporary_files
)
3629 temporary_file
->next
= temporary_files
;
3631 temporary_file
->next
= NULL
;
3632 temporary_files
= temporary_file
;
3638 static void shi_open_document_usage(const char *command
) {
3640 "Usage: %s NUMBER\n"
3641 "Save document having ordinal NUMBER within current message into temporal\n"
3642 "local file, open the file by xdg-open utility and then remove the file.\n"
3648 static int shi_open_document(int argc
, const char **argv
) {
3649 const struct isds_document
*document
;
3650 char filename
[10] = "shiXXXXXX";
3652 char **command
= NULL
;
3655 if (!argv
|| !argv
[1] || !*argv
[1] || argc
> 3) {
3656 shi_open_document_usage(argv
[0]);
3660 document
= locate_document_by_ordinal_string(argv
[1]);
3661 if (!document
) return -1;
3663 /* Create temporary file for the document */
3664 fd
= create_new_file(filename
, 0);
3670 if (document
->is_xml
)
3671 retval
= save_xml_to_file(filename
, fd
, document
->xml_node_list
,
3672 document
->dmMimeType
, 0);
3674 retval
= save_data_to_file(filename
, fd
, document
->data
,
3675 document
->data_length
, document
->dmMimeType
, 0);
3677 /* Open the file with external utility */
3679 /* Construct command arguments to execute */
3680 command
= expand_open_command(filename
, document
->dmMimeType
);
3685 /* XXX: Do not use system(3) as we cannot escape uknown shell */
3686 retval
= execute_system_command(command
);
3687 for (char **arg
= command
; *arg
; arg
++) free(*arg
);
3692 /* Remove the file */
3693 /* XXX: We do not know when external program opens the file. We cannot
3694 * remove it immediately. Register the filename and remove all temporary
3695 * files at exit of shigofumi if requested by configuration. */
3696 if (cfg_getbool(configuration
, CONFIG_CLEAN_TEMPORARY_FILES
)) {
3697 if (register_temporary_file(filename
))
3698 fprintf(stderr
, _("Warning: Temporary file `%s' could not been "
3699 "registered for later removal. Remove the file by "
3700 "hand, please.\n"), filename
);
3706 static void shi_compose_usage(const char *command
) {
3708 "Usage: %s OPTION...\n"
3709 "Compose and send a message to recipient defined by his box ID.\n"
3710 "Each option requires an argument (if not stated otherwise):\n"
3711 " -s * message subject\n"
3713 "Recipient options:\n"
3714 " -b * recipient box ID\n"
3715 " -U organisation unit name\n"
3716 " -N organisation unit number\n"
3717 " -P to hands of given person\n"
3719 "Sender organisation structure options:\n"
3720 " -I publish user's identity\n"
3724 "Message identifier options:\n"
3725 " -r sender reference number\n"
3726 " -f sender file ID\n"
3727 " -R recipient reference number\n"
3728 " -F recipient file ID\n"
3730 "Legal title options:\n"
3731 " -y year act has been issued\n"
3732 " -a ordinal number of act in a year\n"
3733 " -e section of the act\n"
3734 " -o paragraph of the act\n"
3735 " -i point of the paragraph of the act\n"
3737 "Delivery options:\n"
3738 " -p personal delivery required\n"
3739 " -t allow substitutable delivery\n"
3740 " -A non-OVM sender acts as public authority\n"
3741 " -C commercial type; accepted values:\n"
3742 " K Commercial message paid by sender or sponsor\n"
3743 " I Initiatory commercial message offering to pay response\n"
3744 " O Commercial response paid by recipient\n"
3745 " V Public message paid by government\n"
3746 " Missing option defaults to K or O (see `commercialsending' command)\n"
3747 " if sending to non-OVM recipient with enabled commercial receiving,\n"
3748 " otherwise it defaults to V.\n"
3750 "Document options:\n"
3751 " -d * read document from local file. If `-' is specified,\n"
3752 " run text editor.\n"
3753 " -D document name (defaults to base local file name)\n"
3754 " -x transport subset of the document as a XML.\n"
3755 " Argument is XPath expression specifying desired node set\n"
3756 " -m override MIME type (guessed on -d)\n"
3757 " -g document ID (must be unique per message)\n"
3758 " -G reference to other document using its ID\n"
3759 " -c document is digital signature of other document (NO argument\n"
3762 "Options marked with asterisk are mandatory, other are optional. Another soft\n"
3763 "dependencies can emerge upon using specific option. They are not mandated by\n"
3764 "ISDS currently, but client library or this program can force them to assure\n"
3765 "semantically complete message. Following soft dependencies are recommended:\n"
3766 " -y <=> -a act number and year must be used at the same time\n"
3767 " -i => -o act point requires act paragraph\n"
3768 " -o => -e act paragraph requires act section\n"
3769 " -e => -a act section requires act number\n"
3770 " -G => -g document with referenced ID must exist\n"
3771 " -c => -G signature must refer to signed document\n"
3772 " -c first document cannot be signature\n"
3773 " -C I => -r sender reference number allows responder to reply to this message\n"
3774 " -C O -> -R recipient reference number must match sender reference number of\n"
3775 " initiatory message\n"
3777 "More documents can be attached to a message by repeating `-d' option.\n"
3778 "Document order will be preserved. Other document options affect immediately\n"
3779 "preceding `-d' document only. E.g. `-d /tmp/foo.pdf -m application/pdf\n"
3780 "-d /tmp/bar.txt -m text/plain' attaches first PDF file, then textual file.\n"
3782 "The same applies to recipient options that must start with box ID (-b).\n"
3783 "If more recipients specified, each of them will get a copy of composed\n"
3784 "message. ISDS will assign message identifier to each copy in turn.\n"
3790 static int shi_compose(int argc
, const char **argv
) {
3795 struct isds_message
*message
= NULL
;
3796 struct isds_envelope
*envelope
= NULL
;
3797 struct isds_list
*documents
= NULL
;
3798 struct isds_document
*document
= NULL
;
3799 struct isds_list
*copies
= NULL
, *copy_item
= NULL
;
3800 struct isds_message_copy
*copy
= NULL
;
3801 char *message_id_locale
= NULL
, *recipient_id_locale
= NULL
,
3802 *dmStatus_locale
= NULL
;
3804 if (!argv
|| !argv
[1] || !*argv
[1]) {
3805 fprintf(stderr
, _("Error: No argument supplied\n"));
3806 shi_compose_usage((argv
)?argv
[0]:NULL
);
3810 message
= calloc(1, sizeof(*message
));
3812 fprintf(stderr
, _("Error: Not enough memory\n"));
3816 envelope
= calloc(1, sizeof(*envelope
));
3818 fprintf(stderr
, _("Error: Not enough memory\n"));
3822 message
->envelope
= envelope
;
3826 while ((opt
= getopt(argc
, (char * const *)argv
, "s:" "b:U:N:P:" "I:u:n:"
3827 "r:f:R:F:" "y:a:e:o:i:" "p:t:A:C:" "d:D:x:m:g:G:c"
3831 FILL_OR_LEAVE(envelope
->dmAnnotation
, optarg
);
3834 /* Recipient options */
3838 /* First recipient */
3839 CALLOC_OR_LEAVE(copies
);
3840 copies
->destructor
=
3841 (void(*)(void **)) isds_message_copy_free
;
3844 /* Next recipient */
3845 CALLOC_OR_LEAVE(copy_item
->next
);
3846 copy_item
->next
->destructor
=
3847 (void(*)(void **)) isds_message_copy_free
;
3848 copy_item
= copy_item
->next
;
3850 CALLOC_OR_LEAVE(copy
);
3851 copy_item
->data
= copy
;
3853 /* Copy recipient box ID */
3854 FILL_OR_LEAVE(copy
->dbIDRecipient
, optarg
);
3859 _("Error: %s: Recipient box ID (-b) must precede "
3860 "recipient organisation unit name (-%c)\n"),
3865 FILL_OR_LEAVE(copy
->dmRecipientOrgUnit
, optarg
);
3870 _("Error: %s: Recipient box ID (-b) must precede "
3871 "recipient organisation unit number (-%c)\n"),
3876 FILL_LONGINT_OR_LEAVE(copy
->dmRecipientOrgUnitNum
, optarg
);
3881 _("Error: %s: Recipient box ID (-b) must precede "
3882 "to-hands option (-%c)\n"), optarg
, opt
);
3886 FILL_OR_LEAVE(copy
->dmToHands
, optarg
);
3889 /* Sender organisation structure options */
3891 FILL_BOOLEAN_OR_LEAVE(envelope
->dmPublishOwnID
, optarg
);
3894 FILL_OR_LEAVE(envelope
->dmSenderOrgUnit
, optarg
);
3897 FILL_LONGINT_OR_LEAVE(envelope
->dmSenderOrgUnitNum
, optarg
);
3900 /* Message identifier options */
3902 FILL_OR_LEAVE(envelope
->dmSenderRefNumber
, optarg
);
3905 FILL_OR_LEAVE(envelope
->dmSenderIdent
, optarg
);
3908 FILL_OR_LEAVE(envelope
->dmRecipientRefNumber
, optarg
);
3911 FILL_OR_LEAVE(envelope
->dmRecipientIdent
, optarg
);
3914 /* Legal title options */
3916 FILL_LONGINT_OR_LEAVE(envelope
->dmLegalTitleYear
, optarg
);
3919 FILL_LONGINT_OR_LEAVE(envelope
->dmLegalTitleLaw
, optarg
);
3922 FILL_OR_LEAVE(envelope
->dmLegalTitleSect
, optarg
);
3925 FILL_OR_LEAVE(envelope
->dmLegalTitlePar
, optarg
);
3928 FILL_OR_LEAVE(envelope
->dmLegalTitlePoint
, optarg
);
3931 /* Delivery options */
3933 FILL_BOOLEAN_OR_LEAVE(envelope
->dmPersonalDelivery
, optarg
);
3936 FILL_BOOLEAN_OR_LEAVE(envelope
->dmAllowSubstDelivery
, optarg
);
3939 FILL_BOOLEAN_OR_LEAVE(envelope
->dmOVM
, optarg
);
3942 FILL_OR_LEAVE(envelope
->dmType
, optarg
);
3945 /* Document options */
3949 /* First document */
3950 CALLOC_OR_LEAVE(message
->documents
);
3951 message
->documents
->destructor
=
3952 (void(*)(void **)) isds_document_free
;
3953 documents
= message
->documents
;
3954 CALLOC_OR_LEAVE(document
);
3955 documents
->data
= document
;
3956 document
->dmFileMetaType
= FILEMETATYPE_MAIN
;
3959 CALLOC_OR_LEAVE(documents
->next
);
3960 documents
->next
->destructor
=
3961 (void(*)(void **)) isds_document_free
;
3962 documents
= documents
->next
;
3963 CALLOC_OR_LEAVE(document
);
3964 documents
->data
= document
;
3965 document
->dmFileMetaType
= FILEMETATYPE_ENCLOSURE
;
3968 if (strcmp(optarg
, "-")) {
3969 /* Load file if specified. Keep editing new file after
3970 * processing all arguments. */
3971 if (load_data_from_file(optarg
, &document
->data
,
3972 &document
->data_length
, &document
->dmMimeType
)) {
3976 /* XXX: POSIX basename() modifies argument */
3977 FILL_OR_LEAVE(document
->dmFileDescr
, basename(optarg
));
3983 _("Error: %s: Document file (-d) must precede "
3984 "document name (-%c)\n"), optarg
, opt
);
3988 FILL_OR_LEAVE(document
->dmFileDescr
, optarg
);
3993 _("Error: %s: Document file (-d) must precede "
3994 "XPath expression (-%c)\n"), optarg
, opt
);
3998 /* Load XML node list */
3999 char *xpath_expr
= NULL
;
4000 FILL_OR_LEAVE(xpath_expr
, optarg
);
4001 retval
= load_xml_subtree_from_memory(
4002 document
->data
, document
->data_length
,
4003 &document
->xml_node_list
, xpath_expr
);
4008 /* Switch document type to XML */
4009 document
->is_xml
= 1;
4010 zfree(document
->data
);
4011 document
->data_length
= 0;
4012 documents
->destructor
=
4013 (void(*)(void **)) free_document_with_xml_node_list
;
4018 _("Error: %s: Document file (-d) must precede "
4019 "MIME type (-%c)\n"), optarg
, opt
);
4023 FILL_OR_LEAVE(document
->dmMimeType
, optarg
);
4028 _("Error: %s: Document file (-d) must precede "
4029 "document ID (-%c)\n"), optarg
, opt
);
4033 FILL_OR_LEAVE(document
->dmFileGuid
, optarg
);
4038 _("Error: %s: Document file (-d) must precede "
4039 "document reference (-%c)\n"), optarg
, opt
);
4043 FILL_OR_LEAVE(document
->dmUpFileGuid
, optarg
);
4048 _("Error: Document file (-d) must precede "
4049 "document signature type (-%c)\n"), opt
);
4053 document
->dmFileMetaType
= FILEMETATYPE_SIGNATURE
;
4057 shi_compose_usage(argv
[0]);
4063 /* All options must be recognized */
4064 if (optind
!= argc
) {
4065 fprintf(stderr
, _("Error: Superfluous argument\n"));
4066 shi_compose_usage(argv
[0]);
4072 fprintf(stderr
, _("Error: No recipient box ID specified\n"));
4073 shi_compose_usage(argv
[0]);
4078 /* TODO: Check Legal Title soft dependencies */
4080 /* Compose missing documents */
4081 for (i
= 1, documents
= message
->documents
; NULL
!= documents
;
4082 documents
= documents
->next
, i
++) {
4083 document
= documents
->data
;
4084 if (NULL
== document
->data
) {
4085 printf(_("Editing document #%u...\n"), i
);
4086 if (edit_new_textual_document(document
)) {
4087 fprintf(stderr
, _("Composition aborted.\n"));
4095 oprintf(_("Following message has been composed:\n"));
4096 format_message(message
);
4097 oprintf(_("Following recipients have been specified:\n"));
4098 format_copies(copies
);
4099 /* TODO: correction */
4100 if (cfg_getbool(configuration
, CONFIG_CONFIRM_SEND
)) {
4101 if (!shi_ask_yes_no(_("Send composed message?"), 1, batch_mode
)) {
4102 fprintf(stderr
, _("Composition aborted.\n"));
4108 /* Send a message */
4109 printf(_("Sending message...\n"));
4110 err
= isds_send_message_to_multiple_recipients(cisds
, message
, copies
);
4111 finish_isds_operation(cisds
, err
);
4112 if (err
&& err
!= IE_PARTIAL_SUCCESS
) {
4117 /* Show results for each copy */
4118 for (copy_item
= copies
; copy_item
; copy_item
= copy_item
->next
) {
4119 if (!copy_item
->data
) continue;
4120 copy
= (struct isds_message_copy
*) copy_item
->data
;
4121 recipient_id_locale
= utf82locale(copy
->dbIDRecipient
);
4125 if (copy
->dmStatus
) dmStatus_locale
= utf82locale(copy
->dmStatus
);
4126 if (dmStatus_locale
)
4127 oprintf(_("%s: Failed: %s: %s\n"),
4128 recipient_id_locale
,
4129 isds_strerror(copy
->error
), dmStatus_locale
);
4131 oprintf(_("%s: Failed: %s\n"), recipient_id_locale
,
4132 isds_strerror(copy
->error
));
4133 zfree(dmStatus_locale
);
4135 message_id_locale
= utf82locale(copy
->dmID
);
4136 oprintf(_("%s: Succeeded. Assigned message ID: %s\n"),
4137 recipient_id_locale
, message_id_locale
);
4138 free(message_id_locale
);
4141 free(recipient_id_locale
);
4145 isds_message_free(&message
);
4146 isds_list_free(&copies
);
4151 #undef FILL_LONGINT_OR_LEAVE
4152 #undef FILL_BOOLEAN_OR_LEAVE
4153 #undef CALLOC_OR_LEAVE
4154 #undef FILL_OR_LEAVE
4157 static void shi_save_stamp_usage(const char *command
) {
4160 "Save message time stamp into local FILE.\n"),
4165 static int shi_save_stamp(int argc
, const char **argv
) {
4166 _Bool overwrite
= cfg_getbool(configuration
, CONFIG_OVERWRITEFILES
);
4168 if (!argv
|| !argv
[1] || !*argv
[1]) {
4169 shi_save_message_usage(argv
[0]);
4174 fprintf(stderr
, _("No message loaded\n"));
4177 if (!message
->envelope
|| !message
->envelope
->timestamp
||
4178 message
->envelope
->timestamp_length
== 0) {
4179 fprintf(stderr
, _("Loaded message is missing time stamp\n"));
4183 return save_data_to_file(argv
[1], -1,
4184 message
->envelope
->timestamp
, message
->envelope
->timestamp_length
,
4185 "application/timestamp-reply", overwrite
);
4189 /* Return message of current message list identified by message ID expressed
4190 * as string. In case of error return NULL. */
4191 static const struct isds_message
*locate_message_by_id(const char *id_locale
) {
4193 const struct isds_list
*item
;
4194 const struct isds_message
*message
= NULL
;
4196 if (NULL
== id_locale
) return NULL
;
4198 id
= locale2utf8(id_locale
);
4200 fprintf(stderr
, _("Could not convert message ID `%s' to UTF-8\n"),
4205 if (NULL
== messages
) {
4206 fprintf(stderr
, _("No message list loaded\n"));
4211 for (item
= messages
; NULL
!= item
; item
= item
->next
) {
4212 if (NULL
== item
->data
) continue;
4213 message
= (const struct isds_message
*) item
->data
;
4214 if (NULL
== message
->envelope
|| NULL
== message
->envelope
->dmID
)
4216 if (!strcmp(id
, message
->envelope
->dmID
)) {
4221 fprintf(stderr
, _("List does not contain message `%s'\n"), id_locale
);
4229 static void shi_show_list_usage(const char *command
) {
4231 "Usage: %s [MESSAGE_ID]\n"
4232 "If no MESSAGE_ID is given, show current message list.\n"
4233 "If MESSAGE_ID is specified, show details about message with the MESSAGE_ID\n"
4234 "on the current message list.\n"),
4239 static int shi_show_list(int argc
, const char **argv
) {
4240 if (NULL
== messages
) {
4241 fprintf(stderr
, _("No message list loaded\n"));
4245 if (argc
> 2 || (2 == argc
&& NULL
== argv
[1])) {
4246 shi_show_list_usage((argv
[0] == NULL
) ? NULL
: argv
[0]);
4251 /* Show details about one message */
4252 const struct isds_message
*message
= locate_message_by_id(argv
[1]);
4253 if (NULL
== message
) return -1;
4254 format_message(message
);
4259 oprintf((messages_are_outgoing
) ?
4260 ngettext("You have %'lu outgoing message\n",
4261 "You have %'lu outgoing messages\n", total_messages
) :
4262 ngettext("You have %'lu incoming message\n",
4263 "You have %'lu incoming messages\n", total_messages
),
4265 print_message_list(messages
, messages_are_outgoing
);
4270 static void describe_message_listing_flags(void) {
4272 "The third column displays flags describing status of a message:\n"
4273 " First character is a commercial type of the message:\n"
4274 " P public non-commercial message\n"
4275 " C commercial message\n"
4276 " I commercial message offering a paid response\n"
4277 " i like I, but the offer has expired\n"
4278 " R commerical message as a reply to I\n"
4279 " The second character is a delivery status of the message:\n"
4280 " > message has been sent into the system\n"
4281 " t message has been time-stamped by the system\n"
4282 " I message contained an infected document\n"
4283 " N message has been delivered ordinaly\n"
4284 " n message has been delivered substitutingly\n"
4285 " O message has been accepted by the recipient\n"
4286 " \" \" message has been read\n"
4287 " ! message could not been delivered\n"
4288 " D message content has been deleted\n"
4289 " S message has been stored in the long term storage\n"
4290 " ? unrecognized state\n"));
4294 static void shi_list_incoming_usage(const char *command
) {
4297 "List messages received into your box.\n"
4300 describe_message_listing_flags();
4304 static int shi_list_incoming(int argc
, const char **argv
) {
4307 printf(_("Listing incoming messages...\n"));
4308 err
= isds_get_list_of_received_messages(cisds
, NULL
, NULL
, NULL
,
4309 MESSAGESTATE_ANY
, 0, &total_messages
, &messages
);
4310 finish_isds_operation(cisds
, err
);
4313 select_completion(COMPL_COMMAND
);
4316 messages_are_outgoing
= 0;
4318 shi_show_list(0, NULL
);
4320 set_prompt(_("%s %'lu"), argv
[0], total_messages
);
4321 select_completion(COMPL_LIST
);
4326 static void shi_list_outgoing_usage(const char *command
) {
4329 "List messages sent from your box.\n"
4332 describe_message_listing_flags();
4336 static int shi_list_outgoing(int argc
, const char **argv
) {
4339 printf(_("Listing outgoing messages...\n"));
4340 err
= isds_get_list_of_sent_messages(cisds
, NULL
, NULL
, NULL
,
4341 MESSAGESTATE_ANY
, 0, &total_messages
, &messages
);
4342 finish_isds_operation(cisds
, err
);
4345 select_completion(COMPL_COMMAND
);
4348 messages_are_outgoing
= 1;
4350 shi_show_list(0, NULL
);
4352 set_prompt(_("%s %'lu"), argv
[0], total_messages
);
4353 select_completion(COMPL_LIST
);
4358 /* Submit document for conversion and print assigned identifier */
4359 static int do_convert(const struct isds_document
*document
) {
4362 struct tm
*date
= NULL
;
4364 if (!document
) return -1;
4366 printf(_("Submitting document for authorized conversion...\n"));
4368 err
= czp_convert_document(czechpoint
, document
, &id
, &date
);
4369 finish_isds_operation(czechpoint
, err
);
4372 char *name_locale
= utf82locale(document
->dmFileDescr
);
4373 char *date_string
= tm2string(date
);
4374 char *id_locale
= utf82locale(id
);
4376 "Document submitted for authorized conversion successfully under name\n"
4378 "Submit identifier assigned by Czech POINT deposit is `%s'.\n"),
4379 name_locale
, date_string
, id_locale
);
4383 oprintf(_("Be ware that submitted document has restricted lifetime "
4385 oprintf(_("See <%s> for more details.\n"), CZPDEPOSIT_URL
);
4388 free(id
); free(date
);
4389 return (err
) ? -1 : 0;
4393 static void shi_convert_file_or_message_usage(const char *command
) {
4395 "Usage: %s [FILE [NAME]]\n"
4396 "Submit local FILE to authorized conversion under NAME. If NAME is missing,\n"
4397 "it will use FILE name. If FILE is missing, it will submit current message.\n"),
4401 "If Czech POINT deposit accepts document, it will return document identifier\n"
4402 "that user is supposed to provide to officer at Czech POINT contact place.\n"
4403 "Currently only PDF 1.3 and higher version files and signed messages are\n"
4405 oprintf(_("See <%s> for more details.\n"), CZPDEPOSIT_URL
);
4409 static int shi_convert_file_or_message(int argc
, const char **argv
) {
4411 struct isds_document document
;
4414 if (!argv
|| argc
> 3) {
4415 shi_convert_file_or_message_usage((argv
)?argv
[0]:NULL
);
4419 memset(&document
, 0, sizeof(document
));
4421 if (NULL
== argv
[1] || !*argv
[1]) {
4422 /* Convert current message */
4424 fprintf(stderr
, _("No message loaded\n"));
4427 if (!message
->raw
|| !message
->raw_length
) {
4429 _("Current message is missing raw representation\n"));
4432 document
.dmFileDescr
= astrcat(
4433 (NULL
!= message
->envelope
&& NULL
!= message
->envelope
->dmID
) ?
4434 message
->envelope
->dmID
:
4437 if (NULL
== document
.dmFileDescr
) {
4438 printf(_("Could not build document name from message ID\n"));
4441 document
.data
= message
->raw
;
4442 document
.data_length
= message
->raw_length
;
4444 /* Convert local file */
4445 if (argc
== 3 && argv
[2] && *argv
[2])
4446 document
.dmFileDescr
= locale2utf8(argv
[2]);
4448 document
.dmFileDescr
= locale2utf8(argv
[1]);
4449 if (!document
.dmFileDescr
) {
4450 printf(_("Could not convert document name to UTF-8\n"));
4454 printf(_("Loading document from file `%s'...\n"), argv
[1]);
4455 if (mmap_file(argv
[1], &fd
, &document
.data
, &document
.data_length
)) {
4456 free(document
.dmFileDescr
);
4461 retval
= do_convert(&document
);
4464 munmap_file(fd
, document
.data
, document
.data_length
);
4466 free(document
.dmFileDescr
);
4471 static void shi_convert_document_usage(const char *command
) {
4473 "Usage: %s NUMBER\n"
4474 "Submit message document with ordinal NUMBER to authorized conversion.\n"),
4478 "If Czech POINT deposit accepts document, it will return document identifier\n"
4479 "that user is supposed to provide to officer at Czech POINT contact place.\n"
4480 "Currently only PDF 1.3 and higher version files and signed messages are\n"
4482 oprintf(_("See <%s> for more details.\n"), CZPDEPOSIT_URL
);
4486 static int shi_convert_document(int argc
, const char **argv
) {
4487 const struct isds_document
*document
;
4489 if (!argv
|| !argv
[1] || !*argv
[1]) {
4490 shi_convert_document_usage((argv
)?argv
[0]:NULL
);
4494 document
= locate_document_by_ordinal_string(argv
[1]);
4495 if (!document
) return -1;
4497 return do_convert(document
);
4501 static void shi_resign_usage(const char *command
) {
4503 "Usage: %s [FILE]\n"
4504 "Send message or delivery data to re-sign it and to add current time stamp.\n"
4505 "If FILE is specified, message will be loaded from local file. Otherwise\n"
4506 "current message will be sent.\n"),
4510 "Only signed messages or delivery data without time stamp are accepted by\n"
4511 "ISDS. Output re-signed message or delivery data will be loaded.\n"));
4515 static int shi_resign(int argc
, const char **argv
) {
4517 void *data
= NULL
; /* Static */
4518 void *resigned_data
= NULL
; /* Dynamic, stored into message */
4519 size_t data_length
= 0, resigned_data_length
= 0;
4520 struct tm
*valid_to
= NULL
; /* Dynamic */
4524 if (!argv
|| argc
> 3) {
4525 shi_resign_usage((argv
)?argv
[0]:NULL
);
4529 if (NULL
== argv
[1] || !*argv
[1]) {
4530 /* Use current message */
4532 fprintf(stderr
, _("No message or delivery data loaded\n"));
4535 if (!message
->raw
|| !message
->raw_length
) {
4536 fprintf(stderr
, _("Current message or delivery data "
4537 "is missing raw representation\n"));
4540 data
= message
->raw
;
4541 data_length
= message
->raw_length
;
4543 /* Use local file */
4544 printf(_("Loading message or delivery data from file `%s'...\n"),
4546 if (mmap_file(argv
[1], &fd
, &data
, &data_length
)) {
4551 printf(_("Re-signing...\n"));
4552 err
= isds_resign_message(cisds
, data
, data_length
,
4553 &resigned_data
, &resigned_data_length
, &valid_to
);
4554 finish_isds_operation(cisds
, err
);
4557 munmap_file(fd
, data
, data_length
);
4564 print_header_tm(_("New time stamp expires"), valid_to
);
4567 error
= do_load_anything(resigned_data
, resigned_data_length
, BUFFER_MOVE
);
4568 if (error
) free(resigned_data
);
4575 static void shi_print_usage(const char *command
) {
4577 "Usage: %s STRING LENGTH\n"
4578 "Prints STRING into LENGTH columns. Negative LENGTH means not to cut\n"
4579 "overflowing string.\n"
4580 "This should be locale and terminal agnostic.\n"),
4585 static int shi_print(int argc
, const char **argv
) {
4588 if (!argv
|| !argv
[1] || !argv
[2]) {
4589 shi_print_usage((argv
)?argv
[0]:NULL
);
4593 width
= strtol(argv
[2], NULL
, 10);
4594 if (width
< INT_MIN
) {
4596 _("Length argument must not lesser than %d.\n"), INT_MIN
);
4599 if (width
> INT_MAX
) {
4601 _("Length argument must not be greater than %d.\n"), INT_MAX
);
4606 onprint(argv
[1], width
);
4613 static int shi_tokenize(int argc
, const char **argv
) {
4615 if (!argv
) return 0;
4617 for (int i
= 0; i
< argc
; i
++) {
4618 oprintf(_(">%s<\n"), argv
[i
]);
4624 static int shi_quote(int argc
, const char **argv
) {
4625 char *escaped
, *unescaped
;
4627 if (!argv
) return 0;
4629 oprintf(_("Original\tQuoted\tDequoted\n"));
4630 for (int i
= 0; i
< argc
; i
++) {
4631 escaped
= shi_quote_filename((char *) argv
[i
], 0, NULL
);
4632 unescaped
= shi_dequote_filename((char *) argv
[i
], 0);
4633 oprintf(_(">%s<\t>%s<\t>%s<\n"), argv
[i
], escaped
, unescaped
);
4641 static int shi_ask_yesno(int argc
, const char **argv
) {
4644 answer
= shi_ask_yes_no(argv
[1], 1, batch_mode
);
4645 oprintf(_("Answer is %s.\n"), (answer
) ? _("Yes") : _("No"));
4654 /* pclose(pipe), restore ouput to stdout, show error return code */
4655 int wait_for_shell(FILE **pipe
) {
4658 if (pipe
&& *pipe
) {
4659 retval
= pclose(*pipe
);
4664 fprintf(stderr
, _("Exit code of shell command could not "
4665 "be determined\n"));
4666 else if (WIFEXITED(retval
) && WEXITSTATUS(retval
))
4667 printf(_("Shell command exited with code %d\n"),
4668 WEXITSTATUS(retval
));
4669 else if (WIFSIGNALED(retval
))
4670 printf(_("Shell command terminated by signal "
4671 "#%d\n"), WTERMSIG(retval
));
4678 /* Interactive loop */
4679 void shi_loop(void) {
4680 char *command_line
= NULL
;
4681 char **command_argv
= NULL
;
4684 struct command
*command
= NULL
;
4688 oprintf(_("Use `help' command to get list of available commands.\n"));
4690 select_completion(COMPL_COMMAND
);
4694 command_line
= readline((prompt
) ? prompt
: _("shigofumi> "));
4695 /* Remember not parsable commands too to user be able to get back to
4697 if (command_line
&& *command_line
) {
4698 /* TODO: Omit blank lines */
4699 add_history(command_line
);
4702 command_argv
= tokenize(command_line
, &command_argc
, &shell
);
4704 if (command_argv
&& command_argv
[0]) {
4705 command
= find_command(command_argv
[0]);
4708 fprintf(stderr
, _("Command not understood\n"));
4713 pipe
= popen(shell
, "w");
4715 fprintf(stderr
, _("Could not run shell command `%s':"
4716 " %s\n"), shell
, strerror(errno
));
4722 command
->function(command_argc
,
4723 (const char **) command_argv
);
4724 wait_for_shell(&pipe
);
4729 argv_free(command_argv
);
4731 zfree(command_line
);
4736 /* Non-interactive mode. Commands from @lines are processed until any command
4737 * lines remains or no error occurred. First failure terminates processing.
4738 * @lines is sequence of commands separated by '\n' or '\r'. The content is
4739 * modified during this call.
4740 * @return 0 if all command succeed, otherwise non-zero value
4742 int shi_batch(char *lines
) {
4744 char **command_argv
= NULL
;
4747 struct command
*command
= NULL
;
4752 oprintf(_("Batch mode started.\n"));
4754 select_completion(COMPL_COMMAND
);
4756 while (!retval
&& (command_line
= strtok(lines
, "\n\r"))) {
4757 lines
= NULL
; /* strtok(3) requires it for subsequent calls */
4759 printf(_("Processing command: %s\n"), command_line
);
4761 command_argv
= tokenize(command_line
, &command_argc
, &shell
);
4763 if (command_argv
&& command_argv
[0]) {
4764 command
= find_command(command_argv
[0]);
4767 fprintf(stderr
, _("Command not understood\n"));
4773 pipe
= popen(shell
, "w");
4775 fprintf(stderr
, _("Could not run shell command `%s':"
4776 " %s\n"), shell
, strerror(errno
));
4782 retval
= command
->function(command_argc
,
4783 (const char **) command_argv
);
4784 if (wait_for_shell(&pipe
)) retval
= -1;
4789 argv_free(command_argv
);
4794 fprintf(stderr
, _("Command failed!\n"));
4799 #define COMMON_COMMANDS \
4800 { "accept", shi_accept_message, N_("accept commercial message"), \
4801 shi_accept_message_usage, ARGTYPE_MSGID }, \
4802 { "box", shi_box, N_("show current box details"), NULL, \
4804 { "boxlist", shi_boxlist, N_("get list of all boxes"), shi_boxlist_usage, \
4806 { "cache", shi_cache, N_("show cache details"), NULL, \
4808 { "cd", shi_chdir, N_("change working directory"), shi_chdir_usage, \
4810 { "commercialcredit", shi_commercialcredit, N_("get credit details"), \
4811 shi_commercialcredit_usage, ARGTYPE_BOXID }, \
4812 { "commercialreceiving", shi_commercialreceiving, \
4813 N_("manipulate commercial receiving box status"), \
4814 shi_commercialreceiving_usage, ARGTYPE_BOXID }, \
4815 { "commercialsending", shi_commercialsending, \
4816 N_("manipulate commercial sending box status"), \
4817 shi_commercialsending_usage, ARGTYPE_BOXID }, \
4818 { "compose", shi_compose, N_("compose a message"), shi_compose_usage, \
4820 { "convert", shi_convert_file_or_message, \
4821 N_("submit local document for authorized conversion"), \
4822 shi_convert_file_or_message_usage, ARGTYPE_FILE }, \
4823 { "copying", shi_copying, N_("show this program licence excerpt"), NULL, \
4825 { "debug", shi_debug, N_("set debugging"), shi_debug_usage, \
4827 { "delete", shi_delete_message, N_("delete message from storage"), \
4828 shi_delete_message_usage, ARGTYPE_MSGID }, \
4829 { "delivery", shi_delivery, N_("get message delivery details"), \
4830 shi_delivery_usage, ARGTYPE_MSGID }, \
4831 { "findbox", shi_find_box, N_("search for a box"), shi_find_box_usage, \
4833 { "hash", shi_hash, N_("query ISDS for message hash"), \
4834 shi_hash_usage, ARGTYPE_MSGID }, \
4835 { "help", shi_help, N_("describe commands"), shi_help_usage, \
4836 ARGTYPE_COMMAND }, \
4837 { "load", shi_load_anything, \
4838 N_("load message or message delivery details from local file"), \
4839 shi_load_anything_usage, ARGTYPE_FILE }, \
4840 { "login", shi_login, N_("log into ISDS"), shi_login_usage, \
4842 { "lsi", shi_list_incoming, N_("list received messages"), \
4843 shi_list_incoming_usage, ARGTYPE_NONE }, \
4844 { "lso", shi_list_outgoing, N_("list sent messages"), \
4845 shi_list_outgoing_usage, ARGTYPE_NONE }, \
4846 { "msgi", shi_incoming_message, N_("get incoming message"), \
4847 shi_incoming_message_usage, ARGTYPE_MSGID }, \
4848 { "msgo", shi_outgoing_message, N_("get outgoing message"), \
4849 shi_outgoing_message_usage, ARGTYPE_MSGID }, \
4850 { "passwd", shi_passwd, N_("manipulate user password"), shi_passwd_usage, \
4852 { "pwd", shi_pwd, N_("print working directory"), NULL, ARGTYPE_NONE }, \
4853 { "quit", shi_quit, N_("exit shigofumi"), NULL, ARGTYPE_NONE }, \
4854 { "read", shi_read_message, N_("mark message as read"), \
4855 shi_read_message_usage, ARGTYPE_MSGID }, \
4856 { "resign", shi_resign, N_("re-sign message or delivery data"), \
4857 shi_resign_usage, ARGTYPE_FILE }, \
4858 { "set", shi_settings, N_("show settings"), NULL, ARGTYPE_NONE }, \
4859 { "sender", shi_message_sender, N_("get message sender"), \
4860 shi_message_sender_usage, ARGTYPE_MSGID }, \
4861 { "statbox", shi_stat_box, N_("get status of a box"), shi_stat_box_usage, \
4863 { "user", shi_user, N_("show current user details"), NULL, \
4865 { "users", shi_users, N_("show box users"), shi_users_usage, \
4867 { "version", shi_version, N_("show version of this program"), NULL, \
4869 { NULL, NULL, NULL, NULL, ARGTYPE_NONE }
4871 struct command base_commands
[] = {
4873 { "askyesno", shi_ask_yesno
, N_("demonstrate yes-no question"), NULL
,
4875 { "quote", shi_quote
, N_("demonstrate argument escaping"), NULL
,
4877 { "print", shi_print
, N_("print string into given width"),
4878 shi_print_usage
, ARGTYPE_NONE
},
4879 { "tokenize", shi_tokenize
, N_("demonstrate arguments tokenization"), NULL
,
4885 struct command message_commands
[] = {
4886 { "authenticate", shi_authenticate
, N_("check message authenticity"),
4887 NULL
, ARGTYPE_NONE
},
4888 { "cat", shi_cat_message
, N_("show raw current message"),
4889 shi_cat_message_usage
, ARGTYPE_NONE
},
4890 { "catdoc", shi_cat_document
, N_("show raw document"),
4891 shi_cat_document_usage
, ARGTYPE_DOCID
},
4892 { "convertdoc", shi_convert_document
,
4893 N_("submit document of current message for authorized conversion"),
4894 shi_convert_document_usage
, ARGTYPE_DOCID
},
4895 { "dump", shi_dump_message
, N_("dump current message structure"),
4896 NULL
, ARGTYPE_NONE
},
4897 { "opendoc", shi_open_document
, N_("open document using external utility"),
4898 shi_open_document_usage
, ARGTYPE_DOCID
},
4899 { "savestamp", shi_save_stamp
,
4900 N_("save time stamp of current message into local file"),
4901 shi_save_stamp_usage
, ARGTYPE_FILE
},
4902 { "savedoc", shi_save_document
,
4903 N_("save document of current message into local file"),
4904 shi_save_document_usage
, ARGTYPE_FILE
},
4905 { "save", shi_save_message
, N_("save current message into local file"),
4906 shi_save_message_usage
, ARGTYPE_FILE
},
4907 { "show", shi_show_message
, N_("show current message"), NULL
,
4909 { "verify", shi_verify
, N_("verify current message hash"), NULL
,
4914 struct command list_commands
[] = {
4915 { "show", shi_show_list
, N_("show current message list or list item details"),
4916 shi_show_list_usage
, ARGTYPE_MSGID
},
4920 #undef COMMON_COMMANDS
4923 static void main_version(void) {
4928 shi_copying(0, NULL
);
4932 static void main_usage(const char *command
) {
4934 "Usage: %s [OPTION...]\n"
4935 "Access ISDS, process local data box messages or delivery details, submit\n"
4936 "document to authorized conversion.\n"
4939 " -c FILE use the FILE as configuration file instead of ~/%s\n"
4940 " -e COMMANDS execute COMMANDS (new line separated) and exit\n"
4941 " -V show version info and exit\n"
4943 (command
) ? command
: "shigofumi",
4948 int main(int argc
, char **argv
) {
4950 char *config_file
= NULL
;
4951 char *batch_commands
= NULL
;
4952 int retval
= EXIT_SUCCESS
;
4954 setlocale(LC_ALL
, "");
4956 /* Initialize gettext */
4957 bindtextdomain(PACKAGE
, LOCALEDIR
);
4958 textdomain(PACKAGE
);
4961 /* Default output */
4964 /* Parse arguments */
4966 while ((opt
= getopt(argc
, (char * const *)argv
, "c:e:V")) != -1) {
4969 config_file
= optarg
;
4972 batch_commands
= optarg
;
4976 shi_exit(EXIT_SUCCESS
);
4979 main_usage((argv
[0]) ? basename(argv
[0]): NULL
);
4980 shi_exit(EXIT_FAILURE
);
4985 if (shi_init(config_file
)) {
4986 shi_exit(EXIT_FAILURE
);
4989 /*shi_login(NULL);*/
4991 if (batch_commands
) {
4992 if (shi_batch(batch_commands
))
4993 retval
= EXIT_FAILURE
;