1 #define _XOPEN_SOURCE 600
6 #include <readline/readline.h>
7 #include <readline/history.h>
16 #include <sys/types.h>
19 #include "shigofumi.h"
20 #include "completition.h"
26 #define CONFIG_FILE ".shigofumirc"
27 #define CONFIG_SERVER "base_url"
28 #define CONFIG_USERNAME "username"
29 #define CONFIG_PASSWORD "password"
30 #define CONFIG_DEBUGLEVEL "debug_level"
31 #define CONFIG_VERIFYSERVER "verify_server"
32 #define CONFIG_CAFILE "ca_file"
33 #define CONFIG_CADIRECTORY "ca_directory"
34 #define CONFIG_CRLFILE "crl_file"
35 #define CONFIG_TIMEOUT "timeout"
36 #define CONFIG_LOGFACILITIES "log_facilities"
37 #define CONFIG_LOGFILE "log_file"
38 #define CONFIG_LOGLEVEL "log_level"
39 #define CONFIG_MARKMESSAGEREAD "mark_message_read"
40 #define CONFIG_NORMALIZEMIMETYPE "normalize_mime_type"
41 #define CONFIG_OVERWRITEFILES "overwrite_files"
42 #define CZPDEPOSIT_URL "https://www.czechpoint.cz/uschovna/"
48 cfg_opt_t configuration_syntax
[] = {
49 CFG_STR(CONFIG_SERVER
, (char *)isds_locator
, CFGF_NONE
),
50 CFG_STR(CONFIG_USERNAME
, NULL
, CFGF_NODEFAULT
),
51 CFG_STR(CONFIG_PASSWORD
, NULL
, CFGF_NODEFAULT
),
52 /*CFG_STR(CONFIG_DEBUGLEVEL, NULL, CFGF_NODEFAULT),*/
53 CFG_BOOL(CONFIG_VERIFYSERVER
, cfg_true
, CFGF_NONE
),
54 CFG_STR(CONFIG_CAFILE
, NULL
, CFGF_NODEFAULT
),
55 CFG_STR(CONFIG_CADIRECTORY
, NULL
, CFGF_NODEFAULT
),
56 CFG_STR(CONFIG_CRLFILE
, NULL
, CFGF_NODEFAULT
),
57 CFG_INT(CONFIG_TIMEOUT
, TIMEOUT
, CFGF_NONE
),
58 CFG_STR_LIST(CONFIG_LOGFACILITIES
, "{none}", CFGF_NONE
),
59 CFG_STR(CONFIG_LOGFILE
, NULL
, CFGF_NODEFAULT
),
60 CFG_INT(CONFIG_LOGLEVEL
, LOG_LEVEL
, CFGF_NONE
),
61 CFG_BOOL(CONFIG_MARKMESSAGEREAD
, cfg_false
, CFGF_NONE
),
62 CFG_BOOL(CONFIG_NORMALIZEMIMETYPE
, cfg_true
, CFGF_NONE
),
63 CFG_BOOL(CONFIG_OVERWRITEFILES
, cfg_true
, CFGF_NONE
),
74 struct command (*commands
)[] = NULL
;
78 struct isds_ctx
*cisds
= NULL
;
80 char *username
= NULL
;
81 char *password
= NULL
;
82 struct isds_list
*boxes
= NULL
;
83 struct isds_message
*message
= NULL
;
84 _Bool messages_are_outgoing
= 0;
85 struct isds_list
*messages
= NULL
;
86 unsigned long int total_messages
= 0;
87 struct isds_ctx
*czechpoint
= NULL
;
89 static void discard_credentials(void) {
96 /* Do the cleanup and exit */
97 static void shi_exit(int exit_code
) {
99 discard_credentials();
100 isds_list_free(&boxes
);
101 isds_message_free(&message
);
102 isds_list_free(&messages
);
105 oprintf(_("Logging out...\n"));
107 isds_ctx_free(&cisds
);
109 isds_ctx_free(&czechpoint
);
113 cfg_free(configuration
);
122 /* Set prompt. if @format is NULL, switch to default prompt */
123 static void set_prompt(const char *format
, ...) {
127 va_start(ap
, format
);
128 shi_vasprintf(&buffer
, format
, ap
);
132 shi_asprintf(&prompt
, _("%s> "), buffer
);
141 prompt
= strdup(_("> "));
149 static int shi_load_configuration(const char *config_file
) {
150 char *config_name
= NULL
;
153 /* Get config file */
155 config_name
= (char *) config_file
;
157 if (-1 == shi_asprintf(&config_name
, "%s/%s", getenv("HOME"),
159 fprintf(stderr
, _("Could not build configuration file name\n"));
164 /* Parse configuration */
165 configuration
= cfg_init(configuration_syntax
, CFGF_NONE
);
166 ret
= cfg_parse(configuration
, config_name
);
168 if (ret
== CFG_FILE_ERROR
) {
170 _("Error while opening configuration file `%s': %s\n"),
171 config_name
, strerror(errno
));
173 fprintf(stderr
, _("Error while parsing configuration file `%s'\n"),
176 oprintf(_("Using default configuration\n"));
179 if (config_name
!= config_file
) free(config_name
);
184 static void finish_isds_operation(struct isds_ctx
*ctx
, isds_error err
) {
185 shi_progressbar_finish();
187 if (isds_long_message(ctx
))
188 fprintf(stderr
, _("Error occurred: %s: %s\n"), isds_strerror(err
),
189 isds_long_message(ctx
));
191 fprintf(stderr
, _("Error occurred: %s\n"), isds_strerror(err
));
196 void logger(isds_log_facility facility
, isds_log_level level
,
197 const char *message
, int length
, void *data
) {
199 ssize_t written
, left
= length
;
202 fd
= *((int *) data
);
203 /*printf("\033[32mLOG(%02d,%02d): ", facility, level);
204 printf("%.*s", length, message);
208 written
= write(fd
, message
+ length
- left
, left
);
211 _("Could not save log message into log file: %s\n"
212 "Log message discarded!\n"),
223 /* Redirect ISDS log to file if @file is not NULL. */
224 static int do_log_to_file(const char *file
) {
226 logger_fd
= open_file_for_writing(file
, 0, 1);
227 if (logger_fd
== -1) {
228 fprintf(stderr
, _("Could not redirect ISDS log to file `%s'\n"),
232 isds_set_log_callback(logger
, &logger_fd
);
238 /* Add log facility based on its name. */
239 static int add_log_facility(isds_log_facility
*facilities
, const char *name
) {
240 if (!facilities
) return -1;
242 if (!strcmp(name
, "none")) *facilities
|= ILF_NONE
;
243 else if (!strcmp(name
, "http")) *facilities
|= ILF_HTTP
;
244 else if (!strcmp(name
, "soap")) *facilities
|= ILF_SOAP
;
245 else if (!strcmp(name
, "isds")) *facilities
|= ILF_ISDS
;
246 else if (!strcmp(name
, "file")) *facilities
|= ILF_FILE
;
247 else if (!strcmp(name
, "sec")) *facilities
|= ILF_SEC
;
248 else if (!strcmp(name
, "xml")) *facilities
|= ILF_XML
;
249 else if (!strcmp(name
, "all")) *facilities
|= ILF_ALL
;
251 fprintf(stderr
, _("%s: Unknown log facility\n"), name
);
259 /* Save log facility into confuse configuration */
260 static void save_log_facility(int level
) {
261 cfg_setlist(configuration
, CONFIG_LOGFACILITIES
, 0);
263 if (level
== ILF_ALL
) {
264 cfg_addlist(configuration
, CONFIG_LOGFACILITIES
, 1, "all");
267 if (level
== ILF_NONE
) {
268 cfg_addlist(configuration
, CONFIG_LOGFACILITIES
, 1, "none");
271 if (level
& ILF_HTTP
)
272 cfg_addlist(configuration
, CONFIG_LOGFACILITIES
, 1, "http");
273 if (level
& ILF_SOAP
)
274 cfg_addlist(configuration
, CONFIG_LOGFACILITIES
, 1, "soap");
275 if (level
& ILF_ISDS
)
276 cfg_addlist(configuration
, CONFIG_LOGFACILITIES
, 1, "isds");
277 if (level
& ILF_FILE
)
278 cfg_addlist(configuration
, CONFIG_LOGFACILITIES
, 1, "file");
280 cfg_addlist(configuration
, CONFIG_LOGFACILITIES
, 1, "sec");
282 cfg_addlist(configuration
, CONFIG_LOGFACILITIES
, 1, "xml");
286 /* Clamp long int to unsigned int */
287 static unsigned int normalize_timeout(long int raw
) {
289 oprintf(_("Configured network timeout is less then 0. "
293 if (raw
> UINT_MAX
) {
294 oprintf(_("Configured network timeout is greater then %1$u. "
295 "Clamped to %1$u.\n"), UINT_MAX
);
298 return (unsigned int) raw
;
302 /* Clamp long int to <0;100> */
303 static unsigned int normalize_log_level(long int raw
) {
305 oprintf(_("Configured log level is less then 0. Clamped to 0.\n"));
309 oprintf(_("Configured log level is greater then %1$u. "
310 "Clamped to %1$u.\n"), ILL_ALL
);
313 if (raw
> UINT_MAX
) {
314 oprintf(_("Configured log level is greater then %1$u. "
315 "Clamped to %1$u.\n"), UINT_MAX
);
318 return (unsigned int) raw
;
322 static int shi_init(const char *config_file
) {
325 unsigned int timeout
, log_level
;
326 isds_log_facility log_facility
= ILF_NONE
;
328 oprintf(_("This is Shigofumi, an ISDS client. "
329 "Have a nice e-government.\n"));
331 /* Do not permute arguments in getopt() */
332 if (setenv("POSIXLY_CORRECT", "", 1)) {
334 _("Could not set POSIXLY_CORRECT environment variable\n"));
338 /* Load configuration */
339 if (shi_load_configuration(config_file
))
341 timeout
= normalize_timeout(cfg_getint(configuration
, CONFIG_TIMEOUT
));
342 log_level
= normalize_log_level(cfg_getint(configuration
, CONFIG_LOGLEVEL
));
345 rl_readline_name
= "shigofumi";
346 rl_filename_quote_characters
= "\\ >";
347 rl_filename_quoting_function
= shi_quote_filename
;
348 rl_filename_dequoting_function
= shi_dequote_filename
;
349 rl_char_is_quoted_p
= shi_char_is_quoted
;
351 /* Initialize ISDS */
354 fprintf(stderr
, _("Could not initialize libisds library: %s\n"),
359 /* Set ISDS logging */
360 value
= cfg_getstr(configuration
, CONFIG_LOGFILE
);
361 if (do_log_to_file(value
))
363 for (int i
= 0; i
< cfg_size(configuration
, CONFIG_LOGFACILITIES
); i
++) {
364 if (add_log_facility(&log_facility
,
365 cfg_getnstr(configuration
, CONFIG_LOGFACILITIES
, i
)))
369 isds_set_logging(log_facility
, log_level
);
371 /* Set ISDS context up */
372 cisds
= isds_ctx_create();
374 fprintf(stderr
, _("Could not create ISDS context\n"));
377 err
= isds_set_timeout(cisds
, timeout
);
379 fprintf(stderr
, _("Could not set ISDS network timeout: %s\n"),
382 err
= isds_set_progress_callback(cisds
, shi_progressbar
, NULL
);
384 fprintf(stderr
, _("Could not register network progress bar: %s: %s\n"),
385 isds_strerror(err
), isds_long_message(cisds
));
387 err
= isds_set_opt(cisds
, IOPT_NORMALIZE_MIME_TYPE
,
388 cfg_getbool(configuration
, CONFIG_NORMALIZEMIMETYPE
));
391 cfg_getbool(configuration
, CONFIG_NORMALIZEMIMETYPE
) ?
392 _("Could not enable MIME type normalization: %s: %s\n") :
393 _("Could not disable MIME type normalization: %s: %s\n"),
394 isds_strerror(err
), isds_long_message(cisds
));
396 if (!cfg_getbool(configuration
, CONFIG_VERIFYSERVER
)) {
397 oprintf(_("Warning: Shigofumi disabled server identity verification "
398 "on user request!\n"));
399 err
= isds_set_opt(cisds
, IOPT_TLS_VERIFY_SERVER
, 0);
402 _("Could not disable server identity verification: "
404 isds_strerror(err
), isds_long_message(cisds
));
407 if ((value
= cfg_getstr(configuration
, CONFIG_CAFILE
))) {
408 err
= isds_set_opt(cisds
, IOPT_TLS_CA_FILE
, value
);
411 _("Could not set file with CA certificates: %s: %s: %s\n"),
412 value
, isds_strerror(err
), isds_long_message(cisds
));
415 if ((value
= cfg_getstr(configuration
, CONFIG_CADIRECTORY
))) {
416 err
= isds_set_opt(cisds
, IOPT_TLS_CA_DIRECTORY
, value
);
419 _("Could not set directory with CA certificates: "
421 value
, isds_strerror(err
), isds_long_message(cisds
));
424 if ((value
= cfg_getstr(configuration
, CONFIG_CRLFILE
))) {
425 err
= isds_set_opt(cisds
, IOPT_TLS_CRL_FILE
, value
);
427 fprintf(stderr
, _("Could not set file with CRL: %s: %s: %s\n"),
428 value
, isds_strerror(err
), isds_long_message(cisds
));
433 /* Set Czech POINT context up */
434 czechpoint
= isds_ctx_create();
436 fprintf(stderr
, _("Could not create Czech POINT context\n"));
439 err
= isds_set_timeout(czechpoint
, timeout
);
441 fprintf(stderr
, _("Could not set Czech POINT network timeout: %s\n"),
444 err
= isds_set_progress_callback(czechpoint
, shi_progressbar
, NULL
);
446 fprintf(stderr
, "Could not register network progress bar: %s: %s\n",
447 isds_strerror(err
), isds_long_message(cisds
));
454 static int shi_quit(int argc
, const char **argv
) {
455 shi_exit(EXIT_SUCCESS
);
461 static void shi_help_usage(const char *command
) {
463 "Usage: %s [COMMAND]\n"
464 "Show COMMAND manual or list of currently available commands.\n"
470 static int shi_help(int argc
, const char **argv
) {
471 size_t command_width
= 14;
475 fprintf(stderr
, _("No command is available\n"));
479 if (argc
== 2 && argv
[1] && *argv
[1]) {
480 /* Show usage for given command */
481 for (i
= 0; (*commands
)[i
].name
; i
++) {
482 if (!strcmp((*commands
)[i
].name
, argv
[1])) {
483 if ((*commands
)[i
].usage
)
484 (*commands
)[i
].usage((*commands
)[i
].name
);
485 else if ((*commands
)[i
].description
) {
486 ohprint((*commands
)[i
].name
, command_width
);
487 oprintf(" %s\n", _((*commands
)[i
].description
));
491 _("%s: %s: Command description not defined\n"),
496 fprintf(stderr
, _("%s: %s: No such command exists\n"), argv
[0], argv
[1]);
500 /* Or list all commands */
501 oprintf(_("Following commands are available:\n"));
502 for (i
= 0; (*commands
)[i
].name
; i
++) {
503 ohprint((*commands
)[i
].name
, command_width
);
504 oprintf(" %s\n", _((*commands
)[i
].description
));
511 static void show_version(void) {
512 char *libisds_version
= isds_version();
514 oprintf(_("This is Shigofumi version %s.\n"), PACKAGE_VERSION
);
520 rl_library_version
, libisds_version
);
521 free(libisds_version
);
525 static int shi_version(int argc
, const char **argv
) {
530 "It's a shigofumi. A letter delivered from the afterlife. (Fumika)\n"
531 "A message can not be delivered to dead person. (ISDS specification)\n"
532 "Virtual and real world. They can be compatible. (Program author)\n"
538 static int shi_copying(int argc
, const char **argv
) {
540 "This is Shigofumi, an ISDS client.\n"
541 "Copyright (C) 2010 Petr Pisar\n"
543 "This program is free software: you can redistribute it and/or modify\n"
544 "it under the terms of the GNU General Public License as published by\n"
545 "the Free Software Foundation, either version 3 of the License, or\n"
546 "(at your option) any later version.\n"
548 "This program is distributed in the hope that it will be useful,\n"
549 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
550 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
551 "GNU General Public License for more details.\n"
553 "You should have received a copy of the GNU General Public License\n"
554 "along with this program. If not, see <http://www.gnu.org/licenses/>.\n"
560 static int shi_cache(int argc
, const char **argv
) {
561 const struct isds_list
*item
;
565 for (item
= boxes
, i
= 0; item
; item
= item
->next
, i
++);
567 "Cached box list: %zu\n"),
573 "Cached message list:\n"
575 "\tMessages: %'lu\n"),
576 (messages_are_outgoing
) ? _("Outgoing") : _("Incoming"),
581 oprintf(_("Cached message: %s\n"),
582 (message
->envelope
&& message
->envelope
->dmID
) ?
583 message
->envelope
->dmID
: _("<Unknown ID>"));
590 static void shi_chdir_usage(const char *command
) {
592 "Usage: %s [DIRECTORY]\n"
593 "Change working directory to DIRECTORY.\n"
594 "If no DIRECTORY is supplied, HOME directory will be used.\n"),
599 static int shi_chdir(int argc
, const char **argv
) {
600 const char *directory
= NULL
;
602 if (!argv
|| argc
> 2) {
603 shi_chdir_usage((argv
) ? argv
[0] : NULL
);
607 if (argc
== 2 && argv
[1] && *argv
[1])
610 directory
= getenv("HOME");
612 oprintf("Environment variable HOME does not exist\n");
616 if (chdir(directory
)) {
617 oprintf(_("Could not change working directory: %s: %s\n"), directory
,
626 static int shi_pwd(int argc
, const char **argv
) {
627 char *buffer
= NULL
, *newbuffer
;
630 while (length
+= 1024) {
631 newbuffer
= realloc(buffer
, length
);
633 fprintf(stderr
, _("Error: Not enough memory\n"));
639 if (getcwd(buffer
, length
)) {
640 oprintf("%s\n", buffer
);
646 fprintf(stderr
, _("Error: Current directory string is too long\n"));
653 static int do_login(void) {
657 oprintf(_("Unattended mode detected. "
658 "Make sure credentials have been preset.\n"));
660 oprintf(_("You are going to insert credentials for your account.\n"
661 "Leave blank line to choose default value.\n"));
663 select_completition(COMPL_NONE
);
665 /* Ask for server base URL */
666 shi_replace_string(&server
, _("Input ISDS base URL: "),
667 cfg_getstr(configuration
, CONFIG_SERVER
), batch_mode
);
669 /* Ask for user name */
670 shi_replace_string(&username
, _("Input ISDS user name: "),
671 cfg_getstr(configuration
, CONFIG_USERNAME
), batch_mode
);
674 password = ask_for_password(_("Input ISDS password: "));
677 fprintf(stderr, _("Could not read ISDS password\n"));
678 shi_exit(EXIT_FAILURE);
681 shi_replace_password(&password
, _("Input ISDS password: "),
682 cfg_getstr(configuration
, CONFIG_PASSWORD
), batch_mode
);
684 select_completition(COMPL_COMMAND
);
687 printf(_("Logging in...\n"));
688 err
= isds_login(cisds
, server
, username
, password
, NULL
);
689 finish_isds_operation(cisds
, err
);
691 printf(_("Log-in failed\n"));
695 oprintf(_("Logged in.\n"));
700 static struct isds_DbOwnerInfo
*do_box(void) {
702 struct isds_DbOwnerInfo
*box
= NULL
;
704 printf(_("Getting box details you are logged in...\n"));
705 err
= isds_GetOwnerInfoFromLogin(cisds
, &box
);
706 finish_isds_operation(cisds
, err
);
712 static int shi_box(int argc
, const char **argv
) {
713 struct isds_DbOwnerInfo
*box
= NULL
;
718 format_DbOwnerInfo(box
);
720 isds_DbOwnerInfo_free(&box
);
725 /* Get info about box with @id.
726 * @id is UTF-8 encoded
727 * Return NULL in case of error, otherwise box description that caller must
729 static struct isds_DbOwnerInfo
*stat_box(const char *id
) {
731 struct isds_DbOwnerInfo criteria
;
732 struct isds_list
*boxes
= NULL
, *item
;
733 struct isds_DbOwnerInfo
*box
= NULL
;
734 char *id_locale
= NULL
;
736 if (!id
|| !*id
) return NULL
;
738 id_locale
= utf82locale(id
);
739 memset(&criteria
, 0, sizeof(criteria
));
740 criteria
.dbID
= (char *) id
;
742 printf(_("Getting details about box with ID `%s'...\n"), id_locale
);
743 err
= isds_FindDataBox(cisds
, &criteria
, &boxes
);
744 finish_isds_operation(cisds
, err
);
747 for(item
= boxes
; item
; item
= item
->next
) {
748 if (!item
->data
) continue;
751 fprintf(stderr
, _("Error: More boxes match ID `%s'\n"), id_locale
);
755 box
= (struct isds_DbOwnerInfo
*) item
->data
;
762 isds_list_free(&boxes
);
768 static void shi_commercial_usage(const char *command
) {
770 "Usage: %s [-0|-1] [BOX_ID]\n"
771 "Manipulate commercial receiving box status.\n"
772 " -O switch off receiving of commercial messages\n"
773 " -1 switch on receiving of commercial messages\n"
774 " BOX_ID affects box with ID BOX_ID; default is box you are logged in\n"
775 "If no option is given, show current commercial receiving status.\n"),
780 /* Manipulate commercial receiving box status */
781 static int shi_commercial(int argc
, const char **argv
) {
783 struct isds_DbOwnerInfo
*box
= NULL
;
786 char *box_id
= NULL
, *box_id_locale
= NULL
;
791 while ((opt
= getopt(argc
, (char * const *)argv
, "01")) != -1) {
800 shi_commercial_usage((argv
)?argv
[0]:NULL
);
804 if (optind
+ 1 < argc
) {
805 fprintf(stderr
, _("Bad invocation\n"));
806 shi_commercial_usage((argv
)?argv
[0]:NULL
);
810 if (!argv
[optind
] || !*argv
[optind
]) {
811 /* Get current box ID */
813 if (!box
|| !box
->dbID
|| !*box
->dbID
) {
814 isds_DbOwnerInfo_free(&box
);
815 fprintf(stderr
, _("Could not get current box ID\n"));
818 box_id
= box
->dbID
; static_box_id
= 1;
819 box_id_locale
= utf82locale(box_id
);
821 /* Box ID supplied as argument */
822 box_id_locale
= (char *) argv
[optind
];
823 box_id
= locale2utf8(box_id_locale
); static_box_id
= 0;
825 fprintf(stderr
, _("Could not convert box ID `%s' to UTF-8\n"),
832 if (!box
) box
= stat_box(box_id
);
834 fprintf(stderr
, _("Could not get details about box ID `%s'\n"),
840 oprintf(_("Commercial receiving status of box `%s': "), box_id_locale
);
841 if (!box
->dbOpenAddressing
)
842 oprintf(_("Unknown\n"));
843 else if (*box
->dbOpenAddressing
)
844 oprintf(_("Positive\n"));
846 oprintf(_("Negative\n"));
848 char *refnumber
= NULL
;
850 _("Switching `%s' box commercial receiving on...\n"):
851 _("Switching `%s' box commercial receiving off...\n"),
853 err
= isds_switch_commercial_receiving(cisds
, box_id
, action
,
855 finish_isds_operation(cisds
, err
);
858 char *refnumber_locale
= utf82locale(refnumber
);
859 oprintf(_("Commercial receiving status successfully changed. "
860 "Assigned reference number: %s\n"),
863 oprintf(_("Commercial receiving status has not been changed.\n"));
870 if (!static_box_id
) free(box_id
);
871 else free(box_id_locale
);
872 isds_DbOwnerInfo_free(&box
);
877 static int shi_user(int argc
, const char **argv
) {
879 struct isds_DbUserInfo
*user
= NULL
;
881 printf(_("Getting user details you are logged as...\n"));
882 err
= isds_GetUserInfoFromLogin(cisds
, &user
);
883 finish_isds_operation(cisds
, err
);
886 format_DbUserInfo(user
);
888 isds_DbUserInfo_free(&user
);
893 static void shi_users_usage(const char *command
) {
896 "Get list of users having access to box with BOX_ID.\n"),
901 static int shi_users(int argc
, const char **argv
) {
903 struct isds_list
*users
= NULL
, *item
;
906 if (!argv
|| !argv
[1] || !*argv
[1]) {
907 shi_users_usage((argv
)?argv
[0]:NULL
);
911 printf(_("Getting users of box with ID `%s'...\n"), argv
[1]);
912 err
= isds_GetDataBoxUsers(cisds
, argv
[1], &users
);
913 finish_isds_operation(cisds
, err
);
916 for (item
= users
, ordinar
= 0; item
; item
=item
->next
) {
917 if (!item
->data
) continue;
919 oprintf(_("\n* User #%d:\n"), ordinar
);
920 format_DbUserInfo(item
->data
);
923 oprintf(_("Empty list of users returned.\n"));
925 isds_list_free(&users
);
930 static int show_password_expiration(void) {
932 struct timeval
*expiration
= NULL
;
934 err
= isds_get_password_expiration(cisds
, &expiration
);
935 finish_isds_operation(cisds
, err
);
937 fprintf(stderr
, "Could not get password expiration time\n");
941 print_header_timeval(_("Your password expires at"), expiration
);
947 /* Change password in ISDS */
948 static int do_passwd(void) {
949 char *old_password
= NULL
;
950 char *new_password
= NULL
;
951 char *new_password2
= NULL
;
952 isds_error err
= IE_ERROR
;
955 select_completition(COMPL_NONE
);
958 "You are going to change your password. If you don't want to change your\n"
959 "password, insert empty string or EOF.\n"
961 "You will be asked for your current (old) password and then for new password.\n"
962 "ISDS forces some criteria new password must fulfill. Current rules are:\n"
963 "\tLength: minimal 8, maximal 32 characters\n"
964 "\tMust contain at least: 1 upper case letter, 1 lower case letter, 1 digit\n"
965 "\tAllowed alphabet: [a-z][A-Z][0-9][!#$%%&()*+,-.:=?@[]_{}|~]\n"
966 "\tMust differ from last 255 passwords\n"
967 "\tMust not contain user ID\n"
968 "\tMust not contain sequence of three or more same characters\n"
969 "\tMust not start with `qwert', `asdgf', or `12345'\n"
970 "Finally, you must repeat your new password to avoid mistakes.\n"
971 "After password change will be confirmed, you must log in again as password\n"
972 "is transmitted to server on each request.\n"
975 old_password
= ask_for_password(_("Old password: "));
976 if (!old_password
|| *old_password
== '\0') {
977 fprintf(stderr
, _("No password supplied\n"));
981 new_password
= ask_for_password(_("New password: "));
982 if (!new_password
|| *new_password
== '\0') {
983 fprintf(stderr
, _("No password supplied\n"));
987 new_password2
= ask_for_password(_("Repeat new password: "));
988 if (!new_password2
|| new_password2
== '\0') {
989 fprintf(stderr
, _("No password supplied\n"));
993 if (strcmp(new_password
, new_password2
)) {
994 fprintf(stderr
, _("New passwords differ\n"));
998 printf(_("Changing password...\n"));
999 err
= isds_change_password(cisds
, old_password
, new_password
);
1000 finish_isds_operation(cisds
, err
);
1002 printf(_("Password change failed\n"));
1005 oprintf(_("Password HAS been successfully changed.\n"));
1011 oprintf(_("Password has NOT been changed!\n"));
1016 free(new_password2
);
1019 select_completition(COMPL_COMMAND
);
1022 "Remember, ISDS password has limited life time.\n"));
1027 static void shi_passwd_usage(const char *command
) {
1030 "Manipulate user password or change it if no option given.\n"
1033 " -S show password expiration time\n"
1038 static int shi_passwd(int argc
, const char **argv
) {
1042 while ((opt
= getopt(argc
, (char * const *)argv
, "S")) != -1) {
1045 return show_password_expiration();
1047 shi_passwd_usage(argv
[0]);
1051 if (optind
!= argc
|| argc
> 1) {
1052 fprintf(stderr
, _("Bad invocation\n"));
1053 shi_passwd_usage((argv
)?argv
[0]:NULL
);
1061 static int shi_login(int argc
, const char **argv
) {
1062 discard_credentials();
1063 if (do_login()) return -1;
1064 cfg_setstr(configuration
, CONFIG_SERVER
, server
);
1065 cfg_setstr(configuration
, CONFIG_USERNAME
, username
);
1066 cfg_setstr(configuration
, CONFIG_PASSWORD
, password
);
1067 show_password_expiration();
1072 static void shi_debug_usage(const char *command
) {
1074 "Usage: %s -l LEVEL [-f FACILITY...] [{-e | -o FILE}]\n"
1075 "Debug FACILITIES on LEVEL.\n"
1077 "-l LEVEL set log level, valid interval <%d,%d>, default is %d\n"
1078 " %d is no logging, %d critical, %d errors,\n"
1079 " %d warnings, %d info, %d debug, %d all\n"
1080 "-f FACILITY debug only given facility, repeat this option to debug\n"
1081 " more facilities; valid values: none, http, soap, isds,\n"
1082 " file, sec, xml, all; default is none\n"
1083 "-e write debug log into stderr\n"
1084 "-o FILE append debug log to FILE\n"
1087 ILL_NONE
, ILL_ALL
, ILL_NONE
,
1088 ILL_NONE
, ILL_CRIT
, ILL_ERR
, ILL_WARNING
,
1089 ILL_INFO
, ILL_DEBUG
, ILL_ALL
);
1092 static int shi_debug(int argc
, const char **argv
) {
1094 int log_level
= ILL_NONE
;
1095 isds_log_facility log_facility
= ILF_NONE
;
1097 _Bool close_log
= 0;
1100 while ((opt
= getopt(argc
, (char * const *)argv
, "l:f:eo:")) != -1) {
1103 log_level
= normalize_log_level(atoi(optarg
));
1106 if (add_log_facility(&log_facility
, optarg
)) return -1;
1114 shi_debug_usage(argv
[0]);
1118 if (optind
== 1 || optind
!= argc
) {
1119 fprintf(stderr
, _("Bad invocation\n"));
1120 shi_debug_usage(argv
[0]);
1126 isds_set_log_callback(NULL
, NULL
);
1128 if (logger_fd
!= -1) {
1129 if (-1 == close(logger_fd
)) {
1130 fprintf(stderr
, _("Closing log file failed: %s\n"),
1135 cfg_setstr(configuration
, CONFIG_LOGFILE
, NULL
);
1137 if (do_log_to_file(file
))
1139 if (file
) cfg_setstr(configuration
, CONFIG_LOGFILE
, file
);
1141 /* Set log levels */
1142 isds_set_logging(log_facility
, log_level
);
1143 cfg_setint(configuration
, CONFIG_LOGLEVEL
, log_level
);
1144 save_log_facility(log_facility
);
1150 static void show_setting_str(const char *variable
, _Bool cenzore
) {
1151 if (!variable
) return;
1153 const char *value
= cfg_getstr(configuration
, variable
);
1157 oprintf(_("%s = <set>\n"), variable
);
1159 oprintf(_("%s = `%s'\n"), variable
, value
);
1161 oprintf(_("%s = <unset>\n"), variable
);
1166 static void show_setting_boolean(const char *variable
) {
1167 if (!variable
) return;
1169 _Bool value
= cfg_getbool(configuration
, variable
);
1172 oprintf(_("%s = <true>\n"), variable
);
1174 oprintf(_("%s = <false>\n"), variable
);
1179 static void show_setting_int(const char *variable
) {
1180 if (!variable
) return;
1182 long int value
= cfg_getint(configuration
, variable
);
1184 oprintf(_("%s = %ld\n"), variable
, value
);
1188 static void show_setting_strlist(const char *variable
) {
1189 if (!variable
) return;
1191 int length
= cfg_size(configuration
, variable
);
1194 oprintf(_("%s = <unset>\n"), variable
);
1196 oprintf(_("%s = {"), variable
);
1197 for (int i
= 0; i
< length
; i
++) {
1198 const char *value
= cfg_getnstr(configuration
, variable
, i
);
1200 oprintf(_("`%s', "), value
);
1202 oprintf(_("`%s'}\n"), value
);
1208 static int shi_settings(int argc
, const char **argv
) {
1210 int log_level = ILL_NONE;
1211 isds_log_facility log_facility = ILF_NONE;
1213 _Bool close_log = 0;*/
1217 while ((opt = getopt(argc, (char * const *)argv, "l:f:eo:")) != -1) {
1220 log_level = normalize_log_level(atoi(optarg));
1223 if (add_log_facility(&log_facility, optarg)) return -1;
1231 shi_debug_usage(argv[0]);
1235 if (optind == 1 || optind != argc) {
1236 printf(_("Bad invocation\n"));
1237 shi_debug_usage(argv[0]);
1242 oprintf(_("Current settings:\n"));
1244 show_setting_str(CONFIG_SERVER
, 0);
1245 show_setting_str(CONFIG_USERNAME
, 0);
1246 show_setting_str(CONFIG_PASSWORD
, 1),
1247 show_setting_boolean(CONFIG_VERIFYSERVER
);
1248 show_setting_str(CONFIG_CAFILE
, 0);
1249 show_setting_str(CONFIG_CADIRECTORY
, 0);
1250 show_setting_str(CONFIG_CRLFILE
, 0);
1251 show_setting_int(CONFIG_TIMEOUT
);
1252 show_setting_strlist(CONFIG_LOGFACILITIES
);
1253 show_setting_str(CONFIG_LOGFILE
, 0);
1254 show_setting_int(CONFIG_LOGLEVEL
);
1255 show_setting_boolean(CONFIG_MARKMESSAGEREAD
);
1256 show_setting_boolean(CONFIG_NORMALIZEMIMETYPE
);
1257 show_setting_boolean(CONFIG_OVERWRITEFILES
);
1263 static void shi_find_box_usage(const char *command
) {
1265 "Usage: %s {OPTION... | BOX_ID}\n"
1266 "Get information about box with BOX_ID or boxes meeting other criteria.\n"
1267 "Each search option requires an argument:\n"
1268 " -t box type; accepted values:\n"
1269 " FO Private individual\n"
1270 " PFO Self-employed individual\n"
1271 " PFO_ADVOK Lawyer\n"
1272 " PFO_DANPOR Tax advisor\n"
1273 " PFO_INSSPR Insolvency administrator\n"
1274 " PO Organisation\n"
1275 " PO_ZAK Organization based by law\n"
1276 " PO_REQ Organization based on request\n"
1277 " OVM Public authority\n"
1278 " OVM_NOTAR Notary\n"
1279 " OVM_EXEKUT Executor\n"
1280 " OVM_REQ Public authority based on request\n"
1281 " -j identity number\n"
1283 "Person name options:\n"
1287 " -b last name at birth\n"
1288 " -s subject name\n"
1291 " -d birth date (locale or full ISO 8601 date)\n"
1293 " -y birth county\n"
1299 " -z number in street\n"
1300 " -Z number in municipality\n"
1307 " -p phone number\n"
1309 " -r registry code\n"
1310 " -a box status; accepted values:\n"
1311 " ACCESSIBLE Accessible\n"
1312 " TEMP_INACCESSIBLE Temporary inaccessible\n"
1313 " NOT_YET_ACCESSIBLE Not yet accessible\n"
1314 " PERM_INACCESSIBLE Permanently inaccessible\n"
1315 " REMOVED Deleted\n"
1316 " -o act as public authority; boolean values: 0 is false, 1 is true\n"
1317 " -k receive commercial messages; boolean values\n"
1319 "Not all option combinations are meaningful or allowed. For example box\n"
1320 "type is always required (except direct box ID query).\n"
1321 "ISDS can refuse to answer to much broad query. Not all boxes are searchable\n"
1328 /* Allow reassignment */
1329 #define FILL_OR_LEAVE(variable, locale) { \
1331 (variable) = locale2utf8(locale); \
1332 if (!(variable)) { \
1333 fprintf(stderr, _("Error: Not enough memory\n")); \
1339 #define CALLOC_OR_LEAVE(structure) { \
1340 if (!(structure)) { \
1341 (structure) = calloc(1, sizeof(*(structure))); \
1342 if (!(structure)) { \
1343 fprintf(stderr, _("Error: Not enough memory\n")); \
1350 #define FILL_BOOLEAN_OR_LEAVE(variable, locale) { \
1352 (variable) = malloc(sizeof(*(variable))); \
1353 if (!(variable)) { \
1354 fprintf(stderr, _("Error: Not enough memory\n")); \
1358 if (!strcmp((locale), "0")) *(variable) = 0; \
1359 else if (!strcmp((locale), "1")) *(variable) = 1; \
1361 fprintf(stderr, _("%s: %s: Unknown boolean value\n"), \
1362 argv[0], (locale)); \
1368 #define FILL_LONGINT_OR_LEAVE(variable, locale) { \
1369 if (!(locale) || !*(locale)) { \
1370 fprintf(stderr, _("%s: Empty integer value\n"), argv[0]); \
1376 (variable) = malloc(sizeof(*(variable))); \
1377 if (!(variable)) { \
1378 fprintf(stderr, _("Error: Not enough memory\n")); \
1382 (*variable) = strtol((locale), &endptr, 0); \
1384 fprintf(stderr, _("%s: %s: Invalid integer value\n"), \
1385 argv[0], (locale)); \
1391 static int shi_find_box(int argc
, const char **argv
) {
1394 struct isds_DbOwnerInfo
*criteria
= NULL
;
1395 struct isds_list
*item
;
1399 if (!argv
|| !argv
[1] || !*argv
[1]) {
1400 fprintf(stderr
, _("Error: No argument supplied\n"));
1401 shi_find_box_usage((argv
)?argv
[0]:NULL
);
1405 criteria
= calloc(1, sizeof(*criteria
));
1407 fprintf(stderr
, _("Error: Not enough memory\n"));
1414 while ((opt
= getopt(argc
, (char * const *)argv
, "t:j:s:"
1415 "f:m:l:b:s:" "d:w:y:c:" "W:S:z:Z:P:C:"
1416 "n:e:p:i:r:a:o:k:")) != -1) {
1419 criteria
->dbType
= malloc(sizeof(*criteria
->dbType
));
1420 if (!criteria
->dbType
) {
1421 fprintf(stderr
, _("Error: Not enough memory\n"));
1425 if (!strcmp(optarg
, "FO"))
1426 *criteria
->dbType
= DBTYPE_FO
;
1427 else if (!strcmp(optarg
, "PFO"))
1428 *criteria
->dbType
= DBTYPE_PFO
;
1429 else if (!strcmp(optarg
, "PFO_ADVOK"))
1430 *criteria
->dbType
= DBTYPE_PFO_ADVOK
;
1431 else if (!strcmp(optarg
, "PFO_DANPOR"))
1432 *criteria
->dbType
= DBTYPE_PFO_DANPOR
;
1433 else if (!strcmp(optarg
, "PFO_INSSPR"))
1434 *criteria
->dbType
= DBTYPE_PFO_INSSPR
;
1435 else if (!strcmp(optarg
, "PO"))
1436 *criteria
->dbType
= DBTYPE_PO
;
1437 else if (!strcmp(optarg
, "PO_ZAK"))
1438 *criteria
->dbType
= DBTYPE_PO_ZAK
;
1439 else if (!strcmp(optarg
, "PO_REQ"))
1440 *criteria
->dbType
= DBTYPE_PO_REQ
;
1441 else if (!strcmp(optarg
, "OVM"))
1442 *criteria
->dbType
= DBTYPE_OVM
;
1443 else if (!strcmp(optarg
, "OVM_NOTAR"))
1444 *criteria
->dbType
= DBTYPE_OVM_NOTAR
;
1445 else if (!strcmp(optarg
, "OVM_EXEKUT"))
1446 *criteria
->dbType
= DBTYPE_OVM_EXEKUT
;
1447 else if (!strcmp(optarg
, "OVM_REQ"))
1448 *criteria
->dbType
= DBTYPE_OVM_REQ
;
1450 fprintf(stderr
, _("%s: %s: Unknown box type\n"),
1458 FILL_OR_LEAVE(criteria
->ic
, optarg
);
1463 CALLOC_OR_LEAVE(criteria
->personName
);
1464 FILL_OR_LEAVE(criteria
->personName
->pnFirstName
, optarg
);
1467 CALLOC_OR_LEAVE(criteria
->personName
);
1468 FILL_OR_LEAVE(criteria
->personName
->pnMiddleName
, optarg
);
1471 CALLOC_OR_LEAVE(criteria
->personName
);
1472 FILL_OR_LEAVE(criteria
->personName
->pnLastName
, optarg
);
1475 CALLOC_OR_LEAVE(criteria
->personName
);
1476 FILL_OR_LEAVE(criteria
->personName
->pnLastNameAtBirth
, optarg
);
1479 FILL_OR_LEAVE(criteria
->firmName
, optarg
);
1484 CALLOC_OR_LEAVE(criteria
->birthInfo
);
1485 criteria
->birthInfo
->biDate
= datestring2tm(optarg
);
1486 if (!criteria
->birthInfo
->biDate
) {
1487 fprintf(stderr
, _("Error: Could not parse date: %s\n"),
1494 CALLOC_OR_LEAVE(criteria
->birthInfo
);
1495 FILL_OR_LEAVE(criteria
->birthInfo
->biCity
, optarg
);
1498 CALLOC_OR_LEAVE(criteria
->birthInfo
);
1499 FILL_OR_LEAVE(criteria
->birthInfo
->biCounty
, optarg
);
1502 CALLOC_OR_LEAVE(criteria
->birthInfo
);
1503 FILL_OR_LEAVE(criteria
->birthInfo
->biState
, optarg
);
1508 CALLOC_OR_LEAVE(criteria
->address
);
1509 FILL_OR_LEAVE(criteria
->address
->adCity
, optarg
);
1512 CALLOC_OR_LEAVE(criteria
->address
);
1513 FILL_OR_LEAVE(criteria
->address
->adStreet
, optarg
);
1516 CALLOC_OR_LEAVE(criteria
->address
);
1517 FILL_OR_LEAVE(criteria
->address
->adNumberInStreet
, optarg
);
1520 CALLOC_OR_LEAVE(criteria
->address
);
1521 FILL_OR_LEAVE(criteria
->address
->adNumberInMunicipality
,
1525 CALLOC_OR_LEAVE(criteria
->address
);
1526 FILL_OR_LEAVE(criteria
->address
->adZipCode
, optarg
);
1529 CALLOC_OR_LEAVE(criteria
->address
);
1530 FILL_OR_LEAVE(criteria
->address
->adState
, optarg
);
1535 FILL_OR_LEAVE(criteria
->nationality
, optarg
);
1538 FILL_OR_LEAVE(criteria
->email
, optarg
);
1541 FILL_OR_LEAVE(criteria
->telNumber
, optarg
);
1544 FILL_OR_LEAVE(criteria
->identifier
, optarg
);
1547 FILL_OR_LEAVE(criteria
->registryCode
, optarg
);
1550 criteria
->dbState
= malloc(sizeof(*criteria
->dbState
));
1551 if (!criteria
->dbState
) {
1552 fprintf(stderr
, _("Error: Not enough memory\n"));
1556 if (!strcmp(optarg
, "ACCESSIBLE"))
1557 *criteria
->dbState
= DBSTATE_ACCESSIBLE
;
1558 else if (!strcmp(optarg
, "TEMP_INACCESSIBLE"))
1559 *criteria
->dbState
= DBSTATE_TEMP_UNACCESSIBLE
;
1560 else if (!strcmp(optarg
, "NOT_YET_ACCESSIBLE"))
1561 *criteria
->dbState
= DBSTATE_NOT_YET_ACCESSIBLE
;
1562 else if (!strcmp(optarg
, "PERM_INACCESSIBLE"))
1563 *criteria
->dbState
= DBSTATE_PERM_UNACCESSIBLE
;
1564 else if (!strcmp(optarg
, "REMOVED"))
1565 *criteria
->dbState
= DBSTATE_REMOVED
;
1567 fprintf(stderr
, _("%s: %s: Unknown box status\n"),
1574 FILL_BOOLEAN_OR_LEAVE(criteria
->dbEffectiveOVM
, optarg
);
1577 FILL_BOOLEAN_OR_LEAVE(criteria
->dbOpenAddressing
, optarg
);
1581 shi_find_box_usage(argv
[0]);
1587 /* There must be an option and all of them must be recognized, if not only
1588 * BOX_ID supplied */
1589 if (argc
> 2 && optind
!= argc
) {
1590 fprintf(stderr
, _("Error: Superfluous argument\n"));
1591 shi_find_box_usage(argv
[0]);
1596 /* If only box ID is supplied use it */
1597 if (argc
== 2 && argv
[1] && *argv
[1]) {
1598 criteria
->dbID
= locale2utf8(argv
[1]);
1599 if (!criteria
->dbID
) {
1600 fprintf(stderr
, _("Error: Not enough memory\n"));
1606 printf(_("Searching boxes...\n"));
1607 err
= isds_FindDataBox(cisds
, criteria
, &boxes
);
1608 finish_isds_operation(cisds
, err
);
1611 for(item
= boxes
; item
; item
= item
->next
) {
1612 if (!item
->data
) continue;
1615 oprintf(_("\n* Result #%d:\n"), order
);
1616 format_DbOwnerInfo(item
->data
);
1620 isds_DbOwnerInfo_free(&criteria
);
1625 static void shi_stat_box_usage(const char *command
) {
1627 "Usage: %s BOX_ID...\n"
1628 "Get status of box with BOX_ID. More boxes can be specified.\n"),
1633 /* Get boxes status */
1634 static int shi_stat_box(int argc
, const char **argv
) {
1639 if (!argv
|| !*argv
|| argc
< 2 || !argv
[1]) {
1640 fprintf(stderr
, _("Missing box ID\n"));
1641 shi_stat_box_usage((argv
[0])?argv
[0]:NULL
);
1645 for (int i
= 1; i
< argc
; i
++) {
1646 if (!argv
[i
] || !*argv
[i
]) continue;
1649 id
= locale2utf8(argv
[i
]);
1651 fprintf(stderr
, _("%s: Could not covert box ID to UTF-8\n"),
1656 printf(_("Getting status of box `%s'...\n"), argv
[i
]);
1657 err
= isds_CheckDataBox(cisds
, id
, &status
);
1658 finish_isds_operation(cisds
, err
);
1661 oprintf(_("Status of box `%s': %s\n"),
1662 argv
[i
], DbState2string(&status
));
1669 static void shi_compose_usage(const char *command
) {
1671 "Usage: %s OPTION...\n"
1672 "Compose and send a message to recipient defined by his box ID.\n"
1673 "Each option requires an argument (if not stated otherwise):\n"
1674 " -s * message subject\n"
1676 "Recipient options:\n"
1677 " -b * recipient box ID\n"
1678 " -U organisation unit name\n"
1679 " -N organisation unit number\n"
1680 " -P to hands of given person\n"
1682 "Sender organisation structure options:\n"
1686 "Message identifier options:\n"
1687 " -r sender reference number\n"
1688 " -f sender file ID\n"
1689 " -R recipient reference number\n"
1690 " -F recipient file ID\n"
1692 "Legal title options:\n"
1693 " -y year act has been issued\n"
1694 " -a ordinal number of act in a year\n"
1695 " -e section of the act\n"
1696 " -o paragraph of the act\n"
1697 " -i point of the paragraph of the act\n"
1699 "Delivery options:\n"
1700 " -p personal delivery required\n"
1701 " -t allow substitutable delivery\n"
1702 " -A non-OVM sender acts as public authority\n"
1704 "Document options:\n"
1705 " -d * read document from local file\n"
1706 " -D document name (defaults to base local file name)\n"
1707 " -x transport subset of the document as a XML.\n"
1708 " Argument is XPath expression specifying desired node set\n"
1709 " -m override MIME type (guessed on -d)\n"
1710 " -g document ID (must be unique per message)\n"
1711 " -G reference to other document using its ID\n"
1712 " -c document is digital signature of other document (NO argument\n"
1715 "Options marked with asterisk are mandatory, other are optional. Another soft\n"
1716 "dependencies can emerge upon using specific option. They are not mandated by\n"
1717 "ISDS currently, but client library or this program can force them to assure\n"
1718 "semantically complete message. Following soft dependencies are recommended:\n"
1719 " -y <=> -a act number and year must be used at the same time\n"
1720 " -i => -o act point requires act paragraph\n"
1721 " -o => -e act paragraph requires act section\n"
1722 " -e => -a act section requires act number\n"
1723 " -G => -g document with referenced ID must exist\n"
1724 " -c => -G signature must refer to signed document\n"
1725 " -c first document cannot be signature\n"
1727 "More documents can be attached to a message by repeating `-d' option.\n"
1728 "Document order will be preserved. Other document options affect immediately\n"
1729 "preceding `-d' document only. E.g. `-d /tmp/foo.pdf -m application/pdf\n"
1730 "-d /tmp/bar.txt -m text/plain' attaches first PDF file, then textual file.\n"
1732 "The same applies to recipient options that must start with box ID (-b).\n"
1733 "If more recipients specified, each of them will get a copy of composed\n"
1734 "message. ISDS will assign message identifier to each copy in turn.\n"
1740 static int shi_compose(int argc
, const char **argv
) {
1744 struct isds_message
*message
= NULL
;
1745 struct isds_envelope
*envelope
= NULL
;
1746 struct isds_list
*documents
= NULL
;
1747 struct isds_document
*document
= NULL
;
1748 struct isds_list
*copies
= NULL
, *copy_item
= NULL
;
1749 struct isds_message_copy
*copy
= NULL
;
1750 char *message_id_locale
= NULL
, *recipient_id_locale
= NULL
,
1751 *dmStatus_locale
= NULL
;
1753 if (!argv
|| !argv
[1] || !*argv
[1]) {
1754 fprintf(stderr
, _("Error: No argument supplied\n"));
1755 shi_compose_usage((argv
)?argv
[0]:NULL
);
1759 message
= calloc(1, sizeof(*message
));
1761 fprintf(stderr
, _("Error: Not enough memory\n"));
1765 envelope
= calloc(1, sizeof(*envelope
));
1767 fprintf(stderr
, _("Error: Not enough memory\n"));
1771 message
->envelope
= envelope
;
1775 while ((opt
= getopt(argc
, (char * const *)argv
, "s:" "b:U:N:P:" "u:n:"
1776 "r:f:R:F:" "y:a:e:o:i:" "p:t:A:" "d:D:x:m:g:G:c"
1780 FILL_OR_LEAVE(envelope
->dmAnnotation
, optarg
);
1783 /* Recipient options */
1787 /* First recipient */
1788 CALLOC_OR_LEAVE(copies
);
1789 copies
->destructor
=
1790 (void(*)(void **)) isds_message_copy_free
;
1793 /* Next recipient */
1794 CALLOC_OR_LEAVE(copy_item
->next
);
1795 copy_item
->next
->destructor
=
1796 (void(*)(void **)) isds_message_copy_free
;
1797 copy_item
= copy_item
->next
;
1799 CALLOC_OR_LEAVE(copy
);
1800 copy_item
->data
= copy
;
1802 /* Copy recipient box ID */
1803 FILL_OR_LEAVE(copy
->dbIDRecipient
, optarg
);
1808 _("Error: %s: Recipient box ID (-b) must precede "
1809 "recipient organisation unit name (-%c)\n"),
1814 FILL_OR_LEAVE(copy
->dmRecipientOrgUnit
, optarg
);
1819 _("Error: %s: Recipient box ID (-b) must precede "
1820 "recipient organisation unit number (-%c)\n"),
1825 FILL_LONGINT_OR_LEAVE(copy
->dmRecipientOrgUnitNum
, optarg
);
1830 _("Error: %s: Recipient box ID (-b) must precede "
1831 "to-hands option (-%c)\n"), optarg
, opt
);
1835 FILL_OR_LEAVE(copy
->dmToHands
, optarg
);
1838 /* Sender organisation structure options */
1840 FILL_OR_LEAVE(envelope
->dmSenderOrgUnit
, optarg
);
1843 FILL_LONGINT_OR_LEAVE(envelope
->dmSenderOrgUnitNum
, optarg
);
1846 /* Message identifier options */
1848 FILL_OR_LEAVE(envelope
->dmSenderRefNumber
, optarg
);
1851 FILL_OR_LEAVE(envelope
->dmSenderIdent
, optarg
);
1854 FILL_OR_LEAVE(envelope
->dmRecipientRefNumber
, optarg
);
1857 FILL_OR_LEAVE(envelope
->dmRecipientIdent
, optarg
);
1860 /* Legal title options */
1862 FILL_LONGINT_OR_LEAVE(envelope
->dmLegalTitleYear
, optarg
);
1865 FILL_LONGINT_OR_LEAVE(envelope
->dmLegalTitleLaw
, optarg
);
1868 FILL_OR_LEAVE(envelope
->dmLegalTitleSect
, optarg
);
1871 FILL_OR_LEAVE(envelope
->dmLegalTitlePar
, optarg
);
1874 FILL_OR_LEAVE(envelope
->dmLegalTitlePoint
, optarg
);
1877 /* Delivery options */
1879 FILL_BOOLEAN_OR_LEAVE(envelope
->dmPersonalDelivery
, optarg
);
1882 FILL_BOOLEAN_OR_LEAVE(envelope
->dmAllowSubstDelivery
, optarg
);
1885 FILL_BOOLEAN_OR_LEAVE(envelope
->dmOVM
, optarg
);
1888 /* Document options */
1892 /* First document */
1893 CALLOC_OR_LEAVE(message
->documents
);
1894 message
->documents
->destructor
=
1895 (void(*)(void **)) isds_document_free
;
1896 documents
= message
->documents
;
1897 CALLOC_OR_LEAVE(document
);
1898 documents
->data
= document
;
1899 document
->dmFileMetaType
= FILEMETATYPE_MAIN
;
1902 CALLOC_OR_LEAVE(documents
->next
);
1903 documents
->next
->destructor
=
1904 (void(*)(void **)) isds_document_free
;
1905 documents
= documents
->next
;
1906 CALLOC_OR_LEAVE(document
);
1907 documents
->data
= document
;
1908 document
->dmFileMetaType
= FILEMETATYPE_ENCLOSURE
;
1911 if (load_data_from_file(optarg
, &document
->data
,
1912 &document
->data_length
, &document
->dmMimeType
)) {
1916 /* XXX: POSIX basename() modifies argument */
1917 FILL_OR_LEAVE(document
->dmFileDescr
, basename(optarg
));
1922 _("Error: %s: Document file (-d) must precede "
1923 "document name (-%c)\n"), optarg
, opt
);
1927 FILL_OR_LEAVE(document
->dmFileDescr
, optarg
);
1932 _("Error: %s: Document file (-d) must precede "
1933 "XPath expression (-%c)\n"), optarg
, opt
);
1937 /* Load XML node list */
1938 char *xpath_expr
= NULL
;
1939 FILL_OR_LEAVE(xpath_expr
, optarg
);
1940 retval
= load_xml_subtree_from_memory(
1941 document
->data
, document
->data_length
,
1942 &document
->xml_node_list
, xpath_expr
);
1947 /* Switch document type to XML */
1948 document
->is_xml
= 1;
1949 zfree(document
->data
);
1950 document
->data_length
= 0;
1951 documents
->destructor
=
1952 (void(*)(void **)) free_document_with_xml_node_list
;
1957 _("Error: %s: Document file (-d) must precede "
1958 "MIME type (-%c)\n"), optarg
, opt
);
1962 FILL_OR_LEAVE(document
->dmMimeType
, optarg
);
1967 _("Error: %s: Document file (-d) must precede "
1968 "document ID (-%c)\n"), optarg
, opt
);
1972 FILL_OR_LEAVE(document
->dmFileGuid
, optarg
);
1977 _("Error: %s: Document file (-d) must precede "
1978 "document reference (-%c)\n"), optarg
, opt
);
1982 FILL_OR_LEAVE(document
->dmUpFileGuid
, optarg
);
1987 _("Error: Document file (-d) must precede "
1988 "document signature type (-%c)\n"), opt
);
1992 document
->dmFileMetaType
= FILEMETATYPE_SIGNATURE
;
1996 shi_compose_usage(argv
[0]);
2002 /* All options must be recognized */
2003 if (optind
!= argc
) {
2004 fprintf(stderr
, _("Error: Superfluous argument\n"));
2005 shi_compose_usage(argv
[0]);
2011 fprintf(stderr
, _("Error: No recipient box ID specified\n"));
2012 shi_compose_usage(argv
[0]);
2017 /* TODO: Check Legal Title soft dependencies */
2020 oprintf(_("Following message has been composed:\n"));
2021 format_message(message
);
2022 oprintf(_("Following recipients have been specified:\n"));
2023 format_copies(copies
);
2024 /* TODO: Confirmation, correction */
2026 /* Send a message */
2027 printf(_("Sending message...\n"));
2028 err
= isds_send_message_to_multiple_recipients(cisds
, message
, copies
);
2029 finish_isds_operation(cisds
, err
);
2030 if (err
&& err
!= IE_PARTIAL_SUCCESS
) {
2035 /* Show results for each copy */
2036 for (copy_item
= copies
; copy_item
; copy_item
= copy_item
->next
) {
2037 if (!copy_item
->data
) continue;
2038 copy
= (struct isds_message_copy
*) copy_item
->data
;
2039 recipient_id_locale
= utf82locale(copy
->dbIDRecipient
);
2043 if (copy
->dmStatus
) dmStatus_locale
= utf82locale(copy
->dmStatus
);
2044 if (dmStatus_locale
)
2045 oprintf(_("%s: Failed: %s: %s\n"),
2046 recipient_id_locale
,
2047 isds_strerror(copy
->error
), dmStatus_locale
);
2049 oprintf(_("%s: Failed: %s\n"), recipient_id_locale
,
2050 isds_strerror(copy
->error
));
2051 zfree(dmStatus_locale
);
2053 message_id_locale
= utf82locale(copy
->dmID
);
2054 oprintf(_("%s: Succeeded. Assigned message ID: %s\n"),
2055 recipient_id_locale
, message_id_locale
);
2056 free(message_id_locale
);
2059 free(recipient_id_locale
);
2063 isds_message_free(&message
);
2064 isds_list_free(&copies
);
2068 #undef FILL_LONGINT_OR_LEAVE
2069 #undef FILL_BOOLEAN_OR_LEAVE
2070 #undef CALLOC_OR_LEAVE
2071 #undef FILL_OR_LEAVE
2074 static void shi_delivery_usage(const char *command
) {
2076 "Usage: %s MESSAGE_ID\n"
2077 "Get delivery data about message with MESSAGE_ID.\n"),
2082 static int shi_delivery(int argc
, const char **argv
) {
2086 if (!argv
|| !argv
[1] || !*argv
[1]) {
2087 shi_delivery_usage(argv
[0]);
2092 printf(_("Getting delivery info...\n"));
2093 err
= isds_get_signed_delivery_info(cisds
, id
, &message
);
2094 finish_isds_operation(cisds
, err
);
2097 select_completition(COMPL_COMMAND
);
2101 format_message(message
);
2103 if (message
->envelope
&& message
->envelope
->dmID
)
2104 set_prompt(_("%s %s"), argv
[0], message
->envelope
->dmID
);
2106 set_prompt("%s", argv
[0]);
2107 select_completition(COMPL_MSG
);
2112 static int shi_dump_message(int argc
, const char **argv
) {
2114 fprintf(stderr
, _("No message loaded\n"));
2118 print_message(message
);
2123 static void shi_hash_usage(const char *command
) {
2125 "Usage: %s [MESSAGE_ID]\n"
2126 "Retrieve message hash stored in ISDS.\n"
2127 "If MESSAGE_ID is defined, query for that message.\n"
2128 "Otherwise use current message.\n"),
2133 static int shi_hash(int argc
, const char **argv
) {
2135 const char *id
= NULL
;
2136 struct isds_hash
*hash
= NULL
;
2137 char *hash_string
= NULL
;
2139 if (!argv
|| argc
> 2) {
2140 shi_hash_usage((argv
)?argv
[0]:NULL
);
2143 if (argc
== 2 && argv
[1] && *argv
[1])
2147 fprintf(stderr
, _("No message loaded\n"));
2150 if (!message
->envelope
|| !message
->envelope
->dmID
) {
2151 fprintf(stderr
, _("Current message is missing ID\n"));
2154 id
= message
->envelope
->dmID
;
2157 printf(_("Getting message hash...\n"));
2158 err
= isds_download_message_hash(cisds
, id
, &hash
);
2159 finish_isds_operation(cisds
, err
);
2162 hash_string
= hash2string(hash
);
2163 oprintf(_("ISDS states message with `%s' ID has following hash:\n%s\n"),
2167 isds_hash_free(&hash
);
2172 static int shi_verify(int argc
, const char **argv
) {
2175 struct isds_hash
*retrieved_hash
= NULL
, *stored_hash
= NULL
;
2176 char *hash_string
= NULL
;
2180 fprintf(stderr
, _("No message loaded\n"));
2184 if (!message
->envelope
) {
2185 fprintf(stderr
, _("Current message is missing envelope\n"));
2188 stored_hash
= message
->envelope
->hash
;
2189 message
->envelope
->hash
= NULL
;
2191 if (message
->envelope
->dmID
) {
2192 /* Verify remote hash */
2193 oprintf(_("Remote hash check:\n"));
2195 printf(_("Getting message hash...\n"));
2196 err
= isds_download_message_hash(cisds
, message
->envelope
->dmID
,
2198 finish_isds_operation(cisds
, err
);
2200 if (retrieved_hash
) {
2201 hash_string
= hash2string(retrieved_hash
);
2202 ohprint(_("Retrieved:"), width
);
2203 oprintf("%s\n", hash_string
);
2207 if (retrieved_hash
&& message
->raw
) {
2208 err
= isds_compute_message_hash(cisds
, message
,
2209 retrieved_hash
->algorithm
);
2210 finish_isds_operation(cisds
, err
);
2213 hash_string
= hash2string(message
->envelope
->hash
);
2214 ohprint(_("Computed:"), width
);
2215 oprintf("%s\n", hash_string
);
2220 err
= isds_hash_cmp(retrieved_hash
, message
->envelope
->hash
);
2223 oprintf(_("Hashes match.\n")); break;
2225 oprintf(_("Hashes do not match.\n"));
2229 oprintf(_("Hashes could not be compared.\n"));
2234 free(retrieved_hash
);
2239 /* Verify stored hash */
2240 oprintf(_("Stored hash check:\n"));
2242 hash_string
= hash2string(stored_hash
);
2243 ohprint(_("Stored:"), width
);
2244 oprintf("%s\n", hash_string
);
2248 err
= isds_compute_message_hash(cisds
, message
,
2249 stored_hash
->algorithm
);
2250 finish_isds_operation(cisds
, err
);
2253 hash_string
= hash2string(message
->envelope
->hash
);
2254 ohprint(_("Computed:"), width
);
2255 oprintf("%s\n", hash_string
);
2260 err
= isds_hash_cmp(stored_hash
, message
->envelope
->hash
);
2263 oprintf(_("Hashes match.\n")); break;
2265 oprintf(_("Hashes do not match.\n"));
2269 oprintf(_("Hashes could not be compared.\n"));
2274 isds_hash_free(&message
->envelope
->hash
);
2277 message
->envelope
->hash
= stored_hash
;
2282 static int shi_authenticate(int argc
, const char **argv
) {
2287 fprintf(stderr
, _("No message loaded\n"));
2290 if (!message
->raw
|| message
->raw_length
== 0) {
2291 fprintf(stderr
, _("Current message is missing raw representation\n"));
2295 printf(_("Submitting message to authenticity check...\n"));
2296 err
= isds_authenticate_message(cisds
, message
->raw
, message
->raw_length
);
2297 finish_isds_operation(cisds
, (err
== IE_NOTUNIQ
) ? IE_SUCCESS
: err
);
2301 oprintf(_("Message originates in ISDS.\n")); break;
2303 oprintf(_("Message is unknown to ISDS or has been tampered.\n"));
2315 static void shi_accept_message_usage(const char *command
) {
2317 "Usage: %s [MESSAGE_ID...]\n"
2318 "Accept commercial message moving its state to received.\n"
2319 "If MESSAGE_ID is defined, accept that message. More messages can be specified.\n"
2320 "Otherwise accept all commercial incoming messages.\n"),
2325 static int shi_accept_message(int argc
, const char **argv
) {
2329 /* Process messages named in argv */
2330 for (int i
= 1; i
< argc
; i
++) {
2331 if (!argv
[i
] || !*argv
[i
]) continue;
2333 id
= locale2utf8(argv
[i
]);
2336 _("Error: Could not convert message ID to UTF-8: %s\n"),
2341 printf(_("Accepting message `%s'...\n"), argv
[i
]);
2342 err
= isds_mark_message_received(cisds
, id
);
2343 finish_isds_operation(cisds
, err
);
2349 oprintf(_("Message `%s' accepted\n"), argv
[i
]);
2354 /* TODO: list commercial not received messages and accept all of them
2357 _("Error: No message ID supplied. Accepting all commercial "
2358 "messages not implemented yet.\n"));
2366 /* Mark message as read. At one form of ID must be provided.
2367 * @id is UTF-8 encoded message ID
2368 * @id_locale is locale encoded message ID. @id takes preference. */
2369 static int do_read_message(const char *id
, const char *id_locale
) {
2373 if ((!id
|| !*id
) && (!id_locale
|| !*id_locale
)) return -1;
2377 id_locale
= utf82locale(id
);
2380 id
= locale2utf8(id_locale
);
2383 _("Error: Could not convert message ID to UTF-8: %s\n"),
2389 printf(_("Marking message `%s' as read...\n"), id_locale
);
2390 err
= isds_mark_message_read(cisds
, id
);
2391 finish_isds_operation(cisds
, err
);
2394 oprintf(_("Message `%s' marked as read\n"), id_locale
);
2396 if (static_id
) free((char *) id_locale
);
2397 else free((char *) id
);
2399 return (err
) ? -1 : 0;
2403 static void shi_read_message_usage(const char *command
) {
2405 "Usage: %s [MESSAGE_ID...]\n"
2406 "Mark message as read moving its state to read.\n"
2408 "When new incoming message is download, its state is not changed on server.\n"
2409 "Client must mark such message as read explicitly. You can use this command\n"
2410 "to do so, if not done automatically at download time by your client.\n"
2412 "If MESSAGE_ID is defined, mark that message. More messages can be specified.\n"
2413 "Otherwise marks currently loaded message.\n"),
2418 static int shi_read_message(int argc
, const char **argv
) {
2419 for (int i
= 1; i
< argc
; i
++) {
2420 if (!argv
[i
] || !*argv
[i
]) continue;
2421 if (do_read_message(NULL
, argv
[i
]))
2427 fprintf(stderr
, _("No message loaded\n"));
2430 if (!message
->envelope
) {
2431 fprintf(stderr
, _("Loaded message is missing envelope\n"));
2434 if (!message
->envelope
->dmID
|| !*message
->envelope
->dmID
) {
2435 fprintf(stderr
, _("Loaded message is missing ID\n"));
2438 return do_read_message(message
->envelope
->dmID
, NULL
);
2445 static void shi_cat_message_usage(const char *command
) {
2448 "Print unformated raw representation of current message.\n"
2450 "This is the same content you would get into file by `save' command.\n"
2452 "Be ware the binary stream can screw your terminal. No new line character\n"
2453 "will be appended to the end of the output.\n"),
2458 static int shi_cat_message(int argc
, const char **argv
) {
2460 fprintf(stderr
, _("No message loaded\n"));
2464 if (!message
->raw
|| !message
->raw_length
) {
2465 fprintf(stderr
, _("Current message is missing raw representation\n"));
2469 if (owrite(message
->raw
, message
->raw_length
) != message
->raw_length
) {
2470 fprintf(stderr
, _("Error while printing message content\n"));
2478 static int shi_show_message(int argc
, const char **argv
) {
2480 fprintf(stderr
, _("No message loaded\n"));
2484 format_message(message
);
2489 static void shi_incoming_message_usage(const char *command
) {
2491 "Usage: %s [-r] MESSAGE_ID\n"
2492 "Get incoming message with MESSAGE_ID.\n"
2494 " -r Mark mesage as read\n"),
2499 static int shi_incoming_message(int argc
, const char **argv
) {
2503 _Bool mark_as_read
= 0;
2506 while ((opt
= getopt(argc
, (char * const *)argv
, "r")) != -1) {
2512 shi_incoming_message_usage((argv
)?argv
[0]:NULL
);
2516 if (optind
+ 1 != argc
|| !argv
|| !argv
[optind
] || !*argv
[optind
]) {
2517 fprintf(stderr
, _("Bad invocation\n"));
2518 shi_incoming_message_usage((argv
)?argv
[0]:NULL
);
2523 printf(_("Getting incoming message...\n"));
2524 err
= isds_get_signed_received_message(cisds
, id
, &message
);
2525 finish_isds_operation(cisds
, err
);
2528 select_completition(COMPL_COMMAND
);
2532 format_message(message
);
2534 if (message
->envelope
&& message
->envelope
->dmID
)
2535 set_prompt(_("%s %s"), argv
[0], message
->envelope
->dmID
);
2537 set_prompt("%s", argv
[0]);
2538 select_completition(COMPL_MSG
);
2540 if (mark_as_read
|| cfg_getbool(configuration
, CONFIG_MARKMESSAGEREAD
)) {
2541 if (message
->envelope
&& message
->envelope
->dmMessageStatus
&&
2542 ! (*message
->envelope
->dmMessageStatus
& MESSAGESTATE_READ
))
2543 return do_read_message(id
, NULL
);
2549 static void shi_outgoing_message_usage(const char *command
) {
2551 "Usage: %s MESSAGE_ID\n"
2552 "Get outgoing message with MESSAGE_ID.\n"),
2557 static int shi_outgoing_message(int argc
, const char **argv
) {
2561 if (!argv
|| !argv
[1] || !*argv
[1]) {
2562 shi_outgoing_message_usage(argv
[0]);
2567 printf(_("Getting outgoing message...\n"));
2568 err
= isds_get_signed_sent_message(cisds
, id
, &message
);
2569 finish_isds_operation(cisds
, err
);
2572 select_completition(COMPL_COMMAND
);
2576 format_message(message
);
2577 if (message
->envelope
&& message
->envelope
->dmID
)
2578 set_prompt(_("%s %s"), argv
[0], message
->envelope
->dmID
);
2580 set_prompt("%s", argv
[0]);
2581 select_completition(COMPL_MSG
);
2586 static void shi_load_anything_usage(const char *command
) {
2589 "Load message or message delivery details from local FILE.\n"),
2594 static int shi_load_anything(int argc
, const char **argv
) {
2596 void *buffer
= NULL
;
2598 isds_raw_type raw_type
;
2600 char *type_name
= NULL
;
2602 if (!argv
|| !argv
[1] || !*argv
[1]) {
2603 shi_load_anything_usage((argv
)?argv
[0]:NULL
);
2607 printf(_("Loading file `%s'...\n"), argv
[1]);
2609 if (mmap_file(argv
[1], &fd
, &buffer
, &length
)) return -1;
2611 printf(_("Detecting file format...\n"));
2612 err
= isds_guess_raw_type(cisds
, &raw_type
, buffer
, length
);
2613 finish_isds_operation(cisds
, err
);
2616 if (err
== IE_NOTSUP
)
2617 fprintf(stderr
, _("Unknown format. Could not parse the file.\n"));
2619 fprintf(stderr
, _("Error while detecting format. "
2620 "Could not parse the file.\n"));
2623 case RAWTYPE_INCOMING_MESSAGE
:
2624 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
:
2625 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
:
2626 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
:
2627 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
2628 err
= isds_load_message(cisds
, raw_type
,
2629 buffer
, length
, &message
, BUFFER_COPY
);
2630 finish_isds_operation(cisds
, err
);
2631 type_name
= N_("message");
2634 case RAWTYPE_DELIVERYINFO
:
2635 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO
:
2636 case RAWTYPE_CMS_SIGNED_DELIVERYINFO
:
2637 err
= isds_load_delivery_info(cisds
, raw_type
,
2638 buffer
, length
, &message
, BUFFER_COPY
);
2639 finish_isds_operation(cisds
, err
);
2640 type_name
= N_("delivery");
2645 _("Unsupported format. Could not parse the file.\n"));
2650 munmap_file(fd
, buffer
, length
);
2654 select_completition(COMPL_COMMAND
);
2658 format_message(message
);
2660 if (message
->envelope
&& message
->envelope
->dmID
)
2661 set_prompt(_("%s %s"), _(type_name
), message
->envelope
->dmID
);
2663 set_prompt("%s", _(type_name
));
2664 select_completition(COMPL_MSG
);
2669 static void shi_save_message_usage(const char *command
) {
2672 "Save message into local FILE.\n"),
2677 static const char *raw_type2mime(isds_raw_type raw_type
) {
2679 case RAWTYPE_INCOMING_MESSAGE
:
2680 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
:
2681 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
:
2682 case RAWTYPE_DELIVERYINFO
:
2683 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO
:
2686 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
:
2687 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
2688 case RAWTYPE_CMS_SIGNED_DELIVERYINFO
:
2689 return "application/pkcs7-mime";
2697 static int shi_save_message(int argc
, const char **argv
) {
2698 _Bool overwrite
= cfg_getbool(configuration
, CONFIG_OVERWRITEFILES
);
2700 if (!argv
|| !argv
[1] || !*argv
[1]) {
2701 shi_save_message_usage(argv
[0]);
2706 fprintf(stderr
, _("No message loaded\n"));
2709 if (!message
->raw
|| message
->raw_length
== 0) {
2710 fprintf(stderr
, _("Loaded message is missing raw representation\n"));
2714 return save_data_to_file(argv
[1], -1, message
->raw
, message
->raw_length
,
2715 raw_type2mime(message
->raw_type
), overwrite
);
2719 /* Return document of current message identified by ordinal number expressed
2720 * as string. In case of error return NULL. */
2721 static const struct isds_document
*locate_document_by_ordinal_string(
2722 const char *number
) {
2723 const struct isds_list
*item
;
2724 const struct isds_document
*document
= NULL
;
2727 if (!number
) return NULL
;
2729 ordinar
= atoi(number
);
2731 fprintf(stderr
, _("%s: Document number must be positive number\n"),
2737 fprintf(stderr
, _("No message loaded\n"));
2742 for (item
= message
->documents
, i
= 0; item
; item
= item
->next
) {
2743 if (!item
->data
) continue;
2744 if (++i
== ordinar
) {
2745 document
= (const struct isds_document
*) item
->data
;
2750 fprintf(stderr
, _("Message does not contain document #%d\n"), ordinar
);
2758 static void shi_cat_document_usage(const char *command
) {
2760 "Usage: %s NUMBER\n"
2761 "Print document selected with ordinal NUMBER.\n"),
2765 static int shi_cat_document(int argc
, const char **argv
) {
2766 const struct isds_document
*document
;
2768 if (!argv
|| !argv
[1] || !*argv
[1] || argc
> 3) {
2769 shi_cat_document_usage(argv
[0]);
2773 document
= locate_document_by_ordinal_string(argv
[1]);
2774 if (!document
) return -1;
2776 if (document
->is_xml
) {
2777 xmlBufferPtr buffer
= NULL
;
2780 if (serialize_xml_to_buffer(&buffer
, document
->xml_node_list
))
2783 written
= owrite(buffer
->content
, buffer
->use
);
2784 xmlBufferFree(buffer
);
2785 if (written
!= buffer
->use
) {
2786 fprintf(stderr
, _("Error while printing document content\n"));
2790 if (!document
->data
|| !document
->data_length
) {
2791 fprintf(stderr
, _("Document is missing raw representation\n"));
2795 if (owrite(document
->data
, document
->data_length
) != document
->data_length
) {
2796 fprintf(stderr
, _("Error while printing document content\n"));
2805 static void shi_save_document_usage(const char *command
) {
2807 "Usage: %s NUMBER [DESTINATION]\n"
2808 "Save document having ordinal NUMBER within current message into local file.\n"
2809 "If DESTINATION is file (or does not exist yet), document will be saved into\n"
2811 "If DESTINATION is existing directory, file name equaled to document name\n"
2812 "will be saved into DESTINATION.\n"
2813 "If DESTINATION is missing, document name will be used as file name and\n"
2814 "saved into working directory.\n"
2815 "Be aware that document name does not embed malicious characters (slashes).\n"
2817 "If the document is a binary stream, image of the document will be copied\n"
2818 "into a file. If the document is a XML document, the XML tree will be serialized\n"
2819 "into a file. If XML document stands for one element or one text node, the node\n"
2820 "(and its children recursively) will be serialized. If XML document compounds\n"
2821 "more nodes or a comment or a processing instruction, parent node from ISDS name\n"
2822 "space will be used to ensure output serialized XML well-formness.\n"
2828 static int shi_save_document(int argc
, const char **argv
) {
2829 const struct isds_document
*document
;
2830 const char *dirname
= NULL
;
2831 char *filename
= NULL
, *path
= NULL
;
2833 _Bool overwrite
= cfg_getbool(configuration
, CONFIG_OVERWRITEFILES
);
2835 if (!argv
|| !argv
[1] || !*argv
[1] || argc
> 3) {
2836 shi_save_document_usage(argv
[0]);
2840 document
= locate_document_by_ordinal_string(argv
[1]);
2841 if (!document
) return -1;
2843 /* Select directory and file name */
2844 if (argv
[2] && *argv
[2]) {
2845 if (!is_directory(argv
[2])) {
2848 filename
= strdup(argv
[2]);
2850 fprintf(stderr
, _("Not enough memory\n"));
2855 if (!filename
&& document
->dmFileDescr
&& &document
->dmFileDescr
) {
2856 filename
= utf82locale(document
->dmFileDescr
);
2858 fprintf(stderr
, _("Not enough memory\n"));
2864 _("File name neither supplied, nor document name exists\n"
2865 "Please, supply one.\n"));
2871 path
= astrcat3(dirname
, "/", filename
);
2878 fprintf(stderr
, _("Not enough memory\n"));
2883 if (document
->is_xml
)
2884 retval
= save_xml_to_file(path
, -1, document
->xml_node_list
,
2885 document
->dmMimeType
, overwrite
);
2887 retval
= save_data_to_file(path
, -1, document
->data
,
2888 document
->data_length
, document
->dmMimeType
, overwrite
);
2894 /* Execute program specified as NULL terminated array of arguments. argv[0] is
2895 * subject of PATH search variable look-up. The program is executed directly,
2896 * it's not a shell command. */
2897 static int execute_system_command(char *const argv
[]) { pid_t pid
;
2899 if (!argv
|| !argv
[0]) return -1;
2903 /* Could not fork */
2904 fprintf(stderr
, _("Could not fork\n"));
2906 } else if (pid
== 0) {
2908 execvp(argv
[0], argv
);
2909 fprintf(stderr
, _("Could not execute:"));
2910 for (char *const *arg
= argv
; *arg
; arg
++)
2911 fprintf(stderr
, " %s", *arg
);
2912 fprintf(stderr
, _(": %s\n"), strerror(errno
));
2915 /* Wait for the command */
2918 if (-1 == waitpid(pid
, &retval
, 0)) {
2919 fprintf(stderr
, _("Could not wait for executed command\n"));
2924 fprintf(stderr
, _("Exit code of command could not "
2925 "be determined\n"));
2926 else if (WIFEXITED(retval
) && WEXITSTATUS(retval
))
2927 printf(_("Command exited with code %d\n"),
2928 WEXITSTATUS(retval
));
2929 else if (WIFSIGNALED(retval
))
2930 printf(_("Command terminated by signal "
2931 "#%d\n"), WTERMSIG(retval
));
2937 static void shi_open_document_usage(const char *command
) {
2939 "Usage: %s NUMBER\n"
2940 "Save document having ordinal NUMBER within current message into temporal\n"
2941 "local file, open the file by xdg-open utility and then remove the file.\n"
2947 static int shi_open_document(int argc
, const char **argv
) {
2948 const struct isds_document
*document
;
2949 char filename
[10] = "shiXXXXXX";
2951 char **command
= NULL
;
2954 if (!argv
|| !argv
[1] || !*argv
[1] || argc
> 3) {
2955 shi_open_document_usage(argv
[0]);
2959 document
= locate_document_by_ordinal_string(argv
[1]);
2960 if (!document
) return -1;
2962 /* Create temporary file for the document */
2963 fd
= mkstemp(filename
);
2965 fprintf(stderr
, _("Could not create temporary file: %s\n"), strerror(errno
));
2970 if (document
->is_xml
)
2971 retval
= save_xml_to_file(filename
, fd
, document
->xml_node_list
,
2972 document
->dmMimeType
, 0);
2974 retval
= save_data_to_file(filename
, fd
, document
->data
,
2975 document
->data_length
, document
->dmMimeType
, 0);
2977 /* Open the file with external utility */
2979 /* Construct command arguments to execute */
2980 command
= malloc(3 * sizeof(*command
));
2982 fprintf(stderr
, _("Error: Not enough memory\n"));
2985 command
[0] = "xdg-open";
2986 command
[1] = filename
;
2989 /* XXX: Do not use system(3) as we cannot escape uknown shell */
2990 retval
= execute_system_command(command
);
2995 /* Remove the file */
3001 static void shi_save_stamp_usage(const char *command
) {
3004 "Save message time stamp into local FILE.\n"),
3009 static int shi_save_stamp(int argc
, const char **argv
) {
3010 _Bool overwrite
= cfg_getbool(configuration
, CONFIG_OVERWRITEFILES
);
3012 if (!argv
|| !argv
[1] || !*argv
[1]) {
3013 shi_save_message_usage(argv
[0]);
3018 fprintf(stderr
, _("No message loaded\n"));
3021 if (!message
->envelope
|| !message
->envelope
->timestamp
||
3022 message
->envelope
->timestamp_length
== 0) {
3023 fprintf(stderr
, _("Loaded message is missing time stamp\n"));
3027 return save_data_to_file(argv
[1], -1,
3028 message
->envelope
->timestamp
, message
->envelope
->timestamp_length
,
3029 "application/timestamp-reply", overwrite
);
3033 static int shi_show_list(int argc
, const char **argv
) {
3035 fprintf(stderr
, _("No message list loaded\n"));
3039 oprintf((messages_are_outgoing
) ?
3040 ngettext("You have %'lu outgoing message\n",
3041 "You have %'lu outgoing messages\n", total_messages
) :
3042 ngettext("You have %'lu incoming message\n",
3043 "You have %'lu incoming messages\n", total_messages
),
3045 print_message_list(messages
, messages_are_outgoing
);
3050 static int shi_list_incoming(int argc
, const char **argv
) {
3053 printf(_("Listing incoming messages...\n"));
3054 err
= isds_get_list_of_received_messages(cisds
, NULL
, NULL
, NULL
,
3055 MESSAGESTATE_ANY
, 0, &total_messages
, &messages
);
3056 finish_isds_operation(cisds
, err
);
3059 select_completition(COMPL_COMMAND
);
3062 messages_are_outgoing
= 0;
3064 shi_show_list(0, NULL
);
3066 set_prompt(_("%s %'lu"), argv
[0], total_messages
);
3067 select_completition(COMPL_LIST
);
3072 static int shi_list_outgoing(int argc
, const char **argv
) {
3075 printf(_("Listing outgoing messages...\n"));
3076 err
= isds_get_list_of_sent_messages(cisds
, NULL
, NULL
, NULL
,
3077 MESSAGESTATE_ANY
, 0, &total_messages
, &messages
);
3078 finish_isds_operation(cisds
, err
);
3081 select_completition(COMPL_COMMAND
);
3084 messages_are_outgoing
= 1;
3086 shi_show_list(0, NULL
);
3088 set_prompt(_("%s %'lu"), argv
[0], total_messages
);
3089 select_completition(COMPL_LIST
);
3094 /* Submit document for conversion and print assigned identifier */
3095 static int do_convert(const struct isds_document
*document
) {
3098 struct tm
*date
= NULL
;
3100 if (!document
) return -1;
3102 printf(_("Submitting document for authorized conversion...\n"));
3104 err
= czp_convert_document(czechpoint
, document
, &id
, &date
);
3105 finish_isds_operation(czechpoint
, err
);
3108 char *name_locale
= utf82locale(document
->dmFileDescr
);
3109 char *date_string
= tm2string(date
);
3110 char *id_locale
= utf82locale(id
);
3112 "Document submitted for authorized conversion successfully under name\n"
3114 "Submit identifier assigned by Czech POINT deposit is `%s'.\n"),
3115 name_locale
, date_string
, id_locale
);
3119 oprintf(_("Be ware that submitted document has restricted lifetime "
3121 oprintf(_("See <%s> for more details.\n"), CZPDEPOSIT_URL
);
3124 free(id
); free(date
);
3125 return (err
) ? -1 : 0;
3129 static void shi_convert_file_usage(const char *command
) {
3131 "Usage: %s FILE [NAME]\n"
3132 "Submit local FILE to authorized conversion under NAME. If NAME is missing,\n"
3133 "it will use FILE name.\n"), command
);
3136 "If Czech POINT deposit accepts document, it will return document identifier\n"
3137 "that user is supposed to provide to officer at Czech POINT contact place.\n"
3138 "Currently only PDF 1.3 and higher version files are accepted.\n"));
3139 oprintf(_("See <%s> for more details.\n"), CZPDEPOSIT_URL
);
3143 static int shi_convert_file(int argc
, const char **argv
) {
3145 struct isds_document document
;
3148 if (!argv
|| argc
> 3 || !argv
[1] || !*argv
[1]) {
3149 shi_convert_file_usage((argv
)?argv
[0]:NULL
);
3153 memset(&document
, 0, sizeof(document
));
3155 if (argc
== 3 && argv
[2] && *argv
[2])
3156 document
.dmFileDescr
= locale2utf8(argv
[2]);
3158 document
.dmFileDescr
= locale2utf8(argv
[1]);
3159 if (!document
.dmFileDescr
) {
3160 printf(_("Could not convert document name to UTF-8\n"));
3164 printf(_("Loading document from file `%s'...\n"), argv
[1]);
3165 if (mmap_file(argv
[1], &fd
, &document
.data
, &document
.data_length
)) {
3166 free(document
.dmFileDescr
);
3170 retval
= do_convert(&document
);
3172 munmap_file(fd
, document
.data
, document
.data_length
);
3173 free(document
.dmFileDescr
);
3178 static void shi_convert_document_usage(const char *command
) {
3180 "Usage: %s NUMBER\n"
3181 "Submit message document with ordinal NUMBER to authorized conversion.\n"),
3185 "If Czech POINT deposit accepts document, it will return document identifier\n"
3186 "that user is supposed to provide to officer at Czech POINT contact place.\n"
3187 "Currently only PDF 1.3 and higher version files are accepted.\n"));
3188 oprintf(_("See <%s> for more details.\n"), CZPDEPOSIT_URL
);
3192 static int shi_convert_document(int argc
, const char **argv
) {
3193 const struct isds_document
*document
;
3195 if (!argv
|| !argv
[1] || !*argv
[1]) {
3196 shi_convert_document_usage((argv
)?argv
[0]:NULL
);
3200 document
= locate_document_by_ordinal_string(argv
[1]);
3201 if (!document
) return -1;
3203 return do_convert(document
);
3208 static void shi_print_usage(const char *command
) {
3210 "Usage: %s STRING LENGTH\n"
3211 "Prints STRING into LENGTH columns. Negative LENGTH means not to cut\n"
3212 "overflowing string.\n"
3213 "This should be locale and terminal agnostic.\n"),
3218 static int shi_print(int argc
, const char **argv
) {
3221 if (!argv
|| !argv
[1] || !argv
[2]) {
3222 shi_print_usage((argv
)?argv
[0]:NULL
);
3226 width
= strtol(argv
[2], NULL
, 10);
3227 if (width
< INT_MIN
) {
3229 _("Length argument must not lesser than %d.\n"), INT_MIN
);
3232 if (width
> INT_MAX
) {
3234 _("Length argument must not be greater than %d.\n"), INT_MAX
);
3239 onprint(argv
[1], width
);
3246 static int shi_tokenize(int argc
, const char **argv
) {
3248 if (!argv
) return 0;
3250 for (int i
= 0; i
< argc
; i
++) {
3251 oprintf(_(">%s<\n"), argv
[i
]);
3257 static int shi_quote(int argc
, const char **argv
) {
3258 char *escaped
, *unescaped
;
3260 if (!argv
) return 0;
3262 oprintf(_("Original\tQuoted\tDequoted\n"));
3263 for (int i
= 0; i
< argc
; i
++) {
3264 escaped
= shi_quote_filename((char *) argv
[i
], 0, NULL
);
3265 unescaped
= shi_dequote_filename((char *) argv
[i
], 0);
3266 oprintf(_(">%s<\t>%s<\t>%s<\n"), argv
[i
], escaped
, unescaped
);
3275 /* pclose(pipe), restore ouput to stdout, show error return code */
3276 int wait_for_shell(FILE **pipe
) {
3279 if (pipe
&& *pipe
) {
3280 retval
= pclose(*pipe
);
3285 fprintf(stderr
, _("Exit code of shell command could not "
3286 "be determined\n"));
3287 else if (WIFEXITED(retval
) && WEXITSTATUS(retval
))
3288 printf(_("Shell command exited with code %d\n"),
3289 WEXITSTATUS(retval
));
3290 else if (WIFSIGNALED(retval
))
3291 printf(_("Shell command terminated by signal "
3292 "#%d\n"), WTERMSIG(retval
));
3299 /* Interactive loop */
3300 void shi_loop(void) {
3301 char *command_line
= NULL
;
3302 char **command_argv
= NULL
;
3305 struct command
*command
= NULL
;
3309 oprintf(_("Use `help' command to get list of available commands.\n"));
3311 select_completition(COMPL_COMMAND
);
3315 command_line
= readline((prompt
) ? prompt
: _("shigofumi> "));
3316 /* Remember not parsable commands too to user be able to get back to
3318 if (command_line
&& *command_line
) {
3319 /* TODO: Omit blank lines */
3320 add_history(command_line
);
3323 command_argv
= tokenize(command_line
, &command_argc
, &shell
);
3325 if (command_argv
&& command_argv
[0]) {
3326 command
= find_command(command_argv
[0]);
3329 fprintf(stderr
, _("Command not understood\n"));
3334 pipe
= popen(shell
, "w");
3336 fprintf(stderr
, _("Could not run shell command `%s':"
3337 " %s\n"), shell
, strerror(errno
));
3343 command
->function(command_argc
,
3344 (const char **) command_argv
);
3345 wait_for_shell(&pipe
);
3350 argv_free(command_argv
);
3352 zfree(command_line
);
3357 /* Non-interactive mode. Commands from @lines are processed until any command
3358 * lines remains or no error occurred. First failure terminates processing.
3359 * @lines is sequence of commands separated by '\n' or '\r'. The content is
3360 * modified during this call.
3361 * @return 0 if all command succeed, otherwise non-zero value
3363 int shi_batch(char *lines
) {
3365 char **command_argv
= NULL
;
3368 struct command
*command
= NULL
;
3373 oprintf(_("Batch mode started.\n"));
3375 select_completition(COMPL_COMMAND
);
3377 while (!retval
&& (command_line
= strtok(lines
, "\n\r"))) {
3378 lines
= NULL
; /* strtok(3) requires it for subsequent calls */
3380 printf(_("Processing command: %s\n"), command_line
);
3382 command_argv
= tokenize(command_line
, &command_argc
, &shell
);
3384 if (command_argv
&& command_argv
[0]) {
3385 command
= find_command(command_argv
[0]);
3388 fprintf(stderr
, _("Command not understood\n"));
3394 pipe
= popen(shell
, "w");
3396 fprintf(stderr
, _("Could not run shell command `%s':"
3397 " %s\n"), shell
, strerror(errno
));
3403 retval
= command
->function(command_argc
,
3404 (const char **) command_argv
);
3405 if (wait_for_shell(&pipe
)) retval
= -1;
3410 argv_free(command_argv
);
3415 fprintf(stderr
, _("Command failed!\n"));
3420 #define COMMON_COMMANDS \
3421 { "accept", shi_accept_message, N_("accept commercial message"), \
3422 shi_accept_message_usage, ARGTYPE_MSGID }, \
3423 { "box", shi_box, N_("show current box details"), NULL, \
3425 { "cache", shi_cache, N_("show cache details"), NULL, \
3427 { "cd", shi_chdir, N_("change working directory"), shi_chdir_usage, \
3429 { "commercial", shi_commercial, \
3430 N_("manipulate commercial receiving box status"), \
3431 shi_commercial_usage, ARGTYPE_BOXID }, \
3432 { "compose", shi_compose, N_("compose a message"), shi_compose_usage, \
3434 { "convert", shi_convert_file, \
3435 N_("submit local document for authorized conversion"), \
3436 shi_convert_file_usage, ARGTYPE_FILE }, \
3437 { "copying", shi_copying, N_("show this program licence excerpt"), NULL, \
3439 { "debug", shi_debug, N_("set debugging"), shi_debug_usage, \
3441 { "delivery", shi_delivery, N_("get message delivery details"), \
3442 shi_delivery_usage, ARGTYPE_MSGID }, \
3443 { "findbox", shi_find_box, N_("search for a box"), shi_find_box_usage, \
3445 { "hash", shi_hash, N_("query ISDS for message hash"), \
3446 shi_hash_usage, ARGTYPE_MSGID }, \
3447 { "help", shi_help, N_("describe commands"), shi_help_usage, \
3448 ARGTYPE_COMMAND }, \
3449 { "load", shi_load_anything, \
3450 N_("load message or message delivery details from local file"), \
3451 shi_load_anything_usage, ARGTYPE_FILE }, \
3452 { "login", shi_login, N_("log into ISDS"), NULL, ARGTYPE_NONE }, \
3453 { "lsi", shi_list_incoming, N_("list received messages"), NULL, \
3455 { "lso", shi_list_outgoing, N_("list sent messages"), NULL, \
3457 { "msgi", shi_incoming_message, N_("get incoming message"), \
3458 shi_incoming_message_usage, ARGTYPE_MSGID }, \
3459 { "msgo", shi_outgoing_message, N_("get outgoing message"), \
3460 shi_outgoing_message_usage, ARGTYPE_MSGID }, \
3461 { "passwd", shi_passwd, N_("manipulate user password"), shi_passwd_usage, \
3463 { "pwd", shi_pwd, N_("print working directory"), NULL, ARGTYPE_NONE }, \
3464 { "quit", shi_quit, N_("exit shigofumi"), NULL, ARGTYPE_NONE }, \
3465 { "read", shi_read_message, N_("mark message as read"), \
3466 shi_read_message_usage, ARGTYPE_MSGID }, \
3467 { "set", shi_settings, N_("show settings"), NULL, ARGTYPE_NONE }, \
3468 { "statbox", shi_stat_box, N_("get status of a box"), shi_stat_box_usage, \
3470 { "user", shi_user, N_("show current user details"), NULL, \
3472 { "users", shi_users, N_("show box users"), shi_users_usage, \
3474 { "version", shi_version, N_("show version of this program"), NULL, \
3476 { NULL, NULL, NULL, NULL, ARGTYPE_NONE }
3478 struct command base_commands
[] = {
3480 { "quote", shi_quote
, N_("demonstrate argument escaping"), NULL
,
3482 { "print", shi_print
, N_("print string into given width"),
3483 shi_print_usage
, ARGTYPE_NONE
},
3484 { "tokenize", shi_tokenize
, N_("demonstrate arguments tokenization"), NULL
,
3490 struct command message_commands
[] = {
3491 { "authenticate", shi_authenticate
, N_("check message authenticity"),
3492 NULL
, ARGTYPE_NONE
},
3493 { "cat", shi_cat_message
, N_("show raw current message"),
3494 shi_cat_message_usage
, ARGTYPE_NONE
},
3495 { "catdoc", shi_cat_document
, N_("show raw document"),
3496 shi_cat_document_usage
, ARGTYPE_DOCID
},
3497 { "convertdoc", shi_convert_document
,
3498 N_("submit document of current message for authorized conversion"),
3499 shi_convert_document_usage
, ARGTYPE_DOCID
},
3500 { "dump", shi_dump_message
, N_("dump current message structure"),
3501 NULL
, ARGTYPE_NONE
},
3502 { "opendoc", shi_open_document
, N_("open document using external utility"),
3503 shi_open_document_usage
, ARGTYPE_DOCID
},
3504 { "savestamp", shi_save_stamp
,
3505 N_("save time stamp of current message into local file"),
3506 shi_save_stamp_usage
, ARGTYPE_FILE
},
3507 { "savedoc", shi_save_document
,
3508 N_("save document of current message into local file"),
3509 shi_save_document_usage
, ARGTYPE_FILE
},
3510 { "save", shi_save_message
, N_("save current message into local file"),
3511 shi_save_message_usage
, ARGTYPE_FILE
},
3512 { "show", shi_show_message
, N_("show current message"), NULL
,
3514 { "verify", shi_verify
, N_("verify current message hash"), NULL
,
3519 struct command list_commands
[] = {
3520 { "show", shi_show_list
, N_("show current message list"), NULL
,
3525 #undef COMMON_COMMANDS
3528 static void main_version(void) {
3533 shi_copying(0, NULL
);
3537 static void main_usage(const char *command
) {
3539 "Usage: %s [OPTION...]\n"
3540 "Access ISDS, process local data box messages or delivery details, submit\n"
3541 "document to authorized conversion.\n"
3544 " -c FILE use the FILE as configuration file instead of ~/%s\n"
3545 " -e COMMANDS execute COMMANDS (new line separated) and exit\n"
3546 " -V show version info and exit\n"
3548 (command
) ? command
: "shigofumi",
3553 int main(int argc
, char **argv
) {
3555 char *config_file
= NULL
;
3556 char *batch_commands
= NULL
;
3557 int retval
= EXIT_SUCCESS
;
3559 setlocale(LC_ALL
, "");
3561 /* Initialize gettext */
3562 bindtextdomain(PACKAGE
, LOCALEDIR
);
3563 textdomain(PACKAGE
);
3566 /* Default output */
3569 /* Parse arguments */
3571 while ((opt
= getopt(argc
, (char * const *)argv
, "c:e:V")) != -1) {
3574 config_file
= optarg
;
3577 batch_commands
= optarg
;
3581 shi_exit(EXIT_SUCCESS
);
3584 main_usage((argv
[0]) ? basename(argv
[0]): NULL
);
3585 shi_exit(EXIT_FAILURE
);
3590 if (shi_init(config_file
)) {
3591 shi_exit(EXIT_FAILURE
);
3594 /*shi_login(NULL);*/
3596 if (batch_commands
) {
3597 if (shi_batch(batch_commands
))
3598 retval
= EXIT_FAILURE
;