1 #define _XOPEN_SOURCE 600
6 #include <readline/readline.h>
7 #include <readline/history.h>
17 #include "shigofumi.h"
18 #include "completition.h"
23 #define CONFIG_FILE ".shigofumirc"
24 #define CONFIG_SERVER "base_url"
25 #define CONFIG_USERNAME "username"
26 #define CONFIG_PASSWORD "password"
27 #define CONFIG_DEBUGLEVEL "debug_level"
28 #define CONFIG_VERIFYSERVER "verify_server"
29 #define CONFIG_CAFILE "ca_file"
30 #define CONFIG_CADIRECTORY "ca_directory"
31 #define CONFIG_CRLFILE "crl_file"
32 #define CONFIG_TIMEOUT "timeout"
33 #define CONFIG_LOGFACILITIES "log_facilities"
34 #define CONFIG_LOGFILE "log_file"
35 #define CONFIG_LOGLEVEL "log_level"
36 #define CONFIG_MARKMESSAGEREAD "mark_message_read"
37 #define CONFIG_NORMALIZEMIMETYPE "normalize_mime_type"
38 #define CONFIG_OVERWRITEFILES "overwrite_files"
39 #define CZPDEPOSIT_URL "https://www.czechpoint.cz/uschovna/"
45 cfg_opt_t configuration_syntax
[] = {
46 CFG_STR(CONFIG_SERVER
, (char *)isds_locator
, CFGF_NONE
),
47 CFG_STR(CONFIG_USERNAME
, NULL
, CFGF_NODEFAULT
),
48 CFG_STR(CONFIG_PASSWORD
, NULL
, CFGF_NODEFAULT
),
49 /*CFG_STR(CONFIG_DEBUGLEVEL, NULL, CFGF_NODEFAULT),*/
50 CFG_BOOL(CONFIG_VERIFYSERVER
, cfg_true
, CFGF_NONE
),
51 CFG_STR(CONFIG_CAFILE
, NULL
, CFGF_NODEFAULT
),
52 CFG_STR(CONFIG_CADIRECTORY
, NULL
, CFGF_NODEFAULT
),
53 CFG_STR(CONFIG_CRLFILE
, NULL
, CFGF_NODEFAULT
),
54 CFG_INT(CONFIG_TIMEOUT
, TIMEOUT
, CFGF_NONE
),
55 CFG_STR_LIST(CONFIG_LOGFACILITIES
, "{none}", CFGF_NONE
),
56 CFG_STR(CONFIG_LOGFILE
, NULL
, CFGF_NODEFAULT
),
57 CFG_INT(CONFIG_LOGLEVEL
, LOG_LEVEL
, CFGF_NONE
),
58 CFG_BOOL(CONFIG_MARKMESSAGEREAD
, cfg_false
, CFGF_NONE
),
59 CFG_BOOL(CONFIG_NORMALIZEMIMETYPE
, cfg_true
, CFGF_NONE
),
60 CFG_BOOL(CONFIG_OVERWRITEFILES
, cfg_true
, CFGF_NONE
),
71 struct command (*commands
)[] = NULL
;
74 struct isds_ctx
*cisds
= NULL
;
76 char *username
= NULL
;
77 char *password
= NULL
;
78 struct isds_list
*boxes
= NULL
;
79 struct isds_message
*message
= NULL
;
80 _Bool messages_are_outgoing
= 0;
81 struct isds_list
*messages
= NULL
;
82 unsigned long int total_messages
= 0;
83 struct isds_ctx
*czechpoint
= NULL
;
85 static void discard_credentials(void) {
92 /* Do the cleanup and exit */
93 static void shi_exit(int exit_code
) {
95 discard_credentials();
96 isds_list_free(&boxes
);
97 isds_message_free(&message
);
98 isds_list_free(&messages
);
101 printf(_("Logging out...\n"));
103 isds_ctx_free(&cisds
);
105 isds_ctx_free(&czechpoint
);
109 cfg_free(configuration
);
118 /* Set prompt. if @format is NULL, switch to default prompt */
119 static void set_prompt(const char *format
, ...) {
123 va_start(ap
, format
);
124 shi_vasprintf(&buffer
, format
, ap
);
128 shi_asprintf(&prompt
, _("%s> "), buffer
);
137 prompt
= strdup(_("> "));
145 static int shi_load_configuration(const char *config_file
) {
146 char *config_name
= NULL
;
149 /* Get config file */
151 config_name
= (char *) config_file
;
153 if (-1 == shi_asprintf(&config_name
, "%s/%s", getenv("HOME"),
155 fprintf(stderr
, _("Could not build configuration file name\n"));
160 /* Parse configuration */
161 configuration
= cfg_init(configuration_syntax
, CFGF_NONE
);
162 ret
= cfg_parse(configuration
, config_name
);
164 if (ret
== CFG_FILE_ERROR
) {
166 _("Error while opening configuration file `%s': %s\n"),
167 config_name
, strerror(errno
));
169 fprintf(stderr
, _("Error while parsing configuration file `%s'\n"),
172 printf(_("Using default configuration\n"));
175 if (config_name
!= config_file
) free(config_name
);
180 static void finish_isds_operation(struct isds_ctx
*ctx
, isds_error err
) {
181 shi_progressbar_finish();
183 if (isds_long_message(ctx
))
184 printf(_("Error occurred: %s: %s\n"), isds_strerror(err
),
185 isds_long_message(ctx
));
187 printf(_("Error occurred: %s\n"), isds_strerror(err
));
192 void logger(isds_log_facility facility
, isds_log_level level
,
193 const char *message
, int length
, void *data
) {
195 ssize_t written
, left
= length
;
198 fd
= *((int *) data
);
199 /*printf("\033[32mLOG(%02d,%02d): ", facility, level);
200 printf("%.*s", length, message);
204 written
= write(fd
, message
+ length
- left
, left
);
207 _("Could not save log message into log file: %s\n"
208 "Log message discarded!\n"),
219 /* Redirect ISDS log to file if @file is not NULL. */
220 static int do_log_to_file(const char *file
) {
222 logger_fd
= open_file_for_writing(file
, 0, 1);
223 if (logger_fd
== -1) {
224 fprintf(stderr
, _("Could not redirect ISDS log to file `%s'\n"),
228 isds_set_log_callback(logger
, &logger_fd
);
234 /* Add log facility based on its name. */
235 static int add_log_facility(isds_log_facility
*facilities
, const char *name
) {
236 if (!facilities
) return -1;
238 if (!strcmp(name
, "none")) *facilities
|= ILF_NONE
;
239 else if (!strcmp(name
, "http")) *facilities
|= ILF_HTTP
;
240 else if (!strcmp(name
, "soap")) *facilities
|= ILF_SOAP
;
241 else if (!strcmp(name
, "isds")) *facilities
|= ILF_ISDS
;
242 else if (!strcmp(name
, "file")) *facilities
|= ILF_FILE
;
243 else if (!strcmp(name
, "sec")) *facilities
|= ILF_SEC
;
244 else if (!strcmp(name
, "xml")) *facilities
|= ILF_XML
;
245 else if (!strcmp(name
, "all")) *facilities
|= ILF_ALL
;
247 printf(_("%s: Unknown log facility\n"), name
);
255 /* Save log facility into confuse configuration */
256 static void save_log_facility(int level
) {
257 cfg_setlist(configuration
, CONFIG_LOGFACILITIES
, 0);
259 if (level
== ILF_ALL
) {
260 cfg_addlist(configuration
, CONFIG_LOGFACILITIES
, 1, "all");
263 if (level
== ILF_NONE
) {
264 cfg_addlist(configuration
, CONFIG_LOGFACILITIES
, 1, "none");
267 if (level
& ILF_HTTP
)
268 cfg_addlist(configuration
, CONFIG_LOGFACILITIES
, 1, "http");
269 if (level
& ILF_SOAP
)
270 cfg_addlist(configuration
, CONFIG_LOGFACILITIES
, 1, "soap");
271 if (level
& ILF_ISDS
)
272 cfg_addlist(configuration
, CONFIG_LOGFACILITIES
, 1, "isds");
273 if (level
& ILF_FILE
)
274 cfg_addlist(configuration
, CONFIG_LOGFACILITIES
, 1, "file");
276 cfg_addlist(configuration
, CONFIG_LOGFACILITIES
, 1, "sec");
278 cfg_addlist(configuration
, CONFIG_LOGFACILITIES
, 1, "xml");
282 /* Clamp long int to unsigned int */
283 static unsigned int normalize_timeout(long int raw
) {
285 printf(_("Configured network timeout is less then 0. Clamped to 0.\n"));
288 if (raw
> UINT_MAX
) {
289 printf(_("Configured network timeout is greater then %1$u. "
290 "Clamped to %1$u.\n"), UINT_MAX
);
293 return (unsigned int) raw
;
297 /* Clamp long int to <0;100> */
298 static unsigned int normalize_log_level(long int raw
) {
300 printf(_("Configured log level is less then 0. Clamped to 0.\n"));
304 printf(_("Configured log level is greater then %1$u. "
305 "Clamped to %1$u.\n"), ILL_ALL
);
308 if (raw
> UINT_MAX
) {
309 printf(_("Configured log level is greater then %1$u. "
310 "Clamped to %1$u.\n"), UINT_MAX
);
313 return (unsigned int) raw
;
317 static int shi_init(const char *config_file
) {
320 unsigned int timeout
, log_level
;
321 isds_log_facility log_facility
= ILF_NONE
;
323 printf(_("This is Shigofumi, an ISDS client. Have a nice e-government.\n"));
325 /* Do not permute arguments in getopt() */
326 if (setenv("POSIXLY_CORRECT", "", 1)) {
328 _("Could not set POSIXLY_CORRECT environment variable\n"));
332 /* Load configuration */
333 if (shi_load_configuration(config_file
))
335 timeout
= normalize_timeout(cfg_getint(configuration
, CONFIG_TIMEOUT
));
336 log_level
= normalize_log_level(cfg_getint(configuration
, CONFIG_LOGLEVEL
));
339 rl_readline_name
= "shigofumi";
340 rl_filename_quote_characters
= "\\ >";
341 rl_filename_quoting_function
= shi_quote_filename
;
342 rl_filename_dequoting_function
= shi_dequote_filename
;
343 rl_char_is_quoted_p
= shi_char_is_quoted
;
345 /* Initialize ISDS */
348 fprintf(stderr
, _("Could not initialize libisds library: %s\n"),
353 /* Set ISDS logging */
354 value
= cfg_getstr(configuration
, CONFIG_LOGFILE
);
355 if (do_log_to_file(value
))
357 for (int i
= 0; i
< cfg_size(configuration
, CONFIG_LOGFACILITIES
); i
++) {
358 if (add_log_facility(&log_facility
,
359 cfg_getnstr(configuration
, CONFIG_LOGFACILITIES
, i
)))
363 isds_set_logging(log_facility
, log_level
);
365 /* Set ISDS context up */
366 cisds
= isds_ctx_create();
368 fprintf(stderr
, _("Could not create ISDS context\n"));
371 err
= isds_set_timeout(cisds
, timeout
);
373 fprintf(stderr
, _("Could not set ISDS network timeout: %s\n"),
376 err
= isds_set_progress_callback(cisds
, shi_progressbar
, NULL
);
378 fprintf(stderr
, _("Could not register network progress bar: %s: %s\n"),
379 isds_strerror(err
), isds_long_message(cisds
));
381 err
= isds_set_opt(cisds
, IOPT_NORMALIZE_MIME_TYPE
,
382 cfg_getbool(configuration
, CONFIG_NORMALIZEMIMETYPE
));
385 cfg_getbool(configuration
, CONFIG_NORMALIZEMIMETYPE
) ?
386 _("Could not enable MIME type normalization: %s: %s\n") :
387 _("Could not disable MIME type normalization: %s: %s\n"),
388 isds_strerror(err
), isds_long_message(cisds
));
390 if (!cfg_getbool(configuration
, CONFIG_VERIFYSERVER
)) {
391 printf(_("Warning: Shigofumi disabled server identity verification "
392 "on user request!\n"));
393 err
= isds_set_opt(cisds
, IOPT_TLS_VERIFY_SERVER
, 0);
396 _("Could not disable server identity verification: "
398 isds_strerror(err
), isds_long_message(cisds
));
401 if ((value
= cfg_getstr(configuration
, CONFIG_CAFILE
))) {
402 err
= isds_set_opt(cisds
, IOPT_TLS_CA_FILE
, value
);
405 _("Could not set file with CA certificates: %s: %s: %s\n"),
406 value
, isds_strerror(err
), isds_long_message(cisds
));
409 if ((value
= cfg_getstr(configuration
, CONFIG_CADIRECTORY
))) {
410 err
= isds_set_opt(cisds
, IOPT_TLS_CA_DIRECTORY
, value
);
413 _("Could not set directory with CA certificates: "
415 value
, isds_strerror(err
), isds_long_message(cisds
));
418 if ((value
= cfg_getstr(configuration
, CONFIG_CRLFILE
))) {
419 err
= isds_set_opt(cisds
, IOPT_TLS_CRL_FILE
, value
);
421 fprintf(stderr
, _("Could not set file with CRL: %s: %s: %s\n"),
422 value
, isds_strerror(err
), isds_long_message(cisds
));
427 /* Set Czech POINT context up */
428 czechpoint
= isds_ctx_create();
430 fprintf(stderr
, _("Could not create Czech POINT context\n"));
433 err
= isds_set_timeout(czechpoint
, timeout
);
435 fprintf(stderr
, _("Could not set Czech POINT network timeout: %s\n"),
438 err
= isds_set_progress_callback(czechpoint
, shi_progressbar
, NULL
);
440 fprintf(stderr
, "Could not register network progress bar: %s: %s\n",
441 isds_strerror(err
), isds_long_message(cisds
));
448 static int shi_quit(int argc
, const char **argv
) {
449 shi_exit(EXIT_SUCCESS
);
455 static void shi_help_usage(const char *command
) {
457 "Usage: %s [COMMAND]\n"
458 "Show COMMAND manual or list of currently available commands.\n"
464 static int shi_help(int argc
, const char **argv
) {
465 size_t command_width
= 14;
469 printf(_("No command is available\n"));
473 if (argc
== 2 && argv
[1] && *argv
[1]) {
474 /* Show usage for given command */
475 for (i
= 0; (*commands
)[i
].name
; i
++) {
476 if (!strcmp((*commands
)[i
].name
, argv
[1])) {
477 if ((*commands
)[i
].usage
)
478 (*commands
)[i
].usage((*commands
)[i
].name
);
479 else if ((*commands
)[i
].description
) {
480 fhprint(stdout
, (*commands
)[i
].name
, command_width
);
481 printf(" %s\n", _((*commands
)[i
].description
));
484 printf(_("%s: %s: Command description not defined\n"),
489 printf(_("%s: %s: No such command exists\n"), argv
[0], argv
[1]);
493 /* Or list all commands */
494 printf(_("Following commands are available:\n"));
495 for (i
= 0; (*commands
)[i
].name
; i
++) {
496 fhprint(stdout
, (*commands
)[i
].name
, command_width
);
497 printf(" %s\n", _((*commands
)[i
].description
));
504 static void show_version(void) {
505 char *libisds_version
= isds_version();
507 printf(_("This is Shigofumi version %s.\n"), PACKAGE_VERSION
);
513 rl_library_version
, libisds_version
);
514 free(libisds_version
);
518 static int shi_version(int argc
, const char **argv
) {
523 "It's a shigofumi. A letter delivered from the afterlife. (Fumika)\n"
524 "A message can not be delivered to dead person. (ISDS specification)\n"
525 "Virtual and real world. They can be compatible. (Program author)\n"
531 static int shi_copying(int argc
, const char **argv
) {
533 "This is Shigofumi, an ISDS client.\n"
534 "Copyright (C) 2010 Petr Pisar\n"
536 "This program is free software: you can redistribute it and/or modify\n"
537 "it under the terms of the GNU General Public License as published by\n"
538 "the Free Software Foundation, either version 3 of the License, or\n"
539 "(at your option) any later version.\n"
541 "This program is distributed in the hope that it will be useful,\n"
542 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
543 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
544 "GNU General Public License for more details.\n"
546 "You should have received a copy of the GNU General Public License\n"
547 "along with this program. If not, see <http://www.gnu.org/licenses/>.\n"
553 static int shi_cache(int argc
, const char **argv
) {
554 const struct isds_list
*item
;
558 for (item
= boxes
, i
= 0; item
; item
= item
->next
, i
++);
560 "Cached box list: %zu\n"),
566 "Cached message list:\n"
568 "\tMessages: %'lu\n"),
569 (messages_are_outgoing
) ? _("Outgoing") : _("Incoming"),
574 printf(_("Cached message: %s\n"),
575 (message
->envelope
&& message
->envelope
->dmID
) ?
576 message
->envelope
->dmID
: _("<Unknown ID>"));
583 static void shi_chdir_usage(const char *command
) {
585 "Usage: %s [DIRECTORY]\n"
586 "Change working directory to DIRECTORY.\n"
587 "If no DIRECTORY is supplied, HOME directory will be used.\n"),
592 static int shi_chdir(int argc
, const char **argv
) {
593 const char *directory
= NULL
;
595 if (!argv
|| argc
> 2) {
596 shi_chdir_usage((argv
) ? argv
[0] : NULL
);
600 if (argc
== 2 && argv
[1] && *argv
[1])
603 directory
= getenv("HOME");
605 printf("Environment variable HOME does not exist\n");
609 if (chdir(directory
)) {
610 printf(_("Could not change working directory: %s: %s\n"), directory
,
619 static int shi_pwd(int argc
, const char **argv
) {
620 char *buffer
= NULL
, *newbuffer
;
623 while (length
+= 1024) {
624 newbuffer
= realloc(buffer
, length
);
626 printf(_("Error: Not enough memory\n"));
632 if (getcwd(buffer
, length
)) {
633 printf("%s\n", buffer
);
639 printf(_("Error: Current directory string is too long\n"));
646 static int do_login(void) {
650 printf(_("Unattended mode detected. "
651 "Make sure credentials have been preset.\n"));
653 printf(_("You are going to insert credentials for your account.\n"
654 "Leave blank line to choose default value.\n"));
656 select_completition(COMPL_NONE
);
658 /* Ask for server base URL */
659 shi_replace_string(&server
, _("Input ISDS base URL: "),
660 cfg_getstr(configuration
, CONFIG_SERVER
), batch_mode
);
662 /* Ask for user name */
663 shi_replace_string(&username
, _("Input ISDS user name: "),
664 cfg_getstr(configuration
, CONFIG_USERNAME
), batch_mode
);
667 password = ask_for_password(_("Input ISDS password: "));
670 fprintf(stderr, _("Could not read ISDS password\n"));
671 shi_exit(EXIT_FAILURE);
674 shi_replace_password(&password
, _("Input ISDS password: "),
675 cfg_getstr(configuration
, CONFIG_PASSWORD
), batch_mode
);
677 select_completition(COMPL_COMMAND
);
680 printf(_("Logging in...\n"));
681 err
= isds_login(cisds
, server
, username
, password
, NULL
);
682 finish_isds_operation(cisds
, err
);
684 printf(_("Log-in failed\n"));
688 printf(_("Logged in.\n"));
693 static struct isds_DbOwnerInfo
*do_box(void) {
695 struct isds_DbOwnerInfo
*box
= NULL
;
697 printf(_("Getting box details you are logged in...\n"));
698 err
= isds_GetOwnerInfoFromLogin(cisds
, &box
);
699 finish_isds_operation(cisds
, err
);
705 static int shi_box(int argc
, const char **argv
) {
706 struct isds_DbOwnerInfo
*box
= NULL
;
711 format_DbOwnerInfo(box
);
713 isds_DbOwnerInfo_free(&box
);
718 /* Get info about box with @id.
719 * @id is UTF-8 encoded
720 * Return NULL in case of error, otherwise box description that caller must
722 static struct isds_DbOwnerInfo
*stat_box(const char *id
) {
724 struct isds_DbOwnerInfo criteria
;
725 struct isds_list
*boxes
= NULL
, *item
;
726 struct isds_DbOwnerInfo
*box
= NULL
;
727 char *id_locale
= NULL
;
729 if (!id
|| !*id
) return NULL
;
731 id_locale
= utf82locale(id
);
732 memset(&criteria
, 0, sizeof(criteria
));
733 criteria
.dbID
= (char *) id
;
735 printf(_("Getting details about box with ID `%s'...\n"), id_locale
);
736 err
= isds_FindDataBox(cisds
, &criteria
, &boxes
);
737 finish_isds_operation(cisds
, err
);
740 for(item
= boxes
; item
; item
= item
->next
) {
741 if (!item
->data
) continue;
744 printf(_("Error: More boxes match ID `%s'\n"), id_locale
);
748 box
= (struct isds_DbOwnerInfo
*) item
->data
;
755 isds_list_free(&boxes
);
761 static void shi_commercial_usage(const char *command
) {
763 "Usage: %s [-0|-1] [BOX_ID]\n"
764 "Manipulate commercial receiving box status.\n"
765 " -O switch off receiving of commercial messages\n"
766 " -1 switch on receiving of commercial messages\n"
767 " BOX_ID affects box with ID BOX_ID; default is box you are logged in\n"
768 "If no option is given, show current commercial receiving status.\n"),
773 /* Manipulate commercial receiving box status */
774 static int shi_commercial(int argc
, const char **argv
) {
776 struct isds_DbOwnerInfo
*box
= NULL
;
779 char *box_id
= NULL
, *box_id_locale
= NULL
;
784 while ((opt
= getopt(argc
, (char * const *)argv
, "01")) != -1) {
793 shi_commercial_usage((argv
)?argv
[0]:NULL
);
797 if (optind
+ 1 < argc
) {
798 printf(_("Bad invocation\n"));
799 shi_commercial_usage((argv
)?argv
[0]:NULL
);
803 if (!argv
[optind
] || !*argv
[optind
]) {
804 /* Get current box ID */
806 if (!box
|| !box
->dbID
|| !*box
->dbID
) {
807 isds_DbOwnerInfo_free(&box
);
808 printf(_("Could not get current box ID\n"));
811 box_id
= box
->dbID
; static_box_id
= 1;
812 box_id_locale
= utf82locale(box_id
);
814 /* Box ID supplied as argument */
815 box_id_locale
= (char *) argv
[optind
];
816 box_id
= locale2utf8(box_id_locale
); static_box_id
= 0;
818 printf(_("Could not convert box ID `%s' to UTF-8\n"),
825 if (!box
) box
= stat_box(box_id
);
827 printf(_("Could not get details about box ID `%s'\n"),
833 printf(_("Commercial receiving status of box `%s': "), box_id_locale
);
834 if (!box
->dbOpenAddressing
)
835 printf(_("Unknown\n"));
836 else if (*box
->dbOpenAddressing
)
837 printf(_("Positive\n"));
839 printf(_("Negative\n"));
841 char *refnumber
= NULL
;
843 _("Switching `%s' box commercial receiving on...\n"):
844 _("Switching `%s' box commercial receiving off...\n"),
846 err
= isds_switch_commercial_receiving(cisds
, box_id
, action
,
848 finish_isds_operation(cisds
, err
);
851 char *refnumber_locale
= utf82locale(refnumber
);
852 printf(_("Commercial receiving status successfully changed. "
853 "Assigned reference number: %s\n"),
856 printf(_("Commercial receiving status has not been changed.\n"));
863 if (!static_box_id
) free(box_id
);
864 else free(box_id_locale
);
865 isds_DbOwnerInfo_free(&box
);
870 static int shi_user(int argc
, const char **argv
) {
872 struct isds_DbUserInfo
*user
= NULL
;
874 printf(_("Getting user details you are logged as...\n"));
875 err
= isds_GetUserInfoFromLogin(cisds
, &user
);
876 finish_isds_operation(cisds
, err
);
879 format_DbUserInfo(user
);
881 isds_DbUserInfo_free(&user
);
886 static void shi_users_usage(const char *command
) {
889 "Get list of users having access to box with BOX_ID.\n"),
894 static int shi_users(int argc
, const char **argv
) {
896 struct isds_list
*users
= NULL
, *item
;
899 if (!argv
|| !argv
[1] || !*argv
[1]) {
900 shi_users_usage((argv
)?argv
[0]:NULL
);
904 printf(_("Getting users of box with ID `%s'...\n"), argv
[1]);
905 err
= isds_GetDataBoxUsers(cisds
, argv
[1], &users
);
906 finish_isds_operation(cisds
, err
);
909 for (item
= users
, ordinar
= 0; item
; item
=item
->next
) {
910 if (!item
->data
) continue;
912 printf(_("\n* User #%d:\n"), ordinar
);
913 format_DbUserInfo(item
->data
);
916 printf(_("Empty list of users returned.\n"));
918 isds_list_free(&users
);
923 static int show_password_expiration(void) {
925 struct timeval
*expiration
= NULL
;
927 err
= isds_get_password_expiration(cisds
, &expiration
);
928 finish_isds_operation(cisds
, err
);
930 fprintf(stderr
, "Could not get password expiration time\n");
934 print_header_timeval(_("Your password expires at"), expiration
);
940 /* Change password in ISDS */
941 static int do_passwd(void) {
942 char *old_password
= NULL
;
943 char *new_password
= NULL
;
944 char *new_password2
= NULL
;
945 isds_error err
= IE_ERROR
;
948 select_completition(COMPL_NONE
);
951 "You are going to change your password. If you don't want to change your\n"
952 "password, insert empty string or EOF.\n"
954 "You will be asked for your current (old) password and then for new password.\n"
955 "ISDS forces some criteria new password must fulfill. Current rules are:\n"
956 "\tLength: minimal 8, maximal 32 characters\n"
957 "\tMust contain at least: 1 upper case letter, 1 lower case letter, 1 digit\n"
958 "\tAllowed alphabet: [a-z][A-Z][0-9][!#$%%&()*+,-.:=?@[]_{}|~]\n"
959 "\tMust differ from last 255 passwords\n"
960 "\tMust not contain user ID\n"
961 "\tMust not contain sequence of three or more same characters\n"
962 "\tMust not start with `qwert', `asdgf', or `12345'\n"
963 "Finally, you must repeat your new password to avoid mistakes.\n"
964 "After password change will be confirmed, you must log in again as password\n"
965 "is transmitted to server on each request.\n"
968 old_password
= ask_for_password(_("Old password: "));
969 if (!old_password
|| *old_password
== '\0') {
970 fprintf(stderr
, _("No password supplied\n"));
974 new_password
= ask_for_password(_("New password: "));
975 if (!new_password
|| *new_password
== '\0') {
976 fprintf(stderr
, _("No password supplied\n"));
980 new_password2
= ask_for_password(_("Repeat new password: "));
981 if (!new_password2
|| new_password2
== '\0') {
982 fprintf(stderr
, _("No password supplied\n"));
986 if (strcmp(new_password
, new_password2
)) {
987 fprintf(stderr
, _("New passwords differ\n"));
991 printf(_("Changing password...\n"));
992 err
= isds_change_password(cisds
, old_password
, new_password
);
993 finish_isds_operation(cisds
, err
);
995 printf(_("Password change failed\n"));
998 printf(_("Password HAS been successfully changed.\n"));
1004 printf(_("Password has NOT been changed!\n"));
1009 free(new_password2
);
1012 select_completition(COMPL_COMMAND
);
1015 "Remember, ISDS password has limited life time.\n"));
1020 static void shi_passwd_usage(const char *command
) {
1023 "Manipulate user password or change it if no option given.\n"
1026 " -S show password expiration time\n"
1031 static int shi_passwd(int argc
, const char **argv
) {
1035 while ((opt
= getopt(argc
, (char * const *)argv
, "S")) != -1) {
1038 return show_password_expiration();
1040 shi_passwd_usage(argv
[0]);
1044 if (optind
!= argc
|| argc
> 1) {
1045 printf(_("Bad invocation\n"));
1046 shi_passwd_usage((argv
)?argv
[0]:NULL
);
1054 static int shi_login(int argc
, const char **argv
) {
1055 discard_credentials();
1056 if (do_login()) return -1;
1057 cfg_setstr(configuration
, CONFIG_SERVER
, server
);
1058 cfg_setstr(configuration
, CONFIG_USERNAME
, username
);
1059 cfg_setstr(configuration
, CONFIG_PASSWORD
, password
);
1060 show_password_expiration();
1065 static void shi_debug_usage(const char *command
) {
1067 "Usage: %s -l LEVEL [-f FACILITY...] [{-e | -o FILE}]\n"
1068 "Debug FACILITIES on LEVEL.\n"
1070 "-l LEVEL set log level, valid interval <%d,%d>, default is %d\n"
1071 " %d is no logging, %d critical, %d errors,\n"
1072 " %d warnings, %d info, %d debug, %d all\n"
1073 "-f FACILITY debug only given facility, repeat this option to debug\n"
1074 " more facilities; valid values: none, http, soap, isds,\n"
1075 " file, sec, xml, all; default is none\n"
1076 "-e write debug log into stderr\n"
1077 "-o FILE append debug log to FILE\n"
1080 ILL_NONE
, ILL_ALL
, ILL_NONE
,
1081 ILL_NONE
, ILL_CRIT
, ILL_ERR
, ILL_WARNING
,
1082 ILL_INFO
, ILL_DEBUG
, ILL_ALL
);
1085 static int shi_debug(int argc
, const char **argv
) {
1087 int log_level
= ILL_NONE
;
1088 isds_log_facility log_facility
= ILF_NONE
;
1090 _Bool close_log
= 0;
1093 while ((opt
= getopt(argc
, (char * const *)argv
, "l:f:eo:")) != -1) {
1096 log_level
= normalize_log_level(atoi(optarg
));
1099 if (add_log_facility(&log_facility
, optarg
)) return -1;
1107 shi_debug_usage(argv
[0]);
1111 if (optind
== 1 || optind
!= argc
) {
1112 printf(_("Bad invocation\n"));
1113 shi_debug_usage(argv
[0]);
1119 isds_set_log_callback(NULL
, NULL
);
1121 if (logger_fd
!= -1) {
1122 if (-1 == close(logger_fd
)) {
1123 fprintf(stderr
, _("Closing log file failed: %s\n"),
1128 cfg_setstr(configuration
, CONFIG_LOGFILE
, NULL
);
1130 if (do_log_to_file(file
))
1132 if (file
) cfg_setstr(configuration
, CONFIG_LOGFILE
, file
);
1134 /* Set log levels */
1135 isds_set_logging(log_facility
, log_level
);
1136 cfg_setint(configuration
, CONFIG_LOGLEVEL
, log_level
);
1137 save_log_facility(log_facility
);
1143 static void show_setting_str(const char *variable
, _Bool cenzore
) {
1144 if (!variable
) return;
1146 const char *value
= cfg_getstr(configuration
, variable
);
1150 printf(_("%s = <set>\n"), variable
);
1152 printf(_("%s = `%s'\n"), variable
, value
);
1154 printf(_("%s = <unset>\n"), variable
);
1159 static void show_setting_boolean(const char *variable
) {
1160 if (!variable
) return;
1162 _Bool value
= cfg_getbool(configuration
, variable
);
1165 printf(_("%s = <true>\n"), variable
);
1167 printf(_("%s = <false>\n"), variable
);
1172 static void show_setting_int(const char *variable
) {
1173 if (!variable
) return;
1175 long int value
= cfg_getint(configuration
, variable
);
1177 printf(_("%s = %ld\n"), variable
, value
);
1181 static void show_setting_strlist(const char *variable
) {
1182 if (!variable
) return;
1184 int length
= cfg_size(configuration
, variable
);
1187 printf(_("%s = <unset>\n"), variable
);
1189 printf(_("%s = {"), variable
);
1190 for (int i
= 0; i
< length
; i
++) {
1191 const char *value
= cfg_getnstr(configuration
, variable
, i
);
1193 printf(_("`%s', "), value
);
1195 printf(_("`%s'}\n"), value
);
1201 static int shi_settings(int argc
, const char **argv
) {
1203 int log_level = ILL_NONE;
1204 isds_log_facility log_facility = ILF_NONE;
1206 _Bool close_log = 0;*/
1210 while ((opt = getopt(argc, (char * const *)argv, "l:f:eo:")) != -1) {
1213 log_level = normalize_log_level(atoi(optarg));
1216 if (add_log_facility(&log_facility, optarg)) return -1;
1224 shi_debug_usage(argv[0]);
1228 if (optind == 1 || optind != argc) {
1229 printf(_("Bad invocation\n"));
1230 shi_debug_usage(argv[0]);
1235 printf(_("Current settings:\n"));
1237 show_setting_str(CONFIG_SERVER
, 0);
1238 show_setting_str(CONFIG_USERNAME
, 0);
1239 show_setting_str(CONFIG_PASSWORD
, 1),
1240 show_setting_boolean(CONFIG_VERIFYSERVER
);
1241 show_setting_str(CONFIG_CAFILE
, 0);
1242 show_setting_str(CONFIG_CADIRECTORY
, 0);
1243 show_setting_str(CONFIG_CRLFILE
, 0);
1244 show_setting_int(CONFIG_TIMEOUT
);
1245 show_setting_strlist(CONFIG_LOGFACILITIES
);
1246 show_setting_str(CONFIG_LOGFILE
, 0);
1247 show_setting_int(CONFIG_LOGLEVEL
);
1248 show_setting_boolean(CONFIG_MARKMESSAGEREAD
);
1249 show_setting_boolean(CONFIG_NORMALIZEMIMETYPE
);
1250 show_setting_boolean(CONFIG_OVERWRITEFILES
);
1256 static void shi_find_box_usage(const char *command
) {
1258 "Usage: %s {OPTION... | BOX_ID}\n"
1259 "Get information about box with BOX_ID or boxes meeting other criteria.\n"
1260 "Each search option requires an argument:\n"
1261 " -t box type; accepted values:\n"
1262 " FO Private individual\n"
1263 " PFO Self-employed individual\n"
1264 " PFO_ADVOK Lawyer\n"
1265 " PFO_DANPOR Tax advisor\n"
1266 " PFO_INSSPR Insolvency administrator\n"
1267 " PO Organisation\n"
1268 " PO_ZAK Organization based by law\n"
1269 " PO_REQ Organization based on request\n"
1270 " OVM Public authority\n"
1271 " OVM_NOTAR Notary\n"
1272 " OVM_EXEKUT Executor\n"
1273 " OVM_REQ Public authority based on request\n"
1274 " -j identity number\n"
1276 "Person name options:\n"
1280 " -b last name at birth\n"
1281 " -s subject name\n"
1284 " -d birth date (locale or full ISO 8601 date)\n"
1286 " -y birth county\n"
1292 " -z number in street\n"
1293 " -Z number in municipality\n"
1300 " -p phone number\n"
1302 " -r registry code\n"
1303 " -a box status; accepted values:\n"
1304 " ACCESSIBLE Accessible\n"
1305 " TEMP_INACCESSIBLE Temporary inaccessible\n"
1306 " NOT_YET_ACCESSIBLE Not yet accessible\n"
1307 " PERM_INACCESSIBLE Permanently inaccessible\n"
1308 " REMOVED Deleted\n"
1309 " -o act as public authority; boolean values: 0 is false, 1 is true\n"
1310 " -k receive commercial messages; boolean values\n"
1312 "Not all option combinations are meaningful or allowed. For example box\n"
1313 "type is always required (except direct box ID query).\n"
1314 "ISDS can refuse to answer to much broad query. Not all boxes are searchable\n"
1321 /* Allow reassignment */
1322 #define FILL_OR_LEAVE(variable, locale) { \
1324 (variable) = locale2utf8(locale); \
1325 if (!(variable)) { \
1326 printf(_("Error: Not enough memory\n")); \
1332 #define CALLOC_OR_LEAVE(structure) { \
1333 if (!(structure)) { \
1334 (structure) = calloc(1, sizeof(*(structure))); \
1335 if (!(structure)) { \
1336 printf(_("Error: Not enough memory\n")); \
1343 #define FILL_BOOLEAN_OR_LEAVE(variable, locale) { \
1345 (variable) = malloc(sizeof(*(variable))); \
1346 if (!(variable)) { \
1347 printf(_("Error: Not enough memory\n")); \
1351 if (!strcmp((locale), "0")) *(variable) = 0; \
1352 else if (!strcmp((locale), "1")) *(variable) = 1; \
1354 printf(_("%s: %s: Unknown boolean value\n"), argv[0], (locale)); \
1360 #define FILL_LONGINT_OR_LEAVE(variable, locale) { \
1361 if (!(locale) || !*(locale)) { \
1362 printf(_("%s: Empty integer value\n"), argv[0]); \
1368 (variable) = malloc(sizeof(*(variable))); \
1369 if (!(variable)) { \
1370 printf(_("Error: Not enough memory\n")); \
1374 (*variable) = strtol((locale), &endptr, 0); \
1376 printf(_("%s: %s: Invalid integer value\n"), argv[0], (locale)); \
1382 static int shi_find_box(int argc
, const char **argv
) {
1385 struct isds_DbOwnerInfo
*criteria
= NULL
;
1386 struct isds_list
*item
;
1390 if (!argv
|| !argv
[1] || !*argv
[1]) {
1391 printf(_("Error: No argument supplied\n"));
1392 shi_find_box_usage((argv
)?argv
[0]:NULL
);
1396 criteria
= calloc(1, sizeof(*criteria
));
1398 printf(_("Error: Not enough memory\n"));
1405 while ((opt
= getopt(argc
, (char * const *)argv
, "t:j:s:"
1406 "f:m:l:b:s:" "d:w:y:c:" "W:S:z:Z:P:C:"
1407 "n:e:p:i:r:a:o:k:")) != -1) {
1410 criteria
->dbType
= malloc(sizeof(*criteria
->dbType
));
1411 if (!criteria
->dbType
) {
1412 printf(_("Error: Not enough memory\n"));
1416 if (!strcmp(optarg
, "FO"))
1417 *criteria
->dbType
= DBTYPE_FO
;
1418 else if (!strcmp(optarg
, "PFO"))
1419 *criteria
->dbType
= DBTYPE_PFO
;
1420 else if (!strcmp(optarg
, "PFO_ADVOK"))
1421 *criteria
->dbType
= DBTYPE_PFO_ADVOK
;
1422 else if (!strcmp(optarg
, "PFO_DANPOR"))
1423 *criteria
->dbType
= DBTYPE_PFO_DANPOR
;
1424 else if (!strcmp(optarg
, "PFO_INSSPR"))
1425 *criteria
->dbType
= DBTYPE_PFO_INSSPR
;
1426 else if (!strcmp(optarg
, "PO"))
1427 *criteria
->dbType
= DBTYPE_PO
;
1428 else if (!strcmp(optarg
, "PO_ZAK"))
1429 *criteria
->dbType
= DBTYPE_PO_ZAK
;
1430 else if (!strcmp(optarg
, "PO_REQ"))
1431 *criteria
->dbType
= DBTYPE_PO_REQ
;
1432 else if (!strcmp(optarg
, "OVM"))
1433 *criteria
->dbType
= DBTYPE_OVM
;
1434 else if (!strcmp(optarg
, "OVM_NOTAR"))
1435 *criteria
->dbType
= DBTYPE_OVM_NOTAR
;
1436 else if (!strcmp(optarg
, "OVM_EXEKUT"))
1437 *criteria
->dbType
= DBTYPE_OVM_EXEKUT
;
1438 else if (!strcmp(optarg
, "OVM_REQ"))
1439 *criteria
->dbType
= DBTYPE_OVM_REQ
;
1441 printf(_("%s: %s: Unknown box type\n"), argv
[0], optarg
);
1448 FILL_OR_LEAVE(criteria
->ic
, optarg
);
1453 CALLOC_OR_LEAVE(criteria
->personName
);
1454 FILL_OR_LEAVE(criteria
->personName
->pnFirstName
, optarg
);
1457 CALLOC_OR_LEAVE(criteria
->personName
);
1458 FILL_OR_LEAVE(criteria
->personName
->pnMiddleName
, optarg
);
1461 CALLOC_OR_LEAVE(criteria
->personName
);
1462 FILL_OR_LEAVE(criteria
->personName
->pnLastName
, optarg
);
1465 CALLOC_OR_LEAVE(criteria
->personName
);
1466 FILL_OR_LEAVE(criteria
->personName
->pnLastNameAtBirth
, optarg
);
1469 FILL_OR_LEAVE(criteria
->firmName
, optarg
);
1474 CALLOC_OR_LEAVE(criteria
->birthInfo
);
1475 criteria
->birthInfo
->biDate
= datestring2tm(optarg
);
1476 if (!criteria
->birthInfo
->biDate
) {
1477 printf(_("Error: Could not parse date: %s\n"), optarg
);
1483 CALLOC_OR_LEAVE(criteria
->birthInfo
);
1484 FILL_OR_LEAVE(criteria
->birthInfo
->biCity
, optarg
);
1487 CALLOC_OR_LEAVE(criteria
->birthInfo
);
1488 FILL_OR_LEAVE(criteria
->birthInfo
->biCounty
, optarg
);
1491 CALLOC_OR_LEAVE(criteria
->birthInfo
);
1492 FILL_OR_LEAVE(criteria
->birthInfo
->biState
, optarg
);
1497 CALLOC_OR_LEAVE(criteria
->address
);
1498 FILL_OR_LEAVE(criteria
->address
->adCity
, optarg
);
1501 CALLOC_OR_LEAVE(criteria
->address
);
1502 FILL_OR_LEAVE(criteria
->address
->adStreet
, optarg
);
1505 CALLOC_OR_LEAVE(criteria
->address
);
1506 FILL_OR_LEAVE(criteria
->address
->adNumberInStreet
, optarg
);
1509 CALLOC_OR_LEAVE(criteria
->address
);
1510 FILL_OR_LEAVE(criteria
->address
->adNumberInMunicipality
,
1514 CALLOC_OR_LEAVE(criteria
->address
);
1515 FILL_OR_LEAVE(criteria
->address
->adZipCode
, optarg
);
1518 CALLOC_OR_LEAVE(criteria
->address
);
1519 FILL_OR_LEAVE(criteria
->address
->adState
, optarg
);
1524 FILL_OR_LEAVE(criteria
->nationality
, optarg
);
1527 FILL_OR_LEAVE(criteria
->email
, optarg
);
1530 FILL_OR_LEAVE(criteria
->telNumber
, optarg
);
1533 FILL_OR_LEAVE(criteria
->identifier
, optarg
);
1536 FILL_OR_LEAVE(criteria
->registryCode
, optarg
);
1539 criteria
->dbState
= malloc(sizeof(*criteria
->dbState
));
1540 if (!criteria
->dbState
) {
1541 printf(_("Error: Not enough memory\n"));
1545 if (!strcmp(optarg
, "ACCESSIBLE"))
1546 *criteria
->dbState
= DBSTATE_ACCESSIBLE
;
1547 else if (!strcmp(optarg
, "TEMP_INACCESSIBLE"))
1548 *criteria
->dbState
= DBSTATE_TEMP_UNACCESSIBLE
;
1549 else if (!strcmp(optarg
, "NOT_YET_ACCESSIBLE"))
1550 *criteria
->dbState
= DBSTATE_NOT_YET_ACCESSIBLE
;
1551 else if (!strcmp(optarg
, "PERM_INACCESSIBLE"))
1552 *criteria
->dbState
= DBSTATE_PERM_UNACCESSIBLE
;
1553 else if (!strcmp(optarg
, "REMOVED"))
1554 *criteria
->dbState
= DBSTATE_REMOVED
;
1556 printf(_("%s: %s: Unknown box status\n"), argv
[0], optarg
);
1562 FILL_BOOLEAN_OR_LEAVE(criteria
->dbEffectiveOVM
, optarg
);
1565 FILL_BOOLEAN_OR_LEAVE(criteria
->dbOpenAddressing
, optarg
);
1569 shi_find_box_usage(argv
[0]);
1575 /* There must be an option and all of them must be recognized, if not only
1576 * BOX_ID supplied */
1577 if (argc
> 2 && optind
!= argc
) {
1578 printf(_("Error: Superfluous argument\n"));
1579 shi_find_box_usage(argv
[0]);
1584 /* If only box ID is supplied use it */
1585 if (argc
== 2 && argv
[1] && *argv
[1]) {
1586 criteria
->dbID
= locale2utf8(argv
[1]);
1587 if (!criteria
->dbID
) {
1588 printf(_("Error: Not enough memory\n"));
1594 printf(_("Searching boxes...\n"));
1595 err
= isds_FindDataBox(cisds
, criteria
, &boxes
);
1596 finish_isds_operation(cisds
, err
);
1599 for(item
= boxes
; item
; item
= item
->next
) {
1600 if (!item
->data
) continue;
1603 printf(_("\n* Result #%d:\n"), order
);
1604 format_DbOwnerInfo(item
->data
);
1608 isds_DbOwnerInfo_free(&criteria
);
1613 static void shi_stat_box_usage(const char *command
) {
1615 "Usage: %s BOX_ID...\n"
1616 "Get status of box with BOX_ID. More boxes can be specified.\n"),
1621 /* Get boxes status */
1622 static int shi_stat_box(int argc
, const char **argv
) {
1627 if (!argv
|| !*argv
|| argc
< 2 || !argv
[1]) {
1628 printf(_("Missing box ID\n"));
1629 shi_stat_box_usage((argv
[0])?argv
[0]:NULL
);
1633 for (int i
= 1; i
< argc
; i
++) {
1634 if (!argv
[i
] || !*argv
[i
]) continue;
1637 id
= locale2utf8(argv
[i
]);
1639 printf(_("%s: Could not covert box ID to UTF-8\n"), argv
[i
]);
1643 printf(_("Getting status of box `%s'...\n"), argv
[i
]);
1644 err
= isds_CheckDataBox(cisds
, id
, &status
);
1645 finish_isds_operation(cisds
, err
);
1648 printf(_("Status of box `%s': %s\n"),
1649 argv
[i
], DbState2string(&status
));
1656 static void shi_compose_usage(const char *command
) {
1658 "Usage: %s OPTION...\n"
1659 "Compose and send a message to recipient defined by his box ID.\n"
1660 "Each option requires an argument (if not stated otherwise):\n"
1661 " -s * message subject\n"
1663 "Recipient options:\n"
1664 " -b * recipient box ID\n"
1665 " -U organisation unit name\n"
1666 " -N organisation unit number\n"
1667 " -P to hands of given person\n"
1669 "Sender organisation structure options:\n"
1673 "Message identifier options:\n"
1674 " -r sender reference number\n"
1675 " -f sender file ID\n"
1676 " -R recipient reference number\n"
1677 " -F recipient file ID\n"
1679 "Legal title options:\n"
1680 " -y year act has been issued\n"
1681 " -a ordinal number of act in a year\n"
1682 " -e section of the act\n"
1683 " -o paragraph of the act\n"
1684 " -i point of the paragraph of the act\n"
1686 "Delivery options:\n"
1687 " -p personal delivery required\n"
1688 " -t allow substitutable delivery\n"
1689 " -A non-OVM sender acts as public authority\n"
1691 "Document options:\n"
1692 " -d * read document from local file\n"
1693 " -D document name (defaults to base local file name)\n"
1694 " -x transport subset of the document as a XML.\n"
1695 " Argument is XPath expression specifying desired node set\n"
1696 " -m override MIME type (guessed on -d)\n"
1697 " -g document ID (must be unique per message)\n"
1698 " -G reference to other document using its ID\n"
1699 " -c document is digital signature of other document (NO argument\n"
1702 "Options marked with asterisk are mandatory, other are optional. Another soft\n"
1703 "dependencies can emerge upon using specific option. They are not mandated by\n"
1704 "ISDS currently, but client library or this program can force them to assure\n"
1705 "semantically complete message. Following soft dependencies are recommended:\n"
1706 " -y <=> -a act number and year must be used at the same time\n"
1707 " -i => -o act point requires act paragraph\n"
1708 " -o => -e act paragraph requires act section\n"
1709 " -e => -a act section requires act number\n"
1710 " -G => -g document with referenced ID must exist\n"
1711 " -c => -G signature must refer to signed document\n"
1712 " -c first document cannot be signature\n"
1714 "More documents can be attached to a message by repeating `-d' option.\n"
1715 "Document order will be preserved. Other document options affect immediately\n"
1716 "preceding `-d' document only. E.g. `-d /tmp/foo.pdf -m application/pdf\n"
1717 "-d /tmp/bar.txt -m text/plain' attaches first PDF file, then textual file.\n"
1719 "The same applies to recipient options that must start with box ID (-b).\n"
1720 "If more recipients specified, each of them will get a copy of composed\n"
1721 "message. ISDS will assign message identifier to each copy in turn.\n"
1727 static int shi_compose(int argc
, const char **argv
) {
1731 struct isds_message
*message
= NULL
;
1732 struct isds_envelope
*envelope
= NULL
;
1733 struct isds_list
*documents
= NULL
;
1734 struct isds_document
*document
= NULL
;
1735 struct isds_list
*copies
= NULL
, *copy_item
= NULL
;
1736 struct isds_message_copy
*copy
= NULL
;
1737 char *message_id_locale
= NULL
, *recipient_id_locale
= NULL
,
1738 *dmStatus_locale
= NULL
;
1740 if (!argv
|| !argv
[1] || !*argv
[1]) {
1741 printf(_("Error: No argument supplied\n"));
1742 shi_compose_usage((argv
)?argv
[0]:NULL
);
1746 message
= calloc(1, sizeof(*message
));
1748 printf(_("Error: Not enough memory\n"));
1752 envelope
= calloc(1, sizeof(*envelope
));
1754 printf(_("Error: Not enough memory\n"));
1758 message
->envelope
= envelope
;
1762 while ((opt
= getopt(argc
, (char * const *)argv
, "s:" "b:U:N:P:" "u:n:"
1763 "r:f:R:F:" "y:a:e:o:i:" "p:t:A:" "d:D:x:m:g:G:c"
1767 FILL_OR_LEAVE(envelope
->dmAnnotation
, optarg
);
1770 /* Recipient options */
1774 /* First recipient */
1775 CALLOC_OR_LEAVE(copies
);
1776 copies
->destructor
=
1777 (void(*)(void **)) isds_message_copy_free
;
1780 /* Next recipient */
1781 CALLOC_OR_LEAVE(copy_item
->next
);
1782 copy_item
->next
->destructor
=
1783 (void(*)(void **)) isds_message_copy_free
;
1784 copy_item
= copy_item
->next
;
1786 CALLOC_OR_LEAVE(copy
);
1787 copy_item
->data
= copy
;
1789 /* Copy recipient box ID */
1790 FILL_OR_LEAVE(copy
->dbIDRecipient
, optarg
);
1794 printf(_("Error: %s: Recipient box ID (-b) must precede "
1795 "recipient organisation unit name (-%c)\n"),
1800 FILL_OR_LEAVE(copy
->dmRecipientOrgUnit
, optarg
);
1804 printf(_("Error: %s: Recipient box ID (-b) must precede "
1805 "recipient organisation unit number (-%c)\n"),
1810 FILL_LONGINT_OR_LEAVE(copy
->dmRecipientOrgUnitNum
, optarg
);
1814 printf(_("Error: %s: Recipient box ID (-b) must precede "
1815 "to-hands option (-%c)\n"), optarg
, opt
);
1819 FILL_OR_LEAVE(copy
->dmToHands
, optarg
);
1822 /* Sender organisation structure options */
1824 FILL_OR_LEAVE(envelope
->dmSenderOrgUnit
, optarg
);
1827 FILL_LONGINT_OR_LEAVE(envelope
->dmSenderOrgUnitNum
, optarg
);
1830 /* Message identifier options */
1832 FILL_OR_LEAVE(envelope
->dmSenderRefNumber
, optarg
);
1835 FILL_OR_LEAVE(envelope
->dmSenderIdent
, optarg
);
1838 FILL_OR_LEAVE(envelope
->dmRecipientRefNumber
, optarg
);
1841 FILL_OR_LEAVE(envelope
->dmRecipientIdent
, optarg
);
1844 /* Legal title options */
1846 FILL_LONGINT_OR_LEAVE(envelope
->dmLegalTitleYear
, optarg
);
1849 FILL_LONGINT_OR_LEAVE(envelope
->dmLegalTitleLaw
, optarg
);
1852 FILL_OR_LEAVE(envelope
->dmLegalTitleSect
, optarg
);
1855 FILL_OR_LEAVE(envelope
->dmLegalTitlePar
, optarg
);
1858 FILL_OR_LEAVE(envelope
->dmLegalTitlePoint
, optarg
);
1861 /* Delivery options */
1863 FILL_BOOLEAN_OR_LEAVE(envelope
->dmPersonalDelivery
, optarg
);
1866 FILL_BOOLEAN_OR_LEAVE(envelope
->dmAllowSubstDelivery
, optarg
);
1869 FILL_BOOLEAN_OR_LEAVE(envelope
->dmOVM
, optarg
);
1872 /* Document options */
1876 /* First document */
1877 CALLOC_OR_LEAVE(message
->documents
);
1878 message
->documents
->destructor
=
1879 (void(*)(void **)) isds_document_free
;
1880 documents
= message
->documents
;
1881 CALLOC_OR_LEAVE(document
);
1882 documents
->data
= document
;
1883 document
->dmFileMetaType
= FILEMETATYPE_MAIN
;
1886 CALLOC_OR_LEAVE(documents
->next
);
1887 documents
->next
->destructor
=
1888 (void(*)(void **)) isds_document_free
;
1889 documents
= documents
->next
;
1890 CALLOC_OR_LEAVE(document
);
1891 documents
->data
= document
;
1892 document
->dmFileMetaType
= FILEMETATYPE_ENCLOSURE
;
1895 if (load_data_from_file(optarg
, &document
->data
,
1896 &document
->data_length
, &document
->dmMimeType
)) {
1900 /* XXX: POSIX basename() modifies argument */
1901 FILL_OR_LEAVE(document
->dmFileDescr
, basename(optarg
));
1905 printf(_("Error: %s: Document file (-d) must precede "
1906 "document name (-%c)\n"), optarg
, opt
);
1910 FILL_OR_LEAVE(document
->dmFileDescr
, optarg
);
1914 printf(_("Error: %s: Document file (-d) must precede "
1915 "XPath expression (-%c)\n"), optarg
, opt
);
1919 /* Load XML node list */
1920 char *xpath_expr
= NULL
;
1921 FILL_OR_LEAVE(xpath_expr
, optarg
);
1922 retval
= load_xml_subtree_from_memory(
1923 document
->data
, document
->data_length
,
1924 &document
->xml_node_list
, xpath_expr
);
1929 /* Switch document type to XML */
1930 document
->is_xml
= 1;
1931 zfree(document
->data
);
1932 document
->data_length
= 0;
1933 documents
->destructor
=
1934 (void(*)(void **)) free_document_with_xml_node_list
;
1938 printf(_("Error: %s: Document file (-d) must precede "
1939 "MIME type (-%c)\n"), optarg
, opt
);
1943 FILL_OR_LEAVE(document
->dmMimeType
, optarg
);
1947 printf(_("Error: %s: Document file (-d) must precede "
1948 "document ID (-%c)\n"), optarg
, opt
);
1952 FILL_OR_LEAVE(document
->dmFileGuid
, optarg
);
1956 printf(_("Error: %s: Document file (-d) must precede "
1957 "document reference (-%c)\n"), optarg
, opt
);
1961 FILL_OR_LEAVE(document
->dmUpFileGuid
, optarg
);
1965 printf(_("Error: Document file (-d) must precede "
1966 "document signature type (-%c)\n"), opt
);
1970 document
->dmFileMetaType
= FILEMETATYPE_SIGNATURE
;
1974 shi_compose_usage(argv
[0]);
1980 /* All options must be recognized */
1981 if (optind
!= argc
) {
1982 printf(_("Error: Superfluous argument\n"));
1983 shi_compose_usage(argv
[0]);
1989 printf(_("Error: No recipient box ID specified\n"));
1990 shi_compose_usage(argv
[0]);
1995 /* TODO: Check Legal Title soft dependencies */
1998 printf(_("Following message has been composed:\n"));
1999 format_message(message
);
2000 printf(_("Following recipients have been specified:\n"));
2001 format_copies(copies
);
2002 /* TODO: Confirmation, correction */
2004 /* Send a message */
2005 printf(_("Sending message...\n"));
2006 err
= isds_send_message_to_multiple_recipients(cisds
, message
, copies
);
2007 finish_isds_operation(cisds
, err
);
2008 if (err
&& err
!= IE_PARTIAL_SUCCESS
) {
2013 /* Show results for each copy */
2014 for (copy_item
= copies
; copy_item
; copy_item
= copy_item
->next
) {
2015 if (!copy_item
->data
) continue;
2016 copy
= (struct isds_message_copy
*) copy_item
->data
;
2017 recipient_id_locale
= utf82locale(copy
->dbIDRecipient
);
2021 if (copy
->dmStatus
) dmStatus_locale
= utf82locale(copy
->dmStatus
);
2022 if (dmStatus_locale
)
2023 printf(_("%s: Failed: %s: %s\n"),
2024 recipient_id_locale
,
2025 isds_strerror(copy
->error
), dmStatus_locale
);
2027 printf(_("%s: Failed: %s\n"), recipient_id_locale
,
2028 isds_strerror(copy
->error
));
2029 zfree(dmStatus_locale
);
2031 message_id_locale
= utf82locale(copy
->dmID
);
2032 printf(_("%s: Succeeded. Assigned message ID: %s\n"),
2033 recipient_id_locale
, message_id_locale
);
2034 free(message_id_locale
);
2037 free(recipient_id_locale
);
2041 isds_message_free(&message
);
2042 isds_list_free(&copies
);
2046 #undef FILL_LONGINT_OR_LEAVE
2047 #undef FILL_BOOLEAN_OR_LEAVE
2048 #undef CALLOC_OR_LEAVE
2049 #undef FILL_OR_LEAVE
2052 static void shi_delivery_usage(const char *command
) {
2054 "Usage: %s MESSAGE_ID\n"
2055 "Get delivery data about message with MESSAGE_ID.\n"),
2060 static int shi_delivery(int argc
, const char **argv
) {
2064 if (!argv
|| !argv
[1] || !*argv
[1]) {
2065 shi_delivery_usage(argv
[0]);
2070 printf(_("Getting delivery info...\n"));
2071 err
= isds_get_signed_delivery_info(cisds
, id
, &message
);
2072 finish_isds_operation(cisds
, err
);
2075 select_completition(COMPL_COMMAND
);
2079 format_message(message
);
2081 if (message
->envelope
&& message
->envelope
->dmID
)
2082 set_prompt(_("%s %s"), argv
[0], message
->envelope
->dmID
);
2084 set_prompt("%s", argv
[0]);
2085 select_completition(COMPL_MSG
);
2090 static int shi_dump_message(int argc
, const char **argv
) {
2092 printf(_("No message loaded\n"));
2096 print_message(message
);
2101 static void shi_hash_usage(const char *command
) {
2103 "Usage: %s [MESSAGE_ID]\n"
2104 "Retrieve message hash stored in ISDS.\n"
2105 "If MESSAGE_ID is defined, query for that message.\n"
2106 "Otherwise use current message.\n"),
2111 static int shi_hash(int argc
, const char **argv
) {
2113 const char *id
= NULL
;
2114 struct isds_hash
*hash
= NULL
;
2115 char *hash_string
= NULL
;
2117 if (!argv
|| argc
> 2) {
2118 shi_hash_usage((argv
)?argv
[0]:NULL
);
2121 if (argc
== 2 && argv
[1] && *argv
[1])
2125 printf(_("No message loaded\n"));
2128 if (!message
->envelope
|| !message
->envelope
->dmID
) {
2129 printf(_("Current message is missing ID\n"));
2132 id
= message
->envelope
->dmID
;
2135 printf(_("Getting message hash...\n"));
2136 err
= isds_download_message_hash(cisds
, id
, &hash
);
2137 finish_isds_operation(cisds
, err
);
2140 hash_string
= hash2string(hash
);
2141 printf(_("ISDS states message with `%s' ID has following hash:\n%s\n"),
2145 isds_hash_free(&hash
);
2150 static int shi_verify(int argc
, const char **argv
) {
2153 struct isds_hash
*retrieved_hash
= NULL
, *stored_hash
= NULL
;
2154 char *hash_string
= NULL
;
2158 printf(_("No message loaded\n"));
2162 if (!message
->envelope
) {
2163 printf(_("Current message is missing envelope\n"));
2166 stored_hash
= message
->envelope
->hash
;
2167 message
->envelope
->hash
= NULL
;
2169 if (message
->envelope
->dmID
) {
2170 /* Verify remote hash */
2171 printf(_("Remote hash check:\n"));
2173 printf(_("Getting message hash...\n"));
2174 err
= isds_download_message_hash(cisds
, message
->envelope
->dmID
,
2176 finish_isds_operation(cisds
, err
);
2178 if (retrieved_hash
) {
2179 hash_string
= hash2string(retrieved_hash
);
2180 fhprint(stdout
, _("Retrieved:"), width
);
2181 printf("%s\n", hash_string
);
2185 if (retrieved_hash
&& message
->raw
) {
2186 err
= isds_compute_message_hash(cisds
, message
,
2187 retrieved_hash
->algorithm
);
2188 finish_isds_operation(cisds
, err
);
2191 hash_string
= hash2string(message
->envelope
->hash
);
2192 fhprint(stdout
, _("Computed:"), width
);
2193 printf("%s\n", hash_string
);
2198 err
= isds_hash_cmp(retrieved_hash
, message
->envelope
->hash
);
2201 printf(_("Hashes match.\n")); break;
2203 printf(_("Hashes do not match.\n"));
2207 printf(_("Hashes could not be compared.\n"));
2212 free(retrieved_hash
);
2217 /* Verify stored hash */
2218 printf(_("Stored hash check:\n"));
2220 hash_string
= hash2string(stored_hash
);
2221 fhprint(stdout
, _("Stored:"), width
);
2222 printf("%s\n", hash_string
);
2226 err
= isds_compute_message_hash(cisds
, message
,
2227 stored_hash
->algorithm
);
2228 finish_isds_operation(cisds
, err
);
2231 hash_string
= hash2string(message
->envelope
->hash
);
2232 fhprint(stdout
, _("Computed:"), width
);
2233 printf("%s\n", hash_string
);
2238 err
= isds_hash_cmp(stored_hash
, message
->envelope
->hash
);
2241 printf(_("Hashes match.\n")); break;
2243 printf(_("Hashes do not match.\n"));
2247 printf(_("Hashes could not be compared.\n"));
2252 isds_hash_free(&message
->envelope
->hash
);
2255 message
->envelope
->hash
= stored_hash
;
2260 static int shi_authenticate(int argc
, const char **argv
) {
2265 printf(_("No message loaded\n"));
2268 if (!message
->raw
|| message
->raw_length
== 0) {
2269 printf(_("Current message is missing raw representation\n"));
2273 printf(_("Submitting message to authenticity check...\n"));
2274 err
= isds_authenticate_message(cisds
, message
->raw
, message
->raw_length
);
2275 finish_isds_operation(cisds
, (err
== IE_NOTUNIQ
) ? IE_SUCCESS
: err
);
2279 printf(_("Message originates in ISDS.\n")); break;
2281 printf(_("Message is unknown to ISDS or has been tampered.\n"));
2293 static void shi_accept_message_usage(const char *command
) {
2295 "Usage: %s [MESSAGE_ID...]\n"
2296 "Accept commercial message moving its state to received.\n"
2297 "If MESSAGE_ID is defined, accept that message. More messages can be specified.\n"
2298 "Otherwise accept all commercial incoming messages.\n"),
2303 static int shi_accept_message(int argc
, const char **argv
) {
2307 /* Process messages named in argv */
2308 for (int i
= 1; i
< argc
; i
++) {
2309 if (!argv
[i
] || !*argv
[i
]) continue;
2311 id
= locale2utf8(argv
[i
]);
2313 printf(_("Error: Could not convert message ID to UTF-8: %s\n"),
2318 printf(_("Accepting message `%s'...\n"), argv
[i
]);
2319 err
= isds_mark_message_received(cisds
, id
);
2320 finish_isds_operation(cisds
, err
);
2326 printf(_("Message `%s' accepted\n"), argv
[i
]);
2331 /* TODO: list commercial not received messages and accept all of them
2333 printf(_("Error: No message ID supplied. Accepting all commercial "
2334 "messages not implemented yet.\n"));
2342 /* Mark message as read. At one form of ID must be provided.
2343 * @id is UTF-8 encoded message ID
2344 * @id_locale is locale encoded message ID. @id takes preference. */
2345 static int do_read_message(const char *id
, const char *id_locale
) {
2349 if ((!id
|| !*id
) && (!id_locale
|| !*id_locale
)) return -1;
2353 id_locale
= utf82locale(id
);
2356 id
= locale2utf8(id_locale
);
2358 printf(_("Error: Could not convert message ID to UTF-8: %s\n"),
2364 printf(_("Marking message `%s' as read...\n"), id_locale
);
2365 err
= isds_mark_message_read(cisds
, id
);
2366 finish_isds_operation(cisds
, err
);
2369 printf(_("Message `%s' marked as read\n"), id_locale
);
2371 if (static_id
) free((char *) id_locale
);
2372 else free((char *) id
);
2374 return (err
) ? -1 : 0;
2378 static void shi_read_message_usage(const char *command
) {
2380 "Usage: %s [MESSAGE_ID...]\n"
2381 "Mark message as read moving its state to read.\n"
2383 "When new incoming message is download, its state is not changed on server.\n"
2384 "Client must mark such message as read explicitly. You can use this command\n"
2385 "to do so, if not done automatically at download time by your client.\n"
2387 "If MESSAGE_ID is defined, mark that message. More messages can be specified.\n"
2388 "Otherwise marks currently loaded message.\n"),
2393 static int shi_read_message(int argc
, const char **argv
) {
2394 for (int i
= 1; i
< argc
; i
++) {
2395 if (!argv
[i
] || !*argv
[i
]) continue;
2396 if (do_read_message(NULL
, argv
[i
]))
2402 printf(_("No message loaded\n"));
2405 if (!message
->envelope
) {
2406 printf(_("Loaded message is missing envelope\n"));
2409 if (!message
->envelope
->dmID
|| !*message
->envelope
->dmID
) {
2410 printf(_("Loaded message is missing ID\n"));
2413 return do_read_message(message
->envelope
->dmID
, NULL
);
2420 static int shi_show_message(int argc
, const char **argv
) {
2422 printf(_("No message loaded\n"));
2426 format_message(message
);
2431 static void shi_incoming_message_usage(const char *command
) {
2433 "Usage: %s [-r] MESSAGE_ID\n"
2434 "Get incoming message with MESSAGE_ID.\n"
2436 " -r Mark mesage as read\n"),
2441 static int shi_incoming_message(int argc
, const char **argv
) {
2445 _Bool mark_as_read
= 0;
2448 while ((opt
= getopt(argc
, (char * const *)argv
, "r")) != -1) {
2454 shi_incoming_message_usage((argv
)?argv
[0]:NULL
);
2458 if (optind
+ 1 != argc
|| !argv
|| !argv
[optind
] || !*argv
[optind
]) {
2459 printf(_("Bad invocation\n"));
2460 shi_incoming_message_usage((argv
)?argv
[0]:NULL
);
2465 printf(_("Getting incoming message...\n"));
2466 err
= isds_get_signed_received_message(cisds
, id
, &message
);
2467 finish_isds_operation(cisds
, err
);
2470 select_completition(COMPL_COMMAND
);
2474 format_message(message
);
2476 if (message
->envelope
&& message
->envelope
->dmID
)
2477 set_prompt(_("%s %s"), argv
[0], message
->envelope
->dmID
);
2479 set_prompt("%s", argv
[0]);
2480 select_completition(COMPL_MSG
);
2482 if (mark_as_read
|| cfg_getbool(configuration
, CONFIG_MARKMESSAGEREAD
)) {
2483 return do_read_message(id
, NULL
);
2489 static void shi_outgoing_message_usage(const char *command
) {
2491 "Usage: %s MESSAGE_ID\n"
2492 "Get outgoing message with MESSAGE_ID.\n"),
2497 static int shi_outgoing_message(int argc
, const char **argv
) {
2501 if (!argv
|| !argv
[1] || !*argv
[1]) {
2502 shi_outgoing_message_usage(argv
[0]);
2507 printf(_("Getting outgoing message...\n"));
2508 err
= isds_get_signed_sent_message(cisds
, id
, &message
);
2509 finish_isds_operation(cisds
, err
);
2512 select_completition(COMPL_COMMAND
);
2516 format_message(message
);
2517 if (message
->envelope
&& message
->envelope
->dmID
)
2518 set_prompt(_("%s %s"), argv
[0], message
->envelope
->dmID
);
2520 set_prompt("%s", argv
[0]);
2521 select_completition(COMPL_MSG
);
2526 static void shi_load_anything_usage(const char *command
) {
2529 "Load message or message delivery details from local FILE.\n"),
2534 static int shi_load_anything(int argc
, const char **argv
) {
2536 void *buffer
= NULL
;
2538 isds_raw_type raw_type
;
2540 char *type_name
= NULL
;
2542 if (!argv
|| !argv
[1] || !*argv
[1]) {
2543 shi_load_anything_usage((argv
)?argv
[0]:NULL
);
2547 printf(_("Loading file `%s'...\n"), argv
[1]);
2549 if (mmap_file(argv
[1], &fd
, &buffer
, &length
)) return -1;
2551 printf(_("Detecting file format...\n"));
2552 err
= isds_guess_raw_type(cisds
, &raw_type
, buffer
, length
);
2553 finish_isds_operation(cisds
, err
);
2556 if (err
== IE_NOTSUP
)
2557 printf(_("Unknown format. Could not parse the file.\n"));
2559 printf(_("Error while detecting format. "
2560 "Could not parse the file.\n"));
2563 case RAWTYPE_INCOMING_MESSAGE
:
2564 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
:
2565 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
:
2566 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
:
2567 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
2568 err
= isds_load_message(cisds
, raw_type
,
2569 buffer
, length
, &message
, BUFFER_COPY
);
2570 finish_isds_operation(cisds
, err
);
2571 type_name
= N_("message");
2574 case RAWTYPE_DELIVERYINFO
:
2575 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO
:
2576 case RAWTYPE_CMS_SIGNED_DELIVERYINFO
:
2577 err
= isds_load_delivery_info(cisds
, raw_type
,
2578 buffer
, length
, &message
, BUFFER_COPY
);
2579 finish_isds_operation(cisds
, err
);
2580 type_name
= N_("delivery");
2584 printf(_("Unsupported format. Could not parse the file.\n"));
2589 munmap_file(fd
, buffer
, length
);
2593 select_completition(COMPL_COMMAND
);
2597 format_message(message
);
2599 if (message
->envelope
&& message
->envelope
->dmID
)
2600 set_prompt(_("%s %s"), _(type_name
), message
->envelope
->dmID
);
2602 set_prompt("%s", _(type_name
));
2603 select_completition(COMPL_MSG
);
2608 static void shi_save_message_usage(const char *command
) {
2611 "Save message into local FILE.\n"),
2616 static const char *raw_type2mime(isds_raw_type raw_type
) {
2618 case RAWTYPE_INCOMING_MESSAGE
:
2619 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
:
2620 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
:
2621 case RAWTYPE_DELIVERYINFO
:
2622 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO
:
2625 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
:
2626 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
2627 case RAWTYPE_CMS_SIGNED_DELIVERYINFO
:
2628 return "application/pkcs7-mime";
2636 static int shi_save_message(int argc
, const char **argv
) {
2637 _Bool overwrite
= cfg_getbool(configuration
, CONFIG_OVERWRITEFILES
);
2639 if (!argv
|| !argv
[1] || !*argv
[1]) {
2640 shi_save_message_usage(argv
[0]);
2645 printf(_("No message loaded\n"));
2648 if (!message
->raw
|| message
->raw_length
== 0) {
2649 printf(_("Loaded message is missing raw representation\n"));
2653 return save_data_to_file(argv
[1], message
->raw
, message
->raw_length
,
2654 raw_type2mime(message
->raw_type
), overwrite
);
2658 /* Return document of current message identified by ordinal number expressed
2659 * as string. In case of error return NULL. */
2660 static const struct isds_document
*locate_document_by_ordinal_string(
2661 const char *number
) {
2662 const struct isds_list
*item
;
2663 const struct isds_document
*document
= NULL
;
2666 if (!number
) return NULL
;
2668 ordinar
= atoi(number
);
2670 printf(_("%s: Document number must be positive number\n"), number
);
2675 printf(_("No message loaded\n"));
2680 for (item
= message
->documents
, i
= 0; item
; item
= item
->next
) {
2681 if (!item
->data
) continue;
2682 if (++i
== ordinar
) {
2683 document
= (const struct isds_document
*) item
->data
;
2688 printf(_("Message does not contain document #%d\n"), ordinar
);
2696 static void shi_save_document_usage(const char *command
) {
2698 "Usage: %s NUMBER [DESTINATION]\n"
2699 "Save document having ordinal NUMBER within current message into local file.\n"
2700 "If DESTINATION is file (or does not exist yet), document will be saved into\n"
2702 "If DESTINATION is existing directory, file name equaled to document name\n"
2703 "will be saved into DESTINATION.\n"
2704 "If DESTINATION is missing, document name will be used as file name and\n"
2705 "saved into working directory.\n"
2706 "Be aware that document name does not embed malicious characters (slashes).\n"
2708 "If the document is a binary stream, image of the document will be copied\n"
2709 "into a file. If the document is a XML document, the XML tree will be serialized\n"
2710 "into a file. If XML document stands for one element or one text node, the node\n"
2711 "(and its children recursively) will be serialized. If XML document compounds\n"
2712 "more nodes or a comment or a processing instruction, parent node from ISDS name\n"
2713 "space will be used to ensure output serialized XML well-formness.\n"
2719 static int shi_save_document(int argc
, const char **argv
) {
2720 const struct isds_document
*document
;
2721 const char *dirname
= NULL
;
2722 char *filename
= NULL
, *path
= NULL
;
2724 _Bool overwrite
= cfg_getbool(configuration
, CONFIG_OVERWRITEFILES
);
2726 if (!argv
|| !argv
[1] || !*argv
[1] || argc
> 3) {
2727 shi_save_document_usage(argv
[0]);
2731 document
= locate_document_by_ordinal_string(argv
[1]);
2732 if (!document
) return -1;
2734 /* Select directory and file name */
2735 if (argv
[2] && *argv
[2]) {
2736 if (!is_directory(argv
[2])) {
2739 filename
= strdup(argv
[2]);
2741 printf(_("Not enough memory\n"));
2746 if (!filename
&& document
->dmFileDescr
&& &document
->dmFileDescr
) {
2747 filename
= utf82locale(document
->dmFileDescr
);
2749 printf(_("Not enough memory\n"));
2754 printf(_("File name neither supplied, nor document name exists\n"
2755 "Please, supply one.\n"));
2761 path
= astrcat3(dirname
, "/", filename
);
2768 printf(_("Not enough memory\n"));
2773 if (document
->is_xml
)
2774 retval
= save_xml_to_file(path
, document
->xml_node_list
,
2775 document
->dmMimeType
, overwrite
);
2777 retval
= save_data_to_file(path
, document
->data
, document
->data_length
,
2778 document
->dmMimeType
, overwrite
);
2784 static void shi_save_stamp_usage(const char *command
) {
2787 "Save message time stamp into local FILE.\n"),
2792 static int shi_save_stamp(int argc
, const char **argv
) {
2793 _Bool overwrite
= cfg_getbool(configuration
, CONFIG_OVERWRITEFILES
);
2795 if (!argv
|| !argv
[1] || !*argv
[1]) {
2796 shi_save_message_usage(argv
[0]);
2801 printf(_("No message loaded\n"));
2804 if (!message
->envelope
|| !message
->envelope
->timestamp
||
2805 message
->envelope
->timestamp_length
== 0) {
2806 printf(_("Loaded message is missing time stamp\n"));
2810 return save_data_to_file(argv
[1],
2811 message
->envelope
->timestamp
, message
->envelope
->timestamp_length
,
2812 "application/timestamp-reply", overwrite
);
2816 static int shi_show_list(int argc
, const char **argv
) {
2818 printf(_("No message list loaded\n"));
2822 printf((messages_are_outgoing
) ?
2823 ngettext("You have %'lu outgoing message\n",
2824 "You have %'lu outgoing messages\n", total_messages
) :
2825 ngettext("You have %'lu incoming message\n",
2826 "You have %'lu incoming messages\n", total_messages
),
2828 print_message_list(messages
, messages_are_outgoing
);
2833 static int shi_list_incoming(int argc
, const char **argv
) {
2836 printf(_("Listing incoming messages...\n"));
2837 err
= isds_get_list_of_received_messages(cisds
, NULL
, NULL
, NULL
,
2838 MESSAGESTATE_ANY
, 0, &total_messages
, &messages
);
2839 finish_isds_operation(cisds
, err
);
2842 select_completition(COMPL_COMMAND
);
2845 messages_are_outgoing
= 0;
2847 shi_show_list(0, NULL
);
2849 set_prompt(_("%s %'lu"), argv
[0], total_messages
);
2850 select_completition(COMPL_LIST
);
2855 static int shi_list_outgoing(int argc
, const char **argv
) {
2858 printf(_("Listing outgoing messages...\n"));
2859 err
= isds_get_list_of_sent_messages(cisds
, NULL
, NULL
, NULL
,
2860 MESSAGESTATE_ANY
, 0, &total_messages
, &messages
);
2861 finish_isds_operation(cisds
, err
);
2864 select_completition(COMPL_COMMAND
);
2867 messages_are_outgoing
= 1;
2869 shi_show_list(0, NULL
);
2871 set_prompt(_("%s %'lu"), argv
[0], total_messages
);
2872 select_completition(COMPL_LIST
);
2877 /* Submit document for conversion and print assigned identifier */
2878 static int do_convert(const struct isds_document
*document
) {
2881 struct tm
*date
= NULL
;
2883 if (!document
) return -1;
2885 printf(_("Submitting document for authorized conversion...\n"));
2887 err
= czp_convert_document(czechpoint
, document
, &id
, &date
);
2888 finish_isds_operation(czechpoint
, err
);
2891 char *name_locale
= utf82locale(document
->dmFileDescr
);
2892 char *date_string
= tm2string(date
);
2893 char *id_locale
= utf82locale(id
);
2895 "Document submitted for authorized conversion successfully under name\n"
2897 "Submit identifier assigned by Czech POINT deposit is `%s'.\n"),
2898 name_locale
, date_string
, id_locale
);
2902 printf(_("Be ware that submitted document has restricted lifetime "
2904 printf(_("See <%s> for more details.\n"), CZPDEPOSIT_URL
);
2907 free(id
); free(date
);
2908 return (err
) ? -1 : 0;
2912 static void shi_convert_file_usage(const char *command
) {
2914 "Usage: %s FILE [NAME]\n"
2915 "Submit local FILE to authorized conversion under NAME. If NAME is missing,\n"
2916 "it will use FILE name.\n"), command
);
2919 "If Czech POINT deposit accepts document, it will return document identifier\n"
2920 "that user is supposed to provide to officer at Czech POINT contact place.\n"
2921 "Currently only PDF 1.3 and higher version files are accepted.\n"));
2922 printf(_("See <%s> for more details.\n"), CZPDEPOSIT_URL
);
2926 static int shi_convert_file(int argc
, const char **argv
) {
2928 struct isds_document document
;
2931 if (!argv
|| argc
> 3 || !argv
[1] || !*argv
[1]) {
2932 shi_convert_file_usage((argv
)?argv
[0]:NULL
);
2936 memset(&document
, 0, sizeof(document
));
2938 if (argc
== 3 && argv
[2] && *argv
[2])
2939 document
.dmFileDescr
= locale2utf8(argv
[2]);
2941 document
.dmFileDescr
= locale2utf8(argv
[1]);
2942 if (!document
.dmFileDescr
) {
2943 printf(_("Could not convert document name to UTF-8\n"));
2947 printf(_("Loading document from file `%s'...\n"), argv
[1]);
2948 if (mmap_file(argv
[1], &fd
, &document
.data
, &document
.data_length
)) {
2949 free(document
.dmFileDescr
);
2953 retval
= do_convert(&document
);
2955 munmap_file(fd
, document
.data
, document
.data_length
);
2956 free(document
.dmFileDescr
);
2961 static void shi_convert_document_usage(const char *command
) {
2963 "Usage: %s NUMBER\n"
2964 "Submit message document with ordinal NUMBER to authorized conversion.\n"),
2968 "If Czech POINT deposit accepts document, it will return document identifier\n"
2969 "that user is supposed to provide to officer at Czech POINT contact place.\n"
2970 "Currently only PDF 1.3 and higher version files are accepted.\n"));
2971 printf(_("See <%s> for more details.\n"), CZPDEPOSIT_URL
);
2975 static int shi_convert_document(int argc
, const char **argv
) {
2976 const struct isds_document
*document
;
2978 if (!argv
|| !argv
[1] || !*argv
[1]) {
2979 shi_convert_document_usage((argv
)?argv
[0]:NULL
);
2983 document
= locate_document_by_ordinal_string(argv
[1]);
2984 if (!document
) return -1;
2986 return do_convert(document
);
2991 static void shi_print_usage(const char *command
) {
2993 "Usage: %s STRING LENGTH\n"
2994 "Prints STRING into LENGTH columns. Negative LENGTH means not to cut\n"
2995 "overflowing string.\n"
2996 "This should be locale and terminal agnostic.\n"),
3001 static int shi_print(int argc
, const char **argv
) {
3004 if (!argv
|| !argv
[1] || !argv
[2]) {
3005 shi_print_usage((argv
)?argv
[0]:NULL
);
3009 width
= strtol(argv
[2], NULL
, 10);
3010 if (width
< INT_MIN
) {
3012 _("Length argument must not lesser than %d.\n"), INT_MIN
);
3015 if (width
> INT_MAX
) {
3017 _("Length argument must not be greater than %d.\n"), INT_MAX
);
3022 fnprint(stdout
, argv
[1], width
);
3029 static int shi_tokenize(int argc
, const char **argv
) {
3031 if (!argv
) return 0;
3033 for (int i
= 0; i
< argc
; i
++) {
3034 printf(_(">%s<\n"), argv
[i
]);
3040 static int shi_quote(int argc
, const char **argv
) {
3041 char *escaped
, *unescaped
;
3043 if (!argv
) return 0;
3045 printf(_("Original\tQuoted\tDequoted\n"));
3046 for (int i
= 0; i
< argc
; i
++) {
3047 escaped
= shi_quote_filename((char *) argv
[i
], 0, NULL
);
3048 unescaped
= shi_dequote_filename((char *) argv
[i
], 0);
3049 printf(_(">%s<\t>%s<\t>%s<\n"), argv
[i
], escaped
, unescaped
);
3058 /* Interactive loop */
3059 void shi_loop(void) {
3060 char *command_line
= NULL
;
3061 char **command_argv
= NULL
;
3063 struct command
*command
= NULL
;
3065 printf(_("Use `help' command to get list of available commands.\n"));
3067 select_completition(COMPL_COMMAND
);
3071 command_line
= readline((prompt
) ? prompt
: _("shigofumi> "));
3072 /* Remember not parsable commands too to user be able to get back to
3074 if (command_line
&& *command_line
) {
3075 /* TODO: Omit blank lines */
3076 add_history(command_line
);
3079 command_argv
= tokenize(command_line
, &command_argc
);
3081 if (command_argv
&& command_argv
[0]) {
3082 command
= find_command(command_argv
[0]);
3085 fprintf(stderr
, _("Command not understood\n"));
3087 command
->function(command_argc
, (const char **) command_argv
);
3091 argv_free(command_argv
);
3092 zfree(command_line
);
3097 /* Non-interactive mode. Commands from @lines are processed until any command
3098 * lines remains or no error occurred. First failure terminates processing.
3099 * @lines is sequence of commands separated by '\n' or '\r'. The content is
3100 * modified during this call.
3101 * @return 0 if all command succeed, otherwise non-zero value
3103 int shi_batch(char *lines
) {
3105 char **command_argv
= NULL
;
3107 struct command
*command
= NULL
;
3110 printf(_("Batch mode started.\n"));
3112 select_completition(COMPL_COMMAND
);
3114 while (!retval
&& (command_line
= strtok(lines
, "\n\r"))) {
3115 lines
= NULL
; /* strtok(3) requires it for subsequent calls */
3117 printf(_("Processing command: %s\n"), command_line
);
3119 command_argv
= tokenize(command_line
, &command_argc
);
3121 if (command_argv
&& command_argv
[0]) {
3122 command
= find_command(command_argv
[0]);
3125 fprintf(stderr
, _("Command not understood\n"));
3128 retval
= command
->function(command_argc
,
3129 (const char **) command_argv
);
3133 argv_free(command_argv
);
3137 fprintf(stderr
, _("Command failed!\n"));
3142 #define COMMON_COMMANDS \
3143 { "accept", shi_accept_message, N_("accept commercial message"), \
3144 shi_accept_message_usage, ARGTYPE_MSGID }, \
3145 { "box", shi_box, N_("show current box details"), NULL, \
3147 { "cache", shi_cache, N_("show cache details"), NULL, \
3149 { "cd", shi_chdir, N_("change working directory"), shi_chdir_usage, \
3151 { "commercial", shi_commercial, \
3152 N_("manipulate commercial receiving box status"), \
3153 shi_commercial_usage, ARGTYPE_BOXID }, \
3154 { "compose", shi_compose, N_("compose a message"), shi_compose_usage, \
3156 { "convert", shi_convert_file, \
3157 N_("submit local document for authorized conversion"), \
3158 shi_convert_file_usage, ARGTYPE_FILE }, \
3159 { "copying", shi_copying, N_("show this program licence excerpt"), NULL, \
3161 { "debug", shi_debug, N_("set debugging"), shi_debug_usage, \
3163 { "delivery", shi_delivery, N_("get message delivery details"), \
3164 shi_delivery_usage, ARGTYPE_MSGID }, \
3165 { "findbox", shi_find_box, N_("search for a box"), shi_find_box_usage, \
3167 { "hash", shi_hash, N_("query ISDS for message hash"), \
3168 shi_hash_usage, ARGTYPE_MSGID }, \
3169 { "help", shi_help, N_("describe commands"), shi_help_usage, \
3170 ARGTYPE_COMMAND }, \
3171 { "load", shi_load_anything, \
3172 N_("load message or message delivery details from local file"), \
3173 shi_load_anything_usage, ARGTYPE_FILE }, \
3174 { "login", shi_login, N_("log into ISDS"), NULL, ARGTYPE_NONE }, \
3175 { "lsi", shi_list_incoming, N_("list received messages"), NULL, \
3177 { "lso", shi_list_outgoing, N_("list sent messages"), NULL, \
3179 { "msgi", shi_incoming_message, N_("get incoming message"), \
3180 shi_incoming_message_usage, ARGTYPE_MSGID }, \
3181 { "msgo", shi_outgoing_message, N_("get outgoing message"), \
3182 shi_outgoing_message_usage, ARGTYPE_MSGID }, \
3183 { "passwd", shi_passwd, N_("manipulate user password"), shi_passwd_usage, \
3185 { "pwd", shi_pwd, N_("print working directory"), NULL, ARGTYPE_NONE }, \
3186 { "quit", shi_quit, N_("exit shigofumi"), NULL, ARGTYPE_NONE }, \
3187 { "read", shi_read_message, N_("mark message as read"), \
3188 shi_read_message_usage, ARGTYPE_MSGID }, \
3189 { "set", shi_settings, N_("show settings"), NULL, ARGTYPE_NONE }, \
3190 { "statbox", shi_stat_box, N_("get status of a box"), shi_stat_box_usage, \
3192 { "user", shi_user, N_("show current user details"), NULL, \
3194 { "users", shi_users, N_("show box users"), shi_users_usage, \
3196 { "version", shi_version, N_("show version of this program"), NULL, \
3198 { NULL, NULL, NULL, NULL, ARGTYPE_NONE }
3200 struct command base_commands
[] = {
3202 { "quote", shi_quote
, N_("demonstrate argument escaping"), NULL
,
3204 { "print", shi_print
, N_("print string into given width"),
3205 shi_print_usage
, ARGTYPE_NONE
},
3206 { "tokenize", shi_tokenize
, N_("demonstrate arguments tokenization"), NULL
,
3212 struct command message_commands
[] = {
3213 { "authenticate", shi_authenticate
, N_("check message authenticity"),
3214 NULL
, ARGTYPE_NONE
},
3215 { "convertdoc", shi_convert_document
,
3216 N_("submit document of current message for authorized conversion"),
3217 shi_convert_document_usage
, ARGTYPE_DOCID
},
3218 { "dump", shi_dump_message
, N_("dump current message structure"),
3219 NULL
, ARGTYPE_NONE
},
3220 { "savestamp", shi_save_stamp
,
3221 N_("save time stamp of current message into local file"),
3222 shi_save_stamp_usage
, ARGTYPE_FILE
},
3223 { "savedoc", shi_save_document
,
3224 N_("save document of current message into local file"),
3225 shi_save_document_usage
, ARGTYPE_FILE
},
3226 { "save", shi_save_message
, N_("save current message into local file"),
3227 shi_save_message_usage
, ARGTYPE_FILE
},
3228 { "show", shi_show_message
, N_("show current message"), NULL
,
3230 { "verify", shi_verify
, N_("verify current message hash"), NULL
,
3235 struct command list_commands
[] = {
3236 { "show", shi_show_list
, N_("show current message list"), NULL
,
3241 #undef COMMON_COMMANDS
3244 static void main_version(void) {
3249 shi_copying(0, NULL
);
3253 static void main_usage(const char *command
) {
3255 "Usage: %s [OPTION...]\n"
3256 "Access ISDS, Process local data box messages or delivery details, submit\n"
3257 "document to authorized conversion.\n"
3260 " -c FILE use the FILE as configuration file instead of ~/%s\n"
3261 " -e COMMANDS execute COMMANDS (new line separated) and exit\n"
3262 " -V show version info and exit\n"
3264 (command
) ? command
: "shigofumi",
3269 int main(int argc
, char **argv
) {
3271 char *config_file
= NULL
;
3272 char *batch_commands
= NULL
;
3273 int retval
= EXIT_SUCCESS
;
3275 setlocale(LC_ALL
, "");
3277 /* Initialize gettext */
3278 bindtextdomain(PACKAGE
, LOCALEDIR
);
3279 textdomain(PACKAGE
);
3282 /* Parse arguments */
3284 while ((opt
= getopt(argc
, (char * const *)argv
, "c:e:V")) != -1) {
3287 config_file
= optarg
;
3290 batch_commands
= optarg
;
3294 shi_exit(EXIT_SUCCESS
);
3297 main_usage((argv
[0]) ? basename(argv
[0]): NULL
);
3298 shi_exit(EXIT_FAILURE
);
3303 if (shi_init(config_file
)) {
3304 shi_exit(EXIT_FAILURE
);
3307 /*shi_login(NULL);*/
3309 if (batch_commands
) {
3310 if (shi_batch(batch_commands
))
3311 retval
= EXIT_FAILURE
;