diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 16:14:06 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 16:14:06 +0000 |
commit | eee068778cb28ecf3c14e1bf843a95547d72c42d (patch) | |
tree | 0e07b30ddc5ea579d682d5dbe57998200d1c9ab7 /common | |
parent | Initial commit. (diff) | |
download | gnupg2-eee068778cb28ecf3c14e1bf843a95547d72c42d.tar.xz gnupg2-eee068778cb28ecf3c14e1bf843a95547d72c42d.zip |
Adding upstream version 2.2.40.upstream/2.2.40upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'common')
145 files changed, 57244 insertions, 0 deletions
diff --git a/common/ChangeLog-2011 b/common/ChangeLog-2011 new file mode 100644 index 0000000..4b95b35 --- /dev/null +++ b/common/ChangeLog-2011 @@ -0,0 +1,2494 @@ +2011-12-01 Werner Koch <wk@g10code.com> + + NB: ChangeLog files are no longer manually maintained. Starting + on December 1st, 2011 we put change information only in the GIT + commit log, and generate a top-level ChangeLog file from logs at + "make dist". See doc/HACKING for details. + +2011-11-30 Werner Koch <wk@gnupg.org> + + Rewrite dns-cert.c to not use the gpg-only iobuf stuff. + * dns-cert.c: Remove iobuf.h. + (get_dns_cert): Rename to _get_dns_cert. Remove MAX_SIZE arg. + Change iobuf arg to a estream-t. Rewrite function to make use of + estream instead of iobuf. Require all parameters. Return an + gpg_error_t error instead of the type. Add arg ERRSOURCE. + * dns-cert.h (get_dns_cert): New macro to pass the error source to + _gpg_dns_cert. + * t-dns-cert.c (main): Adjust for changes in get_dns_cert. + + * estream.c (es_fopenmem_init): New. + * estream.h (es_fopenmem_init): New. + +2011-11-29 Werner Koch <wk@g10code.com> + + * estream.c (func_mem_create): Don't set FUNC_REALLOC if GROW is + not set. Require FUNC_REALLOC if DATA is NULL and FUNC_FREE is + given. + + * dns-cert.c: Use new CERTTYPE_ constants for better readability. + +2011-11-28 Werner Koch <wk@g10code.com> + + * t-dns-cert.c (main): Increase MAX_SIZE to 64k. + + * dns-cert.c (get_dns_cert): Factor test code out to ... + * t-dns-cert.c: new file. + +2011-10-24 Werner Koch <wk@g10code.com> + + * dotlock.h, dotlock.c: Add alternative to allow distribution of + these files under a modified BSD license + +2011-09-30 Werner Koch <wk@g10code.com> + + Change the license of all JNLIB parts from LPGLv3+ to to LGPLv3+ + or GPLv2+. + + * dotlock.h (DOTLOCK_EXT_SYM_PREFIX): New macro. + +2011-09-29 Werner Koch <wk@g10code.com> + + * dotlock.c (DOTLOCK_USE_PTHREAD): New macro. + [DOTLOCK_USE_PTHREAD] (all_lockfiles_mutex): New. + (LOCK_all_lockfiles, UNLOCK_all_lockfiles): New. Use them to + protect access to all_lockfiles. + (dotlock_set_fd, dotlock_get_fd): New. + +2011-09-28 Werner Koch <wk@g10code.com> + + * dotlock.c (dotlock_take, dotlock_take_unix, dotlock_take_w32): + Implement arbitrary timeout values. + (dotlock_create): Add arg FLAGS for future extensions. + +2011-09-27 Werner Koch <wk@g10code.com> + + * dotlock.c (dotlock_take_unix): Check only the link count and not + the error return from link. + (use_hardlinks_p): New. + (dotlock_create_unix): Test for hardlinks. + (dotlock_take_unix): Implement O_EXCL locking. + +2011-09-23 Werner Koch <wk@g10code.com> + + * dotlock.c: Factor Unix and W32 specific code out into specific + functions. Define HAVE_POSIX_SYSTEM. Rearrange some functions. + (disable_dotlock): Rename to dotlock_disable. + (create_dotlock): Rename to dotlock_create. + (destroy_dotlock): Rename to dotlock_destroy. + (make_dotlock): Rename to dotlock_take. + (release_dotlock): Rename to dotlock_release. + +2011-09-22 Werner Koch <wk@g10code.com> + + * dotlock.c: Remove support for RISCOS. + +2011-08-10 Werner Koch <wk@g10code.com> + + * t-exechelp.c (test_close_all_fds): Don't use the DUMMY_FD var. + + * pka.c (get_pka_info): Remove unused var. + + * signal.c (got_fatal_signal): Remove unused var. + + * estream.c (es_fread, es_fwrite): Remove unused var. + +2011-07-20 Werner Koch <wk@g10code.com> + + * ssh-utils.c, ssh-utils.h: New. + * t-ssh-utils.c: New. + * Makefile.am (t_ssh_utils_LDADD): New. + (module_tests): Add t-ssh-utils.c + +2011-06-01 Marcus Brinkmann <mb@g10code.com> + + * util.h: Undef snprintf before redefining it. + +2011-05-20 Werner Koch <wk@g10code.com> + + * util.h: Remove some error code substitutes. + +2011-04-25 Werner Koch <wk@g10code.com> + + * userids.c (classify_user_id): Add arg OPENPGP_HACK to fix + regression from 2009-12-08. + +2011-04-01 Werner Koch <wk@g10code.com> + + * sysutils.c (get_uint_nonce): New. + +2011-03-03 Werner Koch <wk@g10code.com> + + * estream.c (struct estream_list): Rename to estream_list_s and + simplify. A double linked list is overkill for our purpose. + (do_list_add, do_list_remove): Adjust accordingly. + (_es_get_std_stream): Ditto. + (do_list_iterate, estream_iterator_t): Remove; it is used only at + one place. + (es_fflush): Replace iteration function. Also lock each stream + while flushing all streams. + +2011-02-27 Werner Koch <wk@g10code.com> + + * gettime.c (isotime2epoch): Factor check code out to .. + (isotime_p): .. new. + (isotime_human_p): New. + (string2isotime): New. + * t-gettime.c (test_string2isotime): New. + +2011-02-11 Andrey Jivsov <openpgp@brainhub.org> + + * openpgp-oid.c (openpgp_oid_to_str): Use unsigned int for + get_opaque. Fixes a bug on 64 bit platforms. + +2011-02-08 Werner Koch <wk@g10code.com> + + * http.c (connect_server): Add arg R_HOST_NOT_FOUND. + +2011-02-07 Werner Koch <wk@g10code.com> + + * http.c (my_socket_new, my_socket_ref, my_socket_unref): New. + (cookie_close, cookie_read, cookie_write, http_close, _http_open) + (send_request): Replace use of an socket integer by the new socket + object. + (_http_raw_connect): New. + (fp_onclose_notification): New. + (_http_raw_connect, _http_wait_response, http_close): Register and + unregister this notification. + * http.h (http_raw_connect): New. + + * http.h (parsed_uri_s): Add field IS_OPAQUE. + (http_req_t): Add HTTP_REQ_OPAQUE. + * http.c (do_parse_uri): Parse unknown schemes into PATH. + (my_socket_new, my_socket_ref, my_socket_unref): New. + (send_request): Simplify save_errno stuff. + +2011-02-03 Werner Koch <wk@g10code.com> + + * status.h (STATUS_DECRYPTION_INFO): New. + + * argparse.c (strusage): Update copyright year. + +2011-01-31 Werner Koch <wk@g10code.com> + + * openpgp-oid.c: New. + * t-openpgp-oid.c: New. + +2011-01-20 Werner Koch <wk@g10code.com> + + Fix bug#1313. + + * http.c (my_select): New. Define to pth_select if building with Pth. + (start_server, write_server, cookie_read, cookie_write): Use it. + (my_connect): New. Define to pth_connect if building with Pth. + (connect_server): Use it. + (my_accept): New. Define to pth_accept if building with Pth. + (start_server): Use it. + +2011-01-20 Werner Koch <wk@g10code.com> + + * util.h (struct b64state): Add field LASTERR. + * b64enc.c (enc_start, b64enc_write, b64enc_finish): Handle + LASTERR. This is to make sure that we don't leak strduped data. + * b64dec.c (b64dec_start, b64dec_proc, b64dec_finish): Ditto. + + * http.c (escape_data): New. + (insert_escapes): Implement using escape_data. + (http_escape_data): New. + +2011-01-19 Werner Koch <wk@g10code.com> + + * homedir.c (gnupg_module_name): Use NAME_OF_INSTALLED_GPG instead + of "gpg2". + +2011-01-18 Werner Koch <wk@g10code.com> + + * iobuf.c (file_es_filter_ctx_t): New. + (file_es_filter): New. + (iobuf_esopen): New. + + * membuf.c (clear_membuf, peek_membuf): New. + + * util.h (GPG_ERR_NO_KEYSERVER): New. + + * keyserver.h (keyserver_spec): Move from ../g10/options.h to here. + + * http.c (do_parse_uri): Add arg NO_SCHEME_CHECK. Change all + callers. Support HKP and HKPS. + (_http_parse_uri): Do proper error management. + * http.h (parsed_uri_s): Add field IS_HTTP. + (http_parse_uri): Support NO_SCHEME_CHECK arg. + + * estream.c (es_func_mem_write): Fix computation of NEWSIZE. + +2011-01-10 Werner Koch <wk@g10code.com> + + * session-env.c (update_var): Fix same value detection. Fixes + bug#1311. + +2010-12-17 Werner Koch <wk@g10code.com> + + * asshelp.c (lock_spawning): Add arg VERBOSE. Improve timeout + management. Make callers pass a value for VERBOSE. + (lock_agent_spawning, unlock_agent_spawning): Remove. Change + callers to use lock_spawning and unlock_spawning. + +2010-12-17 Marcus Brinkmann <mb@g10code.com> + + * homedir.c (gnupg_cachedir): Create /temp subdirectories. + +2010-12-02 Werner Koch <wk@g10code.com> + + * miscellaneous.c (gnupg_cipher_algo_name): New. Replace all + users of gcry_cipher_algo_name by this one. + + * logging.c (fun_cookie_s) [W32CE]: Add field USE_WRITEFILE. + (fun_writer) [W32CE]: Make use of it. + (set_file_fd) [W32CE]: Implement special filename "GPG2:". + +2010-11-25 Werner Koch <wk@g10code.com> + + * asshelp.c (start_new_gpg_agent): Change style of startup info. + (start_new_dirmngr): Ditto. + +2010-11-23 Werner Koch <wk@g10code.com> + + * asshelp.c (SECS_TO_WAIT_FOR_AGENT, SECS_TO_WAIT_FOR_DIRMNGR): + Use these constants. For W32CE increase them to 30 seconds. + (start_new_gpg_agent): Print time to startup agent. + (start_new_dirmngr): Ditto. + +2010-11-04 Werner Koch <wk@g10code.com> + + * logging.c (do_logv) [W32]: Don't set a default log stream if the + registry entry is empty. + +2010-10-27 Werner Koch <wk@g10code.com> + + * gettime.c (gnupg_get_isotime): Compare to (time_t)-1. + (epoch2isotime): Ditto. + (IS_INVALID_TIME_T): New. + (asctimestamp): Use new macro. + (strtimestamp, isotimestamp): Ditto. Use snprintf. + +2010-10-25 Werner Koch <wk@g10code.com> + + * logging.c (do_log): Rename to log_log and make global. + +2010-10-20 Werner Koch <wk@g10code.com> + + * i18n.c (i18n_init) [USE_SIMPLE_GETTEXT]: Call textdomain. + +2010-10-14 Werner Koch <wk@g10code.com> + + * asshelp.c (start_new_gpg_agent): Print a notice once the agent + has been started. + (start_new_dirmngr): Likewise. + +2010-10-13 Werner Koch <wk@g10code.com> + + * miscellaneous.c (parse_version_number, parse_version_string) + (gnupg_compare_version): New. + +2010-10-04 Werner Koch <wk@g10code.com> + + * gettime.c (asctimestamp) [W32CE]: Do not print the timezone. + +2010-09-30 Werner Koch <wk@g10code.com> + + * util.h (GPG_ERR_FULLY_CANCELED): Add replacement. + +2010-09-17 Werner Koch <wk@g10code.com> + + * http.c (INADDR_NONE): Provide fallback. + * logging.c (INADDR_NONE): Ditto. + +2010-09-16 Werner Koch <wk@g10code.com> + + * util.h: Add GPG_ERR_MISSING_ISSUER_CERT. + * status.c (get_inv_recpsgnr_code): Ditto. + +2010-09-13 Werner Koch <wk@g10code.com> + + * homedir.c (gnupg_bindir) [W32CE]: Change to bin/. + (gnupg_libexecdir) [W32]: Call gnupg_bindir. + (gnupg_libdir, gnupg_datadir, gnupg_localedir) [W32]: Simplify by + using xstrconcat. + (gnupg_module_name): Ditto. + (w32_rootdir): Strip a trailing "bin". + +2010-09-02 Werner Koch <wk@g10code.com> + + * util.h (GPG_ERR_NOT_INITIALIZED): Define if not defined. + +2010-09-01 Marcus Brinkmann <marcus@g10code.de> + + * estream.c (_es_set_std_fd): Disable debug output. + +2010-08-26 Werner Koch <wk@g10code.com> + + * estream.c (es_convert_mode): Rename to parse_mode. + (parse_mode): Add arg R_CMODE and parse key value pairs. Use Use + 664 as the default mode. Change callers. + (ES_DEFAULT_OPEN_MODE): Remove. + (es_fopen, do_fpopen, do_w32open, es_freopen): Support a creation + mode. + (es_func_file_create): Rename to func_file_create and add arg CMODE. + (es_func_fd_create): Rename to func_fd_create. + (es_func_fp_create): Rename to func_fp_create. + (es_list_add): Rename to do_list_add. + (es_list_remove): Rename to do_list_remove. + (es_list_iterate): Rename to do_list_iterate. + (es_pth_read): Rename to do_pth_read. + (es_deinit): Rename to do_deinit. + (es_init_do): Rename to do_init. + (es_func_mem_create): Rename to func_mem_create. + +2010-08-23 Werner Koch <wk@g10code.com> + + * exechelp-w32ce.c: Rewrite all spawn stuff. + + * exechelp-w32.c (close_all_fds) [W32]: Make it a dummy function. + + * estream.c (es_onclose): New. + (notify_list_t, onclose): New. + (struct estream_internal): Add field ONCLOSE. + (es_initialize, es_deinitialize): Manage new field. + (do_close): Call onclose notify functions. + +2010-08-20 Werner Koch <wk@g10code.com> + + * exechelp-w32.c (create_inheritable_pipe): Change arg to HANDLE. + + * estream.h (es_sysopen_t): New. + * estream.c (es_func_w32_create, es_func_w32_read) + (es_func_w32_write, es_func_w32_seek, es_func_w32_destroy) + (estream_functions_w32, estream_cookie_fd): New. Only for W32. + (es_sysopen, es_sysopen_nc): New. + (do_w32open, do_sysopen): New. + (es_syshd, es_syshd_unlocked): New. + (struct estream_internal): Replace filed FD by SYSHD. + (es_initialize): Clear SYSHD_VALID. + (map_w32_to_errno): New. + (es_get_fd): Remove. + (es_fileno_unlocked): Re-implement using es_syshd. + (es_initialize, es_create): Replace arg FD by SYSHD. + (es_fopen, es_mopen, es_fopenmem, do_fdopen, do_fpopen) + (es_tmpfile): Use SYSHD instead of FD. + (es_destroy): Rename to do_close. + +2010-08-19 Werner Koch <wk@g10code.com> + + * exechelp-posix.c (create_pipe_and_estream): New. + (gnupg_spawn_process): Rework this function and its calling + convention; it is not used anyway. + * exechelp-w32.c (gnupg_spawn_process): Ditto. + +2010-08-18 Werner Koch <wk@g10code.com> + + * logging.c (writen): Add arg IS_SOCKET. + (fun_writer): Pass the is_socket flag. + (do_logv) [W32]: Allow for a default log stream + + * estream.c (struct estream_internal): Remove obsolete fields + PRINT_FP, PRINT_ERRNO, PRINT_ERR and all remaining code cruft. + +2010-08-16 Werner Koch <wk@g10code.com> + + * estream.c (es_printf_unlocked, es_printf): New. + + * asshelp.c (lock_agent_t): Rename to lock_spawn_t. + (lock_agent_spawning, unlock_agent_spawning): Factor code out to ... + (lock_spawning, unlock_spawning): .. new. + (start_new_gpg_agent): Make more use of ERRSOURCE. + (start_new_dirmngr): New. + +2010-08-13 Werner Koch <wk@g10code.com> + + * Makefile.am (audit-events.h, status-codes.h): Fix srcdir problem + amd depend on Makefile.am instead of Makefile. + +2010-08-12 Werner Koch <wk@g10code.com> + + * sysutils.c (gnupg_remove) [W32CE]: Fix returned error. + +2010-08-09 Werner Koch <wk@g10code.com> + + * logging.c (WITH_IPV6): New macro. + (parse_portno): New. From libassuan. + (fun_writer): Support TCP logging on all platforms. + (sock_close): New. + +2010-08-06 Werner Koch <wk@g10code.com> + + * homedir.c (dirmngr_socket_name) [W32CE]: Base on default homedir. + (gnupg_cachedir) [W32CE]: Drop drive letter. + + * http.c (http_open_document): Rename to _http_open_document and + add arg ERRSOURCE. Pass ERRSOURCE to all called funcs. + (http_wait_response, http_open, http_parse_uri): Likewise. + (do_parse_uri, parse_response, store_header): Change to return an + gpg_err_code_t. Change callers. + (send_request): Add arg ERRSOURCE. Change callers. + * http.h (http_open_document, http_wait_response, http_open) + (http_parse_uri): Define as macro. + +2010-08-05 Werner Koch <wk@g10code.com> + + * estream.h (es_asprintf, es_vasprintf): Add lost prototyps. + + * http.c: Require estream and make HTTP_USE_ESTREAM obsolete. It + make the code unreadable and we require estream anyway for GnuPG. + (http_wait_response): Get use of cookies right. + (send_request): s/xtryasprintf/es_asprintf/ to allow standalone + use of the code. + (insert_escapes, connect_server): s/sprintf/snprintf/. + (parse_response): s/my_read_line/es_read_line/. + (my_read_line): Remove. + (write_server): Use pth_write. + +2010-07-26 Werner Koch <wk@g10code.com> + + * estream.c (es_func_fp_write) [W32]: Write smaller chunks. + +2010-07-25 Werner Koch <wk@g10code.com> + + * argparse.c (initialize): Use ARGPARSE_PRINT_WARNING constant. + +2010-07-24 Werner Koch <wk@g10code.com> + + * estream.c (es_set_binary): New. + +2010-07-19 Werner Koch <wk@g10code.com> + + * utf8conv.c (utf8_to_wchar): s/malloc/jnlib_malloc/. + +2010-07-16 Werner Koch <wk@g10code.com> + + * http.h (HTTP_FLAG_IGNORE_CL): Add flag . + * http.c (WITHOUT_GNU_PTH): Test macro for Pth support. + (http_parse_uri): s/xcalloc/xtrycalloc/. + (send_request): Replace of discrete allocation and sprintf by + xtryasprintf. + (http_wait_response): Replace HTTP_FLAG_NO_SHUTDOWN by + HTTP_FLAG_SHUTDOWN to change the default to no shutdown. + (cookie_read) [HAVE_PTH]: Use pth_read. + (longcounter_t): New. + (struct cookie_s): Add support for content length. Turn flag + fields into bit types. + (parse_response): Parse content length header. + (cookie_read): Take care of the content length. + +2010-07-08 Werner Koch <wk@g10code.com> + + * estream.c (estream_functions_file): Remove and replace by + identical estream_functions_fd. + +2010-07-06 Werner Koch <wk@g10code.com> + + * util.h (b64state): Add field STREAM. + * b64enc.c (b64enc_start): Factor code out to .. + (enc_start): new. + (b64enc_start_es, my_fputs): New. + (b64enc_write, b64enc_finish): Support estream. + +2010-06-24 Werner Koch <wk@g10code.com> + + * asshelp.c (lock_agent_spawning) [W32]: Use CreateMutexW. + (start_new_gpg_agent): Use HANG option for gnupg_wait_progress. + Fixes regression from 2010-06-09. + +2010-06-21 Werner Koch <wk@g10code.com> + + * util.h (xfree_fnc): New. + +2010-06-18 Werner Koch <wk@g10code.com> + + * util.h (GPG_ERR_MISSING_KEY) [!GPG_ERR_MISSING_KEY]: New. + + * sexputil.c (make_canon_sexp_pad): Add arg SECURE. + +2010-06-17 Werner Koch <wk@g10code.com> + + * sexputil.c (make_canon_sexp_pad): New. + +2010-06-14 Werner Koch <wk@g10code.com> + + * membuf.c (put_membuf): Add shortcut for !LEN. + +2010-06-11 Marcus Brinkmann <marcus@g10code.de> + + * sysutils.c (translate_sys2libc_fd): Revert last change. + (translate_sys2libc_fd_int): Revert last change. + +2010-06-10 Marcus Brinkmann <marcus@g10code.de> + + * sysutils.c (translate_sys2libc_fd) [HAVE_W32CE_SYSTEM]: + Implement. + (translate_sys2libc_fd_int) [HAVE_W32CE_SYSTEM]: Don't call + translate_sys2libc_fd. + + * estream.c (_es_get_std_stream): Fix cut&paste bug. + +2010-06-09 Werner Koch <wk@g10code.com> + + * exechelp-posix.c, exechelp-w32.c + * exechelp-w32ce.c (gnupg_wait_process): Add new arg HANG. Change + all callers. + (gnupg_release_process): New. Use it after all calls to + gnupg_wait_process. + + * util.h (GNUPG_MODULE_NAME_DIRMNGR_LDAP): New. + * homedir.c (gnupg_cachedir): New. + (w32_try_mkdir): New. + (dirmngr_socket_name): Change standard socket name. + (gnupg_module_name): Support GNUPG_MODULE_NAME_DIRMNGR_LDAP. + + * logging.c (log_set_get_tid_callback): Replace by ... + (log_set_pid_suffix_cb): .. new. + (do_logv): Change accordingly. + +2010-06-08 Marcus Brinkmann <marcus@g10code.de> + + * Makefile.am (AM_CFLAGS): Add $(LIBASSUAN_CFLAGS). + (t_common_ldadd): Add $(LIBASSUAN_LIBS). + * sysutils.c: Include <assuan.h>. + (translate_sys2libc_fd_int): Cast to silence gcc warning. + * iobuf.c: Include <assuan.h> + (translate_file_handle): Fix syntax error. + +2010-06-08 Werner Koch <wk@g10code.com> + + * iobuf.c (translate_file_handle) [W32CE]: Handle rendezvous ids. + +2010-06-07 Werner Koch <wk@g10code.com> + + * sysutils.c [W32CE]: Finish pipe creation. + + * estream.c (es_fname_get, es_fname_set): New. + (fname_set_internal): New. + (struct estream_internal): Add fields printable_fname and + printable_fname_inuse. + (_es_get_std_stream): Set stream name. + (es_fopen, es_freopen, es_deinitialize): Set fname. + + * exechelp-posix.c (gnupg_spawn_process): Allow passing INFILE or + OUTFILE as NULL. + * exechelp-w32.c (gnupg_spawn_process): Ditto. + * exechelp-w32ce.c (gnupg_spawn_process): Return an error for + INFILE or OUTFILE passed as NULL. + +2010-06-01 Werner Koch <wk@g10code.com> + + * logging.c (log_get_stream): Make sture a log stream is available. + +2010-05-30 Werner Koch <wk@g10code.com> + + * init.c (writestring_via_estream): New. + (init_common_subsystems): Register with argparse. + + * argparse.c (argparse_register_outfnc): New. + (writestrings, flushstrings): New. Use them instead of stdout or + stderr based functions. + +2010-05-04 Werner Koch <wk@g10code.com> + + * estream.c (_es_get_std_stream): Re-use registered standard fds. + (IS_INVALID_FD, ESTREAM_SYS_YIELD): New. + (es_func_fd_read, es_func_fd_write, es_func_fd_seek) + (es_func_fd_destroy): Implement a dummy stream. + + * exechelp-w32ce.c (build_w32_commandline): Add args FD0_ISNULL + and FD1_ISNULL. Remove arg PGMNAME. Change callers. + (gnupg_spawn_process_detached): Implement. + (gnupg_spawn_process_fd): Implement one special case for now. + +2010-05-03 Werner Koch <wk@g10code.com> + + * asshelp.c (lock_agent_spawning, unlock_agent_spawning): New. + (start_new_gpg_agent): Test for configured standard socket and + try to fire up the agent in this case. + + * exechelp-posix.c (gnupg_wait_process): Do not log a message if + EXITCODE is given. + (gnupg_spawn_process_detached): Do not reuse PID for the second fork. + +2010-04-26 Werner Koch <wk@g10code.com> + + * utf8conv.c (load_libiconv) [W32CE]: No libiconv warning + + * init.c (init_common_subsystems) [W32CE]: Register the sleep + function before es_init. + +2010-04-20 Werner Koch <wk@g10code.com> + + * estream.c (es_deinit): New. + (es_init_do): Install atexit handler to flush all streams. + + * Makefile.am (common_sources): Add gettime.h. + +2010-04-20 Marcus Brinkmann <marcus@g10code.de> + + * logging.c (do_log_ignore_arg): New helper function. + (log_string): Use it to remove ugly volatile hack that causes gcc + warning. + (log_flush): Likewise. + * sysutils.c (gnupg_unsetenv) [!HAVE_W32CE_SYSTEM]: Return something. + (gnupg_setenv) [!HAVE_W32CE_SYSTEM]: Likewise. + * pka.c (get_pka_info): Solve strict aliasing rule violation. + * t-exechelp.c (test_close_all_fds): Use dummy variables to + silence gcc warning. + +2010-04-15 Werner Koch <wk@g10code.com> + + * util.h: Factor time related functions out to ... + * gettime.h: New. + (gnupg_copy_time): Move to ... + * gettime.c (gnupg_copy_time): New. + + * sysutils.c (gnupg_setenv) [!W32CE]: Add missing return. + (gnupg_unsetenv) [!W32CE]: Add missing return. + +2010-04-14 Werner Koch <wk@g10code.com> + + * Makefile.am (noinst_LIBRARIES) [W32CE]: Exclude libsimple-pwquery. + + * w32help.h (umask) [W32CE]: New. + + * sysutils.c (_gnupg_isatty): New. + * util.h (gnupg_isatty): New. + + * asshelp.c (setup_libassuan_logging): Read ASSUAN_DEBUG envvar. + (my_libassuan_log_handler): Use it. + * sysutils.c (_gnupg_getenv): Implement ASSUAN_DEBUG. + +2010-04-08 Werner Koch <wk@g10code.com> + + * w32help.h (_setmode, setmode) [W32CE]: Provide prototype and + macro. + +2010-04-07 Werner Koch <wk@g10code.com> + + * mischelp.c (timegm): Replace unsetenv/putenv by gnupg_unsetenv. + + * sysutils.c: Include setenv.h. + (gnupg_setenv, gnupg_unsetenv): New. + + +2010-04-06 Werner Koch <wk@g10code.com> + + * sysutils.c (gnupg_mkdir): New. + +2010-03-29 Werner Koch <wk@g10code.com> + + * init.c (sleep_on_exit): Change to 400ms. + +2010-03-25 Werner Koch <wk@g10code.com> + + * init.c (sleep_on_exit) [W32CE]: New. + (init_common_subsystems): Call it. + +2010-03-24 Werner Koch <wk@g10code.com> + + * stringhelp.c (change_slashes, compare_filenames): Replace + HAVE_DRIVE_LETTERS by HAVE_DOSISH_SYSTEM. + (make_basename, make_dirname): Detect backslashes and drive + letters separately. + + * dotlock.c (make_dotlock, create_dotlock, release_dotlock): Use + LockFileEx and UnlockFileEx to support W32CE. + + * ttyio.c (USE_W32_CONSOLE): Replace all _WIN32 by this. + (init_ttyfp) [W32CE]: Use stderr. + + * iobuf.c (FD_FOR_STDIN, FD_FOR_STDOUT) [W32CE]: Use estream. + (translate_file_handle) [W32CE]: Remove handle translation. + +2010-03-23 Werner Koch <wk@g10code.com> + + * sysutils.c (gnupg_remove): New. + +2010-03-22 Werner Koch <wk@g10code.com> + + * exechelp-w32ce.c (build_w32_commandline): Replace by code from + libassuan. + (create_inheritable_pipe): Use _assuan_w32ce_prepare_pipe. + (build_w32_commandline_copy, do_create_pipe): Remove. + + * exechelp-posix.c (gnupg_spawn_process): Change to use estream + also for INFILE and STATUSFILE. + * exechelp-w32.c (gnupg_spawn_process): Ditto. + +2010-03-22 Werner Koch <wk@g10code.com> + + * exechelp.c: Remove after factoring all code out to ... + * exechelp-posix.c, exechelp-w32.c, exechelp-w32ce.c: .. new. + + * exechelp.c (create_inheritable_pipe_r) + (create_inheritable_pipe_w): Fold both into ... + (create_inheritable_pipe): .. New. Change callers to use this. + (gnupg_create_inbound_pipe, gnupg_create_outbound_pipe): Factor + code out to ... + (do_create_pipe): .. New. + + * init.c (parse_std_file_handles): Change to use rendezvous ids. + +2010-03-15 Werner Koch <wk@g10code.com> + + * init.c (init_common_subsystems): Add args ARGCP and + ARGVP. Change all callers to provide them. + (parse_std_file_handles): New. + + * t-sysutils.c (rewind) [W32CE]: Provide a replacement. + + * Makefile.am (module_tests) [W32CE]: Don't build t-exechelp for now. + + * sysutils.c (gnupg_allow_set_foregound_window) [W32CE]: Don't + call AllowSetForegroundWindow. + + * logging.c (isatty) [W32CE]: New. + (fun_writer, set_file_fd): Use estream even for the internal error + messages. + (log_string, log_flush): Make DUMMY_ARG_PTR static. + +2010-03-15 Werner Koch <wk@g10code.com> + + * asshelp.c (send_pinentry_environment) [!HAVE_SETLOCALE]: Do not + define OLD_LC. + * http.c (connect_server) [!USE_DNS_SRV]: Mark SRVTAG unused. + * dns-cert.c (get_dns_cert) [!USE_DNS_CERT]: Mark args unused. + * pka.c (get_pka_info): Ditto. + + * signal.c (pause_on_sigusr): Remove. It was used in ancient gpg + version with shared memory IPC. Last caller removed on 2006-04-18. + (do_block) [W32]: Mark arg unused. + + * exechelp.c (w32_open_null): Use CreateFileW. + + * init.c (init_common_subsystems): Add args ARGCP and ARGVP. + Change all callers to pass them. + + * logging.c (S_IRGRP, S_IROTH, S_IWGRP, S_IWOTH) [W32]: New. + (fun_writer, set_file_fd) [W32]: Disable socket code. + + * localename.c: Include gpg-error.h. + + * util.h (GPG_ERR_NOT_ENABLED): Remove this temporary definition. + +2010-03-12 Werner Koch <wk@g10code.com> + + * status.h (STATUS_ENTER): New. + + * ttyio.c (tty_fprintf): Change to use estream. + + * miscellaneous.c (print_utf8_string): Rename to print_utf8_buffer + and change FP arg to an estream. Change all callers. + (print_utf8_string2): Ditto; new name is to print_utf8_buffer2. + +2010-03-11 Werner Koch <wk@g10code.com> + + * miscellaneous.c (print_string): Remove. + + * estream.c (es_setvbuf): Fix parameter check. + (es_set_buffering): Allow a SIZE of 0. + * asshelp.c (setup_libassuan_logging, my_libassuan_log_handler): New. + * logging.c (do_logv): Add arg IGNORE_ARG_PTR. Change all callers. + (log_string): New. + (log_flush): New. + (set_file_fd): Simplify by using estreams es_stderr. + + * estream.h (es_stdout, es_stderr, es_stdin): New. + +2010-03-10 Werner Koch <wk@g10code.com> + + * estream.c (es_func_fp_read, es_func_fp_write, es_func_fp_seek) + (es_func_fp_destroy): Allow a NULL FP to implement a dummy stream. + (do_fpopen): Ditto. + (es_vfprintf_unlocked): New. + (es_fprintf_unlocked): Make public. + (es_fputs_unlocked): New. + + * logging.h: Replace FILE* by estream_t. + * logging.c: Remove USE_FUNWRITER cpp conditional because we now + use estream. + (my_funopen_hook_ret_t, my_funopen_hook_size_t): Replace by + ssize_t. + (log_get_stream): Change to return an estream_t. + (set_file_fd): Always close the log stream because it can't be + assigned to stderr or stdout directly. Use a dummy estream as + last resort log stream. + (log_test_fd, log_get_fd): Use es_fileno. + (log_get_stream): Assert that we have a log stream. + (do_logv): Use estream functions and lock the output. + +2010-03-10 Werner Koch <wk@g10code.com> + + * util.h: Replace jnlib path part by common. + (snprintf): Use the replacement macro on all platforms. + + * Makefile.am (jnlib_sources): New. + (libcommon_a_SOURCES, libcommonpth_a_SOURCES): Add jnlib_sources. + (jnlib_tests): New. + (noinst_PROGRAMS, TESTS): Add jnlib_tests. + (t_common_ldadd): Remove libjnlib.a. + + * README.jnlib, ChangeLog.jnlib, libjnlib-config.h, argparse.c + * argparse.h, dotlock.c, dotlock.h, dynload.h, logging.c + * logging.h, mischelp.c, mischelp.h, stringhelp.c, stringhelp.h + * strlist.c, strlist.h, types.h, utf8conv.c, utf8conv.h + * w32-afunix.c, w32-afunix.h, w32-reg.c, w32help.h, xmalloc.c + * xmalloc.h, t-stringhelp.c, t-support.c, t-support.h + * t-timestuff.c, t-w32-reg.c: Move from jnlib to here. + + * init.c: Remove "estream.h". + * util.h: Include "estream.h". + + * xasprintf.c, ttyio.c: Remove "estream-printf.h". + +2010-03-08 Werner Koch <wk@g10code.com> + + * exechelp.c [!HAVE_SIGNAL_H]: Do not include signal.h. + (DETACHED_PROCESS, CREATE_NEW_PROCESS_GROUP) [W32CE]: Provide stubs. + + * iobuf.h (iobuf_ioctl_t): New. Use the new macros instead of the + hard wired values. + * iobuf.c (iobuf_append): Remove. + (iobuf_fdopen): Factor code out to ... + (do_iobuf_fdopen): ... new. + (iobuf_fdopen_nc): New. + (iobuf_open_fd_or_name): Implement using iobuf_fdopen_nc. + + * iobuf.c (INVALID_FD): Replace by GNUPG_INVALID_FD. + (fp_or_fd_t): Replace by gnupg_fd_t. + (my_fileno): Replace by the FD2INT macro. + (FILEP_OR_FD_FOR_STDIN, FILEP_OR_FD_FOR_STDOUT): Rename to + FD_FOR_STDIN, FD_FOR_STDOUT. + (file_filter): Make full use of FD_FOR_STDIN. + (USE_SETMODE): Remove. Not needed without stdio. + (my_fopen_ro, my_fopen): Replace unneeded macros. + + * iobuf.c [FILE_FILTER_USES_STDIO]: Remove all code. It has not + been used for a long time. + + * exechelp.h: Include "estream.h". + + * exechelp.c (gnupg_spawn_process): Change OUTFILE to an estream_t. + +2010-03-02 Werner Koch <wk@g10code.com> + + * estream.c, estream.h, estream-printf.c, estream-printf.h: Update + from libestream. + +2010-03-01 Werner Koch <wk@g10code.com> + + * signal.c [!HAVE_SIGNAL_H]: Don't include signal.h. + + * iobuf.c (direct_open) [W32CE]: Make filename to wchar_t. + (iobuf_cancel) [W32CE]: Use DeleteFile. + + * gettime.c (dump_isotime): Use "%s" to print "none". + + * homedir.c (standard_homedir) [W32CE]: Use wchar_t to create the + directory. + (w32_rootdir) [W32CE]: Likewise. + + * sysutils.c (translate_sys2libc_fd) [W32CE]: Add support. + (gnupg_tmpfile) [W32CE]: Ditto. + (_gnupg_getenv) [W32CE]: New. + + * util.h (getpid, getenv) [W32CE]: New. + + * i18n.c (i18n_switchto_utf8) + (i18n_switchback) [USE_SIMPLE_GETTEXT]: Use new function from + libgpg-error which supports proper restoring. + + * sysutils.c (get_session_marker): Simplified by using gcrypt. + +2009-12-08 Marcus Brinkmann <marcus@g10code.de> + + * Makefile.am (audit-events.h, status.h) [!MAINTAINER_MODE]: No + longer include these rules if not in maintainer mode. + +2009-12-08 Werner Koch <wk@g10code.com> + + * userids.h, userids.c: New. + (classify_user_id): Merged from similar fucntions in sm/ and g10/. + + * dns-cert.c (get_dns_cert): Add support for ADNS. + +2009-12-08 Marcus Brinkmann <marcus@g10code.de> + + * asshelp.c (start_new_gpg_agent): Convert posix FD to assuan FD. + + * asshelp.c (start_new_gpg_agent) [HAVE_W32_SYSTEM]: Add missing + argument in assuan_socket_connect invocation. + * iobuf.c (iobuf_open_fd_or_name): Fix type of FD in function + declaration. + +2009-12-07 Werner Koch <wk@g10code.com> + + * pka.c (get_pka_info): Add support for ADNS. + * src.v (getsrv): Add support for ADNS. + + * srv.c (getsrv): s/xrealloc/xtryrealloc/. + +2009-12-04 Werner Koch <wk@g10code.com> + + * Makefile.am (audit-events.h, status-codes.h): Create files in + the source dir. Fixes bug#1164. + +2009-12-02 Werner Koch <wk@g10code.com> + + * audit.c (proc_type_decrypt, proc_type_sign): Implemented. + (proc_type_verify): Print hash algo infos. + * audit.h (AUDIT_DATA_CIPHER_ALGO, AUDIT_BAD_DATA_CIPHER_ALSO) + (AUDIT_NEW_RECP, AUDIT_DECRYPTION_RESULT, AUDIT_RECP_RESULT) + (AUDIT_ATTR_HASH_ALGO, AUDIT_SIGNED_BY, AUDIT_SIGNING_DONE): + +2009-11-05 Marcus Brinkmann <marcus@g10code.de> + + * asshelp.c (start_new_gpg_agent): Update use of + assuan_socket_connect and assuan_pipe_connect. + +2009-11-02 Marcus Brinkmann <marcus@g10code.de> + + * get-passphrase.c (default_inq_cb, membuf_data_cb): Change return + type to gpg_error_t. + +2009-10-28 Werner Koch <wk@g10code.com> + + * status.h (STATUS_MOUNTPOINT): New. + +2009-10-16 Marcus Brinkmann <marcus@g10code.com> + + * Makefile.am (libcommon_a_CFLAGS): Use LIBASSUAN_CFLAGS instead + of LIBASSUAN_PTH_CFLAGS. + +2009-10-13 Werner Koch <wk@g10code.com> + + * exechelp.c (gnupg_kill_process): New. + +2009-09-29 Werner Koch <wk@g10code.com> + + * exechelp.c (create_inheritable_pipe): Rename to + create_inheritable_pipe_w. + (create_inheritable_pipe_r): New. + (gnupg_create_outbound_pipe): New. + + * iobuf.h: Include "sysutils.h" + + * iobuf.c (iobuf_open_fd_or_name): New. + (iobuf_get_fname_nonnull): New. + +2009-09-23 Marcus Brinkmann <marcus@g10code.de> + + * asshelp.c (start_new_gpg_agent): Allocate assuan context before + starting server. + +2009-09-03 Werner Koch <wk@g10code.com> + + Update from libestream: + * estream-printf.c: Include stdint.h only if HAVE_STDINT_H is + defined. + * estream-printf.c: Remove all test code. Use macro DEBUG instead + of TEST for debugging. + * estream-printf.c (pr_float): Make buffer larger for silly high + numbers. + +2009-08-11 David Shaw <dshaw@jabberwocky.com> + + * ttyio.h, ttyio.c (tty_enable_completion): Some ifdefs around + HAVE_LIBREADLINE to allow building when readline isn't available. + +2009-08-06 Werner Koch <wk@g10code.com> + + * status.h (STATUS_INV_SGNR, STATUS_NO_SGNR): New. + * status.c (get_inv_recpsgnr_code): New. + +2009-07-23 David Shaw <dshaw@jabberwocky.com> + + * srv.c (getsrv): Fix type-punning warning. + +2009-07-23 Werner Koch <wk@g10code.com> + + * util.h (GPG_ERR_NOT_ENABLED): New. + * audit.h (enum): Add AUDIT_CRL_CHECK. + * audit.c (proc_type_verify): Show CRL check result. + +2009-07-06 Werner Koch <wk@g10code.com> + + * get-passphrase.c (struct agentargs): Add SESSION_ENV and remove + obsolete args. + (gnupg_prepare_get_passphrase): Ditto. + + * session-env.c, session-env.h: New. + * t-session-env.c: New. + * Makefile.am (common_sources, module_tests): Add them. + * asshelp.h: Include "session-env.h" + * asshelp.c (send_one_option): Add arg PUTENV. + (send_pinentry_environment): Replace most args by SESSION_ENV and + rewrite fucntion. + (start_new_gpg_agent): Likewise. + + * t-exechelp.c (test_close_all_fds): Remove debug code. + +2009-07-01 Werner Koch <wk@g10code.com> + + * sexputil.c (get_pk_algo_from_canon_sexp): New. + +2009-06-29 Werner Koch <wk@g10code.com> + + * estream.c (BUFFER_ROUND_TO_BLOCK): Remove unused macro. + (es_func_mem_write): Rewrite reallocation part. + + * estream.c (es_write_sanitized_utf8_buffer): Typo typo fix. + +2009-06-25 Werner Koch <wk@g10code.com> + + * estream.c (es_write_sanitized_utf8_buffer): Typo fix. + +2009-06-24 Werner Koch <wk@g10code.com> + + * estream.c (es_read_line): In the malloc error case, set + MAX_LENGTH to 0 only if requested. + * xreadline.c (read_line): Ditto. + * estream.c (es_write_sanitized_utf8_buffer): Pass on error from + es_fputs. + * sexputil.c (get_rsa_pk_from_canon_sexp): Check for error after + the loop. Reported by Fabian Keil. + +2009-06-22 Werner Koch <wk@g10code.com> + + * estream.c (es_pth_read, es_pth_write) [W32]: New. + (ESTREAM_SYS_READ, ESTREAM_SYS_WRITE) [HAVE_PTH]: Use them. + +2009-06-03 Werner Koch <wk@g10code.com> + + * estream.c (es_convert_mode): Rewrite and support the "x" flag. + +2009-05-28 David Shaw <dshaw@jabberwocky.com> + + From 1.4: + + * http.h, http.c (send_request) Pass in a STRLIST for additional + headers. Change all callers. + +2009-05-27 David Shaw <dshaw@jabberwocky.com> + + From 1.4: + + * http.h, http.c (send_request): Pass in srvtag and make its + presence sufficient to turn the feature on. + (http_open): From here. + (http_document): And here. + + * srv.c (getsrv): Raise maximum packet size to 2048, as PACKETSZ + is too small these days. + +2009-05-22 Werner Koch <wk@g10code.com> + + * ttyio.c (tty_cleanup_after_signal): New. + +2009-05-19 Werner Koch <wk@g10code.com> + + * simple-pwquery.c (agent_open): Use SUN_LEN + (JNLIB_NEED_AFLOCAL): Define and include mischelp.h. + +2009-05-07 Werner Koch <wk@g10code.com> + + * sexputil.c (get_rsa_pk_from_canon_sexp): New. + * t-sexputil.c (test_make_canon_sexp_from_rsa_pk): Extend the test. + +2009-04-28 Werner Koch <wk@g10code.com> + + * sexputil.c (make_canon_sexp_from_rsa_pk): New. + * t-sexputil.c (test_make_canon_sexp_from_rsa_pk): New. + +2009-04-01 Werner Koch <wk@g10code.com> + + * iobuf.c: Port David's changes from 1.4: + (fd_cache_invalidate): Pass return code from close back. + (direct_open, iobuf_ioctl): Check that return value. + (fd_cache_synchronize): New. + (iobuf_ioctl): Add new sub command 4 (fsync). + + * iobuf.c (fd_cache_strcmp): New. Taken from 1.4. + (fd_cache_invalidate, fd_cache_close, fd_cache_open): Use it. + + * exechelp.c (gnupg_spawn_process): Implement new flag bit 6. + * sysutils.c (gnupg_allow_set_foregound_window): Allow the use of + ASFW_ANY. + + * membuf.c (put_membuf, get_membuf): Wipe memory on out of core. + +2009-03-31 Werner Koch <wk@g10code.com> + + * percent.c (percent_unescape, percent_plus_unescape): New. + (percent_plus_unescape_inplace, percent_unescape_inplace): New. + (do_plus_or_plain_unescape, count_unescape, do_unescape): New. + (do_unescape_inplace): New. + * t-percent.c (test_percent_plus_escape): Test percent_plus_unescape. + + * get-passphrase.c, get-passphrase.h: New. + * Makefile.am (without_pth_sources): New. + +2009-03-18 Werner Koch <wk@g10code.com> + + * exechelp.c: Include sys/resource.h and sys/stat.h. + (get_max_open_fds): New. + (do_exec): Use it. + (get_all_open_fds): New. + (close_all_fds): New. + (do_exec): Use close_all_fds. + * t-exechelp.c: New. + +2009-03-13 David Shaw <dshaw@jabberwocky.com> + + * http.c (do_parse_uri): Properly handle IPv6 literal addresses as + per RFC-2732. Adapted from patch by Phil Pennock. + +2009-03-12 Werner Koch <wk@g10code.com> + + * gettime.c: Include i18n.h. + (dump_isotime): New. + +2009-03-06 Werner Koch <wk@g10code.com> + + * sexputil.c (make_canon_sexp): New. + +2009-03-03 Werner Koch <wk@g10code.com> + + * exechelp.c (do_exec): Make sure that /dev/null connected FDs are + not closed. + +2009-01-19 Werner Koch <wk@g10code.com> + + * audit.c (writeout_li): Translate a few more result strings. + Fixes bug#970. + + * convert.c (hex2str): Fix optimization to append a nul character. + +2008-12-05 Werner Koch <wk@g10code.com> + + * percent.c, t-percent.c: New. + + * exechelp.c (gnupg_spawn_process, gnupg_spawn_process_fd) + (gnupg_spawn_process_detached) [W32]: Remove debug output. + +2008-11-20 Werner Koch <wk@g10code.com> + + * audit.c (writeout_li): Translate OKTEXT. + +2008-11-04 Werner Koch <wk@g10code.com> + + * i18n.c (i18n_init) [USE_SIMPLE_GETTEXT]: Adjust for changed + w32-gettext.c. + * homedir.c (gnupg_localedir): New. + +2008-10-20 Werner Koch <wk@g10code.com> + + * http.c (http_register_tls_callback) [!HTTP_USE_GNUTLS]: Mark + unused arg. + * localename.c (do_nl_locale_name): Ditto. + * audit.c (event2str): Silent gcc warning. + * sysutils.c (translate_sys2libc_fd): Mark unused arg. + (translate_sys2libc_fd_int): Ditto. + * iobuf.c (translate_file_handle): Ditto. + * asshelp.c (send_one_option): Ditto. + * exechelp.c (gnupg_spawn_process): Ditto. + * signal.c (got_usr_signal): Ditto + * estream.c (es_func_fd_create) [!W32]: Ditto. + (es_func_fp_create) [!W32]: Ditto. + (es_write_hexstring): Ditto. + (dummy_mutex_call_void, dummy_mutex_call_int) [HAVE_PTH]: New. + (ESTREAM_MUTEX_LOCK, ESTREAM_MUTEX_UNLOCK, ESTREAM_MUTEX_TRYLOCK) + (ESTREAM_MUTEX_INITIALIZE) [HAVE_PTH]: Use dummy calls so to mark + unused arg. + +2008-10-19 Werner Koch <wk@g10code.com> + + * estream-printf.c (estream_vsnprintf): Fix return value. + (check_snprintf): Add a new test. + (one_test) [W32]: Disable test. + +2008-10-17 Werner Koch <wk@g10code.com> + + * util.h (snprintf) [W32]: Redefine to estream_snprintf. + +2008-09-03 Werner Koch <wk@g10code.com> + + * convert.c (hex2str): New. + (hex2str_alloc): New. + * t-convert.c (test_hex2str): New. + +2008-08-19 Werner Koch <wk@g10code.com> + + * iobuf.c: Avoid passing a NULL (iobuf_t)->desc to the log + function. Should in general never be NULL, but well. Reported by + M. Heneka. + +2008-06-26 Werner Koch <wk@g10code.com> + + * estream.c (es_write_sanitized): Loose check for control + characters to better cope with utf-8. The range 0x80..0x9f is + nowadays not anymore accidently used for control charaters. + +2008-06-25 Marcus Brinkmann <marcus@g10code.de> + + Revert last three changes related to handle translation. + * sysutils.c: + (FD_TRANSLATE_MAX, fd_translate, fd_translate_len) + (translate_table_init, translate_table_lookup): Removed. + * iobuf.c (check_special_filename): Do not use + translate_table_lookup. + * sysutils.h (translate_table_init, translate_table_lookup): + Remove prototypes. + +2008-06-19 Werner Koch <wk@g10code.com> + + * sysutils.c: Remove <ctype.h>. + (fd_translate_max): Use macro for the size. + (translate_table_init): Protect read against EINTR and replace + isspace by spacep. + +2008-06-18 Marcus Brinkmann <marcus@g10code.de> + + * sysutils.c (TRANS_MAX): Bump up to 350 to be on the safe side. + + * sysutils.h (translate_table_init, translate_table_lookup): New + prototypes. + * sysutils.c: Include <ctype.h>. + (FD_TRANSLATE_MAX): New macro. + (fd_translate, fd_translate_len): New static variables. + (translate_table_init, translate_table_lookup): New functions. + (translate_sys2libc_fd_int): Translate file descriptor. + * iobuf.c (check_special_filename): Translate handle values from + special filenames. + +2008-06-16 Werner Koch <wk@g10code.com> + + * homedir.c (w32_commondir): New. + (gnupg_sysconfdir): Use it. + +2008-06-09 Werner Koch <wk@g10code.com> + + * b64dec.c: New. + +2008-06-05 Werner Koch <wk@g10code.com> + + * util.h (gnupg_copy_time): Replace strcpy by memcpy. + +2008-05-26 Werner Koch <wk@g10code.com> + + * asshelp.c (send_one_option, send_pinentry_environment): use + xfree and xtrystrdup. + + * i18n.c (i18n_switchto_utf8) [USE_SIMPLE_GETTEXT]: Return NULL. + + * homedir.c (gnupg_module_name): Add + GNUPG_MODULE_NAME_CONNECT_AGENT and GNUPG_MODULE_NAME_GPGCONF. + +2008-04-21 Werner Koch <wk@g10code.com> + + * http.c (http_wait_response) [W32]: Use DuplicateHandle because + it is a socket. + (cookie_read) [W32]: Use recv in place of read. + +2008-04-08 Werner Koch <wk@g10code.com> + + * i18n.c (i18n_switchto_utf8, i18n_switchback) + [USE_SIMPLE_GETTEXT]: Implement. + +2008-04-07 Werner Koch <wk@g10code.com> + + * b64enc.c (b64enc_start): Detect PGP mode. + (b64enc_finish): Write PGP CRC. + * util.h (struct b64state): Add field CRC. + * t-b64.c: New. + + * pka.c (get_pka_info): Use xtrymalloc and check result. + +2008-03-25 Werner Koch <wk@g10code.com> + + * localename.c: Strip all W32 code. Include w32help.h. + (gnupg_messages_locale_name) [W32]: Use the gettext_localename. + +2008-03-17 Werner Koch <wk@g10code.com> + + * iobuf.c (IOBUF_BUFFER_SIZE): Actually use this macro. + + * simple-pwquery.c (agent_send_all_options): Fix last change. + +2008-03-06 Werner Koch <wk@g10code.com> + + * simple-pwquery.c (agent_send_all_options): Add support for + XAUTHORITY and PINENTRY_USER_DATA. + +2008-02-15 Marcus Brinkmann <marcus@g10code.de> + + * exechelp.c (gnupg_spawn_process_fd): Add flag DETACHED_PROCESS + unconditionally (required for all callers at the moment). + +2008-02-14 Werner Koch <wk@g10code.com> + + * sysutils.c (gnupg_allow_set_foregound_window): New. + (WINVER) [W32]: Define. + +2008-01-31 Werner Koch <wk@g10code.com> + + * audit.c (audit_print_result): Make sure that the output is + always UTF8. + +2008-01-27 Werner Koch <wk@g10code.com> + + * exechelp.c (gnupg_spawn_process): Add arg FLAGS and changed all + callers to pass 0 for it. + +2007-12-13 Werner Koch <wk@g10code.com> + + * sexputil.c (hash_algo_from_sigval): New. + * t-sexputil.c: New. + * Makefile.am (module_tests): Add it. + +2007-12-11 Werner Koch <wk@g10code.com> + + * asshelp.c (send_pinentry_environment): Allow using of old + gpg-agents not capabale of the xauthority and pinentry_user_data + options. + +2007-12-04 Werner Koch <wk@g10code.com> + + * Makefile.am (t_helpfile_LDADD, module_maint_tests): New. + * t-helpfile.c: New. + * helpfile.c: New. + * membuf.h (is_membuf_ready, MEMBUF_ZERO): New. + * localename.c: New. Taken from gettext with modifications as done + for GpgOL. Export one new function. + * util.h (gnupg_messages_locale_name, gnupg_get_help_string): Added. + + * sysutils.c (gnupg_reopen_std): New. Taken from ../g10/gpg.c. + +2007-11-27 Werner Koch <wk@g10code.com> + + * Makefile.am (CLEANFILES): New. + + * homedir.c (dirmngr_socket_name): Use CSIDL_WINDOWS. + +2007-11-15 Werner Koch <wk@g10code.com> + + * asshelp.c (send_pinentry_environment): Add args XAUTHORITY and + PINENTRY_USER_DATA. + (start_new_gpg_agent): Ditto. + +2007-11-07 Werner Koch <wk@g10code.com> + + * status.h: New. + * errors.h: Remove. + +2007-11-05 Werner Koch <wk@g10code.com> + + * audit.c, audit.h: New. + * Makefile.am: Add rules to build audit-events.h. + * exaudit.awk: New. + * mkstrtable.awk: New. Taken from libgpg-error. + +2007-10-19 Werner Koch <wk@g10code.com> + + * i18n.c (i18n_switchto_utf8, i18n_switchback): New. + +2007-10-01 Werner Koch <wk@g10code.com> + + * sysutils.h (FD2INT, INT2FD): New. + +2007-09-21 Werner Koch <wk@g10code.com> + + * homedir.c (default_homedir): Make registry work. Reported by + Marc Mutz. + +2007-08-29 Werner Koch <wk@g10code.com> + + * exechelp.c (gnupg_wait_process): Add arg EXITCODE. Changed all + callers. + (gnupg_create_inbound_pipe): New. + * util.h (GNUPG_MODULE_NAME_GPGSM, GNUPG_MODULE_NAME_GPG): New. + * homedir.c (gnupg_module_name): Add them + +2007-08-28 Werner Koch <wk@g10code.com> + + * gettime.c (check_isotime, add_isotime): New. Originally written + for DirMngr by me. + (add_days_to_isotime): New. + (date2jd, jd2date, days_per_month, days_per_year): New. Taken from + my ancient (1988) code used in Wedit (time2.c). + +2007-08-27 Werner Koch <wk@g10code.com> + + * util.h (GNUPG_MODULE_NAME_CHECK_PATTERN): New. + * homedir.c (gnupg_module_name): Add it. + * exechelp.c (w32_fd_or_null) [W32]: New. + (gnupg_spawn_process_fd): New. + (gnupg_wait_process) [W32]: Close the handle after if the process has + returned. + +2007-08-22 Werner Koch <wk@g10code.com> + + Updated estream from libestream. + + * estream.c (mem_malloc, mem_realloc, mem_free): New. Use them + instead of the ES_MEM_foo. + * estream.c (estream_cookie_mem): Remove members DONT_FREE, + APPEND_ZERO, PTR and SIZE. Add MEMORY_LIMIT. Put GROW into a new + FLAGS struct. + (es_func_mem_create): Remove APPEND_ZERO, DONT_FREE, PTR and + SIZE. Add MEMORY_LIMIT. + (es_func_mem_write, es_func_mem_seek, es_func_mem_destroy): Revamp. + (es_open_memstream): Change API to just take a memory limit and a + mode argument. Rename to .. + (es_fopenmem): .. this. + (HAVE_W32_SYSTEM) [_WIN32]: Define if not defined. + (tmpfd) [W32]: Implement directly using the W32 API. + (es_fgets): Rewrite without using doreadline. + +2007-08-21 Werner Koch <wk@g10code.com> + + * sysutils.c (gnupg_tmpfile): New. + * t-sysutils.c: New. + * Makefile.am (module_tests): Add t-sysutils. + +2007-08-20 Werner Koch <wk@g10code.com> + + * exechelp.c [W32]: Redefine X_OK to F_OK. + +2007-08-16 Werner Koch <wk@g10code.com> + + * Makefile.am (t_convert_DEPENDENCIES): Remove + ($(PROGRAMS)): Remove. + (t_common_ldadd): Use libcommon.a and not the macro. + +2007-08-14 Werner Koch <wk@g10code.com> + + * homedir.c (dirmngr_socket_name): New. + +2007-08-07 Werner Koch <wk@g10code.com> + + * tlv.c, tlv.h: Move from ../scd/. + * tlv.c (parse_sexp, parse_ber_header): Add ERRSOURCE arg and prefix + name with a _. + * tlv.h: Use macro to convey ERRSOURCE. + +2007-08-02 Werner Koch <wk@g10code.com> + + * gc-opt-flags.h: New. + +2007-08-01 Werner Koch <wk@g10code.com> + + * estream-printf.c (read_dummy_value): Removed as it is useless now. + (read_values): Remove check on !vaargs which is not anymore needed + and anyway not portable. Reported by Peter O'Gorman. + +2007-07-16 Werner Koch <wk@g10code.com> + + * estream.c (es_func_file_create): Clear NO_CLOSE flag. + +2007-07-12 Werner Koch <wk@g10code.com> + + * sysutils.h (gnupg_fd_t): New. + * sysutils.c (translate_sys2libc_fd): Use that type instead of int. + (translate_sys2libc_fd_int): New. + +2007-07-09 Werner Koch <wk@g10code.com> + + * t-gettime.c (test_isotime2epoch): Use time_t and not u32. + +2007-07-05 Werner Koch <wk@g10code.com> + + * t-gettime.c: New. + * gettime.c (isotime2epoch, epoch2isotime): New. + +2007-07-04 Werner Koch <wk@g10code.com> + + * estream.c (es_init_do): Do not throw an error if pth has already + been initialized. + +2007-06-26 Werner Koch <wk@g10code.com> + + * Makefile.am ($(PROGRAMS)): New. + + * util.h (init_common_subsystems): Moved to .. + * init.h: .. New. + * util.h: Include init.h. + + * homedir.c (standard_homedir): New. + (default_homedir) [W32]: Reimplemented in terms of + standard_homedir. Fixed memory leak. + +2007-06-25 Werner Koch <wk@g10code.com> + + * iobuf.c: Add more documentation and slighly restructured macro + defintion for better readability. + (FILEP_OR_FD): Rename to fp_or_fd_t. + (CLOSE_CACHE): Rename to close_cache_t. + + * sysutils.c (translate_sys2libc_fd): New using the code from iobuf.c. + * iobuf.c: Include sysutils.h. + (iobuf_translate_file_handle): Remove. + (translate_file_handle): Use new function. + + * estream-printf.c [TEST]: Header including fixes. + (do_format): Do not append a trailing Nul. This avoids spurious + Nuls in the es_printf output. + (estream_vsnprintf, estream_vasprintf): Take this in account. + + * estream.h (struct es__stream): Change FLAGS to a bit structure. + (ES__FLAG_WRITING): Replace by a bit from FLAGS. * estream.c + (struct estream_internal): Rename FLAGS to MODEFLAGS so that they + are not confused with the estream flags. + (es_initialize, es_create): Add arg MODEFLAGS so that we can setup + the intial writemode. Changed all callers to pass them. + (es_convert_mode): Set O_BINARY. + (es_func_fd_create, es_func_fp_create, es_func_file_create) [W32]: + Call setmode if requested. + +2007-06-24 Werner Koch <wk@g10code.com> + + * estream.c (do_fpopen, es_fpopen, es_fpopen_nc): New. + (es_func_fp_create, es_func_fp_read, es_func_fp_write) + (es_func_fp_seek, es_func_fp_destroy): New. + +2007-06-22 Werner Koch <wk@g10code.com> + + * estream.c (es_fdopen): Factored code out to.. + (do_fdopen): .. new. + (es_fdopen_nc): New. + (estream_cookie_fd): Add field NO_CLOSE. + (es_func_fd_create): Add arg NO_CLOSE and changed all callers. + (es_func_fd_destroy): Handle the new flag. + + * homedir.c (gnupg_libexecdir) [W32]: Factor code out to .. + (w32_rootdir): .. new. + (gnupg_sysconfdir, gnupg_libdir, gnupg_datadir) [W32]: Return + name based on w32_rootdir(). + +2007-06-21 Werner Koch <wk@g10code.com> + + * membuf.h (get_membuf_len): New. + + * membuf.c (init_membuf_secure): Really allocate in secure memory. + (put_membuf_str): New. + + * ttyio.c (tty_getf): New. + + * util.h (ctrl_t): Declare it here. + + * asshelp.c (start_new_gpg_agent): New. Based on code from + ../sm/call-agent.c + +2007-06-20 Werner Koch <wk@g10code.com> + + * sysutils.c (gnupg_sleep): New. + * sysutils.h [W32]: Remove _sleep wrapper. Changed all callers to + use gnupg_sleep. + + * exechelp.c (build_w32_commandline_copy): New. + (build_w32_commandline): Factored some code out to new function + and correctly process a PGMNAME with spaces. + (gnupg_spawn_process_detached) [W32]: Implement. + +2007-06-14 Werner Koch <wk@g10code.com> + + * simple-pwquery.h (MAP_SPWQ_ERROR_IMPL): New. + (SPWQ_NO_PIN_ENTRY): New. + * simple-pwquery.c (simple_pw_set_socket): New. + (agent_open): Use it if GPG_AGENT_INFO is not set. + (simple_pwquery): Extended to allow returning of other error codes. + + * util.h (GNUPG_MODULE_NAME_AGENT, GNUPG_MODULE_NAME_PINENTRY) + (GNUPG_MODULE_NAME_SCDAEMON, GNUPG_MODULE_NAME_DIRMNGR) + (GNUPG_MODULE_NAME_PROTECT_TOOL): New. + * homedir.c (gnupg_module_name): New. + (gnupg_bindir): New. + +2007-06-12 Werner Koch <wk@g10code.com> + + * homedir.c (gnupg_sysconfdir): New. + (gnupg_libexecdir): New. Taken from g10/misc.c:get_libexecdir. + (gnupg_datadir): New. + (gnupg_libdir): New. + + * http.c (connect_server) [W32]: Do not call init_sockets if + HTTP_NO_WSASTARTUP is defined. + + * init.c: New. + + * estream.c (es_init_do): Init stream lock here because we can't + use a static initialization with W32pth. + +2007-06-11 Werner Koch <wk@g10code.com> + + * Makefile.am (t_common_ldadd): Use libcommonstd macro. + +2007-06-06 Werner Koch <wk@g10code.com> + + * Makefile.am: Include am/cmacros.am. + + * sysutils.h [W32]: Remove prototypes for the registry access. + * w32reg.c: Move to ../jnlib/w32-reg.c. + + * i18n.c (i18n_init): New. + + * simple-gettext.c: Remove. + + * iobuf.c (iobuf_get_filelength): Rename SIZE to EXSIZE to silent + shadowing warning. + +2007-06-04 Werner Koch <wk@g10code.com> + + * http.c [W32]: Include unistd.h also in this case. + (write_server) [W32]: Fixed error code. + (init_sockets): Fixed syntax error. + (cookie_close): Replace close by sock_close macro. + + * estream.c [w32]: Do not init Mutex. + + * Makefile.am (common_sources) [USE_SNS_SRV]: Build srv.c only + when needed. + + * ttyio.c (init_ttyfp) [W32]: Do not use TTYFP. + + * util.h: Include ../jnlib/dynload.h. + + * dynload.h: Move to ../jnlib. + +2007-05-30 Werner Koch <wk@g10code.com> + + * estream.c (MEM_FREE, MEM_ALLOC, MEM_REALLOC): Prefix with ES_ as + windows.h also has such definitions, + +2007-05-15 Werner Koch <wk@g10code.com> + + * util.h: Do not include gnulib's vasprintf. Redefine asprintf + and vasprintf. + + * xasprintf.c (xasprintf, xtryasprintf): Use estream_vasprintf. + + * estream-printf.h, estream-printf.c: New. Taken from current + libestream SVN. + * Makefile.am (common_sources): Add them. + +2007-05-14 Werner Koch <wk@g10code.com> + + * sexp-parse.h (smklen): New. + * sexputil.c: Include sexp-parse.h. + (make_simple_sexp_from_hexstr): Replace sprintf by smklen. + +2007-05-07 Werner Koch <wk@g10code.com> + + * signal.c (got_fatal_signal): Protect SIG from being clobbered by + a faulty signal implementaion. Suggested by James Juran. + +2007-04-25 Werner Koch <wk@g10code.com> + + * i18n.h (ngettext): New. + * simple-gettext.c (ngettext): New. + +2007-04-20 Werner Koch <wk@g10code.com> + + * miscellaneous.c (my_gcry_logger, my_gcry_outofcore_handler): + Moved from gpg-agent to here. + (my_gcry_fatalerror_handler): new. + (setup_libgcrypt_logging): New. + +2007-03-19 Werner Koch <wk@g10code.com> + + * miscellaneous.c (print_hexstring): New. + * estream.c (es_fprintf_unlocked): New. + (es_write_sanitized): New. + (es_write_hexstring): New. + (es_write_sanitized_utf8_buffer) [GNUPG_MAJOR_VERSION]: New. + +2007-03-09 David Shaw <dshaw@jabberwocky.com> + + From STABLE-BRANCH-1-4 + + * http.c (do_parse_uri): Remove the hkp port 11371 detection. We + implement hkp in the keyserver handler, and the support here makes + it appear like a bad hkp request actually succeeded. + +2007-01-31 Werner Koch <wk@g10code.com> + + * Makefile.am (t_common_ldadd): Add LIBINCONV and LIBINTL. + +2007-01-25 Werner Koch <wk@g10code.com> + + * simple-pwquery.c (simple_pwquery): New arg OPT_CHECK. + +2006-12-13 David Shaw <dshaw@jabberwocky.com> + + * Makefile.am (AM_CPPFLAGS): Include intl/ so we can reference the + built-in headers. + +2006-11-23 Werner Koch <wk@g10code.com> + + * http.c: Include i18n.h + +2006-11-21 Werner Koch <wk@g10code.com> + + * estream.c: Remove explicit Pth soft mapping diabling becuase it + is now done in config.h. + +2006-11-15 Werner Koch <wk@g10code.com> + + * estream.c: Disabled Pth soft mapping. + (my_funopen_hook_ret_t): New. + (print_fun_writer): Use it here. + + * iobuf.c (fd_cache_close): Use %d instead of %p for debug output. + +2006-11-03 Werner Koch <wk@g10code.com> + + * Makefile.am (t_convert_DEPENDENCIES): Add libcommon. From + Gentoo. + +2006-10-24 Marcus Brinkmann <marcus@g10code.de> + + * Makefile.am (libcommon_a_CFLAGS): Add $(LIBASSUAN_CFLAGS). + (libsimple_pwquery_a_CFLAGS): New variable. + +2006-10-20 Werner Koch <wk@g10code.com> + + * convert.c (hex2bin): New. + +2006-10-17 Werner Koch <wk@g10code.com> + + * estream.c (struct estream_internal, es_initialize) + (es_deinitialize, print_fun_writer, es_print): New and modified + functions to avoid tempfiles for printf style printing. + + * Makefile.am (libcommonpth_a_SOURCES): New. We now build a secon + version of the library with explicit Pth support. + * exechelp.c, estream.c: Make use of WITHOUT_GNU_PTH. + +2006-10-08 Werner Koch <wk@g10code.com> + + * gpgrlhelp.c: Trun all functions into dummies if readline is not + available. + +2006-10-06 Werner Koch <wk@g10code.com> + + * Makefile.am (AM_CFLAGS): Use PTH version of libassuan. + + * util.h (GNUPG_GCC_A_SENTINEL): Defined for gcc >= 4. + +2006-10-04 David Shaw <dshaw@jabberwocky.com> + + * gpgrlhelp.c: readline requires stdio.h. + +2006-10-04 Werner Koch <wk@g10code.com> + + * membuf.c (init_membuf_secure): New. + (put_membuf): Make sure that ERRNO is set even if the underlying + malloc code does not work properly. + (get_membuf): Set ERRNO on error. + (get_membuf): Allow to pass LEN as NULL. + +2006-10-02 Werner Koch <wk@g10code.com> + + * iobuf.c (iobuf_unread): Removed. This code is not required. + Also removed the entire unget buffer stuff. + +2006-09-27 Werner Koch <wk@g10code.com> + + * util.h: Do not include strsep.h and strpbrk.h. + (isascii): Removed as it is now in jnlib. + + * iobuf.c (pop_filter, underflow, iobuf_close): Free the unget + buffer. + +2006-09-27 Florian Weimer <fweimer@bfk.de> (wk) + + * iobuf.c (iobuf_unread): New. + +2006-09-22 Werner Koch <wk@g10code.com> + + * i18n.h: Changed license to an all permissive one. + + * ttyio.c (tty_get): We need to use readline too. Added two more + hooks. + +2006-09-21 Werner Koch <wk@g10code.com> + + * ttyio.c (tty_private_set_rl_hooks): New. + (tty_enable_completion, tty_disable_completion): Use a hook to + enable readline support. Now always available. + (tty_cleanup_rl_after_signal): New. + + * ttyio.h: Removed readline specific stuff. Included util.h. + * common-defs.h: New. + +2006-09-15 Werner Koch <wk@g10code.com> + + * convert.c: New. + (hexcolon2bin): New. + (bin2hex, bin2hexcolon, do_binhex): New. + * t-convert.c: New + +2006-09-14 Werner Koch <wk@g10code.com> + + * util.h (out_of_core): Use new gpg_error_from_syserror function. + + * http.c (init_sockets): Changed it to require 2.2 unless it is + build within gnupg 1 where we require 1.1 (and not anymore allow + for 1.0). + +2006-09-07 Werner Koch <wk@g10code.com> + + * exechelp.c (gnupg_spawn_process): Factor out post fork code to .. + (do_exec): .. new function. Allow passing of -1 for the fds. + (gnupg_spawn_process): Terminate gcrypt's secure memory in the child. + (gnupg_spawn_process_detached): New. + +2006-09-06 Werner Koch <wk@g10code.com> + + * maperror.c: Removed. + + * util.h (out_of_core): New. + +2006-09-04 Werner Koch <wk@g10code.com> + + * http.c (http_get_header): New. + (capitalize_header_name, store_header): New. + (parse_response): Store headers away. + (send_request): Return GPG_ERR_NOT_FOUND if connect_server failed. + * http.h: New flag HTTP_FLAG_NEED_HEADER. + +2006-08-21 Werner Koch <wk@g10code.com> + + * Makefile.am (libcommon_a_SOURCES): Added keyserver.h + + * openpgpdefs.h: New. Stripped from ..g10/packet.h. + +2006-08-16 Werner Koch <wk@g10code.com> + + * keyserver.h: Moved from ../include to here. + + * http.c: Include srv.h. + + * srv.c, srv.h: New. Taken from GnuPG 1.4 + +2006-08-14 Werner Koch <wk@g10code.com> + + * http.h (struct http_context_s): Moved to implementation. + * http.c (http_open): Changed call to return a context. + (http_open_document): Ditto. + (http_get_read_ptr, http_get_read_ptr, http_get_status_code): New. + (do_parse_uri): Replaced strlwr by straight code to ease + standalone use of this file. + (http_wait_response): Removed arg STATUS_CODE as it is available + through an accessor function. Adjusted caller. + (http_escape_string): New. + + * estream.c (es_read_line): Renamed to .. + (doreadline): .. this. Changed all callers. + (es_read_line): New. This is theusual limited getline variabnt as + used at several places. Here taken and adjusted from xreadline.c + (es_free): New. + +2006-08-11 Werner Koch <wk@g10code.com> + + * http.c: Major internal changes to optionallly support GNUTLS and + ESTREAM. + (http_open): Move initialization of the stream ... + (send_request): .. here. + (http_register_tls_callback): New. + + * estream.c (es_writen): Try to seek only is a seek function has + been registered. + +2006-08-09 Werner Koch <wk@g10code.com> + + * http.c, http.h: New. Taken from gnupg 1.4.5, merged with + changes done for the Dirmngr project (by g10 Code) and cleaned up + some stuff. + (make_header_line): New. Change all caller to make user of the new + * Makefile.am (libcommon_a_SOURCES): Added http.c and http.h. + +2006-05-23 Werner Koch <wk@g10code.com> + + * gettime.c (isotimestamp): New. + + * ttyio.c (tty_get_ttyname): Posixly correct usage of ctermid. + + * dns-cert.c: New. Taken from 1.4.3's util/cert.c. + * dns-cert.h: New. + +2006-05-22 Werner Koch <wk@g10code.com> + + * pka.c: New. Taked from 1.4.3. + * pka.h: New. + * Makefile.am: Added pka. + +2006-05-19 Werner Koch <wk@g10code.com> + + * yesno.c (answer_is_yes_no_default, answer_is_yes_no_quit): + Updated from 1.4.3. + (answer_is_okay_cancel): new. From 1.4.3. + + * miscellaneous.c (match_multistr): New. Taken from 1.4.3. + + * ttyio.c (tty_enable_completion, tty_disable_completion): New + dummy functions. + * ttyio.h: Add prototypes and stubs. + +2006-04-19 Werner Koch <wk@g10code.com> + + * iobuf.c (iobuf_get_fd): New. Taken from 1.4.3. + (iobuf_is_pipe_filename): New. + (pop_filter): Made static. + (iobuf_skip_rest): New. Orginal patch by Florian + Weimer. Added new argument PARTIAL. + (block_filter): Remove the old gpg indeterminate length mode. + (block_filter): Properly handle a partial body stream + that ends with a 5-byte length that happens to be zero. + (iobuf_set_block_mode, iobuf_in_block_mode): Removed as + superfluous. + (iobuf_get_filelength): New arg OVERFLOW. + (iobuf_get_filelength) [W32]: Use GetFileSizeEx if available + * miscellaneous.c (is_file_compressed): Take care of OVERFLOW. + +2006-04-18 Werner Koch <wk@g10code.com> + + * homedir.c (w32_shgetfolderpath): New. Taken from gpg 1.4.3. + (default_homedir): Use it. + +2005-10-08 Marcus Brinkmann <marcus@g10code.de> + + * signal.c (get_signal_name): Check value of HAVE_DECL_SYS_SIGLIST + instead of just if it is defined. + +2005-09-28 Marcus Brinkmann <marcus@g10code.de> + + * Makefile.am (AM_CFLAGS): Add $(LIBASSUAN_CFLAGS). + +2005-07-04 Marcus Brinkmann <marcus@g10code.de> + + * simple-pwquery.h (simple_pwclear): New prototype. + * simple-pwquery.c (simple_pwclear): New function. + +2005-06-15 Werner Koch <wk@g10code.com> + + * miscellaneous.c (make_printable_string): Made P a void*. + + * sexputil.c (keygrip_from_canon_sexp, cmp_simple_canon_sexp): + Fixed signed/unsigned pointer mismatch. + (make_simple_sexp_from_hexstr): Ditto. This is all too ugly; I + wonder why gcc-4's default is to warn about them and forcing us to + use cast the warning away. + * iobuf.c (block_filter): Ditto. + (iobuf_flush): Ditto. + (iobuf_read_line): Ditto. + (iobuf_read): Make BUFFER a void *. + (iobuf_write): Make BUFFER a const void *. + * ttyio.c (tty_print_utf8_string2): Ditto. + * estream.c (estream_cookie_mem): Make MEMORY unsigned char*. + (es_write): Make BUFFER a void *. + (es_writen): Ditto. + (es_func_fd_read, es_func_fd_write, es_func_mem_read) + (es_func_mem_write): Ditto. + (es_read, es_readn): Ditto. + (es_func_mem_write): Made MEMORY_NEW an unsigned char *. + * estream.h (es_cookie_read_function_t) + (es_cookie_write_function_t): Changed buffer arg to void*. + +2005-06-03 Werner Koch <wk@g10code.com> + + * estream.c: Use HAVE_CONFIG_H and not USE_CONFIG_H! + (es_func_fd_read, es_func_fd_write): Protect against EINTR. + +2005-06-01 Werner Koch <wk@g10code.com> + + * Makefile.am (AM_CPPFLAGS): Added. + + * util.h: Add some includes for gnulib. + (ttyname, isascii): Define them inline. + * fseeko.c, ftello.c: Removed. + * strsep.c, mkdtemp.c: Removed. + * ttyname.c, isascii.c: Removed. + +2005-05-31 Werner Koch <wk@g10code.com> + + * dynload.h: s/__inline__/inline/. + +2005-05-13 Werner Koch <wk@g10code.com> + + * signal.c (got_fatal_signal): Print the signal number if we can't + get a name for it. + (get_signal_name): Return NULL if no name is available. Fixed + conditional for sys_siglist to the correct one. + +2005-04-17 Werner Koch <wk@g10code.com> + + * sexputil.c (cmp_simple_canon_sexp): New. + (make_simple_sexp_from_hexstr): New. + +2005-04-07 Werner Koch <wk@g10code.com> + + * sexputil.c: New. + +2005-04-11 Marcus Brinkmann <marcus@g10code.de> + + * simple-pwquery.c (simple_pwquery): Use spwq_secure_free. + +2005-03-03 Werner Koch <wk@g10code.com> + + * Makefile.am (AM_CFLAGS): Added PTH_CFLAGS. Noted by Kazu Yamamoto. + +2005-02-25 Werner Koch <wk@g10code.com> + + * xasprintf.c (xtryasprintf): New. + +2005-01-26 Moritz Schulte <moritz@g10code.com> + + * Makefile.am (libcommon_a_SOURCES): New source files: estream.c, + estream.h. + * estream.c, estream.h: New files. + +2005-01-03 Werner Koch <wk@g10code.com> + + * asshelp.c (send_pinentry_environment): Fixed changed from + 2004-12-18; cut+paste error for lc-messages. + +2004-12-21 Werner Koch <wk@g10code.com> + + * simple-pwquery.c (agent_open) [W32]: Implement for W32. + (readline) [W32]: Use recv instead of read. + (writen) [W32]: Use send instead of write. + (my_stpcpy): Define a stpcpy replacement so that this file + continues to be self-contained. + (agent_send_all_options) [W32]: Don't call ttyname. + +2004-12-21 Marcus Brinkmann <marcus@g10code.de> + + * simple-pwquery.h (simple_query): Add prototype. + * simple-pwquery.c (simple_query): New function. + +2004-12-21 Werner Koch <wk@g10code.com> + + * signal.c (got_fatal_signal, got_usr_signal) + (got_fatal_signal) [DOSISH]: Don't build. + * simple-gettext.c: Include sysutils.h + + * homedir.c: New. Use CSIDL_APPDATA for W32 as the default home + directory. + * Makefile.am (libcommon_a_SOURCES): Add it. + (EXTRA_DIST): Removed mkerror and mkerrtok. + +2004-12-20 Werner Koch <wk@g10code.com> + + * sysutils.h [W32]: Define sleep. + * util.h: Add prototype for mkdtemp. + + * membuf.c (put_membuf): Wipe out buffer after a failed realloc. + +2004-12-19 Werner Koch <wk@g10code.com> + + * maperror.c (map_assuan_err_with_source): Oops, args were swapped. + +2004-12-18 Werner Koch <wk@g10code.com> + + * maperror.c (map_assuan_err): Renamed to .. + (map_assuan_err_with_source): .. this and add arg SOURCE.c + * asshelp.c (send_pinentry_environment, send_one_option): Add arg + ERRSOURCE. + +2004-12-15 Werner Koch <wk@g10code.com> + + * sysutils.h [W32]: Prototypes for registry functions. + * w32reg.c: Include sysutils.h + + * simple-pwquery.c [W32]: Dummy code to allow a build. + + * exechelp.c [W32]: Implemented for W32 . + + * ttyname.c: New. + + * asshelp.c (send_one_option): New. + (send_pinentry_environment): Cleaned up and made sure that empty + values are not send. + +2004-12-07 Werner Koch <wk@g10code.com> + + * asshelp.c (send_pinentry_environment) [W32]: Do not use ttyname. + +2004-12-06 Werner Koch <wk@g10code.com> + + * exechelp.h, exechelp.c: New. Based on code from ../sm/import.c. + +2004-12-03 Werner Koch <wk@g10code.com> + + * strsep.c: Fixed copyright comments. + +2004-11-26 Werner Koch <wk@g10code.com> + + * simple-gettext.c: New taken from gnupg 1.3.x + + * simple-pwquery.c [_WIN32]: Include winsock2.h. + (agent_open): Disable it until we have our AF_UNIX implementation + ready. + * fseeko.c, ftello.c: Include sys/types for the sake of W32. + +2004-11-23 Werner Koch <wk@g10code.com> + + * b64enc.c: Include stdio.h and string.h + +2004-08-18 Werner Koch <wk@g10code.de> + + * simple-pwquery.c (simple_pwquery): Handle gpg-error style return + code for canceled. + +2004-07-20 Werner Koch <wk@g10code.de> + + * maperror.c: Removed header ksba.h. Not required anymore. + +2004-06-14 Werner Koch <wk@gnupg.org> + + * xreadline.c: New. Based on the iobuf_read_line function. + +2004-05-12 Werner Koch <wk@gnupg.org> + + * util.h (xtrycalloc_secure,xtrymalloc_secure): New. + +2004-05-11 Werner Koch <wk@gnupg.org> + + * sysutils.c (disable_core_dumps): Only set the current limit. + (enable_core_dumps): New. + +2004-04-13 Werner Koch <wk@gnupg.org> + + * simple-pwquery.c (copy_and_escape): Relaxed quoting. + +2004-04-05 Werner Koch <wk@gnupg.org> + + * errors.h (STATUS_NEWSIG): New. + +2004-03-11 Werner Koch <wk@gnupg.org> + + * dynload.h [__MINGW32__]: Define RTLD_LAZY. + +2004-03-09 Werner Koch <wk@gnupg.org> + + * maperror.c (map_assuan_err): Map the Locale_Problem item. + +2004-03-03 Werner Koch <wk@gnupg.org> + + * asshelp.c, asshelp.h: New. + (send_pinentry_environment): New. Code taken from ../sm/call-agent.c. + +2004-02-19 Werner Koch <wk@gnupg.org> + + * simple-pwquery.c (agent_open): Don't mangle INFOSTR. + +2004-02-17 Werner Koch <wk@gnupg.org> + + * simple-pwquery.c (agent_open): Ignore an empty GPG_AGENT_INFO. + + * errors.h: Added STATUS_IMPORT_OK. + +2004-02-10 Werner Koch <wk@gnupg.org> + + * b64enc.c: New. Based on code from ../sm/base64.c. + +2004-01-30 Marcus Brinkmann <marcus@g10code.de> + + * Makefile.am (libcommon_a_SOURCES): Add xasprintf.c. + * miscellaneous.c (xasprintf): Moved to ... + * xasprintf (xasprintf): ... here. New file. + This allows to use xasprintf without sucking in gpg-error. + +2004-01-27 Werner Koch <wk@gnupg.org> + + * sexp-parse.h: New; moved from../agent. + + * util.h (xtoi_4): New. + +2003-12-23 Werner Koch <wk@gnupg.org> + + * maperror.c (map_assuan_err): Prepared for a new error code. + +2003-12-17 Werner Koch <wk@gnupg.org> + + * gettime.c (asctimestamp): Add a note on a non-avoidable gcc warning. + + * util.h [!HAVE_VASPRINTF]: Add printf format attribute to the + replacement function. + + * miscellaneous.c (xasprintf): New. + +2003-11-14 Werner Koch <wk@gnupg.org> + + * mkdtemp.c (mkdtemp): Use gcry_create_nonce. + + * cryptmiss.c: Removed. + +2003-11-13 Werner Koch <wk@gnupg.org> + + * util.h (vasprintf): Also fixed the prototype. + + * vasprintf.c (vasprintf): ARGS should not be a pointer. Fixed + segv on Solaris. Reported by Andrew J. Schorr. + +2003-11-12 Werner Koch <wk@gnupg.org> + + * maperror.c (map_ksba_err, map_gcry_err, map_kbx_err): Removed. + +2003-10-31 Werner Koch <wk@gnupg.org> + + * util.h (gnupg_isotime_t): New. + (gnupg_copy_time): New. + + * gettime.c (gnupg_get_isotime): New. + +2003-09-23 Werner Koch <wk@gnupg.org> + + * iobuf.c (check_special_filename): Replaced is isdigit by digitp + to avoid passing negative values and potential locale problems. + Problem noted by Christian Biere. + + * util.h (ascii_isspace): New. + +2003-09-18 Werner Koch <wk@gnupg.org> + + * ttyio.c (tty_fprintf): New. + (tty_print_string, tty_print_utf8_string2) + (tty_print_utf8_string): Made P argument const byte*. + +2003-08-20 Marcus Brinkmann <marcus@g10code.de> + + * maperror.c (map_ksba_err): Map -1. Use gpg_err_make to set + the error source. + +2003-08-14 Timo Schulz <twoaday@freakmail.de> + + * dynload.h. New. W32 wrapper around the dynload mechanism. + +2003-07-15 Werner Koch <wk@gnupg.org> + + * simple-pwquery.c, simple-pwquery.h: New; moved from ../agent. + * Makefile.am (libsimple_pwquery_a_LIBADD): New. + +2003-06-25 Werner Koch <wk@gnupg.org> + + * maperror.c (map_to_assuan_status): Directly map 0 to 0. + +2003-06-17 Werner Koch <wk@gnupg.org> + + * gettime.c (scan_isodatestr,add_days_to_timestamp,strtimevalue) + (strtimestamp,asctimestamp): New. Code taken from gnupg 1.3.2 + mischelp.c. + + * yesno.c: New. Code taken from gnupg 1.3.2 mischelp.c + + * miscellaneous.c: New. + + * util.h: Include utf8conf.h + +2003-06-16 Werner Koch <wk@gnupg.org> + + * gettime.c (make_timestamp): New. + + * ttyio.c: New. Taken from gnupg 1.2. + * ttyio.h: Move from ../include. + +2003-06-13 Werner Koch <wk@gnupg.org> + + * util.h (seterr): Removed macro. + (xmalloc_secure,xcalloc_secure): New. + +2003-06-11 Werner Koch <wk@gnupg.org> + + * iobuf.c (iobuf_writebyte,iobuf_write): Return error code from + iobuf_flush. + (iobuf_writestr): Ditto. + +2003-06-10 Werner Koch <wk@gnupg.org> + + * iobuf.c, iobuf.h: New. Taken from current gnupg 1.3 CVS. Run + indent on it and adjusted error handling to libgpg-error style. + Replaced IOBUF by iobuf_t. Renamed malloc functions. + +2003-06-04 Werner Koch <wk@gnupg.org> + + * errors.h: Removed all error codes. We keep the status codes for + now. + * Makefile.am: Do not create errors.c anymore; remove it from the + sources. + + * maperror.c: Don't include error.h. Change all error codes to + libgpg-error style. + (map_assuan_err): Changed to new Assuan error code convention. + (map_to_assuan_status): Likewise. + (map_gcry_err,map_kbx_err): Not needed. For now dummy functions. + + * membuf.c, membuf.h: New. Code taken from ../sm/call-agent.h. + * Makefile.am: Added above. + +2003-04-29 Werner Koch <wk@gnupg.org> + + * util.h (fopencokokie): Removed prototype and struct. + + * fopencookie.c: Removed. + + * maperror.c: Use system assuan.h + +2002-10-31 Neal H. Walfield <neal@g10code.de> + + * isascii.c: New file. + * putc_unlocked.c: Likewise. + +2002-10-28 Neal H. Walfield <neal@g10code.de> + + * signal.c (caught_fatal_sig): Remove superfluous zero + initializer. + (caught_sigusr1): Likewise. + +2002-09-04 Neal H. Walfield <neal@g10code.de> + + * vasprintf.c (vasprintf) [va_copy]: Use va_copy. + [!va_copy && __va_copy]: Use __va_copy. + [!va_copy && !__va_copy]: Only now fall back to using memcpy. + +2002-08-21 Werner Koch <wk@gnupg.org> + + * errors.h: Added STATUS_IMPORT_PROBLEM. + +2002-08-20 Werner Koch <wk@gnupg.org> + + * vasprintf.c: Hack to handle NULL for %s. + +2002-08-09 Werner Koch <wk@gnupg.org> + + * signal.c: New. Taken from GnuPG 1.1.91. + +2002-07-23 Werner Koch <wk@gnupg.org> + + * util.h (_IO_cookie_io_functions_t): Fixed typo. Noted by + Richard Lefebvre. + +2002-07-22 Werner Koch <wk@gnupg.org> + + * fseeko.c, ftello.c: New. + +2002-06-28 Werner Koch <wk@gnupg.org> + + * maperror.c (map_to_assuan_status): Map more errorcodes to Bad + Certificate. + +2002-06-26 Werner Koch <wk@gnupg.org> + + * maperror.c (map_to_assuan_status): Map EOF to No_Data_Available. + +2002-06-10 Werner Koch <wk@gnupg.org> + + * errors.h (gnupg_error_token): Add new prototype. + (STATUS_ERROR): New. + + * mkerrtok: New. + * Makefile.am: Use it to create the new error token function. + +2002-06-04 Werner Koch <wk@gnupg.org> + + * maperror.c (map_to_assuan_status): Map Bad_CA_Certificate. + +2002-05-23 Werner Koch <wk@gnupg.org> + + * no-pth.c, Makefile.am: Removed. + +2002-05-22 Werner Koch <wk@gnupg.org> + + * mkdtemp.c: Replaced byte by unsigned char because it is no longer + defined in gcrypt.h. + +2002-05-21 Werner Koch <wk@gnupg.org> + + * maperror.c (map_gcry_err): Add libgcrypt's new S-expression errors. + (map_ksba_err): Add a few mappings. + +2002-05-14 Werner Koch <wk@gnupg.org> + + * gettime.c: New. + +2002-05-03 Werner Koch <wk@gnupg.org> + + * errors.h: Added STARUS_EXPSIG and STATUS_EXPKEYSIG. + +2002-04-15 Werner Koch <wk@gnupg.org> + + * cryptmiss.c: New. + +2002-02-14 Werner Koch <wk@gnupg.org> + + * maperror.c: Add more assuan<->gnupg mappings. + +2002-02-12 Werner Koch <wk@gnupg.org> + + * fopencookie.c: Dummy function. + + * vasprintf.c: New. Taken from binutils-2.9.1 and dropped all non + ANSI-C stuff. Merged with asprintf version. + + * no-pth.c: New. + +2002-01-23 Werner Koch <wk@gnupg.org> + + * mkdtemp.c: Copied from gnupg-1.0.6c and changed to use libgcrypt. + +2002-01-19 Werner Koch <wk@gnupg.org> + + * sysutils.c: New. This is the misc.c file from gnupg 1.0.6 with + the OpenPGP stuff removed. + * sysutils.h: New. + +2002-01-15 Werner Koch <wk@gnupg.org> + + * maperror.c: Add mapping for Not_Trusted. + +2002-01-11 Werner Koch <wk@gnupg.org> + + * maperror.c (map_assuan_err): Codes for CRL + +2002-01-08 Werner Koch <wk@gnupg.org> + + * util.h (spacep): New. + +2002-01-02 Werner Koch <wk@gnupg.org> + + * maperror.c (map_to_assuan_status): New. Merged from ../agent + and ../sm. + +2001-12-20 Werner Koch <wk@gnupg.org> + + * maperror.c (map_gcry_err): Add some mappings. + +2001-12-18 Werner Koch <wk@gnupg.org> + + * Makefile.am (AM_CPPFLAGS): Include flags for gcrypt and ksba + +2001-12-14 Werner Koch <wk@gnupg.org> + + * util.h (digitp, hexdigitp): New ctype like macros. + (atoi_1,atoi_2,atoi_4,xtoi_1,xtoi_2): New. + + + Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, + 2009, 2010, 2011 Free Software Foundation, Inc. + + This file is free software; as a special exception the author gives + unlimited permission to copy and/or distribute it, with or without + modifications, as long as this notice is preserved. + + This file is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY, to the extent permitted by law; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +Local Variables: +buffer-read-only: t +End: diff --git a/common/ChangeLog-2011.include b/common/ChangeLog-2011.include new file mode 100644 index 0000000..b9ca861 --- /dev/null +++ b/common/ChangeLog-2011.include @@ -0,0 +1,458 @@ +# This is the ChangeLog-2011 from the former ../include directory. It +# was moved to here after the removal of the directory on 2014-01-29. + +2011-12-01 Werner Koch <wk@g10code.com> + + NB: ChangeLog files are no longer manually maintained. Starting + on December 1st, 2011 we put change information only in the GIT + commit log, and generate a top-level ChangeLog file from logs at + "make dist". See doc/HACKING for details. + +2011-02-01 Werner Koch <wk@g10code.com> + + * cipher.h (PUBKEY_MAX_NPKEY, PUBKEY_MAX_NSKEY): Bump up to + accommodate gcrypt ECC keys. + +2011-01-21 Werner Koch <wk@g10code.com> + + * cipher.h (GCRY_PK_USAGE_CERT): Remove compatibility macros + because we now require libgcrypt 1.4.6. + (GCRY_PK_ECDH): Add replacement. + +2009-08-20 Daiki Ueno <ueno@unixuser.org> (wk) + + * cipher.h (struct DEK): Add field S2K_CACHEID. + +2008-04-18 Werner Koch <wk@g10code.com> + + * cipher.h (CIPHER_ALGO_CAMELLIA256): Change ID to 13. + (CIPHER_ALGO_CAMELLIA192): New. + +2007-12-12 Werner Koch <wk@g10code.com> + + * cipher.h (CIPHER_ALGO_CAMELLIA128, CIPHER_ALGO_CAMELLIA256): New. + +2006-09-20 Werner Koch <wk@g10code.com> + + * errors.h, http.h, memory.h, mpi.h, util.h, i18n.h: Removed. + * Makefile.am: New. + * distfiles: Removed. + +2006-08-16 Werner Koch <wk@g10code.com> + + * keyserver.h: Moved to ../common. + * http.h: Retired. + +2006-04-28 Werner Koch <wk@g10code.com> + + * cipher.h (DIGEST_ALGO_SHA224): Define it. + +2006-04-18 Werner Koch <wk@g10code.com> + + * keyserver.h, i18n.h, http.h, cipher.h: Updated to gpg 1.4.3. + +2003-09-04 David Shaw <dshaw@jabberwocky.com> + + * cipher.h: Drop TIGER/192 support. + + * types.h: Prefer using uint64_t when creating a 64-bit unsigned + type. This avoids a warning on compilers that support but complain + about unsigned long long. + + * util.h: Make sure that only ascii is passed to isfoo + functions. (From Werner on stable branch). + +2003-09-04 Werner Koch <wk@gnupg.org> + + * cipher.h (PUBKEY_USAGE_AUTH): Added. + +2003-07-03 Werner Koch <wk@gnupg.org> + + * cipher.h (DBG_CIPHER,g10c_debug_mode): Removed. + +2003-06-11 Werner Koch <wk@gnupg.org> + + * cipher.h: Include gcrypt.h and mapped cipher algo names to + gcrypt ones. Removed twofish_old and skipjack. Removed all + handle definitions and other raerely used stuff. This file will + eventually be entirely removed. + +2003-06-10 Werner Koch <wk@gnupg.org> + + * types.h (struct strlist): Removed. + +2003-05-24 David Shaw <dshaw@jabberwocky.com> + + * cipher.h, i18n.h, iobuf.h, memory.h, mpi.h, types.h, util.h: + Edit all preprocessor instructions to remove whitespace before the + '#'. This is not required by C89, but there are some compilers + out there that don't like it. + +2003-05-14 David Shaw <dshaw@jabberwocky.com> + + * types.h: Add initializer macros for 64-bit unsigned type. + +2003-05-02 David Shaw <dshaw@jabberwocky.com> + + * cipher.h: Add constants for compression algorithms. + +2003-03-11 David Shaw <dshaw@jabberwocky.com> + + * http.h: Add HTTP_FLAG_TRY_SRV. + +2003-02-11 David Shaw <dshaw@jabberwocky.com> + + * types.h: Try and use uint64_t for a 64-bit type. + +2003-02-04 David Shaw <dshaw@jabberwocky.com> + + * cipher.h: Add constants for new SHAs. + +2002-11-13 David Shaw <dshaw@jabberwocky.com> + + * util.h [__CYGWIN32__]: Don't need the registry prototypes. From + Werner on stable branch. + +2002-11-06 David Shaw <dshaw@jabberwocky.com> + + * util.h: Add wipememory2() macro (same as wipememory, but can + specify the byte to wipe with). + +2002-10-31 Stefan Bellon <sbellon@sbellon.de> + + * util.h [__riscos__]: Prefixed all RISC OS prototypes with + riscos_* + + * zlib-riscos.h: New. This is macro magic in order to make the + zlib library calls indeed call the RISC OS ZLib module. + +2002-10-31 David Shaw <dshaw@jabberwocky.com> + + * util.h: Add wipememory() macro. + +2002-10-29 Stefan Bellon <sbellon@sbellon.de> + + * util.h: Added parameter argument to make_basename() needed for + filetype support. + [__riscos__]: Added prototype. + +2002-10-28 Stefan Bellon <sbellon@sbellon.de> + + * util.h [__riscos__]: Added prototypes for new filetype support. + +2002-10-19 David Shaw <dshaw@jabberwocky.com> + + * distfiles, _regex.h: Add _regex.h from glibc 2.3.1. + +2002-10-14 David Shaw <dshaw@jabberwocky.com> + + * keyserver.h: Go to KEYSERVER_PROTO_VERSION 1. + +2002-10-08 David Shaw <dshaw@jabberwocky.com> + + * keyserver.h: Add new error code KEYSERVER_UNREACHABLE. + +2002-10-03 David Shaw <dshaw@jabberwocky.com> + + * util.h: Add new log_warning logger command which can be switched + between log_info and log_error via log_set_strict. + +2002-09-24 David Shaw <dshaw@jabberwocky.com> + + * keyserver.h: Add some new error codes for better GPA support. + +2002-09-10 Werner Koch <wk@gnupg.org> + + * mpi.h (mpi_is_protected, mpi_set_protect_flag) + (mpi_clear_protect_flag): Removed. + (mpi_get_nbit_info, mpi_set_nbit_info): Removed. + +2002-08-13 David Shaw <dshaw@jabberwocky.com> + + * cipher.h: Add AES aliases for RIJNDAEL algo numbers. + +2002-08-07 David Shaw <dshaw@jabberwocky.com> + + * cipher.h: Add md_algo_present(). + +2002-08-06 Stefan Bellon <sbellon@sbellon.de> + + * util.h [__riscos__]: Added riscos_getchar(). + +2002-06-21 Stefan Bellon <sbellon@sbellon.de> + + * util.h [__riscos__]: Further moving away of RISC OS specific + stuff from general code. + +2002-06-20 Stefan Bellon <sbellon@sbellon.de> + + * util.h [__riscos__]: Added riscos_set_filetype(). + +2002-06-14 David Shaw <dshaw@jabberwocky.com> + + * util.h: Add pop_strlist() from strgutil.c. + +2002-06-07 Stefan Bellon <sbellon@sbellon.de> + + * util.h [__riscos__]: RISC OS needs strings.h for strcasecmp() + and strncasecmp(). + +2002-05-22 Werner Koch <wk@gnupg.org> + + * util.h: Add strncasecmp. Removed stricmp and memicmp. + +2002-05-10 Stefan Bellon <sbellon@sbellon.de> + + * mpi.h: New function mpi_debug_alloc_like for M_DEBUG. + + * util.h [__riscos__]: Make use of __func__ that later + Norcroft compiler provides. + + * memory.h: Fixed wrong definition of m_alloc_secure_clear. + +2002-04-23 David Shaw <dshaw@jabberwocky.com> + + * util.h: New function answer_is_yes_no_default() to give a + default answer. + +2002-04-22 Stefan Bellon <sbellon@sbellon.de> + + * util.h [__riscos__]: Removed riscos_open, riscos_fopen and + riscos_fstat as those special versions aren't needed anymore. + +2002-02-19 David Shaw <dshaw@jabberwocky.com> + + * keyserver.h: Add KEYSERVER_NOT_SUPPORTED for unsupported actions + (say, a keyserver that has no way to search, or a readonly + keyserver that has no way to add). + +2002-01-02 Stefan Bellon <sbellon@sbellon.de> + + * util.h [__riscos__]: Updated prototype list. + + * types.h [__riscos__]: Changed comment wording. + +2001-12-27 David Shaw <dshaw@jabberwocky.com> + + * KEYSERVER_SCHEME_NOT_FOUND should be 127 to match the POSIX + system() (via /bin/sh) way of signaling this. + + * Added G10ERR_KEYSERVER + +2001-12-27 Werner Koch <wk@gnupg.org> + + * util.h [MINGW32]: Fixed name of include file. + +2001-12-22 Timo Schulz <ts@winpt.org> + + * util.h (is_file_compressed): New. + +2001-12-19 Werner Koch <wk@gnupg.org> + + * util.h [CYGWIN32]: Allow this as an alias for MINGW32. Include + stdarg.h because we use the va_list type. By Disastry. + +2001-09-28 Werner Koch <wk@gnupg.org> + + * cipher.h (PUBKEY_USAGE_CERT): New. + +2001-09-07 Werner Koch <wk@gnupg.org> + + * util.h: Add strsep(). + +2001-08-30 Werner Koch <wk@gnupg.org> + + * cipher.h (DEK): Added use_mdc. + +2001-08-24 Werner Koch <wk@gnupg.org> + + * cipher.h (md_write): Made buf arg const. + +2001-08-20 Werner Koch <wk@gnupg.org> + + * cipher.h (DEK): Added algo_info_printed; + + * util.h [__riscos__]: Added prototypes and made sure that we + never use __attribute__. + * cipher.h, iobuf.h, memory.h, mpi.h [__riscos__]: extern hack. + * i18n.h [__riscos__]: Use another include file + +2001-05-30 Werner Koch <wk@gnupg.org> + + * ttyio.h (tty_printf): Add missing parenthesis for non gcc. + * http.h: Removed trailing comma to make old ccs happy. Both are + by Albert Chin. + +2001-05-25 Werner Koch <wk@gnupg.org> + + * ttyio.h (tty_printf): Add printf attribute. + +2001-04-23 Werner Koch <wk@gnupg.org> + + * http.h: New flag HTTP_FLAG_NO_SHUTDOWN. + +2001-04-13 Werner Koch <wk@gnupg.org> + + * iobuf.h: Removed iobuf_fopen. + +2001-03-01 Werner Koch <wk@gnupg.org> + + * errors.h (G10ERR_UNU_SECKEY,G10ERR_UNU_PUBKEY): New + +2000-11-30 Werner Koch <wk@gnupg.org> + + * iobuf.h (iobuf_translate_file_handle): Add prototype. + +2000-11-11 Paul Eggert <eggert@twinsun.com> + + * iobuf.h (iobuf_get_filelength): Now returns off_t, not u32. + (struct iobuf_struct, iobuf_set_limit, + iobuf_tell, iobuf_seek): Use off_t, not ulong, for file offsets. + +2000-10-12 Werner Koch <wk@gnupg.org> + + * mpi.h: Changed the way mpi_limb_t is defined. + +Wed Sep 6 17:55:47 CEST 2000 Werner Koch <wk@openit.de> + + * iobuf.c (IOBUF_FILELENGTH_LIMIT): New. + +2000-03-14 14:03:43 Werner Koch (wk@habibti.openit.de) + + * types.h (HAVE_U64_TYPEDEF): Defined depending on configure test. + +Thu Jan 13 19:31:58 CET 2000 Werner Koch <wk@gnupg.de> + + * types.h (HAVE_U64_TYPEDEF): Add a test for _LONGLONG which fixes + this long living SGI bug. Reported by Alec Habig. + +Sat Dec 4 12:30:28 CET 1999 Werner Koch <wk@gnupg.de> + + * iobuf.h (IOBUFCTRL_CANCEL): Nww. + +Mon Oct 4 21:23:04 CEST 1999 Werner Koch <wk@gnupg.de> + + * errors.h (G10ERR_NOT_PROCESSED): New. + +Wed Sep 15 16:22:17 CEST 1999 Werner Koch <wk@isil.d.shuttle.de> + + + * i18n.h: Add support for simple-gettext. + +Tue Jun 29 21:44:25 CEST 1999 Werner Koch <wk@isil.d.shuttle.de> + + + * util.h (stricmp): Use strcasecmp as replacement. + +Sat Jun 26 12:15:59 CEST 1999 Werner Koch <wk@isil.d.shuttle.de> + + + * cipher.h (MD_HANDLE): Assigned a structure name. + +Fri Apr 9 12:26:25 CEST 1999 Werner Koch <wk@isil.d.shuttle.de> + + * cipher.h (BLOWFISH160): Removed. + +Tue Apr 6 19:58:12 CEST 1999 Werner Koch <wk@isil.d.shuttle.de> + + * cipher.h (DEK): increased max. key length to 32 bytes + + +Sat Feb 20 21:40:49 CET 1999 Werner Koch <wk@isil.d.shuttle.de> + + * g10lib.h: Removed file and changed all files that includes this. + +Tue Feb 16 14:10:02 CET 1999 Werner Koch <wk@isil.d.shuttle.de> + + * types.h (STRLIST): Add field flags. + +Wed Feb 10 17:15:39 CET 1999 Werner Koch <wk@isil.d.shuttle.de> + + * cipher.h (CIPHER_ALGO_TWOFISH): Chnaged ID to 10 and renamed + the old experimenatl algorithm to xx_OLD. + +Thu Jan 7 18:00:58 CET 1999 Werner Koch <wk@isil.d.shuttle.de> + + * cipher.h (MD_BUFFER_SIZE): Removed. + +Mon Dec 14 21:18:49 CET 1998 Werner Koch <wk@isil.d.shuttle.de> + + * types.h: fix for SUNPRO_C + +Tue Dec 8 13:15:16 CET 1998 Werner Koch <wk@isil.d.shuttle.de> + + * mpi.h (MPI): Changed the structure name to gcry_mpi and + changed all users. + +Tue Oct 20 11:40:00 1998 Werner Koch (wk@isil.d.shuttle.de) + + * iobuf.h (iobuf_get_temp_buffer): New. + +Tue Oct 13 12:40:48 1998 Werner Koch (wk@isil.d.shuttle.de) + + * iobuf.h (iobuf_get): Now uses .nofast + (iobuf_get2): Removed. + +Mon Sep 14 09:17:22 1998 Werner Koch (wk@(none)) + + * util.h (HAVE_ATEXIT): New. + (HAVE_RAISE): New. + +Mon Jul 6 10:41:55 1998 Werner Koch (wk@isil.d.shuttle.de) + + * cipher.h (PUBKEY_USAGE_): New. + +Mon Jul 6 09:49:51 1998 Werner Koch (wk@isil.d.shuttle.de) + + * iobuf.h (iobuf_set_error): New. + (iobuf_error): New. + +Sat Jun 13 17:31:32 1998 Werner Koch (wk@isil.d.shuttle.de) + + * g10lib.h: New as interface for the g10lib. + +Mon Jun 8 22:14:48 1998 Werner Koch (wk@isil.d.shuttle.de) + + * cipher.h (CIPHER_ALGO_CAST5): Changed name from .. CAST + +Thu May 21 13:25:51 1998 Werner Koch (wk@isil.d.shuttle.de) + + * cipher.h: removed ROT 5 and changed one id and add dummy + +Tue May 19 18:09:05 1998 Werner Koch (wk@isil.d.shuttle.de) + + * cipher.h (DIGEST_ALGO_TIGER): Chnaged id from 101 to 6. + +Mon May 4 16:37:17 1998 Werner Koch (wk@isil.d.shuttle.de) + + * cipher.h (PUBKEY_ALGO_ELGAMAL_E): New, with value of the + old one. + * (is_ELGAMAL, is_RSA): New macros + +Sun Apr 26 14:35:24 1998 Werner Koch (wk@isil.d.shuttle.de) + + * types.h: New type u64 + +Mon Mar 9 12:59:55 1998 Werner Koch (wk@isil.d.shuttle.de) + + * cipher.h: Included dsa.h. + +Tue Mar 3 15:11:21 1998 Werner Koch (wk@isil.d.shuttle.de) + + * cipher.h (random.h): Add new header and move all relevalt + functions to this header. + + + Copyright 1998, 1999, 2000, 2001 Free Software Foundation, Inc. + + This file is free software; as a special exception the author gives + unlimited permission to copy and/or distribute it, with or without + modifications, as long as this notice is preserved. + + This file is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY, to the extent permitted by law; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +Local Variables: +buffer-read-only: t +End: diff --git a/common/ChangeLog.jnlib b/common/ChangeLog.jnlib new file mode 100644 index 0000000..6c0db13 --- /dev/null +++ b/common/ChangeLog.jnlib @@ -0,0 +1,783 @@ +2011-12-01 Werner Koch <wk@g10code.com> + + NB: ChangeLog files are no longer manually maintained. Starting + on December 1st, 2011 we put change information only in the GIT + commit log, and generate a top-level ChangeLog file from logs at + "make dist". See doc/HACKING for details. + + [Update 2015-04-24: README.jnlib has been removed and all + references to JNLIB, except for this file, have been removed.] + +2010-03-10 Werner Koch <wk@g10code.com> + + See gnupg/common/ChangeLog for newer changes. + + JNLIB has been merged into GnuPG's common directory. README.jnlib + list the files making up JNLIB. + + * README: Rename to README.jnlib + * ChangeLog: Rename to ChangeLog.jnlib. + * Makefile.am: Remove. + +2010-03-01 Werner Koch <wk@g10code.com> + + * t-w32-reg.c: New. + + * w32-reg.c (read_w32_registry_string) + (write_w32_registry_string): Support W32CE. + +2010-02-26 Werner Koch <wk@g10code.com> + + * t-timestuff.c: New. + + * dynload.h (dlopen, dlsym) [W32CE]: Map to wchar_t. + + * mischelp.c (_jnlib_free): New. + (same_file_p) [W32CE]: Map to wchar_t. + + * utf8conv.c (set_native_charset) [W32CE]: Do not use + GetConsoleOutputCP. + (wchar_to_utf8, utf8_to_wchar) [W32]: New. + + * Makefile.am (t_jnlib_ldadd) [W32CE]: Add gpg-error. + + * t-support.h (getenv) [HAVE_GETENV]: Add getenv stub. + [W32CE]: Include gpg-error.h + * t-support.c (gpg_err_code_from_errno) + (gpg_err_code_from_syserror) [GPG_ERROR_H]: Do not build. + + * t-stringhelp.c (gethome) [!HAVE_GETPWUID]: Keep result of getenv. + + * dotlock.c [!HAVE_SIGNAL_H]: Don't include signal.h. + (create_dotlock) [W32CE]: Map filename top wchar_t. + + * libjnlib-config.h [USE_SIMPLE_GETTEXT]: Include gpg-error.h and + remove w32help.h. + (jnlib_set_errno): New. Use it everywhere to set ERRNO. + (getenv) [!HAVE_GETENV]: New. + (getpid) [W32E]: New. + + * stringhelp.c (get_pwdir) [!HAVE_PWD_H]: Mark unused args. + (w32_strerror) [W32CE]: Use a simple implementation. + + * w32help.h [USE_SIMPLE_GETTEXT]: Remove all definitions; we are + now using the gpg-error included implementation. + * w32-gettext.c: Remove. + + * mischelp.c (same_file_p): Fix bug in case the second file can't + be opened. + +2009-10-19 Werner Koch <wk@g10code.com> + + * strlist.c (add_to_strlist_try): New. + +2009-09-22 Werner Koch <wk@g10code.com> + + * dotlock.h (DOTLOCK): Rename to dotlock_t. Change all users. + +2009-08-26 Werner Koch <wk@g10code.com> + + * stringhelp.c (do_make_filename): Factor some code out to .. + (get_pwdir): .. new. + +2009-08-26 Werner Koch <wk@g10code.com> + + * stringhelp.c [HAVE_PWD_H]: Include pwd.h. + (do_make_filename): New. + (make_filename, make_filename_try): Implement using the new + function. + * t-stringhelp.c (test_make_filename_try): New. + * t-support.c (gcry_strdup): Fix. + + * stringhelp.h (make_filename, make_filename_try): Add sentinel + attribute. + +2009-08-25 Werner Koch <wk@g10code.com> + + * stringhelp.c: Include errno.h. + (do_strconcat): New. + (strconcat, xstrconcat): New. + * types.h (GNUPG_GCC_A_SENTINEL): New. + * t-stringhelp.c (test_strconcat, test_xstrconcat): New. + (main): Run them. + +2009-07-07 Werner Koch <wk@g10code.com> + + * stringhelp.c (make_filename_try): Use jnlib_malloc. + + * dotlock.c (read_lockfile): Replace jnlib_xmalloc by jnlib_malloc. + +2009-06-04 Werner Koch <wk@g10code.com> + + * mischelp.h: Include SUN_LEN etc also for W32. + +2009-05-19 Werner Koch <wk@g10code.com> + + * mischelp.h: Define PF_LOCAL, AF_LOCAL and SUN_LEN if requested. + * logging.c (fun_writer): Use SUN_LEN to fix a Mac OS X freeze. + +2009-03-25 Werner Koch <wk@g10code.com> + + * logging.c (fun_closer): Never close fd 2. + (set_file_fd): Close logstream early. + +2009-02-25 Werner Koch <wk@g10code.com> + + * logging.c (get_tid_callback): New. + (do_logv): Use it. + (log_set_get_tid_callback): New. + +2009-01-22 Werner Koch <wk@g10code.com> + + * t-support.c (gpg_err_code_from_errno) + (gpg_err_code_from_syserror): New. + +2008-11-20 Werner Koch <wk@g10code.com> + + * argparse.c (arg_parse): Fix last change. + +2008-11-11 Werner Koch <wk@g10code.com> + + * argparse.h: Add a bunch of macros and constants. + * argparse.c: Use the new macros. Re-indent the code. Change + license back to LGPL 2.1. + +2008-11-04 Werner Koch <wk@g10code.com> + + * w32-gettext.c: Merged with code from libgpg-error and rewrote + most parts. + + * Makefile.am (AM_CFLAGS): Add -DJNLIB_IN_JNLIB. + +2008-10-29 Werner Koch <wk@g10code.com> + + * stringhelp.c (make_filename): Implement using macros. Factor some + code out to .. + (change_slashes): New. + (make_filename_try): New. + + * w32-gettext.c (gettext): Return if no domain is loaded. + Reported by Tom Pegios. + +2008-10-28 Werner Koch <wk@g10code.com> + + * w32-gettext.c (gettext): Try the binary search if the string was + not found in the hash table. + +2008-10-20 Werner Koch <wk@g10code.com> + + * w32-afunix.c (_w32_sock_connect): Mark ADDRLEN as unused. + + * dotlock.c (release_dotlock): Do not mix declaration and code. + + * stringhelp.c (make_basename): Silent gcc warning about unused arg. + * argparse.c (store_alias): Ditto. + (find_long_option): + +2008-10-15 Werner Koch <wk@g10code.com> + + * logging.c (do_logv) [W32]: Flush the log stream. + +2008-09-29 Werner Koch <wk@g10code.com> + + * argparse.c (ARGERR_): Use constants for error values. + (optfile_parse): Prettify. Replace xmalloc and xrealloc by malloc + and realloc. + * libjnlib-config.h (jnlib_strdup, jnlib_realloc): New. + +2008-06-26 Werner Koch <wk@g10code.com> + + * stringhelp.c (print_sanitized_buffer2): Loose check for control + characters to better cope with utf-8. The range 0x80..0x9f is + nowadays not anymore accidently used for control charaters. + +2008-06-13 Werner Koch <wk@g10code.com> + + * dotlock.c: Reformat code and implement locking for W32. + (create_dotlock): Use snprintf. + +2008-06-11 Werner Koch <wk@g10code.com> + + * utf8conv.c: Remove useless variable ACTIVE_CHARSET. Suggested + by Petr Uzel. + +2008-05-26 Werner Koch <wk@g10code.com> + + * argparse.c (usage): Make sure to print a trailing LF for usage(1). + +2008-04-08 Werner Koch <wk@g10code.com> + + * w32-gettext.c (gettext_select_utf8): New. + (get_string): Support switching encodings. + (load_domain): Allocate space for DATA_NATIVE. + +2008-03-25 Werner Koch <wk@g10code.com> + + * w32-gettext.c (_nl_locale_name): New. Taken from + ../common/localename and GNU gettext's localename.c. + (set_gettext_file): Rewritten. + (gettext_localename): New. + +2008-03-17 Werner Koch <wk@g10code.com> + + * logging.c (my_funopen_hook_size_t): New. + (fun_writer): Use it to cope with fopencookie/funopen differences. + * dotlock.c (read_lockfile): Initialize PID. Reported by Stéphane + Corthésy. + +2008-02-22 Werner Koch <wk@g10code.com> + + * argparse.c (strusage): Set copyright year to 2008. + +2007-11-19 Werner Koch <wk@g10code.com> + + * stringhelp.c (percent_escape): Factor code out to + (do_percent_escape): .. new. + (try_percent_escape): New. + +2007-10-01 Werner Koch <wk@g10code.com> + + * w32-afunix.c: Only keep the client related code. + (read_port_and_nonce): New. Taken from Assuan. + (_w32_sock_connect): Rewritten. + +2007-08-29 Werner Koch <wk@g10code.com> + + * argparse.c (initialize): Make strings translatable and remove + extra LF. + +2007-08-24 Werner Koch <wk@g10code.com> + + * mischelp.c (same_file_p): New. + (libjnlib_dummy_mischelp_func): Remove as we now always have one + function. + +2007-08-09 Werner Koch <wk@g10code.com> + + * argparse.c (show_help): Expand the @EMAIL@ macro in the package + bug reporting address. + +2007-08-02 Werner Koch <wk@g10code.com> + + * t-stringhelp.c (test_compare_filenames): New. + + * stringhelp.c (compare_filenames) [HAVE_DRIVE_LETTERS]: Fixed + comparison to take slash and backslash in account. + (make_filename): Avoid mixing / and \. + +2007-07-04 Werner Koch <wk@g10code.com> + + * utf8conv.c (load_libiconv): Remove URL from translatble string. + + Switched JNLIB from LGPLv2.1 to LGPLv3. + +2007-07-01 Werner Koch <wk@g10code.com> + + * argparse.c (strusage): Use id 10 for the license string; + default to GPL3+. Change long note to version 3 or later. + (show_version): Print the license info. + +2007-06-19 Werner Koch <wk@g10code.com> + + * Makefile.am: Add support for regression tests. + * t-support.h, t-support.c: New. + * t-stringhelp.c: New. + + * stringhelp.c (percent_escape): Add arg EXTRA to make it a more + general function. Changed all callers. + +2007-06-18 Werner Koch <wk@g10code.com> + + * w32-afunix.c (_w32_sock_bind): Changed to properly detect an + already used socket. + +2007-06-18 Marcus Brinkmann <marcus@g10code.de> + + * stringhelp.h (percent_escape): New prototype. + * stringhelp.c (percent_escape): New function. + +2007-06-11 Werner Koch <wk@g10code.com> + + * utf8conv.c (jnlib_iconv_open, jnlib_iconv, jnlib_iconv_close): New. + +2007-06-06 Werner Koch <wk@g10code.com> + + * w32help.h: New. + * w32-gettext.c: New. Taken from gnupg 1.4, added ngettext, + changed to use jnlib malloc functions and put under the LGPL. + * w32-reg.c: New. Taken from../common/w32reg.c and changed to + LGPL. Changed API to use the jnlib malloc functions. + * Makefile.am (libjnlib_a_SOURCES) [!W32]: Do not build the w32 + specific modules. + + * dotlock.c: Include stringhelp.h for stpcpy prototype. + +2007-06-04 Werner Koch <wk@g10code.com> + + * dynload.h: New. Taken from ../common and changed to LGPL. + + * utf8conv.c (load_libiconv): New. Taken from GnuPG 1.4 + +2007-05-30 Werner Koch <wk@g10code.com> + + * w32-pth.h, w32-pth.c: Remove. + +2007-04-25 Werner Koch <wk@g10code.com> + + * argparse.c (long_opt_strlen): Fixed for utf-8. + +2007-03-07 Werner Koch <wk@g10code.com> + + * argparse.c (strusage): Set copyright year to 2007. + +2007-01-25 Werner Koch <wk@g10code.com> + + * stringhelp.c (utf8_charcount): New. + +2006-11-29 Werner Koch <wk@g10code.com> + + * utf8conv.c (set_native_charset) [HAVE_W32_SYSTEM]: Fixed typo in + macro name. + +2006-11-15 Werner Koch <wk@g10code.com> + + * logging.c (my_funopen_hook_ret_t): New. + (fun_writer): Use it. + +2006-10-19 Werner Koch <wk@g10code.com> + + * stringhelp.c (memrchr) [!HAVE_MEMRCHR]: Provide a replacement. + +2006-09-27 Werner Koch <wk@g10code.com> + + * mischelp.c: New. + (timegm): Copied from gnupg 1.4, changed from GPL to LGPL. Fixed + a memory leak. + + * stringhelp.h (isascii): New. + + * stringhelp.c (strsep): New. Copied from gnupg 1.4.5 + util/strgutil.c. + + * strlist.h (STRLIST): Removed deprecated typedef. + + * types.h: Made cpp commands work with old compilers. Also shows + up nicer with Emacs' font locking. + + * w32-afunix.c (_w32_sock_connect): Set ERRNO for an invalid port. + + Changed license from GPL to LGPL. Note that all code has either + been written by me, David, employees of g10 Code or taken from + glibc. + + * libjnlib-config.h, stringhelp.c, stringhelp.h: + * strlist.c, strlist.h, utf8conv.c, utf8conv.h: + * argparse.c, argparse.h, logging.c, logging.h: + * dotlock.c, dotlock.h, types.h, mischelp.h: + * xmalloc.c, xmalloc.h, w32-pth.c, w32-pth.h: + * w32-afunix.c, w32-afunix.h: Tagged them to be long to jnlib + which is a part of GnuPG but also used by other projetcs. + +2006-09-22 Werner Koch <wk@g10code.com> + + * utf8conv.c: Reworked to match the gnupg 1.4.5 code. This now + requires iconv support but this is reasonable for all modern + systems. + +2006-08-29 Werner Koch <wk@g10code.com> + + * logging.c (do_logv): Emit a missing LF for fatal errors. + +2006-06-28 Werner Koch <wk@g10code.com> + + * dotlock.c (make_dotlock, release_dotlock, read_lockfile) + (maybe_deadlock, destroy_dotlock, create_dotlock): Re-indented. + (create_dotlock): Repalces some log_fatal by log_error as it was + not intended that they should terminate. Write the nodename to + the locking file. Code cleanups. + (read_lockfile): Reworked to read the node name. + (make_dotlock): Test for identical node name and delete lock stale + file. + (release_dotlock): Likewise. + +2006-05-23 Werner Koch <wk@g10code.com> + + * libjnlib-config.h (JNLIB_NEED_UTF8CONV): Fixed typo in name. + + * dotlock.c (release_dotlock): Don't act if we don't have any + locks at all. + (destroy_dotlock): New. From 1.4.3. + (dotlock_remove_lockfiles): Make use of destroy function. + +2006-05-19 Werner Koch <wk@g10code.com> + + * strlist.c (append_to_strlist2): Enabled. + + * stringhelp.c (print_sanitized_buffer2): New. Changed the rules + to match the behaviour of print_string2 from gnupg 1.4.3. + (print_sanitized_buffer): Use the new function. + (print_sanitized_string2): New. + (hextobyte): New. Taken from gpg 1.4.3. + +2006-04-28 Werner Koch <wk@g10code.com> + + * stringhelp.c (print_sanitized_buffer): Fix bug where the count + got wrong for the \xNN representation. + (sanitize_buffer): Fix bug where some control characters lose part + of their \xNN representation. + +2006-04-20 Werner Koch <wk@g10code.com> + + * stringhelp.c (make_basename): New arg INPUTPATH for future + riscos compatibility. + +2006-04-18 Werner Koch <wk@g10code.com> + + * libjnlib-config.h (JNLIB_NEED_UTF8CONF): Defined. + * strlist.c (add_to_strlist2) [JNLIB_NEED_UTF8CONV]: Enabled. + +2005-06-15 Werner Koch <wk@g10code.com> + + * stringhelp.c (sanitize_buffer): Make P a void*. + (ascii_memistr, memistr): Ditto. + (ascii_memcasecmp): Ditto. + * logging.c (writen): Use void * for arg BUFFER. + * stringhelp.c (memistr): Fixed unsigned/signed pointer conflict. + (ascii_memistr): Ditto. + (ascii_memcasemem): Ditto. + * utf8conv.c (utf8_to_native): Ditto. + (utf8_to_native): Ditto. + * argparse.c (show_version): Removed non-required cast. + +2005-01-19 Werner Koch <wk@g10code.com> + + * logging.c (fun_writer): Don't fallback to stderr. Print to + stderr only if connected to a tty. + +2004-12-20 Werner Koch <wk@g10code.com> + + * w32-pth.c (do_pth_event_free): The events are hold in a ring + buffer. Adjust for that. + (do_pth_event_body): Ditto. + (pth_event_isolate): Ditto. + (do_pth_wait): Ditto. + (_pth_event_count): Renamed to .. + (event_count): .. and adjusted as above. + (pth_init): Define 3 debug levels and change all debug calls to + make use of them. This makes the moule now silent. + +2004-12-19 Werner Koch <wk@g10code.com> + + * w32-pth.c (pth_init): Enable debugging depending on env var. + (pth_self): New. + (pth_mutex_release, pth_mutex_acquire): Implemented directly using + the W32 API. + +2004-12-18 Werner Koch <wk@g10code.com> + + * w32-pth.c (pth_init): Reverse return values. Use TRUE and FALSE + constants. + (pth_kill, pth_mutex_acquire, pth_attr_set, pth_join, pth_cancel): + Ditto. + +2004-12-15 Werner Koch <wk@g10code.com> + + * logging.c [W32]: Don't include unavailable headers. + +2004-12-14 Werner Koch <wk@g10code.com> + + * w32-pth.c (_pth_strerror): Renamed to ... + (w32_strerror): .. this. And let callers provide a buffer. + (spawn_helper_thread): Removed HD arg and hardwire the stack size + to 32k. + (do_pth_wait): Removed use of ATTR; not needed for the helper + threads. + (helper_thread): Renamed to .. + (launch_thread): .. this. Release handle if not joinable. + (struct pth_priv_hd_s): Renamed to ... + (struct thread_info_s): .. this. Add member JOINABLE and TH. + +2004-12-14 Timo Schulz <twoaday@g10code.com> + + * w32-pth.c (pth_kill): Just release the crit section if + pth_init was really called. And set all handles to NULL. + (_pth_strerror): New. + (do_pth_wait): Before we enter the loop we check if there + are too much events in the ring. + +2004-12-14 Werner Koch <wk@g10code.com> + + * w32-pth.h (pth_event_occured): Removed macro. + * w32-pth.c: Fixed license statement; its under the LGPL. + (enter_pth, leave_pth): Use them to bracket almost all public + functions. + +2004-12-13 Timo Schulz <twoaday@g10code.com> + + * w32-pth.c (enter_pth, leave_pth): New. + (pth_init): Initialize global mutex section. + (pth_kill): Release global mutex section. + (helper_thread): New. + (pth_spawn): Make sure only one thread is running. + +2004-12-13 Werner Koch <wk@g10code.com> + + * stringhelp.c (w32_strerror) [W32]: New. + + * w32-pth.c, w32-pth.h: Added real code written by Timo Schulz. + Not finished, though. + +2004-12-07 Werner Koch <wk@g10code.com> + + * w32-pth.c, w32-pth.h: New. + +2004-11-26 Werner Koch <wk@g10code.com> + + * logging.c [_WIN32]: Don't include socket headers. + +2004-11-30 Timo Schulz <ts@g10code.com> + + * w32-afunix.c: New. AF_UNIX emulation for W32. + * w32-afunix.h: Likewise. + +2004-11-22 Werner Koch <wk@g10code.com> + + * logging.c (log_test_fd): Add test on LOGSTREAM. Reported by + Barry Schwartz. + +2004-11-18 Werner Koch <wk@g10code.com> + + * logging.c: Explicitly include sys/stat.h for the S_I* constants. + +2004-10-21 Werner Koch <wk@g10code.com> + + * logging.c (do_logv): Use set_log_stream to setup a default. + (log_set_file): Factored code out to .. + (set_file_fd): .. New function to allow using a file descriptor. + (log_set_fd): Make use of new fucntion. + (fun_writer): Reworked. + +2004-08-18 Werner Koch <wk@g10code.de> + + * stringhelp.c (print_sanitized_utf8_string): Actually implement + it. + +2004-06-21 Werner Koch <wk@g10code.com> + + * logging.c (log_set_file): Do not close an old logstream if it + used to be stderr or stdout. + +2004-05-05 Werner Koch <wk@gnupg.org> + + * logging.c (log_set_file): Oops, don't close if LOGSTREAM is NULL. + +2004-04-30 Werner Koch <wk@gnupg.org> + + * logging.c (log_set_file): Make sure the log stream will be + closed even if the stderr fileno will be assigned to a new socket. + +2004-04-16 Werner Koch <wk@gnupg.org> + + * logging.h (JNLIB_LOG_WITH_PREFIX): Add constants for the flag + values. + * logging.c (log_set_prefix): New flag DETACHED. + (fun_writer): Take care of this flag. + (log_test_fd): New. + +2004-02-18 Werner Koch <wk@gnupg.org> + + * stringhelp.c (print_sanitized_buffer): Don't care about + non-ASCII characaters. + (sanitize_buffer): Ditto. + +2004-02-12 Werner Koch <wk@gnupg.org> + + * Makefile.am: Replaced INCLUDES by AM_CPPFLAGS. + +2004-01-05 Werner Koch <wk@gnupg.org> + + * argparse.c (strusage): Changed default copyright year to 2004. + +2003-12-17 Werner Koch <wk@gnupg.org> + + * argparse.c (initialize): Replaced use of non-literal format + args. Suggested by Florian Weimer. + +2003-12-16 Werner Koch <wk@gnupg.org> + + * logging.c (writen, fun_writer, fun_closer): New. + (log_set_file): Add feature to log to a socket. + (log_set_file, do_logv): Force printing with prefix and pid. + +2003-11-13 Werner Koch <wk@gnupg.org> + + * strlist.c (strlist_copy): New. + + * dotlock.c: Define DIRSEP_C et al. if not defined. + +2003-11-06 Werner Koch <wk@gnupg.org> + + * strlist.h (strlist_t): New. STRLIST is now deprecated. + +2003-06-18 Werner Koch <wk@gnupg.org> + + * strlist.c (strlist_pop): New. + + * dotlock.c (dotlock_remove_lockfiles): Prefixed with dotlock_ and + made global. + +2003-06-17 Werner Koch <wk@gnupg.org> + + * stringhelp.c (length_sans_trailing_chars) + (length_sans_trailing_ws): New. + + * logging.c (log_inc_errorcount): New. + + * stringhelp.c (print_sanitized_utf8_buffer): Implement utf8 + conversion. + (sanitize_buffer): New. Based on gnupg 1.3.2 make_printable_string. + + * dotlock.c: Updated to match the version from 1.3.2 + * utf8conv.c: New. Code taken from strgutil.c of gnupg 1.3.2. + * utf8conv.h: New. + +2003-06-16 Werner Koch <wk@gnupg.org> + + * logging.c (do_logv): Hack to optionally suppress a leading space. + + * stringhelp.c (ascii_strncasecmp): New. Taken from gnupg 1.3. + (ascii_memistr): New. Taken from gnupg 1.3 + +2003-06-13 Werner Koch <wk@gnupg.org> + + * mischelp.h (wipememory2,wipememory): New. Taken from GnuPG 1.3.2. + +2002-06-04 Werner Koch <wk@gnupg.org> + + * stringhelp.c (print_sanitized_utf8_string): New. No real + implementation for now. + (print_sanitized_utf8_buffer): Ditto. + +2002-04-04 Werner Koch <wk@gnupg.org> + + * logging.c (log_get_prefix): New. + +2002-03-15 Werner Koch <wk@gnupg.org> + + * argparse.c (optfile_parse): Fixed missing argument handling. + +2002-02-25 Werner Koch <wk@gnupg.org> + + * stringhelp.c (ascii_memcasemem): New. + +2002-02-14 Werner Koch <wk@gnupg.org> + + * Makefile.am (INCLUDES): Add cflags for libgcrypt. + +2002-02-07 Werner Koch <wk@gnupg.org> + + * logging.c (log_set_fd): New. + + * stringhelp.c (print_sanitized_buffer): New. + (print_sanitized_string): New. + +2002-01-24 Werner Koch <wk@gnupg.org> + + * argparse.c (strusage): Set default copyright notice year to 2002. + + Fixed the copyright notice of this file, as it has always been + part of GnuPG and therefore belongs to the FSF. + +2001-11-01 Marcus Brinkmann <marcus@g10code.de> + + * logging.c (log_printf): Do not initialize ARG_PTR with 0, we + don't know the correct type. Instead, run va_start and va_end + unconditionally. + Reported by Jose Carlos Garcia Sogo <jsogo@debian.org>. + +2002-01-19 Werner Koch <wk@gnupg.org> + + * logging.c (log_get_stream): New. + +2001-12-05 Werner Koch <wk@gnupg.org> + + * logging.c (log_set_prefix): New. + (do_logv): Include prefix and pid only if enabled. Print time only + when explicitly enabled. + (log_logv): New. + * logging.h: Include log_logv() only when requested. + +2001-11-06 Werner Koch <wk@gnupg.org> + + * strlist.c, strlist.h: New. Taken from pgnupg/util/strgutil.c + +2001-08-30 Werner Koch <wk@gnupg.org> + + * logging.c (log_printf): Don't pass NULL instead of arg_ptr. + +2001-07-19 Werner Koch <wk@gnupg.org> + + * stringhelp.c (ascii_memistr,ascii_isupper,ascii_islower, + ascii_toupper,ascii_tolower, ascii_strcasecmp, ascii_memcasecmp): New. + +2000-07-26 10:02:51 Werner Koch (wk@habibti.openit.de) + + * stringhelp.c.: Add stdarg.h + * argparse.h: s/ulong/unsigned long/ although this should be defined + by types.h. + +2000-06-28 19:40:23 Werner Koch (wk@habibti.openit.de) + + * Makefile.am: Replaced second logging.c by .h + +2000-05-24 08:58:15 Werner Koch (wk@habibti.openit.de) + + * logging.c (log_get_errorcount): New. + +2000-05-24 08:44:47 Werner Koch (wk@habibti.openit.de) + + * stringhelp.c: Added a few filename related helper functions. + +2000-05-11 18:04:43 Werner Koch (wk@habibti.openit.de) + + * xmalloc.c (xstrcat2): Replaced stpcpy to quickly address W32 + problems. + +2000-05-02 19:43:38 Werner Koch (wk@habibti.openit.de) + + * xmalloc.c (xstrcat2): New. + +Mon Jan 24 13:04:28 CET 2000 Werner Koch <wk@gnupg.de> + + * README: New. + * Makefile.am: new. + * argparse.c, argparse.h, logging.c, logging.h: + * mischelp.h, stringhelp.c, stringhelp.h, xmalloc.c: + * xmalloc.h, dotlock.c: Moved from ../util to here. + * dotlock.h: New. + * libjnlib-config.h: New. + + * logging.c (log_set_file): New. + (log_printf): New. + (do_logv): Add kludge to insert LFs. + + + *********************************************************** + * Please note that JNLIB is maintained as part of GnuPG. * + * You may find it source-copied in other packages. * + *********************************************************** + + Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, + 2010 Free Software Foundation, Inc. + + This file is free software; as a special exception the author gives + unlimited permission to copy and/or distribute it, with or without + modifications, as long as this notice is preserved. + + This file is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY, to the extent permitted by law; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +Local Variables: +buffer-read-only: t +End: diff --git a/common/Makefile.am b/common/Makefile.am new file mode 100644 index 0000000..bd281d6 --- /dev/null +++ b/common/Makefile.am @@ -0,0 +1,235 @@ +# Makefile for common gnupg modules +# Copyright (C) 2001, 2003, 2007, 2010 Free Software Foundation, Inc. +# +# This file is part of GnuPG. +# +# GnuPG is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# GnuPG is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <https://www.gnu.org/licenses/>. + +## Process this file with automake to produce Makefile.in + +EXTRA_DIST = mkstrtable.awk exaudit.awk exstatus.awk ChangeLog-2011 \ + audit-events.h status-codes.h ChangeLog.jnlib \ + ChangeLog-2011.include w32info-rc.h.in gnupg.ico \ + all-tests.scm + +noinst_LIBRARIES = libcommon.a libcommonpth.a libgpgrl.a +if !HAVE_W32CE_SYSTEM +noinst_LIBRARIES += libsimple-pwquery.a +endif +noinst_PROGRAMS = $(module_tests) $(module_maint_tests) +if DISABLE_TESTS +TESTS = +else +TESTS = $(module_tests) +endif + +BUILT_SOURCES = audit-events.h status-codes.h + +MAINTAINERCLEANFILES = audit-events.h status-codes.h + +AM_CPPFLAGS = + +AM_CFLAGS = $(LIBGCRYPT_CFLAGS) $(LIBASSUAN_CFLAGS) $(KSBA_CFLAGS) + +include $(top_srcdir)/am/cmacros.am + + +common_sources = \ + common-defs.h \ + util.h utilproto.h fwddecl.h i18n.c i18n.h \ + types.h host2net.h dynload.h w32help.h \ + mapstrings.c stringhelp.c stringhelp.h \ + strlist.c strlist.h \ + utf8conv.c utf8conv.h \ + argparse.c argparse.h \ + logging.c logging.h \ + dotlock.c dotlock.h \ + mischelp.c mischelp.h \ + status.c status.h\ + shareddefs.h \ + openpgpdefs.h \ + gc-opt-flags.h \ + sexp-parse.h \ + tlv.c tlv.h tlv-builder.c \ + init.c init.h \ + sexputil.c \ + sysutils.c sysutils.h \ + homedir.c \ + gettime.c gettime.h \ + yesno.c \ + b64enc.c b64dec.c zb32.c zb32.h \ + convert.c \ + percent.c \ + mbox-util.c mbox-util.h \ + miscellaneous.c \ + xasprintf.c \ + xreadline.c \ + membuf.c membuf.h \ + ccparray.c ccparray.h \ + iobuf.c iobuf.h \ + ttyio.c ttyio.h \ + asshelp.c asshelp2.c asshelp.h \ + exechelp.h \ + signal.c \ + audit.c audit.h \ + localename.c \ + session-env.c session-env.h \ + userids.c userids.h \ + openpgp-oid.c \ + ssh-utils.c ssh-utils.h \ + agent-opt.c \ + helpfile.c \ + mkdir_p.c mkdir_p.h \ + strlist.c strlist.h \ + exectool.c exectool.h \ + server-help.c server-help.h \ + name-value.c name-value.h \ + recsel.c recsel.h \ + ksba-io-support.c ksba-io-support.h \ + openpgp-fpr.c \ + compliance.c compliance.h + + +if HAVE_W32_SYSTEM +common_sources += w32-reg.c w32-cmdline.c +endif + +# To make the code easier to read we have split home some code into +# separate source files. +if HAVE_W32_SYSTEM +if HAVE_W32CE_SYSTEM +common_sources += exechelp-w32ce.c +else +common_sources += exechelp-w32.c +endif +else +common_sources += exechelp-posix.c +endif + +# Sources only useful without NPTH. +without_npth_sources = \ + get-passphrase.c get-passphrase.h + +# Sources only useful with NPTH. +with_npth_sources = \ + call-gpg.c call-gpg.h + +libcommon_a_SOURCES = $(common_sources) $(without_npth_sources) +libcommon_a_CFLAGS = $(AM_CFLAGS) $(LIBASSUAN_CFLAGS) -DWITHOUT_NPTH=1 + +libcommonpth_a_SOURCES = $(common_sources) $(with_npth_sources) +libcommonpth_a_CFLAGS = $(AM_CFLAGS) $(LIBASSUAN_CFLAGS) $(NPTH_CFLAGS) + +if !HAVE_W32CE_SYSTEM +libsimple_pwquery_a_SOURCES = \ + simple-pwquery.c simple-pwquery.h asshelp.c asshelp.h +libsimple_pwquery_a_CFLAGS = $(AM_CFLAGS) $(LIBASSUAN_CFLAGS) +endif + +libgpgrl_a_SOURCES = \ + gpgrlhelp.c + +if MAINTAINER_MODE +# Note: Due to the dependency on Makefile, the file will always be +# rebuilt, so we allow this only in maintainer mode. + +# Create the audit-events.h include file from audit.h +# Note: We create the target file in the source directory because it +# is a distributed built source. If we would not do that we may end +# up with two files and then it is not clear which version of the +# files will be picked up. +audit-events.h: Makefile.am mkstrtable.awk exaudit.awk audit.h + $(AWK) -f $(srcdir)/exaudit.awk $(srcdir)/audit.h \ + | $(AWK) -f $(srcdir)/mkstrtable.awk -v textidx=3 -v nogettext=1 \ + -v pkg_namespace=eventstr_ > $(srcdir)/audit-events.h + +# Create the status-codes.h include file from status.h +status-codes.h: Makefile.am mkstrtable.awk exstatus.awk status.h + $(AWK) -f $(srcdir)/exstatus.awk $(srcdir)/status.h \ + | $(AWK) -f $(srcdir)/mkstrtable.awk -v textidx=3 -v nogettext=1 \ + -v pkg_namespace=statusstr_ > $(srcdir)/status-codes.h +endif + +# +# Module tests +# +module_tests = t-stringhelp t-timestuff \ + t-convert t-percent t-gettime t-sysutils t-sexputil \ + t-session-env t-openpgp-oid t-ssh-utils \ + t-mapstrings t-zb32 t-mbox-util t-iobuf t-strlist \ + t-name-value t-ccparray t-recsel t-w32-cmdline +if !HAVE_W32CE_SYSTEM +module_tests += t-exechelp t-exectool +endif +if HAVE_W32_SYSTEM +module_tests += t-w32-reg +endif + +if MAINTAINER_MODE +module_maint_tests = t-helpfile t-b64 +else +module_maint_tests = +endif + +t_extra_src = t-support.h + +t_common_cflags = $(KSBA_CFLAGS) $(LIBGCRYPT_CFLAGS) \ + $(LIBASSUAN_CFLAGS) $(GPG_ERROR_CFLAGS) $(INCICONV) +t_common_ldadd = libcommon.a \ + $(LIBGCRYPT_LIBS) $(LIBASSUAN_LIBS) $(GPG_ERROR_LIBS) \ + $(LIBINTL) $(LIBICONV) $(NETLIBS) + + +# Common tests +t_stringhelp_SOURCES = t-stringhelp.c $(t_extra_src) +t_stringhelp_LDADD = $(t_common_ldadd) + +t_timestuff_SOURCES = t-timestuff.c $(t_extra_src) +t_timestuff_LDADD = $(t_common_ldadd) + +t_convert_LDADD = $(t_common_ldadd) +t_percent_LDADD = $(t_common_ldadd) +t_gettime_LDADD = $(t_common_ldadd) +t_sysutils_LDADD = $(t_common_ldadd) +t_helpfile_LDADD = $(t_common_ldadd) +t_sexputil_LDADD = $(t_common_ldadd) +t_b64_LDADD = $(t_common_ldadd) +t_exechelp_LDADD = $(t_common_ldadd) +t_exectool_LDADD = $(t_common_ldadd) +t_session_env_LDADD = $(t_common_ldadd) +t_openpgp_oid_LDADD = $(t_common_ldadd) +t_ssh_utils_LDADD = $(t_common_ldadd) +t_mapstrings_LDADD = $(t_common_ldadd) + +t_zb32_SOURCES = t-zb32.c $(t_extra_src) +t_zb32_LDADD = $(t_common_ldadd) + +t_mbox_util_LDADD = $(t_common_ldadd) +t_iobuf_LDADD = $(t_common_ldadd) +t_strlist_LDADD = $(t_common_ldadd) +t_name_value_LDADD = $(t_common_ldadd) +t_ccparray_LDADD = $(t_common_ldadd) +t_recsel_LDADD = $(t_common_ldadd) + +t_w32_cmdline_SOURCES = t-w32-cmdline.c w32-cmdline.c $(t_extra_src) +t_w32_cmdline_LDADD = $(t_common_ldadd) + +# System specific test +if HAVE_W32_SYSTEM +t_w32_reg_SOURCES = t-w32-reg.c $(t_extra_src) +t_w32_reg_LDADD = $(t_common_ldadd) +endif + +# All programs should depend on the created libs. +$(PROGRAMS) : libcommon.a libcommonpth.a diff --git a/common/Makefile.in b/common/Makefile.in new file mode 100644 index 0000000..d78f9c0 --- /dev/null +++ b/common/Makefile.in @@ -0,0 +1,3509 @@ +# Makefile.in generated by automake 1.16.3 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2020 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# Makefile for common gnupg modules +# Copyright (C) 2001, 2003, 2007, 2010 Free Software Foundation, Inc. +# +# This file is part of GnuPG. +# +# GnuPG is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# GnuPG is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <https://www.gnu.org/licenses/>. + +# cmacros.am - C macro definitions +# Copyright (C) 2004 Free Software Foundation, Inc. +# +# This file is part of GnuPG. +# +# GnuPG is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# GnuPG is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <https://www.gnu.org/licenses/>. + + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +@HAVE_W32CE_SYSTEM_FALSE@am__append_1 = libsimple-pwquery.a +noinst_PROGRAMS = $(am__EXEEXT_3) $(am__EXEEXT_4) +@DISABLE_TESTS_FALSE@TESTS = $(am__EXEEXT_3) +@HAVE_DOSISH_SYSTEM_FALSE@am__append_2 = -DGNUPG_BINDIR="\"$(bindir)\"" \ +@HAVE_DOSISH_SYSTEM_FALSE@ -DGNUPG_LIBEXECDIR="\"$(libexecdir)\"" \ +@HAVE_DOSISH_SYSTEM_FALSE@ -DGNUPG_LIBDIR="\"$(libdir)/@PACKAGE@\"" \ +@HAVE_DOSISH_SYSTEM_FALSE@ -DGNUPG_DATADIR="\"$(datadir)/@PACKAGE@\"" \ +@HAVE_DOSISH_SYSTEM_FALSE@ -DGNUPG_SYSCONFDIR="\"$(sysconfdir)/@PACKAGE@\"" \ +@HAVE_DOSISH_SYSTEM_FALSE@ -DGNUPG_LOCALSTATEDIR="\"$(localstatedir)\"" + + +# If a specific protect tool program has been defined, pass its name +# to cc. Note that these macros should not be used directly but via +# the gnupg_module_name function. +@GNUPG_AGENT_PGM_TRUE@am__append_3 = -DGNUPG_DEFAULT_AGENT="\"@GNUPG_AGENT_PGM@\"" +@GNUPG_PINENTRY_PGM_TRUE@am__append_4 = -DGNUPG_DEFAULT_PINENTRY="\"@GNUPG_PINENTRY_PGM@\"" +@GNUPG_SCDAEMON_PGM_TRUE@am__append_5 = -DGNUPG_DEFAULT_SCDAEMON="\"@GNUPG_SCDAEMON_PGM@\"" +@GNUPG_DIRMNGR_PGM_TRUE@am__append_6 = -DGNUPG_DEFAULT_DIRMNGR="\"@GNUPG_DIRMNGR_PGM@\"" +@GNUPG_PROTECT_TOOL_PGM_TRUE@am__append_7 = -DGNUPG_DEFAULT_PROTECT_TOOL="\"@GNUPG_PROTECT_TOOL_PGM@\"" +@GNUPG_DIRMNGR_LDAP_PGM_TRUE@am__append_8 = -DGNUPG_DEFAULT_DIRMNGR_LDAP="\"@GNUPG_DIRMNGR_LDAP_PGM@\"" +@HAVE_W32_SYSTEM_TRUE@am__append_9 = w32-reg.c w32-cmdline.c + +# To make the code easier to read we have split home some code into +# separate source files. +@HAVE_W32CE_SYSTEM_TRUE@@HAVE_W32_SYSTEM_TRUE@am__append_10 = exechelp-w32ce.c +@HAVE_W32CE_SYSTEM_FALSE@@HAVE_W32_SYSTEM_TRUE@am__append_11 = exechelp-w32.c +@HAVE_W32_SYSTEM_FALSE@am__append_12 = exechelp-posix.c +@HAVE_W32CE_SYSTEM_FALSE@am__append_13 = t-exechelp t-exectool +@HAVE_W32_SYSTEM_TRUE@am__append_14 = t-w32-reg +subdir = common +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/autobuild.m4 \ + $(top_srcdir)/m4/codeset.m4 $(top_srcdir)/m4/gettext.m4 \ + $(top_srcdir)/m4/gpg-error.m4 $(top_srcdir)/m4/iconv.m4 \ + $(top_srcdir)/m4/isc-posix.m4 $(top_srcdir)/m4/ksba.m4 \ + $(top_srcdir)/m4/lcmessage.m4 $(top_srcdir)/m4/ldap.m4 \ + $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \ + $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libassuan.m4 \ + $(top_srcdir)/m4/libgcrypt.m4 $(top_srcdir)/m4/nls.m4 \ + $(top_srcdir)/m4/npth.m4 $(top_srcdir)/m4/ntbtls.m4 \ + $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/m4/po.m4 \ + $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/m4/readline.m4 \ + $(top_srcdir)/m4/socklen.m4 $(top_srcdir)/m4/sys_socket_h.m4 \ + $(top_srcdir)/m4/tar-ustar.m4 $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(SHELL) $(top_srcdir)/build-aux/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = w32info-rc.h +CONFIG_CLEAN_VPATH_FILES = +@HAVE_W32CE_SYSTEM_FALSE@am__EXEEXT_1 = t-exechelp$(EXEEXT) \ +@HAVE_W32CE_SYSTEM_FALSE@ t-exectool$(EXEEXT) +@HAVE_W32_SYSTEM_TRUE@am__EXEEXT_2 = t-w32-reg$(EXEEXT) +am__EXEEXT_3 = t-stringhelp$(EXEEXT) t-timestuff$(EXEEXT) \ + t-convert$(EXEEXT) t-percent$(EXEEXT) t-gettime$(EXEEXT) \ + t-sysutils$(EXEEXT) t-sexputil$(EXEEXT) t-session-env$(EXEEXT) \ + t-openpgp-oid$(EXEEXT) t-ssh-utils$(EXEEXT) \ + t-mapstrings$(EXEEXT) t-zb32$(EXEEXT) t-mbox-util$(EXEEXT) \ + t-iobuf$(EXEEXT) t-strlist$(EXEEXT) t-name-value$(EXEEXT) \ + t-ccparray$(EXEEXT) t-recsel$(EXEEXT) t-w32-cmdline$(EXEEXT) \ + $(am__EXEEXT_1) $(am__EXEEXT_2) +@MAINTAINER_MODE_TRUE@am__EXEEXT_4 = t-helpfile$(EXEEXT) \ +@MAINTAINER_MODE_TRUE@ t-b64$(EXEEXT) +PROGRAMS = $(noinst_PROGRAMS) +LIBRARIES = $(noinst_LIBRARIES) +ARFLAGS = cru +AM_V_AR = $(am__v_AR_@AM_V@) +am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@) +am__v_AR_0 = @echo " AR " $@; +am__v_AR_1 = +libcommon_a_AR = $(AR) $(ARFLAGS) +libcommon_a_LIBADD = +am__libcommon_a_SOURCES_DIST = common-defs.h util.h utilproto.h \ + fwddecl.h i18n.c i18n.h types.h host2net.h dynload.h w32help.h \ + mapstrings.c stringhelp.c stringhelp.h strlist.c strlist.h \ + utf8conv.c utf8conv.h argparse.c argparse.h logging.c \ + logging.h dotlock.c dotlock.h mischelp.c mischelp.h status.c \ + status.h shareddefs.h openpgpdefs.h gc-opt-flags.h \ + sexp-parse.h tlv.c tlv.h tlv-builder.c init.c init.h \ + sexputil.c sysutils.c sysutils.h homedir.c gettime.c gettime.h \ + yesno.c b64enc.c b64dec.c zb32.c zb32.h convert.c percent.c \ + mbox-util.c mbox-util.h miscellaneous.c xasprintf.c \ + xreadline.c membuf.c membuf.h ccparray.c ccparray.h iobuf.c \ + iobuf.h ttyio.c ttyio.h asshelp.c asshelp2.c asshelp.h \ + exechelp.h signal.c audit.c audit.h localename.c session-env.c \ + session-env.h userids.c userids.h openpgp-oid.c ssh-utils.c \ + ssh-utils.h agent-opt.c helpfile.c mkdir_p.c mkdir_p.h \ + exectool.c exectool.h server-help.c server-help.h name-value.c \ + name-value.h recsel.c recsel.h ksba-io-support.c \ + ksba-io-support.h openpgp-fpr.c compliance.c compliance.h \ + w32-reg.c w32-cmdline.c exechelp-w32ce.c exechelp-w32.c \ + exechelp-posix.c get-passphrase.c get-passphrase.h +@HAVE_W32_SYSTEM_TRUE@am__objects_1 = libcommon_a-w32-reg.$(OBJEXT) \ +@HAVE_W32_SYSTEM_TRUE@ libcommon_a-w32-cmdline.$(OBJEXT) +@HAVE_W32CE_SYSTEM_TRUE@@HAVE_W32_SYSTEM_TRUE@am__objects_2 = libcommon_a-exechelp-w32ce.$(OBJEXT) +@HAVE_W32CE_SYSTEM_FALSE@@HAVE_W32_SYSTEM_TRUE@am__objects_3 = libcommon_a-exechelp-w32.$(OBJEXT) +@HAVE_W32_SYSTEM_FALSE@am__objects_4 = \ +@HAVE_W32_SYSTEM_FALSE@ libcommon_a-exechelp-posix.$(OBJEXT) +am__objects_5 = libcommon_a-i18n.$(OBJEXT) \ + libcommon_a-mapstrings.$(OBJEXT) \ + libcommon_a-stringhelp.$(OBJEXT) libcommon_a-strlist.$(OBJEXT) \ + libcommon_a-utf8conv.$(OBJEXT) libcommon_a-argparse.$(OBJEXT) \ + libcommon_a-logging.$(OBJEXT) libcommon_a-dotlock.$(OBJEXT) \ + libcommon_a-mischelp.$(OBJEXT) libcommon_a-status.$(OBJEXT) \ + libcommon_a-tlv.$(OBJEXT) libcommon_a-tlv-builder.$(OBJEXT) \ + libcommon_a-init.$(OBJEXT) libcommon_a-sexputil.$(OBJEXT) \ + libcommon_a-sysutils.$(OBJEXT) libcommon_a-homedir.$(OBJEXT) \ + libcommon_a-gettime.$(OBJEXT) libcommon_a-yesno.$(OBJEXT) \ + libcommon_a-b64enc.$(OBJEXT) libcommon_a-b64dec.$(OBJEXT) \ + libcommon_a-zb32.$(OBJEXT) libcommon_a-convert.$(OBJEXT) \ + libcommon_a-percent.$(OBJEXT) libcommon_a-mbox-util.$(OBJEXT) \ + libcommon_a-miscellaneous.$(OBJEXT) \ + libcommon_a-xasprintf.$(OBJEXT) \ + libcommon_a-xreadline.$(OBJEXT) libcommon_a-membuf.$(OBJEXT) \ + libcommon_a-ccparray.$(OBJEXT) libcommon_a-iobuf.$(OBJEXT) \ + libcommon_a-ttyio.$(OBJEXT) libcommon_a-asshelp.$(OBJEXT) \ + libcommon_a-asshelp2.$(OBJEXT) libcommon_a-signal.$(OBJEXT) \ + libcommon_a-audit.$(OBJEXT) libcommon_a-localename.$(OBJEXT) \ + libcommon_a-session-env.$(OBJEXT) \ + libcommon_a-userids.$(OBJEXT) \ + libcommon_a-openpgp-oid.$(OBJEXT) \ + libcommon_a-ssh-utils.$(OBJEXT) \ + libcommon_a-agent-opt.$(OBJEXT) libcommon_a-helpfile.$(OBJEXT) \ + libcommon_a-mkdir_p.$(OBJEXT) libcommon_a-strlist.$(OBJEXT) \ + libcommon_a-exectool.$(OBJEXT) \ + libcommon_a-server-help.$(OBJEXT) \ + libcommon_a-name-value.$(OBJEXT) libcommon_a-recsel.$(OBJEXT) \ + libcommon_a-ksba-io-support.$(OBJEXT) \ + libcommon_a-openpgp-fpr.$(OBJEXT) \ + libcommon_a-compliance.$(OBJEXT) $(am__objects_1) \ + $(am__objects_2) $(am__objects_3) $(am__objects_4) +am__objects_6 = libcommon_a-get-passphrase.$(OBJEXT) +am_libcommon_a_OBJECTS = $(am__objects_5) $(am__objects_6) +libcommon_a_OBJECTS = $(am_libcommon_a_OBJECTS) +libcommonpth_a_AR = $(AR) $(ARFLAGS) +libcommonpth_a_LIBADD = +am__libcommonpth_a_SOURCES_DIST = common-defs.h util.h utilproto.h \ + fwddecl.h i18n.c i18n.h types.h host2net.h dynload.h w32help.h \ + mapstrings.c stringhelp.c stringhelp.h strlist.c strlist.h \ + utf8conv.c utf8conv.h argparse.c argparse.h logging.c \ + logging.h dotlock.c dotlock.h mischelp.c mischelp.h status.c \ + status.h shareddefs.h openpgpdefs.h gc-opt-flags.h \ + sexp-parse.h tlv.c tlv.h tlv-builder.c init.c init.h \ + sexputil.c sysutils.c sysutils.h homedir.c gettime.c gettime.h \ + yesno.c b64enc.c b64dec.c zb32.c zb32.h convert.c percent.c \ + mbox-util.c mbox-util.h miscellaneous.c xasprintf.c \ + xreadline.c membuf.c membuf.h ccparray.c ccparray.h iobuf.c \ + iobuf.h ttyio.c ttyio.h asshelp.c asshelp2.c asshelp.h \ + exechelp.h signal.c audit.c audit.h localename.c session-env.c \ + session-env.h userids.c userids.h openpgp-oid.c ssh-utils.c \ + ssh-utils.h agent-opt.c helpfile.c mkdir_p.c mkdir_p.h \ + exectool.c exectool.h server-help.c server-help.h name-value.c \ + name-value.h recsel.c recsel.h ksba-io-support.c \ + ksba-io-support.h openpgp-fpr.c compliance.c compliance.h \ + w32-reg.c w32-cmdline.c exechelp-w32ce.c exechelp-w32.c \ + exechelp-posix.c call-gpg.c call-gpg.h +@HAVE_W32_SYSTEM_TRUE@am__objects_7 = \ +@HAVE_W32_SYSTEM_TRUE@ libcommonpth_a-w32-reg.$(OBJEXT) \ +@HAVE_W32_SYSTEM_TRUE@ libcommonpth_a-w32-cmdline.$(OBJEXT) +@HAVE_W32CE_SYSTEM_TRUE@@HAVE_W32_SYSTEM_TRUE@am__objects_8 = libcommonpth_a-exechelp-w32ce.$(OBJEXT) +@HAVE_W32CE_SYSTEM_FALSE@@HAVE_W32_SYSTEM_TRUE@am__objects_9 = libcommonpth_a-exechelp-w32.$(OBJEXT) +@HAVE_W32_SYSTEM_FALSE@am__objects_10 = libcommonpth_a-exechelp-posix.$(OBJEXT) +am__objects_11 = libcommonpth_a-i18n.$(OBJEXT) \ + libcommonpth_a-mapstrings.$(OBJEXT) \ + libcommonpth_a-stringhelp.$(OBJEXT) \ + libcommonpth_a-strlist.$(OBJEXT) \ + libcommonpth_a-utf8conv.$(OBJEXT) \ + libcommonpth_a-argparse.$(OBJEXT) \ + libcommonpth_a-logging.$(OBJEXT) \ + libcommonpth_a-dotlock.$(OBJEXT) \ + libcommonpth_a-mischelp.$(OBJEXT) \ + libcommonpth_a-status.$(OBJEXT) libcommonpth_a-tlv.$(OBJEXT) \ + libcommonpth_a-tlv-builder.$(OBJEXT) \ + libcommonpth_a-init.$(OBJEXT) \ + libcommonpth_a-sexputil.$(OBJEXT) \ + libcommonpth_a-sysutils.$(OBJEXT) \ + libcommonpth_a-homedir.$(OBJEXT) \ + libcommonpth_a-gettime.$(OBJEXT) \ + libcommonpth_a-yesno.$(OBJEXT) libcommonpth_a-b64enc.$(OBJEXT) \ + libcommonpth_a-b64dec.$(OBJEXT) libcommonpth_a-zb32.$(OBJEXT) \ + libcommonpth_a-convert.$(OBJEXT) \ + libcommonpth_a-percent.$(OBJEXT) \ + libcommonpth_a-mbox-util.$(OBJEXT) \ + libcommonpth_a-miscellaneous.$(OBJEXT) \ + libcommonpth_a-xasprintf.$(OBJEXT) \ + libcommonpth_a-xreadline.$(OBJEXT) \ + libcommonpth_a-membuf.$(OBJEXT) \ + libcommonpth_a-ccparray.$(OBJEXT) \ + libcommonpth_a-iobuf.$(OBJEXT) libcommonpth_a-ttyio.$(OBJEXT) \ + libcommonpth_a-asshelp.$(OBJEXT) \ + libcommonpth_a-asshelp2.$(OBJEXT) \ + libcommonpth_a-signal.$(OBJEXT) libcommonpth_a-audit.$(OBJEXT) \ + libcommonpth_a-localename.$(OBJEXT) \ + libcommonpth_a-session-env.$(OBJEXT) \ + libcommonpth_a-userids.$(OBJEXT) \ + libcommonpth_a-openpgp-oid.$(OBJEXT) \ + libcommonpth_a-ssh-utils.$(OBJEXT) \ + libcommonpth_a-agent-opt.$(OBJEXT) \ + libcommonpth_a-helpfile.$(OBJEXT) \ + libcommonpth_a-mkdir_p.$(OBJEXT) \ + libcommonpth_a-strlist.$(OBJEXT) \ + libcommonpth_a-exectool.$(OBJEXT) \ + libcommonpth_a-server-help.$(OBJEXT) \ + libcommonpth_a-name-value.$(OBJEXT) \ + libcommonpth_a-recsel.$(OBJEXT) \ + libcommonpth_a-ksba-io-support.$(OBJEXT) \ + libcommonpth_a-openpgp-fpr.$(OBJEXT) \ + libcommonpth_a-compliance.$(OBJEXT) $(am__objects_7) \ + $(am__objects_8) $(am__objects_9) $(am__objects_10) +am__objects_12 = libcommonpth_a-call-gpg.$(OBJEXT) +am_libcommonpth_a_OBJECTS = $(am__objects_11) $(am__objects_12) +libcommonpth_a_OBJECTS = $(am_libcommonpth_a_OBJECTS) +libgpgrl_a_AR = $(AR) $(ARFLAGS) +libgpgrl_a_LIBADD = +am_libgpgrl_a_OBJECTS = gpgrlhelp.$(OBJEXT) +libgpgrl_a_OBJECTS = $(am_libgpgrl_a_OBJECTS) +libsimple_pwquery_a_AR = $(AR) $(ARFLAGS) +libsimple_pwquery_a_LIBADD = +am__libsimple_pwquery_a_SOURCES_DIST = simple-pwquery.c \ + simple-pwquery.h asshelp.c asshelp.h +@HAVE_W32CE_SYSTEM_FALSE@am_libsimple_pwquery_a_OBJECTS = libsimple_pwquery_a-simple-pwquery.$(OBJEXT) \ +@HAVE_W32CE_SYSTEM_FALSE@ libsimple_pwquery_a-asshelp.$(OBJEXT) +libsimple_pwquery_a_OBJECTS = $(am_libsimple_pwquery_a_OBJECTS) +t_b64_SOURCES = t-b64.c +t_b64_OBJECTS = t-b64.$(OBJEXT) +am__DEPENDENCIES_1 = +am__DEPENDENCIES_2 = libcommon.a $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) +t_b64_DEPENDENCIES = $(am__DEPENDENCIES_2) +t_ccparray_SOURCES = t-ccparray.c +t_ccparray_OBJECTS = t-ccparray.$(OBJEXT) +t_ccparray_DEPENDENCIES = $(am__DEPENDENCIES_2) +t_convert_SOURCES = t-convert.c +t_convert_OBJECTS = t-convert.$(OBJEXT) +t_convert_DEPENDENCIES = $(am__DEPENDENCIES_2) +t_exechelp_SOURCES = t-exechelp.c +t_exechelp_OBJECTS = t-exechelp.$(OBJEXT) +t_exechelp_DEPENDENCIES = $(am__DEPENDENCIES_2) +t_exectool_SOURCES = t-exectool.c +t_exectool_OBJECTS = t-exectool.$(OBJEXT) +t_exectool_DEPENDENCIES = $(am__DEPENDENCIES_2) +t_gettime_SOURCES = t-gettime.c +t_gettime_OBJECTS = t-gettime.$(OBJEXT) +t_gettime_DEPENDENCIES = $(am__DEPENDENCIES_2) +t_helpfile_SOURCES = t-helpfile.c +t_helpfile_OBJECTS = t-helpfile.$(OBJEXT) +t_helpfile_DEPENDENCIES = $(am__DEPENDENCIES_2) +t_iobuf_SOURCES = t-iobuf.c +t_iobuf_OBJECTS = t-iobuf.$(OBJEXT) +t_iobuf_DEPENDENCIES = $(am__DEPENDENCIES_2) +t_mapstrings_SOURCES = t-mapstrings.c +t_mapstrings_OBJECTS = t-mapstrings.$(OBJEXT) +t_mapstrings_DEPENDENCIES = $(am__DEPENDENCIES_2) +t_mbox_util_SOURCES = t-mbox-util.c +t_mbox_util_OBJECTS = t-mbox-util.$(OBJEXT) +t_mbox_util_DEPENDENCIES = $(am__DEPENDENCIES_2) +t_name_value_SOURCES = t-name-value.c +t_name_value_OBJECTS = t-name-value.$(OBJEXT) +t_name_value_DEPENDENCIES = $(am__DEPENDENCIES_2) +t_openpgp_oid_SOURCES = t-openpgp-oid.c +t_openpgp_oid_OBJECTS = t-openpgp-oid.$(OBJEXT) +t_openpgp_oid_DEPENDENCIES = $(am__DEPENDENCIES_2) +t_percent_SOURCES = t-percent.c +t_percent_OBJECTS = t-percent.$(OBJEXT) +t_percent_DEPENDENCIES = $(am__DEPENDENCIES_2) +t_recsel_SOURCES = t-recsel.c +t_recsel_OBJECTS = t-recsel.$(OBJEXT) +t_recsel_DEPENDENCIES = $(am__DEPENDENCIES_2) +t_session_env_SOURCES = t-session-env.c +t_session_env_OBJECTS = t-session-env.$(OBJEXT) +t_session_env_DEPENDENCIES = $(am__DEPENDENCIES_2) +t_sexputil_SOURCES = t-sexputil.c +t_sexputil_OBJECTS = t-sexputil.$(OBJEXT) +t_sexputil_DEPENDENCIES = $(am__DEPENDENCIES_2) +t_ssh_utils_SOURCES = t-ssh-utils.c +t_ssh_utils_OBJECTS = t-ssh-utils.$(OBJEXT) +t_ssh_utils_DEPENDENCIES = $(am__DEPENDENCIES_2) +am__objects_13 = +am_t_stringhelp_OBJECTS = t-stringhelp.$(OBJEXT) $(am__objects_13) +t_stringhelp_OBJECTS = $(am_t_stringhelp_OBJECTS) +t_stringhelp_DEPENDENCIES = $(am__DEPENDENCIES_2) +t_strlist_SOURCES = t-strlist.c +t_strlist_OBJECTS = t-strlist.$(OBJEXT) +t_strlist_DEPENDENCIES = $(am__DEPENDENCIES_2) +t_sysutils_SOURCES = t-sysutils.c +t_sysutils_OBJECTS = t-sysutils.$(OBJEXT) +t_sysutils_DEPENDENCIES = $(am__DEPENDENCIES_2) +am_t_timestuff_OBJECTS = t-timestuff.$(OBJEXT) $(am__objects_13) +t_timestuff_OBJECTS = $(am_t_timestuff_OBJECTS) +t_timestuff_DEPENDENCIES = $(am__DEPENDENCIES_2) +am_t_w32_cmdline_OBJECTS = t-w32-cmdline.$(OBJEXT) \ + w32-cmdline.$(OBJEXT) $(am__objects_13) +t_w32_cmdline_OBJECTS = $(am_t_w32_cmdline_OBJECTS) +t_w32_cmdline_DEPENDENCIES = $(am__DEPENDENCIES_2) +am__t_w32_reg_SOURCES_DIST = t-w32-reg.c t-support.h +@HAVE_W32_SYSTEM_TRUE@am_t_w32_reg_OBJECTS = t-w32-reg.$(OBJEXT) \ +@HAVE_W32_SYSTEM_TRUE@ $(am__objects_13) +t_w32_reg_OBJECTS = $(am_t_w32_reg_OBJECTS) +@HAVE_W32_SYSTEM_TRUE@t_w32_reg_DEPENDENCIES = $(am__DEPENDENCIES_2) +am_t_zb32_OBJECTS = t-zb32.$(OBJEXT) $(am__objects_13) +t_zb32_OBJECTS = $(am_t_zb32_OBJECTS) +t_zb32_DEPENDENCIES = $(am__DEPENDENCIES_2) +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/gpgrlhelp.Po \ + ./$(DEPDIR)/libcommon_a-agent-opt.Po \ + ./$(DEPDIR)/libcommon_a-argparse.Po \ + ./$(DEPDIR)/libcommon_a-asshelp.Po \ + ./$(DEPDIR)/libcommon_a-asshelp2.Po \ + ./$(DEPDIR)/libcommon_a-audit.Po \ + ./$(DEPDIR)/libcommon_a-b64dec.Po \ + ./$(DEPDIR)/libcommon_a-b64enc.Po \ + ./$(DEPDIR)/libcommon_a-ccparray.Po \ + ./$(DEPDIR)/libcommon_a-compliance.Po \ + ./$(DEPDIR)/libcommon_a-convert.Po \ + ./$(DEPDIR)/libcommon_a-dotlock.Po \ + ./$(DEPDIR)/libcommon_a-exechelp-posix.Po \ + ./$(DEPDIR)/libcommon_a-exechelp-w32.Po \ + ./$(DEPDIR)/libcommon_a-exechelp-w32ce.Po \ + ./$(DEPDIR)/libcommon_a-exectool.Po \ + ./$(DEPDIR)/libcommon_a-get-passphrase.Po \ + ./$(DEPDIR)/libcommon_a-gettime.Po \ + ./$(DEPDIR)/libcommon_a-helpfile.Po \ + ./$(DEPDIR)/libcommon_a-homedir.Po \ + ./$(DEPDIR)/libcommon_a-i18n.Po \ + ./$(DEPDIR)/libcommon_a-init.Po \ + ./$(DEPDIR)/libcommon_a-iobuf.Po \ + ./$(DEPDIR)/libcommon_a-ksba-io-support.Po \ + ./$(DEPDIR)/libcommon_a-localename.Po \ + ./$(DEPDIR)/libcommon_a-logging.Po \ + ./$(DEPDIR)/libcommon_a-mapstrings.Po \ + ./$(DEPDIR)/libcommon_a-mbox-util.Po \ + ./$(DEPDIR)/libcommon_a-membuf.Po \ + ./$(DEPDIR)/libcommon_a-miscellaneous.Po \ + ./$(DEPDIR)/libcommon_a-mischelp.Po \ + ./$(DEPDIR)/libcommon_a-mkdir_p.Po \ + ./$(DEPDIR)/libcommon_a-name-value.Po \ + ./$(DEPDIR)/libcommon_a-openpgp-fpr.Po \ + ./$(DEPDIR)/libcommon_a-openpgp-oid.Po \ + ./$(DEPDIR)/libcommon_a-percent.Po \ + ./$(DEPDIR)/libcommon_a-recsel.Po \ + ./$(DEPDIR)/libcommon_a-server-help.Po \ + ./$(DEPDIR)/libcommon_a-session-env.Po \ + ./$(DEPDIR)/libcommon_a-sexputil.Po \ + ./$(DEPDIR)/libcommon_a-signal.Po \ + ./$(DEPDIR)/libcommon_a-ssh-utils.Po \ + ./$(DEPDIR)/libcommon_a-status.Po \ + ./$(DEPDIR)/libcommon_a-stringhelp.Po \ + ./$(DEPDIR)/libcommon_a-strlist.Po \ + ./$(DEPDIR)/libcommon_a-sysutils.Po \ + ./$(DEPDIR)/libcommon_a-tlv-builder.Po \ + ./$(DEPDIR)/libcommon_a-tlv.Po \ + ./$(DEPDIR)/libcommon_a-ttyio.Po \ + ./$(DEPDIR)/libcommon_a-userids.Po \ + ./$(DEPDIR)/libcommon_a-utf8conv.Po \ + ./$(DEPDIR)/libcommon_a-w32-cmdline.Po \ + ./$(DEPDIR)/libcommon_a-w32-reg.Po \ + ./$(DEPDIR)/libcommon_a-xasprintf.Po \ + ./$(DEPDIR)/libcommon_a-xreadline.Po \ + ./$(DEPDIR)/libcommon_a-yesno.Po \ + ./$(DEPDIR)/libcommon_a-zb32.Po \ + ./$(DEPDIR)/libcommonpth_a-agent-opt.Po \ + ./$(DEPDIR)/libcommonpth_a-argparse.Po \ + ./$(DEPDIR)/libcommonpth_a-asshelp.Po \ + ./$(DEPDIR)/libcommonpth_a-asshelp2.Po \ + ./$(DEPDIR)/libcommonpth_a-audit.Po \ + ./$(DEPDIR)/libcommonpth_a-b64dec.Po \ + ./$(DEPDIR)/libcommonpth_a-b64enc.Po \ + ./$(DEPDIR)/libcommonpth_a-call-gpg.Po \ + ./$(DEPDIR)/libcommonpth_a-ccparray.Po \ + ./$(DEPDIR)/libcommonpth_a-compliance.Po \ + ./$(DEPDIR)/libcommonpth_a-convert.Po \ + ./$(DEPDIR)/libcommonpth_a-dotlock.Po \ + ./$(DEPDIR)/libcommonpth_a-exechelp-posix.Po \ + ./$(DEPDIR)/libcommonpth_a-exechelp-w32.Po \ + ./$(DEPDIR)/libcommonpth_a-exechelp-w32ce.Po \ + ./$(DEPDIR)/libcommonpth_a-exectool.Po \ + ./$(DEPDIR)/libcommonpth_a-gettime.Po \ + ./$(DEPDIR)/libcommonpth_a-helpfile.Po \ + ./$(DEPDIR)/libcommonpth_a-homedir.Po \ + ./$(DEPDIR)/libcommonpth_a-i18n.Po \ + ./$(DEPDIR)/libcommonpth_a-init.Po \ + ./$(DEPDIR)/libcommonpth_a-iobuf.Po \ + ./$(DEPDIR)/libcommonpth_a-ksba-io-support.Po \ + ./$(DEPDIR)/libcommonpth_a-localename.Po \ + ./$(DEPDIR)/libcommonpth_a-logging.Po \ + ./$(DEPDIR)/libcommonpth_a-mapstrings.Po \ + ./$(DEPDIR)/libcommonpth_a-mbox-util.Po \ + ./$(DEPDIR)/libcommonpth_a-membuf.Po \ + ./$(DEPDIR)/libcommonpth_a-miscellaneous.Po \ + ./$(DEPDIR)/libcommonpth_a-mischelp.Po \ + ./$(DEPDIR)/libcommonpth_a-mkdir_p.Po \ + ./$(DEPDIR)/libcommonpth_a-name-value.Po \ + ./$(DEPDIR)/libcommonpth_a-openpgp-fpr.Po \ + ./$(DEPDIR)/libcommonpth_a-openpgp-oid.Po \ + ./$(DEPDIR)/libcommonpth_a-percent.Po \ + ./$(DEPDIR)/libcommonpth_a-recsel.Po \ + ./$(DEPDIR)/libcommonpth_a-server-help.Po \ + ./$(DEPDIR)/libcommonpth_a-session-env.Po \ + ./$(DEPDIR)/libcommonpth_a-sexputil.Po \ + ./$(DEPDIR)/libcommonpth_a-signal.Po \ + ./$(DEPDIR)/libcommonpth_a-ssh-utils.Po \ + ./$(DEPDIR)/libcommonpth_a-status.Po \ + ./$(DEPDIR)/libcommonpth_a-stringhelp.Po \ + ./$(DEPDIR)/libcommonpth_a-strlist.Po \ + ./$(DEPDIR)/libcommonpth_a-sysutils.Po \ + ./$(DEPDIR)/libcommonpth_a-tlv-builder.Po \ + ./$(DEPDIR)/libcommonpth_a-tlv.Po \ + ./$(DEPDIR)/libcommonpth_a-ttyio.Po \ + ./$(DEPDIR)/libcommonpth_a-userids.Po \ + ./$(DEPDIR)/libcommonpth_a-utf8conv.Po \ + ./$(DEPDIR)/libcommonpth_a-w32-cmdline.Po \ + ./$(DEPDIR)/libcommonpth_a-w32-reg.Po \ + ./$(DEPDIR)/libcommonpth_a-xasprintf.Po \ + ./$(DEPDIR)/libcommonpth_a-xreadline.Po \ + ./$(DEPDIR)/libcommonpth_a-yesno.Po \ + ./$(DEPDIR)/libcommonpth_a-zb32.Po \ + ./$(DEPDIR)/libsimple_pwquery_a-asshelp.Po \ + ./$(DEPDIR)/libsimple_pwquery_a-simple-pwquery.Po \ + ./$(DEPDIR)/t-b64.Po ./$(DEPDIR)/t-ccparray.Po \ + ./$(DEPDIR)/t-convert.Po ./$(DEPDIR)/t-exechelp.Po \ + ./$(DEPDIR)/t-exectool.Po ./$(DEPDIR)/t-gettime.Po \ + ./$(DEPDIR)/t-helpfile.Po ./$(DEPDIR)/t-iobuf.Po \ + ./$(DEPDIR)/t-mapstrings.Po ./$(DEPDIR)/t-mbox-util.Po \ + ./$(DEPDIR)/t-name-value.Po ./$(DEPDIR)/t-openpgp-oid.Po \ + ./$(DEPDIR)/t-percent.Po ./$(DEPDIR)/t-recsel.Po \ + ./$(DEPDIR)/t-session-env.Po ./$(DEPDIR)/t-sexputil.Po \ + ./$(DEPDIR)/t-ssh-utils.Po ./$(DEPDIR)/t-stringhelp.Po \ + ./$(DEPDIR)/t-strlist.Po ./$(DEPDIR)/t-sysutils.Po \ + ./$(DEPDIR)/t-timestuff.Po ./$(DEPDIR)/t-w32-cmdline.Po \ + ./$(DEPDIR)/t-w32-reg.Po ./$(DEPDIR)/t-zb32.Po \ + ./$(DEPDIR)/w32-cmdline.Po +am__mv = mv -f +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libcommon_a_SOURCES) $(libcommonpth_a_SOURCES) \ + $(libgpgrl_a_SOURCES) $(libsimple_pwquery_a_SOURCES) t-b64.c \ + t-ccparray.c t-convert.c t-exechelp.c t-exectool.c t-gettime.c \ + t-helpfile.c t-iobuf.c t-mapstrings.c t-mbox-util.c \ + t-name-value.c t-openpgp-oid.c t-percent.c t-recsel.c \ + t-session-env.c t-sexputil.c t-ssh-utils.c \ + $(t_stringhelp_SOURCES) t-strlist.c t-sysutils.c \ + $(t_timestuff_SOURCES) $(t_w32_cmdline_SOURCES) \ + $(t_w32_reg_SOURCES) $(t_zb32_SOURCES) +DIST_SOURCES = $(am__libcommon_a_SOURCES_DIST) \ + $(am__libcommonpth_a_SOURCES_DIST) $(libgpgrl_a_SOURCES) \ + $(am__libsimple_pwquery_a_SOURCES_DIST) t-b64.c t-ccparray.c \ + t-convert.c t-exechelp.c t-exectool.c t-gettime.c t-helpfile.c \ + t-iobuf.c t-mapstrings.c t-mbox-util.c t-name-value.c \ + t-openpgp-oid.c t-percent.c t-recsel.c t-session-env.c \ + t-sexputil.c t-ssh-utils.c $(t_stringhelp_SOURCES) t-strlist.c \ + t-sysutils.c $(t_timestuff_SOURCES) $(t_w32_cmdline_SOURCES) \ + $(am__t_w32_reg_SOURCES_DIST) $(t_zb32_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__tty_colors_dummy = \ + mgn= red= grn= lgn= blu= brg= std=; \ + am__color_tests=no +am__tty_colors = { \ + $(am__tty_colors_dummy); \ + if test "X$(AM_COLOR_TESTS)" = Xno; then \ + am__color_tests=no; \ + elif test "X$(AM_COLOR_TESTS)" = Xalways; then \ + am__color_tests=yes; \ + elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \ + am__color_tests=yes; \ + fi; \ + if test $$am__color_tests = yes; then \ + red='[0;31m'; \ + grn='[0;32m'; \ + lgn='[1;32m'; \ + blu='[1;34m'; \ + mgn='[0;35m'; \ + brg='[1m'; \ + std='[m'; \ + fi; \ +} +am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/w32info-rc.h.in \ + $(top_srcdir)/am/cmacros.am $(top_srcdir)/build-aux/depcomp \ + $(top_srcdir)/build-aux/mkinstalldirs README +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +AWK_HEX_NUMBER_OPTION = @AWK_HEX_NUMBER_OPTION@ +BUILD_FILEVERSION = @BUILD_FILEVERSION@ +BUILD_HOSTNAME = @BUILD_HOSTNAME@ +BUILD_INCLUDED_LIBINTL = @BUILD_INCLUDED_LIBINTL@ +BUILD_REVISION = @BUILD_REVISION@ +BUILD_TIMESTAMP = @BUILD_TIMESTAMP@ +BUILD_VERSION = @BUILD_VERSION@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CC_FOR_BUILD = @CC_FOR_BUILD@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DL_LIBS = @DL_LIBS@ +DNSLIBS = @DNSLIBS@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ENCFS = @ENCFS@ +EXEEXT = @EXEEXT@ +FUSERMOUNT = @FUSERMOUNT@ +GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@ +GMSGFMT = @GMSGFMT@ +GMSGFMT_015 = @GMSGFMT_015@ +GNUPG_AGENT_PGM = @GNUPG_AGENT_PGM@ +GNUPG_DIRMNGR_LDAP_PGM = @GNUPG_DIRMNGR_LDAP_PGM@ +GNUPG_DIRMNGR_PGM = @GNUPG_DIRMNGR_PGM@ +GNUPG_PINENTRY_PGM = @GNUPG_PINENTRY_PGM@ +GNUPG_PROTECT_TOOL_PGM = @GNUPG_PROTECT_TOOL_PGM@ +GNUPG_SCDAEMON_PGM = @GNUPG_SCDAEMON_PGM@ +GPGKEYS_LDAP = @GPGKEYS_LDAP@ +GPGRT_CONFIG = @GPGRT_CONFIG@ +GPG_ERROR_CFLAGS = @GPG_ERROR_CFLAGS@ +GPG_ERROR_CONFIG = @GPG_ERROR_CONFIG@ +GPG_ERROR_LIBS = @GPG_ERROR_LIBS@ +GPG_ERROR_MT_CFLAGS = @GPG_ERROR_MT_CFLAGS@ +GPG_ERROR_MT_LIBS = @GPG_ERROR_MT_LIBS@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INTLLIBS = @INTLLIBS@ +INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@ +KSBA_CFLAGS = @KSBA_CFLAGS@ +KSBA_CONFIG = @KSBA_CONFIG@ +KSBA_LIBS = @KSBA_LIBS@ +LBER_LIBS = @LBER_LIBS@ +LDAPLIBS = @LDAPLIBS@ +LDAP_CPPFLAGS = @LDAP_CPPFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBASSUAN_CFLAGS = @LIBASSUAN_CFLAGS@ +LIBASSUAN_CONFIG = @LIBASSUAN_CONFIG@ +LIBASSUAN_LIBS = @LIBASSUAN_LIBS@ +LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@ +LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@ +LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@ +LIBGNUTLS_CFLAGS = @LIBGNUTLS_CFLAGS@ +LIBGNUTLS_LIBS = @LIBGNUTLS_LIBS@ +LIBICONV = @LIBICONV@ +LIBINTL = @LIBINTL@ +LIBOBJS = @LIBOBJS@ +LIBREADLINE = @LIBREADLINE@ +LIBS = @LIBS@ +LIBUSB_CPPFLAGS = @LIBUSB_CPPFLAGS@ +LIBUSB_LIBS = @LIBUSB_LIBS@ +LIBUTIL_LIBS = @LIBUTIL_LIBS@ +LN_S = @LN_S@ +LTLIBICONV = @LTLIBICONV@ +LTLIBINTL = @LTLIBINTL@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +MSGFMT = @MSGFMT@ +MSGFMT_015 = @MSGFMT_015@ +MSGMERGE = @MSGMERGE@ +NETLIBS = @NETLIBS@ +NPTH_CFLAGS = @NPTH_CFLAGS@ +NPTH_CONFIG = @NPTH_CONFIG@ +NPTH_LIBS = @NPTH_LIBS@ +NTBTLS_CFLAGS = @NTBTLS_CFLAGS@ +NTBTLS_CONFIG = @NTBTLS_CONFIG@ +NTBTLS_LIBS = @NTBTLS_LIBS@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_GT = @PACKAGE_GT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +POSUB = @POSUB@ +RANLIB = @RANLIB@ +SENDMAIL = @SENDMAIL@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SHRED = @SHRED@ +SQLITE3_CFLAGS = @SQLITE3_CFLAGS@ +SQLITE3_LIBS = @SQLITE3_LIBS@ +STRIP = @STRIP@ +SYSROOT = @SYSROOT@ +SYS_SOCKET_H = @SYS_SOCKET_H@ +TAR = @TAR@ +USE_C99_CFLAGS = @USE_C99_CFLAGS@ +USE_INCLUDED_LIBINTL = @USE_INCLUDED_LIBINTL@ +USE_NLS = @USE_NLS@ +VERSION = @VERSION@ +W32SOCKLIBS = @W32SOCKLIBS@ +WINDRES = @WINDRES@ +XGETTEXT = @XGETTEXT@ +XGETTEXT_015 = @XGETTEXT_015@ +XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@ +YAT2M = @YAT2M@ +ZLIBS = @ZLIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = $(datadir)/locale +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +EXTRA_DIST = mkstrtable.awk exaudit.awk exstatus.awk ChangeLog-2011 \ + audit-events.h status-codes.h ChangeLog.jnlib \ + ChangeLog-2011.include w32info-rc.h.in gnupg.ico \ + all-tests.scm + +noinst_LIBRARIES = libcommon.a libcommonpth.a libgpgrl.a \ + $(am__append_1) +BUILT_SOURCES = audit-events.h status-codes.h +MAINTAINERCLEANFILES = audit-events.h status-codes.h + +# NB: AM_CFLAGS may also be used by tools running on the build +# platform to create source files. +AM_CPPFLAGS = -DLOCALEDIR=\"$(localedir)\" $(am__append_2) \ + $(am__append_3) $(am__append_4) $(am__append_5) \ + $(am__append_6) $(am__append_7) $(am__append_8) +AM_CFLAGS = $(LIBGCRYPT_CFLAGS) $(LIBASSUAN_CFLAGS) $(KSBA_CFLAGS) +@HAVE_W32CE_SYSTEM_FALSE@extra_sys_libs = + +# Under Windows we use LockFileEx. WindowsCE provides this only on +# the WindowsMobile 6 platform and thus we need to use the coredll6 +# import library. We also want to use a stacksize of 256k instead of +# the 2MB which is the default with cegcc. 256k is the largest stack +# we use with pth. +@HAVE_W32CE_SYSTEM_TRUE@extra_sys_libs = -lcoredll6 +@HAVE_W32CE_SYSTEM_FALSE@extra_bin_ldflags = +@HAVE_W32CE_SYSTEM_TRUE@extra_bin_ldflags = -Wl,--stack=0x40000 +resource_objs = + +# Convenience macros +libcommon = ../common/libcommon.a +libcommonpth = ../common/libcommonpth.a +libcommontls = ../common/libcommontls.a +libcommontlsnpth = ../common/libcommontlsnpth.a +common_sources = common-defs.h util.h utilproto.h fwddecl.h i18n.c \ + i18n.h types.h host2net.h dynload.h w32help.h mapstrings.c \ + stringhelp.c stringhelp.h strlist.c strlist.h utf8conv.c \ + utf8conv.h argparse.c argparse.h logging.c logging.h dotlock.c \ + dotlock.h mischelp.c mischelp.h status.c status.h shareddefs.h \ + openpgpdefs.h gc-opt-flags.h sexp-parse.h tlv.c tlv.h \ + tlv-builder.c init.c init.h sexputil.c sysutils.c sysutils.h \ + homedir.c gettime.c gettime.h yesno.c b64enc.c b64dec.c zb32.c \ + zb32.h convert.c percent.c mbox-util.c mbox-util.h \ + miscellaneous.c xasprintf.c xreadline.c membuf.c membuf.h \ + ccparray.c ccparray.h iobuf.c iobuf.h ttyio.c ttyio.h \ + asshelp.c asshelp2.c asshelp.h exechelp.h signal.c audit.c \ + audit.h localename.c session-env.c session-env.h userids.c \ + userids.h openpgp-oid.c ssh-utils.c ssh-utils.h agent-opt.c \ + helpfile.c mkdir_p.c mkdir_p.h strlist.c strlist.h exectool.c \ + exectool.h server-help.c server-help.h name-value.c \ + name-value.h recsel.c recsel.h ksba-io-support.c \ + ksba-io-support.h openpgp-fpr.c compliance.c compliance.h \ + $(am__append_9) $(am__append_10) $(am__append_11) \ + $(am__append_12) + +# Sources only useful without NPTH. +without_npth_sources = \ + get-passphrase.c get-passphrase.h + + +# Sources only useful with NPTH. +with_npth_sources = \ + call-gpg.c call-gpg.h + +libcommon_a_SOURCES = $(common_sources) $(without_npth_sources) +libcommon_a_CFLAGS = $(AM_CFLAGS) $(LIBASSUAN_CFLAGS) -DWITHOUT_NPTH=1 +libcommonpth_a_SOURCES = $(common_sources) $(with_npth_sources) +libcommonpth_a_CFLAGS = $(AM_CFLAGS) $(LIBASSUAN_CFLAGS) $(NPTH_CFLAGS) +@HAVE_W32CE_SYSTEM_FALSE@libsimple_pwquery_a_SOURCES = \ +@HAVE_W32CE_SYSTEM_FALSE@ simple-pwquery.c simple-pwquery.h asshelp.c asshelp.h + +@HAVE_W32CE_SYSTEM_FALSE@libsimple_pwquery_a_CFLAGS = $(AM_CFLAGS) $(LIBASSUAN_CFLAGS) +libgpgrl_a_SOURCES = \ + gpgrlhelp.c + + +# +# Module tests +# +module_tests = t-stringhelp t-timestuff t-convert t-percent t-gettime \ + t-sysutils t-sexputil t-session-env t-openpgp-oid t-ssh-utils \ + t-mapstrings t-zb32 t-mbox-util t-iobuf t-strlist t-name-value \ + t-ccparray t-recsel t-w32-cmdline $(am__append_13) \ + $(am__append_14) +@MAINTAINER_MODE_FALSE@module_maint_tests = +@MAINTAINER_MODE_TRUE@module_maint_tests = t-helpfile t-b64 +t_extra_src = t-support.h +t_common_cflags = $(KSBA_CFLAGS) $(LIBGCRYPT_CFLAGS) \ + $(LIBASSUAN_CFLAGS) $(GPG_ERROR_CFLAGS) $(INCICONV) + +t_common_ldadd = libcommon.a \ + $(LIBGCRYPT_LIBS) $(LIBASSUAN_LIBS) $(GPG_ERROR_LIBS) \ + $(LIBINTL) $(LIBICONV) $(NETLIBS) + + +# Common tests +t_stringhelp_SOURCES = t-stringhelp.c $(t_extra_src) +t_stringhelp_LDADD = $(t_common_ldadd) +t_timestuff_SOURCES = t-timestuff.c $(t_extra_src) +t_timestuff_LDADD = $(t_common_ldadd) +t_convert_LDADD = $(t_common_ldadd) +t_percent_LDADD = $(t_common_ldadd) +t_gettime_LDADD = $(t_common_ldadd) +t_sysutils_LDADD = $(t_common_ldadd) +t_helpfile_LDADD = $(t_common_ldadd) +t_sexputil_LDADD = $(t_common_ldadd) +t_b64_LDADD = $(t_common_ldadd) +t_exechelp_LDADD = $(t_common_ldadd) +t_exectool_LDADD = $(t_common_ldadd) +t_session_env_LDADD = $(t_common_ldadd) +t_openpgp_oid_LDADD = $(t_common_ldadd) +t_ssh_utils_LDADD = $(t_common_ldadd) +t_mapstrings_LDADD = $(t_common_ldadd) +t_zb32_SOURCES = t-zb32.c $(t_extra_src) +t_zb32_LDADD = $(t_common_ldadd) +t_mbox_util_LDADD = $(t_common_ldadd) +t_iobuf_LDADD = $(t_common_ldadd) +t_strlist_LDADD = $(t_common_ldadd) +t_name_value_LDADD = $(t_common_ldadd) +t_ccparray_LDADD = $(t_common_ldadd) +t_recsel_LDADD = $(t_common_ldadd) +t_w32_cmdline_SOURCES = t-w32-cmdline.c w32-cmdline.c $(t_extra_src) +t_w32_cmdline_LDADD = $(t_common_ldadd) + +# System specific test +@HAVE_W32_SYSTEM_TRUE@t_w32_reg_SOURCES = t-w32-reg.c $(t_extra_src) +@HAVE_W32_SYSTEM_TRUE@t_w32_reg_LDADD = $(t_common_ldadd) +all: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) all-am + +.SUFFIXES: +.SUFFIXES: .c .o .obj .rc +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/am/cmacros.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu common/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu common/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; +$(top_srcdir)/am/cmacros.am $(am__empty): + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +w32info-rc.h: $(top_builddir)/config.status $(srcdir)/w32info-rc.h.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ + +clean-noinstPROGRAMS: + -test -z "$(noinst_PROGRAMS)" || rm -f $(noinst_PROGRAMS) + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) + +libcommon.a: $(libcommon_a_OBJECTS) $(libcommon_a_DEPENDENCIES) $(EXTRA_libcommon_a_DEPENDENCIES) + $(AM_V_at)-rm -f libcommon.a + $(AM_V_AR)$(libcommon_a_AR) libcommon.a $(libcommon_a_OBJECTS) $(libcommon_a_LIBADD) + $(AM_V_at)$(RANLIB) libcommon.a + +libcommonpth.a: $(libcommonpth_a_OBJECTS) $(libcommonpth_a_DEPENDENCIES) $(EXTRA_libcommonpth_a_DEPENDENCIES) + $(AM_V_at)-rm -f libcommonpth.a + $(AM_V_AR)$(libcommonpth_a_AR) libcommonpth.a $(libcommonpth_a_OBJECTS) $(libcommonpth_a_LIBADD) + $(AM_V_at)$(RANLIB) libcommonpth.a + +libgpgrl.a: $(libgpgrl_a_OBJECTS) $(libgpgrl_a_DEPENDENCIES) $(EXTRA_libgpgrl_a_DEPENDENCIES) + $(AM_V_at)-rm -f libgpgrl.a + $(AM_V_AR)$(libgpgrl_a_AR) libgpgrl.a $(libgpgrl_a_OBJECTS) $(libgpgrl_a_LIBADD) + $(AM_V_at)$(RANLIB) libgpgrl.a + +libsimple-pwquery.a: $(libsimple_pwquery_a_OBJECTS) $(libsimple_pwquery_a_DEPENDENCIES) $(EXTRA_libsimple_pwquery_a_DEPENDENCIES) + $(AM_V_at)-rm -f libsimple-pwquery.a + $(AM_V_AR)$(libsimple_pwquery_a_AR) libsimple-pwquery.a $(libsimple_pwquery_a_OBJECTS) $(libsimple_pwquery_a_LIBADD) + $(AM_V_at)$(RANLIB) libsimple-pwquery.a + +t-b64$(EXEEXT): $(t_b64_OBJECTS) $(t_b64_DEPENDENCIES) $(EXTRA_t_b64_DEPENDENCIES) + @rm -f t-b64$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_b64_OBJECTS) $(t_b64_LDADD) $(LIBS) + +t-ccparray$(EXEEXT): $(t_ccparray_OBJECTS) $(t_ccparray_DEPENDENCIES) $(EXTRA_t_ccparray_DEPENDENCIES) + @rm -f t-ccparray$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_ccparray_OBJECTS) $(t_ccparray_LDADD) $(LIBS) + +t-convert$(EXEEXT): $(t_convert_OBJECTS) $(t_convert_DEPENDENCIES) $(EXTRA_t_convert_DEPENDENCIES) + @rm -f t-convert$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_convert_OBJECTS) $(t_convert_LDADD) $(LIBS) + +t-exechelp$(EXEEXT): $(t_exechelp_OBJECTS) $(t_exechelp_DEPENDENCIES) $(EXTRA_t_exechelp_DEPENDENCIES) + @rm -f t-exechelp$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_exechelp_OBJECTS) $(t_exechelp_LDADD) $(LIBS) + +t-exectool$(EXEEXT): $(t_exectool_OBJECTS) $(t_exectool_DEPENDENCIES) $(EXTRA_t_exectool_DEPENDENCIES) + @rm -f t-exectool$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_exectool_OBJECTS) $(t_exectool_LDADD) $(LIBS) + +t-gettime$(EXEEXT): $(t_gettime_OBJECTS) $(t_gettime_DEPENDENCIES) $(EXTRA_t_gettime_DEPENDENCIES) + @rm -f t-gettime$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_gettime_OBJECTS) $(t_gettime_LDADD) $(LIBS) + +t-helpfile$(EXEEXT): $(t_helpfile_OBJECTS) $(t_helpfile_DEPENDENCIES) $(EXTRA_t_helpfile_DEPENDENCIES) + @rm -f t-helpfile$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_helpfile_OBJECTS) $(t_helpfile_LDADD) $(LIBS) + +t-iobuf$(EXEEXT): $(t_iobuf_OBJECTS) $(t_iobuf_DEPENDENCIES) $(EXTRA_t_iobuf_DEPENDENCIES) + @rm -f t-iobuf$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_iobuf_OBJECTS) $(t_iobuf_LDADD) $(LIBS) + +t-mapstrings$(EXEEXT): $(t_mapstrings_OBJECTS) $(t_mapstrings_DEPENDENCIES) $(EXTRA_t_mapstrings_DEPENDENCIES) + @rm -f t-mapstrings$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_mapstrings_OBJECTS) $(t_mapstrings_LDADD) $(LIBS) + +t-mbox-util$(EXEEXT): $(t_mbox_util_OBJECTS) $(t_mbox_util_DEPENDENCIES) $(EXTRA_t_mbox_util_DEPENDENCIES) + @rm -f t-mbox-util$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_mbox_util_OBJECTS) $(t_mbox_util_LDADD) $(LIBS) + +t-name-value$(EXEEXT): $(t_name_value_OBJECTS) $(t_name_value_DEPENDENCIES) $(EXTRA_t_name_value_DEPENDENCIES) + @rm -f t-name-value$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_name_value_OBJECTS) $(t_name_value_LDADD) $(LIBS) + +t-openpgp-oid$(EXEEXT): $(t_openpgp_oid_OBJECTS) $(t_openpgp_oid_DEPENDENCIES) $(EXTRA_t_openpgp_oid_DEPENDENCIES) + @rm -f t-openpgp-oid$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_openpgp_oid_OBJECTS) $(t_openpgp_oid_LDADD) $(LIBS) + +t-percent$(EXEEXT): $(t_percent_OBJECTS) $(t_percent_DEPENDENCIES) $(EXTRA_t_percent_DEPENDENCIES) + @rm -f t-percent$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_percent_OBJECTS) $(t_percent_LDADD) $(LIBS) + +t-recsel$(EXEEXT): $(t_recsel_OBJECTS) $(t_recsel_DEPENDENCIES) $(EXTRA_t_recsel_DEPENDENCIES) + @rm -f t-recsel$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_recsel_OBJECTS) $(t_recsel_LDADD) $(LIBS) + +t-session-env$(EXEEXT): $(t_session_env_OBJECTS) $(t_session_env_DEPENDENCIES) $(EXTRA_t_session_env_DEPENDENCIES) + @rm -f t-session-env$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_session_env_OBJECTS) $(t_session_env_LDADD) $(LIBS) + +t-sexputil$(EXEEXT): $(t_sexputil_OBJECTS) $(t_sexputil_DEPENDENCIES) $(EXTRA_t_sexputil_DEPENDENCIES) + @rm -f t-sexputil$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_sexputil_OBJECTS) $(t_sexputil_LDADD) $(LIBS) + +t-ssh-utils$(EXEEXT): $(t_ssh_utils_OBJECTS) $(t_ssh_utils_DEPENDENCIES) $(EXTRA_t_ssh_utils_DEPENDENCIES) + @rm -f t-ssh-utils$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_ssh_utils_OBJECTS) $(t_ssh_utils_LDADD) $(LIBS) + +t-stringhelp$(EXEEXT): $(t_stringhelp_OBJECTS) $(t_stringhelp_DEPENDENCIES) $(EXTRA_t_stringhelp_DEPENDENCIES) + @rm -f t-stringhelp$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_stringhelp_OBJECTS) $(t_stringhelp_LDADD) $(LIBS) + +t-strlist$(EXEEXT): $(t_strlist_OBJECTS) $(t_strlist_DEPENDENCIES) $(EXTRA_t_strlist_DEPENDENCIES) + @rm -f t-strlist$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_strlist_OBJECTS) $(t_strlist_LDADD) $(LIBS) + +t-sysutils$(EXEEXT): $(t_sysutils_OBJECTS) $(t_sysutils_DEPENDENCIES) $(EXTRA_t_sysutils_DEPENDENCIES) + @rm -f t-sysutils$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_sysutils_OBJECTS) $(t_sysutils_LDADD) $(LIBS) + +t-timestuff$(EXEEXT): $(t_timestuff_OBJECTS) $(t_timestuff_DEPENDENCIES) $(EXTRA_t_timestuff_DEPENDENCIES) + @rm -f t-timestuff$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_timestuff_OBJECTS) $(t_timestuff_LDADD) $(LIBS) + +t-w32-cmdline$(EXEEXT): $(t_w32_cmdline_OBJECTS) $(t_w32_cmdline_DEPENDENCIES) $(EXTRA_t_w32_cmdline_DEPENDENCIES) + @rm -f t-w32-cmdline$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_w32_cmdline_OBJECTS) $(t_w32_cmdline_LDADD) $(LIBS) + +t-w32-reg$(EXEEXT): $(t_w32_reg_OBJECTS) $(t_w32_reg_DEPENDENCIES) $(EXTRA_t_w32_reg_DEPENDENCIES) + @rm -f t-w32-reg$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_w32_reg_OBJECTS) $(t_w32_reg_LDADD) $(LIBS) + +t-zb32$(EXEEXT): $(t_zb32_OBJECTS) $(t_zb32_DEPENDENCIES) $(EXTRA_t_zb32_DEPENDENCIES) + @rm -f t-zb32$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_zb32_OBJECTS) $(t_zb32_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gpgrlhelp.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-agent-opt.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-argparse.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-asshelp.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-asshelp2.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-audit.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-b64dec.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-b64enc.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-ccparray.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-compliance.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-convert.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-dotlock.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-exechelp-posix.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-exechelp-w32.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-exechelp-w32ce.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-exectool.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-get-passphrase.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-gettime.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-helpfile.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-homedir.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-i18n.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-init.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-iobuf.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-ksba-io-support.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-localename.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-logging.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-mapstrings.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-mbox-util.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-membuf.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-miscellaneous.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-mischelp.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-mkdir_p.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-name-value.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-openpgp-fpr.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-openpgp-oid.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-percent.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-recsel.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-server-help.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-session-env.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-sexputil.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-signal.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-ssh-utils.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-status.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-stringhelp.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-strlist.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-sysutils.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-tlv-builder.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-tlv.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-ttyio.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-userids.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-utf8conv.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-w32-cmdline.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-w32-reg.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-xasprintf.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-xreadline.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-yesno.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommon_a-zb32.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-agent-opt.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-argparse.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-asshelp.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-asshelp2.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-audit.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-b64dec.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-b64enc.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-call-gpg.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-ccparray.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-compliance.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-convert.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-dotlock.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-exechelp-posix.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-exechelp-w32.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-exechelp-w32ce.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-exectool.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-gettime.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-helpfile.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-homedir.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-i18n.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-init.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-iobuf.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-ksba-io-support.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-localename.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-logging.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-mapstrings.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-mbox-util.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-membuf.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-miscellaneous.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-mischelp.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-mkdir_p.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-name-value.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-openpgp-fpr.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-openpgp-oid.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-percent.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-recsel.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-server-help.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-session-env.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-sexputil.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-signal.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-ssh-utils.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-status.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-stringhelp.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-strlist.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-sysutils.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-tlv-builder.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-tlv.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-ttyio.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-userids.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-utf8conv.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-w32-cmdline.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-w32-reg.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-xasprintf.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-xreadline.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-yesno.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcommonpth_a-zb32.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsimple_pwquery_a-asshelp.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libsimple_pwquery_a-simple-pwquery.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-b64.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-ccparray.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-convert.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-exechelp.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-exectool.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-gettime.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-helpfile.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-iobuf.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-mapstrings.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-mbox-util.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-name-value.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-openpgp-oid.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-percent.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-recsel.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-session-env.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-sexputil.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-ssh-utils.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-stringhelp.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-strlist.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-sysutils.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-timestuff.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-w32-cmdline.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-w32-reg.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t-zb32.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/w32-cmdline.Po@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +libcommon_a-i18n.o: i18n.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-i18n.o -MD -MP -MF $(DEPDIR)/libcommon_a-i18n.Tpo -c -o libcommon_a-i18n.o `test -f 'i18n.c' || echo '$(srcdir)/'`i18n.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-i18n.Tpo $(DEPDIR)/libcommon_a-i18n.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='i18n.c' object='libcommon_a-i18n.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-i18n.o `test -f 'i18n.c' || echo '$(srcdir)/'`i18n.c + +libcommon_a-i18n.obj: i18n.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-i18n.obj -MD -MP -MF $(DEPDIR)/libcommon_a-i18n.Tpo -c -o libcommon_a-i18n.obj `if test -f 'i18n.c'; then $(CYGPATH_W) 'i18n.c'; else $(CYGPATH_W) '$(srcdir)/i18n.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-i18n.Tpo $(DEPDIR)/libcommon_a-i18n.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='i18n.c' object='libcommon_a-i18n.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-i18n.obj `if test -f 'i18n.c'; then $(CYGPATH_W) 'i18n.c'; else $(CYGPATH_W) '$(srcdir)/i18n.c'; fi` + +libcommon_a-mapstrings.o: mapstrings.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-mapstrings.o -MD -MP -MF $(DEPDIR)/libcommon_a-mapstrings.Tpo -c -o libcommon_a-mapstrings.o `test -f 'mapstrings.c' || echo '$(srcdir)/'`mapstrings.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-mapstrings.Tpo $(DEPDIR)/libcommon_a-mapstrings.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mapstrings.c' object='libcommon_a-mapstrings.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-mapstrings.o `test -f 'mapstrings.c' || echo '$(srcdir)/'`mapstrings.c + +libcommon_a-mapstrings.obj: mapstrings.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-mapstrings.obj -MD -MP -MF $(DEPDIR)/libcommon_a-mapstrings.Tpo -c -o libcommon_a-mapstrings.obj `if test -f 'mapstrings.c'; then $(CYGPATH_W) 'mapstrings.c'; else $(CYGPATH_W) '$(srcdir)/mapstrings.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-mapstrings.Tpo $(DEPDIR)/libcommon_a-mapstrings.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mapstrings.c' object='libcommon_a-mapstrings.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-mapstrings.obj `if test -f 'mapstrings.c'; then $(CYGPATH_W) 'mapstrings.c'; else $(CYGPATH_W) '$(srcdir)/mapstrings.c'; fi` + +libcommon_a-stringhelp.o: stringhelp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-stringhelp.o -MD -MP -MF $(DEPDIR)/libcommon_a-stringhelp.Tpo -c -o libcommon_a-stringhelp.o `test -f 'stringhelp.c' || echo '$(srcdir)/'`stringhelp.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-stringhelp.Tpo $(DEPDIR)/libcommon_a-stringhelp.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='stringhelp.c' object='libcommon_a-stringhelp.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-stringhelp.o `test -f 'stringhelp.c' || echo '$(srcdir)/'`stringhelp.c + +libcommon_a-stringhelp.obj: stringhelp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-stringhelp.obj -MD -MP -MF $(DEPDIR)/libcommon_a-stringhelp.Tpo -c -o libcommon_a-stringhelp.obj `if test -f 'stringhelp.c'; then $(CYGPATH_W) 'stringhelp.c'; else $(CYGPATH_W) '$(srcdir)/stringhelp.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-stringhelp.Tpo $(DEPDIR)/libcommon_a-stringhelp.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='stringhelp.c' object='libcommon_a-stringhelp.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-stringhelp.obj `if test -f 'stringhelp.c'; then $(CYGPATH_W) 'stringhelp.c'; else $(CYGPATH_W) '$(srcdir)/stringhelp.c'; fi` + +libcommon_a-strlist.o: strlist.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-strlist.o -MD -MP -MF $(DEPDIR)/libcommon_a-strlist.Tpo -c -o libcommon_a-strlist.o `test -f 'strlist.c' || echo '$(srcdir)/'`strlist.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-strlist.Tpo $(DEPDIR)/libcommon_a-strlist.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strlist.c' object='libcommon_a-strlist.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-strlist.o `test -f 'strlist.c' || echo '$(srcdir)/'`strlist.c + +libcommon_a-strlist.obj: strlist.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-strlist.obj -MD -MP -MF $(DEPDIR)/libcommon_a-strlist.Tpo -c -o libcommon_a-strlist.obj `if test -f 'strlist.c'; then $(CYGPATH_W) 'strlist.c'; else $(CYGPATH_W) '$(srcdir)/strlist.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-strlist.Tpo $(DEPDIR)/libcommon_a-strlist.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strlist.c' object='libcommon_a-strlist.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-strlist.obj `if test -f 'strlist.c'; then $(CYGPATH_W) 'strlist.c'; else $(CYGPATH_W) '$(srcdir)/strlist.c'; fi` + +libcommon_a-utf8conv.o: utf8conv.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-utf8conv.o -MD -MP -MF $(DEPDIR)/libcommon_a-utf8conv.Tpo -c -o libcommon_a-utf8conv.o `test -f 'utf8conv.c' || echo '$(srcdir)/'`utf8conv.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-utf8conv.Tpo $(DEPDIR)/libcommon_a-utf8conv.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utf8conv.c' object='libcommon_a-utf8conv.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-utf8conv.o `test -f 'utf8conv.c' || echo '$(srcdir)/'`utf8conv.c + +libcommon_a-utf8conv.obj: utf8conv.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-utf8conv.obj -MD -MP -MF $(DEPDIR)/libcommon_a-utf8conv.Tpo -c -o libcommon_a-utf8conv.obj `if test -f 'utf8conv.c'; then $(CYGPATH_W) 'utf8conv.c'; else $(CYGPATH_W) '$(srcdir)/utf8conv.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-utf8conv.Tpo $(DEPDIR)/libcommon_a-utf8conv.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utf8conv.c' object='libcommon_a-utf8conv.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-utf8conv.obj `if test -f 'utf8conv.c'; then $(CYGPATH_W) 'utf8conv.c'; else $(CYGPATH_W) '$(srcdir)/utf8conv.c'; fi` + +libcommon_a-argparse.o: argparse.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-argparse.o -MD -MP -MF $(DEPDIR)/libcommon_a-argparse.Tpo -c -o libcommon_a-argparse.o `test -f 'argparse.c' || echo '$(srcdir)/'`argparse.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-argparse.Tpo $(DEPDIR)/libcommon_a-argparse.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='argparse.c' object='libcommon_a-argparse.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-argparse.o `test -f 'argparse.c' || echo '$(srcdir)/'`argparse.c + +libcommon_a-argparse.obj: argparse.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-argparse.obj -MD -MP -MF $(DEPDIR)/libcommon_a-argparse.Tpo -c -o libcommon_a-argparse.obj `if test -f 'argparse.c'; then $(CYGPATH_W) 'argparse.c'; else $(CYGPATH_W) '$(srcdir)/argparse.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-argparse.Tpo $(DEPDIR)/libcommon_a-argparse.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='argparse.c' object='libcommon_a-argparse.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-argparse.obj `if test -f 'argparse.c'; then $(CYGPATH_W) 'argparse.c'; else $(CYGPATH_W) '$(srcdir)/argparse.c'; fi` + +libcommon_a-logging.o: logging.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-logging.o -MD -MP -MF $(DEPDIR)/libcommon_a-logging.Tpo -c -o libcommon_a-logging.o `test -f 'logging.c' || echo '$(srcdir)/'`logging.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-logging.Tpo $(DEPDIR)/libcommon_a-logging.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='logging.c' object='libcommon_a-logging.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-logging.o `test -f 'logging.c' || echo '$(srcdir)/'`logging.c + +libcommon_a-logging.obj: logging.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-logging.obj -MD -MP -MF $(DEPDIR)/libcommon_a-logging.Tpo -c -o libcommon_a-logging.obj `if test -f 'logging.c'; then $(CYGPATH_W) 'logging.c'; else $(CYGPATH_W) '$(srcdir)/logging.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-logging.Tpo $(DEPDIR)/libcommon_a-logging.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='logging.c' object='libcommon_a-logging.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-logging.obj `if test -f 'logging.c'; then $(CYGPATH_W) 'logging.c'; else $(CYGPATH_W) '$(srcdir)/logging.c'; fi` + +libcommon_a-dotlock.o: dotlock.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-dotlock.o -MD -MP -MF $(DEPDIR)/libcommon_a-dotlock.Tpo -c -o libcommon_a-dotlock.o `test -f 'dotlock.c' || echo '$(srcdir)/'`dotlock.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-dotlock.Tpo $(DEPDIR)/libcommon_a-dotlock.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dotlock.c' object='libcommon_a-dotlock.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-dotlock.o `test -f 'dotlock.c' || echo '$(srcdir)/'`dotlock.c + +libcommon_a-dotlock.obj: dotlock.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-dotlock.obj -MD -MP -MF $(DEPDIR)/libcommon_a-dotlock.Tpo -c -o libcommon_a-dotlock.obj `if test -f 'dotlock.c'; then $(CYGPATH_W) 'dotlock.c'; else $(CYGPATH_W) '$(srcdir)/dotlock.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-dotlock.Tpo $(DEPDIR)/libcommon_a-dotlock.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dotlock.c' object='libcommon_a-dotlock.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-dotlock.obj `if test -f 'dotlock.c'; then $(CYGPATH_W) 'dotlock.c'; else $(CYGPATH_W) '$(srcdir)/dotlock.c'; fi` + +libcommon_a-mischelp.o: mischelp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-mischelp.o -MD -MP -MF $(DEPDIR)/libcommon_a-mischelp.Tpo -c -o libcommon_a-mischelp.o `test -f 'mischelp.c' || echo '$(srcdir)/'`mischelp.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-mischelp.Tpo $(DEPDIR)/libcommon_a-mischelp.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mischelp.c' object='libcommon_a-mischelp.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-mischelp.o `test -f 'mischelp.c' || echo '$(srcdir)/'`mischelp.c + +libcommon_a-mischelp.obj: mischelp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-mischelp.obj -MD -MP -MF $(DEPDIR)/libcommon_a-mischelp.Tpo -c -o libcommon_a-mischelp.obj `if test -f 'mischelp.c'; then $(CYGPATH_W) 'mischelp.c'; else $(CYGPATH_W) '$(srcdir)/mischelp.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-mischelp.Tpo $(DEPDIR)/libcommon_a-mischelp.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mischelp.c' object='libcommon_a-mischelp.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-mischelp.obj `if test -f 'mischelp.c'; then $(CYGPATH_W) 'mischelp.c'; else $(CYGPATH_W) '$(srcdir)/mischelp.c'; fi` + +libcommon_a-status.o: status.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-status.o -MD -MP -MF $(DEPDIR)/libcommon_a-status.Tpo -c -o libcommon_a-status.o `test -f 'status.c' || echo '$(srcdir)/'`status.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-status.Tpo $(DEPDIR)/libcommon_a-status.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='status.c' object='libcommon_a-status.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-status.o `test -f 'status.c' || echo '$(srcdir)/'`status.c + +libcommon_a-status.obj: status.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-status.obj -MD -MP -MF $(DEPDIR)/libcommon_a-status.Tpo -c -o libcommon_a-status.obj `if test -f 'status.c'; then $(CYGPATH_W) 'status.c'; else $(CYGPATH_W) '$(srcdir)/status.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-status.Tpo $(DEPDIR)/libcommon_a-status.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='status.c' object='libcommon_a-status.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-status.obj `if test -f 'status.c'; then $(CYGPATH_W) 'status.c'; else $(CYGPATH_W) '$(srcdir)/status.c'; fi` + +libcommon_a-tlv.o: tlv.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-tlv.o -MD -MP -MF $(DEPDIR)/libcommon_a-tlv.Tpo -c -o libcommon_a-tlv.o `test -f 'tlv.c' || echo '$(srcdir)/'`tlv.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-tlv.Tpo $(DEPDIR)/libcommon_a-tlv.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tlv.c' object='libcommon_a-tlv.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-tlv.o `test -f 'tlv.c' || echo '$(srcdir)/'`tlv.c + +libcommon_a-tlv.obj: tlv.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-tlv.obj -MD -MP -MF $(DEPDIR)/libcommon_a-tlv.Tpo -c -o libcommon_a-tlv.obj `if test -f 'tlv.c'; then $(CYGPATH_W) 'tlv.c'; else $(CYGPATH_W) '$(srcdir)/tlv.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-tlv.Tpo $(DEPDIR)/libcommon_a-tlv.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tlv.c' object='libcommon_a-tlv.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-tlv.obj `if test -f 'tlv.c'; then $(CYGPATH_W) 'tlv.c'; else $(CYGPATH_W) '$(srcdir)/tlv.c'; fi` + +libcommon_a-tlv-builder.o: tlv-builder.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-tlv-builder.o -MD -MP -MF $(DEPDIR)/libcommon_a-tlv-builder.Tpo -c -o libcommon_a-tlv-builder.o `test -f 'tlv-builder.c' || echo '$(srcdir)/'`tlv-builder.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-tlv-builder.Tpo $(DEPDIR)/libcommon_a-tlv-builder.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tlv-builder.c' object='libcommon_a-tlv-builder.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-tlv-builder.o `test -f 'tlv-builder.c' || echo '$(srcdir)/'`tlv-builder.c + +libcommon_a-tlv-builder.obj: tlv-builder.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-tlv-builder.obj -MD -MP -MF $(DEPDIR)/libcommon_a-tlv-builder.Tpo -c -o libcommon_a-tlv-builder.obj `if test -f 'tlv-builder.c'; then $(CYGPATH_W) 'tlv-builder.c'; else $(CYGPATH_W) '$(srcdir)/tlv-builder.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-tlv-builder.Tpo $(DEPDIR)/libcommon_a-tlv-builder.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tlv-builder.c' object='libcommon_a-tlv-builder.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-tlv-builder.obj `if test -f 'tlv-builder.c'; then $(CYGPATH_W) 'tlv-builder.c'; else $(CYGPATH_W) '$(srcdir)/tlv-builder.c'; fi` + +libcommon_a-init.o: init.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-init.o -MD -MP -MF $(DEPDIR)/libcommon_a-init.Tpo -c -o libcommon_a-init.o `test -f 'init.c' || echo '$(srcdir)/'`init.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-init.Tpo $(DEPDIR)/libcommon_a-init.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='init.c' object='libcommon_a-init.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-init.o `test -f 'init.c' || echo '$(srcdir)/'`init.c + +libcommon_a-init.obj: init.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-init.obj -MD -MP -MF $(DEPDIR)/libcommon_a-init.Tpo -c -o libcommon_a-init.obj `if test -f 'init.c'; then $(CYGPATH_W) 'init.c'; else $(CYGPATH_W) '$(srcdir)/init.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-init.Tpo $(DEPDIR)/libcommon_a-init.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='init.c' object='libcommon_a-init.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-init.obj `if test -f 'init.c'; then $(CYGPATH_W) 'init.c'; else $(CYGPATH_W) '$(srcdir)/init.c'; fi` + +libcommon_a-sexputil.o: sexputil.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-sexputil.o -MD -MP -MF $(DEPDIR)/libcommon_a-sexputil.Tpo -c -o libcommon_a-sexputil.o `test -f 'sexputil.c' || echo '$(srcdir)/'`sexputil.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-sexputil.Tpo $(DEPDIR)/libcommon_a-sexputil.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sexputil.c' object='libcommon_a-sexputil.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-sexputil.o `test -f 'sexputil.c' || echo '$(srcdir)/'`sexputil.c + +libcommon_a-sexputil.obj: sexputil.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-sexputil.obj -MD -MP -MF $(DEPDIR)/libcommon_a-sexputil.Tpo -c -o libcommon_a-sexputil.obj `if test -f 'sexputil.c'; then $(CYGPATH_W) 'sexputil.c'; else $(CYGPATH_W) '$(srcdir)/sexputil.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-sexputil.Tpo $(DEPDIR)/libcommon_a-sexputil.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sexputil.c' object='libcommon_a-sexputil.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-sexputil.obj `if test -f 'sexputil.c'; then $(CYGPATH_W) 'sexputil.c'; else $(CYGPATH_W) '$(srcdir)/sexputil.c'; fi` + +libcommon_a-sysutils.o: sysutils.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-sysutils.o -MD -MP -MF $(DEPDIR)/libcommon_a-sysutils.Tpo -c -o libcommon_a-sysutils.o `test -f 'sysutils.c' || echo '$(srcdir)/'`sysutils.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-sysutils.Tpo $(DEPDIR)/libcommon_a-sysutils.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sysutils.c' object='libcommon_a-sysutils.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-sysutils.o `test -f 'sysutils.c' || echo '$(srcdir)/'`sysutils.c + +libcommon_a-sysutils.obj: sysutils.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-sysutils.obj -MD -MP -MF $(DEPDIR)/libcommon_a-sysutils.Tpo -c -o libcommon_a-sysutils.obj `if test -f 'sysutils.c'; then $(CYGPATH_W) 'sysutils.c'; else $(CYGPATH_W) '$(srcdir)/sysutils.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-sysutils.Tpo $(DEPDIR)/libcommon_a-sysutils.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sysutils.c' object='libcommon_a-sysutils.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-sysutils.obj `if test -f 'sysutils.c'; then $(CYGPATH_W) 'sysutils.c'; else $(CYGPATH_W) '$(srcdir)/sysutils.c'; fi` + +libcommon_a-homedir.o: homedir.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-homedir.o -MD -MP -MF $(DEPDIR)/libcommon_a-homedir.Tpo -c -o libcommon_a-homedir.o `test -f 'homedir.c' || echo '$(srcdir)/'`homedir.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-homedir.Tpo $(DEPDIR)/libcommon_a-homedir.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='homedir.c' object='libcommon_a-homedir.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-homedir.o `test -f 'homedir.c' || echo '$(srcdir)/'`homedir.c + +libcommon_a-homedir.obj: homedir.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-homedir.obj -MD -MP -MF $(DEPDIR)/libcommon_a-homedir.Tpo -c -o libcommon_a-homedir.obj `if test -f 'homedir.c'; then $(CYGPATH_W) 'homedir.c'; else $(CYGPATH_W) '$(srcdir)/homedir.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-homedir.Tpo $(DEPDIR)/libcommon_a-homedir.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='homedir.c' object='libcommon_a-homedir.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-homedir.obj `if test -f 'homedir.c'; then $(CYGPATH_W) 'homedir.c'; else $(CYGPATH_W) '$(srcdir)/homedir.c'; fi` + +libcommon_a-gettime.o: gettime.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-gettime.o -MD -MP -MF $(DEPDIR)/libcommon_a-gettime.Tpo -c -o libcommon_a-gettime.o `test -f 'gettime.c' || echo '$(srcdir)/'`gettime.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-gettime.Tpo $(DEPDIR)/libcommon_a-gettime.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gettime.c' object='libcommon_a-gettime.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-gettime.o `test -f 'gettime.c' || echo '$(srcdir)/'`gettime.c + +libcommon_a-gettime.obj: gettime.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-gettime.obj -MD -MP -MF $(DEPDIR)/libcommon_a-gettime.Tpo -c -o libcommon_a-gettime.obj `if test -f 'gettime.c'; then $(CYGPATH_W) 'gettime.c'; else $(CYGPATH_W) '$(srcdir)/gettime.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-gettime.Tpo $(DEPDIR)/libcommon_a-gettime.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gettime.c' object='libcommon_a-gettime.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-gettime.obj `if test -f 'gettime.c'; then $(CYGPATH_W) 'gettime.c'; else $(CYGPATH_W) '$(srcdir)/gettime.c'; fi` + +libcommon_a-yesno.o: yesno.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-yesno.o -MD -MP -MF $(DEPDIR)/libcommon_a-yesno.Tpo -c -o libcommon_a-yesno.o `test -f 'yesno.c' || echo '$(srcdir)/'`yesno.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-yesno.Tpo $(DEPDIR)/libcommon_a-yesno.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='yesno.c' object='libcommon_a-yesno.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-yesno.o `test -f 'yesno.c' || echo '$(srcdir)/'`yesno.c + +libcommon_a-yesno.obj: yesno.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-yesno.obj -MD -MP -MF $(DEPDIR)/libcommon_a-yesno.Tpo -c -o libcommon_a-yesno.obj `if test -f 'yesno.c'; then $(CYGPATH_W) 'yesno.c'; else $(CYGPATH_W) '$(srcdir)/yesno.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-yesno.Tpo $(DEPDIR)/libcommon_a-yesno.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='yesno.c' object='libcommon_a-yesno.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-yesno.obj `if test -f 'yesno.c'; then $(CYGPATH_W) 'yesno.c'; else $(CYGPATH_W) '$(srcdir)/yesno.c'; fi` + +libcommon_a-b64enc.o: b64enc.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-b64enc.o -MD -MP -MF $(DEPDIR)/libcommon_a-b64enc.Tpo -c -o libcommon_a-b64enc.o `test -f 'b64enc.c' || echo '$(srcdir)/'`b64enc.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-b64enc.Tpo $(DEPDIR)/libcommon_a-b64enc.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='b64enc.c' object='libcommon_a-b64enc.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-b64enc.o `test -f 'b64enc.c' || echo '$(srcdir)/'`b64enc.c + +libcommon_a-b64enc.obj: b64enc.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-b64enc.obj -MD -MP -MF $(DEPDIR)/libcommon_a-b64enc.Tpo -c -o libcommon_a-b64enc.obj `if test -f 'b64enc.c'; then $(CYGPATH_W) 'b64enc.c'; else $(CYGPATH_W) '$(srcdir)/b64enc.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-b64enc.Tpo $(DEPDIR)/libcommon_a-b64enc.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='b64enc.c' object='libcommon_a-b64enc.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-b64enc.obj `if test -f 'b64enc.c'; then $(CYGPATH_W) 'b64enc.c'; else $(CYGPATH_W) '$(srcdir)/b64enc.c'; fi` + +libcommon_a-b64dec.o: b64dec.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-b64dec.o -MD -MP -MF $(DEPDIR)/libcommon_a-b64dec.Tpo -c -o libcommon_a-b64dec.o `test -f 'b64dec.c' || echo '$(srcdir)/'`b64dec.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-b64dec.Tpo $(DEPDIR)/libcommon_a-b64dec.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='b64dec.c' object='libcommon_a-b64dec.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-b64dec.o `test -f 'b64dec.c' || echo '$(srcdir)/'`b64dec.c + +libcommon_a-b64dec.obj: b64dec.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-b64dec.obj -MD -MP -MF $(DEPDIR)/libcommon_a-b64dec.Tpo -c -o libcommon_a-b64dec.obj `if test -f 'b64dec.c'; then $(CYGPATH_W) 'b64dec.c'; else $(CYGPATH_W) '$(srcdir)/b64dec.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-b64dec.Tpo $(DEPDIR)/libcommon_a-b64dec.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='b64dec.c' object='libcommon_a-b64dec.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-b64dec.obj `if test -f 'b64dec.c'; then $(CYGPATH_W) 'b64dec.c'; else $(CYGPATH_W) '$(srcdir)/b64dec.c'; fi` + +libcommon_a-zb32.o: zb32.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-zb32.o -MD -MP -MF $(DEPDIR)/libcommon_a-zb32.Tpo -c -o libcommon_a-zb32.o `test -f 'zb32.c' || echo '$(srcdir)/'`zb32.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-zb32.Tpo $(DEPDIR)/libcommon_a-zb32.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='zb32.c' object='libcommon_a-zb32.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-zb32.o `test -f 'zb32.c' || echo '$(srcdir)/'`zb32.c + +libcommon_a-zb32.obj: zb32.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-zb32.obj -MD -MP -MF $(DEPDIR)/libcommon_a-zb32.Tpo -c -o libcommon_a-zb32.obj `if test -f 'zb32.c'; then $(CYGPATH_W) 'zb32.c'; else $(CYGPATH_W) '$(srcdir)/zb32.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-zb32.Tpo $(DEPDIR)/libcommon_a-zb32.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='zb32.c' object='libcommon_a-zb32.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-zb32.obj `if test -f 'zb32.c'; then $(CYGPATH_W) 'zb32.c'; else $(CYGPATH_W) '$(srcdir)/zb32.c'; fi` + +libcommon_a-convert.o: convert.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-convert.o -MD -MP -MF $(DEPDIR)/libcommon_a-convert.Tpo -c -o libcommon_a-convert.o `test -f 'convert.c' || echo '$(srcdir)/'`convert.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-convert.Tpo $(DEPDIR)/libcommon_a-convert.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='convert.c' object='libcommon_a-convert.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-convert.o `test -f 'convert.c' || echo '$(srcdir)/'`convert.c + +libcommon_a-convert.obj: convert.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-convert.obj -MD -MP -MF $(DEPDIR)/libcommon_a-convert.Tpo -c -o libcommon_a-convert.obj `if test -f 'convert.c'; then $(CYGPATH_W) 'convert.c'; else $(CYGPATH_W) '$(srcdir)/convert.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-convert.Tpo $(DEPDIR)/libcommon_a-convert.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='convert.c' object='libcommon_a-convert.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-convert.obj `if test -f 'convert.c'; then $(CYGPATH_W) 'convert.c'; else $(CYGPATH_W) '$(srcdir)/convert.c'; fi` + +libcommon_a-percent.o: percent.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-percent.o -MD -MP -MF $(DEPDIR)/libcommon_a-percent.Tpo -c -o libcommon_a-percent.o `test -f 'percent.c' || echo '$(srcdir)/'`percent.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-percent.Tpo $(DEPDIR)/libcommon_a-percent.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='percent.c' object='libcommon_a-percent.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-percent.o `test -f 'percent.c' || echo '$(srcdir)/'`percent.c + +libcommon_a-percent.obj: percent.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-percent.obj -MD -MP -MF $(DEPDIR)/libcommon_a-percent.Tpo -c -o libcommon_a-percent.obj `if test -f 'percent.c'; then $(CYGPATH_W) 'percent.c'; else $(CYGPATH_W) '$(srcdir)/percent.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-percent.Tpo $(DEPDIR)/libcommon_a-percent.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='percent.c' object='libcommon_a-percent.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-percent.obj `if test -f 'percent.c'; then $(CYGPATH_W) 'percent.c'; else $(CYGPATH_W) '$(srcdir)/percent.c'; fi` + +libcommon_a-mbox-util.o: mbox-util.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-mbox-util.o -MD -MP -MF $(DEPDIR)/libcommon_a-mbox-util.Tpo -c -o libcommon_a-mbox-util.o `test -f 'mbox-util.c' || echo '$(srcdir)/'`mbox-util.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-mbox-util.Tpo $(DEPDIR)/libcommon_a-mbox-util.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mbox-util.c' object='libcommon_a-mbox-util.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-mbox-util.o `test -f 'mbox-util.c' || echo '$(srcdir)/'`mbox-util.c + +libcommon_a-mbox-util.obj: mbox-util.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-mbox-util.obj -MD -MP -MF $(DEPDIR)/libcommon_a-mbox-util.Tpo -c -o libcommon_a-mbox-util.obj `if test -f 'mbox-util.c'; then $(CYGPATH_W) 'mbox-util.c'; else $(CYGPATH_W) '$(srcdir)/mbox-util.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-mbox-util.Tpo $(DEPDIR)/libcommon_a-mbox-util.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mbox-util.c' object='libcommon_a-mbox-util.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-mbox-util.obj `if test -f 'mbox-util.c'; then $(CYGPATH_W) 'mbox-util.c'; else $(CYGPATH_W) '$(srcdir)/mbox-util.c'; fi` + +libcommon_a-miscellaneous.o: miscellaneous.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-miscellaneous.o -MD -MP -MF $(DEPDIR)/libcommon_a-miscellaneous.Tpo -c -o libcommon_a-miscellaneous.o `test -f 'miscellaneous.c' || echo '$(srcdir)/'`miscellaneous.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-miscellaneous.Tpo $(DEPDIR)/libcommon_a-miscellaneous.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='miscellaneous.c' object='libcommon_a-miscellaneous.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-miscellaneous.o `test -f 'miscellaneous.c' || echo '$(srcdir)/'`miscellaneous.c + +libcommon_a-miscellaneous.obj: miscellaneous.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-miscellaneous.obj -MD -MP -MF $(DEPDIR)/libcommon_a-miscellaneous.Tpo -c -o libcommon_a-miscellaneous.obj `if test -f 'miscellaneous.c'; then $(CYGPATH_W) 'miscellaneous.c'; else $(CYGPATH_W) '$(srcdir)/miscellaneous.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-miscellaneous.Tpo $(DEPDIR)/libcommon_a-miscellaneous.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='miscellaneous.c' object='libcommon_a-miscellaneous.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-miscellaneous.obj `if test -f 'miscellaneous.c'; then $(CYGPATH_W) 'miscellaneous.c'; else $(CYGPATH_W) '$(srcdir)/miscellaneous.c'; fi` + +libcommon_a-xasprintf.o: xasprintf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-xasprintf.o -MD -MP -MF $(DEPDIR)/libcommon_a-xasprintf.Tpo -c -o libcommon_a-xasprintf.o `test -f 'xasprintf.c' || echo '$(srcdir)/'`xasprintf.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-xasprintf.Tpo $(DEPDIR)/libcommon_a-xasprintf.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='xasprintf.c' object='libcommon_a-xasprintf.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-xasprintf.o `test -f 'xasprintf.c' || echo '$(srcdir)/'`xasprintf.c + +libcommon_a-xasprintf.obj: xasprintf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-xasprintf.obj -MD -MP -MF $(DEPDIR)/libcommon_a-xasprintf.Tpo -c -o libcommon_a-xasprintf.obj `if test -f 'xasprintf.c'; then $(CYGPATH_W) 'xasprintf.c'; else $(CYGPATH_W) '$(srcdir)/xasprintf.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-xasprintf.Tpo $(DEPDIR)/libcommon_a-xasprintf.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='xasprintf.c' object='libcommon_a-xasprintf.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-xasprintf.obj `if test -f 'xasprintf.c'; then $(CYGPATH_W) 'xasprintf.c'; else $(CYGPATH_W) '$(srcdir)/xasprintf.c'; fi` + +libcommon_a-xreadline.o: xreadline.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-xreadline.o -MD -MP -MF $(DEPDIR)/libcommon_a-xreadline.Tpo -c -o libcommon_a-xreadline.o `test -f 'xreadline.c' || echo '$(srcdir)/'`xreadline.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-xreadline.Tpo $(DEPDIR)/libcommon_a-xreadline.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='xreadline.c' object='libcommon_a-xreadline.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-xreadline.o `test -f 'xreadline.c' || echo '$(srcdir)/'`xreadline.c + +libcommon_a-xreadline.obj: xreadline.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-xreadline.obj -MD -MP -MF $(DEPDIR)/libcommon_a-xreadline.Tpo -c -o libcommon_a-xreadline.obj `if test -f 'xreadline.c'; then $(CYGPATH_W) 'xreadline.c'; else $(CYGPATH_W) '$(srcdir)/xreadline.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-xreadline.Tpo $(DEPDIR)/libcommon_a-xreadline.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='xreadline.c' object='libcommon_a-xreadline.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-xreadline.obj `if test -f 'xreadline.c'; then $(CYGPATH_W) 'xreadline.c'; else $(CYGPATH_W) '$(srcdir)/xreadline.c'; fi` + +libcommon_a-membuf.o: membuf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-membuf.o -MD -MP -MF $(DEPDIR)/libcommon_a-membuf.Tpo -c -o libcommon_a-membuf.o `test -f 'membuf.c' || echo '$(srcdir)/'`membuf.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-membuf.Tpo $(DEPDIR)/libcommon_a-membuf.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='membuf.c' object='libcommon_a-membuf.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-membuf.o `test -f 'membuf.c' || echo '$(srcdir)/'`membuf.c + +libcommon_a-membuf.obj: membuf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-membuf.obj -MD -MP -MF $(DEPDIR)/libcommon_a-membuf.Tpo -c -o libcommon_a-membuf.obj `if test -f 'membuf.c'; then $(CYGPATH_W) 'membuf.c'; else $(CYGPATH_W) '$(srcdir)/membuf.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-membuf.Tpo $(DEPDIR)/libcommon_a-membuf.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='membuf.c' object='libcommon_a-membuf.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-membuf.obj `if test -f 'membuf.c'; then $(CYGPATH_W) 'membuf.c'; else $(CYGPATH_W) '$(srcdir)/membuf.c'; fi` + +libcommon_a-ccparray.o: ccparray.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-ccparray.o -MD -MP -MF $(DEPDIR)/libcommon_a-ccparray.Tpo -c -o libcommon_a-ccparray.o `test -f 'ccparray.c' || echo '$(srcdir)/'`ccparray.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-ccparray.Tpo $(DEPDIR)/libcommon_a-ccparray.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ccparray.c' object='libcommon_a-ccparray.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-ccparray.o `test -f 'ccparray.c' || echo '$(srcdir)/'`ccparray.c + +libcommon_a-ccparray.obj: ccparray.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-ccparray.obj -MD -MP -MF $(DEPDIR)/libcommon_a-ccparray.Tpo -c -o libcommon_a-ccparray.obj `if test -f 'ccparray.c'; then $(CYGPATH_W) 'ccparray.c'; else $(CYGPATH_W) '$(srcdir)/ccparray.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-ccparray.Tpo $(DEPDIR)/libcommon_a-ccparray.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ccparray.c' object='libcommon_a-ccparray.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-ccparray.obj `if test -f 'ccparray.c'; then $(CYGPATH_W) 'ccparray.c'; else $(CYGPATH_W) '$(srcdir)/ccparray.c'; fi` + +libcommon_a-iobuf.o: iobuf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-iobuf.o -MD -MP -MF $(DEPDIR)/libcommon_a-iobuf.Tpo -c -o libcommon_a-iobuf.o `test -f 'iobuf.c' || echo '$(srcdir)/'`iobuf.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-iobuf.Tpo $(DEPDIR)/libcommon_a-iobuf.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='iobuf.c' object='libcommon_a-iobuf.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-iobuf.o `test -f 'iobuf.c' || echo '$(srcdir)/'`iobuf.c + +libcommon_a-iobuf.obj: iobuf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-iobuf.obj -MD -MP -MF $(DEPDIR)/libcommon_a-iobuf.Tpo -c -o libcommon_a-iobuf.obj `if test -f 'iobuf.c'; then $(CYGPATH_W) 'iobuf.c'; else $(CYGPATH_W) '$(srcdir)/iobuf.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-iobuf.Tpo $(DEPDIR)/libcommon_a-iobuf.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='iobuf.c' object='libcommon_a-iobuf.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-iobuf.obj `if test -f 'iobuf.c'; then $(CYGPATH_W) 'iobuf.c'; else $(CYGPATH_W) '$(srcdir)/iobuf.c'; fi` + +libcommon_a-ttyio.o: ttyio.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-ttyio.o -MD -MP -MF $(DEPDIR)/libcommon_a-ttyio.Tpo -c -o libcommon_a-ttyio.o `test -f 'ttyio.c' || echo '$(srcdir)/'`ttyio.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-ttyio.Tpo $(DEPDIR)/libcommon_a-ttyio.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ttyio.c' object='libcommon_a-ttyio.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-ttyio.o `test -f 'ttyio.c' || echo '$(srcdir)/'`ttyio.c + +libcommon_a-ttyio.obj: ttyio.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-ttyio.obj -MD -MP -MF $(DEPDIR)/libcommon_a-ttyio.Tpo -c -o libcommon_a-ttyio.obj `if test -f 'ttyio.c'; then $(CYGPATH_W) 'ttyio.c'; else $(CYGPATH_W) '$(srcdir)/ttyio.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-ttyio.Tpo $(DEPDIR)/libcommon_a-ttyio.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ttyio.c' object='libcommon_a-ttyio.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-ttyio.obj `if test -f 'ttyio.c'; then $(CYGPATH_W) 'ttyio.c'; else $(CYGPATH_W) '$(srcdir)/ttyio.c'; fi` + +libcommon_a-asshelp.o: asshelp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-asshelp.o -MD -MP -MF $(DEPDIR)/libcommon_a-asshelp.Tpo -c -o libcommon_a-asshelp.o `test -f 'asshelp.c' || echo '$(srcdir)/'`asshelp.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-asshelp.Tpo $(DEPDIR)/libcommon_a-asshelp.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='asshelp.c' object='libcommon_a-asshelp.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-asshelp.o `test -f 'asshelp.c' || echo '$(srcdir)/'`asshelp.c + +libcommon_a-asshelp.obj: asshelp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-asshelp.obj -MD -MP -MF $(DEPDIR)/libcommon_a-asshelp.Tpo -c -o libcommon_a-asshelp.obj `if test -f 'asshelp.c'; then $(CYGPATH_W) 'asshelp.c'; else $(CYGPATH_W) '$(srcdir)/asshelp.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-asshelp.Tpo $(DEPDIR)/libcommon_a-asshelp.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='asshelp.c' object='libcommon_a-asshelp.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-asshelp.obj `if test -f 'asshelp.c'; then $(CYGPATH_W) 'asshelp.c'; else $(CYGPATH_W) '$(srcdir)/asshelp.c'; fi` + +libcommon_a-asshelp2.o: asshelp2.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-asshelp2.o -MD -MP -MF $(DEPDIR)/libcommon_a-asshelp2.Tpo -c -o libcommon_a-asshelp2.o `test -f 'asshelp2.c' || echo '$(srcdir)/'`asshelp2.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-asshelp2.Tpo $(DEPDIR)/libcommon_a-asshelp2.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='asshelp2.c' object='libcommon_a-asshelp2.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-asshelp2.o `test -f 'asshelp2.c' || echo '$(srcdir)/'`asshelp2.c + +libcommon_a-asshelp2.obj: asshelp2.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-asshelp2.obj -MD -MP -MF $(DEPDIR)/libcommon_a-asshelp2.Tpo -c -o libcommon_a-asshelp2.obj `if test -f 'asshelp2.c'; then $(CYGPATH_W) 'asshelp2.c'; else $(CYGPATH_W) '$(srcdir)/asshelp2.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-asshelp2.Tpo $(DEPDIR)/libcommon_a-asshelp2.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='asshelp2.c' object='libcommon_a-asshelp2.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-asshelp2.obj `if test -f 'asshelp2.c'; then $(CYGPATH_W) 'asshelp2.c'; else $(CYGPATH_W) '$(srcdir)/asshelp2.c'; fi` + +libcommon_a-signal.o: signal.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-signal.o -MD -MP -MF $(DEPDIR)/libcommon_a-signal.Tpo -c -o libcommon_a-signal.o `test -f 'signal.c' || echo '$(srcdir)/'`signal.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-signal.Tpo $(DEPDIR)/libcommon_a-signal.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='signal.c' object='libcommon_a-signal.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-signal.o `test -f 'signal.c' || echo '$(srcdir)/'`signal.c + +libcommon_a-signal.obj: signal.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-signal.obj -MD -MP -MF $(DEPDIR)/libcommon_a-signal.Tpo -c -o libcommon_a-signal.obj `if test -f 'signal.c'; then $(CYGPATH_W) 'signal.c'; else $(CYGPATH_W) '$(srcdir)/signal.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-signal.Tpo $(DEPDIR)/libcommon_a-signal.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='signal.c' object='libcommon_a-signal.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-signal.obj `if test -f 'signal.c'; then $(CYGPATH_W) 'signal.c'; else $(CYGPATH_W) '$(srcdir)/signal.c'; fi` + +libcommon_a-audit.o: audit.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-audit.o -MD -MP -MF $(DEPDIR)/libcommon_a-audit.Tpo -c -o libcommon_a-audit.o `test -f 'audit.c' || echo '$(srcdir)/'`audit.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-audit.Tpo $(DEPDIR)/libcommon_a-audit.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='audit.c' object='libcommon_a-audit.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-audit.o `test -f 'audit.c' || echo '$(srcdir)/'`audit.c + +libcommon_a-audit.obj: audit.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-audit.obj -MD -MP -MF $(DEPDIR)/libcommon_a-audit.Tpo -c -o libcommon_a-audit.obj `if test -f 'audit.c'; then $(CYGPATH_W) 'audit.c'; else $(CYGPATH_W) '$(srcdir)/audit.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-audit.Tpo $(DEPDIR)/libcommon_a-audit.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='audit.c' object='libcommon_a-audit.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-audit.obj `if test -f 'audit.c'; then $(CYGPATH_W) 'audit.c'; else $(CYGPATH_W) '$(srcdir)/audit.c'; fi` + +libcommon_a-localename.o: localename.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-localename.o -MD -MP -MF $(DEPDIR)/libcommon_a-localename.Tpo -c -o libcommon_a-localename.o `test -f 'localename.c' || echo '$(srcdir)/'`localename.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-localename.Tpo $(DEPDIR)/libcommon_a-localename.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='localename.c' object='libcommon_a-localename.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-localename.o `test -f 'localename.c' || echo '$(srcdir)/'`localename.c + +libcommon_a-localename.obj: localename.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-localename.obj -MD -MP -MF $(DEPDIR)/libcommon_a-localename.Tpo -c -o libcommon_a-localename.obj `if test -f 'localename.c'; then $(CYGPATH_W) 'localename.c'; else $(CYGPATH_W) '$(srcdir)/localename.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-localename.Tpo $(DEPDIR)/libcommon_a-localename.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='localename.c' object='libcommon_a-localename.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-localename.obj `if test -f 'localename.c'; then $(CYGPATH_W) 'localename.c'; else $(CYGPATH_W) '$(srcdir)/localename.c'; fi` + +libcommon_a-session-env.o: session-env.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-session-env.o -MD -MP -MF $(DEPDIR)/libcommon_a-session-env.Tpo -c -o libcommon_a-session-env.o `test -f 'session-env.c' || echo '$(srcdir)/'`session-env.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-session-env.Tpo $(DEPDIR)/libcommon_a-session-env.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='session-env.c' object='libcommon_a-session-env.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-session-env.o `test -f 'session-env.c' || echo '$(srcdir)/'`session-env.c + +libcommon_a-session-env.obj: session-env.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-session-env.obj -MD -MP -MF $(DEPDIR)/libcommon_a-session-env.Tpo -c -o libcommon_a-session-env.obj `if test -f 'session-env.c'; then $(CYGPATH_W) 'session-env.c'; else $(CYGPATH_W) '$(srcdir)/session-env.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-session-env.Tpo $(DEPDIR)/libcommon_a-session-env.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='session-env.c' object='libcommon_a-session-env.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-session-env.obj `if test -f 'session-env.c'; then $(CYGPATH_W) 'session-env.c'; else $(CYGPATH_W) '$(srcdir)/session-env.c'; fi` + +libcommon_a-userids.o: userids.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-userids.o -MD -MP -MF $(DEPDIR)/libcommon_a-userids.Tpo -c -o libcommon_a-userids.o `test -f 'userids.c' || echo '$(srcdir)/'`userids.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-userids.Tpo $(DEPDIR)/libcommon_a-userids.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='userids.c' object='libcommon_a-userids.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-userids.o `test -f 'userids.c' || echo '$(srcdir)/'`userids.c + +libcommon_a-userids.obj: userids.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-userids.obj -MD -MP -MF $(DEPDIR)/libcommon_a-userids.Tpo -c -o libcommon_a-userids.obj `if test -f 'userids.c'; then $(CYGPATH_W) 'userids.c'; else $(CYGPATH_W) '$(srcdir)/userids.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-userids.Tpo $(DEPDIR)/libcommon_a-userids.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='userids.c' object='libcommon_a-userids.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-userids.obj `if test -f 'userids.c'; then $(CYGPATH_W) 'userids.c'; else $(CYGPATH_W) '$(srcdir)/userids.c'; fi` + +libcommon_a-openpgp-oid.o: openpgp-oid.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-openpgp-oid.o -MD -MP -MF $(DEPDIR)/libcommon_a-openpgp-oid.Tpo -c -o libcommon_a-openpgp-oid.o `test -f 'openpgp-oid.c' || echo '$(srcdir)/'`openpgp-oid.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-openpgp-oid.Tpo $(DEPDIR)/libcommon_a-openpgp-oid.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='openpgp-oid.c' object='libcommon_a-openpgp-oid.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-openpgp-oid.o `test -f 'openpgp-oid.c' || echo '$(srcdir)/'`openpgp-oid.c + +libcommon_a-openpgp-oid.obj: openpgp-oid.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-openpgp-oid.obj -MD -MP -MF $(DEPDIR)/libcommon_a-openpgp-oid.Tpo -c -o libcommon_a-openpgp-oid.obj `if test -f 'openpgp-oid.c'; then $(CYGPATH_W) 'openpgp-oid.c'; else $(CYGPATH_W) '$(srcdir)/openpgp-oid.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-openpgp-oid.Tpo $(DEPDIR)/libcommon_a-openpgp-oid.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='openpgp-oid.c' object='libcommon_a-openpgp-oid.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-openpgp-oid.obj `if test -f 'openpgp-oid.c'; then $(CYGPATH_W) 'openpgp-oid.c'; else $(CYGPATH_W) '$(srcdir)/openpgp-oid.c'; fi` + +libcommon_a-ssh-utils.o: ssh-utils.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-ssh-utils.o -MD -MP -MF $(DEPDIR)/libcommon_a-ssh-utils.Tpo -c -o libcommon_a-ssh-utils.o `test -f 'ssh-utils.c' || echo '$(srcdir)/'`ssh-utils.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-ssh-utils.Tpo $(DEPDIR)/libcommon_a-ssh-utils.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ssh-utils.c' object='libcommon_a-ssh-utils.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-ssh-utils.o `test -f 'ssh-utils.c' || echo '$(srcdir)/'`ssh-utils.c + +libcommon_a-ssh-utils.obj: ssh-utils.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-ssh-utils.obj -MD -MP -MF $(DEPDIR)/libcommon_a-ssh-utils.Tpo -c -o libcommon_a-ssh-utils.obj `if test -f 'ssh-utils.c'; then $(CYGPATH_W) 'ssh-utils.c'; else $(CYGPATH_W) '$(srcdir)/ssh-utils.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-ssh-utils.Tpo $(DEPDIR)/libcommon_a-ssh-utils.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ssh-utils.c' object='libcommon_a-ssh-utils.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-ssh-utils.obj `if test -f 'ssh-utils.c'; then $(CYGPATH_W) 'ssh-utils.c'; else $(CYGPATH_W) '$(srcdir)/ssh-utils.c'; fi` + +libcommon_a-agent-opt.o: agent-opt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-agent-opt.o -MD -MP -MF $(DEPDIR)/libcommon_a-agent-opt.Tpo -c -o libcommon_a-agent-opt.o `test -f 'agent-opt.c' || echo '$(srcdir)/'`agent-opt.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-agent-opt.Tpo $(DEPDIR)/libcommon_a-agent-opt.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='agent-opt.c' object='libcommon_a-agent-opt.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-agent-opt.o `test -f 'agent-opt.c' || echo '$(srcdir)/'`agent-opt.c + +libcommon_a-agent-opt.obj: agent-opt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-agent-opt.obj -MD -MP -MF $(DEPDIR)/libcommon_a-agent-opt.Tpo -c -o libcommon_a-agent-opt.obj `if test -f 'agent-opt.c'; then $(CYGPATH_W) 'agent-opt.c'; else $(CYGPATH_W) '$(srcdir)/agent-opt.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-agent-opt.Tpo $(DEPDIR)/libcommon_a-agent-opt.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='agent-opt.c' object='libcommon_a-agent-opt.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-agent-opt.obj `if test -f 'agent-opt.c'; then $(CYGPATH_W) 'agent-opt.c'; else $(CYGPATH_W) '$(srcdir)/agent-opt.c'; fi` + +libcommon_a-helpfile.o: helpfile.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-helpfile.o -MD -MP -MF $(DEPDIR)/libcommon_a-helpfile.Tpo -c -o libcommon_a-helpfile.o `test -f 'helpfile.c' || echo '$(srcdir)/'`helpfile.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-helpfile.Tpo $(DEPDIR)/libcommon_a-helpfile.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='helpfile.c' object='libcommon_a-helpfile.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-helpfile.o `test -f 'helpfile.c' || echo '$(srcdir)/'`helpfile.c + +libcommon_a-helpfile.obj: helpfile.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-helpfile.obj -MD -MP -MF $(DEPDIR)/libcommon_a-helpfile.Tpo -c -o libcommon_a-helpfile.obj `if test -f 'helpfile.c'; then $(CYGPATH_W) 'helpfile.c'; else $(CYGPATH_W) '$(srcdir)/helpfile.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-helpfile.Tpo $(DEPDIR)/libcommon_a-helpfile.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='helpfile.c' object='libcommon_a-helpfile.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-helpfile.obj `if test -f 'helpfile.c'; then $(CYGPATH_W) 'helpfile.c'; else $(CYGPATH_W) '$(srcdir)/helpfile.c'; fi` + +libcommon_a-mkdir_p.o: mkdir_p.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-mkdir_p.o -MD -MP -MF $(DEPDIR)/libcommon_a-mkdir_p.Tpo -c -o libcommon_a-mkdir_p.o `test -f 'mkdir_p.c' || echo '$(srcdir)/'`mkdir_p.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-mkdir_p.Tpo $(DEPDIR)/libcommon_a-mkdir_p.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mkdir_p.c' object='libcommon_a-mkdir_p.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-mkdir_p.o `test -f 'mkdir_p.c' || echo '$(srcdir)/'`mkdir_p.c + +libcommon_a-mkdir_p.obj: mkdir_p.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-mkdir_p.obj -MD -MP -MF $(DEPDIR)/libcommon_a-mkdir_p.Tpo -c -o libcommon_a-mkdir_p.obj `if test -f 'mkdir_p.c'; then $(CYGPATH_W) 'mkdir_p.c'; else $(CYGPATH_W) '$(srcdir)/mkdir_p.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-mkdir_p.Tpo $(DEPDIR)/libcommon_a-mkdir_p.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mkdir_p.c' object='libcommon_a-mkdir_p.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-mkdir_p.obj `if test -f 'mkdir_p.c'; then $(CYGPATH_W) 'mkdir_p.c'; else $(CYGPATH_W) '$(srcdir)/mkdir_p.c'; fi` + +libcommon_a-exectool.o: exectool.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-exectool.o -MD -MP -MF $(DEPDIR)/libcommon_a-exectool.Tpo -c -o libcommon_a-exectool.o `test -f 'exectool.c' || echo '$(srcdir)/'`exectool.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-exectool.Tpo $(DEPDIR)/libcommon_a-exectool.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='exectool.c' object='libcommon_a-exectool.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-exectool.o `test -f 'exectool.c' || echo '$(srcdir)/'`exectool.c + +libcommon_a-exectool.obj: exectool.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-exectool.obj -MD -MP -MF $(DEPDIR)/libcommon_a-exectool.Tpo -c -o libcommon_a-exectool.obj `if test -f 'exectool.c'; then $(CYGPATH_W) 'exectool.c'; else $(CYGPATH_W) '$(srcdir)/exectool.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-exectool.Tpo $(DEPDIR)/libcommon_a-exectool.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='exectool.c' object='libcommon_a-exectool.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-exectool.obj `if test -f 'exectool.c'; then $(CYGPATH_W) 'exectool.c'; else $(CYGPATH_W) '$(srcdir)/exectool.c'; fi` + +libcommon_a-server-help.o: server-help.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-server-help.o -MD -MP -MF $(DEPDIR)/libcommon_a-server-help.Tpo -c -o libcommon_a-server-help.o `test -f 'server-help.c' || echo '$(srcdir)/'`server-help.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-server-help.Tpo $(DEPDIR)/libcommon_a-server-help.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='server-help.c' object='libcommon_a-server-help.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-server-help.o `test -f 'server-help.c' || echo '$(srcdir)/'`server-help.c + +libcommon_a-server-help.obj: server-help.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-server-help.obj -MD -MP -MF $(DEPDIR)/libcommon_a-server-help.Tpo -c -o libcommon_a-server-help.obj `if test -f 'server-help.c'; then $(CYGPATH_W) 'server-help.c'; else $(CYGPATH_W) '$(srcdir)/server-help.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-server-help.Tpo $(DEPDIR)/libcommon_a-server-help.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='server-help.c' object='libcommon_a-server-help.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-server-help.obj `if test -f 'server-help.c'; then $(CYGPATH_W) 'server-help.c'; else $(CYGPATH_W) '$(srcdir)/server-help.c'; fi` + +libcommon_a-name-value.o: name-value.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-name-value.o -MD -MP -MF $(DEPDIR)/libcommon_a-name-value.Tpo -c -o libcommon_a-name-value.o `test -f 'name-value.c' || echo '$(srcdir)/'`name-value.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-name-value.Tpo $(DEPDIR)/libcommon_a-name-value.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='name-value.c' object='libcommon_a-name-value.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-name-value.o `test -f 'name-value.c' || echo '$(srcdir)/'`name-value.c + +libcommon_a-name-value.obj: name-value.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-name-value.obj -MD -MP -MF $(DEPDIR)/libcommon_a-name-value.Tpo -c -o libcommon_a-name-value.obj `if test -f 'name-value.c'; then $(CYGPATH_W) 'name-value.c'; else $(CYGPATH_W) '$(srcdir)/name-value.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-name-value.Tpo $(DEPDIR)/libcommon_a-name-value.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='name-value.c' object='libcommon_a-name-value.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-name-value.obj `if test -f 'name-value.c'; then $(CYGPATH_W) 'name-value.c'; else $(CYGPATH_W) '$(srcdir)/name-value.c'; fi` + +libcommon_a-recsel.o: recsel.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-recsel.o -MD -MP -MF $(DEPDIR)/libcommon_a-recsel.Tpo -c -o libcommon_a-recsel.o `test -f 'recsel.c' || echo '$(srcdir)/'`recsel.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-recsel.Tpo $(DEPDIR)/libcommon_a-recsel.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='recsel.c' object='libcommon_a-recsel.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-recsel.o `test -f 'recsel.c' || echo '$(srcdir)/'`recsel.c + +libcommon_a-recsel.obj: recsel.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-recsel.obj -MD -MP -MF $(DEPDIR)/libcommon_a-recsel.Tpo -c -o libcommon_a-recsel.obj `if test -f 'recsel.c'; then $(CYGPATH_W) 'recsel.c'; else $(CYGPATH_W) '$(srcdir)/recsel.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-recsel.Tpo $(DEPDIR)/libcommon_a-recsel.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='recsel.c' object='libcommon_a-recsel.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-recsel.obj `if test -f 'recsel.c'; then $(CYGPATH_W) 'recsel.c'; else $(CYGPATH_W) '$(srcdir)/recsel.c'; fi` + +libcommon_a-ksba-io-support.o: ksba-io-support.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-ksba-io-support.o -MD -MP -MF $(DEPDIR)/libcommon_a-ksba-io-support.Tpo -c -o libcommon_a-ksba-io-support.o `test -f 'ksba-io-support.c' || echo '$(srcdir)/'`ksba-io-support.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-ksba-io-support.Tpo $(DEPDIR)/libcommon_a-ksba-io-support.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ksba-io-support.c' object='libcommon_a-ksba-io-support.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-ksba-io-support.o `test -f 'ksba-io-support.c' || echo '$(srcdir)/'`ksba-io-support.c + +libcommon_a-ksba-io-support.obj: ksba-io-support.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-ksba-io-support.obj -MD -MP -MF $(DEPDIR)/libcommon_a-ksba-io-support.Tpo -c -o libcommon_a-ksba-io-support.obj `if test -f 'ksba-io-support.c'; then $(CYGPATH_W) 'ksba-io-support.c'; else $(CYGPATH_W) '$(srcdir)/ksba-io-support.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-ksba-io-support.Tpo $(DEPDIR)/libcommon_a-ksba-io-support.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ksba-io-support.c' object='libcommon_a-ksba-io-support.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-ksba-io-support.obj `if test -f 'ksba-io-support.c'; then $(CYGPATH_W) 'ksba-io-support.c'; else $(CYGPATH_W) '$(srcdir)/ksba-io-support.c'; fi` + +libcommon_a-openpgp-fpr.o: openpgp-fpr.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-openpgp-fpr.o -MD -MP -MF $(DEPDIR)/libcommon_a-openpgp-fpr.Tpo -c -o libcommon_a-openpgp-fpr.o `test -f 'openpgp-fpr.c' || echo '$(srcdir)/'`openpgp-fpr.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-openpgp-fpr.Tpo $(DEPDIR)/libcommon_a-openpgp-fpr.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='openpgp-fpr.c' object='libcommon_a-openpgp-fpr.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-openpgp-fpr.o `test -f 'openpgp-fpr.c' || echo '$(srcdir)/'`openpgp-fpr.c + +libcommon_a-openpgp-fpr.obj: openpgp-fpr.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-openpgp-fpr.obj -MD -MP -MF $(DEPDIR)/libcommon_a-openpgp-fpr.Tpo -c -o libcommon_a-openpgp-fpr.obj `if test -f 'openpgp-fpr.c'; then $(CYGPATH_W) 'openpgp-fpr.c'; else $(CYGPATH_W) '$(srcdir)/openpgp-fpr.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-openpgp-fpr.Tpo $(DEPDIR)/libcommon_a-openpgp-fpr.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='openpgp-fpr.c' object='libcommon_a-openpgp-fpr.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-openpgp-fpr.obj `if test -f 'openpgp-fpr.c'; then $(CYGPATH_W) 'openpgp-fpr.c'; else $(CYGPATH_W) '$(srcdir)/openpgp-fpr.c'; fi` + +libcommon_a-compliance.o: compliance.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-compliance.o -MD -MP -MF $(DEPDIR)/libcommon_a-compliance.Tpo -c -o libcommon_a-compliance.o `test -f 'compliance.c' || echo '$(srcdir)/'`compliance.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-compliance.Tpo $(DEPDIR)/libcommon_a-compliance.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='compliance.c' object='libcommon_a-compliance.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-compliance.o `test -f 'compliance.c' || echo '$(srcdir)/'`compliance.c + +libcommon_a-compliance.obj: compliance.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-compliance.obj -MD -MP -MF $(DEPDIR)/libcommon_a-compliance.Tpo -c -o libcommon_a-compliance.obj `if test -f 'compliance.c'; then $(CYGPATH_W) 'compliance.c'; else $(CYGPATH_W) '$(srcdir)/compliance.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-compliance.Tpo $(DEPDIR)/libcommon_a-compliance.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='compliance.c' object='libcommon_a-compliance.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-compliance.obj `if test -f 'compliance.c'; then $(CYGPATH_W) 'compliance.c'; else $(CYGPATH_W) '$(srcdir)/compliance.c'; fi` + +libcommon_a-w32-reg.o: w32-reg.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-w32-reg.o -MD -MP -MF $(DEPDIR)/libcommon_a-w32-reg.Tpo -c -o libcommon_a-w32-reg.o `test -f 'w32-reg.c' || echo '$(srcdir)/'`w32-reg.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-w32-reg.Tpo $(DEPDIR)/libcommon_a-w32-reg.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='w32-reg.c' object='libcommon_a-w32-reg.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-w32-reg.o `test -f 'w32-reg.c' || echo '$(srcdir)/'`w32-reg.c + +libcommon_a-w32-reg.obj: w32-reg.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-w32-reg.obj -MD -MP -MF $(DEPDIR)/libcommon_a-w32-reg.Tpo -c -o libcommon_a-w32-reg.obj `if test -f 'w32-reg.c'; then $(CYGPATH_W) 'w32-reg.c'; else $(CYGPATH_W) '$(srcdir)/w32-reg.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-w32-reg.Tpo $(DEPDIR)/libcommon_a-w32-reg.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='w32-reg.c' object='libcommon_a-w32-reg.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-w32-reg.obj `if test -f 'w32-reg.c'; then $(CYGPATH_W) 'w32-reg.c'; else $(CYGPATH_W) '$(srcdir)/w32-reg.c'; fi` + +libcommon_a-w32-cmdline.o: w32-cmdline.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-w32-cmdline.o -MD -MP -MF $(DEPDIR)/libcommon_a-w32-cmdline.Tpo -c -o libcommon_a-w32-cmdline.o `test -f 'w32-cmdline.c' || echo '$(srcdir)/'`w32-cmdline.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-w32-cmdline.Tpo $(DEPDIR)/libcommon_a-w32-cmdline.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='w32-cmdline.c' object='libcommon_a-w32-cmdline.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-w32-cmdline.o `test -f 'w32-cmdline.c' || echo '$(srcdir)/'`w32-cmdline.c + +libcommon_a-w32-cmdline.obj: w32-cmdline.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-w32-cmdline.obj -MD -MP -MF $(DEPDIR)/libcommon_a-w32-cmdline.Tpo -c -o libcommon_a-w32-cmdline.obj `if test -f 'w32-cmdline.c'; then $(CYGPATH_W) 'w32-cmdline.c'; else $(CYGPATH_W) '$(srcdir)/w32-cmdline.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-w32-cmdline.Tpo $(DEPDIR)/libcommon_a-w32-cmdline.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='w32-cmdline.c' object='libcommon_a-w32-cmdline.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-w32-cmdline.obj `if test -f 'w32-cmdline.c'; then $(CYGPATH_W) 'w32-cmdline.c'; else $(CYGPATH_W) '$(srcdir)/w32-cmdline.c'; fi` + +libcommon_a-exechelp-w32ce.o: exechelp-w32ce.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-exechelp-w32ce.o -MD -MP -MF $(DEPDIR)/libcommon_a-exechelp-w32ce.Tpo -c -o libcommon_a-exechelp-w32ce.o `test -f 'exechelp-w32ce.c' || echo '$(srcdir)/'`exechelp-w32ce.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-exechelp-w32ce.Tpo $(DEPDIR)/libcommon_a-exechelp-w32ce.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='exechelp-w32ce.c' object='libcommon_a-exechelp-w32ce.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-exechelp-w32ce.o `test -f 'exechelp-w32ce.c' || echo '$(srcdir)/'`exechelp-w32ce.c + +libcommon_a-exechelp-w32ce.obj: exechelp-w32ce.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-exechelp-w32ce.obj -MD -MP -MF $(DEPDIR)/libcommon_a-exechelp-w32ce.Tpo -c -o libcommon_a-exechelp-w32ce.obj `if test -f 'exechelp-w32ce.c'; then $(CYGPATH_W) 'exechelp-w32ce.c'; else $(CYGPATH_W) '$(srcdir)/exechelp-w32ce.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-exechelp-w32ce.Tpo $(DEPDIR)/libcommon_a-exechelp-w32ce.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='exechelp-w32ce.c' object='libcommon_a-exechelp-w32ce.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-exechelp-w32ce.obj `if test -f 'exechelp-w32ce.c'; then $(CYGPATH_W) 'exechelp-w32ce.c'; else $(CYGPATH_W) '$(srcdir)/exechelp-w32ce.c'; fi` + +libcommon_a-exechelp-w32.o: exechelp-w32.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-exechelp-w32.o -MD -MP -MF $(DEPDIR)/libcommon_a-exechelp-w32.Tpo -c -o libcommon_a-exechelp-w32.o `test -f 'exechelp-w32.c' || echo '$(srcdir)/'`exechelp-w32.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-exechelp-w32.Tpo $(DEPDIR)/libcommon_a-exechelp-w32.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='exechelp-w32.c' object='libcommon_a-exechelp-w32.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-exechelp-w32.o `test -f 'exechelp-w32.c' || echo '$(srcdir)/'`exechelp-w32.c + +libcommon_a-exechelp-w32.obj: exechelp-w32.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-exechelp-w32.obj -MD -MP -MF $(DEPDIR)/libcommon_a-exechelp-w32.Tpo -c -o libcommon_a-exechelp-w32.obj `if test -f 'exechelp-w32.c'; then $(CYGPATH_W) 'exechelp-w32.c'; else $(CYGPATH_W) '$(srcdir)/exechelp-w32.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-exechelp-w32.Tpo $(DEPDIR)/libcommon_a-exechelp-w32.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='exechelp-w32.c' object='libcommon_a-exechelp-w32.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-exechelp-w32.obj `if test -f 'exechelp-w32.c'; then $(CYGPATH_W) 'exechelp-w32.c'; else $(CYGPATH_W) '$(srcdir)/exechelp-w32.c'; fi` + +libcommon_a-exechelp-posix.o: exechelp-posix.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-exechelp-posix.o -MD -MP -MF $(DEPDIR)/libcommon_a-exechelp-posix.Tpo -c -o libcommon_a-exechelp-posix.o `test -f 'exechelp-posix.c' || echo '$(srcdir)/'`exechelp-posix.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-exechelp-posix.Tpo $(DEPDIR)/libcommon_a-exechelp-posix.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='exechelp-posix.c' object='libcommon_a-exechelp-posix.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-exechelp-posix.o `test -f 'exechelp-posix.c' || echo '$(srcdir)/'`exechelp-posix.c + +libcommon_a-exechelp-posix.obj: exechelp-posix.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-exechelp-posix.obj -MD -MP -MF $(DEPDIR)/libcommon_a-exechelp-posix.Tpo -c -o libcommon_a-exechelp-posix.obj `if test -f 'exechelp-posix.c'; then $(CYGPATH_W) 'exechelp-posix.c'; else $(CYGPATH_W) '$(srcdir)/exechelp-posix.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-exechelp-posix.Tpo $(DEPDIR)/libcommon_a-exechelp-posix.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='exechelp-posix.c' object='libcommon_a-exechelp-posix.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-exechelp-posix.obj `if test -f 'exechelp-posix.c'; then $(CYGPATH_W) 'exechelp-posix.c'; else $(CYGPATH_W) '$(srcdir)/exechelp-posix.c'; fi` + +libcommon_a-get-passphrase.o: get-passphrase.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-get-passphrase.o -MD -MP -MF $(DEPDIR)/libcommon_a-get-passphrase.Tpo -c -o libcommon_a-get-passphrase.o `test -f 'get-passphrase.c' || echo '$(srcdir)/'`get-passphrase.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-get-passphrase.Tpo $(DEPDIR)/libcommon_a-get-passphrase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='get-passphrase.c' object='libcommon_a-get-passphrase.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-get-passphrase.o `test -f 'get-passphrase.c' || echo '$(srcdir)/'`get-passphrase.c + +libcommon_a-get-passphrase.obj: get-passphrase.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -MT libcommon_a-get-passphrase.obj -MD -MP -MF $(DEPDIR)/libcommon_a-get-passphrase.Tpo -c -o libcommon_a-get-passphrase.obj `if test -f 'get-passphrase.c'; then $(CYGPATH_W) 'get-passphrase.c'; else $(CYGPATH_W) '$(srcdir)/get-passphrase.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommon_a-get-passphrase.Tpo $(DEPDIR)/libcommon_a-get-passphrase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='get-passphrase.c' object='libcommon_a-get-passphrase.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommon_a_CFLAGS) $(CFLAGS) -c -o libcommon_a-get-passphrase.obj `if test -f 'get-passphrase.c'; then $(CYGPATH_W) 'get-passphrase.c'; else $(CYGPATH_W) '$(srcdir)/get-passphrase.c'; fi` + +libcommonpth_a-i18n.o: i18n.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-i18n.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-i18n.Tpo -c -o libcommonpth_a-i18n.o `test -f 'i18n.c' || echo '$(srcdir)/'`i18n.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-i18n.Tpo $(DEPDIR)/libcommonpth_a-i18n.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='i18n.c' object='libcommonpth_a-i18n.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-i18n.o `test -f 'i18n.c' || echo '$(srcdir)/'`i18n.c + +libcommonpth_a-i18n.obj: i18n.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-i18n.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-i18n.Tpo -c -o libcommonpth_a-i18n.obj `if test -f 'i18n.c'; then $(CYGPATH_W) 'i18n.c'; else $(CYGPATH_W) '$(srcdir)/i18n.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-i18n.Tpo $(DEPDIR)/libcommonpth_a-i18n.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='i18n.c' object='libcommonpth_a-i18n.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-i18n.obj `if test -f 'i18n.c'; then $(CYGPATH_W) 'i18n.c'; else $(CYGPATH_W) '$(srcdir)/i18n.c'; fi` + +libcommonpth_a-mapstrings.o: mapstrings.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-mapstrings.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-mapstrings.Tpo -c -o libcommonpth_a-mapstrings.o `test -f 'mapstrings.c' || echo '$(srcdir)/'`mapstrings.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-mapstrings.Tpo $(DEPDIR)/libcommonpth_a-mapstrings.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mapstrings.c' object='libcommonpth_a-mapstrings.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-mapstrings.o `test -f 'mapstrings.c' || echo '$(srcdir)/'`mapstrings.c + +libcommonpth_a-mapstrings.obj: mapstrings.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-mapstrings.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-mapstrings.Tpo -c -o libcommonpth_a-mapstrings.obj `if test -f 'mapstrings.c'; then $(CYGPATH_W) 'mapstrings.c'; else $(CYGPATH_W) '$(srcdir)/mapstrings.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-mapstrings.Tpo $(DEPDIR)/libcommonpth_a-mapstrings.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mapstrings.c' object='libcommonpth_a-mapstrings.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-mapstrings.obj `if test -f 'mapstrings.c'; then $(CYGPATH_W) 'mapstrings.c'; else $(CYGPATH_W) '$(srcdir)/mapstrings.c'; fi` + +libcommonpth_a-stringhelp.o: stringhelp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-stringhelp.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-stringhelp.Tpo -c -o libcommonpth_a-stringhelp.o `test -f 'stringhelp.c' || echo '$(srcdir)/'`stringhelp.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-stringhelp.Tpo $(DEPDIR)/libcommonpth_a-stringhelp.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='stringhelp.c' object='libcommonpth_a-stringhelp.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-stringhelp.o `test -f 'stringhelp.c' || echo '$(srcdir)/'`stringhelp.c + +libcommonpth_a-stringhelp.obj: stringhelp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-stringhelp.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-stringhelp.Tpo -c -o libcommonpth_a-stringhelp.obj `if test -f 'stringhelp.c'; then $(CYGPATH_W) 'stringhelp.c'; else $(CYGPATH_W) '$(srcdir)/stringhelp.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-stringhelp.Tpo $(DEPDIR)/libcommonpth_a-stringhelp.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='stringhelp.c' object='libcommonpth_a-stringhelp.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-stringhelp.obj `if test -f 'stringhelp.c'; then $(CYGPATH_W) 'stringhelp.c'; else $(CYGPATH_W) '$(srcdir)/stringhelp.c'; fi` + +libcommonpth_a-strlist.o: strlist.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-strlist.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-strlist.Tpo -c -o libcommonpth_a-strlist.o `test -f 'strlist.c' || echo '$(srcdir)/'`strlist.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-strlist.Tpo $(DEPDIR)/libcommonpth_a-strlist.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strlist.c' object='libcommonpth_a-strlist.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-strlist.o `test -f 'strlist.c' || echo '$(srcdir)/'`strlist.c + +libcommonpth_a-strlist.obj: strlist.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-strlist.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-strlist.Tpo -c -o libcommonpth_a-strlist.obj `if test -f 'strlist.c'; then $(CYGPATH_W) 'strlist.c'; else $(CYGPATH_W) '$(srcdir)/strlist.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-strlist.Tpo $(DEPDIR)/libcommonpth_a-strlist.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strlist.c' object='libcommonpth_a-strlist.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-strlist.obj `if test -f 'strlist.c'; then $(CYGPATH_W) 'strlist.c'; else $(CYGPATH_W) '$(srcdir)/strlist.c'; fi` + +libcommonpth_a-utf8conv.o: utf8conv.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-utf8conv.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-utf8conv.Tpo -c -o libcommonpth_a-utf8conv.o `test -f 'utf8conv.c' || echo '$(srcdir)/'`utf8conv.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-utf8conv.Tpo $(DEPDIR)/libcommonpth_a-utf8conv.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utf8conv.c' object='libcommonpth_a-utf8conv.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-utf8conv.o `test -f 'utf8conv.c' || echo '$(srcdir)/'`utf8conv.c + +libcommonpth_a-utf8conv.obj: utf8conv.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-utf8conv.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-utf8conv.Tpo -c -o libcommonpth_a-utf8conv.obj `if test -f 'utf8conv.c'; then $(CYGPATH_W) 'utf8conv.c'; else $(CYGPATH_W) '$(srcdir)/utf8conv.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-utf8conv.Tpo $(DEPDIR)/libcommonpth_a-utf8conv.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utf8conv.c' object='libcommonpth_a-utf8conv.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-utf8conv.obj `if test -f 'utf8conv.c'; then $(CYGPATH_W) 'utf8conv.c'; else $(CYGPATH_W) '$(srcdir)/utf8conv.c'; fi` + +libcommonpth_a-argparse.o: argparse.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-argparse.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-argparse.Tpo -c -o libcommonpth_a-argparse.o `test -f 'argparse.c' || echo '$(srcdir)/'`argparse.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-argparse.Tpo $(DEPDIR)/libcommonpth_a-argparse.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='argparse.c' object='libcommonpth_a-argparse.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-argparse.o `test -f 'argparse.c' || echo '$(srcdir)/'`argparse.c + +libcommonpth_a-argparse.obj: argparse.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-argparse.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-argparse.Tpo -c -o libcommonpth_a-argparse.obj `if test -f 'argparse.c'; then $(CYGPATH_W) 'argparse.c'; else $(CYGPATH_W) '$(srcdir)/argparse.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-argparse.Tpo $(DEPDIR)/libcommonpth_a-argparse.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='argparse.c' object='libcommonpth_a-argparse.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-argparse.obj `if test -f 'argparse.c'; then $(CYGPATH_W) 'argparse.c'; else $(CYGPATH_W) '$(srcdir)/argparse.c'; fi` + +libcommonpth_a-logging.o: logging.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-logging.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-logging.Tpo -c -o libcommonpth_a-logging.o `test -f 'logging.c' || echo '$(srcdir)/'`logging.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-logging.Tpo $(DEPDIR)/libcommonpth_a-logging.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='logging.c' object='libcommonpth_a-logging.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-logging.o `test -f 'logging.c' || echo '$(srcdir)/'`logging.c + +libcommonpth_a-logging.obj: logging.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-logging.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-logging.Tpo -c -o libcommonpth_a-logging.obj `if test -f 'logging.c'; then $(CYGPATH_W) 'logging.c'; else $(CYGPATH_W) '$(srcdir)/logging.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-logging.Tpo $(DEPDIR)/libcommonpth_a-logging.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='logging.c' object='libcommonpth_a-logging.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-logging.obj `if test -f 'logging.c'; then $(CYGPATH_W) 'logging.c'; else $(CYGPATH_W) '$(srcdir)/logging.c'; fi` + +libcommonpth_a-dotlock.o: dotlock.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-dotlock.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-dotlock.Tpo -c -o libcommonpth_a-dotlock.o `test -f 'dotlock.c' || echo '$(srcdir)/'`dotlock.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-dotlock.Tpo $(DEPDIR)/libcommonpth_a-dotlock.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dotlock.c' object='libcommonpth_a-dotlock.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-dotlock.o `test -f 'dotlock.c' || echo '$(srcdir)/'`dotlock.c + +libcommonpth_a-dotlock.obj: dotlock.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-dotlock.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-dotlock.Tpo -c -o libcommonpth_a-dotlock.obj `if test -f 'dotlock.c'; then $(CYGPATH_W) 'dotlock.c'; else $(CYGPATH_W) '$(srcdir)/dotlock.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-dotlock.Tpo $(DEPDIR)/libcommonpth_a-dotlock.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dotlock.c' object='libcommonpth_a-dotlock.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-dotlock.obj `if test -f 'dotlock.c'; then $(CYGPATH_W) 'dotlock.c'; else $(CYGPATH_W) '$(srcdir)/dotlock.c'; fi` + +libcommonpth_a-mischelp.o: mischelp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-mischelp.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-mischelp.Tpo -c -o libcommonpth_a-mischelp.o `test -f 'mischelp.c' || echo '$(srcdir)/'`mischelp.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-mischelp.Tpo $(DEPDIR)/libcommonpth_a-mischelp.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mischelp.c' object='libcommonpth_a-mischelp.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-mischelp.o `test -f 'mischelp.c' || echo '$(srcdir)/'`mischelp.c + +libcommonpth_a-mischelp.obj: mischelp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-mischelp.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-mischelp.Tpo -c -o libcommonpth_a-mischelp.obj `if test -f 'mischelp.c'; then $(CYGPATH_W) 'mischelp.c'; else $(CYGPATH_W) '$(srcdir)/mischelp.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-mischelp.Tpo $(DEPDIR)/libcommonpth_a-mischelp.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mischelp.c' object='libcommonpth_a-mischelp.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-mischelp.obj `if test -f 'mischelp.c'; then $(CYGPATH_W) 'mischelp.c'; else $(CYGPATH_W) '$(srcdir)/mischelp.c'; fi` + +libcommonpth_a-status.o: status.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-status.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-status.Tpo -c -o libcommonpth_a-status.o `test -f 'status.c' || echo '$(srcdir)/'`status.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-status.Tpo $(DEPDIR)/libcommonpth_a-status.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='status.c' object='libcommonpth_a-status.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-status.o `test -f 'status.c' || echo '$(srcdir)/'`status.c + +libcommonpth_a-status.obj: status.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-status.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-status.Tpo -c -o libcommonpth_a-status.obj `if test -f 'status.c'; then $(CYGPATH_W) 'status.c'; else $(CYGPATH_W) '$(srcdir)/status.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-status.Tpo $(DEPDIR)/libcommonpth_a-status.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='status.c' object='libcommonpth_a-status.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-status.obj `if test -f 'status.c'; then $(CYGPATH_W) 'status.c'; else $(CYGPATH_W) '$(srcdir)/status.c'; fi` + +libcommonpth_a-tlv.o: tlv.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-tlv.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-tlv.Tpo -c -o libcommonpth_a-tlv.o `test -f 'tlv.c' || echo '$(srcdir)/'`tlv.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-tlv.Tpo $(DEPDIR)/libcommonpth_a-tlv.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tlv.c' object='libcommonpth_a-tlv.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-tlv.o `test -f 'tlv.c' || echo '$(srcdir)/'`tlv.c + +libcommonpth_a-tlv.obj: tlv.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-tlv.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-tlv.Tpo -c -o libcommonpth_a-tlv.obj `if test -f 'tlv.c'; then $(CYGPATH_W) 'tlv.c'; else $(CYGPATH_W) '$(srcdir)/tlv.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-tlv.Tpo $(DEPDIR)/libcommonpth_a-tlv.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tlv.c' object='libcommonpth_a-tlv.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-tlv.obj `if test -f 'tlv.c'; then $(CYGPATH_W) 'tlv.c'; else $(CYGPATH_W) '$(srcdir)/tlv.c'; fi` + +libcommonpth_a-tlv-builder.o: tlv-builder.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-tlv-builder.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-tlv-builder.Tpo -c -o libcommonpth_a-tlv-builder.o `test -f 'tlv-builder.c' || echo '$(srcdir)/'`tlv-builder.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-tlv-builder.Tpo $(DEPDIR)/libcommonpth_a-tlv-builder.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tlv-builder.c' object='libcommonpth_a-tlv-builder.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-tlv-builder.o `test -f 'tlv-builder.c' || echo '$(srcdir)/'`tlv-builder.c + +libcommonpth_a-tlv-builder.obj: tlv-builder.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-tlv-builder.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-tlv-builder.Tpo -c -o libcommonpth_a-tlv-builder.obj `if test -f 'tlv-builder.c'; then $(CYGPATH_W) 'tlv-builder.c'; else $(CYGPATH_W) '$(srcdir)/tlv-builder.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-tlv-builder.Tpo $(DEPDIR)/libcommonpth_a-tlv-builder.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tlv-builder.c' object='libcommonpth_a-tlv-builder.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-tlv-builder.obj `if test -f 'tlv-builder.c'; then $(CYGPATH_W) 'tlv-builder.c'; else $(CYGPATH_W) '$(srcdir)/tlv-builder.c'; fi` + +libcommonpth_a-init.o: init.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-init.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-init.Tpo -c -o libcommonpth_a-init.o `test -f 'init.c' || echo '$(srcdir)/'`init.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-init.Tpo $(DEPDIR)/libcommonpth_a-init.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='init.c' object='libcommonpth_a-init.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-init.o `test -f 'init.c' || echo '$(srcdir)/'`init.c + +libcommonpth_a-init.obj: init.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-init.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-init.Tpo -c -o libcommonpth_a-init.obj `if test -f 'init.c'; then $(CYGPATH_W) 'init.c'; else $(CYGPATH_W) '$(srcdir)/init.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-init.Tpo $(DEPDIR)/libcommonpth_a-init.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='init.c' object='libcommonpth_a-init.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-init.obj `if test -f 'init.c'; then $(CYGPATH_W) 'init.c'; else $(CYGPATH_W) '$(srcdir)/init.c'; fi` + +libcommonpth_a-sexputil.o: sexputil.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-sexputil.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-sexputil.Tpo -c -o libcommonpth_a-sexputil.o `test -f 'sexputil.c' || echo '$(srcdir)/'`sexputil.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-sexputil.Tpo $(DEPDIR)/libcommonpth_a-sexputil.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sexputil.c' object='libcommonpth_a-sexputil.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-sexputil.o `test -f 'sexputil.c' || echo '$(srcdir)/'`sexputil.c + +libcommonpth_a-sexputil.obj: sexputil.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-sexputil.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-sexputil.Tpo -c -o libcommonpth_a-sexputil.obj `if test -f 'sexputil.c'; then $(CYGPATH_W) 'sexputil.c'; else $(CYGPATH_W) '$(srcdir)/sexputil.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-sexputil.Tpo $(DEPDIR)/libcommonpth_a-sexputil.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sexputil.c' object='libcommonpth_a-sexputil.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-sexputil.obj `if test -f 'sexputil.c'; then $(CYGPATH_W) 'sexputil.c'; else $(CYGPATH_W) '$(srcdir)/sexputil.c'; fi` + +libcommonpth_a-sysutils.o: sysutils.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-sysutils.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-sysutils.Tpo -c -o libcommonpth_a-sysutils.o `test -f 'sysutils.c' || echo '$(srcdir)/'`sysutils.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-sysutils.Tpo $(DEPDIR)/libcommonpth_a-sysutils.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sysutils.c' object='libcommonpth_a-sysutils.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-sysutils.o `test -f 'sysutils.c' || echo '$(srcdir)/'`sysutils.c + +libcommonpth_a-sysutils.obj: sysutils.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-sysutils.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-sysutils.Tpo -c -o libcommonpth_a-sysutils.obj `if test -f 'sysutils.c'; then $(CYGPATH_W) 'sysutils.c'; else $(CYGPATH_W) '$(srcdir)/sysutils.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-sysutils.Tpo $(DEPDIR)/libcommonpth_a-sysutils.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sysutils.c' object='libcommonpth_a-sysutils.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-sysutils.obj `if test -f 'sysutils.c'; then $(CYGPATH_W) 'sysutils.c'; else $(CYGPATH_W) '$(srcdir)/sysutils.c'; fi` + +libcommonpth_a-homedir.o: homedir.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-homedir.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-homedir.Tpo -c -o libcommonpth_a-homedir.o `test -f 'homedir.c' || echo '$(srcdir)/'`homedir.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-homedir.Tpo $(DEPDIR)/libcommonpth_a-homedir.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='homedir.c' object='libcommonpth_a-homedir.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-homedir.o `test -f 'homedir.c' || echo '$(srcdir)/'`homedir.c + +libcommonpth_a-homedir.obj: homedir.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-homedir.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-homedir.Tpo -c -o libcommonpth_a-homedir.obj `if test -f 'homedir.c'; then $(CYGPATH_W) 'homedir.c'; else $(CYGPATH_W) '$(srcdir)/homedir.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-homedir.Tpo $(DEPDIR)/libcommonpth_a-homedir.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='homedir.c' object='libcommonpth_a-homedir.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-homedir.obj `if test -f 'homedir.c'; then $(CYGPATH_W) 'homedir.c'; else $(CYGPATH_W) '$(srcdir)/homedir.c'; fi` + +libcommonpth_a-gettime.o: gettime.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-gettime.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-gettime.Tpo -c -o libcommonpth_a-gettime.o `test -f 'gettime.c' || echo '$(srcdir)/'`gettime.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-gettime.Tpo $(DEPDIR)/libcommonpth_a-gettime.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gettime.c' object='libcommonpth_a-gettime.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-gettime.o `test -f 'gettime.c' || echo '$(srcdir)/'`gettime.c + +libcommonpth_a-gettime.obj: gettime.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-gettime.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-gettime.Tpo -c -o libcommonpth_a-gettime.obj `if test -f 'gettime.c'; then $(CYGPATH_W) 'gettime.c'; else $(CYGPATH_W) '$(srcdir)/gettime.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-gettime.Tpo $(DEPDIR)/libcommonpth_a-gettime.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gettime.c' object='libcommonpth_a-gettime.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-gettime.obj `if test -f 'gettime.c'; then $(CYGPATH_W) 'gettime.c'; else $(CYGPATH_W) '$(srcdir)/gettime.c'; fi` + +libcommonpth_a-yesno.o: yesno.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-yesno.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-yesno.Tpo -c -o libcommonpth_a-yesno.o `test -f 'yesno.c' || echo '$(srcdir)/'`yesno.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-yesno.Tpo $(DEPDIR)/libcommonpth_a-yesno.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='yesno.c' object='libcommonpth_a-yesno.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-yesno.o `test -f 'yesno.c' || echo '$(srcdir)/'`yesno.c + +libcommonpth_a-yesno.obj: yesno.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-yesno.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-yesno.Tpo -c -o libcommonpth_a-yesno.obj `if test -f 'yesno.c'; then $(CYGPATH_W) 'yesno.c'; else $(CYGPATH_W) '$(srcdir)/yesno.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-yesno.Tpo $(DEPDIR)/libcommonpth_a-yesno.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='yesno.c' object='libcommonpth_a-yesno.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-yesno.obj `if test -f 'yesno.c'; then $(CYGPATH_W) 'yesno.c'; else $(CYGPATH_W) '$(srcdir)/yesno.c'; fi` + +libcommonpth_a-b64enc.o: b64enc.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-b64enc.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-b64enc.Tpo -c -o libcommonpth_a-b64enc.o `test -f 'b64enc.c' || echo '$(srcdir)/'`b64enc.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-b64enc.Tpo $(DEPDIR)/libcommonpth_a-b64enc.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='b64enc.c' object='libcommonpth_a-b64enc.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-b64enc.o `test -f 'b64enc.c' || echo '$(srcdir)/'`b64enc.c + +libcommonpth_a-b64enc.obj: b64enc.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-b64enc.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-b64enc.Tpo -c -o libcommonpth_a-b64enc.obj `if test -f 'b64enc.c'; then $(CYGPATH_W) 'b64enc.c'; else $(CYGPATH_W) '$(srcdir)/b64enc.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-b64enc.Tpo $(DEPDIR)/libcommonpth_a-b64enc.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='b64enc.c' object='libcommonpth_a-b64enc.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-b64enc.obj `if test -f 'b64enc.c'; then $(CYGPATH_W) 'b64enc.c'; else $(CYGPATH_W) '$(srcdir)/b64enc.c'; fi` + +libcommonpth_a-b64dec.o: b64dec.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-b64dec.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-b64dec.Tpo -c -o libcommonpth_a-b64dec.o `test -f 'b64dec.c' || echo '$(srcdir)/'`b64dec.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-b64dec.Tpo $(DEPDIR)/libcommonpth_a-b64dec.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='b64dec.c' object='libcommonpth_a-b64dec.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-b64dec.o `test -f 'b64dec.c' || echo '$(srcdir)/'`b64dec.c + +libcommonpth_a-b64dec.obj: b64dec.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-b64dec.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-b64dec.Tpo -c -o libcommonpth_a-b64dec.obj `if test -f 'b64dec.c'; then $(CYGPATH_W) 'b64dec.c'; else $(CYGPATH_W) '$(srcdir)/b64dec.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-b64dec.Tpo $(DEPDIR)/libcommonpth_a-b64dec.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='b64dec.c' object='libcommonpth_a-b64dec.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-b64dec.obj `if test -f 'b64dec.c'; then $(CYGPATH_W) 'b64dec.c'; else $(CYGPATH_W) '$(srcdir)/b64dec.c'; fi` + +libcommonpth_a-zb32.o: zb32.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-zb32.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-zb32.Tpo -c -o libcommonpth_a-zb32.o `test -f 'zb32.c' || echo '$(srcdir)/'`zb32.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-zb32.Tpo $(DEPDIR)/libcommonpth_a-zb32.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='zb32.c' object='libcommonpth_a-zb32.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-zb32.o `test -f 'zb32.c' || echo '$(srcdir)/'`zb32.c + +libcommonpth_a-zb32.obj: zb32.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-zb32.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-zb32.Tpo -c -o libcommonpth_a-zb32.obj `if test -f 'zb32.c'; then $(CYGPATH_W) 'zb32.c'; else $(CYGPATH_W) '$(srcdir)/zb32.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-zb32.Tpo $(DEPDIR)/libcommonpth_a-zb32.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='zb32.c' object='libcommonpth_a-zb32.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-zb32.obj `if test -f 'zb32.c'; then $(CYGPATH_W) 'zb32.c'; else $(CYGPATH_W) '$(srcdir)/zb32.c'; fi` + +libcommonpth_a-convert.o: convert.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-convert.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-convert.Tpo -c -o libcommonpth_a-convert.o `test -f 'convert.c' || echo '$(srcdir)/'`convert.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-convert.Tpo $(DEPDIR)/libcommonpth_a-convert.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='convert.c' object='libcommonpth_a-convert.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-convert.o `test -f 'convert.c' || echo '$(srcdir)/'`convert.c + +libcommonpth_a-convert.obj: convert.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-convert.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-convert.Tpo -c -o libcommonpth_a-convert.obj `if test -f 'convert.c'; then $(CYGPATH_W) 'convert.c'; else $(CYGPATH_W) '$(srcdir)/convert.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-convert.Tpo $(DEPDIR)/libcommonpth_a-convert.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='convert.c' object='libcommonpth_a-convert.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-convert.obj `if test -f 'convert.c'; then $(CYGPATH_W) 'convert.c'; else $(CYGPATH_W) '$(srcdir)/convert.c'; fi` + +libcommonpth_a-percent.o: percent.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-percent.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-percent.Tpo -c -o libcommonpth_a-percent.o `test -f 'percent.c' || echo '$(srcdir)/'`percent.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-percent.Tpo $(DEPDIR)/libcommonpth_a-percent.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='percent.c' object='libcommonpth_a-percent.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-percent.o `test -f 'percent.c' || echo '$(srcdir)/'`percent.c + +libcommonpth_a-percent.obj: percent.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-percent.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-percent.Tpo -c -o libcommonpth_a-percent.obj `if test -f 'percent.c'; then $(CYGPATH_W) 'percent.c'; else $(CYGPATH_W) '$(srcdir)/percent.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-percent.Tpo $(DEPDIR)/libcommonpth_a-percent.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='percent.c' object='libcommonpth_a-percent.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-percent.obj `if test -f 'percent.c'; then $(CYGPATH_W) 'percent.c'; else $(CYGPATH_W) '$(srcdir)/percent.c'; fi` + +libcommonpth_a-mbox-util.o: mbox-util.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-mbox-util.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-mbox-util.Tpo -c -o libcommonpth_a-mbox-util.o `test -f 'mbox-util.c' || echo '$(srcdir)/'`mbox-util.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-mbox-util.Tpo $(DEPDIR)/libcommonpth_a-mbox-util.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mbox-util.c' object='libcommonpth_a-mbox-util.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-mbox-util.o `test -f 'mbox-util.c' || echo '$(srcdir)/'`mbox-util.c + +libcommonpth_a-mbox-util.obj: mbox-util.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-mbox-util.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-mbox-util.Tpo -c -o libcommonpth_a-mbox-util.obj `if test -f 'mbox-util.c'; then $(CYGPATH_W) 'mbox-util.c'; else $(CYGPATH_W) '$(srcdir)/mbox-util.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-mbox-util.Tpo $(DEPDIR)/libcommonpth_a-mbox-util.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mbox-util.c' object='libcommonpth_a-mbox-util.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-mbox-util.obj `if test -f 'mbox-util.c'; then $(CYGPATH_W) 'mbox-util.c'; else $(CYGPATH_W) '$(srcdir)/mbox-util.c'; fi` + +libcommonpth_a-miscellaneous.o: miscellaneous.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-miscellaneous.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-miscellaneous.Tpo -c -o libcommonpth_a-miscellaneous.o `test -f 'miscellaneous.c' || echo '$(srcdir)/'`miscellaneous.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-miscellaneous.Tpo $(DEPDIR)/libcommonpth_a-miscellaneous.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='miscellaneous.c' object='libcommonpth_a-miscellaneous.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-miscellaneous.o `test -f 'miscellaneous.c' || echo '$(srcdir)/'`miscellaneous.c + +libcommonpth_a-miscellaneous.obj: miscellaneous.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-miscellaneous.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-miscellaneous.Tpo -c -o libcommonpth_a-miscellaneous.obj `if test -f 'miscellaneous.c'; then $(CYGPATH_W) 'miscellaneous.c'; else $(CYGPATH_W) '$(srcdir)/miscellaneous.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-miscellaneous.Tpo $(DEPDIR)/libcommonpth_a-miscellaneous.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='miscellaneous.c' object='libcommonpth_a-miscellaneous.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-miscellaneous.obj `if test -f 'miscellaneous.c'; then $(CYGPATH_W) 'miscellaneous.c'; else $(CYGPATH_W) '$(srcdir)/miscellaneous.c'; fi` + +libcommonpth_a-xasprintf.o: xasprintf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-xasprintf.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-xasprintf.Tpo -c -o libcommonpth_a-xasprintf.o `test -f 'xasprintf.c' || echo '$(srcdir)/'`xasprintf.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-xasprintf.Tpo $(DEPDIR)/libcommonpth_a-xasprintf.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='xasprintf.c' object='libcommonpth_a-xasprintf.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-xasprintf.o `test -f 'xasprintf.c' || echo '$(srcdir)/'`xasprintf.c + +libcommonpth_a-xasprintf.obj: xasprintf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-xasprintf.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-xasprintf.Tpo -c -o libcommonpth_a-xasprintf.obj `if test -f 'xasprintf.c'; then $(CYGPATH_W) 'xasprintf.c'; else $(CYGPATH_W) '$(srcdir)/xasprintf.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-xasprintf.Tpo $(DEPDIR)/libcommonpth_a-xasprintf.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='xasprintf.c' object='libcommonpth_a-xasprintf.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-xasprintf.obj `if test -f 'xasprintf.c'; then $(CYGPATH_W) 'xasprintf.c'; else $(CYGPATH_W) '$(srcdir)/xasprintf.c'; fi` + +libcommonpth_a-xreadline.o: xreadline.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-xreadline.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-xreadline.Tpo -c -o libcommonpth_a-xreadline.o `test -f 'xreadline.c' || echo '$(srcdir)/'`xreadline.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-xreadline.Tpo $(DEPDIR)/libcommonpth_a-xreadline.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='xreadline.c' object='libcommonpth_a-xreadline.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-xreadline.o `test -f 'xreadline.c' || echo '$(srcdir)/'`xreadline.c + +libcommonpth_a-xreadline.obj: xreadline.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-xreadline.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-xreadline.Tpo -c -o libcommonpth_a-xreadline.obj `if test -f 'xreadline.c'; then $(CYGPATH_W) 'xreadline.c'; else $(CYGPATH_W) '$(srcdir)/xreadline.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-xreadline.Tpo $(DEPDIR)/libcommonpth_a-xreadline.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='xreadline.c' object='libcommonpth_a-xreadline.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-xreadline.obj `if test -f 'xreadline.c'; then $(CYGPATH_W) 'xreadline.c'; else $(CYGPATH_W) '$(srcdir)/xreadline.c'; fi` + +libcommonpth_a-membuf.o: membuf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-membuf.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-membuf.Tpo -c -o libcommonpth_a-membuf.o `test -f 'membuf.c' || echo '$(srcdir)/'`membuf.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-membuf.Tpo $(DEPDIR)/libcommonpth_a-membuf.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='membuf.c' object='libcommonpth_a-membuf.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-membuf.o `test -f 'membuf.c' || echo '$(srcdir)/'`membuf.c + +libcommonpth_a-membuf.obj: membuf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-membuf.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-membuf.Tpo -c -o libcommonpth_a-membuf.obj `if test -f 'membuf.c'; then $(CYGPATH_W) 'membuf.c'; else $(CYGPATH_W) '$(srcdir)/membuf.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-membuf.Tpo $(DEPDIR)/libcommonpth_a-membuf.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='membuf.c' object='libcommonpth_a-membuf.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-membuf.obj `if test -f 'membuf.c'; then $(CYGPATH_W) 'membuf.c'; else $(CYGPATH_W) '$(srcdir)/membuf.c'; fi` + +libcommonpth_a-ccparray.o: ccparray.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-ccparray.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-ccparray.Tpo -c -o libcommonpth_a-ccparray.o `test -f 'ccparray.c' || echo '$(srcdir)/'`ccparray.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-ccparray.Tpo $(DEPDIR)/libcommonpth_a-ccparray.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ccparray.c' object='libcommonpth_a-ccparray.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-ccparray.o `test -f 'ccparray.c' || echo '$(srcdir)/'`ccparray.c + +libcommonpth_a-ccparray.obj: ccparray.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-ccparray.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-ccparray.Tpo -c -o libcommonpth_a-ccparray.obj `if test -f 'ccparray.c'; then $(CYGPATH_W) 'ccparray.c'; else $(CYGPATH_W) '$(srcdir)/ccparray.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-ccparray.Tpo $(DEPDIR)/libcommonpth_a-ccparray.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ccparray.c' object='libcommonpth_a-ccparray.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-ccparray.obj `if test -f 'ccparray.c'; then $(CYGPATH_W) 'ccparray.c'; else $(CYGPATH_W) '$(srcdir)/ccparray.c'; fi` + +libcommonpth_a-iobuf.o: iobuf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-iobuf.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-iobuf.Tpo -c -o libcommonpth_a-iobuf.o `test -f 'iobuf.c' || echo '$(srcdir)/'`iobuf.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-iobuf.Tpo $(DEPDIR)/libcommonpth_a-iobuf.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='iobuf.c' object='libcommonpth_a-iobuf.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-iobuf.o `test -f 'iobuf.c' || echo '$(srcdir)/'`iobuf.c + +libcommonpth_a-iobuf.obj: iobuf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-iobuf.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-iobuf.Tpo -c -o libcommonpth_a-iobuf.obj `if test -f 'iobuf.c'; then $(CYGPATH_W) 'iobuf.c'; else $(CYGPATH_W) '$(srcdir)/iobuf.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-iobuf.Tpo $(DEPDIR)/libcommonpth_a-iobuf.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='iobuf.c' object='libcommonpth_a-iobuf.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-iobuf.obj `if test -f 'iobuf.c'; then $(CYGPATH_W) 'iobuf.c'; else $(CYGPATH_W) '$(srcdir)/iobuf.c'; fi` + +libcommonpth_a-ttyio.o: ttyio.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-ttyio.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-ttyio.Tpo -c -o libcommonpth_a-ttyio.o `test -f 'ttyio.c' || echo '$(srcdir)/'`ttyio.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-ttyio.Tpo $(DEPDIR)/libcommonpth_a-ttyio.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ttyio.c' object='libcommonpth_a-ttyio.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-ttyio.o `test -f 'ttyio.c' || echo '$(srcdir)/'`ttyio.c + +libcommonpth_a-ttyio.obj: ttyio.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-ttyio.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-ttyio.Tpo -c -o libcommonpth_a-ttyio.obj `if test -f 'ttyio.c'; then $(CYGPATH_W) 'ttyio.c'; else $(CYGPATH_W) '$(srcdir)/ttyio.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-ttyio.Tpo $(DEPDIR)/libcommonpth_a-ttyio.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ttyio.c' object='libcommonpth_a-ttyio.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-ttyio.obj `if test -f 'ttyio.c'; then $(CYGPATH_W) 'ttyio.c'; else $(CYGPATH_W) '$(srcdir)/ttyio.c'; fi` + +libcommonpth_a-asshelp.o: asshelp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-asshelp.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-asshelp.Tpo -c -o libcommonpth_a-asshelp.o `test -f 'asshelp.c' || echo '$(srcdir)/'`asshelp.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-asshelp.Tpo $(DEPDIR)/libcommonpth_a-asshelp.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='asshelp.c' object='libcommonpth_a-asshelp.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-asshelp.o `test -f 'asshelp.c' || echo '$(srcdir)/'`asshelp.c + +libcommonpth_a-asshelp.obj: asshelp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-asshelp.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-asshelp.Tpo -c -o libcommonpth_a-asshelp.obj `if test -f 'asshelp.c'; then $(CYGPATH_W) 'asshelp.c'; else $(CYGPATH_W) '$(srcdir)/asshelp.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-asshelp.Tpo $(DEPDIR)/libcommonpth_a-asshelp.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='asshelp.c' object='libcommonpth_a-asshelp.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-asshelp.obj `if test -f 'asshelp.c'; then $(CYGPATH_W) 'asshelp.c'; else $(CYGPATH_W) '$(srcdir)/asshelp.c'; fi` + +libcommonpth_a-asshelp2.o: asshelp2.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-asshelp2.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-asshelp2.Tpo -c -o libcommonpth_a-asshelp2.o `test -f 'asshelp2.c' || echo '$(srcdir)/'`asshelp2.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-asshelp2.Tpo $(DEPDIR)/libcommonpth_a-asshelp2.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='asshelp2.c' object='libcommonpth_a-asshelp2.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-asshelp2.o `test -f 'asshelp2.c' || echo '$(srcdir)/'`asshelp2.c + +libcommonpth_a-asshelp2.obj: asshelp2.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-asshelp2.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-asshelp2.Tpo -c -o libcommonpth_a-asshelp2.obj `if test -f 'asshelp2.c'; then $(CYGPATH_W) 'asshelp2.c'; else $(CYGPATH_W) '$(srcdir)/asshelp2.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-asshelp2.Tpo $(DEPDIR)/libcommonpth_a-asshelp2.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='asshelp2.c' object='libcommonpth_a-asshelp2.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-asshelp2.obj `if test -f 'asshelp2.c'; then $(CYGPATH_W) 'asshelp2.c'; else $(CYGPATH_W) '$(srcdir)/asshelp2.c'; fi` + +libcommonpth_a-signal.o: signal.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-signal.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-signal.Tpo -c -o libcommonpth_a-signal.o `test -f 'signal.c' || echo '$(srcdir)/'`signal.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-signal.Tpo $(DEPDIR)/libcommonpth_a-signal.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='signal.c' object='libcommonpth_a-signal.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-signal.o `test -f 'signal.c' || echo '$(srcdir)/'`signal.c + +libcommonpth_a-signal.obj: signal.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-signal.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-signal.Tpo -c -o libcommonpth_a-signal.obj `if test -f 'signal.c'; then $(CYGPATH_W) 'signal.c'; else $(CYGPATH_W) '$(srcdir)/signal.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-signal.Tpo $(DEPDIR)/libcommonpth_a-signal.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='signal.c' object='libcommonpth_a-signal.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-signal.obj `if test -f 'signal.c'; then $(CYGPATH_W) 'signal.c'; else $(CYGPATH_W) '$(srcdir)/signal.c'; fi` + +libcommonpth_a-audit.o: audit.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-audit.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-audit.Tpo -c -o libcommonpth_a-audit.o `test -f 'audit.c' || echo '$(srcdir)/'`audit.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-audit.Tpo $(DEPDIR)/libcommonpth_a-audit.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='audit.c' object='libcommonpth_a-audit.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-audit.o `test -f 'audit.c' || echo '$(srcdir)/'`audit.c + +libcommonpth_a-audit.obj: audit.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-audit.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-audit.Tpo -c -o libcommonpth_a-audit.obj `if test -f 'audit.c'; then $(CYGPATH_W) 'audit.c'; else $(CYGPATH_W) '$(srcdir)/audit.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-audit.Tpo $(DEPDIR)/libcommonpth_a-audit.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='audit.c' object='libcommonpth_a-audit.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-audit.obj `if test -f 'audit.c'; then $(CYGPATH_W) 'audit.c'; else $(CYGPATH_W) '$(srcdir)/audit.c'; fi` + +libcommonpth_a-localename.o: localename.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-localename.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-localename.Tpo -c -o libcommonpth_a-localename.o `test -f 'localename.c' || echo '$(srcdir)/'`localename.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-localename.Tpo $(DEPDIR)/libcommonpth_a-localename.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='localename.c' object='libcommonpth_a-localename.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-localename.o `test -f 'localename.c' || echo '$(srcdir)/'`localename.c + +libcommonpth_a-localename.obj: localename.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-localename.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-localename.Tpo -c -o libcommonpth_a-localename.obj `if test -f 'localename.c'; then $(CYGPATH_W) 'localename.c'; else $(CYGPATH_W) '$(srcdir)/localename.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-localename.Tpo $(DEPDIR)/libcommonpth_a-localename.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='localename.c' object='libcommonpth_a-localename.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-localename.obj `if test -f 'localename.c'; then $(CYGPATH_W) 'localename.c'; else $(CYGPATH_W) '$(srcdir)/localename.c'; fi` + +libcommonpth_a-session-env.o: session-env.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-session-env.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-session-env.Tpo -c -o libcommonpth_a-session-env.o `test -f 'session-env.c' || echo '$(srcdir)/'`session-env.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-session-env.Tpo $(DEPDIR)/libcommonpth_a-session-env.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='session-env.c' object='libcommonpth_a-session-env.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-session-env.o `test -f 'session-env.c' || echo '$(srcdir)/'`session-env.c + +libcommonpth_a-session-env.obj: session-env.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-session-env.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-session-env.Tpo -c -o libcommonpth_a-session-env.obj `if test -f 'session-env.c'; then $(CYGPATH_W) 'session-env.c'; else $(CYGPATH_W) '$(srcdir)/session-env.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-session-env.Tpo $(DEPDIR)/libcommonpth_a-session-env.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='session-env.c' object='libcommonpth_a-session-env.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-session-env.obj `if test -f 'session-env.c'; then $(CYGPATH_W) 'session-env.c'; else $(CYGPATH_W) '$(srcdir)/session-env.c'; fi` + +libcommonpth_a-userids.o: userids.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-userids.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-userids.Tpo -c -o libcommonpth_a-userids.o `test -f 'userids.c' || echo '$(srcdir)/'`userids.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-userids.Tpo $(DEPDIR)/libcommonpth_a-userids.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='userids.c' object='libcommonpth_a-userids.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-userids.o `test -f 'userids.c' || echo '$(srcdir)/'`userids.c + +libcommonpth_a-userids.obj: userids.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-userids.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-userids.Tpo -c -o libcommonpth_a-userids.obj `if test -f 'userids.c'; then $(CYGPATH_W) 'userids.c'; else $(CYGPATH_W) '$(srcdir)/userids.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-userids.Tpo $(DEPDIR)/libcommonpth_a-userids.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='userids.c' object='libcommonpth_a-userids.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-userids.obj `if test -f 'userids.c'; then $(CYGPATH_W) 'userids.c'; else $(CYGPATH_W) '$(srcdir)/userids.c'; fi` + +libcommonpth_a-openpgp-oid.o: openpgp-oid.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-openpgp-oid.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-openpgp-oid.Tpo -c -o libcommonpth_a-openpgp-oid.o `test -f 'openpgp-oid.c' || echo '$(srcdir)/'`openpgp-oid.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-openpgp-oid.Tpo $(DEPDIR)/libcommonpth_a-openpgp-oid.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='openpgp-oid.c' object='libcommonpth_a-openpgp-oid.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-openpgp-oid.o `test -f 'openpgp-oid.c' || echo '$(srcdir)/'`openpgp-oid.c + +libcommonpth_a-openpgp-oid.obj: openpgp-oid.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-openpgp-oid.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-openpgp-oid.Tpo -c -o libcommonpth_a-openpgp-oid.obj `if test -f 'openpgp-oid.c'; then $(CYGPATH_W) 'openpgp-oid.c'; else $(CYGPATH_W) '$(srcdir)/openpgp-oid.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-openpgp-oid.Tpo $(DEPDIR)/libcommonpth_a-openpgp-oid.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='openpgp-oid.c' object='libcommonpth_a-openpgp-oid.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-openpgp-oid.obj `if test -f 'openpgp-oid.c'; then $(CYGPATH_W) 'openpgp-oid.c'; else $(CYGPATH_W) '$(srcdir)/openpgp-oid.c'; fi` + +libcommonpth_a-ssh-utils.o: ssh-utils.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-ssh-utils.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-ssh-utils.Tpo -c -o libcommonpth_a-ssh-utils.o `test -f 'ssh-utils.c' || echo '$(srcdir)/'`ssh-utils.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-ssh-utils.Tpo $(DEPDIR)/libcommonpth_a-ssh-utils.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ssh-utils.c' object='libcommonpth_a-ssh-utils.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-ssh-utils.o `test -f 'ssh-utils.c' || echo '$(srcdir)/'`ssh-utils.c + +libcommonpth_a-ssh-utils.obj: ssh-utils.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-ssh-utils.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-ssh-utils.Tpo -c -o libcommonpth_a-ssh-utils.obj `if test -f 'ssh-utils.c'; then $(CYGPATH_W) 'ssh-utils.c'; else $(CYGPATH_W) '$(srcdir)/ssh-utils.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-ssh-utils.Tpo $(DEPDIR)/libcommonpth_a-ssh-utils.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ssh-utils.c' object='libcommonpth_a-ssh-utils.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-ssh-utils.obj `if test -f 'ssh-utils.c'; then $(CYGPATH_W) 'ssh-utils.c'; else $(CYGPATH_W) '$(srcdir)/ssh-utils.c'; fi` + +libcommonpth_a-agent-opt.o: agent-opt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-agent-opt.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-agent-opt.Tpo -c -o libcommonpth_a-agent-opt.o `test -f 'agent-opt.c' || echo '$(srcdir)/'`agent-opt.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-agent-opt.Tpo $(DEPDIR)/libcommonpth_a-agent-opt.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='agent-opt.c' object='libcommonpth_a-agent-opt.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-agent-opt.o `test -f 'agent-opt.c' || echo '$(srcdir)/'`agent-opt.c + +libcommonpth_a-agent-opt.obj: agent-opt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-agent-opt.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-agent-opt.Tpo -c -o libcommonpth_a-agent-opt.obj `if test -f 'agent-opt.c'; then $(CYGPATH_W) 'agent-opt.c'; else $(CYGPATH_W) '$(srcdir)/agent-opt.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-agent-opt.Tpo $(DEPDIR)/libcommonpth_a-agent-opt.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='agent-opt.c' object='libcommonpth_a-agent-opt.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-agent-opt.obj `if test -f 'agent-opt.c'; then $(CYGPATH_W) 'agent-opt.c'; else $(CYGPATH_W) '$(srcdir)/agent-opt.c'; fi` + +libcommonpth_a-helpfile.o: helpfile.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-helpfile.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-helpfile.Tpo -c -o libcommonpth_a-helpfile.o `test -f 'helpfile.c' || echo '$(srcdir)/'`helpfile.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-helpfile.Tpo $(DEPDIR)/libcommonpth_a-helpfile.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='helpfile.c' object='libcommonpth_a-helpfile.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-helpfile.o `test -f 'helpfile.c' || echo '$(srcdir)/'`helpfile.c + +libcommonpth_a-helpfile.obj: helpfile.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-helpfile.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-helpfile.Tpo -c -o libcommonpth_a-helpfile.obj `if test -f 'helpfile.c'; then $(CYGPATH_W) 'helpfile.c'; else $(CYGPATH_W) '$(srcdir)/helpfile.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-helpfile.Tpo $(DEPDIR)/libcommonpth_a-helpfile.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='helpfile.c' object='libcommonpth_a-helpfile.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-helpfile.obj `if test -f 'helpfile.c'; then $(CYGPATH_W) 'helpfile.c'; else $(CYGPATH_W) '$(srcdir)/helpfile.c'; fi` + +libcommonpth_a-mkdir_p.o: mkdir_p.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-mkdir_p.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-mkdir_p.Tpo -c -o libcommonpth_a-mkdir_p.o `test -f 'mkdir_p.c' || echo '$(srcdir)/'`mkdir_p.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-mkdir_p.Tpo $(DEPDIR)/libcommonpth_a-mkdir_p.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mkdir_p.c' object='libcommonpth_a-mkdir_p.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-mkdir_p.o `test -f 'mkdir_p.c' || echo '$(srcdir)/'`mkdir_p.c + +libcommonpth_a-mkdir_p.obj: mkdir_p.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-mkdir_p.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-mkdir_p.Tpo -c -o libcommonpth_a-mkdir_p.obj `if test -f 'mkdir_p.c'; then $(CYGPATH_W) 'mkdir_p.c'; else $(CYGPATH_W) '$(srcdir)/mkdir_p.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-mkdir_p.Tpo $(DEPDIR)/libcommonpth_a-mkdir_p.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mkdir_p.c' object='libcommonpth_a-mkdir_p.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-mkdir_p.obj `if test -f 'mkdir_p.c'; then $(CYGPATH_W) 'mkdir_p.c'; else $(CYGPATH_W) '$(srcdir)/mkdir_p.c'; fi` + +libcommonpth_a-exectool.o: exectool.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-exectool.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-exectool.Tpo -c -o libcommonpth_a-exectool.o `test -f 'exectool.c' || echo '$(srcdir)/'`exectool.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-exectool.Tpo $(DEPDIR)/libcommonpth_a-exectool.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='exectool.c' object='libcommonpth_a-exectool.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-exectool.o `test -f 'exectool.c' || echo '$(srcdir)/'`exectool.c + +libcommonpth_a-exectool.obj: exectool.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-exectool.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-exectool.Tpo -c -o libcommonpth_a-exectool.obj `if test -f 'exectool.c'; then $(CYGPATH_W) 'exectool.c'; else $(CYGPATH_W) '$(srcdir)/exectool.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-exectool.Tpo $(DEPDIR)/libcommonpth_a-exectool.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='exectool.c' object='libcommonpth_a-exectool.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-exectool.obj `if test -f 'exectool.c'; then $(CYGPATH_W) 'exectool.c'; else $(CYGPATH_W) '$(srcdir)/exectool.c'; fi` + +libcommonpth_a-server-help.o: server-help.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-server-help.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-server-help.Tpo -c -o libcommonpth_a-server-help.o `test -f 'server-help.c' || echo '$(srcdir)/'`server-help.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-server-help.Tpo $(DEPDIR)/libcommonpth_a-server-help.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='server-help.c' object='libcommonpth_a-server-help.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-server-help.o `test -f 'server-help.c' || echo '$(srcdir)/'`server-help.c + +libcommonpth_a-server-help.obj: server-help.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-server-help.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-server-help.Tpo -c -o libcommonpth_a-server-help.obj `if test -f 'server-help.c'; then $(CYGPATH_W) 'server-help.c'; else $(CYGPATH_W) '$(srcdir)/server-help.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-server-help.Tpo $(DEPDIR)/libcommonpth_a-server-help.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='server-help.c' object='libcommonpth_a-server-help.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-server-help.obj `if test -f 'server-help.c'; then $(CYGPATH_W) 'server-help.c'; else $(CYGPATH_W) '$(srcdir)/server-help.c'; fi` + +libcommonpth_a-name-value.o: name-value.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-name-value.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-name-value.Tpo -c -o libcommonpth_a-name-value.o `test -f 'name-value.c' || echo '$(srcdir)/'`name-value.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-name-value.Tpo $(DEPDIR)/libcommonpth_a-name-value.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='name-value.c' object='libcommonpth_a-name-value.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-name-value.o `test -f 'name-value.c' || echo '$(srcdir)/'`name-value.c + +libcommonpth_a-name-value.obj: name-value.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-name-value.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-name-value.Tpo -c -o libcommonpth_a-name-value.obj `if test -f 'name-value.c'; then $(CYGPATH_W) 'name-value.c'; else $(CYGPATH_W) '$(srcdir)/name-value.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-name-value.Tpo $(DEPDIR)/libcommonpth_a-name-value.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='name-value.c' object='libcommonpth_a-name-value.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-name-value.obj `if test -f 'name-value.c'; then $(CYGPATH_W) 'name-value.c'; else $(CYGPATH_W) '$(srcdir)/name-value.c'; fi` + +libcommonpth_a-recsel.o: recsel.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-recsel.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-recsel.Tpo -c -o libcommonpth_a-recsel.o `test -f 'recsel.c' || echo '$(srcdir)/'`recsel.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-recsel.Tpo $(DEPDIR)/libcommonpth_a-recsel.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='recsel.c' object='libcommonpth_a-recsel.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-recsel.o `test -f 'recsel.c' || echo '$(srcdir)/'`recsel.c + +libcommonpth_a-recsel.obj: recsel.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-recsel.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-recsel.Tpo -c -o libcommonpth_a-recsel.obj `if test -f 'recsel.c'; then $(CYGPATH_W) 'recsel.c'; else $(CYGPATH_W) '$(srcdir)/recsel.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-recsel.Tpo $(DEPDIR)/libcommonpth_a-recsel.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='recsel.c' object='libcommonpth_a-recsel.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-recsel.obj `if test -f 'recsel.c'; then $(CYGPATH_W) 'recsel.c'; else $(CYGPATH_W) '$(srcdir)/recsel.c'; fi` + +libcommonpth_a-ksba-io-support.o: ksba-io-support.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-ksba-io-support.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-ksba-io-support.Tpo -c -o libcommonpth_a-ksba-io-support.o `test -f 'ksba-io-support.c' || echo '$(srcdir)/'`ksba-io-support.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-ksba-io-support.Tpo $(DEPDIR)/libcommonpth_a-ksba-io-support.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ksba-io-support.c' object='libcommonpth_a-ksba-io-support.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-ksba-io-support.o `test -f 'ksba-io-support.c' || echo '$(srcdir)/'`ksba-io-support.c + +libcommonpth_a-ksba-io-support.obj: ksba-io-support.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-ksba-io-support.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-ksba-io-support.Tpo -c -o libcommonpth_a-ksba-io-support.obj `if test -f 'ksba-io-support.c'; then $(CYGPATH_W) 'ksba-io-support.c'; else $(CYGPATH_W) '$(srcdir)/ksba-io-support.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-ksba-io-support.Tpo $(DEPDIR)/libcommonpth_a-ksba-io-support.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ksba-io-support.c' object='libcommonpth_a-ksba-io-support.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-ksba-io-support.obj `if test -f 'ksba-io-support.c'; then $(CYGPATH_W) 'ksba-io-support.c'; else $(CYGPATH_W) '$(srcdir)/ksba-io-support.c'; fi` + +libcommonpth_a-openpgp-fpr.o: openpgp-fpr.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-openpgp-fpr.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-openpgp-fpr.Tpo -c -o libcommonpth_a-openpgp-fpr.o `test -f 'openpgp-fpr.c' || echo '$(srcdir)/'`openpgp-fpr.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-openpgp-fpr.Tpo $(DEPDIR)/libcommonpth_a-openpgp-fpr.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='openpgp-fpr.c' object='libcommonpth_a-openpgp-fpr.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-openpgp-fpr.o `test -f 'openpgp-fpr.c' || echo '$(srcdir)/'`openpgp-fpr.c + +libcommonpth_a-openpgp-fpr.obj: openpgp-fpr.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-openpgp-fpr.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-openpgp-fpr.Tpo -c -o libcommonpth_a-openpgp-fpr.obj `if test -f 'openpgp-fpr.c'; then $(CYGPATH_W) 'openpgp-fpr.c'; else $(CYGPATH_W) '$(srcdir)/openpgp-fpr.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-openpgp-fpr.Tpo $(DEPDIR)/libcommonpth_a-openpgp-fpr.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='openpgp-fpr.c' object='libcommonpth_a-openpgp-fpr.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-openpgp-fpr.obj `if test -f 'openpgp-fpr.c'; then $(CYGPATH_W) 'openpgp-fpr.c'; else $(CYGPATH_W) '$(srcdir)/openpgp-fpr.c'; fi` + +libcommonpth_a-compliance.o: compliance.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-compliance.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-compliance.Tpo -c -o libcommonpth_a-compliance.o `test -f 'compliance.c' || echo '$(srcdir)/'`compliance.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-compliance.Tpo $(DEPDIR)/libcommonpth_a-compliance.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='compliance.c' object='libcommonpth_a-compliance.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-compliance.o `test -f 'compliance.c' || echo '$(srcdir)/'`compliance.c + +libcommonpth_a-compliance.obj: compliance.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-compliance.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-compliance.Tpo -c -o libcommonpth_a-compliance.obj `if test -f 'compliance.c'; then $(CYGPATH_W) 'compliance.c'; else $(CYGPATH_W) '$(srcdir)/compliance.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-compliance.Tpo $(DEPDIR)/libcommonpth_a-compliance.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='compliance.c' object='libcommonpth_a-compliance.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-compliance.obj `if test -f 'compliance.c'; then $(CYGPATH_W) 'compliance.c'; else $(CYGPATH_W) '$(srcdir)/compliance.c'; fi` + +libcommonpth_a-w32-reg.o: w32-reg.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-w32-reg.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-w32-reg.Tpo -c -o libcommonpth_a-w32-reg.o `test -f 'w32-reg.c' || echo '$(srcdir)/'`w32-reg.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-w32-reg.Tpo $(DEPDIR)/libcommonpth_a-w32-reg.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='w32-reg.c' object='libcommonpth_a-w32-reg.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-w32-reg.o `test -f 'w32-reg.c' || echo '$(srcdir)/'`w32-reg.c + +libcommonpth_a-w32-reg.obj: w32-reg.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-w32-reg.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-w32-reg.Tpo -c -o libcommonpth_a-w32-reg.obj `if test -f 'w32-reg.c'; then $(CYGPATH_W) 'w32-reg.c'; else $(CYGPATH_W) '$(srcdir)/w32-reg.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-w32-reg.Tpo $(DEPDIR)/libcommonpth_a-w32-reg.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='w32-reg.c' object='libcommonpth_a-w32-reg.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-w32-reg.obj `if test -f 'w32-reg.c'; then $(CYGPATH_W) 'w32-reg.c'; else $(CYGPATH_W) '$(srcdir)/w32-reg.c'; fi` + +libcommonpth_a-w32-cmdline.o: w32-cmdline.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-w32-cmdline.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-w32-cmdline.Tpo -c -o libcommonpth_a-w32-cmdline.o `test -f 'w32-cmdline.c' || echo '$(srcdir)/'`w32-cmdline.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-w32-cmdline.Tpo $(DEPDIR)/libcommonpth_a-w32-cmdline.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='w32-cmdline.c' object='libcommonpth_a-w32-cmdline.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-w32-cmdline.o `test -f 'w32-cmdline.c' || echo '$(srcdir)/'`w32-cmdline.c + +libcommonpth_a-w32-cmdline.obj: w32-cmdline.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-w32-cmdline.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-w32-cmdline.Tpo -c -o libcommonpth_a-w32-cmdline.obj `if test -f 'w32-cmdline.c'; then $(CYGPATH_W) 'w32-cmdline.c'; else $(CYGPATH_W) '$(srcdir)/w32-cmdline.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-w32-cmdline.Tpo $(DEPDIR)/libcommonpth_a-w32-cmdline.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='w32-cmdline.c' object='libcommonpth_a-w32-cmdline.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-w32-cmdline.obj `if test -f 'w32-cmdline.c'; then $(CYGPATH_W) 'w32-cmdline.c'; else $(CYGPATH_W) '$(srcdir)/w32-cmdline.c'; fi` + +libcommonpth_a-exechelp-w32ce.o: exechelp-w32ce.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-exechelp-w32ce.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-exechelp-w32ce.Tpo -c -o libcommonpth_a-exechelp-w32ce.o `test -f 'exechelp-w32ce.c' || echo '$(srcdir)/'`exechelp-w32ce.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-exechelp-w32ce.Tpo $(DEPDIR)/libcommonpth_a-exechelp-w32ce.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='exechelp-w32ce.c' object='libcommonpth_a-exechelp-w32ce.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-exechelp-w32ce.o `test -f 'exechelp-w32ce.c' || echo '$(srcdir)/'`exechelp-w32ce.c + +libcommonpth_a-exechelp-w32ce.obj: exechelp-w32ce.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-exechelp-w32ce.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-exechelp-w32ce.Tpo -c -o libcommonpth_a-exechelp-w32ce.obj `if test -f 'exechelp-w32ce.c'; then $(CYGPATH_W) 'exechelp-w32ce.c'; else $(CYGPATH_W) '$(srcdir)/exechelp-w32ce.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-exechelp-w32ce.Tpo $(DEPDIR)/libcommonpth_a-exechelp-w32ce.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='exechelp-w32ce.c' object='libcommonpth_a-exechelp-w32ce.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-exechelp-w32ce.obj `if test -f 'exechelp-w32ce.c'; then $(CYGPATH_W) 'exechelp-w32ce.c'; else $(CYGPATH_W) '$(srcdir)/exechelp-w32ce.c'; fi` + +libcommonpth_a-exechelp-w32.o: exechelp-w32.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-exechelp-w32.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-exechelp-w32.Tpo -c -o libcommonpth_a-exechelp-w32.o `test -f 'exechelp-w32.c' || echo '$(srcdir)/'`exechelp-w32.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-exechelp-w32.Tpo $(DEPDIR)/libcommonpth_a-exechelp-w32.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='exechelp-w32.c' object='libcommonpth_a-exechelp-w32.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-exechelp-w32.o `test -f 'exechelp-w32.c' || echo '$(srcdir)/'`exechelp-w32.c + +libcommonpth_a-exechelp-w32.obj: exechelp-w32.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-exechelp-w32.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-exechelp-w32.Tpo -c -o libcommonpth_a-exechelp-w32.obj `if test -f 'exechelp-w32.c'; then $(CYGPATH_W) 'exechelp-w32.c'; else $(CYGPATH_W) '$(srcdir)/exechelp-w32.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-exechelp-w32.Tpo $(DEPDIR)/libcommonpth_a-exechelp-w32.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='exechelp-w32.c' object='libcommonpth_a-exechelp-w32.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-exechelp-w32.obj `if test -f 'exechelp-w32.c'; then $(CYGPATH_W) 'exechelp-w32.c'; else $(CYGPATH_W) '$(srcdir)/exechelp-w32.c'; fi` + +libcommonpth_a-exechelp-posix.o: exechelp-posix.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-exechelp-posix.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-exechelp-posix.Tpo -c -o libcommonpth_a-exechelp-posix.o `test -f 'exechelp-posix.c' || echo '$(srcdir)/'`exechelp-posix.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-exechelp-posix.Tpo $(DEPDIR)/libcommonpth_a-exechelp-posix.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='exechelp-posix.c' object='libcommonpth_a-exechelp-posix.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-exechelp-posix.o `test -f 'exechelp-posix.c' || echo '$(srcdir)/'`exechelp-posix.c + +libcommonpth_a-exechelp-posix.obj: exechelp-posix.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-exechelp-posix.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-exechelp-posix.Tpo -c -o libcommonpth_a-exechelp-posix.obj `if test -f 'exechelp-posix.c'; then $(CYGPATH_W) 'exechelp-posix.c'; else $(CYGPATH_W) '$(srcdir)/exechelp-posix.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-exechelp-posix.Tpo $(DEPDIR)/libcommonpth_a-exechelp-posix.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='exechelp-posix.c' object='libcommonpth_a-exechelp-posix.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-exechelp-posix.obj `if test -f 'exechelp-posix.c'; then $(CYGPATH_W) 'exechelp-posix.c'; else $(CYGPATH_W) '$(srcdir)/exechelp-posix.c'; fi` + +libcommonpth_a-call-gpg.o: call-gpg.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-call-gpg.o -MD -MP -MF $(DEPDIR)/libcommonpth_a-call-gpg.Tpo -c -o libcommonpth_a-call-gpg.o `test -f 'call-gpg.c' || echo '$(srcdir)/'`call-gpg.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-call-gpg.Tpo $(DEPDIR)/libcommonpth_a-call-gpg.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='call-gpg.c' object='libcommonpth_a-call-gpg.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-call-gpg.o `test -f 'call-gpg.c' || echo '$(srcdir)/'`call-gpg.c + +libcommonpth_a-call-gpg.obj: call-gpg.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -MT libcommonpth_a-call-gpg.obj -MD -MP -MF $(DEPDIR)/libcommonpth_a-call-gpg.Tpo -c -o libcommonpth_a-call-gpg.obj `if test -f 'call-gpg.c'; then $(CYGPATH_W) 'call-gpg.c'; else $(CYGPATH_W) '$(srcdir)/call-gpg.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcommonpth_a-call-gpg.Tpo $(DEPDIR)/libcommonpth_a-call-gpg.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='call-gpg.c' object='libcommonpth_a-call-gpg.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcommonpth_a_CFLAGS) $(CFLAGS) -c -o libcommonpth_a-call-gpg.obj `if test -f 'call-gpg.c'; then $(CYGPATH_W) 'call-gpg.c'; else $(CYGPATH_W) '$(srcdir)/call-gpg.c'; fi` + +libsimple_pwquery_a-simple-pwquery.o: simple-pwquery.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsimple_pwquery_a_CFLAGS) $(CFLAGS) -MT libsimple_pwquery_a-simple-pwquery.o -MD -MP -MF $(DEPDIR)/libsimple_pwquery_a-simple-pwquery.Tpo -c -o libsimple_pwquery_a-simple-pwquery.o `test -f 'simple-pwquery.c' || echo '$(srcdir)/'`simple-pwquery.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsimple_pwquery_a-simple-pwquery.Tpo $(DEPDIR)/libsimple_pwquery_a-simple-pwquery.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='simple-pwquery.c' object='libsimple_pwquery_a-simple-pwquery.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsimple_pwquery_a_CFLAGS) $(CFLAGS) -c -o libsimple_pwquery_a-simple-pwquery.o `test -f 'simple-pwquery.c' || echo '$(srcdir)/'`simple-pwquery.c + +libsimple_pwquery_a-simple-pwquery.obj: simple-pwquery.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsimple_pwquery_a_CFLAGS) $(CFLAGS) -MT libsimple_pwquery_a-simple-pwquery.obj -MD -MP -MF $(DEPDIR)/libsimple_pwquery_a-simple-pwquery.Tpo -c -o libsimple_pwquery_a-simple-pwquery.obj `if test -f 'simple-pwquery.c'; then $(CYGPATH_W) 'simple-pwquery.c'; else $(CYGPATH_W) '$(srcdir)/simple-pwquery.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsimple_pwquery_a-simple-pwquery.Tpo $(DEPDIR)/libsimple_pwquery_a-simple-pwquery.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='simple-pwquery.c' object='libsimple_pwquery_a-simple-pwquery.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsimple_pwquery_a_CFLAGS) $(CFLAGS) -c -o libsimple_pwquery_a-simple-pwquery.obj `if test -f 'simple-pwquery.c'; then $(CYGPATH_W) 'simple-pwquery.c'; else $(CYGPATH_W) '$(srcdir)/simple-pwquery.c'; fi` + +libsimple_pwquery_a-asshelp.o: asshelp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsimple_pwquery_a_CFLAGS) $(CFLAGS) -MT libsimple_pwquery_a-asshelp.o -MD -MP -MF $(DEPDIR)/libsimple_pwquery_a-asshelp.Tpo -c -o libsimple_pwquery_a-asshelp.o `test -f 'asshelp.c' || echo '$(srcdir)/'`asshelp.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsimple_pwquery_a-asshelp.Tpo $(DEPDIR)/libsimple_pwquery_a-asshelp.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='asshelp.c' object='libsimple_pwquery_a-asshelp.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsimple_pwquery_a_CFLAGS) $(CFLAGS) -c -o libsimple_pwquery_a-asshelp.o `test -f 'asshelp.c' || echo '$(srcdir)/'`asshelp.c + +libsimple_pwquery_a-asshelp.obj: asshelp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsimple_pwquery_a_CFLAGS) $(CFLAGS) -MT libsimple_pwquery_a-asshelp.obj -MD -MP -MF $(DEPDIR)/libsimple_pwquery_a-asshelp.Tpo -c -o libsimple_pwquery_a-asshelp.obj `if test -f 'asshelp.c'; then $(CYGPATH_W) 'asshelp.c'; else $(CYGPATH_W) '$(srcdir)/asshelp.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libsimple_pwquery_a-asshelp.Tpo $(DEPDIR)/libsimple_pwquery_a-asshelp.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='asshelp.c' object='libsimple_pwquery_a-asshelp.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsimple_pwquery_a_CFLAGS) $(CFLAGS) -c -o libsimple_pwquery_a-asshelp.obj `if test -f 'asshelp.c'; then $(CYGPATH_W) 'asshelp.c'; else $(CYGPATH_W) '$(srcdir)/asshelp.c'; fi` + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +check-TESTS: $(TESTS) + @failed=0; all=0; xfail=0; xpass=0; skip=0; \ + srcdir=$(srcdir); export srcdir; \ + list=' $(TESTS) '; \ + $(am__tty_colors); \ + if test -n "$$list"; then \ + for tst in $$list; do \ + if test -f ./$$tst; then dir=./; \ + elif test -f $$tst; then dir=; \ + else dir="$(srcdir)/"; fi; \ + if $(TESTS_ENVIRONMENT) $${dir}$$tst $(AM_TESTS_FD_REDIRECT); then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$tst[\ \ ]*) \ + xpass=`expr $$xpass + 1`; \ + failed=`expr $$failed + 1`; \ + col=$$red; res=XPASS; \ + ;; \ + *) \ + col=$$grn; res=PASS; \ + ;; \ + esac; \ + elif test $$? -ne 77; then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$tst[\ \ ]*) \ + xfail=`expr $$xfail + 1`; \ + col=$$lgn; res=XFAIL; \ + ;; \ + *) \ + failed=`expr $$failed + 1`; \ + col=$$red; res=FAIL; \ + ;; \ + esac; \ + else \ + skip=`expr $$skip + 1`; \ + col=$$blu; res=SKIP; \ + fi; \ + echo "$${col}$$res$${std}: $$tst"; \ + done; \ + if test "$$all" -eq 1; then \ + tests="test"; \ + All=""; \ + else \ + tests="tests"; \ + All="All "; \ + fi; \ + if test "$$failed" -eq 0; then \ + if test "$$xfail" -eq 0; then \ + banner="$$All$$all $$tests passed"; \ + else \ + if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \ + banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \ + fi; \ + else \ + if test "$$xpass" -eq 0; then \ + banner="$$failed of $$all $$tests failed"; \ + else \ + if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \ + banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \ + fi; \ + fi; \ + dashes="$$banner"; \ + skipped=""; \ + if test "$$skip" -ne 0; then \ + if test "$$skip" -eq 1; then \ + skipped="($$skip test was not run)"; \ + else \ + skipped="($$skip tests were not run)"; \ + fi; \ + test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \ + dashes="$$skipped"; \ + fi; \ + report=""; \ + if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \ + report="Please report to $(PACKAGE_BUGREPORT)"; \ + test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \ + dashes="$$report"; \ + fi; \ + dashes=`echo "$$dashes" | sed s/./=/g`; \ + if test "$$failed" -eq 0; then \ + col="$$grn"; \ + else \ + col="$$red"; \ + fi; \ + echo "$${col}$$dashes$${std}"; \ + echo "$${col}$$banner$${std}"; \ + test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \ + test -z "$$report" || echo "$${col}$$report$${std}"; \ + echo "$${col}$$dashes$${std}"; \ + test "$$failed" -eq 0; \ + else :; fi + +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am + $(MAKE) $(AM_MAKEFLAGS) check-TESTS +check: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) check-am +all-am: Makefile $(PROGRAMS) $(LIBRARIES) +installdirs: +install: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) install-am +install-exec: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-generic clean-noinstLIBRARIES clean-noinstPROGRAMS \ + mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/gpgrlhelp.Po + -rm -f ./$(DEPDIR)/libcommon_a-agent-opt.Po + -rm -f ./$(DEPDIR)/libcommon_a-argparse.Po + -rm -f ./$(DEPDIR)/libcommon_a-asshelp.Po + -rm -f ./$(DEPDIR)/libcommon_a-asshelp2.Po + -rm -f ./$(DEPDIR)/libcommon_a-audit.Po + -rm -f ./$(DEPDIR)/libcommon_a-b64dec.Po + -rm -f ./$(DEPDIR)/libcommon_a-b64enc.Po + -rm -f ./$(DEPDIR)/libcommon_a-ccparray.Po + -rm -f ./$(DEPDIR)/libcommon_a-compliance.Po + -rm -f ./$(DEPDIR)/libcommon_a-convert.Po + -rm -f ./$(DEPDIR)/libcommon_a-dotlock.Po + -rm -f ./$(DEPDIR)/libcommon_a-exechelp-posix.Po + -rm -f ./$(DEPDIR)/libcommon_a-exechelp-w32.Po + -rm -f ./$(DEPDIR)/libcommon_a-exechelp-w32ce.Po + -rm -f ./$(DEPDIR)/libcommon_a-exectool.Po + -rm -f ./$(DEPDIR)/libcommon_a-get-passphrase.Po + -rm -f ./$(DEPDIR)/libcommon_a-gettime.Po + -rm -f ./$(DEPDIR)/libcommon_a-helpfile.Po + -rm -f ./$(DEPDIR)/libcommon_a-homedir.Po + -rm -f ./$(DEPDIR)/libcommon_a-i18n.Po + -rm -f ./$(DEPDIR)/libcommon_a-init.Po + -rm -f ./$(DEPDIR)/libcommon_a-iobuf.Po + -rm -f ./$(DEPDIR)/libcommon_a-ksba-io-support.Po + -rm -f ./$(DEPDIR)/libcommon_a-localename.Po + -rm -f ./$(DEPDIR)/libcommon_a-logging.Po + -rm -f ./$(DEPDIR)/libcommon_a-mapstrings.Po + -rm -f ./$(DEPDIR)/libcommon_a-mbox-util.Po + -rm -f ./$(DEPDIR)/libcommon_a-membuf.Po + -rm -f ./$(DEPDIR)/libcommon_a-miscellaneous.Po + -rm -f ./$(DEPDIR)/libcommon_a-mischelp.Po + -rm -f ./$(DEPDIR)/libcommon_a-mkdir_p.Po + -rm -f ./$(DEPDIR)/libcommon_a-name-value.Po + -rm -f ./$(DEPDIR)/libcommon_a-openpgp-fpr.Po + -rm -f ./$(DEPDIR)/libcommon_a-openpgp-oid.Po + -rm -f ./$(DEPDIR)/libcommon_a-percent.Po + -rm -f ./$(DEPDIR)/libcommon_a-recsel.Po + -rm -f ./$(DEPDIR)/libcommon_a-server-help.Po + -rm -f ./$(DEPDIR)/libcommon_a-session-env.Po + -rm -f ./$(DEPDIR)/libcommon_a-sexputil.Po + -rm -f ./$(DEPDIR)/libcommon_a-signal.Po + -rm -f ./$(DEPDIR)/libcommon_a-ssh-utils.Po + -rm -f ./$(DEPDIR)/libcommon_a-status.Po + -rm -f ./$(DEPDIR)/libcommon_a-stringhelp.Po + -rm -f ./$(DEPDIR)/libcommon_a-strlist.Po + -rm -f ./$(DEPDIR)/libcommon_a-sysutils.Po + -rm -f ./$(DEPDIR)/libcommon_a-tlv-builder.Po + -rm -f ./$(DEPDIR)/libcommon_a-tlv.Po + -rm -f ./$(DEPDIR)/libcommon_a-ttyio.Po + -rm -f ./$(DEPDIR)/libcommon_a-userids.Po + -rm -f ./$(DEPDIR)/libcommon_a-utf8conv.Po + -rm -f ./$(DEPDIR)/libcommon_a-w32-cmdline.Po + -rm -f ./$(DEPDIR)/libcommon_a-w32-reg.Po + -rm -f ./$(DEPDIR)/libcommon_a-xasprintf.Po + -rm -f ./$(DEPDIR)/libcommon_a-xreadline.Po + -rm -f ./$(DEPDIR)/libcommon_a-yesno.Po + -rm -f ./$(DEPDIR)/libcommon_a-zb32.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-agent-opt.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-argparse.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-asshelp.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-asshelp2.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-audit.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-b64dec.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-b64enc.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-call-gpg.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-ccparray.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-compliance.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-convert.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-dotlock.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-exechelp-posix.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-exechelp-w32.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-exechelp-w32ce.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-exectool.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-gettime.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-helpfile.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-homedir.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-i18n.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-init.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-iobuf.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-ksba-io-support.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-localename.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-logging.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-mapstrings.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-mbox-util.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-membuf.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-miscellaneous.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-mischelp.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-mkdir_p.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-name-value.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-openpgp-fpr.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-openpgp-oid.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-percent.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-recsel.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-server-help.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-session-env.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-sexputil.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-signal.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-ssh-utils.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-status.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-stringhelp.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-strlist.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-sysutils.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-tlv-builder.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-tlv.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-ttyio.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-userids.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-utf8conv.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-w32-cmdline.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-w32-reg.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-xasprintf.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-xreadline.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-yesno.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-zb32.Po + -rm -f ./$(DEPDIR)/libsimple_pwquery_a-asshelp.Po + -rm -f ./$(DEPDIR)/libsimple_pwquery_a-simple-pwquery.Po + -rm -f ./$(DEPDIR)/t-b64.Po + -rm -f ./$(DEPDIR)/t-ccparray.Po + -rm -f ./$(DEPDIR)/t-convert.Po + -rm -f ./$(DEPDIR)/t-exechelp.Po + -rm -f ./$(DEPDIR)/t-exectool.Po + -rm -f ./$(DEPDIR)/t-gettime.Po + -rm -f ./$(DEPDIR)/t-helpfile.Po + -rm -f ./$(DEPDIR)/t-iobuf.Po + -rm -f ./$(DEPDIR)/t-mapstrings.Po + -rm -f ./$(DEPDIR)/t-mbox-util.Po + -rm -f ./$(DEPDIR)/t-name-value.Po + -rm -f ./$(DEPDIR)/t-openpgp-oid.Po + -rm -f ./$(DEPDIR)/t-percent.Po + -rm -f ./$(DEPDIR)/t-recsel.Po + -rm -f ./$(DEPDIR)/t-session-env.Po + -rm -f ./$(DEPDIR)/t-sexputil.Po + -rm -f ./$(DEPDIR)/t-ssh-utils.Po + -rm -f ./$(DEPDIR)/t-stringhelp.Po + -rm -f ./$(DEPDIR)/t-strlist.Po + -rm -f ./$(DEPDIR)/t-sysutils.Po + -rm -f ./$(DEPDIR)/t-timestuff.Po + -rm -f ./$(DEPDIR)/t-w32-cmdline.Po + -rm -f ./$(DEPDIR)/t-w32-reg.Po + -rm -f ./$(DEPDIR)/t-zb32.Po + -rm -f ./$(DEPDIR)/w32-cmdline.Po + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f ./$(DEPDIR)/gpgrlhelp.Po + -rm -f ./$(DEPDIR)/libcommon_a-agent-opt.Po + -rm -f ./$(DEPDIR)/libcommon_a-argparse.Po + -rm -f ./$(DEPDIR)/libcommon_a-asshelp.Po + -rm -f ./$(DEPDIR)/libcommon_a-asshelp2.Po + -rm -f ./$(DEPDIR)/libcommon_a-audit.Po + -rm -f ./$(DEPDIR)/libcommon_a-b64dec.Po + -rm -f ./$(DEPDIR)/libcommon_a-b64enc.Po + -rm -f ./$(DEPDIR)/libcommon_a-ccparray.Po + -rm -f ./$(DEPDIR)/libcommon_a-compliance.Po + -rm -f ./$(DEPDIR)/libcommon_a-convert.Po + -rm -f ./$(DEPDIR)/libcommon_a-dotlock.Po + -rm -f ./$(DEPDIR)/libcommon_a-exechelp-posix.Po + -rm -f ./$(DEPDIR)/libcommon_a-exechelp-w32.Po + -rm -f ./$(DEPDIR)/libcommon_a-exechelp-w32ce.Po + -rm -f ./$(DEPDIR)/libcommon_a-exectool.Po + -rm -f ./$(DEPDIR)/libcommon_a-get-passphrase.Po + -rm -f ./$(DEPDIR)/libcommon_a-gettime.Po + -rm -f ./$(DEPDIR)/libcommon_a-helpfile.Po + -rm -f ./$(DEPDIR)/libcommon_a-homedir.Po + -rm -f ./$(DEPDIR)/libcommon_a-i18n.Po + -rm -f ./$(DEPDIR)/libcommon_a-init.Po + -rm -f ./$(DEPDIR)/libcommon_a-iobuf.Po + -rm -f ./$(DEPDIR)/libcommon_a-ksba-io-support.Po + -rm -f ./$(DEPDIR)/libcommon_a-localename.Po + -rm -f ./$(DEPDIR)/libcommon_a-logging.Po + -rm -f ./$(DEPDIR)/libcommon_a-mapstrings.Po + -rm -f ./$(DEPDIR)/libcommon_a-mbox-util.Po + -rm -f ./$(DEPDIR)/libcommon_a-membuf.Po + -rm -f ./$(DEPDIR)/libcommon_a-miscellaneous.Po + -rm -f ./$(DEPDIR)/libcommon_a-mischelp.Po + -rm -f ./$(DEPDIR)/libcommon_a-mkdir_p.Po + -rm -f ./$(DEPDIR)/libcommon_a-name-value.Po + -rm -f ./$(DEPDIR)/libcommon_a-openpgp-fpr.Po + -rm -f ./$(DEPDIR)/libcommon_a-openpgp-oid.Po + -rm -f ./$(DEPDIR)/libcommon_a-percent.Po + -rm -f ./$(DEPDIR)/libcommon_a-recsel.Po + -rm -f ./$(DEPDIR)/libcommon_a-server-help.Po + -rm -f ./$(DEPDIR)/libcommon_a-session-env.Po + -rm -f ./$(DEPDIR)/libcommon_a-sexputil.Po + -rm -f ./$(DEPDIR)/libcommon_a-signal.Po + -rm -f ./$(DEPDIR)/libcommon_a-ssh-utils.Po + -rm -f ./$(DEPDIR)/libcommon_a-status.Po + -rm -f ./$(DEPDIR)/libcommon_a-stringhelp.Po + -rm -f ./$(DEPDIR)/libcommon_a-strlist.Po + -rm -f ./$(DEPDIR)/libcommon_a-sysutils.Po + -rm -f ./$(DEPDIR)/libcommon_a-tlv-builder.Po + -rm -f ./$(DEPDIR)/libcommon_a-tlv.Po + -rm -f ./$(DEPDIR)/libcommon_a-ttyio.Po + -rm -f ./$(DEPDIR)/libcommon_a-userids.Po + -rm -f ./$(DEPDIR)/libcommon_a-utf8conv.Po + -rm -f ./$(DEPDIR)/libcommon_a-w32-cmdline.Po + -rm -f ./$(DEPDIR)/libcommon_a-w32-reg.Po + -rm -f ./$(DEPDIR)/libcommon_a-xasprintf.Po + -rm -f ./$(DEPDIR)/libcommon_a-xreadline.Po + -rm -f ./$(DEPDIR)/libcommon_a-yesno.Po + -rm -f ./$(DEPDIR)/libcommon_a-zb32.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-agent-opt.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-argparse.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-asshelp.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-asshelp2.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-audit.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-b64dec.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-b64enc.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-call-gpg.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-ccparray.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-compliance.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-convert.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-dotlock.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-exechelp-posix.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-exechelp-w32.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-exechelp-w32ce.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-exectool.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-gettime.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-helpfile.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-homedir.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-i18n.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-init.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-iobuf.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-ksba-io-support.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-localename.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-logging.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-mapstrings.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-mbox-util.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-membuf.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-miscellaneous.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-mischelp.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-mkdir_p.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-name-value.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-openpgp-fpr.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-openpgp-oid.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-percent.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-recsel.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-server-help.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-session-env.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-sexputil.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-signal.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-ssh-utils.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-status.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-stringhelp.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-strlist.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-sysutils.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-tlv-builder.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-tlv.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-ttyio.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-userids.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-utf8conv.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-w32-cmdline.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-w32-reg.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-xasprintf.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-xreadline.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-yesno.Po + -rm -f ./$(DEPDIR)/libcommonpth_a-zb32.Po + -rm -f ./$(DEPDIR)/libsimple_pwquery_a-asshelp.Po + -rm -f ./$(DEPDIR)/libsimple_pwquery_a-simple-pwquery.Po + -rm -f ./$(DEPDIR)/t-b64.Po + -rm -f ./$(DEPDIR)/t-ccparray.Po + -rm -f ./$(DEPDIR)/t-convert.Po + -rm -f ./$(DEPDIR)/t-exechelp.Po + -rm -f ./$(DEPDIR)/t-exectool.Po + -rm -f ./$(DEPDIR)/t-gettime.Po + -rm -f ./$(DEPDIR)/t-helpfile.Po + -rm -f ./$(DEPDIR)/t-iobuf.Po + -rm -f ./$(DEPDIR)/t-mapstrings.Po + -rm -f ./$(DEPDIR)/t-mbox-util.Po + -rm -f ./$(DEPDIR)/t-name-value.Po + -rm -f ./$(DEPDIR)/t-openpgp-oid.Po + -rm -f ./$(DEPDIR)/t-percent.Po + -rm -f ./$(DEPDIR)/t-recsel.Po + -rm -f ./$(DEPDIR)/t-session-env.Po + -rm -f ./$(DEPDIR)/t-sexputil.Po + -rm -f ./$(DEPDIR)/t-ssh-utils.Po + -rm -f ./$(DEPDIR)/t-stringhelp.Po + -rm -f ./$(DEPDIR)/t-strlist.Po + -rm -f ./$(DEPDIR)/t-sysutils.Po + -rm -f ./$(DEPDIR)/t-timestuff.Po + -rm -f ./$(DEPDIR)/t-w32-cmdline.Po + -rm -f ./$(DEPDIR)/t-w32-reg.Po + -rm -f ./$(DEPDIR)/t-zb32.Po + -rm -f ./$(DEPDIR)/w32-cmdline.Po + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: all check check-am install install-am install-exec \ + install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-TESTS \ + check-am clean clean-generic clean-noinstLIBRARIES \ + clean-noinstPROGRAMS cscopelist-am ctags ctags-am distclean \ + distclean-compile distclean-generic distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic pdf pdf-am ps ps-am tags tags-am uninstall \ + uninstall-am + +.PRECIOUS: Makefile + + +@HAVE_W32_SYSTEM_TRUE@.rc.o: +@HAVE_W32_SYSTEM_TRUE@ $(WINDRES) $(DEFAULT_INCLUDES) $(INCLUDES) "$<" "$@" + +# Note: Due to the dependency on Makefile, the file will always be +# rebuilt, so we allow this only in maintainer mode. + +# Create the audit-events.h include file from audit.h +# Note: We create the target file in the source directory because it +# is a distributed built source. If we would not do that we may end +# up with two files and then it is not clear which version of the +# files will be picked up. +@MAINTAINER_MODE_TRUE@audit-events.h: Makefile.am mkstrtable.awk exaudit.awk audit.h +@MAINTAINER_MODE_TRUE@ $(AWK) -f $(srcdir)/exaudit.awk $(srcdir)/audit.h \ +@MAINTAINER_MODE_TRUE@ | $(AWK) -f $(srcdir)/mkstrtable.awk -v textidx=3 -v nogettext=1 \ +@MAINTAINER_MODE_TRUE@ -v pkg_namespace=eventstr_ > $(srcdir)/audit-events.h + +# Create the status-codes.h include file from status.h +@MAINTAINER_MODE_TRUE@status-codes.h: Makefile.am mkstrtable.awk exstatus.awk status.h +@MAINTAINER_MODE_TRUE@ $(AWK) -f $(srcdir)/exstatus.awk $(srcdir)/status.h \ +@MAINTAINER_MODE_TRUE@ | $(AWK) -f $(srcdir)/mkstrtable.awk -v textidx=3 -v nogettext=1 \ +@MAINTAINER_MODE_TRUE@ -v pkg_namespace=statusstr_ > $(srcdir)/status-codes.h + +# All programs should depend on the created libs. +$(PROGRAMS) : libcommon.a libcommonpth.a + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/common/README b/common/README new file mode 100644 index 0000000..73799a8 --- /dev/null +++ b/common/README @@ -0,0 +1 @@ +Common functionality used by all modules of GnuPG. diff --git a/common/agent-opt.c b/common/agent-opt.c new file mode 100644 index 0000000..6d9f9e7 --- /dev/null +++ b/common/agent-opt.c @@ -0,0 +1,106 @@ +/* agent-opt.c - Helper for certain agent options + * Copyright (C) 2013 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdlib.h> +#include <string.h> + +#include "shareddefs.h" + + +/* Parse VALUE and return an integer representing a pinentry_mode_t. + (-1) is returned for an invalid VALUE. */ +int +parse_pinentry_mode (const char *value) +{ + int result; + + if (!strcmp (value, "ask") || !strcmp (value, "default")) + result = PINENTRY_MODE_ASK; + else if (!strcmp (value, "cancel")) + result = PINENTRY_MODE_CANCEL; + else if (!strcmp (value, "error")) + result = PINENTRY_MODE_ERROR; + else if (!strcmp (value, "loopback")) + result = PINENTRY_MODE_LOOPBACK; + else + result = -1; + + return result; +} + +/* Return the string representation for the pinentry MODE. Returns + "?" for an invalid mode. */ +const char * +str_pinentry_mode (pinentry_mode_t mode) +{ + switch (mode) + { + case PINENTRY_MODE_ASK: return "ask"; + case PINENTRY_MODE_CANCEL: return "cancel"; + case PINENTRY_MODE_ERROR: return "error"; + case PINENTRY_MODE_LOOPBACK: return "loopback"; + } + return "?"; +} + + +/* Parse VALUE and return an integer representing a request_origin_t. + * (-1) is returned for an invalid VALUE. */ +int +parse_request_origin (const char *value) +{ + int result; + + if (!strcmp (value, "none") || !strcmp (value, "local")) + result = REQUEST_ORIGIN_LOCAL; + else if (!strcmp (value, "remote")) + result = REQUEST_ORIGIN_REMOTE; + else if (!strcmp (value, "browser")) + result = REQUEST_ORIGIN_BROWSER; + else + result = -1; + + return result; +} + + +/* Return the string representation for the request origin. Returns + * "?" for an invalid mode. */ +const char * +str_request_origin (request_origin_t mode) +{ + switch (mode) + { + case REQUEST_ORIGIN_LOCAL: return "local"; + case REQUEST_ORIGIN_REMOTE: return "remote"; + case REQUEST_ORIGIN_BROWSER: return "browser"; + } + return "?"; +} diff --git a/common/all-tests.scm b/common/all-tests.scm new file mode 100644 index 0000000..54f1153 --- /dev/null +++ b/common/all-tests.scm @@ -0,0 +1,45 @@ +;; Copyright (C) 2017 g10 Code GmbH +;; +;; This file is part of GnuPG. +;; +;; GnuPG is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation; either version 3 of the License, or +;; (at your option) any later version. +;; +;; GnuPG is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program; if not, see <http://www.gnu.org/licenses/>. + +(export all-tests + ;; XXX: Currently, the makefile parser does not understand this + ;; Makefile.am, so we hardcode the list of tests here. + (map (lambda (name) + (test::binary #f + (path-join "common" name) + (path-join (getenv "objdir") "common" name))) + (list "t-stringhelp" + "t-timestuff" + "t-convert" + "t-percent" + "t-gettime" + "t-sysutils" + "t-sexputil" + "t-session-env" + "t-openpgp-oid" + "t-ssh-utils" + "t-mapstrings" + "t-zb32" + "t-mbox-util" + "t-iobuf" + "t-strlist" + "t-name-value" + "t-ccparray" + "t-recsel" + "t-exechelp" + "t-exectool" + ))) diff --git a/common/argparse.c b/common/argparse.c new file mode 100644 index 0000000..b63f800 --- /dev/null +++ b/common/argparse.c @@ -0,0 +1,2809 @@ +/* [argparse.c wk 17.06.97] Argument Parser for option handling + * Copyright (C) 1998-2001, 2006-2008, 2012 Free Software Foundation, Inc. + * Copyright (C) 1997-2001, 2006-2008, 2013-2017 Werner Koch + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute and/or modify this + * part of GnuPG under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * GnuPG is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copies of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, see <https://gnu.org/licenses/>. + */ + +/* This is a modified version of gpgrt/libgpg-error src/argparse.c. + * We use this to require a dependency on a newer gpgrt version. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <string.h> +#include <stdarg.h> +#include <limits.h> +#include <errno.h> +#include <unistd.h> +#include <time.h> + +#include "util.h" +#include "common-defs.h" +#include "i18n.h" +#include "mischelp.h" +#include "stringhelp.h" +#include "logging.h" +#include "utf8conv.h" +#include "sysutils.h" +#include "argparse.h" + + +/* Optional handler to write strings. See gnupg_set_usage_outfnc. */ +static int (*custom_outfnc) (int, const char *); + + +#if USE_INTERNAL_ARGPARSE + +/* The almost always needed user handler for strusage. */ +static const char *(*strusage_handler)( int ) = NULL; +/* Optional handler to map strings. See gnupg_set_fixed_string_mapper. */ +static const char *(*fixed_string_mapper)(const char*); + + +/* Hidden argparse flag used to mark the object as initialized. */ +#define ARGPARSE_FLAG__INITIALIZED (1u << ((8*4)-1)) + +/* Special short options which are auto-inserterd. Must fit into an + * unsigned short. */ +#define ARGPARSE_SHORTOPT_HELP 32768 +#define ARGPARSE_SHORTOPT_VERSION 32769 +#define ARGPARSE_SHORTOPT_WARRANTY 32770 +#define ARGPARSE_SHORTOPT_DUMP_OPTIONS 32771 +#define ARGPARSE_SHORTOPT_DUMP_OPTTBL 32772 + + +/* The malloced configuration directories or NULL. */ +static struct +{ + char *user; + char *sys; +} confdir; + + +/* The states for the gnupg_argparser machinery. */ +enum argparser_states + { + STATE_init = 0, + STATE_open_sys, + STATE_open_user, + STATE_open_cmdline, + STATE_read_sys, + STATE_read_user, + STATE_read_cmdline, + STATE_finished + }; + + +/* An internal object used to store the user provided option table and + * some meta information. */ +typedef struct +{ + unsigned short short_opt; + unsigned short ordinal; /* (for --help) */ + unsigned int flags; + const char *long_opt; /* Points into the user provided table. */ + const char *description; /* Points into the user provided table. */ + unsigned int forced:1; /* Forced to use the sysconf value. */ + unsigned int ignore:1; /* Ignore this option everywhere but in + * the sysconf file. */ + unsigned int explicit_ignore:1; /* Ignore was explicitly set. */ +} opttable_t; + + +/* Internal object of the public gnupg_argparse_t object. */ +struct _argparse_internal_s +{ + int idx; /* Note that this is saved and restored in gnupg_argparser. */ + int inarg; /* (index into args) */ + unsigned int verbose:1; /* Print diagnostics. */ + unsigned int stopped:1; /* Option processing has stopped. */ + unsigned int in_sysconf:1; /* Processing global config file. */ + unsigned int mark_forced:1; /* Mark options as forced. */ + unsigned int mark_ignore:1; /* Mark options as to be ignored. */ + unsigned int explicit_ignore:1; /* Option has explicitly been set + * to ignore or unignore. */ + unsigned int ignore_all_seen:1; /* [ignore-all] has been seen. */ + unsigned int user_seen:1; /* A [user] has been seen. */ + unsigned int user_wildcard:1; /* A [user *] has been seen. */ + unsigned int user_any_active:1; /* Any user section was active. */ + unsigned int user_active:1; /* User section active. */ + unsigned int explicit_confopt:1; /* A conffile option has been given. */ + char *explicit_conffile; /* Malloced name of an explicit + * conffile. */ + char *username; /* Malloced current user name. */ + unsigned int opt_flags; /* Current option flags. */ + enum argparser_states state; /* State of the gnupg_argparser. */ + const char *last; + void *aliases; + const void *cur_alias; + void *iio_list; + estream_t conffp; + char *confname; + opttable_t *opts; /* Malloced option table. */ + unsigned int nopts; /* Number of items in OPTS. */ +}; + + +typedef struct alias_def_s *ALIAS_DEF; +struct alias_def_s { + ALIAS_DEF next; + char *name; /* malloced buffer with name, \0, value */ + const char *value; /* ptr into name */ +}; + + +/* Object to store the names for the --ignore-invalid-option option. + This is a simple linked list. */ +typedef struct iio_item_def_s *IIO_ITEM_DEF; +struct iio_item_def_s +{ + IIO_ITEM_DEF next; + char name[1]; /* String with the long option name. */ +}; + + +static int set_opt_arg (gnupg_argparse_t *arg, unsigned int flags, char *s); +static void show_help (opttable_t *opts, unsigned int nopts,unsigned int flags); +static void show_version (void); +static void dump_option_table (gnupg_argparse_t *arg); +static int writestrings (int is_error, const char *string, + ...) GPGRT_ATTR_SENTINEL(0); + +static int arg_parse (gnupg_argparse_t *arg, gnupg_opt_t *opts, int no_init); + + + +/* Set a function to write strings which is then used instead of + * estream. The first arg of that function is MODE and the second the + * STRING to write. A mode of 1 is used for writing to stdout and a + * mode of 2 to write to stderr. Other modes are reserved and should + * not output anything. A NULL for STRING requests a flush. */ +void +gnupg_set_usage_outfnc (int (*f)(int, const char *)) +{ + custom_outfnc = f; +} + + +/* Register function F as a string mapper which takes a string as + * argument, replaces known "@FOO@" style macros and returns a new + * fixed string. Warning: The input STRING must have been allocated + * statically. */ +void +gnupg_set_fixed_string_mapper (const char *(*f)(const char*)) +{ + fixed_string_mapper = f; +} + + +/* Register a configuration directory for use by the argparse + * functions. The defined values for WHAT are: + * + * GNUPG_CONFDIR_SYS The systems's configuration dir. + * The default is /etc + * + * GNUPG_CONFDIR_USER The user's configuration directory. + * The default is $HOME. + * + * A trailing slash is ignored; to have the function lookup + * configuration files in the current directory, use ".". There is no + * error return; more configuraion values may be added in future + * revisions of this library. + */ +void +gnupg_set_confdir (int what, const char *name) +{ + char *buf, *p; + + if (what == GNUPG_CONFDIR_SYS) + { + xfree (confdir.sys); + buf = confdir.sys = xtrystrdup (name); + } + else if (what == GNUPG_CONFDIR_USER) + { + xfree (confdir.user); + buf = confdir.user = xtrystrdup (name); + } + else + return; + + if (!buf) + log_fatal ("out of core in %s\n", __func__); +#ifdef HAVE_W32_SYSTEM + for (p=buf; *p; p++) + if (*p == '\\') + *p = '/'; +#endif + /* Strip trailing slashes unless buf is "/" or any other single char + * string. */ + if (*buf) + { + for (p=buf + strlen (buf)-1; p > buf; p--) + if (*p == '/') + *p = 0; + else + break; + } +} + + + +static const char * +map_fixed_string (const char *string) +{ + return fixed_string_mapper? fixed_string_mapper (string) : string; +} + +#endif /* USE_INTERNAL_ARGPARSE */ + + +/* Write STRING and all following const char * arguments either to + stdout or, if IS_ERROR is set, to stderr. The list of strings must + be terminated by a NULL. */ +static int +writestrings (int is_error, const char *string, ...) +{ + va_list arg_ptr; + const char *s; + int count = 0; + + if (string) + { + s = string; + va_start (arg_ptr, string); + do + { /* Fixme: Swicth to estream? */ + if (custom_outfnc) + custom_outfnc (is_error? 2:1, s); + else + fputs (s, is_error? stderr : stdout); + count += strlen (s); + } + while ((s = va_arg (arg_ptr, const char *))); + va_end (arg_ptr); + } + return count; +} + + +static void +flushstrings (int is_error) +{ + if (custom_outfnc) + custom_outfnc (is_error? 2:1, NULL); + else + fflush (is_error? stderr : stdout); +} + + +#if USE_INTERNAL_ARGPARSE + +static void +deinitialize (gnupg_argparse_t *arg) +{ + if (arg->internal) + { + xfree (arg->internal->username); + xfree (arg->internal->explicit_conffile); + xfree (arg->internal->opts); + xfree (arg->internal); + arg->internal = NULL; + } + + arg->flags &= ARGPARSE_FLAG__INITIALIZED; + arg->lineno = 0; + arg->err = 0; +} + +/* Our own exit handler to clean up used memory. */ +static void +my_exit (gnupg_argparse_t *arg, int code) +{ + deinitialize (arg); + exit (code); +} + + +static gpg_err_code_t +initialize (gnupg_argparse_t *arg, gnupg_opt_t *opts, estream_t fp) +{ + /* We use a dedicated flag to detect whether *ARG has been + * initialized. This is because the old version of that struct, as + * used in GnuPG, had no requirement to zero out all fields of the + * object and existing code still sets only argc,argv and flags. */ + if (!(arg->flags & ARGPARSE_FLAG__INITIALIZED) + || (arg->flags & ARGPARSE_FLAG_RESET) + || !arg->internal) + { + /* Allocate internal data. */ + if (!(arg->flags & ARGPARSE_FLAG__INITIALIZED) || !arg->internal) + { + arg->internal = xtrymalloc (sizeof *arg->internal); + if (!arg->internal) + return gpg_err_code_from_syserror (); + arg->flags |= ARGPARSE_FLAG__INITIALIZED; /* Mark as initialized. */ + } + else if (arg->internal->opts) + xfree (arg->internal->opts); + arg->internal->opts = NULL; + arg->internal->nopts = 0; + + /* Initialize this instance. */ + arg->internal->idx = 0; + arg->internal->last = NULL; + arg->internal->inarg = 0; + arg->internal->stopped = 0; + arg->internal->in_sysconf = 0; + arg->internal->user_seen = 0; + arg->internal->user_wildcard = 0; + arg->internal->user_any_active = 0; + arg->internal->user_active = 0; + arg->internal->username = NULL; + arg->internal->mark_forced = 0; + arg->internal->mark_ignore = 0; + arg->internal->explicit_ignore = 0; + arg->internal->ignore_all_seen = 0; + arg->internal->explicit_confopt = 0; + arg->internal->explicit_conffile = NULL; + arg->internal->opt_flags = 0; + arg->internal->state = STATE_init; + arg->internal->aliases = NULL; + arg->internal->cur_alias = NULL; + arg->internal->iio_list = NULL; + arg->internal->conffp = NULL; + arg->internal->confname = NULL; + + /* Clear the copy of the option list. */ + /* Clear the error indicator. */ + arg->err = 0; + + /* Usually an option file will be parsed from the start. + * However, we do not open the stream and thus we have no way to + * know the current lineno. Using this flag we can allow the + * user to provide a lineno which we don't reset. */ + if (fp || arg->internal->conffp || !(arg->flags & ARGPARSE_FLAG_NOLINENO)) + arg->lineno = 0; + + /* Need to clear the reset request. */ + arg->flags &= ~ARGPARSE_FLAG_RESET; + + /* Check initial args. */ + if ( *arg->argc < 0 ) + log_bug ("invalid argument passed to gnupg_argparse\n"); + + } + + /* Create an array with pointers to the provided list of options. + * Keeping a copy is useful to sort that array and thus do a binary + * search and to allow for extra space at the end to insert the + * hidden options. An ARGPARSE_FLAG_RESET can be used to reinit + * this array. */ + if (!arg->internal->opts) + { + int seen_help = 0; + int seen_version = 0; + int seen_warranty = 0; + int seen_dump_options = 0; + int seen_dump_option_table = 0; + int i; + + for (i=0; opts[i].short_opt; i++) + { + if (opts[i].long_opt) + { + if (!strcmp(opts[i].long_opt, "help")) + seen_help = 1; + else if (!strcmp(opts[i].long_opt, "version")) + seen_version = 1; + else if (!strcmp(opts[i].long_opt, "warranty")) + seen_warranty = 1; + else if (!strcmp(opts[i].long_opt, "dump-options")) + seen_dump_options = 1; + else if (!strcmp(opts[i].long_opt, "dump-option-table")) + seen_dump_option_table = 1; + } + } + i += 5; /* The number of the above internal options. */ + i++; /* End of list marker. */ + arg->internal->opts = xtrycalloc (i, sizeof *arg->internal->opts); + if (!arg->internal->opts) + return gpg_err_code_from_syserror (); + for(i=0; opts[i].short_opt; i++) + { + arg->internal->opts[i].short_opt = opts[i].short_opt; + arg->internal->opts[i].flags = opts[i].flags; + arg->internal->opts[i].long_opt = opts[i].long_opt; + arg->internal->opts[i].description = opts[i].description; + arg->internal->opts[i].ordinal = i; + } + + if (!seen_help) + { + arg->internal->opts[i].short_opt = ARGPARSE_SHORTOPT_HELP; + arg->internal->opts[i].flags = ARGPARSE_TYPE_NONE; + arg->internal->opts[i].long_opt = "help"; + arg->internal->opts[i].description = "@"; + arg->internal->opts[i].ordinal = i; + i++; + } + if (!seen_version) + { + arg->internal->opts[i].short_opt = ARGPARSE_SHORTOPT_VERSION; + arg->internal->opts[i].flags = ARGPARSE_TYPE_NONE; + arg->internal->opts[i].long_opt = "version"; + arg->internal->opts[i].description = "@"; + arg->internal->opts[i].ordinal = i; + i++; + } + + if (!seen_warranty) + { + arg->internal->opts[i].short_opt = ARGPARSE_SHORTOPT_WARRANTY; + arg->internal->opts[i].flags = ARGPARSE_TYPE_NONE; + arg->internal->opts[i].long_opt = "warranty"; + arg->internal->opts[i].description = "@"; + arg->internal->opts[i].ordinal = i; + i++; + } + + if (!seen_dump_option_table) + { + arg->internal->opts[i].short_opt = ARGPARSE_SHORTOPT_DUMP_OPTTBL; + arg->internal->opts[i].flags = ARGPARSE_TYPE_NONE; + arg->internal->opts[i].long_opt = "dump-option-table"; + arg->internal->opts[i].description = "@"; + arg->internal->opts[i].ordinal = i; + i++; + } + + if (!seen_dump_options) + { + arg->internal->opts[i].short_opt = ARGPARSE_SHORTOPT_DUMP_OPTIONS; + arg->internal->opts[i].flags = ARGPARSE_TYPE_NONE; + arg->internal->opts[i].long_opt = "dump-options"; + arg->internal->opts[i].description = "@"; + arg->internal->opts[i].ordinal = i; + i++; + } + /* Take care: When adding new options remember to increase the + * size of the array. */ + + arg->internal->opts[i].short_opt = 0; + + /* Note that we do not count the end marker but keep it in the + * table anyway as an extra item. */ + arg->internal->nopts = i; + } + + if (arg->err) + { + /* Last option was erroneous. */ + const char *s; + + if (!fp && arg->internal->conffp) + fp = arg->internal->conffp; + + if (fp) + { + if ( arg->r_opt == ARGPARSE_UNEXPECTED_ARG ) + s = _("argument not expected"); + else if ( arg->r_opt == ARGPARSE_READ_ERROR ) + s = _("read error"); + else if ( arg->r_opt == ARGPARSE_KEYWORD_TOO_LONG ) + s = _("keyword too long"); + else if ( arg->r_opt == ARGPARSE_MISSING_ARG ) + s = _("missing argument"); + else if ( arg->r_opt == ARGPARSE_INVALID_ARG ) + s = _("invalid argument"); + else if ( arg->r_opt == ARGPARSE_INVALID_COMMAND ) + s = _("invalid command"); + else if ( arg->r_opt == ARGPARSE_INVALID_ALIAS ) + s = _("invalid alias definition"); + else if ( arg->r_opt == ARGPARSE_PERMISSION_ERROR ) + s = _("permission error"); + else if ( arg->r_opt == ARGPARSE_OUT_OF_CORE ) + s = _("out of core"); + else if ( arg->r_opt == ARGPARSE_NO_CONFFILE ) + s = NULL; /* Error has already been printed. */ + else if ( arg->r_opt == ARGPARSE_INVALID_META ) + s = _("invalid meta command"); + else if ( arg->r_opt == ARGPARSE_UNKNOWN_META ) + s = _("unknown meta command"); + else if ( arg->r_opt == ARGPARSE_UNEXPECTED_META ) + s = _("unexpected meta command"); + else + s = _("invalid option"); + if (s) + log_error ("%s:%u: %s\n", + gpgrt_fname_get (fp), arg->lineno, s); + } + else + { + s = arg->internal->last? arg->internal->last:"[??]"; + + if ( arg->r_opt == ARGPARSE_MISSING_ARG ) + log_error (_("missing argument for option \"%.50s\"\n"), s); + else if ( arg->r_opt == ARGPARSE_INVALID_ARG ) + log_error (_("invalid argument for option \"%.50s\"\n"), s); + else if ( arg->r_opt == ARGPARSE_UNEXPECTED_ARG ) + log_error (_("option \"%.50s\" does not expect " + "an argument\n"), s); + else if ( arg->r_opt == ARGPARSE_INVALID_COMMAND ) + log_error (_("invalid command \"%.50s\"\n"), s); + else if ( arg->r_opt == ARGPARSE_AMBIGUOUS_OPTION ) + log_error (_("option \"%.50s\" is ambiguous\n"), s); + else if ( arg->r_opt == ARGPARSE_AMBIGUOUS_COMMAND ) + log_error (_("command \"%.50s\" is ambiguous\n"),s ); + else if ( arg->r_opt == ARGPARSE_OUT_OF_CORE ) + log_error ("%s\n", _("out of core")); + else if ( arg->r_opt == ARGPARSE_PERMISSION_ERROR ) + log_error ("%s\n", _("permission error")); + else if ( arg->r_opt == ARGPARSE_NO_CONFFILE) + ; /* Error has already been printed. */ + else if ( arg->r_opt == ARGPARSE_INVALID_META ) + log_error ("%s\n", _("invalid meta command")); + else if ( arg->r_opt == ARGPARSE_UNKNOWN_META ) + log_error ("%s\n", _("unknown meta command")); + else if ( arg->r_opt == ARGPARSE_UNEXPECTED_META ) + log_error ("%s\n",_("unexpected meta command")); + else + log_error (_("invalid option \"%.50s\"\n"), s); + } + if (arg->err != ARGPARSE_PRINT_WARNING) + my_exit (arg, 2); + arg->err = 0; + } + + /* Zero out the return value union. */ + arg->r.ret_str = NULL; + arg->r.ret_long = 0; + + return 0; +} + + +static void +store_alias( ARGPARSE_ARGS *arg, char *name, char *value ) +{ + /* TODO: replace this dummy function with a rea one + * and fix the probelms IRIX has with (ALIAS_DEV)arg.. + * used as lvalue + */ + (void)arg; + (void)name; + (void)value; +#if 0 + ALIAS_DEF a = xmalloc( sizeof *a ); + a->name = name; + a->value = value; + a->next = (ALIAS_DEF)arg->internal.aliases; + (ALIAS_DEF)arg->internal.aliases = a; +#endif +} + + +/* Return true if KEYWORD is in the ignore-invalid-option list. */ +static int +ignore_invalid_option_p (ARGPARSE_ARGS *arg, const char *keyword) +{ + IIO_ITEM_DEF item = arg->internal->iio_list; + + for (; item; item = item->next) + if (!strcmp (item->name, keyword)) + return 1; + return 0; +} + + +/* Add the keywords up to the next LF to the list of to be ignored + options. After returning FP will either be at EOF or the next + character read wll be the first of a new line. The function + returns 0 on success or true on malloc failure. */ +static int +ignore_invalid_option_add (ARGPARSE_ARGS *arg, estream_t fp) +{ + IIO_ITEM_DEF item; + int c; + char name[100]; + int namelen = 0; + int ready = 0; + enum { skipWS, collectNAME, skipNAME, addNAME} state = skipWS; + + while (!ready) + { + c = gpgrt_getc (fp); + if (c == '\n') + ready = 1; + else if (c == EOF) + { + c = '\n'; + ready = 1; + } + again: + switch (state) + { + case skipWS: + if (!isascii (c) || !isspace(c)) + { + namelen = 0; + state = collectNAME; + goto again; + } + break; + + case collectNAME: + if (isspace (c)) + { + state = addNAME; + goto again; + } + else if (namelen < DIM(name)-1) + name[namelen++] = c; + else /* Too long. */ + state = skipNAME; + break; + + case skipNAME: + if (isspace (c)) + { + state = skipWS; + goto again; + } + break; + + case addNAME: + name[namelen] = 0; + if (!ignore_invalid_option_p (arg, name)) + { + item = xtrymalloc (sizeof *item + namelen); + if (!item) + return 1; + strcpy (item->name, name); + item->next = (IIO_ITEM_DEF)arg->internal->iio_list; + arg->internal->iio_list = item; + } + state = skipWS; + goto again; + } + } + return 0; +} + + +/* Clear the entire ignore-invalid-option list. */ +static void +ignore_invalid_option_clear (ARGPARSE_ARGS *arg) +{ + IIO_ITEM_DEF item, tmpitem; + + for (item = arg->internal->iio_list; item; item = tmpitem) + { + tmpitem = item->next; + xfree (item); + } + arg->internal->iio_list = NULL; +} + + +/* Make sure the username field is filled. Return 0 on success. */ +static int +assure_username (gnupg_argparse_t *arg) +{ + if (!arg->internal->username) + { + arg->internal->username = gnupg_getusername (); + if (!arg->internal->username) + { + log_error ("%s:%u: error getting current user's name: %s\n", + arg->internal->confname, arg->lineno, + gpg_strerror (gpg_error_from_syserror ())); + /* Not necessary the correct error code but given that we + * either have a malloc error or some internal system error, + * it is the best we can do. */ + return ARGPARSE_PERMISSION_ERROR; + } + } + return 0; +} + + +/* Implementation of the "user" command. ARG is the context. ARGS is + * a non-empty string which this function is allowed to modify. */ +static int +handle_meta_user (gnupg_argparse_t *arg, unsigned int alternate, char *args) +{ + int rc; + + (void)alternate; + + rc = assure_username (arg); + if (rc) + return rc; + + arg->internal->user_seen = 1; + if (*args == '*' && !args[1]) + { + arg->internal->user_wildcard = 1; + arg->internal->user_active = !arg->internal->user_any_active; + } + else if (arg->internal->user_wildcard) + { + /* All other user statements are ignored after a wildcard. */ + arg->internal->user_active = 0; + } + else if (!strcasecmp (args, arg->internal->username)) + { + arg->internal->user_any_active = 1; + arg->internal->user_active = 1; + } + else + { + arg->internal->user_active = 0; + } + + return 0; +} + + +/* Implementation of the "force" command. ARG is the context. A + * value of 0 for ALTERNATE is "force", a value of 1 requests an + * unforce". ARGS is the empty string and not used. */ +static int +handle_meta_force (gnupg_argparse_t *arg, unsigned int alternate, char *args) +{ + (void)args; + + arg->internal->mark_forced = alternate? 0 : 1; + + return 0; +} + + +/* Implementation of the "ignore" command. ARG is the context. A + * value of 0 for ALTERNATE is a plain "ignore", a value of 1 request + * an "unignore, a value of 2 requests an "ignore-all". ARGS is the + * empty string and not used. */ +static int +handle_meta_ignore (gnupg_argparse_t *arg, unsigned int alternate, char *args) +{ + (void)args; + + if (!alternate) + { + arg->internal->mark_ignore = 1; + arg->internal->explicit_ignore = 1; + } + else if (alternate == 1) + { + arg->internal->mark_ignore = 0; + arg->internal->explicit_ignore = 1; + } + else + arg->internal->ignore_all_seen = 1; + + return 0; +} + + +/* Implementation of the "echo" command. ARG is the context. If + * ALTERNATE is true the filename is not printed. ARGS is the string + * to log. */ +static int +handle_meta_echo (gnupg_argparse_t *arg, unsigned int alternate, char *args) +{ + int rc = 0; + char *p, *pend; + + if (alternate) + log_info ("%s", ""); + else + log_info ("%s:%u: ", arg->internal->confname, arg->lineno); + + while (*args) + { + p = strchr (args, '$'); + if (!p) + { + log_printf ("%s", args); + break; + } + *p = 0; + log_printf ("%s", args); + if (p[1] == '$') + { + log_printf ("$"); + args = p+2; + continue; + } + if (p[1] != '{') + { + log_printf ("$"); + args = p+1; + continue; + } + pend = strchr (p+2, '}'); + if (!pend) /* No closing brace. */ + { + log_printf ("$"); + args = p+1; + continue; + } + p += 2; + *pend = 0; + args = pend+1; + if (!strcmp (p, "user")) + { + rc = assure_username (arg); + if (rc) + goto leave; + log_printf ("%s", arg->internal->username); + } + else if (!strcmp (p, "file")) + log_printf ("%s", arg->internal->confname); + else if (!strcmp (p, "line")) + log_printf ("%u", arg->lineno); + else if (!strcmp (p, "epoch")) + log_printf ("%lu", (unsigned long)time (NULL)); + } + + leave: + log_printf ("\n"); + return rc; +} + + +/* Implementation of the "verbose" command. ARG is the context. If + * ALTERNATE is true the verbosity is disabled. ARGS is not used. */ +static int +handle_meta_verbose (gnupg_argparse_t *arg, unsigned int alternate, char *args) +{ + (void)args; + + if (alternate) + arg->internal->verbose = 0; + else + arg->internal->verbose = 1; + return 0; +} + +/* Handle a meta command. KEYWORD has the content inside the brackets + * with leading and trailing spaces removed. The function may modify + * KEYWORD. On success 0 is returned, on error an ARGPARSE_ error + * code is returned. */ +static int +handle_metacmd (gnupg_argparse_t *arg, char *keyword) +{ + static struct { + const char *name; /* Name of the command. */ + unsigned short alternate; /* Use alternate version of the command. */ + unsigned short needarg:1; /* Command requires an argument. */ + unsigned short always:1; /* Command allowed in all conf files. */ + unsigned short noskip:1; /* Even done in non-active [user] mode. */ + int (*func)(gnupg_argparse_t *arg, + unsigned int alternate, char *args); /*handler*/ + } cmds[] = + {{ "user", 0, 1, 0, 1, handle_meta_user }, + { "force", 0, 0, 0, 0, handle_meta_force }, + { "+force", 0, 0, 0, 0, handle_meta_force }, + { "-force", 1, 0, 0, 0, handle_meta_force }, + { "ignore", 0, 0, 0, 0, handle_meta_ignore }, + { "+ignore", 0, 0, 0, 0, handle_meta_ignore }, + { "-ignore", 1, 0, 0, 0, handle_meta_ignore }, + { "ignore-all", 2, 0, 0, 0, handle_meta_ignore }, + { "+ignore-all", 2, 0, 0, 0, handle_meta_ignore }, + { "verbose", 0, 0, 1, 1, handle_meta_verbose }, + { "+verbose", 0, 0, 1, 1, handle_meta_verbose }, + { "-verbose", 1, 0, 1, 1, handle_meta_verbose }, + { "echo", 0, 1, 1, 1, handle_meta_echo }, + { "-echo", 1, 1, 1, 1, handle_meta_echo }, + { "info", 0, 1, 1, 0, handle_meta_echo }, + { "-info", 1, 1, 1, 0, handle_meta_echo } + }; + char *rest; + int i; + + for (rest = keyword; *rest && !(isascii (*rest) && isspace (*rest)); rest++) + ; + if (*rest) + { + *rest++ = 0; + trim_spaces (rest); + } + + for (i=0; i < DIM (cmds); i++) + if (!strcmp (cmds[i].name, keyword)) + break; + if (!(i < DIM (cmds))) + return ARGPARSE_UNKNOWN_META; + if (cmds[i].needarg && !*rest) + return ARGPARSE_MISSING_ARG; + if (!cmds[i].needarg && *rest) + return ARGPARSE_UNEXPECTED_ARG; + if (!arg->internal->in_sysconf && !cmds[i].always) + return ARGPARSE_UNEXPECTED_META; + + if (!cmds[i].noskip + && arg->internal->in_sysconf + && arg->internal->user_seen + && !arg->internal->user_active) + return 0; /* Skip this meta command. */ + + return cmds[i].func (arg, cmds[i].alternate, rest); +} + + +/* Helper for gnupg_argparse. */ +static void +prepare_arg_return (gnupg_argparse_t *arg, opttable_t *opts, + int idx, int in_alias, int set_ignore) +{ + /* No argument found at the end of the line. */ + if (in_alias) + arg->r_opt = ARGPARSE_MISSING_ARG; + else if (!(opts[idx].flags & ARGPARSE_TYPE_MASK)) + arg->r_type = ARGPARSE_TYPE_NONE; /* Does not take an arg. */ + else if ((opts[idx].flags & ARGPARSE_OPT_OPTIONAL)) + arg->r_type = ARGPARSE_TYPE_NONE; /* No optional argument. */ + else if (!(opts[idx].ignore && !opts[idx].forced) && !set_ignore) + arg->r_opt = ARGPARSE_MISSING_ARG; + + /* If the caller wants us to return the attributes or + * ignored options, or these flags in. */ + if ((arg->flags & ARGPARSE_FLAG_WITHATTR)) + { + if (opts[idx].ignore) + arg->r_type |= ARGPARSE_ATTR_IGNORE; + if (opts[idx].forced) + arg->r_type |= ARGPARSE_ATTR_FORCE; + if (set_ignore) + arg->r_type |= ARGPARSE_OPT_IGNORE; + } +} + +/**************** + * Get options from a file. + * Lines starting with '#' are comment lines. + * Syntax is simply a keyword and the argument. + * Valid keywords are all keywords from the long_opt list without + * the leading dashes. The special keywords "help", "warranty" and "version" + * are not valid here. + * The special keyword "alias" may be used to store alias definitions, + * which are later expanded like long options. + * The option + * ignore-invalid-option OPTIONNAMEs + * is recognized and updates a list of option which should be ignored if they + * are not defined. + * Caller must free returned strings. + * If called with FP set to NULL command line args are parse instead. + * + * Q: Should we allow the syntax + * keyword = value + * and accept for boolean options a value of 1/0, yes/no or true/false? + * Note: Abbreviation of options is here not allowed. + */ +int +gnupg_argparse (estream_t fp, gnupg_argparse_t *arg, gnupg_opt_t *opts_orig) +{ + enum { Ainit, + Acomment, /* In a comment line. */ + Acopykeyword, /* Collecting a keyword. */ + Awaitarg, /* Wait for an argument. */ + Acopyarg, /* Copy the argument. */ + Akeyword_eol, /* Got keyword at end of line. */ + Akeyword_spc, /* Got keyword at space. */ + Acopymetacmd, /* Copy a meta command. */ + Askipmetacmd, /* Skip spaces after metacmd. */ + Askipmetacmd2,/* Skip comment after metacmd. */ + Ametacmd, /* Process the metacmd. */ + Askipandleave /* Skip the rest of the line and then leave. */ + } state; + opttable_t *opts; + unsigned int nopts; + int i, c; + int idx = 0; + char keyword[100]; + char *buffer = NULL; + size_t buflen = 0; + int in_alias=0; + int set_ignore = 0; + int unread_buf[3]; /* We use an int so that we can store EOF. */ + int unread_buf_count = 0; + + if (arg && !opts_orig) + { + deinitialize (arg); + return 0; + } + + if (!fp) /* Divert to arg_parse() in this case. */ + return arg_parse (arg, opts_orig, 0); + + if (initialize (arg, opts_orig, fp)) + return (arg->r_opt = ARGPARSE_OUT_OF_CORE); + + opts = arg->internal->opts; + nopts = arg->internal->nopts; + + /* If the LINENO is zero we assume that we are at the start of a + * file and we skip over a possible Byte Order Mark. */ + if (!arg->lineno) + { + unread_buf[0] = gpgrt_fgetc (fp); + unread_buf[1] = gpgrt_fgetc (fp); + unread_buf[2] = gpgrt_fgetc (fp); + if (unread_buf[0] != 0xef + || unread_buf[1] != 0xbb + || unread_buf[2] != 0xbf) + unread_buf_count = 3; + } + + arg->internal->opt_flags = 0; + + /* Find the next keyword. */ + state = Ainit; + i = 0; + for (;;) + { + nextstate: + /* Before scanning the next char handle the keyword seen states. */ + if (state == Akeyword_eol || state == Akeyword_spc) + { + /* We are either at the end of a line or right after a + * keyword. In the latter case we need to find the keyword + * so that we can decide whether an argument is required. */ + + /* Check the keyword. */ + for (idx=0; idx < nopts; idx++ ) + { + if (opts[idx].long_opt && !strcmp (opts[idx].long_opt, keyword)) + break; + } + arg->r_opt = opts[idx].short_opt; + if (!(idx < nopts)) + { + /* The option (keyword) is not known - check for + * internal keywords before returning an error. */ + if (state == Akeyword_spc && !strcmp (keyword, "alias")) + { + in_alias = 1; + state = Awaitarg; + } + else if (!strcmp (keyword, "ignore-invalid-option")) + { + /* We might have keywords as argument - add them to + * the list of ignored keywords. Note that we + * ignore empty argument lists and thus do not to + * call the function in the Akeyword_eol state. */ + if (state == Akeyword_spc) + { + if (ignore_invalid_option_add (arg, fp)) + { + arg->r_opt = ARGPARSE_OUT_OF_CORE; + goto leave; + } + arg->lineno++; + } + state = Ainit; + i = 0; + } + else if (ignore_invalid_option_p (arg, keyword)) + { + /* This invalid option is already in the iio list. */ + state = state == Akeyword_eol? Ainit : Acomment; + i = 0; + } + else + { + arg->r_opt = ((opts[idx].flags & ARGPARSE_OPT_COMMAND) + ? ARGPARSE_INVALID_COMMAND + : ARGPARSE_INVALID_OPTION); + if (state == Akeyword_spc) + state = Askipandleave; + else + goto leave; + } + } + else if (state != Akeyword_spc + && arg->internal->in_sysconf + && arg->internal->user_seen + && !arg->internal->user_active) + { + /* We are in a [user] meta command and it is not active. + * Skip the command. */ + state = state == Akeyword_eol? Ainit : Acomment; + i = 0; + } + else if (state != Akeyword_spc + && (opts[idx].flags & ARGPARSE_OPT_IGNORE)) + { + /* Known option is configured to be ignored. Start from + * scratch (new line) or process like a comment. */ + state = state == Akeyword_eol? Ainit : Acomment; + i = 0; + } + else /* Known option */ + { + set_ignore = 0; + + if (arg->internal->in_sysconf) + { + /* Set the current forced and ignored attributes. */ + if (arg->internal->mark_forced) + opts[idx].forced = 1; + if (arg->internal->mark_ignore) + opts[idx].ignore = 1; + if (arg->internal->explicit_ignore) + opts[idx].explicit_ignore = 1; + + if (opts[idx].ignore && !opts[idx].forced) + { + if (arg->internal->verbose) + log_info ("%s:%u: ignoring option \"--%s\"\n", + arg->internal->confname, + arg->lineno, + opts[idx].long_opt); + if ((arg->flags & ARGPARSE_FLAG_WITHATTR)) + set_ignore = 1; + else + { + state = state == Akeyword_eol? Ainit : Acomment; + i = 0; + goto nextstate; /* Ignore this one. */ + } + } + } + else /* Non-sysconf file */ + { /* Act upon the forced and ignored attributes. */ + if (opts[idx].ignore || opts[idx].forced) + { + if (arg->internal->verbose) + log_info ("%s:%u: ignoring option \"--%s\"" + " due to attributes:%s%s\n", + arg->internal->confname, + arg->lineno, + opts[idx].long_opt, + opts[idx].forced? " forced":"", + opts[idx].ignore? " ignore":""); + if ((arg->flags & ARGPARSE_FLAG_WITHATTR)) + set_ignore = 1; + else + { + state = state == Akeyword_eol? Ainit : Acomment; + i = 0; + goto nextstate; /* Ignore this one. */ + } + } + } + + if (state == Akeyword_spc) + { + /* If we shall ignore but not set the option we skip + * the argument. Otherwise we would need to use a + * made-up but not used args in the conf file. */ + if (set_ignore || (opts[idx].ignore && !opts[idx].forced)) + { + prepare_arg_return (arg, opts, idx, 0, set_ignore); + set_ignore = 0; + state = Askipandleave; + } + else + state = Awaitarg; + } + else + { + prepare_arg_return (arg, opts, idx, 0, set_ignore); + set_ignore = 0; + goto leave; + } + + } + } /* (end state Akeyword_eol/Akeyword_spc) */ + else if (state == Ametacmd) + { + /* We are at the end of a line. */ + log_assert (*keyword == '['); + trim_spaces (keyword+1); + if (!keyword[1]) + { + arg->r_opt = ARGPARSE_INVALID_META; /* Empty. */ + goto leave; + } + c = handle_metacmd (arg, keyword+1); + if (c) + { + arg->r_opt = c; /* Return error. */ + goto leave; + } + state = Ainit; + i = 0; + } + + /* Get the next character from the line. */ + if (unread_buf_count) + c = unread_buf[3 - unread_buf_count--]; + else + c = gpgrt_fgetc (fp); + + if (c == '\n' || c== EOF ) + { /* Handle end of line. */ + if ( c != EOF ) + arg->lineno++; + if (state == Askipandleave) + goto leave; + else if (state == Acopykeyword) + { + keyword[i] = 0; + state = Akeyword_eol; + goto nextstate; + } + else if (state == Acopymetacmd) + { + arg->r_opt = ARGPARSE_INVALID_META; /* "]" missing */ + goto leave; + } + else if (state == Askipmetacmd || state == Askipmetacmd2) + { + state = Ametacmd; + goto nextstate; + } + else if (state == Awaitarg) + { + /* No argument found at the end of the line. */ + prepare_arg_return (arg, opts, idx, in_alias, set_ignore); + set_ignore = 0; + goto leave; + } + else if (state == Acopyarg) + { + /* Has an argument at the end of a line. */ + if (in_alias) + { + if (!buffer) + arg->r_opt = ARGPARSE_UNEXPECTED_ARG; + else + { + char *p; + + buffer[i] = 0; + p = strpbrk (buffer, " \t"); + if (p) + { + *p++ = 0; + trim_spaces (p); + } + if (!p || !*p) + { + xfree (buffer); + arg->r_opt = ARGPARSE_INVALID_ALIAS; + } + else + { + store_alias (arg, buffer, p); + } + } + } + else if (!(opts[idx].flags & ARGPARSE_TYPE_MASK)) + arg->r_opt = ARGPARSE_UNEXPECTED_ARG; + else + { + char *p; + + if (!buffer) + { + keyword[i] = 0; + buffer = xtrystrdup (keyword); + if (!buffer) + arg->r_opt = ARGPARSE_OUT_OF_CORE; + } + else + buffer[i] = 0; + + if (buffer) + { + trim_spaces (buffer); + p = buffer; + if (*p == '"') + { + /* Remove quotes. */ + p++; + if (*p && p[strlen(p)-1] == '\"' ) + p[strlen(p)-1] = 0; + } + if (!set_opt_arg (arg, opts[idx].flags, p)) + xfree (buffer); + else + gpgrt_annotate_leaked_object (buffer); + /* If the caller wants us to return the attributes or + * ignored options, or these flags in. */ + if ((arg->flags & ARGPARSE_FLAG_WITHATTR)) + { + if (opts[idx].ignore) + arg->r_type |= ARGPARSE_ATTR_IGNORE; + if (opts[idx].forced) + arg->r_type |= ARGPARSE_ATTR_FORCE; + if (set_ignore) + arg->r_type |= ARGPARSE_OPT_IGNORE; + } + } + } + goto leave; + } + else if (c == EOF) + { + ignore_invalid_option_clear (arg); + if (gpgrt_ferror (fp)) + arg->r_opt = ARGPARSE_READ_ERROR; + else + arg->r_opt = 0; /* EOF. */ + goto leave; + } + state = Ainit; + i = 0; + } /* (end handle end of line) */ + else if (state == Askipandleave) + ; /* Skip. */ + else if (state == Ainit && isascii (c) && isspace(c)) + ; /* Skip leading white space. */ + else if (state == Ainit && c == '#' ) + state = Acomment; /* Start of a comment. */ + else if (state == Acomment || state == Askipmetacmd2) + ; /* Skip comments. */ + else if (state == Askipmetacmd) + { + if (c == '#') + state = Askipmetacmd2; + else if (!(isascii (c) && isspace(c))) + { + arg->r_opt = ARGPARSE_INVALID_META; + state = Askipandleave; + } + } + else if (state == Acopykeyword && isascii (c) && isspace(c)) + { + keyword[i] = 0; + state = Akeyword_spc; + goto nextstate; + } + else if (state == Acopymetacmd && c == ']') + { + keyword[i] = 0; + state = Askipmetacmd; + goto nextstate; + } + else if (state == Awaitarg) + { + /* Skip leading spaces of the argument. */ + if (!isascii (c) || !isspace(c)) + { + i = 0; + keyword[i++] = c; + state = Acopyarg; + } + } + else if (state == Acopyarg) + { + /* Collect the argument. */ + if (buffer) + { + if (i < buflen-1) + buffer[i++] = c; + else + { + char *tmp; + size_t tmplen = buflen + 50; + + tmp = xtryrealloc (buffer, tmplen); + if (tmp) + { + buflen = tmplen; + buffer = tmp; + buffer[i++] = c; + } + else + { + xfree (buffer); + arg->r_opt = ARGPARSE_OUT_OF_CORE; + goto leave; + } + } + } + else if (i < DIM(keyword)-1) + keyword[i++] = c; + else + { + size_t tmplen = DIM(keyword) + 50; + buffer = xtrymalloc (tmplen); + if (buffer) + { + buflen = tmplen; + memcpy(buffer, keyword, i); + buffer[i++] = c; + } + else + { + arg->r_opt = ARGPARSE_OUT_OF_CORE; + goto leave; + } + } + } + else if (i >= DIM(keyword)-1) + { + arg->r_opt = ARGPARSE_KEYWORD_TOO_LONG; + state = Askipandleave; /* Skip rest of line and leave. */ + } + else if (!i) + { + state = c == '[' ? Acopymetacmd : Acopykeyword; + keyword[i++] = c; + } + else + { + keyword[i++] = c; + } + } + + leave: + return arg->r_opt; +} + + +/* Return true if the list of options OPTS has any option marked with + * ARGPARSE_OPT_CONFFILE. */ +static int +any_opt_conffile (opttable_t *opts, unsigned int nopts) +{ + int i; + + for (i=0; i < nopts; i++ ) + if ((opts[i].flags & ARGPARSE_OPT_CONFFILE)) + return 1; + return 0; +} + + +/* Return true if FNAME is an absolute filename. */ +static int +is_absfname (const char *fname) +{ + const char *s; + +#ifdef HAVE_W32_SYSTEM + s = strchr (fname, ':'); + if (s) + s++; + else + s = fname; +#else + s = fname; +#endif + + return (*s == '/' +#ifdef HAVE_W32_SYSTEM + || *s == DIRSEP_C +#endif + ); +} + + +/* If FNAME specifies two files of the form + * NAME1:/NAME2 (Unix) + * or + * NAME1;[x:]/NAME2 (Windows) + * return a pointer to the delimiter or NULL if there is none. + */ +static const char * +is_twopartfname (const char *fname) +{ + const char *s; + + if ((s = strchr (fname, PATHSEP_C)) && is_absfname (s+1) && s != fname) + return s; + return NULL; +} + + +/* Try to use a version-ed config file name. A version-ed config file + * name is one which has the packages version number appended. For + * example if the standard config file name is "foo.conf" and the + * version of the foo program is 1.2.3-beta1 the following config + * files are tried in order until one is readable: + * + * foo.conf-1.2.3-beta1 + * foo.conf-1.2.3 + * foo.conf-1.2 + * foo.conf-1 + * foo.conf + * + * The argument CONFIGNAME should already be expanded. On success a + * newly allocated file name is returned. On error NULL is returned. + */ +static char * +try_versioned_conffile (const char *configname) +{ + const char *version = strusage (13); + char *name; + char *dash, *endp; + + if (!version || !*version) + return NULL; /* No program version known. */ + + name = strconcat (configname, "-", version, NULL); + if (!name) + return NULL; /* Oops: Out of core - ignore. */ + dash = name + strlen (configname); + + endp = dash + strlen (dash) - 1; + while (endp > dash) + { + if (!gnupg_access (name, R_OK)) + { + return name; + } + for (; endp > dash; endp--) + { + if (*endp == '-' || *endp == '.') + { + *endp = 0; + break; + } + } + } + + xfree (name); + return NULL; +} + + +/* This function is called after a sysconf file has been read. */ +static void +finish_read_sys (gnupg_argparse_t *arg) +{ + opttable_t *opts = arg->internal->opts; + unsigned int nopts = arg->internal->nopts; + int i; + + if (arg->internal->ignore_all_seen) + { + /* [ignore-all] was used: Set all options which have not + * explictly been set as ignore or not ignore to ignore. */ + for (i = 0; i < nopts; i++) + { + if (!opts[i].explicit_ignore) + opts[i].ignore = 1; + } + } + + /* Reset all flags which pertain only to sysconf files. */ + arg->internal->in_sysconf = 0; + arg->internal->user_active = 0; + arg->internal->mark_forced = 0; + arg->internal->mark_ignore = 0; + arg->internal->explicit_ignore = 0; + arg->internal->ignore_all_seen = 0; +} + +/* The full arg parser which handles option files and command line + * arguments. The behaviour depends on the combinations of CONFNAME + * and the ARGPARSE_FLAG_xxx values: + * + * | CONFNAME | SYS | USER | Action | + * |----------+-----+------+--------------------| + * | NULL | - | - | cmdline | + * | string | 0 | 1 | user, cmdline | + * | string | 1 | 0 | sys, cmdline | + * | string | 1 | 1 | sys, user, cmdline | + * + * Note that if an option has been flagged with ARGPARSE_OPT_CONFFILE + * and a type of ARGPARSE_TYPE_STRING that option is not returned but + * the specified configuration file is processed directly; if + * ARGPARSE_TYPE_NONE is used no user configuration files are + * processed and from the system configuration files only those which + * are immutable are processed. The string values for CONFNAME shall + * not include a directory part because that is taken from the values + * set by gnupg_set_confdir. However, if CONFNAME is a twopart + * filename delimited by a colon (semicolon on Windows) with the + * second part being an absolute filename, the first part is used for + * the SYS file and the the entire second part for the USER file. + */ +int +gnupg_argparser (gnupg_argparse_t *arg, gnupg_opt_t *opts, + const char *confname) +{ + /* First check whether releasing the resources has been requested. */ + if (arg && !opts) + { + deinitialize (arg); + return 0; + } + + /* Make sure that the internal data object is ready and also print + * warnings or errors from the last iteration. */ + if (initialize (arg, opts, NULL)) + return (arg->r_opt = ARGPARSE_OUT_OF_CORE); + + next_state: + switch (arg->internal->state) + { + case STATE_init: + if (arg->argc && arg->argv && *arg->argc + && any_opt_conffile (arg->internal->opts, arg->internal->nopts)) + { + /* The list of option allow for conf files + * (e.g. gpg's "--option FILE" and "--no-options") + * Now check whether one was really given on the command + * line. Note that we don't need to run this code if no + * argument array was provided. */ + int save_argc = *arg->argc; + char **save_argv = *arg->argv; + unsigned int save_flags = arg->flags; + int save_idx = arg->internal->idx; + int any_no_conffile = 0; + + arg->flags = (ARGPARSE_FLAG_KEEP | ARGPARSE_FLAG_NOVERSION + | ARGPARSE_FLAG__INITIALIZED); + while (arg_parse (arg, opts, 1)) + { + if ((arg->internal->opt_flags & ARGPARSE_OPT_CONFFILE)) + { + arg->internal->explicit_confopt = 1; + if ((arg->r_type & ARGPARSE_TYPE_MASK) == ARGPARSE_TYPE_STRING + && !arg->internal->explicit_conffile) + { + /* Store the first conffile name. All further + * conf file options are not handled. */ + arg->internal->explicit_conffile + = xtrystrdup (arg->r.ret_str); + if (!arg->internal->explicit_conffile) + return (arg->r_opt = ARGPARSE_OUT_OF_CORE); + + } + else if ((arg->r_type & ARGPARSE_TYPE_MASK) + == ARGPARSE_TYPE_NONE) + any_no_conffile = 1; + } + } + if (any_no_conffile) + { + /* A NoConffile option overrides any other conf file option. */ + xfree (arg->internal->explicit_conffile); + arg->internal->explicit_conffile = NULL; + } + /* Restore parser. */ + *arg->argc = save_argc; + *arg->argv = save_argv; + arg->flags = save_flags; + arg->internal->idx = save_idx; + } + + if (confname && *confname) + { + if ((arg->flags & ARGPARSE_FLAG_SYS)) + arg->internal->state = STATE_open_sys; + else if ((arg->flags & ARGPARSE_FLAG_USER)) + arg->internal->state = STATE_open_user; + else + return (arg->r_opt = ARGPARSE_INVALID_ARG); + } + else + arg->internal->state = STATE_open_cmdline; + goto next_state; + + case STATE_open_sys: + { + /* If it is a two part name take the first part. */ + const char *s; + char *tmpname = NULL; + + if ((s = is_twopartfname (confname))) + { + tmpname = xtrymalloc (s - confname + 1); + if (!tmpname) + return (arg->r_opt = ARGPARSE_OUT_OF_CORE); + memcpy (tmpname, confname, s-confname); + tmpname[s-confname] = 0; + s = tmpname; + } + else + s = confname; + xfree (arg->internal->confname); + arg->internal->confname = make_filename_try + (confdir.sys? confdir.sys : "/etc", s, NULL); + xfree (tmpname); + if (!arg->internal->confname) + return (arg->r_opt = ARGPARSE_OUT_OF_CORE); + } + arg->lineno = 0; + arg->internal->idx = 0; + arg->internal->verbose = 0; + arg->internal->stopped = 0; + arg->internal->inarg = 0; + gpgrt_fclose (arg->internal->conffp); + arg->internal->conffp = gpgrt_fopen (arg->internal->confname, "r"); + if (!arg->internal->conffp) + { + if ((arg->flags & ARGPARSE_FLAG_VERBOSE) || arg->internal->verbose) + log_info (_("Note: no default option file '%s'\n"), + arg->internal->confname); + if ((arg->flags & ARGPARSE_FLAG_USER)) + arg->internal->state = STATE_open_user; + else + arg->internal->state = STATE_open_cmdline; + goto next_state; + } + + if ((arg->flags & ARGPARSE_FLAG_VERBOSE) || arg->internal->verbose) + log_info (_("reading options from '%s'\n"), + arg->internal->confname); + arg->internal->state = STATE_read_sys; + arg->internal->in_sysconf = 1; + arg->r.ret_str = xtrystrdup (arg->internal->confname); + if (!arg->r.ret_str) + arg->r_opt = ARGPARSE_OUT_OF_CORE; + else + { + gpgrt_annotate_leaked_object (arg->r.ret_str); + arg->r_opt = ARGPARSE_CONFFILE; + arg->r_type = ARGPARSE_TYPE_STRING; + } + break; + + case STATE_open_user: + if (arg->internal->explicit_confopt + && arg->internal->explicit_conffile) + { + /* An explict option to use a specific configuration file + * has been given - use that one. */ + xfree (arg->internal->confname); + arg->internal->confname + = xtrystrdup (arg->internal->explicit_conffile); + if (!arg->internal->confname) + return (arg->r_opt = ARGPARSE_OUT_OF_CORE); + } + else if (arg->internal->explicit_confopt) + { + /* An explict option not to use a configuration file has + * been given - leap direct to command line reading. */ + arg->internal->state = STATE_open_cmdline; + goto next_state; + } + else + { + /* Use the standard configure file. If it is a two part + * name take the second part. If it is the standard name + * and ARGPARSE_FLAG_USERVERS is set try versioned config + * files. */ + const char *s; + char *nconf; + + xfree (arg->internal->confname); + if ((s = is_twopartfname (confname))) + { + arg->internal->confname = make_filename_try (s + 1, NULL); + if (!arg->internal->confname) + return (arg->r_opt = ARGPARSE_OUT_OF_CORE); + } + else + { + arg->internal->confname = make_filename_try + (confdir.user? confdir.user : "~/.config", confname, NULL); + if (!arg->internal->confname) + return (arg->r_opt = ARGPARSE_OUT_OF_CORE); + if ((arg->flags & ARGPARSE_FLAG_USERVERS) + && (nconf = try_versioned_conffile (arg->internal->confname))) + { + xfree (arg->internal->confname); + arg->internal->confname = nconf; + } + } + } + arg->lineno = 0; + arg->internal->idx = 0; + arg->internal->verbose = 0; + arg->internal->stopped = 0; + arg->internal->inarg = 0; + arg->internal->in_sysconf = 0; + gpgrt_fclose (arg->internal->conffp); + arg->internal->conffp = gpgrt_fopen (arg->internal->confname, "r"); + if (!arg->internal->conffp) + { + arg->internal->state = STATE_open_cmdline; + if (arg->internal->explicit_confopt) + { + log_error (_("option file '%s': %s\n"), + arg->internal->confname, strerror (errno)); + return (arg->r_opt = ARGPARSE_NO_CONFFILE); + } + else + { + if ((arg->flags & ARGPARSE_FLAG_VERBOSE) + || arg->internal->verbose) + log_info (_("Note: no default option file '%s'\n"), + arg->internal->confname); + goto next_state; + } + } + + if ((arg->flags & ARGPARSE_FLAG_VERBOSE) || arg->internal->verbose) + log_info (_("reading options from '%s'\n"), + arg->internal->confname); + arg->internal->state = STATE_read_user; + arg->r.ret_str = xtrystrdup (arg->internal->confname); + if (!arg->r.ret_str) + arg->r_opt = ARGPARSE_OUT_OF_CORE; + else + { + gpgrt_annotate_leaked_object (arg->r.ret_str); + arg->r_opt = ARGPARSE_CONFFILE; + arg->r_type = ARGPARSE_TYPE_STRING; + } + break; + + case STATE_open_cmdline: + gpgrt_fclose (arg->internal->conffp); + arg->internal->conffp = NULL; + xfree (arg->internal->confname); + arg->internal->confname = NULL; + arg->internal->idx = 0; + arg->internal->verbose = 0; + arg->internal->stopped = 0; + arg->internal->inarg = 0; + arg->internal->in_sysconf = 0; + if (!arg->argc || !arg->argv || !*arg->argv) + { + /* No or empty argument vector - don't bother to parse things. */ + arg->internal->state = STATE_finished; + goto next_state; + } + arg->r_opt = ARGPARSE_CONFFILE; + arg->r_type = ARGPARSE_TYPE_NONE; + arg->r.ret_str = NULL; + arg->internal->state = STATE_read_cmdline; + break; + + case STATE_read_sys: + arg->r_opt = gnupg_argparse (arg->internal->conffp, arg, opts); + if (!arg->r_opt) + { + finish_read_sys (arg); + arg->internal->state = STATE_open_user; + goto next_state; + } + if ((arg->internal->opt_flags & ARGPARSE_OPT_CONFFILE)) + goto next_state; /* Already handled - again. */ + break; + + case STATE_read_user: + arg->r_opt = gnupg_argparse (arg->internal->conffp, arg, opts); + if (!arg->r_opt) + { + arg->internal->state = STATE_open_cmdline; + goto next_state; + } + if ((arg->internal->opt_flags & ARGPARSE_OPT_CONFFILE)) + goto next_state; /* Already handled - again. */ + break; + + case STATE_read_cmdline: + arg->r_opt = arg_parse (arg, opts, 1); + if (!arg->r_opt) + { + arg->internal->state = STATE_finished; + goto next_state; + } + if ((arg->internal->opt_flags & ARGPARSE_OPT_CONFFILE)) + goto next_state; /* Already handled - again. */ + break; + + case STATE_finished: + arg->r_opt = 0; + break; + } + + return arg->r_opt; +} + + + +/* Given the list of options in ARG and a keyword, return the index of + * the long option matching KEYWORD. On error -1 is returned for not + * found or -2 for ambigious keyword. */ +static int +find_long_option (gnupg_argparse_t *arg, const char *keyword) +{ + int i; + size_t n; + opttable_t *opts = arg->internal->opts; + unsigned int nopts = arg->internal->nopts; + + /* Would be better if we can do a binary search, but it is not + * possible to reorder our option table because we would mess up our + * help strings. What we can do is: Build an option lookup table + * when this function is first invoked. The latter has already been + * done. */ + if (!*keyword) + return -1; + for (i=0; i < nopts; i++ ) + if (opts[i].long_opt && !strcmp (opts[i].long_opt, keyword)) + return i; + /* Not found. See whether it is an abbreviation. Aliases may not + * be abbreviated, though. */ + n = strlen (keyword); + for (i=0; i < nopts; i++) + { + if (opts[i].long_opt && !strncmp (opts[i].long_opt, keyword, n)) + { + int j; + for (j=i+1; j < nopts; j++) + { + if (opts[j].long_opt + && !strncmp (opts[j].long_opt, keyword, n) + && !(opts[j].short_opt == opts[i].short_opt + && opts[j].flags == opts[i].flags ) ) + return -2; /* Abbreviation is ambiguous. */ + } + return i; + } + } + return -1; /* Not found. */ +} + + +/* The option parser for command line options. */ +static int +arg_parse (gnupg_argparse_t *arg, gnupg_opt_t *opts_orig, int no_init) +{ + int idx; + opttable_t *opts; + unsigned int nopts; + int argc; + char **argv; + char *s, *s2; + int i; + + if (no_init) + ; + else if (initialize (arg, opts_orig, NULL)) + return (arg->r_opt = ARGPARSE_OUT_OF_CORE); + + opts = arg->internal->opts; + nopts = arg->internal->nopts; + argc = *arg->argc; + argv = *arg->argv; + idx = arg->internal->idx; + + if (!idx && argc && !(arg->flags & ARGPARSE_FLAG_ARG0)) + { + /* Skip the first argument. */ + argc--; argv++; idx++; + } + + next_one: + if (!argc || (s = *argv) == NULL) + { + /* No more args. */ + arg->r_opt = 0; + goto leave; /* Ready. */ + } + + arg->internal->last = s; + arg->internal->opt_flags = 0; + + if (arg->internal->stopped && (arg->flags & ARGPARSE_FLAG_ALL)) + { + arg->r_opt = ARGPARSE_IS_ARG; /* Not an option but an argument. */ + arg->r_type = ARGPARSE_TYPE_STRING; + arg->r.ret_str = s; + argc--; argv++; idx++; /* set to next one */ + } + else if (arg->internal->stopped) + { + arg->r_opt = 0; + goto leave; /* Ready. */ + } + else if ( *s == '-' && s[1] == '-' ) + { + /* Long option. */ + char *argpos; + + arg->internal->inarg = 0; + if (!s[2] && !(arg->flags & ARGPARSE_FLAG_NOSTOP)) + { + /* Stop option processing. */ + arg->internal->stopped = 1; + arg->flags |= ARGPARSE_FLAG_STOP_SEEN; + argc--; argv++; idx++; + goto next_one; + } + + argpos = strchr( s+2, '=' ); + if ( argpos ) + *argpos = 0; + i = find_long_option (arg, s+2); + if ( argpos ) + *argpos = '='; + + if (i > 0 && opts[i].short_opt == ARGPARSE_SHORTOPT_HELP) + { + show_help (opts, nopts, arg->flags); + my_exit (arg, 0); + } + else if (i > 0 && opts[i].short_opt == ARGPARSE_SHORTOPT_VERSION) + { + if (!(arg->flags & ARGPARSE_FLAG_NOVERSION)) + { + show_version (); + my_exit (arg, 0); + } + } + else if (i > 0 && opts[i].short_opt == ARGPARSE_SHORTOPT_WARRANTY) + { + writestrings (0, strusage (16), "\n", NULL); + my_exit (arg, 0); + } + else if (i > 0 && opts[i].short_opt == ARGPARSE_SHORTOPT_DUMP_OPTTBL) + dump_option_table (arg); + else if (i > 0 && opts[i].short_opt == ARGPARSE_SHORTOPT_DUMP_OPTIONS) + { + for (i=0; i < nopts; i++ ) + { + if (opts[i].long_opt && !(opts[i].flags & ARGPARSE_OPT_IGNORE)) + writestrings (0, "--", opts[i].long_opt, "\n", NULL); + } + my_exit (arg, 0); + } + + if ( i == -2 ) + arg->r_opt = ARGPARSE_AMBIGUOUS_OPTION; + else if ( i == -1 ) + { + arg->r_opt = ARGPARSE_INVALID_OPTION; + arg->r.ret_str = s+2; + } + else + arg->r_opt = opts[i].short_opt; + + if ( i < 0 ) + ; + else if ( (opts[i].flags & ARGPARSE_TYPE_MASK) ) + { + if ( argpos ) + { + s2 = argpos+1; + if ( !*s2 ) + s2 = NULL; + } + else + s2 = argv[1]; + + if ( !s2 && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) ) + { + arg->r_type = ARGPARSE_TYPE_NONE; /* Argument is optional. */ + } + else if ( !s2 ) + { + arg->r_opt = ARGPARSE_MISSING_ARG; + } + else if ( !argpos && *s2 == '-' + && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) ) + { + /* The argument is optional and the next seems to be an + option. We do not check this possible option but + assume no argument */ + arg->r_type = ARGPARSE_TYPE_NONE; + } + else + { + set_opt_arg (arg, opts[i].flags, s2); + if ( !argpos ) + { + argc--; argv++; idx++; /* Skip one. */ + } + } + } + else + { + /* Does not take an argument. */ + if ( argpos ) + arg->r_type = ARGPARSE_UNEXPECTED_ARG; + else + { + arg->internal->opt_flags = opts[i].flags; + arg->r_type = ARGPARSE_TYPE_NONE; + } + } + argc--; argv++; idx++; /* Set to next one. */ + } + else if ( (*s == '-' && s[1]) || arg->internal->inarg ) + { + /* Short option. */ + int dash_kludge = 0; + + i = 0; + if ( !arg->internal->inarg ) + { + arg->internal->inarg++; + if ( (arg->flags & ARGPARSE_FLAG_ONEDASH) ) + { + for (i=0; i < nopts; i++ ) + if ( opts[i].long_opt && !strcmp (opts[i].long_opt, s+1)) + { + dash_kludge = 1; + break; + } + } + } + s += arg->internal->inarg; + + if (!dash_kludge ) + { + for (i=0; i < nopts; i++ ) + if ( opts[i].short_opt == *s ) + break; + } + + if ( !opts[i].short_opt && ( *s == 'h' || *s == '?' ) ) + { + show_help (opts, nopts, arg->flags); + my_exit (arg, 0); + } + + arg->r_opt = opts[i].short_opt; + if (!opts[i].short_opt ) + { + arg->r_opt = (opts[i].flags & ARGPARSE_OPT_COMMAND)? + ARGPARSE_INVALID_COMMAND:ARGPARSE_INVALID_OPTION; + arg->internal->inarg++; /* Point to the next arg. */ + arg->r.ret_str = s; + } + else if ( (opts[i].flags & ARGPARSE_TYPE_MASK) ) + { + if ( s[1] && !dash_kludge ) + { + s2 = s+1; + set_opt_arg (arg, opts[i].flags, s2); + } + else + { + s2 = argv[1]; + if ( !s2 && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) ) + { + arg->r_type = ARGPARSE_TYPE_NONE; + arg->internal->opt_flags = opts[i].flags; + } + else if ( !s2 ) + { + arg->r_opt = ARGPARSE_MISSING_ARG; + } + else if ( *s2 == '-' && s2[1] + && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) ) + { + /* The argument is optional and the next seems to + be an option. We do not check this possible + option but assume no argument. */ + arg->r_type = ARGPARSE_TYPE_NONE; + arg->internal->opt_flags = opts[i].flags; + } + else + { + set_opt_arg (arg, opts[i].flags, s2); + argc--; argv++; idx++; /* Skip one. */ + } + } + s = "x"; /* This is so that !s[1] yields false. */ + } + else + { + /* Does not take an argument. */ + arg->r_type = ARGPARSE_TYPE_NONE; + arg->internal->opt_flags = opts[i].flags; + arg->internal->inarg++; /* Point to the next arg. */ + } + if ( !s[1] || dash_kludge ) + { + /* No more concatenated short options. */ + arg->internal->inarg = 0; + argc--; argv++; idx++; + } + } + else if ( arg->flags & ARGPARSE_FLAG_MIXED ) + { + arg->r_opt = ARGPARSE_IS_ARG; + arg->r_type = ARGPARSE_TYPE_STRING; + arg->r.ret_str = s; + argc--; argv++; idx++; /* Set to next one. */ + } + else + { + arg->internal->stopped = 1; /* Stop option processing. */ + goto next_one; + } + + if (arg->r_opt > 0 && i >= 0 && i < nopts + && ((opts[i].ignore && opts[i].explicit_ignore) || opts[i].forced)) + { + + if ((arg->flags & ARGPARSE_FLAG_WITHATTR)) + { + if (opts[i].ignore) + arg->r_type |= ARGPARSE_ATTR_IGNORE; + if (opts[i].forced) + arg->r_type |= ARGPARSE_ATTR_FORCE; + arg->r_type |= ARGPARSE_OPT_IGNORE; + } + else + { + log_info (_("Note: ignoring option \"--%s\"" + " due to global config\n"), + opts[i].long_opt); + goto next_one; /* Skip ignored/forced option. */ + } + } + + leave: + *arg->argc = argc; + *arg->argv = argv; + arg->internal->idx = idx; + return arg->r_opt; +} + + + +/* Returns: -1 on error, 0 for an integer type and 1 for a non integer + type argument. */ +static int +set_opt_arg (gnupg_argparse_t *arg, unsigned flags, char *s) +{ + int base = (flags & ARGPARSE_OPT_PREFIX)? 0 : 10; + long l; + + arg->internal->opt_flags = flags; + switch ( (arg->r_type = (flags & ARGPARSE_TYPE_MASK)) ) + { + case ARGPARSE_TYPE_LONG: + case ARGPARSE_TYPE_INT: + errno = 0; + l = strtol (s, NULL, base); + if ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE) + { + arg->r_opt = ARGPARSE_INVALID_ARG; + return -1; + } + if (arg->r_type == ARGPARSE_TYPE_LONG) + arg->r.ret_long = l; + else if ( (l < 0 && l < INT_MIN) || l > INT_MAX ) + { + arg->r_opt = ARGPARSE_INVALID_ARG; + return -1; + } + else + arg->r.ret_int = (int)l; + return 0; + + case ARGPARSE_TYPE_ULONG: + while (isascii (*s) && isspace(*s)) + s++; + if (*s == '-') + { + arg->r.ret_ulong = 0; + arg->r_opt = ARGPARSE_INVALID_ARG; + return -1; + } + errno = 0; + arg->r.ret_ulong = strtoul (s, NULL, base); + if (arg->r.ret_ulong == ULONG_MAX && errno == ERANGE) + { + arg->r_opt = ARGPARSE_INVALID_ARG; + return -1; + } + return 0; + + case ARGPARSE_TYPE_STRING: + default: + arg->r.ret_str = s; + return 1; + } +} + + +/* Return the length of the option O. This needs to consider the + * description as well as the option name. */ +static size_t +long_opt_strlen (opttable_t *o) +{ + size_t n = strlen (o->long_opt); + + if ( o->description && *o->description == '|' ) + { + const char *s; + int is_utf8 = is_native_utf8 (); + + s=o->description+1; + if ( *s != '=' ) + n++; + /* For a (mostly) correct length calculation we exclude + * continuation bytes (10xxxxxx) if we are on a native utf8 + * terminal. */ + for (; *s && *s != '|'; s++ ) + if ( is_utf8 && (*s&0xc0) != 0x80 ) + n++; + } + return n; +} + + +/* Qsort compare for show_help. */ +static int +cmp_ordtbl (const void *a_v, const void *b_v) +{ + const unsigned short *a = a_v; + const unsigned short *b = b_v; + + return *a - *b; +} + + +/**************** + * Print formatted help. The description string has some special + * meanings: + * - A description string which is "@" suppresses help output for + * this option + * - a description which starts with a '@' and is followed by + * any other characters is printed as is; this may be used for examples + * and such. This is a legacy methiod, moder codes uses the flags + * ARGPARSE_OPT_VERBATIM or ARGPARSE_OPT_HEADER. + * - A description which starts with a '|' outputs the string between this + * bar and the next one as arguments of the long option. + */ +static void +show_help (opttable_t *opts, unsigned int nopts, unsigned int flags) +{ + const char *s; + char tmp[2]; + unsigned int *ordtbl = NULL; + + show_version (); + writestrings (0, "\n", NULL); + s = strusage (42); + if (s && *s == '1') + { + s = strusage (40); + writestrings (1, s, NULL); + if (*s && s[strlen(s)] != '\n') + writestrings (1, "\n", NULL); + } + s = strusage(41); + writestrings (0, s, "\n", NULL); + if ( nopts ) + { + /* Auto format the option description. */ + int i,j,indent; + const char *last_header = NULL; + + ordtbl = xtrycalloc (nopts, sizeof *ordtbl); + if (!ordtbl) + { + writestrings (1, "\nOoops: Out of memory whilst printing the help.\n", + NULL); + goto leave; + } + + /* Get max. length of long options. */ + for (i=indent=0; i < nopts; i++ ) + { + if ( opts[i].long_opt ) + if ( !opts[i].description || *opts[i].description != '@' ) + if ( (j=long_opt_strlen(opts+i)) > indent && j < 35 ) + indent = j; + ordtbl[i] = opts[i].ordinal; + } + + qsort (ordtbl, nopts, sizeof *ordtbl, cmp_ordtbl); + + /* The first option needs to have a description; if not do not + * print the help at all. */ + if (!opts[ordtbl[0]].description) + goto leave; + + /* Example: " -v, --verbose Viele Sachen ausgeben" */ + indent += 10; + if ( *opts[ordtbl[0]].description != '@' + && !(opts[ordtbl[0]].flags + & (ARGPARSE_OPT_VERBATIM|ARGPARSE_OPT_HEADER))) + writestrings (0, "Options:", "\n", NULL); + for (i=0; i < nopts; i++ ) + { + s = map_fixed_string (_( opts[ordtbl[i]].description )); + if ( s && *s== '@' && !s[1] ) /* Hide this line. */ + continue; + if ( s && (opts[ordtbl[i]].flags & ARGPARSE_OPT_HEADER)) + { + /* We delay printing until we have found one real output + * line. This avoids having a header above an empty + * section. */ + last_header = s; + continue; + } + if (last_header) + { + if (*last_header) + writestrings (0, "\n", last_header, ":\n", NULL); + last_header = NULL; + } + if ( s && (opts[ordtbl[i]].flags & ARGPARSE_OPT_VERBATIM)) + { + writestrings (0, s, NULL); + continue; + } + if ( s && *s == '@' ) /* Unindented legacy comment only line. */ + { + for (s++; *s; s++ ) + { + if ( *s == '\n' ) + { + if( s[1] ) + writestrings (0, "\n", NULL); + } + else + { + tmp[0] = *s; + tmp[1] = 0; + writestrings (0, tmp, NULL); + } + } + writestrings (0, "\n", NULL); + continue; + } + + j = 3; + if ( opts[ordtbl[i]].short_opt < 256 ) + { + tmp[0] = opts[ordtbl[i]].short_opt; + tmp[1] = 0; + writestrings (0, " -", tmp, NULL ); + if ( !opts[ordtbl[i]].long_opt ) + { + if (s && *s == '|' ) + { + writestrings (0, " ", NULL); j++; + for (s++ ; *s && *s != '|'; s++, j++ ) + { + tmp[0] = *s; + tmp[1] = 0; + writestrings (0, tmp, NULL); + } + if ( *s ) + s++; + } + } + } + else + writestrings (0, " ", NULL); + if ( opts[ordtbl[i]].long_opt ) + { + tmp[0] = opts[ordtbl[i]].short_opt < 256?',':' '; + tmp[1] = 0; + j += writestrings (0, tmp, " --", opts[ordtbl[i]].long_opt, NULL); + if (s && *s == '|' ) + { + if ( *++s != '=' ) + { + writestrings (0, " ", NULL); + j++; + } + for ( ; *s && *s != '|'; s++, j++ ) + { + tmp[0] = *s; + tmp[1] = 0; + writestrings (0, tmp, NULL); + } + if ( *s ) + s++; + } + writestrings (0, " ", NULL); + j += 3; + } + for (;j < indent; j++ ) + writestrings (0, " ", NULL); + if ( s ) + { + if ( *s && j > indent ) + { + writestrings (0, "\n", NULL); + for (j=0;j < indent; j++ ) + writestrings (0, " ", NULL); + } + for (; *s; s++ ) + { + if ( *s == '\n' ) + { + if ( s[1] ) + { + writestrings (0, "\n", NULL); + for (j=0; j < indent; j++ ) + writestrings (0, " ", NULL); + } + } + else + { + tmp[0] = *s; + tmp[1] = 0; + writestrings (0, tmp, NULL); + } + } + } + writestrings (0, "\n", NULL); + } + if ( (flags & ARGPARSE_FLAG_ONEDASH) ) + writestrings (0, "\n(A single dash may be used " + "instead of the double ones)\n", NULL); + } + if ( (s=strusage(19)) ) + { + writestrings (0, "\n", NULL); + writestrings (0, s, NULL); + } + + leave: + flushstrings (0); + xfree (ordtbl); +} + + +static void +show_version () +{ + const char *s; + int i; + + /* Version line. */ + writestrings (0, strusage (11), NULL); + if ((s=strusage (12))) + writestrings (0, " (", s, ")", NULL); + writestrings (0, " ", strusage (13), "\n", NULL); + /* Additional version lines. */ + for (i=20; i < 30; i++) + if ((s=strusage (i))) + writestrings (0, s, "\n", NULL); + /* Copyright string. */ + if ((s=strusage (14))) + writestrings (0, s, "\n", NULL); + /* Licence string. */ + if( (s=strusage (10)) ) + writestrings (0, s, "\n", NULL); + /* Copying conditions. */ + if ( (s=strusage(15)) ) + writestrings (0, s, NULL); + /* Thanks. */ + if ((s=strusage(18))) + writestrings (0, s, NULL); + /* Additional program info. */ + for (i=30; i < 40; i++ ) + if ( (s=strusage (i)) ) + writestrings (0, s, NULL); + flushstrings (0); +} + + +/* Print the table of options with flags etc. */ +static void +dump_option_table (gnupg_argparse_t *arg) +{ + opttable_t *opts; + unsigned int nopts; + const char *s; + char tmp[50]; + unsigned int *ordtbl = NULL; + int i; + + opts = arg->internal->opts; + nopts = arg->internal->nopts; + if (!nopts) + return; + + ordtbl = xtrycalloc (nopts, sizeof *ordtbl); + if (!ordtbl) + { + writestrings (1, "\nOoops: Out of memory whilst dumping the table.\n", + NULL); + flushstrings (1); + my_exit (arg, 2); + } + for (i=0; i < nopts; i++ ) + ordtbl[i] = opts[i].ordinal; + qsort (ordtbl, nopts, sizeof *ordtbl, cmp_ordtbl); + for (i=0; i < nopts; i++ ) + { + if (!opts[ordtbl[i]].long_opt) + continue; + writestrings (0, opts[ordtbl[i]].long_opt, ":", NULL); + snprintf (tmp, sizeof tmp, "%u:%u:", + opts[ordtbl[i]].short_opt, + opts[ordtbl[i]].flags); + writestrings (0, tmp, NULL); + s = opts[ordtbl[i]].description; + if (s) + { + for (; *s; s++) + { + if (*s == '%' || *s == ':' || *s == '\n') + snprintf (tmp, sizeof tmp, "%%%02X", *s); + else + { + tmp[0] = *s; + tmp[1] = 0; + } + writestrings (0, tmp, NULL); + } + } + writestrings (0, ":\n", NULL); + } + + flushstrings (0); + xfree (ordtbl); + my_exit (arg, 0); +} + + + +/* Level + * 0: Print copyright string to stderr + * 1: Print a short usage hint to stderr and terminate + * 2: Print a long usage hint to stdout and terminate + * 8: Return NULL for UTF-8 or string with the native charset. + * 9: Return the SPDX License tag. + * 10: Return license info string + * 11: Return the name of the program + * 12: Return optional name of package which includes this program. + * 13: version string + * 14: copyright string + * 15: Short copying conditions (with LFs) + * 16: Long copying conditions (with LFs) + * 17: Optional printable OS name + * 18: Optional thanks list (with LFs) + * 19: Bug report info + *20..29: Additional lib version strings. + *30..39: Additional program info (with LFs) + * 40: short usage note (with LF) + * 41: long usage note (with LF) + * 42: Flag string: + * First char is '1': + * The short usage notes needs to be printed + * before the long usage note. + */ +const char * +strusage( int level ) +{ + const char *p = strusage_handler? strusage_handler(level) : NULL; + const char *tmp; + + if ( p ) + return map_static_macro_string (p); + + switch ( level ) + { + + case 8: break; /* Default to utf-8. */ + case 9: p = "GPL-3.0-or-later"; break; + case 10: + tmp = strusage (9); + if (tmp && !strcmp (tmp, "LGPL-2.1-or-later")) + p = ("License GNU LGPL-2.1-or-later <https://gnu.org/licenses/>"); + else /* Default to GPLv3+. */ + p =("License GNU GPL-3.0-or-later <https://gnu.org/licenses/gpl.html>"); + break; + case 11: p = "foo"; break; + case 13: p = "0.0"; break; + case 14: p = GNUPG_DEF_COPYRIGHT_LINE; break; + case 15: p = +"This is free software: you are free to change and redistribute it.\n" +"There is NO WARRANTY, to the extent permitted by law.\n"; + break; + case 16: + tmp = strusage (9); + if (tmp && !strcmp (tmp, "LGPL-2.1-or-later")) + p = +"This is free software; you can redistribute it and/or modify\n" +"it under the terms of the GNU Lesser General Public License as\n" +"published by the Free Software Foundation; either version 2.1 of\n" +"the License, or (at your option) any later version.\n\n" +"It is distributed in the hope that it will be useful,\n" +"but WITHOUT ANY WARRANTY; without even the implied warranty of\n" +"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" +"GNU Lesser General Public License for more details.\n\n" +"You should have received a copy of the GNU Lesser General Public License\n" +"along with this software. If not, see <https://gnu.org/licenses/>.\n"; + else /* Default */ + p = +"This is free software; you can redistribute it and/or modify\n" +"it under the terms of the GNU General Public License as published by\n" +"the Free Software Foundation; either version 3 of the License, or\n" +"(at your option) any later version.\n\n" +"It is distributed in the hope that it will be useful,\n" +"but WITHOUT ANY WARRANTY; without even the implied warranty of\n" +"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" +"GNU General Public License for more details.\n\n" +"You should have received a copy of the GNU General Public License\n" +"along with this software. If not, see <https://gnu.org/licenses/>.\n"; + break; + case 40: /* short and long usage */ + case 41: p = ""; break; + } + + return p; +} + + +/* Set the usage handler. This function is basically a constructor. */ +void +set_strusage ( const char *(*f)( int ) ) +{ + strusage_handler = f; +} + +#endif /* USE_INTERNAL_ARGPARSE */ + + +void +usage (int level) +{ + const char *p; + + if (!level) + { + writestrings (1, strusage(11), " ", strusage(13), "; ", + strusage (14), "\n", NULL); + flushstrings (1); + } + else if (level == 1) + { + p = strusage (40); + writestrings (1, p, NULL); + if (*p && p[strlen(p)] != '\n') + writestrings (1, "\n", NULL); + exit (2); + } + else if (level == 2) + { + p = strusage (42); + if (p && *p == '1') + { + p = strusage (40); + writestrings (1, p, NULL); + if (*p && p[strlen(p)] != '\n') + writestrings (1, "\n", NULL); + } + writestrings (0, strusage(41), "\n", NULL); + exit (0); + } +} diff --git a/common/argparse.h b/common/argparse.h new file mode 100644 index 0000000..282aaea --- /dev/null +++ b/common/argparse.h @@ -0,0 +1,281 @@ +/* argparse.h - Argument parser for option handling. + * Copyright (C) 1998,1999,2000,2001,2006 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute and/or modify this + * part of GnuPG under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * GnuPG is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copies of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef GNUPG_COMMON_ARGPARSE_H +#define GNUPG_COMMON_ARGPARSE_H + +#include <stdio.h> +#include <gpg-error.h> + +#if GPGRT_VERSION_NUMBER < 0x012600 /* 1.38 */ + +#define USE_INTERNAL_ARGPARSE 1 + +/* We use a copy of the code from the new gpgrt parser. */ + +struct _argparse_internal_s; +typedef struct +{ + int *argc; /* Pointer to ARGC (value subject to change). */ + char ***argv; /* Pointer to ARGV (value subject to change). */ + unsigned int flags; /* Global flags. May be set prior to calling the + parser. The parser may change the value. */ + int err; /* Print error description for last option. + Either 0, ARGPARSE_PRINT_WARNING or + ARGPARSE_PRINT_ERROR. */ + unsigned int lineno;/* The current line number. */ + int r_opt; /* Returns option code. */ + int r_type; /* Returns type of option value. */ + union { + int ret_int; + long ret_long; + unsigned long ret_ulong; + char *ret_str; + } r; /* Return values */ + + struct _argparse_internal_s *internal; +} gnupg_argparse_t; + + +typedef struct +{ + int short_opt; + const char *long_opt; + unsigned int flags; + const char *description; /* Optional option description. */ +} gnupg_opt_t; + + +typedef gnupg_argparse_t ARGPARSE_ARGS; +typedef gnupg_opt_t ARGPARSE_OPTS; + +/* Short options. */ +#define ARGPARSE_SHORTOPT_HELP 32768 +#define ARGPARSE_SHORTOPT_VERSION 32769 +#define ARGPARSE_SHORTOPT_WARRANTY 32770 +#define ARGPARSE_SHORTOPT_DUMP_OPTIONS 32771 + + +/* Global flags (ARGPARSE_ARGS). */ +#define ARGPARSE_FLAG_KEEP 1 /* Do not remove options form argv. */ +#define ARGPARSE_FLAG_ALL 2 /* Do not stop at last option but return + remaining args with R_OPT set to -1. */ +#define ARGPARSE_FLAG_MIXED 4 /* Assume options and args are mixed. */ +#define ARGPARSE_FLAG_NOSTOP 8 /* Do not stop processing at "--". */ +#define ARGPARSE_FLAG_ARG0 16 /* Do not skip the first arg. */ +#define ARGPARSE_FLAG_ONEDASH 32 /* Allow long options with one dash. */ +#define ARGPARSE_FLAG_NOVERSION 64 /* No output for "--version". */ +#define ARGPARSE_FLAG_RESET 128 /* Request to reset the internal state. */ +#define ARGPARSE_FLAG_STOP_SEEN 256 /* Set to true if a "--" has been seen. */ +#define ARGPARSE_FLAG_NOLINENO 512 /* Do not zero the lineno field. */ +#define ARGPARSE_FLAG_SYS 1024 /* Use system config file. */ +#define ARGPARSE_FLAG_USER 2048 /* Use user config file. */ +#define ARGPARSE_FLAG_VERBOSE 4096 /* Print additional argparser info. */ +#define ARGPARSE_FLAG_USERVERS 8192 /* Try version-ed user config files. */ +#define ARGPARSE_FLAG_WITHATTR 16384 /* Return attribute bits. */ + +/* Flags for each option (ARGPARSE_OPTS). The type code may be + ORed with the OPT flags. */ +#define ARGPARSE_TYPE_NONE 0 /* Does not take an argument. */ +#define ARGPARSE_TYPE_INT 1 /* Takes an int argument. */ +#define ARGPARSE_TYPE_STRING 2 /* Takes a string argument. */ +#define ARGPARSE_TYPE_LONG 3 /* Takes a long argument. */ +#define ARGPARSE_TYPE_ULONG 4 /* Takes an unsigned long argument. */ +#define ARGPARSE_OPT_OPTIONAL (1<<3) /* Argument is optional. */ +#define ARGPARSE_OPT_PREFIX (1<<4) /* Allow 0x etc. prefixed values. */ +#define ARGPARSE_OPT_IGNORE (1<<6) /* Ignore command or option. */ +#define ARGPARSE_OPT_COMMAND (1<<7) /* The argument is a command. */ +#define ARGPARSE_OPT_CONFFILE (1<<8) /* The value is a conffile. */ +#define ARGPARSE_OPT_HEADER (1<<9) /* The value is printed as a header. */ +#define ARGPARSE_OPT_VERBATIM (1<<10)/* The value is printed verbatim. */ +#define ARGPARSE_ATTR_FORCE (1<<14)/* Attribute force is set. */ +#define ARGPARSE_ATTR_IGNORE (1<<15)/* Attribute ignore is set. */ + +#define ARGPARSE_TYPE_MASK 7 /* Mask for the type values (internal). */ + +/* A set of macros to make option definitions easier to read. */ +#define ARGPARSE_x(s,l,t,f,d) \ + { (s), (l), ARGPARSE_TYPE_ ## t | (f), (d) } + +#define ARGPARSE_s(s,l,t,d) \ + { (s), (l), ARGPARSE_TYPE_ ## t, (d) } +#define ARGPARSE_s_n(s,l,d) \ + { (s), (l), ARGPARSE_TYPE_NONE, (d) } +#define ARGPARSE_s_i(s,l,d) \ + { (s), (l), ARGPARSE_TYPE_INT, (d) } +#define ARGPARSE_s_s(s,l,d) \ + { (s), (l), ARGPARSE_TYPE_STRING, (d) } +#define ARGPARSE_s_l(s,l,d) \ + { (s), (l), ARGPARSE_TYPE_LONG, (d) } +#define ARGPARSE_s_u(s,l,d) \ + { (s), (l), ARGPARSE_TYPE_ULONG, (d) } + +#define ARGPARSE_o(s,l,t,d) \ + { (s), (l), (ARGPARSE_TYPE_ ## t | ARGPARSE_OPT_OPTIONAL), (d) } +#define ARGPARSE_o_n(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_NONE | ARGPARSE_OPT_OPTIONAL), (d) } +#define ARGPARSE_o_i(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_INT | ARGPARSE_OPT_OPTIONAL), (d) } +#define ARGPARSE_o_s(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_STRING | ARGPARSE_OPT_OPTIONAL), (d) } +#define ARGPARSE_o_l(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_LONG | ARGPARSE_OPT_OPTIONAL), (d) } +#define ARGPARSE_o_u(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_ULONG | ARGPARSE_OPT_OPTIONAL), (d) } + +#define ARGPARSE_p(s,l,t,d) \ + { (s), (l), (ARGPARSE_TYPE_ ## t | ARGPARSE_OPT_PREFIX), (d) } +#define ARGPARSE_p_n(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_NONE | ARGPARSE_OPT_PREFIX), (d) } +#define ARGPARSE_p_i(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_INT | ARGPARSE_OPT_PREFIX), (d) } +#define ARGPARSE_p_s(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_STRING | ARGPARSE_OPT_PREFIX), (d) } +#define ARGPARSE_p_l(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_LONG | ARGPARSE_OPT_PREFIX), (d) } +#define ARGPARSE_p_u(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_ULONG | ARGPARSE_OPT_PREFIX), (d) } + +#define ARGPARSE_op(s,l,t,d) \ + { (s), (l), (ARGPARSE_TYPE_ ## t \ + | ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) } +#define ARGPARSE_op_n(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_NONE \ + | ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) } +#define ARGPARSE_op_i(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_INT \ + | ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) } +#define ARGPARSE_op_s(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_STRING \ + | ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) } +#define ARGPARSE_op_l(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_LONG \ + | ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) } +#define ARGPARSE_op_u(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_ULONG \ + | ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) } + +#define ARGPARSE_c(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_NONE | ARGPARSE_OPT_COMMAND), (d) } + +#define ARGPARSE_conffile(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_STRING|ARGPARSE_OPT_CONFFILE), (d) } + +#define ARGPARSE_noconffile(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_NONE|ARGPARSE_OPT_CONFFILE), (d) } + +#define ARGPARSE_ignore(s,l) \ + { (s), (l), (ARGPARSE_OPT_IGNORE), "@" } + +#define ARGPARSE_group(s,d) \ + { (s), NULL, 0, (d) } + +/* Verbatim print the string D in the help output. It does not make + * use of the "@" hack as ARGPARSE_group does. */ +#define ARGPARSE_verbatim(d) \ + { 1, NULL, (ARGPARSE_OPT_VERBATIM), (d) } + +/* Same as ARGPARSE_verbatim but also print a colon and a LF. N can + * be used give a symbolic name to the header. Nothing is printed if + * D is the empty string. */ +#define ARGPARSE_header(n,d) \ + { 1, (n), (ARGPARSE_OPT_HEADER), (d) } + +/* Mark the end of the list (mandatory). */ +#define ARGPARSE_end() \ + { 0, NULL, 0, NULL } + + +/* Other constants. */ +#define ARGPARSE_PRINT_WARNING 1 +#define ARGPARSE_PRINT_ERROR 2 + + +/* Error values. */ +#define ARGPARSE_IS_ARG (-1) +#define ARGPARSE_INVALID_OPTION (-2) +#define ARGPARSE_MISSING_ARG (-3) +#define ARGPARSE_KEYWORD_TOO_LONG (-4) +#define ARGPARSE_READ_ERROR (-5) +#define ARGPARSE_UNEXPECTED_ARG (-6) +#define ARGPARSE_INVALID_COMMAND (-7) +#define ARGPARSE_AMBIGUOUS_OPTION (-8) +#define ARGPARSE_AMBIGUOUS_COMMAND (-9) +#define ARGPARSE_INVALID_ALIAS (-10) +#define ARGPARSE_OUT_OF_CORE (-11) +#define ARGPARSE_INVALID_ARG (-12) +#define ARGPARSE_PERMISSION_ERROR (-13) +#define ARGPARSE_NO_CONFFILE (-14) +#define ARGPARSE_CONFFILE (-15) +#define ARGPARSE_INVALID_META (-16) +#define ARGPARSE_UNKNOWN_META (-17) +#define ARGPARSE_UNEXPECTED_META (-18) + +/* Values used for gnupg_set_confdir. */ +#define GNUPG_CONFDIR_USER 1 /* The user's configuration dir. */ +#define GNUPG_CONFDIR_SYS 2 /* The systems's configuration dir. */ + +/* Take care: gpgrt_argparse keeps state in ARG and requires that + * either ARGPARSE_FLAG_RESET is used after OPTS has been changed or + * gpgrt_argparse (NULL, ARG, NULL) is called first. */ +int gnupg_argparse (gpgrt_stream_t fp, + gnupg_argparse_t *arg, gnupg_opt_t *opts); +int gnupg_argparser (gnupg_argparse_t *arg, gnupg_opt_t *opts, + const char *confname); + +const char *strusage (int level); +void set_strusage (const char *(*f)( int )); +void gnupg_set_usage_outfnc (int (*f)(int, const char *)); +void gnupg_set_fixed_string_mapper (const char *(*f)(const char*)); +void gnupg_set_confdir (int what, const char *name); + +#else /* !USE_INTERNAL_ARGPARSE */ + +#define GNUPG_CONFDIR_USER GPGRT_CONFDIR_USER +#define GNUPG_CONFDIR_SYS GPGRT_CONFDIR_SYS + +typedef gpgrt_argparse_t gnupg_argparse_t; +typedef gpgrt_opt_t gnupg_opt_t; +typedef gpgrt_argparse_t ARGPARSE_ARGS; +typedef gpgrt_opt_t ARGPARSE_OPTS; + +#define gnupg_argparse(a,b,c) gpgrt_argparse ((a),(b),(c)) +#define gnupg_argparser(a,b,c) gpgrt_argparser ((a),(b),(c)) +#define strusage(a) gpgrt_strusage (a) +#define set_strusage(a) gpgrt_set_strusage (a) +#define gnupg_set_usage_outfnc(a) gpgrt_set_usage_outfnc ((a)) +#define gnupg_set_fixed_string_mapper(a) gpgrt_set_fixed_string_mapper ((a)) +#define gnupg_set_confdir(a,b) gpgrt_set_confdir ((a),(b)) + +#endif /* !USE_INTERNAL_ARGPARSE */ + +void usage (int level); + +#endif /*GNUPG_COMMON_ARGPARSE_H*/ diff --git a/common/asshelp.c b/common/asshelp.c new file mode 100644 index 0000000..d87017e --- /dev/null +++ b/common/asshelp.c @@ -0,0 +1,685 @@ +/* asshelp.c - Helper functions for Assuan + * Copyright (C) 2002, 2004, 2007, 2009, 2010 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#ifdef HAVE_LOCALE_H +#include <locale.h> +#endif + +#include "i18n.h" +#include "util.h" +#include "exechelp.h" +#include "sysutils.h" +#include "status.h" +#include "membuf.h" +#include "asshelp.h" + +/* The type we use for lock_agent_spawning. */ +#ifdef HAVE_W32_SYSTEM +# define lock_spawn_t HANDLE +#else +# define lock_spawn_t dotlock_t +#endif + +/* The time we wait until the agent or the dirmngr are ready for + operation after we started them before giving up. */ +#ifdef HAVE_W32CE_SYSTEM +# define SECS_TO_WAIT_FOR_AGENT 30 +# define SECS_TO_WAIT_FOR_DIRMNGR 30 +#else +# define SECS_TO_WAIT_FOR_AGENT 5 +# define SECS_TO_WAIT_FOR_DIRMNGR 5 +#endif + +/* A bitfield that specifies the assuan categories to log. This is + identical to the default log handler of libassuan. We need to do + it ourselves because we use a custom log handler and want to use + the same assuan variables to select the categories to log. */ +static int log_cats; +#define TEST_LOG_CAT(x) (!! (log_cats & (1 << (x - 1)))) + +/* The assuan log monitor used to temporary inhibit log messages from + * assuan. */ +static int (*my_log_monitor) (assuan_context_t ctx, + unsigned int cat, + const char *msg); + + +static int +my_libassuan_log_handler (assuan_context_t ctx, void *hook, + unsigned int cat, const char *msg) +{ + unsigned int dbgval; + + if (! TEST_LOG_CAT (cat)) + return 0; + + dbgval = hook? *(unsigned int*)hook : 0; + if (!(dbgval & 1024)) + return 0; /* Assuan debugging is not enabled. */ + + if (ctx && my_log_monitor && !my_log_monitor (ctx, cat, msg)) + return 0; /* Temporary disabled. */ + + if (msg) + log_string (GPGRT_LOG_DEBUG, msg); + + return 1; +} + + +/* Setup libassuan to use our own logging functions. Should be used + early at startup. */ +void +setup_libassuan_logging (unsigned int *debug_var_address, + int (*log_monitor)(assuan_context_t ctx, + unsigned int cat, + const char *msg)) +{ + char *flagstr; + + flagstr = getenv ("ASSUAN_DEBUG"); + if (flagstr) + log_cats = atoi (flagstr); + else /* Default to log the control channel. */ + log_cats = (1 << (ASSUAN_LOG_CONTROL - 1)); + my_log_monitor = log_monitor; + assuan_set_log_cb (my_libassuan_log_handler, debug_var_address); +} + + +/* Change the Libassuan log categories to those given by NEWCATS. + NEWCATS is 0 the default category of ASSUAN_LOG_CONTROL is + selected. Note, that setup_libassuan_logging overrides the values + given here. */ +void +set_libassuan_log_cats (unsigned int newcats) +{ + if (newcats) + log_cats = newcats; + else /* Default to log the control channel. */ + log_cats = (1 << (ASSUAN_LOG_CONTROL - 1)); +} + + + +static gpg_error_t +send_one_option (assuan_context_t ctx, gpg_err_source_t errsource, + const char *name, const char *value, int use_putenv) +{ + gpg_error_t err; + char *optstr; + + (void)errsource; + + if (!value || !*value) + err = 0; /* Avoid sending empty strings. */ + else if (asprintf (&optstr, "OPTION %s%s=%s", + use_putenv? "putenv=":"", name, value) < 0) + err = gpg_error_from_syserror (); + else + { + err = assuan_transact (ctx, optstr, NULL, NULL, NULL, NULL, NULL, NULL); + xfree (optstr); + } + + return err; +} + + +/* Send the assuan commands pertaining to the pinentry environment. The + OPT_* arguments are optional and may be used to override the + defaults taken from the current locale. */ +gpg_error_t +send_pinentry_environment (assuan_context_t ctx, + gpg_err_source_t errsource, + const char *opt_lc_ctype, + const char *opt_lc_messages, + session_env_t session_env) + +{ + gpg_error_t err = 0; +#if defined(HAVE_SETLOCALE) + char *old_lc = NULL; +#endif + char *dft_lc = NULL; + const char *dft_ttyname; + int iterator; + const char *name, *assname, *value; + int is_default; + + iterator = 0; + while ((name = session_env_list_stdenvnames (&iterator, &assname))) + { + value = session_env_getenv_or_default (session_env, name, NULL); + if (!value) + continue; + + if (assname) + err = send_one_option (ctx, errsource, assname, value, 0); + else + { + err = send_one_option (ctx, errsource, name, value, 1); + if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION) + err = 0; /* Server too old; can't pass the new envvars. */ + } + if (err) + return err; + } + + + dft_ttyname = session_env_getenv_or_default (session_env, "GPG_TTY", + &is_default); + if (dft_ttyname && !is_default) + dft_ttyname = NULL; /* We need the default value. */ + + /* Send the value for LC_CTYPE. */ +#if defined(HAVE_SETLOCALE) && defined(LC_CTYPE) + old_lc = setlocale (LC_CTYPE, NULL); + if (old_lc) + { + old_lc = xtrystrdup (old_lc); + if (!old_lc) + return gpg_error_from_syserror (); + } + dft_lc = setlocale (LC_CTYPE, ""); +#endif + if (opt_lc_ctype || (dft_ttyname && dft_lc)) + { + err = send_one_option (ctx, errsource, "lc-ctype", + opt_lc_ctype ? opt_lc_ctype : dft_lc, 0); + } +#if defined(HAVE_SETLOCALE) && defined(LC_CTYPE) + if (old_lc) + { + setlocale (LC_CTYPE, old_lc); + xfree (old_lc); + } +#endif + if (err) + return err; + + /* Send the value for LC_MESSAGES. */ +#if defined(HAVE_SETLOCALE) && defined(LC_MESSAGES) + old_lc = setlocale (LC_MESSAGES, NULL); + if (old_lc) + { + old_lc = xtrystrdup (old_lc); + if (!old_lc) + return gpg_error_from_syserror (); + } + dft_lc = setlocale (LC_MESSAGES, ""); +#endif + if (opt_lc_messages || (dft_ttyname && dft_lc)) + { + err = send_one_option (ctx, errsource, "lc-messages", + opt_lc_messages ? opt_lc_messages : dft_lc, 0); + } +#if defined(HAVE_SETLOCALE) && defined(LC_MESSAGES) + if (old_lc) + { + setlocale (LC_MESSAGES, old_lc); + xfree (old_lc); + } +#endif + if (err) + return err; + + return 0; +} + + +/* Lock a spawning process. The caller needs to provide the address + of a variable to store the lock information and the name or the + process. */ +static gpg_error_t +lock_spawning (lock_spawn_t *lock, const char *homedir, const char *name, + int verbose) +{ + char *fname; + (void)verbose; + + *lock = NULL; + + fname = make_absfilename_try + (homedir, + !strcmp (name, "agent")? "gnupg_spawn_agent_sentinel": + !strcmp (name, "dirmngr")? "gnupg_spawn_dirmngr_sentinel": + /* */ "gnupg_spawn_unknown_sentinel", + NULL); + if (!fname) + return gpg_error_from_syserror (); + + *lock = dotlock_create (fname, 0); + xfree (fname); + if (!*lock) + return gpg_error_from_syserror (); + + /* FIXME: We should use a timeout of 5000 here - however + make_dotlock does not yet support values other than -1 and 0. */ + if (dotlock_take (*lock, -1)) + return gpg_error_from_syserror (); + + return 0; +} + + +/* Unlock the spawning process. */ +static void +unlock_spawning (lock_spawn_t *lock, const char *name) +{ + if (*lock) + { + (void)name; + dotlock_destroy (*lock); + *lock = NULL; + } +} + +static gpg_error_t +wait_for_sock (int secs, const char *name, const char *sockname, int verbose, assuan_context_t ctx, int *did_success_msg) +{ + gpg_error_t err = 0; + int target_us = secs * 1000000; + int elapsed_us = 0; + /* + * 977us * 1024 = just a little more than 1s. + * so we will double this timeout 10 times in the first + * second, and then switch over to 1s checkins. + */ + int next_sleep_us = 977; + int lastalert = secs+1; + int secsleft; + + while (elapsed_us < target_us) + { + if (verbose) + { + secsleft = (target_us - elapsed_us + 999999)/1000000; + /* log_clock ("left=%d last=%d targ=%d elap=%d next=%d\n", */ + /* secsleft, lastalert, target_us, elapsed_us, */ + /* next_sleep_us); */ + if (secsleft < lastalert) + { + log_info (_("waiting for the %s to come up ... (%ds)\n"), + name, secsleft); + lastalert = secsleft; + } + } + gnupg_usleep (next_sleep_us); + elapsed_us += next_sleep_us; + err = assuan_socket_connect (ctx, sockname, 0, 0); + if (!err) + { + if (verbose) + { + log_info (_("connection to %s established\n"), + name); + *did_success_msg = 1; + } + break; + } + next_sleep_us *= 2; + if (next_sleep_us > 1000000) + next_sleep_us = 1000000; + } + return err; +} + +/* Try to connect to the agent via socket or start it if it is not + running and AUTOSTART is set. Handle the server's initial + greeting. Returns a new assuan context at R_CTX or an error + code. */ +gpg_error_t +start_new_gpg_agent (assuan_context_t *r_ctx, + gpg_err_source_t errsource, + const char *agent_program, + const char *opt_lc_ctype, + const char *opt_lc_messages, + session_env_t session_env, + int autostart, int verbose, int debug, + gpg_error_t (*status_cb)(ctrl_t, int, ...), + ctrl_t status_cb_arg) +{ + gpg_error_t err; + assuan_context_t ctx; + int did_success_msg = 0; + char *sockname; + const char *argv[6]; + + *r_ctx = NULL; + + err = assuan_new (&ctx); + if (err) + { + log_error ("error allocating assuan context: %s\n", gpg_strerror (err)); + return err; + } + + sockname = make_filename_try (gnupg_socketdir (), GPG_AGENT_SOCK_NAME, NULL); + if (!sockname) + { + err = gpg_err_make (errsource, gpg_err_code_from_syserror ()); + assuan_release (ctx); + return err; + } + + err = assuan_socket_connect (ctx, sockname, 0, 0); + if (err && autostart) + { + char *abs_homedir; + lock_spawn_t lock; + char *program = NULL; + const char *program_arg = NULL; + char *p; + const char *s; + int i; + + /* With no success start a new server. */ + if (!agent_program || !*agent_program) + agent_program = gnupg_module_name (GNUPG_MODULE_NAME_AGENT); + else if ((s=strchr (agent_program, '|')) && s[1] == '-' && s[2]=='-') + { + /* Hack to insert an additional option on the command line. */ + program = xtrystrdup (agent_program); + if (!program) + { + gpg_error_t tmperr = gpg_err_make (errsource, + gpg_err_code_from_syserror ()); + xfree (sockname); + assuan_release (ctx); + return tmperr; + } + p = strchr (program, '|'); + *p++ = 0; + program_arg = p; + } + + if (verbose) + log_info (_("no running gpg-agent - starting '%s'\n"), + agent_program); + + if (status_cb) + status_cb (status_cb_arg, STATUS_PROGRESS, + "starting_agent ? 0 0", NULL); + + /* We better pass an absolute home directory to the agent just + in case gpg-agent does not convert the passed name to an + absolute one (which it should do). */ + abs_homedir = make_absfilename_try (gnupg_homedir (), NULL); + if (!abs_homedir) + { + gpg_error_t tmperr = gpg_err_make (errsource, + gpg_err_code_from_syserror ()); + log_error ("error building filename: %s\n",gpg_strerror (tmperr)); + xfree (sockname); + assuan_release (ctx); + xfree (program); + return tmperr; + } + + if (fflush (NULL)) + { + gpg_error_t tmperr = gpg_err_make (errsource, + gpg_err_code_from_syserror ()); + log_error ("error flushing pending output: %s\n", + strerror (errno)); + xfree (sockname); + assuan_release (ctx); + xfree (abs_homedir); + xfree (program); + return tmperr; + } + + /* If the agent has been configured for use with a standard + socket, an environment variable is not required and thus + we can safely start the agent here. */ + i = 0; + argv[i++] = "--homedir"; + argv[i++] = abs_homedir; + argv[i++] = "--use-standard-socket"; + if (program_arg) + argv[i++] = program_arg; + argv[i++] = "--daemon"; + argv[i++] = NULL; + + if (!(err = lock_spawning (&lock, gnupg_homedir (), "agent", verbose)) + && assuan_socket_connect (ctx, sockname, 0, 0)) + { + err = gnupg_spawn_process_detached (program? program : agent_program, + argv, NULL); + if (err) + log_error ("failed to start agent '%s': %s\n", + agent_program, gpg_strerror (err)); + else + err = wait_for_sock (SECS_TO_WAIT_FOR_AGENT, "agent", + sockname, verbose, ctx, &did_success_msg); + } + + unlock_spawning (&lock, "agent"); + xfree (abs_homedir); + xfree (program); + } + xfree (sockname); + if (err) + { + if (autostart || gpg_err_code (err) != GPG_ERR_ASS_CONNECT_FAILED) + log_error ("can't connect to the agent: %s\n", gpg_strerror (err)); + assuan_release (ctx); + return gpg_err_make (errsource, GPG_ERR_NO_AGENT); + } + + if (debug && !did_success_msg) + log_debug ("connection to agent established\n"); + + err = assuan_transact (ctx, "RESET", + NULL, NULL, NULL, NULL, NULL, NULL); + if (!err) + { + err = send_pinentry_environment (ctx, errsource, + opt_lc_ctype, opt_lc_messages, + session_env); + if (gpg_err_code (err) == GPG_ERR_FORBIDDEN + && gpg_err_source (err) == GPG_ERR_SOURCE_GPGAGENT) + { + /* Check whether we are in restricted mode. */ + if (!assuan_transact (ctx, "GETINFO restricted", + NULL, NULL, NULL, NULL, NULL, NULL)) + { + if (verbose) + log_info (_("connection to agent is in restricted mode\n")); + err = 0; + } + } + } + if (err) + { + assuan_release (ctx); + return err; + } + + *r_ctx = ctx; + return 0; +} + + +/* Try to connect to the dirmngr via a socket. On platforms + supporting it, start it up if needed and if AUTOSTART is true. + Returns a new assuan context at R_CTX or an error code. */ +gpg_error_t +start_new_dirmngr (assuan_context_t *r_ctx, + gpg_err_source_t errsource, + const char *dirmngr_program, + int autostart, + int verbose, int debug, + gpg_error_t (*status_cb)(ctrl_t, int, ...), + ctrl_t status_cb_arg) +{ + gpg_error_t err; + assuan_context_t ctx; + const char *sockname; + int did_success_msg = 0; + + *r_ctx = NULL; + + err = assuan_new (&ctx); + if (err) + { + log_error ("error allocating assuan context: %s\n", gpg_strerror (err)); + return err; + } + + sockname = dirmngr_socket_name (); + err = assuan_socket_connect (ctx, sockname, 0, 0); + +#ifdef USE_DIRMNGR_AUTO_START + if (err && autostart) + { + lock_spawn_t lock; + const char *argv[4]; + char *abs_homedir; + + /* No connection: Try start a new Dirmngr. */ + if (!dirmngr_program || !*dirmngr_program) + dirmngr_program = gnupg_module_name (GNUPG_MODULE_NAME_DIRMNGR); + + if (verbose) + log_info (_("no running Dirmngr - starting '%s'\n"), + dirmngr_program); + + if (status_cb) + status_cb (status_cb_arg, STATUS_PROGRESS, + "starting_dirmngr ? 0 0", NULL); + + abs_homedir = make_absfilename (gnupg_homedir (), NULL); + if (!abs_homedir) + { + gpg_error_t tmperr = gpg_err_make (errsource, + gpg_err_code_from_syserror ()); + log_error ("error building filename: %s\n",gpg_strerror (tmperr)); + assuan_release (ctx); + return tmperr; + } + + if (fflush (NULL)) + { + gpg_error_t tmperr = gpg_err_make (errsource, + gpg_err_code_from_syserror ()); + log_error ("error flushing pending output: %s\n", + strerror (errno)); + assuan_release (ctx); + return tmperr; + } + + argv[0] = "--daemon"; + /* Try starting the daemon. Versions of dirmngr < 2.1.15 do + * this only if the home directory is given on the command line. */ + argv[1] = "--homedir"; + argv[2] = abs_homedir; + argv[3] = NULL; + + if (!(err = lock_spawning (&lock, gnupg_homedir (), "dirmngr", verbose)) + && assuan_socket_connect (ctx, sockname, 0, 0)) + { + err = gnupg_spawn_process_detached (dirmngr_program, argv, NULL); + if (err) + log_error ("failed to start the dirmngr '%s': %s\n", + dirmngr_program, gpg_strerror (err)); + else + err = wait_for_sock (SECS_TO_WAIT_FOR_DIRMNGR, "dirmngr", + sockname, verbose, ctx, &did_success_msg); + } + + unlock_spawning (&lock, "dirmngr"); + xfree (abs_homedir); + } +#else + (void)dirmngr_program; + (void)verbose; + (void)status_cb; + (void)status_cb_arg; +#endif /*USE_DIRMNGR_AUTO_START*/ + + if (err) + { + if (autostart || gpg_err_code (err) != GPG_ERR_ASS_CONNECT_FAILED) + log_error ("connecting dirmngr at '%s' failed: %s\n", + sockname, gpg_strerror (err)); + assuan_release (ctx); + return gpg_err_make (errsource, GPG_ERR_NO_DIRMNGR); + } + + if (debug && !did_success_msg) + log_debug ("connection to the dirmngr established\n"); + + *r_ctx = ctx; + return 0; +} + + +/* Return the version of a server using "GETINFO version". On success + 0 is returned and R_VERSION receives a malloced string with the + version which must be freed by the caller. On error NULL is stored + at R_VERSION and an error code returned. Mode is in general 0 but + certain values may be used to modify the used version command: + + MODE == 0 = Use "GETINFO version" + MODE == 2 - Use "SCD GETINFO version" + */ +gpg_error_t +get_assuan_server_version (assuan_context_t ctx, int mode, char **r_version) +{ + gpg_error_t err; + membuf_t data; + + init_membuf (&data, 64); + err = assuan_transact (ctx, + mode == 2? "SCD GETINFO version" + /**/ : "GETINFO version", + put_membuf_cb, &data, + NULL, NULL, NULL, NULL); + if (err) + { + xfree (get_membuf (&data, NULL)); + *r_version = NULL; + } + else + { + put_membuf (&data, "", 1); + *r_version = get_membuf (&data, NULL); + if (!*r_version) + err = gpg_error_from_syserror (); + } + return err; +} diff --git a/common/asshelp.h b/common/asshelp.h new file mode 100644 index 0000000..bf1bd17 --- /dev/null +++ b/common/asshelp.h @@ -0,0 +1,104 @@ +/* asshelp.h - Helper functions for Assuan + * Copyright (C) 2004, 2007 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef GNUPG_COMMON_ASSHELP_H +#define GNUPG_COMMON_ASSHELP_H + +#include <assuan.h> +#include <gpg-error.h> + +#include "session-env.h" +#include "util.h" + +/*-- asshelp.c --*/ + +void setup_libassuan_logging (unsigned int *debug_var_address, + int (*log_monitor)(assuan_context_t ctx, + unsigned int cat, + const char *msg)); +void set_libassuan_log_cats (unsigned int newcats); + + +gpg_error_t +send_pinentry_environment (assuan_context_t ctx, + gpg_err_source_t errsource, + const char *opt_lc_ctype, + const char *opt_lc_messages, + session_env_t session_env); + +/* This function is used by the call-agent.c modules to fire up a new + agent. */ +gpg_error_t +start_new_gpg_agent (assuan_context_t *r_ctx, + gpg_err_source_t errsource, + const char *agent_program, + const char *opt_lc_ctype, + const char *opt_lc_messages, + session_env_t session_env, + int autostart, int verbose, int debug, + gpg_error_t (*status_cb)(ctrl_t, int, ...), + ctrl_t status_cb_arg); + +/* This function is used to connect to the dirmngr. On some platforms + the function is able starts a dirmngr process if needed. */ +gpg_error_t +start_new_dirmngr (assuan_context_t *r_ctx, + gpg_err_source_t errsource, + const char *dirmngr_program, + int autostart, int verbose, int debug, + gpg_error_t (*status_cb)(ctrl_t, int, ...), + ctrl_t status_cb_arg); + +/* Return the version of a server using "GETINFO version". */ +gpg_error_t get_assuan_server_version (assuan_context_t ctx, + int mode, char **r_version); + + +/*-- asshelp2.c --*/ + +/* Helper function to print an assuan status line using a printf + format string. */ +gpg_error_t print_assuan_status (assuan_context_t ctx, + const char *keyword, + const char *format, + ...) GPGRT_ATTR_PRINTF(3,4); +gpg_error_t vprint_assuan_status (assuan_context_t ctx, + const char *keyword, + const char *format, + va_list arg_ptr) GPGRT_ATTR_PRINTF(3,0); + +gpg_error_t vprint_assuan_status_strings (assuan_context_t ctx, + const char *keyword, + va_list arg_ptr); +gpg_error_t print_assuan_status_strings (assuan_context_t ctx, + const char *keyword, + ...) GPGRT_ATTR_SENTINEL(1); + + +#endif /*GNUPG_COMMON_ASSHELP_H*/ diff --git a/common/asshelp2.c b/common/asshelp2.c new file mode 100644 index 0000000..0a7c454 --- /dev/null +++ b/common/asshelp2.c @@ -0,0 +1,136 @@ +/* asshelp2.c - More helper functions for Assuan + * Copyright (C) 2012 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <assuan.h> + +#include "util.h" +#include "asshelp.h" + +/* Helper function to print an assuan status line using a printf + format string. */ +gpg_error_t +vprint_assuan_status (assuan_context_t ctx, + const char *keyword, + const char *format, va_list arg_ptr) +{ + int rc; + char *buf; + + rc = gpgrt_vasprintf (&buf, format, arg_ptr); + if (rc < 0) + return gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); + rc = assuan_write_status (ctx, keyword, buf); + xfree (buf); + return rc; +} + + +/* Helper function to print an assuan status line using a printf + format string. */ +gpg_error_t +print_assuan_status (assuan_context_t ctx, + const char *keyword, + const char *format, ...) +{ + va_list arg_ptr; + gpg_error_t err; + + va_start (arg_ptr, format); + err = vprint_assuan_status (ctx, keyword, format, arg_ptr); + va_end (arg_ptr); + return err; +} + + +/* Helper function to print a list of strings as an assuan status + * line. KEYWORD is the first item on the status line. ARG_PTR is a + * list of strings which are all separated by a space in the output. + * The last argument must be a NULL. Linefeeds and carriage returns + * characters (which are not allowed in an Assuan status line) are + * silently quoted in C-style. */ +gpg_error_t +vprint_assuan_status_strings (assuan_context_t ctx, + const char *keyword, va_list arg_ptr) +{ + gpg_error_t err = 0; + const char *text; + char buf[950], *p; + size_t n; + + p = buf; + n = 0; + while ((text = va_arg (arg_ptr, const char *)) && n < DIM (buf)-3 ) + { + if (n) + { + *p++ = ' '; + n++; + } + for ( ; *text && n < DIM (buf)-3; n++, text++) + { + if (*text == '\n') + { + *p++ = '\\'; + *p++ = 'n'; + n++; + } + else if (*text == '\r') + { + *p++ = '\\'; + *p++ = 'r'; + n++; + } + else + *p++ = *text; + } + } + *p = 0; + err = assuan_write_status (ctx, keyword, buf); + + return err; +} + + +/* See vprint_assuan_status_strings. */ +gpg_error_t +print_assuan_status_strings (assuan_context_t ctx, const char *keyword, ...) +{ + va_list arg_ptr; + gpg_error_t err; + + va_start (arg_ptr, keyword); + err = vprint_assuan_status_strings (ctx, keyword, arg_ptr); + va_end (arg_ptr); + return err; +} diff --git a/common/audit-events.h b/common/audit-events.h new file mode 100644 index 0000000..ae9fde2 --- /dev/null +++ b/common/audit-events.h @@ -0,0 +1,116 @@ +/* Output of mkstrtable.awk. DO NOT EDIT. */ + +/* audit.h - Definitions for the audit subsystem + * Copyright (C) 2007 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +/* The purpose of this complex string table is to produce + optimal code with a minimum of relocations. */ + +static const char eventstr_msgstr[] = + "null event" "\0" + "setup ready" "\0" + "agent ready" "\0" + "dirmngr ready" "\0" + "gpg ready" "\0" + "gpgsm ready" "\0" + "g13 ready" "\0" + "got data" "\0" + "detached signature" "\0" + "cert only sig" "\0" + "data hash algo" "\0" + "attr hash algo" "\0" + "data cipher algo" "\0" + "bad data hash algo" "\0" + "bad data cipher algo" "\0" + "data hashing" "\0" + "read error" "\0" + "write error" "\0" + "usage error" "\0" + "save cert" "\0" + "new sig" "\0" + "sig name" "\0" + "sig status" "\0" + "new recp" "\0" + "recp name" "\0" + "recp result" "\0" + "decryption result" "\0" + "validate chain" "\0" + "chain begin" "\0" + "chain cert" "\0" + "chain rootcert" "\0" + "chain end" "\0" + "chain status" "\0" + "root trusted" "\0" + "crl check" "\0" + "got recipients" "\0" + "session key" "\0" + "encrypted to" "\0" + "encryption done" "\0" + "signed by" "\0" + "signing done"; + +static const int eventstr_msgidx[] = + { + 0, + 11, + 23, + 35, + 49, + 59, + 71, + 81, + 90, + 109, + 123, + 138, + 153, + 170, + 189, + 210, + 223, + 234, + 246, + 258, + 268, + 276, + 285, + 296, + 305, + 315, + 327, + 345, + 360, + 372, + 383, + 398, + 408, + 421, + 434, + 444, + 459, + 471, + 484, + 500, + 510, + + }; + +#define eventstr_msgidxof(code) (0 ? -1 \ + : ((code >= 0) && (code <= 40)) ? (code - 0) \ + : -1) diff --git a/common/audit.c b/common/audit.c new file mode 100644 index 0000000..718f729 --- /dev/null +++ b/common/audit.c @@ -0,0 +1,1324 @@ +/* audit.c - GnuPG's audit subsystem + * Copyright (C) 2007, 2009 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include <assert.h> + +#include "util.h" +#include "i18n.h" +#include "audit.h" +#include "audit-events.h" + +/* A list to maintain a list of helptags. */ +struct helptag_s +{ + struct helptag_s *next; + const char *name; +}; +typedef struct helptag_s *helptag_t; + + +/* One log entry. */ +struct log_item_s +{ + audit_event_t event; /* The event. */ + gpg_error_t err; /* The logged error code. */ + int intvalue; /* A logged integer value. */ + char *string; /* A malloced string or NULL. */ + ksba_cert_t cert; /* A certifciate or NULL. */ + int have_err:1; + int have_intvalue:1; +}; +typedef struct log_item_s *log_item_t; + + + +/* The main audit object. */ +struct audit_ctx_s +{ + const char *failure; /* If set a description of the internal failure. */ + audit_type_t type; + + log_item_t log; /* The table with the log entries. */ + size_t logsize; /* The allocated size for LOG. */ + size_t logused; /* The used size of LOG. */ + + estream_t outstream; /* The current output stream. */ + int use_html; /* The output shall be HTML formatted. */ + int indentlevel; /* Current level of indentation. */ + helptag_t helptags; /* List of help keys. */ +}; + + + + +static void writeout_para (audit_ctx_t ctx, + const char *format, ...) GPGRT_ATTR_PRINTF(2,3); +static void writeout_li (audit_ctx_t ctx, const char *oktext, + const char *format, ...) GPGRT_ATTR_PRINTF(3,4); +static void writeout_rem (audit_ctx_t ctx, + const char *format, ...) GPGRT_ATTR_PRINTF(2,3); + + +/* Add NAME to the list of help tags. NAME needs to be a const string + an this function merly stores this pointer. */ +static void +add_helptag (audit_ctx_t ctx, const char *name) +{ + helptag_t item; + + for (item=ctx->helptags; item; item = item->next) + if (!strcmp (item->name, name)) + return; /* Already in the list. */ + item = xtrycalloc (1, sizeof *item); + if (!item) + return; /* Don't care about memory problems. */ + item->name = name; + item->next = ctx->helptags; + ctx->helptags = item; +} + + +/* Remove all help tags from the context. */ +static void +clear_helptags (audit_ctx_t ctx) +{ + while (ctx->helptags) + { + helptag_t tmp = ctx->helptags->next; + xfree (ctx->helptags); + ctx->helptags = tmp; + } +} + + + +static const char * +event2str (audit_event_t event) +{ + /* We need the cast so that compiler does not complain about an + always true comparison (>= 0) for an unsigned value. */ + int idx = eventstr_msgidxof ((int)event); + if (idx == -1) + return "Unknown event"; + else + return eventstr_msgstr + eventstr_msgidx[idx]; +} + + + +/* Create a new audit context. In case of an error NULL is returned + and errno set appropriately. */ +audit_ctx_t +audit_new (void) +{ + audit_ctx_t ctx; + + ctx = xtrycalloc (1, sizeof *ctx); + + return ctx; +} + + +/* Release an audit context. Passing NULL for CTX is allowed and does + nothing. */ +void +audit_release (audit_ctx_t ctx) +{ + int idx; + if (!ctx) + return; + if (ctx->log) + { + for (idx=0; idx < ctx->logused; idx++) + { + if (ctx->log[idx].string) + xfree (ctx->log[idx].string); + if (ctx->log[idx].cert) + ksba_cert_release (ctx->log[idx].cert); + } + xfree (ctx->log); + } + clear_helptags (ctx); + xfree (ctx); +} + + +/* Set the type for the audit operation. If CTX is NULL, this is a + dummy function. */ +void +audit_set_type (audit_ctx_t ctx, audit_type_t type) +{ + if (!ctx || ctx->failure) + return; /* Audit not enabled or an internal error has occurred. */ + + if (ctx->type && ctx->type != type) + { + ctx->failure = "conflict in type initialization"; + return; + } + ctx->type = type; +} + + +/* Create a new log item and put it into the table. Return that log + item on success; return NULL on memory failure and mark that in + CTX. */ +static log_item_t +create_log_item (audit_ctx_t ctx) +{ + log_item_t item, table; + size_t size; + + if (!ctx->log) + { + size = 10; + table = xtrymalloc (size * sizeof *table); + if (!table) + { + ctx->failure = "Out of memory in create_log_item"; + return NULL; + } + ctx->log = table; + ctx->logsize = size; + item = ctx->log + 0; + ctx->logused = 1; + } + else if (ctx->logused >= ctx->logsize) + { + size = ctx->logsize + 10; + table = xtryrealloc (ctx->log, size * sizeof *table); + if (!table) + { + ctx->failure = "Out of memory while reallocating in create_log_item"; + return NULL; + } + ctx->log = table; + ctx->logsize = size; + item = ctx->log + ctx->logused++; + } + else + item = ctx->log + ctx->logused++; + + item->event = AUDIT_NULL_EVENT; + item->err = 0; + item->have_err = 0; + item->intvalue = 0; + item->have_intvalue = 0; + item->string = NULL; + item->cert = NULL; + + return item; + +} + +/* Add a new event to the audit log. If CTX is NULL, this function + does nothing. */ +void +audit_log (audit_ctx_t ctx, audit_event_t event) +{ + log_item_t item; + + if (!ctx || ctx->failure) + return; /* Audit not enabled or an internal error has occurred. */ + if (!event) + { + ctx->failure = "Invalid event passed to audit_log"; + return; + } + if (!(item = create_log_item (ctx))) + return; + item->event = event; +} + +/* Add a new event to the audit log. If CTX is NULL, this function + does nothing. This version also adds the result of the operation + to the log. */ +void +audit_log_ok (audit_ctx_t ctx, audit_event_t event, gpg_error_t err) +{ + log_item_t item; + + if (!ctx || ctx->failure) + return; /* Audit not enabled or an internal error has occurred. */ + if (!event) + { + ctx->failure = "Invalid event passed to audit_log_ok"; + return; + } + if (!(item = create_log_item (ctx))) + return; + item->event = event; + item->err = err; + item->have_err = 1; +} + + +/* Add a new event to the audit log. If CTX is NULL, this function + does nothing. This version also add the integer VALUE to the log. */ +void +audit_log_i (audit_ctx_t ctx, audit_event_t event, int value) +{ + log_item_t item; + + if (!ctx || ctx->failure) + return; /* Audit not enabled or an internal error has occurred. */ + if (!event) + { + ctx->failure = "Invalid event passed to audit_log_i"; + return; + } + if (!(item = create_log_item (ctx))) + return; + item->event = event; + item->intvalue = value; + item->have_intvalue = 1; +} + + +/* Add a new event to the audit log. If CTX is NULL, this function + does nothing. This version also add the integer VALUE to the log. */ +void +audit_log_s (audit_ctx_t ctx, audit_event_t event, const char *value) +{ + log_item_t item; + char *tmp; + + if (!ctx || ctx->failure) + return; /* Audit not enabled or an internal error has occurred. */ + if (!event) + { + ctx->failure = "Invalid event passed to audit_log_s"; + return; + } + tmp = xtrystrdup (value? value : ""); + if (!tmp) + { + ctx->failure = "Out of memory in audit_event"; + return; + } + if (!(item = create_log_item (ctx))) + { + xfree (tmp); + return; + } + item->event = event; + item->string = tmp; +} + +/* Add a new event to the audit log. If CTX is NULL, this function + does nothing. This version also adds the certificate CERT and the + result of an operation to the log. */ +void +audit_log_cert (audit_ctx_t ctx, audit_event_t event, + ksba_cert_t cert, gpg_error_t err) +{ + log_item_t item; + + if (!ctx || ctx->failure) + return; /* Audit not enabled or an internal error has occurred. */ + if (!event) + { + ctx->failure = "Invalid event passed to audit_log_cert"; + return; + } + if (!(item = create_log_item (ctx))) + return; + item->event = event; + item->err = err; + item->have_err = 1; + if (cert) + { + ksba_cert_ref (cert); + item->cert = cert; + } +} + + +/* Write TEXT to the outstream. */ +static void +writeout (audit_ctx_t ctx, const char *text) +{ + if (ctx->use_html) + { + for (; *text; text++) + { + if (*text == '<') + es_fputs ("<", ctx->outstream); + else if (*text == '&') + es_fputs ("&", ctx->outstream); + else + es_putc (*text, ctx->outstream); + } + } + else + es_fputs (text, ctx->outstream); +} + + +/* Write TEXT to the outstream using a variable argument list. */ +static void +writeout_v (audit_ctx_t ctx, const char *format, va_list arg_ptr) +{ + char *buf; + + gpgrt_vasprintf (&buf, format, arg_ptr); + if (buf) + { + writeout (ctx, buf); + xfree (buf); + } + else + writeout (ctx, "[!!Out of core!!]"); +} + + +/* Write TEXT as a paragraph. */ +static void +writeout_para (audit_ctx_t ctx, const char *format, ...) +{ + va_list arg_ptr; + + if (ctx->use_html) + es_fputs ("<p>", ctx->outstream); + va_start (arg_ptr, format) ; + writeout_v (ctx, format, arg_ptr); + va_end (arg_ptr); + if (ctx->use_html) + es_fputs ("</p>\n", ctx->outstream); + else + es_fputc ('\n', ctx->outstream); +} + + +static void +enter_li (audit_ctx_t ctx) +{ + if (ctx->use_html) + { + if (!ctx->indentlevel) + { + es_fputs ("<table border=\"0\">\n" + " <colgroup>\n" + " <col width=\"80%\" />\n" + " <col width=\"20%\" />\n" + " </colgroup>\n", + ctx->outstream); + } + } + ctx->indentlevel++; +} + + +static void +leave_li (audit_ctx_t ctx) +{ + ctx->indentlevel--; + if (ctx->use_html) + { + if (!ctx->indentlevel) + es_fputs ("</table>\n", ctx->outstream); + } +} + + +/* Write TEXT as a list element. If OKTEXT is not NULL, append it to + the last line. */ +static void +writeout_li (audit_ctx_t ctx, const char *oktext, const char *format, ...) +{ + va_list arg_ptr; + const char *color = NULL; + + if (ctx->use_html && format && oktext) + { + if (!strcmp (oktext, "Yes") + || !strcmp (oktext, "good") ) + color = "green"; + else if (!strcmp (oktext, "No") + || !strcmp (oktext, "bad") ) + color = "red"; + } + + if (format && oktext) + { + const char *s = NULL; + + if (!strcmp (oktext, "Yes")) + oktext = _("Yes"); + else if (!strcmp (oktext, "No")) + oktext = _("No"); + else if (!strcmp (oktext, "good")) + { + /* TRANSLATORS: Copy the prefix between the vertical bars + verbatim. It will not be printed. */ + oktext = _("|audit-log-result|Good"); + } + else if (!strcmp (oktext, "bad")) + oktext = _("|audit-log-result|Bad"); + else if (!strcmp (oktext, "unsupported")) + oktext = _("|audit-log-result|Not supported"); + else if (!strcmp (oktext, "no-cert")) + oktext = _("|audit-log-result|No certificate"); + else if (!strcmp (oktext, "disabled")) + oktext = _("|audit-log-result|Not enabled"); + else if (!strcmp (oktext, "error")) + oktext = _("|audit-log-result|Error"); + else if (!strcmp (oktext, "not-used")) + oktext = _("|audit-log-result|Not used"); + else if (!strcmp (oktext, "okay")) + oktext = _("|audit-log-result|Okay"); + else if (!strcmp (oktext, "skipped")) + oktext = _("|audit-log-result|Skipped"); + else if (!strcmp (oktext, "some")) + oktext = _("|audit-log-result|Some"); + else + s = ""; + + /* If we have set a prefix, skip it. */ + if (!s && *oktext == '|' && (s=strchr (oktext+1,'|'))) + oktext = s+1; + } + + if (ctx->use_html) + { + int i; + + es_fputs (" <tr><td><table><tr><td>", ctx->outstream); + if (color) + es_fprintf (ctx->outstream, "<font color=\"%s\">*</font>", color); + else + es_fputs ("*", ctx->outstream); + for (i=1; i < ctx->indentlevel; i++) + es_fputs (" ", ctx->outstream); + es_fputs ("</td><td>", ctx->outstream); + } + else + es_fprintf (ctx->outstream, "* %*s", (ctx->indentlevel-1)*2, ""); + if (format) + { + va_start (arg_ptr, format) ; + writeout_v (ctx, format, arg_ptr); + va_end (arg_ptr); + } + if (ctx->use_html) + es_fputs ("</td></tr></table>", ctx->outstream); + if (format && oktext) + { + if (ctx->use_html) + { + es_fputs ("</td><td>", ctx->outstream); + if (color) + es_fprintf (ctx->outstream, "<font color=\"%s\">", color); + } + else + writeout (ctx, ": "); + writeout (ctx, oktext); + if (color) + es_fputs ("</font>", ctx->outstream); + } + + if (ctx->use_html) + es_fputs ("</td></tr>\n", ctx->outstream); + else + es_fputc ('\n', ctx->outstream); +} + + +/* Write a remark line. */ +static void +writeout_rem (audit_ctx_t ctx, const char *format, ...) +{ + va_list arg_ptr; + + if (ctx->use_html) + { + int i; + + es_fputs (" <tr><td><table><tr><td>*", ctx->outstream); + for (i=1; i < ctx->indentlevel; i++) + es_fputs (" ", ctx->outstream); + es_fputs (" </td><td> (", ctx->outstream); + + } + else + es_fprintf (ctx->outstream, "* %*s (", (ctx->indentlevel-1)*2, ""); + if (format) + { + va_start (arg_ptr, format) ; + writeout_v (ctx, format, arg_ptr); + va_end (arg_ptr); + } + if (ctx->use_html) + es_fputs (")</td></tr></table></td></tr>\n", ctx->outstream); + else + es_fputs (")\n", ctx->outstream); +} + + +/* Return the first log item for EVENT. If STOPEVENT is not 0 never + look behind that event in the log. If STARTITEM is not NULL start + search _after_that item. */ +static log_item_t +find_next_log_item (audit_ctx_t ctx, log_item_t startitem, + audit_event_t event, audit_event_t stopevent) +{ + int idx; + + for (idx=0; idx < ctx->logused; idx++) + { + if (startitem) + { + if (ctx->log + idx == startitem) + startitem = NULL; + } + else if (stopevent && ctx->log[idx].event == stopevent) + break; + else if (ctx->log[idx].event == event) + return ctx->log + idx; + } + return NULL; +} + + +static log_item_t +find_log_item (audit_ctx_t ctx, audit_event_t event, audit_event_t stopevent) +{ + return find_next_log_item (ctx, NULL, event, stopevent); +} + + +/* Helper to a format a serial number. */ +static char * +format_serial (ksba_const_sexp_t sn) +{ + const char *p = (const char *)sn; + unsigned long n; + char *endp; + + if (!p) + return NULL; + if (*p != '(') + BUG (); /* Not a valid S-expression. */ + n = strtoul (p+1, &endp, 10); + p = endp; + if (*p != ':') + BUG (); /* Not a valid S-expression. */ + return bin2hex (p+1, n, NULL); +} + + +/* Return a malloced string with the serial number and the issuer DN + of the certificate. */ +static char * +get_cert_name (ksba_cert_t cert) +{ + char *result; + ksba_sexp_t sn; + char *issuer, *p; + + if (!cert) + return xtrystrdup ("[no certificate]"); + + issuer = ksba_cert_get_issuer (cert, 0); + sn = ksba_cert_get_serial (cert); + if (issuer && sn) + { + p = format_serial (sn); + if (!p) + result = xtrystrdup ("[invalid S/N]"); + else + { + result = xtrymalloc (strlen (p) + strlen (issuer) + 2 + 1); + if (result) + { + *result = '#'; + strcpy (stpcpy (stpcpy (result+1, p),"/"), issuer); + } + xfree (p); + } + } + else + result = xtrystrdup ("[missing S/N or issuer]"); + ksba_free (sn); + xfree (issuer); + return result; +} + +/* Return a malloced string with the serial number and the issuer DN + of the certificate. */ +static char * +get_cert_subject (ksba_cert_t cert, int idx) +{ + char *result; + char *subject; + + if (!cert) + return xtrystrdup ("[no certificate]"); + + subject = ksba_cert_get_subject (cert, idx); + if (subject) + { + result = xtrymalloc (strlen (subject) + 1 + 1); + if (result) + { + *result = '/'; + strcpy (result+1, subject); + } + } + else + result = NULL; + xfree (subject); + return result; +} + + +/* List the given certificiate. If CERT is NULL, this is a NOP. */ +static void +list_cert (audit_ctx_t ctx, ksba_cert_t cert, int with_subj) +{ + char *name; + int idx; + + name = get_cert_name (cert); + writeout_rem (ctx, "%s", name); + xfree (name); + if (with_subj) + { + enter_li (ctx); + for (idx=0; (name = get_cert_subject (cert, idx)); idx++) + { + writeout_rem (ctx, "%s", name); + xfree (name); + } + leave_li (ctx); + } +} + + +/* List the chain of certificates from STARTITEM up to STOPEVENT. The + certificates are written out as comments. */ +static void +list_certchain (audit_ctx_t ctx, log_item_t startitem, audit_event_t stopevent) +{ + log_item_t item; + + startitem = find_next_log_item (ctx, startitem, AUDIT_CHAIN_BEGIN,stopevent); + writeout_li (ctx, startitem? "Yes":"No", _("Certificate chain available")); + if (!startitem) + return; + + item = find_next_log_item (ctx, startitem, + AUDIT_CHAIN_ROOTCERT, AUDIT_CHAIN_END); + if (!item) + writeout_rem (ctx, "%s", _("root certificate missing")); + else + { + list_cert (ctx, item->cert, 0); + } + item = startitem; + while ( ((item = find_next_log_item (ctx, item, + AUDIT_CHAIN_CERT, AUDIT_CHAIN_END)))) + { + list_cert (ctx, item->cert, 1); + } +} + + + +/* Process an encrypt operation's log. */ +static void +proc_type_encrypt (audit_ctx_t ctx) +{ + log_item_t loopitem, item; + int recp_no, idx; + char numbuf[35]; + int algo; + char *name; + + item = find_log_item (ctx, AUDIT_ENCRYPTION_DONE, 0); + writeout_li (ctx, item?"Yes":"No", "%s", _("Data encryption succeeded")); + + enter_li (ctx); + + item = find_log_item (ctx, AUDIT_GOT_DATA, 0); + writeout_li (ctx, item? "Yes":"No", "%s", _("Data available")); + + item = find_log_item (ctx, AUDIT_SESSION_KEY, 0); + writeout_li (ctx, item? "Yes":"No", "%s", _("Session key created")); + if (item) + { + algo = gcry_cipher_map_name (item->string); + if (algo) + writeout_rem (ctx, _("algorithm: %s"), gnupg_cipher_algo_name (algo)); + else if (item->string && !strcmp (item->string, "1.2.840.113549.3.2")) + writeout_rem (ctx, _("unsupported algorithm: %s"), "RC2"); + else if (item->string) + writeout_rem (ctx, _("unsupported algorithm: %s"), item->string); + else + writeout_rem (ctx, _("seems to be not encrypted")); + } + + item = find_log_item (ctx, AUDIT_GOT_RECIPIENTS, 0); + snprintf (numbuf, sizeof numbuf, "%d", + item && item->have_intvalue? item->intvalue : 0); + writeout_li (ctx, numbuf, "%s", _("Number of recipients")); + + /* Loop over all recipients. */ + loopitem = NULL; + recp_no = 0; + while ((loopitem=find_next_log_item (ctx, loopitem, AUDIT_ENCRYPTED_TO, 0))) + { + recp_no++; + writeout_li (ctx, NULL, _("Recipient %d"), recp_no); + if (loopitem->cert) + { + name = get_cert_name (loopitem->cert); + writeout_rem (ctx, "%s", name); + xfree (name); + enter_li (ctx); + for (idx=0; (name = get_cert_subject (loopitem->cert, idx)); idx++) + { + writeout_rem (ctx, "%s", name); + xfree (name); + } + leave_li (ctx); + } + } + + leave_li (ctx); +} + + + +/* Process a sign operation's log. */ +static void +proc_type_sign (audit_ctx_t ctx) +{ + log_item_t item, loopitem; + int signer, idx; + const char *result; + ksba_cert_t cert; + char *name; + int lastalgo; + + item = find_log_item (ctx, AUDIT_SIGNING_DONE, 0); + writeout_li (ctx, item?"Yes":"No", "%s", _("Data signing succeeded")); + + enter_li (ctx); + + item = find_log_item (ctx, AUDIT_GOT_DATA, 0); + writeout_li (ctx, item? "Yes":"No", "%s", _("Data available")); + /* Write remarks with the data hash algorithms. We use a very + simple scheme to avoid some duplicates. */ + loopitem = NULL; + lastalgo = 0; + while ((loopitem = find_next_log_item + (ctx, loopitem, AUDIT_DATA_HASH_ALGO, AUDIT_NEW_SIG))) + { + if (loopitem->intvalue && loopitem->intvalue != lastalgo) + writeout_rem (ctx, _("data hash algorithm: %s"), + gcry_md_algo_name (loopitem->intvalue)); + lastalgo = loopitem->intvalue; + } + + /* Loop over all signer. */ + loopitem = NULL; + signer = 0; + while ((loopitem=find_next_log_item (ctx, loopitem, AUDIT_NEW_SIG, 0))) + { + signer++; + + item = find_next_log_item (ctx, loopitem, AUDIT_SIGNED_BY, AUDIT_NEW_SIG); + if (!item) + result = "error"; + else if (!item->err) + result = "okay"; + else if (gpg_err_code (item->err) == GPG_ERR_CANCELED) + result = "skipped"; + else + result = gpg_strerror (item->err); + cert = item? item->cert : NULL; + + writeout_li (ctx, result, _("Signer %d"), signer); + item = find_next_log_item (ctx, loopitem, + AUDIT_ATTR_HASH_ALGO, AUDIT_NEW_SIG); + if (item) + writeout_rem (ctx, _("attr hash algorithm: %s"), + gcry_md_algo_name (item->intvalue)); + + if (cert) + { + name = get_cert_name (cert); + writeout_rem (ctx, "%s", name); + xfree (name); + enter_li (ctx); + for (idx=0; (name = get_cert_subject (cert, idx)); idx++) + { + writeout_rem (ctx, "%s", name); + xfree (name); + } + leave_li (ctx); + } + } + + leave_li (ctx); +} + + + +/* Process a decrypt operation's log. */ +static void +proc_type_decrypt (audit_ctx_t ctx) +{ + log_item_t loopitem, item; + int algo, recpno; + char *name; + char numbuf[35]; + int idx; + + item = find_log_item (ctx, AUDIT_DECRYPTION_RESULT, 0); + writeout_li (ctx, item && !item->err?"Yes":"No", + "%s", _("Data decryption succeeded")); + + enter_li (ctx); + + item = find_log_item (ctx, AUDIT_GOT_DATA, 0); + writeout_li (ctx, item? "Yes":"No", "%s", _("Data available")); + + item = find_log_item (ctx, AUDIT_DATA_CIPHER_ALGO, 0); + algo = item? item->intvalue : 0; + writeout_li (ctx, algo?"Yes":"No", "%s", _("Encryption algorithm supported")); + if (algo) + writeout_rem (ctx, _("algorithm: %s"), gnupg_cipher_algo_name (algo)); + + item = find_log_item (ctx, AUDIT_BAD_DATA_CIPHER_ALGO, 0); + if (item && item->string) + { + algo = gcry_cipher_map_name (item->string); + if (algo) + writeout_rem (ctx, _("algorithm: %s"), gnupg_cipher_algo_name (algo)); + else if (item->string && !strcmp (item->string, "1.2.840.113549.3.2")) + writeout_rem (ctx, _("unsupported algorithm: %s"), "RC2"); + else if (item->string) + writeout_rem (ctx, _("unsupported algorithm: %s"), item->string); + else + writeout_rem (ctx, _("seems to be not encrypted")); + } + + + for (recpno = 0, item = NULL; + (item = find_next_log_item (ctx, item, AUDIT_NEW_RECP, 0)); recpno++) + ; + snprintf (numbuf, sizeof numbuf, "%d", recpno); + writeout_li (ctx, numbuf, "%s", _("Number of recipients")); + + /* Loop over all recipients. */ + loopitem = NULL; + while ((loopitem = find_next_log_item (ctx, loopitem, AUDIT_NEW_RECP, 0))) + { + const char *result; + + recpno = loopitem->have_intvalue? loopitem->intvalue : -1; + + item = find_next_log_item (ctx, loopitem, + AUDIT_RECP_RESULT, AUDIT_NEW_RECP); + if (!item) + result = "not-used"; + else if (!item->err) + result = "okay"; + else if (gpg_err_code (item->err) == GPG_ERR_CANCELED) + result = "skipped"; + else + result = gpg_strerror (item->err); + + item = find_next_log_item (ctx, loopitem, + AUDIT_RECP_NAME, AUDIT_NEW_RECP); + writeout_li (ctx, result, _("Recipient %d"), recpno); + if (item && item->string) + writeout_rem (ctx, "%s", item->string); + + /* If we have a certificate write out more infos. */ + item = find_next_log_item (ctx, loopitem, + AUDIT_SAVE_CERT, AUDIT_NEW_RECP); + if (item && item->cert) + { + enter_li (ctx); + for (idx=0; (name = get_cert_subject (item->cert, idx)); idx++) + { + writeout_rem (ctx, "%s", name); + xfree (name); + } + leave_li (ctx); + } + } + + leave_li (ctx); +} + + + +/* Process a verification operation's log. */ +static void +proc_type_verify (audit_ctx_t ctx) +{ + log_item_t loopitem, item; + int signo, count, idx, n_good, n_bad; + char numbuf[35]; + const char *result; + + /* If there is at least one signature status we claim that the + verification succeeded. This does not mean that the data has + verified okay. */ + item = find_log_item (ctx, AUDIT_SIG_STATUS, 0); + writeout_li (ctx, item?"Yes":"No", "%s", _("Data verification succeeded")); + enter_li (ctx); + + item = find_log_item (ctx, AUDIT_GOT_DATA, AUDIT_NEW_SIG); + writeout_li (ctx, item? "Yes":"No", "%s", _("Data available")); + if (!item) + goto leave; + + item = find_log_item (ctx, AUDIT_NEW_SIG, 0); + writeout_li (ctx, item? "Yes":"No", "%s", _("Signature available")); + if (!item) + goto leave; + + /* Print info about the used data hashing algorithms. */ + for (idx=0, n_good=n_bad=0; idx < ctx->logused; idx++) + { + item = ctx->log + idx; + if (item->event == AUDIT_NEW_SIG) + break; + else if (item->event == AUDIT_DATA_HASH_ALGO) + n_good++; + else if (item->event == AUDIT_BAD_DATA_HASH_ALGO) + n_bad++; + } + item = find_log_item (ctx, AUDIT_DATA_HASHING, AUDIT_NEW_SIG); + if (!item || item->err || !n_good) + result = "No"; + else if (n_good && !n_bad) + result = "Yes"; + else + result = "Some"; + writeout_li (ctx, result, "%s", _("Parsing data succeeded")); + if (n_good || n_bad) + { + for (idx=0; idx < ctx->logused; idx++) + { + item = ctx->log + idx; + if (item->event == AUDIT_NEW_SIG) + break; + else if (item->event == AUDIT_DATA_HASH_ALGO) + writeout_rem (ctx, _("data hash algorithm: %s"), + gcry_md_algo_name (item->intvalue)); + else if (item->event == AUDIT_BAD_DATA_HASH_ALGO) + writeout_rem (ctx, _("bad data hash algorithm: %s"), + item->string? item->string:"?"); + } + } + + + /* Loop over all signatures. */ + loopitem = find_log_item (ctx, AUDIT_NEW_SIG, 0); + assert (loopitem); + do + { + signo = loopitem->have_intvalue? loopitem->intvalue : -1; + + item = find_next_log_item (ctx, loopitem, + AUDIT_SIG_STATUS, AUDIT_NEW_SIG); + writeout_li (ctx, item? item->string:"?", _("Signature %d"), signo); + item = find_next_log_item (ctx, loopitem, + AUDIT_SIG_NAME, AUDIT_NEW_SIG); + if (item) + writeout_rem (ctx, "%s", item->string); + + item = find_next_log_item (ctx, loopitem, + AUDIT_DATA_HASH_ALGO, AUDIT_NEW_SIG); + if (item) + writeout_rem (ctx, _("data hash algorithm: %s"), + gcry_md_algo_name (item->intvalue)); + item = find_next_log_item (ctx, loopitem, + AUDIT_ATTR_HASH_ALGO, AUDIT_NEW_SIG); + if (item) + writeout_rem (ctx, _("attr hash algorithm: %s"), + gcry_md_algo_name (item->intvalue)); + + enter_li (ctx); + + /* List the certificate chain. */ + list_certchain (ctx, loopitem, AUDIT_NEW_SIG); + + /* Show the result of the chain validation. */ + item = find_next_log_item (ctx, loopitem, + AUDIT_CHAIN_STATUS, AUDIT_NEW_SIG); + if (item && item->have_err) + { + writeout_li (ctx, item->err? "No":"Yes", + _("Certificate chain valid")); + if (item->err) + writeout_rem (ctx, "%s", gpg_strerror (item->err)); + } + + /* Show whether the root certificate is fine. */ + item = find_next_log_item (ctx, loopitem, + AUDIT_ROOT_TRUSTED, AUDIT_CHAIN_STATUS); + if (item) + { + writeout_li (ctx, item->err?"No":"Yes", "%s", + _("Root certificate trustworthy")); + if (item->err) + { + add_helptag (ctx, "gpgsm.root-cert-not-trusted"); + writeout_rem (ctx, "%s", gpg_strerror (item->err)); + list_cert (ctx, item->cert, 0); + } + } + + /* Show result of the CRL/OCSP check. */ + item = find_next_log_item (ctx, loopitem, + AUDIT_CRL_CHECK, AUDIT_NEW_SIG); + if (item) + { + const char *ok; + switch (gpg_err_code (item->err)) + { + case 0: ok = "good"; break; + case GPG_ERR_TRUE: ok = "n/a"; break; + case GPG_ERR_CERT_REVOKED: ok = "bad"; break; + case GPG_ERR_NOT_ENABLED: ok = "disabled"; break; + case GPG_ERR_NO_CRL_KNOWN: + ok = _("no CRL found for certificate"); + break; + case GPG_ERR_CRL_TOO_OLD: + ok = _("the available CRL is too old"); + break; + default: ok = gpg_strerror (item->err); break; + } + + writeout_li (ctx, ok, "%s", _("CRL/OCSP check of certificates")); + if (item->err + && gpg_err_code (item->err) != GPG_ERR_CERT_REVOKED + && gpg_err_code (item->err) != GPG_ERR_NOT_ENABLED) + add_helptag (ctx, "gpgsm.crl-problem"); + } + + leave_li (ctx); + } + while ((loopitem = find_next_log_item (ctx, loopitem, AUDIT_NEW_SIG, 0))); + + + leave: + /* Always list the certificates stored in the signature. */ + item = NULL; + count = 0; + while ( ((item = find_next_log_item (ctx, item, + AUDIT_SAVE_CERT, AUDIT_NEW_SIG)))) + count++; + snprintf (numbuf, sizeof numbuf, "%d", count); + writeout_li (ctx, numbuf, _("Included certificates")); + item = NULL; + while ( ((item = find_next_log_item (ctx, item, + AUDIT_SAVE_CERT, AUDIT_NEW_SIG)))) + { + char *name = get_cert_name (item->cert); + writeout_rem (ctx, "%s", name); + xfree (name); + enter_li (ctx); + for (idx=0; (name = get_cert_subject (item->cert, idx)); idx++) + { + writeout_rem (ctx, "%s", name); + xfree (name); + } + leave_li (ctx); + } + leave_li (ctx); +} + + + + +/* Print the formatted audit result. THIS IS WORK IN PROGRESS. */ +void +audit_print_result (audit_ctx_t ctx, estream_t out, int use_html) +{ + int idx; + size_t n; + log_item_t item; + helptag_t helptag; + const char *s; + int show_raw = 0; + char *orig_codeset; + + if (!ctx) + return; + + orig_codeset = i18n_switchto_utf8 (); + + /* We use an environment variable to include some debug info in the + log. */ + if ((s = getenv ("gnupg_debug_audit"))) + show_raw = 1; + + assert (!ctx->outstream); + ctx->outstream = out; + ctx->use_html = use_html; + ctx->indentlevel = 0; + clear_helptags (ctx); + + if (use_html) + es_fputs ("<div class=\"" GNUPG_NAME "AuditLog\">\n", ctx->outstream); + + if (!ctx->log || !ctx->logused) + { + writeout_para (ctx, _("No audit log entries.")); + goto leave; + } + + if (show_raw) + { + int maxlen; + + for (idx=0,maxlen=0; idx < DIM (eventstr_msgidx); idx++) + { + n = strlen (eventstr_msgstr + eventstr_msgidx[idx]); + if (n > maxlen) + maxlen = n; + } + + if (use_html) + es_fputs ("<pre>\n", out); + for (idx=0; idx < ctx->logused; idx++) + { + es_fprintf (out, "log: %-*s", + maxlen, event2str (ctx->log[idx].event)); + if (ctx->log[idx].have_intvalue) + es_fprintf (out, " i=%d", ctx->log[idx].intvalue); + if (ctx->log[idx].string) + { + es_fputs (" s='", out); + writeout (ctx, ctx->log[idx].string); + es_fputs ("'", out); + } + if (ctx->log[idx].cert) + es_fprintf (out, " has_cert"); + if (ctx->log[idx].have_err) + { + es_fputs (" err='", out); + writeout (ctx, gpg_strerror (ctx->log[idx].err)); + es_fputs ("'", out); + } + es_fputs ("\n", out); + } + if (use_html) + es_fputs ("</pre>\n", out); + else + es_fputs ("\n", out); + } + + enter_li (ctx); + switch (ctx->type) + { + case AUDIT_TYPE_NONE: + writeout_li (ctx, NULL, _("Unknown operation")); + break; + case AUDIT_TYPE_ENCRYPT: + proc_type_encrypt (ctx); + break; + case AUDIT_TYPE_SIGN: + proc_type_sign (ctx); + break; + case AUDIT_TYPE_DECRYPT: + proc_type_decrypt (ctx); + break; + case AUDIT_TYPE_VERIFY: + proc_type_verify (ctx); + break; + } + item = find_log_item (ctx, AUDIT_AGENT_READY, 0); + if (item && item->have_err) + { + writeout_li (ctx, item->err? "No":"Yes", "%s", _("Gpg-Agent usable")); + if (item->err) + { + writeout_rem (ctx, "%s", gpg_strerror (item->err)); + add_helptag (ctx, "gnupg.agent-problem"); + } + } + item = find_log_item (ctx, AUDIT_DIRMNGR_READY, 0); + if (item && item->have_err) + { + writeout_li (ctx, item->err? "No":"Yes", "%s", _("Dirmngr usable")); + if (item->err) + { + writeout_rem (ctx, "%s", gpg_strerror (item->err)); + add_helptag (ctx, "gnupg.dirmngr-problem"); + } + } + leave_li (ctx); + + + /* Show the help from the collected help tags. */ + if (ctx->helptags) + { + if (use_html) + { + es_fputs ("<hr/>\n", ctx->outstream); + if (ctx->helptags->next) + es_fputs ("<ul>\n", ctx->outstream); + } + else + es_fputs ("\n\n", ctx->outstream); + } + for (helptag = ctx->helptags; helptag; helptag = helptag->next) + { + char *text; + + if (use_html && ctx->helptags->next) + es_fputs ("<li>\n", ctx->outstream); + + text = gnupg_get_help_string (helptag->name, 0); + if (text) + { + writeout_para (ctx, "%s", text); + xfree (text); + } + else + writeout_para (ctx, _("No help available for '%s'."), helptag->name); + if (use_html && ctx->helptags->next) + es_fputs ("</li>\n", ctx->outstream); + if (helptag->next) + es_fputs ("\n", ctx->outstream); + } + if (use_html && ctx->helptags && ctx->helptags->next) + es_fputs ("</ul>\n", ctx->outstream); + + leave: + if (use_html) + es_fputs ("</div>\n", ctx->outstream); + ctx->outstream = NULL; + ctx->use_html = 0; + clear_helptags (ctx); + i18n_switchback (orig_codeset); +} diff --git a/common/audit.h b/common/audit.h new file mode 100644 index 0000000..4ef2645 --- /dev/null +++ b/common/audit.h @@ -0,0 +1,225 @@ +/* audit.h - Definitions for the audit subsystem + * Copyright (C) 2007 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef GNUPG_COMMON_AUDIT_H +#define GNUPG_COMMON_AUDIT_H + +#include <ksba.h> + +struct audit_ctx_s; +typedef struct audit_ctx_s *audit_ctx_t; + +/* Constants for the audit type. */ +typedef enum + { + AUDIT_TYPE_NONE = 0, /* No type set. */ + AUDIT_TYPE_ENCRYPT, /* Data encryption. */ + AUDIT_TYPE_SIGN, /* Signature creation. */ + AUDIT_TYPE_DECRYPT, /* Data decryption. */ + AUDIT_TYPE_VERIFY /* Signature verification. */ + } +audit_type_t; + +/* The events we support. */ +typedef enum + { + AUDIT_NULL_EVENT = 0, + /* No such event. Its value shall be 0 and no other values shall + be assigned to the other enum symbols. This is required so + that the exaudit.awk script comes up with correct values + without running cc. */ + + AUDIT_SETUP_READY, + /* All preparations done so that the actual processing can start + now. This indicates that all parameters are okay and we can + start to process the actual data. */ + + AUDIT_AGENT_READY, /* err */ + /* Indicates whether the gpg-agent is available. For some + operations the agent is not required and thus no such event + will be logged. */ + + AUDIT_DIRMNGR_READY, /* err */ + /* Indicates whether the Dirmngr is available. For some + operations the Dirmngr is not required and thus no such event + will be logged. */ + + AUDIT_GPG_READY, /* err */ + /* Indicates whether the Gpg engine is available. */ + + AUDIT_GPGSM_READY, /* err */ + /* Indicates whether the Gpgsm engine is available. */ + + AUDIT_G13_READY, /* err */ + /* Indicates whether the G13 engine is available. */ + + AUDIT_GOT_DATA, + /* Data to be processed has been seen. */ + + AUDIT_DETACHED_SIGNATURE, + /* The signature is a detached one. */ + + AUDIT_CERT_ONLY_SIG, + /* A certifciate only signature has been detected. */ + + AUDIT_DATA_HASH_ALGO, /* int */ + /* The hash algo given as argument is used for the data. This + event will be repeated for all hash algorithms used with the + data. */ + + AUDIT_ATTR_HASH_ALGO, /* int */ + /* The hash algo given as argument is used to hash the message + digest and other signed attributes of this signature. */ + + AUDIT_DATA_CIPHER_ALGO, /* int */ + /* The cipher algo given as argument is used for this data. */ + + AUDIT_BAD_DATA_HASH_ALGO, /* string */ + /* The hash algo as specified by the signature can't be used. + STRING is the description of this algorithm which usually is an + OID string. STRING may be NULL. */ + + AUDIT_BAD_DATA_CIPHER_ALGO, /* string */ + /* The symmetric cipher algorithm is not supported. STRING is the + description of this algorithm which usually is an OID string. + STRING may be NULL. */ + + AUDIT_DATA_HASHING, /* ok_err */ + /* Logs the result of the data hashing. */ + + AUDIT_READ_ERROR, /* ok_err */ + /* A generic read error occurred. */ + + AUDIT_WRITE_ERROR, /* ok_err */ + /* A generic write error occurred. */ + + AUDIT_USAGE_ERROR, + /* The program was used in an inappropriate way; For example by + passing a data object while the signature does not expect one + or vice versa. */ + + AUDIT_SAVE_CERT, /* cert, ok_err */ + /* Save the certificate received in a message. */ + + AUDIT_NEW_SIG, /* int */ + /* Start the verification of a new signature for the last data + object. The argument is the signature number as used + internally by the program. */ + + AUDIT_SIG_NAME, /* string */ + /* The name of a signer. This is the name or other identification + data as known from the signature and not the name from the + certificate used for verification. An example for STRING when + using CMS is: "#1234/CN=Prostetnic Vogon Jeltz". */ + + AUDIT_SIG_STATUS, /* string */ + /* The signature status of the current signer. This is the last + audit information for one signature. STRING gives the status: + + "error" - there was a problem checking this or any signature. + "unsupported" - the signature type is not supported. + "no-cert" - The certificate of the signer was not found (the + S/N+issuer of the signer is already in the log). + "bad" - bad signature + "good" - good signature + */ + + AUDIT_NEW_RECP, /* int */ + /* A new recipient has been seen during decryption. The argument + is the recipient number as used internally by the program. */ + + AUDIT_RECP_NAME, /* string */ + /* The name of a recipient. This is the name or other identification + data as known from the decryption and not the name from the + certificate used for decryption. An example for STRING when + using CMS is: "#1234/CN=Prostetnic Vogon Jeltz". */ + + AUDIT_RECP_RESULT, /* ok_err */ + /* The status of the session key decryption. This is only written + for recipients tried. */ + + AUDIT_DECRYPTION_RESULT, /* ok_err */ + /* The status of the entire decryption. The decryption was + successful if the error code is 0. */ + + AUDIT_VALIDATE_CHAIN, + /* Start the validation of a certificate chain. */ + + AUDIT_CHAIN_BEGIN, + AUDIT_CHAIN_CERT, /* cert */ + AUDIT_CHAIN_ROOTCERT,/* cert */ + AUDIT_CHAIN_END, + /* These 4 events are used to log the certificates making up a + certificate chain. ROOTCERT is used for the trustanchor and + CERT for all other certificates. */ + + AUDIT_CHAIN_STATUS, /* err */ + /* Tells the final status of the chain validation. */ + + AUDIT_ROOT_TRUSTED, /* cert, err */ + /* Tells whether the root certificate is trusted. This event is + emitted during chain validation. */ + + AUDIT_CRL_CHECK, /* err */ + /* Tells the status of a CRL or OCSP check. */ + + AUDIT_GOT_RECIPIENTS, /* int */ + /* Records the number of recipients to be used for encryption. + This includes the recipients set by --encrypt-to but records 0 + if no real recipient has been given. */ + + AUDIT_SESSION_KEY, /* string */ + /* Mark the creation or availibility of the session key. The + parameter is the algorithm ID. */ + + AUDIT_ENCRYPTED_TO, /* cert, err */ + /* Records the certificate used for encryption and whether the + session key could be encrypted to it (err==0). */ + + AUDIT_ENCRYPTION_DONE, + /* Encryption succeeded. */ + + AUDIT_SIGNED_BY, /* cert, err */ + /* Records the certificate used for signed and whether the signure + could be created (if err==0). */ + + AUDIT_SIGNING_DONE, + /* Signing succeeded. */ + + + AUDIT_LAST_EVENT /* Marker for parsing this list. */ + } +audit_event_t; + + +audit_ctx_t audit_new (void); +void audit_release (audit_ctx_t ctx); +void audit_set_type (audit_ctx_t ctx, audit_type_t type); +void audit_log (audit_ctx_t ctx, audit_event_t event); +void audit_log_ok (audit_ctx_t ctx, audit_event_t event, gpg_error_t err); +void audit_log_i (audit_ctx_t ctx, audit_event_t event, int value); +void audit_log_s (audit_ctx_t ctx, audit_event_t event, const char *value); +void audit_log_cert (audit_ctx_t ctx, audit_event_t event, + ksba_cert_t cert, gpg_error_t err); + +void audit_print_result (audit_ctx_t ctx, estream_t stream, int use_html); + + + +#endif /*GNUPG_COMMON_AUDIT_H*/ diff --git a/common/b64dec.c b/common/b64dec.c new file mode 100644 index 0000000..6af494b --- /dev/null +++ b/common/b64dec.c @@ -0,0 +1,254 @@ +/* b64dec.c - Simple Base64 decoder. + * Copyright (C) 2008, 2011 Free Software Foundation, Inc. + * Copyright (C) 2008, 2011, 2016 g10 Code GmbH + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <assert.h> + +#include "i18n.h" +#include "util.h" + + +/* The reverse base-64 list used for base-64 decoding. */ +static unsigned char const asctobin[128] = + { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f, + 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, + 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, + 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, + 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, + 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, + 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, + 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff + }; + +enum decoder_states + { + s_init, s_idle, s_lfseen, s_beginseen, s_waitheader, s_waitblank, s_begin, + s_b64_0, s_b64_1, s_b64_2, s_b64_3, + s_waitendtitle, s_waitend + }; + + + +/* Initialize the context for the base64 decoder. If TITLE is NULL a + plain base64 decoding is done. If it is the empty string the + decoder will skip everything until a "-----BEGIN " line has been + seen, decoding ends at a "----END " line. */ +gpg_error_t +b64dec_start (struct b64state *state, const char *title) +{ + memset (state, 0, sizeof *state); + if (title) + { + state->title = xtrystrdup (title); + if (!state->title) + state->lasterr = gpg_error_from_syserror (); + else + state->idx = s_init; + } + else + state->idx = s_b64_0; + return state->lasterr; +} + + +/* Do in-place decoding of base-64 data of LENGTH in BUFFER. Stores the + new length of the buffer at R_NBYTES. */ +gpg_error_t +b64dec_proc (struct b64state *state, void *buffer, size_t length, + size_t *r_nbytes) +{ + enum decoder_states ds = state->idx; + unsigned char val = state->radbuf[0]; + int pos = state->quad_count; + char *d, *s; + + if (state->lasterr) + return state->lasterr; + + if (state->stop_seen) + { + *r_nbytes = 0; + state->lasterr = gpg_error (GPG_ERR_EOF); + xfree (state->title); + state->title = NULL; + return state->lasterr; + } + + for (s=d=buffer; length && !state->stop_seen; length--, s++) + { + again: + switch (ds) + { + case s_idle: + if (*s == '\n') + { + ds = s_lfseen; + pos = 0; + } + break; + case s_init: + ds = s_lfseen; + /* fall through */ + case s_lfseen: + if (*s != "-----BEGIN "[pos]) + { + ds = s_idle; + goto again; + } + else if (pos == 10) + { + pos = 0; + ds = s_beginseen; + } + else + pos++; + break; + case s_beginseen: + if (*s != "PGP "[pos]) + ds = s_begin; /* Not a PGP armor. */ + else if (pos == 3) + ds = s_waitheader; + else + pos++; + break; + case s_waitheader: + if (*s == '\n') + ds = s_waitblank; + break; + case s_waitblank: + if (*s == '\n') + ds = s_b64_0; /* blank line found. */ + else if (*s == ' ' || *s == '\r' || *s == '\t') + ; /* Ignore spaces. */ + else + { + /* Armor header line. Note that we don't care that our + * FSM accepts a header prefixed with spaces. */ + ds = s_waitheader; /* Wait for next header. */ + } + break; + case s_begin: + if (*s == '\n') + ds = s_b64_0; + break; + case s_b64_0: + case s_b64_1: + case s_b64_2: + case s_b64_3: + { + int c; + + if (*s == '-' && state->title) + { + /* Not a valid Base64 character: assume end + header. */ + ds = s_waitend; + } + else if (*s == '=') + { + /* Pad character: stop */ + if (ds == s_b64_1) + *d++ = val; + ds = state->title? s_waitendtitle : s_waitend; + } + else if (*s == '\n' || *s == ' ' || *s == '\r' || *s == '\t') + ; /* Skip white spaces. */ + else if ( (*s & 0x80) + || (c = asctobin[*(unsigned char *)s]) == 255) + { + /* Skip invalid encodings. */ + state->invalid_encoding = 1; + } + else if (ds == s_b64_0) + { + val = c << 2; + ds = s_b64_1; + } + else if (ds == s_b64_1) + { + val |= (c>>4)&3; + *d++ = val; + val = (c<<4)&0xf0; + ds = s_b64_2; + } + else if (ds == s_b64_2) + { + val |= (c>>2)&15; + *d++ = val; + val = (c<<6)&0xc0; + ds = s_b64_3; + } + else + { + val |= c&0x3f; + *d++ = val; + ds = s_b64_0; + } + } + break; + case s_waitendtitle: + if (*s == '-') + ds = s_waitend; + break; + case s_waitend: + if ( *s == '\n') + state->stop_seen = 1; + break; + default: + BUG(); + } + } + + + state->idx = ds; + state->radbuf[0] = val; + state->quad_count = pos; + *r_nbytes = (d -(char*) buffer); + return 0; +} + + +/* This function needs to be called before releasing the decoder + state. It may return an error code in case an encoding error has + been found during decoding. */ +gpg_error_t +b64dec_finish (struct b64state *state) +{ + xfree (state->title); + state->title = NULL; + + if (state->lasterr) + return state->lasterr; + + return state->invalid_encoding? gpg_error(GPG_ERR_BAD_DATA): 0; +} diff --git a/common/b64enc.c b/common/b64enc.c new file mode 100644 index 0000000..d633048 --- /dev/null +++ b/common/b64enc.c @@ -0,0 +1,422 @@ +/* b64enc.c - Simple Base64 encoder. + * Copyright (C) 2001, 2003, 2004, 2008, 2010, + * 2011 Free Software Foundation, Inc. + * Copyright (C) 2001, 2003, 2004, 2008, 2010, + * 2011 g10 Code GmbH + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <assert.h> + +#include "i18n.h" +#include "util.h" + +#define B64ENC_DID_HEADER 1 +#define B64ENC_DID_TRAILER 2 +#define B64ENC_NO_LINEFEEDS 16 +#define B64ENC_USE_PGPCRC 32 + +/* The base-64 character list */ +static unsigned char bintoasc[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + +/* Stuff required to create the OpenPGP CRC. This crc_table has been + created using this code: + + #include <stdio.h> + #include <stdint.h> + + #define CRCPOLY 0x864CFB + + int + main (void) + { + int i, j; + uint32_t t; + uint32_t crc_table[256]; + + crc_table[0] = 0; + for (i=j=0; j < 128; j++ ) + { + t = crc_table[j]; + if ( (t & 0x00800000) ) + { + t <<= 1; + crc_table[i++] = t ^ CRCPOLY; + crc_table[i++] = t; + } + else + { + t <<= 1; + crc_table[i++] = t; + crc_table[i++] = t ^ CRCPOLY; + } + } + + puts ("static const u32 crc_table[256] = {"); + for (i=j=0; i < 256; i++) + { + printf ("%s 0x%08lx", j? "":" ", (unsigned long)crc_table[i]); + if (i != 255) + { + putchar (','); + if ( ++j > 5) + { + j = 0; + putchar ('\n'); + } + } + } + puts ("\n};"); + return 0; + } +*/ +#define CRCINIT 0xB704CE +static const u32 crc_table[256] = { + 0x00000000, 0x00864cfb, 0x018ad50d, 0x010c99f6, 0x0393e6e1, 0x0315aa1a, + 0x021933ec, 0x029f7f17, 0x07a18139, 0x0727cdc2, 0x062b5434, 0x06ad18cf, + 0x043267d8, 0x04b42b23, 0x05b8b2d5, 0x053efe2e, 0x0fc54e89, 0x0f430272, + 0x0e4f9b84, 0x0ec9d77f, 0x0c56a868, 0x0cd0e493, 0x0ddc7d65, 0x0d5a319e, + 0x0864cfb0, 0x08e2834b, 0x09ee1abd, 0x09685646, 0x0bf72951, 0x0b7165aa, + 0x0a7dfc5c, 0x0afbb0a7, 0x1f0cd1e9, 0x1f8a9d12, 0x1e8604e4, 0x1e00481f, + 0x1c9f3708, 0x1c197bf3, 0x1d15e205, 0x1d93aefe, 0x18ad50d0, 0x182b1c2b, + 0x192785dd, 0x19a1c926, 0x1b3eb631, 0x1bb8faca, 0x1ab4633c, 0x1a322fc7, + 0x10c99f60, 0x104fd39b, 0x11434a6d, 0x11c50696, 0x135a7981, 0x13dc357a, + 0x12d0ac8c, 0x1256e077, 0x17681e59, 0x17ee52a2, 0x16e2cb54, 0x166487af, + 0x14fbf8b8, 0x147db443, 0x15712db5, 0x15f7614e, 0x3e19a3d2, 0x3e9fef29, + 0x3f9376df, 0x3f153a24, 0x3d8a4533, 0x3d0c09c8, 0x3c00903e, 0x3c86dcc5, + 0x39b822eb, 0x393e6e10, 0x3832f7e6, 0x38b4bb1d, 0x3a2bc40a, 0x3aad88f1, + 0x3ba11107, 0x3b275dfc, 0x31dced5b, 0x315aa1a0, 0x30563856, 0x30d074ad, + 0x324f0bba, 0x32c94741, 0x33c5deb7, 0x3343924c, 0x367d6c62, 0x36fb2099, + 0x37f7b96f, 0x3771f594, 0x35ee8a83, 0x3568c678, 0x34645f8e, 0x34e21375, + 0x2115723b, 0x21933ec0, 0x209fa736, 0x2019ebcd, 0x228694da, 0x2200d821, + 0x230c41d7, 0x238a0d2c, 0x26b4f302, 0x2632bff9, 0x273e260f, 0x27b86af4, + 0x252715e3, 0x25a15918, 0x24adc0ee, 0x242b8c15, 0x2ed03cb2, 0x2e567049, + 0x2f5ae9bf, 0x2fdca544, 0x2d43da53, 0x2dc596a8, 0x2cc90f5e, 0x2c4f43a5, + 0x2971bd8b, 0x29f7f170, 0x28fb6886, 0x287d247d, 0x2ae25b6a, 0x2a641791, + 0x2b688e67, 0x2beec29c, 0x7c3347a4, 0x7cb50b5f, 0x7db992a9, 0x7d3fde52, + 0x7fa0a145, 0x7f26edbe, 0x7e2a7448, 0x7eac38b3, 0x7b92c69d, 0x7b148a66, + 0x7a181390, 0x7a9e5f6b, 0x7801207c, 0x78876c87, 0x798bf571, 0x790db98a, + 0x73f6092d, 0x737045d6, 0x727cdc20, 0x72fa90db, 0x7065efcc, 0x70e3a337, + 0x71ef3ac1, 0x7169763a, 0x74578814, 0x74d1c4ef, 0x75dd5d19, 0x755b11e2, + 0x77c46ef5, 0x7742220e, 0x764ebbf8, 0x76c8f703, 0x633f964d, 0x63b9dab6, + 0x62b54340, 0x62330fbb, 0x60ac70ac, 0x602a3c57, 0x6126a5a1, 0x61a0e95a, + 0x649e1774, 0x64185b8f, 0x6514c279, 0x65928e82, 0x670df195, 0x678bbd6e, + 0x66872498, 0x66016863, 0x6cfad8c4, 0x6c7c943f, 0x6d700dc9, 0x6df64132, + 0x6f693e25, 0x6fef72de, 0x6ee3eb28, 0x6e65a7d3, 0x6b5b59fd, 0x6bdd1506, + 0x6ad18cf0, 0x6a57c00b, 0x68c8bf1c, 0x684ef3e7, 0x69426a11, 0x69c426ea, + 0x422ae476, 0x42aca88d, 0x43a0317b, 0x43267d80, 0x41b90297, 0x413f4e6c, + 0x4033d79a, 0x40b59b61, 0x458b654f, 0x450d29b4, 0x4401b042, 0x4487fcb9, + 0x461883ae, 0x469ecf55, 0x479256a3, 0x47141a58, 0x4defaaff, 0x4d69e604, + 0x4c657ff2, 0x4ce33309, 0x4e7c4c1e, 0x4efa00e5, 0x4ff69913, 0x4f70d5e8, + 0x4a4e2bc6, 0x4ac8673d, 0x4bc4fecb, 0x4b42b230, 0x49ddcd27, 0x495b81dc, + 0x4857182a, 0x48d154d1, 0x5d26359f, 0x5da07964, 0x5cace092, 0x5c2aac69, + 0x5eb5d37e, 0x5e339f85, 0x5f3f0673, 0x5fb94a88, 0x5a87b4a6, 0x5a01f85d, + 0x5b0d61ab, 0x5b8b2d50, 0x59145247, 0x59921ebc, 0x589e874a, 0x5818cbb1, + 0x52e37b16, 0x526537ed, 0x5369ae1b, 0x53efe2e0, 0x51709df7, 0x51f6d10c, + 0x50fa48fa, 0x507c0401, 0x5542fa2f, 0x55c4b6d4, 0x54c82f22, 0x544e63d9, + 0x56d11cce, 0x56575035, 0x575bc9c3, 0x57dd8538 +}; + + +static gpg_error_t +enc_start (struct b64state *state, FILE *fp, estream_t stream, + const char *title) +{ + memset (state, 0, sizeof *state); + state->fp = fp; + state->stream = stream; + state->lasterr = 0; + if (title && !*title) + state->flags |= B64ENC_NO_LINEFEEDS; + else if (title) + { + if (!strncmp (title, "PGP ", 4)) + { + state->flags |= B64ENC_USE_PGPCRC; + state->crc = CRCINIT; + } + state->title = xtrystrdup (title); + if (!state->title) + state->lasterr = gpg_error_from_syserror (); + } + return state->lasterr; +} + + +/* Prepare for base-64 writing to the stream FP. If TITLE is not NULL + and not an empty string, this string will be used as the title for + the armor lines, with TITLE being an empty string, we don't write + the header lines and furthermore even don't write any linefeeds. + If TITLE starts with "PGP " the OpenPGP CRC checksum will be + written as well. With TITLE being NULL, we merely don't write + header but make sure that lines are not too long. Note, that we + don't write any output unless at least one byte get written using + b64enc_write. */ +gpg_error_t +b64enc_start (struct b64state *state, FILE *fp, const char *title) +{ + return enc_start (state, fp, NULL, title); +} + +/* Same as b64enc_start but takes an estream. */ +gpg_error_t +b64enc_start_es (struct b64state *state, estream_t fp, const char *title) +{ + return enc_start (state, NULL, fp, title); +} + + +static int +my_fputs (const char *string, struct b64state *state) +{ + if (state->stream) + return es_fputs (string, state->stream); + else + return fputs (string, state->fp); +} + + +/* Write NBYTES from BUFFER to the Base 64 stream identified by + STATE. With BUFFER and NBYTES being 0, merely do a fflush on the + stream. */ +gpg_error_t +b64enc_write (struct b64state *state, const void *buffer, size_t nbytes) +{ + unsigned char radbuf[4]; + int idx, quad_count; + const unsigned char *p; + + if (state->lasterr) + return state->lasterr; + + if (!nbytes) + { + if (buffer) + if (state->stream? es_fflush (state->stream) : fflush (state->fp)) + goto write_error; + return 0; + } + + if (!(state->flags & B64ENC_DID_HEADER)) + { + if (state->title) + { + if ( my_fputs ("-----BEGIN ", state) == EOF + || my_fputs (state->title, state) == EOF + || my_fputs ("-----\n", state) == EOF) + goto write_error; + if ( (state->flags & B64ENC_USE_PGPCRC) + && my_fputs ("\n", state) == EOF) + goto write_error; + } + + state->flags |= B64ENC_DID_HEADER; + } + + idx = state->idx; + quad_count = state->quad_count; + assert (idx < 4); + memcpy (radbuf, state->radbuf, idx); + + if ( (state->flags & B64ENC_USE_PGPCRC) ) + { + size_t n; + u32 crc = state->crc; + + for (p=buffer, n=nbytes; n; p++, n-- ) + crc = ((u32)crc << 8) ^ crc_table[((crc >> 16)&0xff) ^ *p]; + state->crc = (crc & 0x00ffffff); + } + + for (p=buffer; nbytes; p++, nbytes--) + { + radbuf[idx++] = *p; + if (idx > 2) + { + char tmp[4]; + + tmp[0] = bintoasc[(*radbuf >> 2) & 077]; + tmp[1] = bintoasc[(((*radbuf<<4)&060)|((radbuf[1] >> 4)&017))&077]; + tmp[2] = bintoasc[(((radbuf[1]<<2)&074)|((radbuf[2]>>6)&03))&077]; + tmp[3] = bintoasc[radbuf[2]&077]; + if (state->stream) + { + for (idx=0; idx < 4; idx++) + es_putc (tmp[idx], state->stream); + idx = 0; + if (es_ferror (state->stream)) + goto write_error; + } + else + { + for (idx=0; idx < 4; idx++) + putc (tmp[idx], state->fp); + idx = 0; + if (ferror (state->fp)) + goto write_error; + } + if (++quad_count >= (64/4)) + { + quad_count = 0; + if (!(state->flags & B64ENC_NO_LINEFEEDS) + && my_fputs ("\n", state) == EOF) + goto write_error; + } + } + } + memcpy (state->radbuf, radbuf, idx); + state->idx = idx; + state->quad_count = quad_count; + return 0; + + write_error: + state->lasterr = gpg_error_from_syserror (); + if (state->title) + { + xfree (state->title); + state->title = NULL; + } + return state->lasterr; +} + + +gpg_error_t +b64enc_finish (struct b64state *state) +{ + gpg_error_t err = 0; + unsigned char radbuf[4]; + int idx, quad_count; + char tmp[4]; + + if (state->lasterr) + return state->lasterr; + + if (!(state->flags & B64ENC_DID_HEADER)) + goto cleanup; + + /* Flush the base64 encoding */ + idx = state->idx; + quad_count = state->quad_count; + assert (idx < 4); + memcpy (radbuf, state->radbuf, idx); + + if (idx) + { + tmp[0] = bintoasc[(*radbuf>>2)&077]; + if (idx == 1) + { + tmp[1] = bintoasc[((*radbuf << 4) & 060) & 077]; + tmp[2] = '='; + tmp[3] = '='; + } + else + { + tmp[1] = bintoasc[(((*radbuf<<4)&060)|((radbuf[1]>>4)&017))&077]; + tmp[2] = bintoasc[((radbuf[1] << 2) & 074) & 077]; + tmp[3] = '='; + } + if (state->stream) + { + for (idx=0; idx < 4; idx++) + es_putc (tmp[idx], state->stream); + if (es_ferror (state->stream)) + goto write_error; + } + else + { + for (idx=0; idx < 4; idx++) + putc (tmp[idx], state->fp); + if (ferror (state->fp)) + goto write_error; + } + + if (++quad_count >= (64/4)) + { + quad_count = 0; + if (!(state->flags & B64ENC_NO_LINEFEEDS) + && my_fputs ("\n", state) == EOF) + goto write_error; + } + } + + /* Finish the last line and write the trailer. */ + if (quad_count + && !(state->flags & B64ENC_NO_LINEFEEDS) + && my_fputs ("\n", state) == EOF) + goto write_error; + + if ( (state->flags & B64ENC_USE_PGPCRC) ) + { + /* Write the CRC. */ + my_fputs ("=", state); + radbuf[0] = state->crc >>16; + radbuf[1] = state->crc >> 8; + radbuf[2] = state->crc; + tmp[0] = bintoasc[(*radbuf>>2)&077]; + tmp[1] = bintoasc[(((*radbuf<<4)&060)|((radbuf[1]>>4)&017))&077]; + tmp[2] = bintoasc[(((radbuf[1]<<2)&074)|((radbuf[2]>>6)&03))&077]; + tmp[3] = bintoasc[radbuf[2]&077]; + if (state->stream) + { + for (idx=0; idx < 4; idx++) + es_putc (tmp[idx], state->stream); + if (es_ferror (state->stream)) + goto write_error; + } + else + { + for (idx=0; idx < 4; idx++) + putc (tmp[idx], state->fp); + if (ferror (state->fp)) + goto write_error; + } + if (!(state->flags & B64ENC_NO_LINEFEEDS) + && my_fputs ("\n", state) == EOF) + goto write_error; + } + + if (state->title) + { + if ( my_fputs ("-----END ", state) == EOF + || my_fputs (state->title, state) == EOF + || my_fputs ("-----\n", state) == EOF) + goto write_error; + } + + goto cleanup; + + write_error: + err = gpg_error_from_syserror (); + + cleanup: + if (state->title) + { + xfree (state->title); + state->title = NULL; + } + state->fp = NULL; + state->stream = NULL; + state->lasterr = err; + return err; +} diff --git a/common/call-gpg.c b/common/call-gpg.c new file mode 100644 index 0000000..c1472e9 --- /dev/null +++ b/common/call-gpg.c @@ -0,0 +1,753 @@ +/* call-gpg.c - Communication with the GPG + * Copyright (C) 2009 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#include <assert.h> +#include <assuan.h> +#include <errno.h> +#include <npth.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <time.h> + +#include "call-gpg.h" +#include "exechelp.h" +#include "i18n.h" +#include "logging.h" +#include "membuf.h" +#include "strlist.h" +#include "util.h" + + +static GPGRT_INLINE gpg_error_t +my_error_from_syserror (void) +{ + return gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); +} + +static GPGRT_INLINE gpg_error_t +my_error_from_errno (int e) +{ + return gpg_err_make (default_errsource, gpg_err_code_from_errno (e)); +} + + +/* Fire up a new GPG. Handle the server's initial greeting. Returns + 0 on success and stores the assuan context at R_CTX. */ +static gpg_error_t +start_gpg (ctrl_t ctrl, const char *gpg_program, strlist_t gpg_arguments, + int input_fd, int output_fd, assuan_context_t *r_ctx) +{ + gpg_error_t err; + assuan_context_t ctx = NULL; + const char *pgmname; + const char **argv; + assuan_fd_t no_close_list[5]; + int i; + char line[ASSUAN_LINELENGTH]; + + (void)ctrl; + + *r_ctx = NULL; + + err = assuan_new (&ctx); + if (err) + { + log_error ("can't allocate assuan context: %s\n", gpg_strerror (err)); + return err; + } + + /* The first time we are used, initialize the gpg_program variable. */ + if ( !gpg_program || !*gpg_program ) + gpg_program = gnupg_module_name (GNUPG_MODULE_NAME_GPG); + + /* Compute argv[0]. */ + if ( !(pgmname = strrchr (gpg_program, '/'))) + pgmname = gpg_program; + else + pgmname++; + + if (fflush (NULL)) + { + err = my_error_from_syserror (); + log_error ("error flushing pending output: %s\n", gpg_strerror (err)); + return err; + } + + argv = xtrycalloc (strlist_length (gpg_arguments) + 3, sizeof *argv); + if (argv == NULL) + { + err = my_error_from_syserror (); + return err; + } + i = 0; + argv[i++] = pgmname; + argv[i++] = "--server"; + for (; gpg_arguments; gpg_arguments = gpg_arguments->next) + argv[i++] = gpg_arguments->d; + argv[i++] = NULL; + + i = 0; + if (log_get_fd () != -1) + no_close_list[i++] = assuan_fd_from_posix_fd (log_get_fd ()); + no_close_list[i++] = assuan_fd_from_posix_fd (fileno (stderr)); + if (input_fd != -1) + no_close_list[i++] = assuan_fd_from_posix_fd (input_fd); + if (output_fd != -1) + no_close_list[i++] = assuan_fd_from_posix_fd (output_fd); + no_close_list[i] = ASSUAN_INVALID_FD; + + /* Connect to GPG and perform initial handshaking. */ + err = assuan_pipe_connect (ctx, gpg_program, argv, no_close_list, + NULL, NULL, 0); + if (err) + { + assuan_release (ctx); + log_error ("can't connect to GPG: %s\n", gpg_strerror (err)); + return gpg_error (GPG_ERR_NO_ENGINE); + } + + if (input_fd != -1) + { + snprintf (line, sizeof line, "INPUT FD=%d", input_fd); + err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); + if (err) + { + assuan_release (ctx); + log_error ("error sending INPUT command: %s\n", gpg_strerror (err)); + return err; + } + } + + if (output_fd != -1) + { + snprintf (line, sizeof line, "OUTPUT FD=%d", output_fd); + err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); + if (err) + { + assuan_release (ctx); + log_error ("error sending OUTPUT command: %s\n", gpg_strerror (err)); + return err; + } + } + + *r_ctx = ctx; + return 0; +} + + +/* Release the assuan context created by start_gpg. */ +static void +release_gpg (assuan_context_t ctx) +{ + assuan_release (ctx); +} + + + +/* The data passed to the writer_thread. */ +struct writer_thread_parms +{ + int fd; + const void *data; + size_t datalen; + estream_t stream; + gpg_error_t *err_addr; +}; + + +/* The thread started by start_writer. */ +static void * +writer_thread_main (void *arg) +{ + gpg_error_t err = 0; + struct writer_thread_parms *parm = arg; + char _buffer[4096]; + char *buffer; + size_t length; + + if (parm->stream) + { + buffer = _buffer; + err = es_read (parm->stream, buffer, sizeof _buffer, &length); + if (err) + { + log_error ("reading stream failed: %s\n", gpg_strerror (err)); + goto leave; + } + } + else + { + buffer = (char *) parm->data; + length = parm->datalen; + } + + while (length) + { + ssize_t nwritten; + + nwritten = npth_write (parm->fd, buffer, length < 4096? length:4096); + if (nwritten < 0) + { + if (errno == EINTR) + continue; + err = my_error_from_syserror (); + break; /* Write error. */ + } + length -= nwritten; + + if (parm->stream) + { + if (length == 0) + { + err = es_read (parm->stream, buffer, sizeof _buffer, &length); + if (err) + { + log_error ("reading stream failed: %s\n", + gpg_strerror (err)); + break; + } + if (length == 0) + /* We're done. */ + break; + } + } + else + buffer += nwritten; + } + + leave: + *parm->err_addr = err; + if (close (parm->fd)) + log_error ("closing writer fd %d failed: %s\n", parm->fd, strerror (errno)); + xfree (parm); + return NULL; +} + + +/* Fire up a thread to send (DATA,DATALEN) to the file descriptor FD. + On success the thread receives the ownership over FD. The thread + ID is stored at R_TID. WRITER_ERR is the address of an gpg_error_t + variable to receive a possible write error after the thread has + finished. */ +static gpg_error_t +start_writer (int fd, const void *data, size_t datalen, estream_t stream, + npth_t *r_thread, gpg_error_t *err_addr) +{ + gpg_error_t err; + struct writer_thread_parms *parm; + npth_attr_t tattr; + npth_t thread; + int ret; + + memset (r_thread, '\0', sizeof (*r_thread)); + *err_addr = 0; + + parm = xtrymalloc (sizeof *parm); + if (!parm) + return my_error_from_syserror (); + parm->fd = fd; + parm->data = data; + parm->datalen = datalen; + parm->stream = stream; + parm->err_addr = err_addr; + + npth_attr_init (&tattr); + npth_attr_setdetachstate (&tattr, NPTH_CREATE_JOINABLE); + + ret = npth_create (&thread, &tattr, writer_thread_main, parm); + if (ret) + { + err = my_error_from_errno (ret); + log_error ("error spawning writer thread: %s\n", gpg_strerror (err)); + } + else + { + npth_setname_np (thread, "fd-writer"); + err = 0; + *r_thread = thread; + } + npth_attr_destroy (&tattr); + + return err; +} + + + +/* The data passed to the reader_thread. */ +struct reader_thread_parms +{ + int fd; + membuf_t *mb; + estream_t stream; + gpg_error_t *err_addr; +}; + + +/* The thread started by start_reader. */ +static void * +reader_thread_main (void *arg) +{ + gpg_error_t err = 0; + struct reader_thread_parms *parm = arg; + char buffer[4096]; + int nread; + + while ( (nread = npth_read (parm->fd, buffer, sizeof buffer)) ) + { + if (nread < 0) + { + if (errno == EINTR) + continue; + err = my_error_from_syserror (); + break; /* Read error. */ + } + + if (parm->stream) + { + const char *p = buffer; + size_t nwritten; + while (nread) + { + err = es_write (parm->stream, p, nread, &nwritten); + if (err) + { + log_error ("writing stream failed: %s\n", + gpg_strerror (err)); + goto leave; + } + nread -= nwritten; + p += nwritten; + } + } + else + put_membuf (parm->mb, buffer, nread); + } + + leave: + *parm->err_addr = err; + if (close (parm->fd)) + log_error ("closing reader fd %d failed: %s\n", parm->fd, strerror (errno)); + xfree (parm); + return NULL; +} + + +/* Fire up a thread to receive data from the file descriptor FD. On + success the thread receives the ownership over FD. The thread ID + is stored at R_TID. After the thread has finished an error from + the thread will be stored at ERR_ADDR. */ +static gpg_error_t +start_reader (int fd, membuf_t *mb, estream_t stream, + npth_t *r_thread, gpg_error_t *err_addr) +{ + gpg_error_t err; + struct reader_thread_parms *parm; + npth_attr_t tattr; + npth_t thread; + int ret; + + memset (r_thread, '\0', sizeof (*r_thread)); + *err_addr = 0; + + parm = xtrymalloc (sizeof *parm); + if (!parm) + return my_error_from_syserror (); + parm->fd = fd; + parm->mb = mb; + parm->stream = stream; + parm->err_addr = err_addr; + + npth_attr_init (&tattr); + npth_attr_setdetachstate (&tattr, NPTH_CREATE_JOINABLE); + + ret = npth_create (&thread, &tattr, reader_thread_main, parm); + if (ret) + { + err = my_error_from_errno (ret); + log_error ("error spawning reader thread: %s\n", gpg_strerror (err)); + } + else + { + npth_setname_np (thread, "fd-reader"); + err = 0; + *r_thread = thread; + } + npth_attr_destroy (&tattr); + + return err; +} + + + + +/* Call GPG to encrypt a block of data. + + + */ +static gpg_error_t +_gpg_encrypt (ctrl_t ctrl, + const char *gpg_program, + strlist_t gpg_arguments, + const void *plain, size_t plainlen, + estream_t plain_stream, + strlist_t keys, + membuf_t *reader_mb, + estream_t cipher_stream) +{ + gpg_error_t err; + assuan_context_t ctx = NULL; + int outbound_fds[2] = { -1, -1 }; + int inbound_fds[2] = { -1, -1 }; + npth_t writer_thread = (npth_t)0; + npth_t reader_thread = (npth_t)0; + gpg_error_t writer_err, reader_err; + char line[ASSUAN_LINELENGTH]; + strlist_t sl; + int ret; + + /* Make sure that either the stream interface xor the buffer + interface is used. */ + assert ((plain == NULL) != (plain_stream == NULL)); + assert ((reader_mb == NULL) != (cipher_stream == NULL)); + + /* Create two pipes. */ + err = gnupg_create_outbound_pipe (outbound_fds, NULL, 0); + if (!err) + err = gnupg_create_inbound_pipe (inbound_fds, NULL, 0); + if (err) + { + log_error (_("error creating a pipe: %s\n"), gpg_strerror (err)); + goto leave; + } + + /* Start GPG and send the INPUT and OUTPUT commands. */ + err = start_gpg (ctrl, gpg_program, gpg_arguments, + outbound_fds[0], inbound_fds[1], &ctx); + if (err) + goto leave; + close (outbound_fds[0]); outbound_fds[0] = -1; + close (inbound_fds[1]); inbound_fds[1] = -1; + + /* Start a writer thread to feed the INPUT command of the server. */ + err = start_writer (outbound_fds[1], plain, plainlen, plain_stream, + &writer_thread, &writer_err); + if (err) + return err; + outbound_fds[1] = -1; /* The thread owns the FD now. */ + + /* Start a reader thread to eat from the OUTPUT command of the + server. */ + err = start_reader (inbound_fds[0], reader_mb, cipher_stream, + &reader_thread, &reader_err); + if (err) + return err; + outbound_fds[0] = -1; /* The thread owns the FD now. */ + + /* Run the encryption. */ + for (sl = keys; sl; sl = sl->next) + { + snprintf (line, sizeof line, "RECIPIENT -- %s", sl->d); + err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); + if (err) + { + log_error ("the engine's RECIPIENT command failed: %s <%s>\n", + gpg_strerror (err), gpg_strsource (err)); + goto leave; + } + } + + err = assuan_transact (ctx, "ENCRYPT", NULL, NULL, NULL, NULL, NULL, NULL); + if (err) + { + log_error ("the engine's ENCRYPT command failed: %s <%s>\n", + gpg_strerror (err), gpg_strsource (err)); + goto leave; + } + + /* Wait for reader and return the data. */ + ret = npth_join (reader_thread, NULL); + if (ret) + { + err = my_error_from_errno (ret); + log_error ("waiting for reader thread failed: %s\n", gpg_strerror (err)); + goto leave; + } + /* FIXME: Not really valid, as npth_t is an opaque type. */ + memset (&reader_thread, '\0', sizeof (reader_thread)); + if (reader_err) + { + err = reader_err; + log_error ("read error in reader thread: %s\n", gpg_strerror (err)); + goto leave; + } + + /* Wait for the writer to catch a writer error. */ + ret = npth_join (writer_thread, NULL); + if (ret) + { + err = my_error_from_errno (ret); + log_error ("waiting for writer thread failed: %s\n", gpg_strerror (err)); + goto leave; + } + memset (&writer_thread, '\0', sizeof (writer_thread)); + if (writer_err) + { + err = writer_err; + log_error ("write error in writer thread: %s\n", gpg_strerror (err)); + goto leave; + } + + leave: + /* FIXME: Not valid, as npth_t is an opaque type. */ + if (reader_thread) + npth_detach (reader_thread); + if (writer_thread) + npth_detach (writer_thread); + if (outbound_fds[0] != -1) + close (outbound_fds[0]); + if (outbound_fds[1] != -1) + close (outbound_fds[1]); + if (inbound_fds[0] != -1) + close (inbound_fds[0]); + if (inbound_fds[1] != -1) + close (inbound_fds[1]); + release_gpg (ctx); + return err; +} + +gpg_error_t +gpg_encrypt_blob (ctrl_t ctrl, + const char *gpg_program, + strlist_t gpg_arguments, + const void *plain, size_t plainlen, + strlist_t keys, + void **r_ciph, size_t *r_ciphlen) +{ + gpg_error_t err; + membuf_t reader_mb; + + *r_ciph = NULL; + *r_ciphlen = 0; + + /* Init the memory buffer to receive the encrypted stuff. */ + init_membuf (&reader_mb, 4096); + + err = _gpg_encrypt (ctrl, gpg_program, gpg_arguments, + plain, plainlen, NULL, + keys, + &reader_mb, NULL); + + if (! err) + { + /* Return the data. */ + *r_ciph = get_membuf (&reader_mb, r_ciphlen); + if (!*r_ciph) + { + err = my_error_from_syserror (); + log_error ("error while storing the data in the reader thread: %s\n", + gpg_strerror (err)); + } + } + + xfree (get_membuf (&reader_mb, NULL)); + return err; +} + +gpg_error_t +gpg_encrypt_stream (ctrl_t ctrl, + const char *gpg_program, + strlist_t gpg_arguments, + estream_t plain_stream, + strlist_t keys, + estream_t cipher_stream) +{ + return _gpg_encrypt (ctrl, gpg_program, gpg_arguments, + NULL, 0, plain_stream, + keys, + NULL, cipher_stream); +} + +/* Call GPG to decrypt a block of data. + + + */ +static gpg_error_t +_gpg_decrypt (ctrl_t ctrl, + const char *gpg_program, + strlist_t gpg_arguments, + const void *ciph, size_t ciphlen, + estream_t cipher_stream, + membuf_t *reader_mb, + estream_t plain_stream) +{ + gpg_error_t err; + assuan_context_t ctx = NULL; + int outbound_fds[2] = { -1, -1 }; + int inbound_fds[2] = { -1, -1 }; + npth_t writer_thread = (npth_t)0; + npth_t reader_thread = (npth_t)0; + gpg_error_t writer_err, reader_err; + int ret; + + /* Make sure that either the stream interface xor the buffer + interface is used. */ + assert ((ciph == NULL) != (cipher_stream == NULL)); + assert ((reader_mb == NULL) != (plain_stream == NULL)); + + /* Create two pipes. */ + err = gnupg_create_outbound_pipe (outbound_fds, NULL, 0); + if (!err) + err = gnupg_create_inbound_pipe (inbound_fds, NULL, 0); + if (err) + { + log_error (_("error creating a pipe: %s\n"), gpg_strerror (err)); + goto leave; + } + + /* Start GPG and send the INPUT and OUTPUT commands. */ + err = start_gpg (ctrl, gpg_program, gpg_arguments, + outbound_fds[0], inbound_fds[1], &ctx); + if (err) + goto leave; + close (outbound_fds[0]); outbound_fds[0] = -1; + close (inbound_fds[1]); inbound_fds[1] = -1; + + /* Start a writer thread to feed the INPUT command of the server. */ + err = start_writer (outbound_fds[1], ciph, ciphlen, cipher_stream, + &writer_thread, &writer_err); + if (err) + return err; + outbound_fds[1] = -1; /* The thread owns the FD now. */ + + /* Start a reader thread to eat from the OUTPUT command of the + server. */ + err = start_reader (inbound_fds[0], reader_mb, plain_stream, + &reader_thread, &reader_err); + if (err) + return err; + outbound_fds[0] = -1; /* The thread owns the FD now. */ + + /* Run the decryption. */ + err = assuan_transact (ctx, "DECRYPT", NULL, NULL, NULL, NULL, NULL, NULL); + if (err) + { + log_error ("the engine's DECRYPT command failed: %s <%s>\n", + gpg_strerror (err), gpg_strsource (err)); + goto leave; + } + + /* Wait for reader and return the data. */ + ret = npth_join (reader_thread, NULL); + if (ret) + { + err = my_error_from_errno (ret); + log_error ("waiting for reader thread failed: %s\n", gpg_strerror (err)); + goto leave; + } + memset (&reader_thread, '\0', sizeof (reader_thread)); + if (reader_err) + { + err = reader_err; + log_error ("read error in reader thread: %s\n", gpg_strerror (err)); + goto leave; + } + + /* Wait for the writer to catch a writer error. */ + ret = npth_join (writer_thread, NULL); + if (ret) + { + err = my_error_from_errno (ret); + log_error ("waiting for writer thread failed: %s\n", gpg_strerror (err)); + goto leave; + } + memset (&writer_thread, '\0', sizeof (writer_thread)); + if (writer_err) + { + err = writer_err; + log_error ("write error in writer thread: %s\n", gpg_strerror (err)); + goto leave; + } + + leave: + if (reader_thread) + npth_detach (reader_thread); + if (writer_thread) + npth_detach (writer_thread); + if (outbound_fds[0] != -1) + close (outbound_fds[0]); + if (outbound_fds[1] != -1) + close (outbound_fds[1]); + if (inbound_fds[0] != -1) + close (inbound_fds[0]); + if (inbound_fds[1] != -1) + close (inbound_fds[1]); + release_gpg (ctx); + return err; +} + +gpg_error_t +gpg_decrypt_blob (ctrl_t ctrl, + const char *gpg_program, + strlist_t gpg_arguments, + const void *ciph, size_t ciphlen, + void **r_plain, size_t *r_plainlen) +{ + gpg_error_t err; + membuf_t reader_mb; + + *r_plain = NULL; + *r_plainlen = 0; + + /* Init the memory buffer to receive the encrypted stuff. */ + init_membuf_secure (&reader_mb, 1024); + + err = _gpg_decrypt (ctrl, gpg_program, gpg_arguments, + ciph, ciphlen, NULL, + &reader_mb, NULL); + + if (! err) + { + /* Return the data. */ + *r_plain = get_membuf (&reader_mb, r_plainlen); + if (!*r_plain) + { + err = my_error_from_syserror (); + log_error ("error while storing the data in the reader thread: %s\n", + gpg_strerror (err)); + } + } + + xfree (get_membuf (&reader_mb, NULL)); + return err; +} + +gpg_error_t +gpg_decrypt_stream (ctrl_t ctrl, + const char *gpg_program, + strlist_t gpg_arguments, + estream_t cipher_stream, + estream_t plain_stream) +{ + return _gpg_decrypt (ctrl, gpg_program, gpg_arguments, + NULL, 0, cipher_stream, + NULL, plain_stream); +} diff --git a/common/call-gpg.h b/common/call-gpg.h new file mode 100644 index 0000000..fd7d2e6 --- /dev/null +++ b/common/call-gpg.h @@ -0,0 +1,54 @@ +/* call-gpg.h - Defs for the communication with GPG + * Copyright (C) 2009 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef GNUPG_COMMON_CALL_GPG_H +#define GNUPG_COMMON_CALL_GPG_H + +#include <gpg-error.h> + +#include "fwddecl.h" +#include "strlist.h" + +gpg_error_t gpg_encrypt_blob (ctrl_t ctrl, + const char *gpg_program, + strlist_t gpg_arguments, + const void *plain, size_t plainlen, + strlist_t keys, + void **r_ciph, size_t *r_ciphlen); + +gpg_error_t gpg_encrypt_stream (ctrl_t ctrl, + const char *gpg_program, + strlist_t gpg_arguments, + estream_t plain_stream, + strlist_t keys, + estream_t cipher_stream); + +gpg_error_t gpg_decrypt_blob (ctrl_t ctrl, + const char *gpg_program, + strlist_t gpg_arguments, + const void *ciph, size_t ciphlen, + void **r_plain, size_t *r_plainlen); + +gpg_error_t gpg_decrypt_stream (ctrl_t ctrl, + const char *gpg_program, + strlist_t gpg_arguments, + estream_t cipher_stream, + estream_t plain_stream); + +#endif /*GNUPG_COMMON_CALL_GPG_H*/ diff --git a/common/ccparray.c b/common/ccparray.c new file mode 100644 index 0000000..ff3eb40 --- /dev/null +++ b/common/ccparray.c @@ -0,0 +1,148 @@ +/* ccparray.c - A simple dynamic array for character pointer. + * Copyright (C) 2016 g10 Code GmbH + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdlib.h> +#include <errno.h> +#include <stdarg.h> + +#include "util.h" +#include "ccparray.h" + + +/* A simple implementation of a dynamic array of const char pointers. + * The example code: + * + * ccparray_t ccp; + * const char **argv; + * int i; + * + * ccparray_init (&ccp, 0); + * ccparray_put (&ccp, "First arg"); + * ccparray_put (&ccp, "Second arg"); + * ccparray_put (&ccp, NULL); + * ccparray_put (&ccp, "Fourth arg"); + * argv = ccparray_get (&ccp, NULL); + * if (!argv) + * die ("error building array: %s\n", strerror (errno)); + * for (i=0; argv[i]; i++) + * printf ("[%d] = '%s'\n", i, argv[i]); + * xfree (argv); + * + * will result in this output: + * + * [0] = 'First arg' + * [1] = 'Second arg' + * + * Note that allocation errors are detected but only returned with the + * final ccparray_get(); this helps not to clutter the code with out + * of core checks. + */ + +void +ccparray_init (ccparray_t *cpa, unsigned int initialsize) +{ + if (!initialsize) + cpa->size = 16; + else if (initialsize < (1<<16)) + cpa->size = initialsize; + else + cpa->size = (1<<16); + + cpa->count = 0; + cpa->out_of_core = 0; + cpa->array = xtrycalloc (cpa->size, sizeof *cpa->array); + if (!cpa->array) + cpa->out_of_core = errno; +} + + +void +ccparray_put (ccparray_t *cpa, const char *value) +{ + if (cpa->out_of_core) + return; + + if (cpa->count + 1 >= cpa->size) + { + const char **newarray; + size_t n, newsize; + + if (cpa->size < 8) + newsize = 16; + else if (cpa->size < 4096) + newsize = 2 * cpa->size; + else if (cpa->size < (1<<16)) + newsize = cpa->size + 2048; + else + { + cpa->out_of_core = ENOMEM; + return; + } + + newarray = xtrycalloc (newsize, sizeof *newarray); + if (!newarray) + { + cpa->out_of_core = errno ? errno : ENOMEM; + return; + } + for (n=0; n < cpa->size; n++) + newarray[n] = cpa->array[n]; + xfree (cpa->array); + cpa->array = newarray; + cpa->size = newsize; + + } + cpa->array[cpa->count++] = value; +} + + +const char ** +ccparray_get (ccparray_t *cpa, size_t *r_count) +{ + const char **result; + + if (cpa->out_of_core) + { + if (cpa->array) + { + xfree (cpa->array); + cpa->array = NULL; + } + gpg_err_set_errno (cpa->out_of_core); + return NULL; + } + + result= cpa->array; + if (r_count) + *r_count = cpa->count; + cpa->array = NULL; + cpa->out_of_core = ENOMEM; /* hack to make sure it won't get reused. */ + return result; +} diff --git a/common/ccparray.h b/common/ccparray.h new file mode 100644 index 0000000..1ecf95b --- /dev/null +++ b/common/ccparray.h @@ -0,0 +1,51 @@ +/* ccparray.c - A simple dynamic array for character pointer. + * Copyright (C) 2016 g10 Code GmbH + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef GNUPG_COMMON_CCPARRAY_H +#define GNUPG_COMMON_CCPARRAY_H + +/* The definition of the structure is private, we only need it here, + * so it can be allocated on the stack. */ +struct _ccparray_private_s +{ + unsigned int count; + unsigned int size; + int out_of_core; + const char **array; +}; + +typedef struct _ccparray_private_s ccparray_t; + + +void ccparray_init (ccparray_t *cpa, unsigned int initialsize); +void ccparray_put (ccparray_t *cpa, const char *value); +const char **ccparray_get (ccparray_t *cpa, size_t *r_nelems); + + +#endif /*GNUPG_COMMON_CCPARRAY_H*/ diff --git a/common/common-defs.h b/common/common-defs.h new file mode 100644 index 0000000..b1928e6 --- /dev/null +++ b/common/common-defs.h @@ -0,0 +1,54 @@ +/* common-defs.h - Private declarations for common/ + * Copyright (C) 2006 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef GNUPG_COMMON_COMMON_DEFS_H +#define GNUPG_COMMON_COMMON_DEFS_H + + +/* Dummy replacement for getenv. */ +#ifndef HAVE_GETENV +#define getenv(a) (NULL) +#endif + +#ifdef HAVE_W32CE_SYSTEM +#define getpid() GetCurrentProcessId () +#endif + + +/*-- ttyio.c --*/ +void tty_private_set_rl_hooks (void (*init_stream) (FILE *), + void (*set_completer) (rl_completion_func_t*), + void (*inhibit_completion) (int), + void (*cleanup_after_signal) (void), + char *(*readline_fun) (const char*), + void (*add_history_fun) (const char*)); + + + +#endif /*GNUPG_COMMON_COMMON_DEFS_H*/ diff --git a/common/compliance.c b/common/compliance.c new file mode 100644 index 0000000..eaecee7 --- /dev/null +++ b/common/compliance.c @@ -0,0 +1,702 @@ +/* compliance.c - Functions for compliance modi + * Copyright (C) 2017 g10 Code GmbH + * Copyright (C) 2017 Bundesamt für Sicherheit in der Informationstechnik + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <gcrypt.h> + +#include "openpgpdefs.h" +#include "logging.h" +#include "util.h" +#include "i18n.h" +#include "compliance.h" + +static int initialized; +static int module; + +/* This value is used by DSA and RSA checks in addition to the hard + * coded length checks. It allows to increase the required key length + * using a confue file. */ +static unsigned int min_compliant_rsa_length; + +/* Return the address of a compliance cache variable for COMPLIANCE. + * If no such variable exists NULL is returned. FOR_RNG returns the + * cache variable for the RNG compliance check. */ +static int * +get_compliance_cache (enum gnupg_compliance_mode compliance, int for_rng) +{ + static int r_gnupg = -1, s_gnupg = -1; + static int r_rfc4880 = -1, s_rfc4880 = -1; + static int r_rfc2440 = -1, s_rfc2440 = -1; + static int r_pgp6 = -1, s_pgp6 = -1; + static int r_pgp7 = -1, s_pgp7 = -1; + static int r_pgp8 = -1, s_pgp8 = -1; + static int r_de_vs = -1, s_de_vs = -1; + + int *ptr = NULL; + + switch (compliance) + { + case CO_GNUPG: ptr = for_rng? &r_gnupg : &s_gnupg ; break; + case CO_RFC4880: ptr = for_rng? &r_rfc4880 : &s_rfc4880; break; + case CO_RFC2440: ptr = for_rng? &r_rfc2440 : &s_rfc2440; break; + case CO_PGP6: ptr = for_rng? &r_pgp6 : &s_pgp6 ; break; + case CO_PGP7: ptr = for_rng? &r_pgp7 : &s_pgp7 ; break; + case CO_PGP8: ptr = for_rng? &r_pgp8 : &s_pgp8 ; break; + case CO_DE_VS: ptr = for_rng? &r_de_vs : &s_de_vs ; break; + } + + return ptr; +} + + +/* Initializes the module. Must be called with the current + * GNUPG_MODULE_NAME. Checks a few invariants, and tunes the policies + * for the given module. */ +void +gnupg_initialize_compliance (int gnupg_module_name) +{ + log_assert (! initialized); + + /* We accept both OpenPGP-style and gcrypt-style algorithm ids. + * Assert that they are compatible. */ + log_assert ((int) GCRY_PK_RSA == (int) PUBKEY_ALGO_RSA); + log_assert ((int) GCRY_PK_RSA_E == (int) PUBKEY_ALGO_RSA_E); + log_assert ((int) GCRY_PK_RSA_S == (int) PUBKEY_ALGO_RSA_S); + log_assert ((int) GCRY_PK_ELG_E == (int) PUBKEY_ALGO_ELGAMAL_E); + log_assert ((int) GCRY_PK_DSA == (int) PUBKEY_ALGO_DSA); + log_assert ((int) GCRY_PK_ECC == (int) PUBKEY_ALGO_ECDH); + log_assert ((int) GCRY_PK_ELG == (int) PUBKEY_ALGO_ELGAMAL); + log_assert ((int) GCRY_CIPHER_NONE == (int) CIPHER_ALGO_NONE); + log_assert ((int) GCRY_CIPHER_IDEA == (int) CIPHER_ALGO_IDEA); + log_assert ((int) GCRY_CIPHER_3DES == (int) CIPHER_ALGO_3DES); + log_assert ((int) GCRY_CIPHER_CAST5 == (int) CIPHER_ALGO_CAST5); + log_assert ((int) GCRY_CIPHER_BLOWFISH == (int) CIPHER_ALGO_BLOWFISH); + log_assert ((int) GCRY_CIPHER_AES == (int) CIPHER_ALGO_AES); + log_assert ((int) GCRY_CIPHER_AES192 == (int) CIPHER_ALGO_AES192); + log_assert ((int) GCRY_CIPHER_AES256 == (int) CIPHER_ALGO_AES256); + log_assert ((int) GCRY_CIPHER_TWOFISH == (int) CIPHER_ALGO_TWOFISH); + log_assert ((int) GCRY_MD_MD5 == (int) DIGEST_ALGO_MD5); + log_assert ((int) GCRY_MD_SHA1 == (int) DIGEST_ALGO_SHA1); + log_assert ((int) GCRY_MD_RMD160 == (int) DIGEST_ALGO_RMD160); + log_assert ((int) GCRY_MD_SHA256 == (int) DIGEST_ALGO_SHA256); + log_assert ((int) GCRY_MD_SHA384 == (int) DIGEST_ALGO_SHA384); + log_assert ((int) GCRY_MD_SHA512 == (int) DIGEST_ALGO_SHA512); + log_assert ((int) GCRY_MD_SHA224 == (int) DIGEST_ALGO_SHA224); + + switch (gnupg_module_name) + { + case GNUPG_MODULE_NAME_GPGSM: + case GNUPG_MODULE_NAME_GPG: + break; + + default: + log_assert (!"no policies for this module"); + } + + module = gnupg_module_name; + initialized = 1; +} + +/* Return true if ALGO with a key of KEYLENGTH is compliant to the + * given COMPLIANCE mode. If KEY is not NULL, various bits of + * information will be extracted from it. If CURVENAME is not NULL, it + * is assumed to be the already computed. ALGO may be either an + * OpenPGP-style pubkey_algo_t, or a gcrypt-style enum gcry_pk_algos, + * both are compatible from the point of view of this function. */ +int +gnupg_pk_is_compliant (enum gnupg_compliance_mode compliance, int algo, + unsigned int algo_flags, + gcry_mpi_t key[], unsigned int keylength, + const char *curvename) +{ + enum { is_rsa, is_dsa, is_elg, is_ecc } algotype; + int result = 0; + + if (! initialized) + return 0; + + switch (algo) + { + case PUBKEY_ALGO_RSA: + case PUBKEY_ALGO_RSA_E: + case PUBKEY_ALGO_RSA_S: + algotype = is_rsa; + break; + + case PUBKEY_ALGO_DSA: + algotype = is_dsa; + break; + + case PUBKEY_ALGO_ELGAMAL_E: + algotype = is_elg; + break; + + case PUBKEY_ALGO_ECDH: + case PUBKEY_ALGO_ECDSA: + case PUBKEY_ALGO_EDDSA: + algotype = is_ecc; + break; + + case PUBKEY_ALGO_ELGAMAL: + return 0; /* Signing with Elgamal is not at all supported. */ + + default: /* Unknown. */ + return 0; + } + + if (compliance == CO_DE_VS) + { + char *curve = NULL; + + switch (algotype) + { + case is_elg: + result = 0; + break; + + case is_rsa: + result = ((keylength == 2048 + || keylength == 3072 + || keylength == 4096) + && keylength >= min_compliant_rsa_length); + /* Although rsaPSS was not part of the original evaluation + * we got word that we can claim compliance. */ + (void)algo_flags; + break; + + case is_dsa: + if (key) + { + size_t P = gcry_mpi_get_nbits (key[0]); + size_t Q = gcry_mpi_get_nbits (key[1]); + result = (Q == 256 + && (P == 2048 || P == 3072) + && P >= min_compliant_rsa_length); + } + break; + + case is_ecc: + if (!curvename && key) + { + curve = openpgp_oid_to_str (key[0]); + curvename = openpgp_oid_to_curve (curve, 0); + if (!curvename) + curvename = curve; + } + + result = (curvename + && (algo == PUBKEY_ALGO_ECDH + || algo == PUBKEY_ALGO_ECDSA) + && (!strcmp (curvename, "brainpoolP256r1") + || !strcmp (curvename, "brainpoolP384r1") + || !strcmp (curvename, "brainpoolP512r1"))); + break; + + default: + result = 0; + } + xfree (curve); + } + else + { + result = 1; /* Assume compliance. */ + } + + return result; +} + + +/* Return true if ALGO with the given KEYLENGTH is allowed in the + * given COMPLIANCE mode. USE specifies for which use case the + * predicate is evaluated. This way policies can be strict in what + * they produce, and liberal in what they accept. */ +int +gnupg_pk_is_allowed (enum gnupg_compliance_mode compliance, + enum pk_use_case use, int algo, + unsigned int algo_flags, gcry_mpi_t key[], + unsigned int keylength, const char *curvename) +{ + int result = 0; + + if (! initialized) + return 1; + + switch (compliance) + { + case CO_DE_VS: + switch (algo) + { + case PUBKEY_ALGO_RSA: + case PUBKEY_ALGO_RSA_E: + case PUBKEY_ALGO_RSA_S: + switch (use) + { + case PK_USE_DECRYPTION: + case PK_USE_VERIFICATION: + result = 1; + break; + case PK_USE_ENCRYPTION: + case PK_USE_SIGNING: + result = ((keylength == 2048 + || keylength == 3072 + || keylength == 4096) + && keylength >= min_compliant_rsa_length); + break; + default: + log_assert (!"reached"); + } + (void)algo_flags; + break; + + case PUBKEY_ALGO_DSA: + if (use == PK_USE_VERIFICATION) + result = 1; + else if (use == PK_USE_SIGNING && key) + { + size_t P = gcry_mpi_get_nbits (key[0]); + size_t Q = gcry_mpi_get_nbits (key[1]); + result = (Q == 256 + && (P == 2048 || P == 3072) + && keylength >= min_compliant_rsa_length); + } + break; + + case PUBKEY_ALGO_ELGAMAL: + case PUBKEY_ALGO_ELGAMAL_E: + result = (use == PK_USE_DECRYPTION); + break; + + case PUBKEY_ALGO_ECDH: + if (use == PK_USE_DECRYPTION) + result = 1; + else if (use == PK_USE_ENCRYPTION) + { + char *curve = NULL; + + if (!curvename && key) + { + curve = openpgp_oid_to_str (key[0]); + curvename = openpgp_oid_to_curve (curve, 0); + if (!curvename) + curvename = curve; + } + + result = (curvename + && (!strcmp (curvename, "brainpoolP256r1") + || !strcmp (curvename, "brainpoolP384r1") + || !strcmp (curvename, "brainpoolP512r1"))); + + xfree (curve); + } + break; + + case PUBKEY_ALGO_ECDSA: + if (use == PK_USE_VERIFICATION) + result = 1; + else + { + char *curve = NULL; + + if (! curvename && key) + { + curve = openpgp_oid_to_str (key[0]); + curvename = openpgp_oid_to_curve (curve, 0); + if (!curvename) + curvename = curve; + } + + result = (use == PK_USE_SIGNING + && curvename + && (!strcmp (curvename, "brainpoolP256r1") + || !strcmp (curvename, "brainpoolP384r1") + || !strcmp (curvename, "brainpoolP512r1"))); + xfree (curve); + } + break; + + + case PUBKEY_ALGO_EDDSA: + break; + + default: + break; + } + break; + + default: + /* The default policy is to allow all algorithms. */ + result = 1; + } + + return result; +} + + +/* Return true if (CIPHER, MODE) is compliant to the given COMPLIANCE mode. */ +int +gnupg_cipher_is_compliant (enum gnupg_compliance_mode compliance, + cipher_algo_t cipher, + enum gcry_cipher_modes mode) +{ + if (! initialized) + return 0; + + switch (compliance) + { + case CO_DE_VS: + switch (cipher) + { + case CIPHER_ALGO_AES: + case CIPHER_ALGO_AES192: + case CIPHER_ALGO_AES256: + case CIPHER_ALGO_3DES: + switch (module) + { + case GNUPG_MODULE_NAME_GPG: + return mode == GCRY_CIPHER_MODE_CFB; + case GNUPG_MODULE_NAME_GPGSM: + return mode == GCRY_CIPHER_MODE_CBC; + } + log_assert (!"reached"); + + default: + return 0; + } + log_assert (!"reached"); + + default: + return 0; + } + + log_assert (!"reached"); +} + + +/* Return true if CIPHER is allowed in the given COMPLIANCE mode. If + * PRODUCER is true, the predicate is evaluated for the producer, if + * false for the consumer. This way policies can be strict in what + * they produce, and liberal in what they accept. */ +int +gnupg_cipher_is_allowed (enum gnupg_compliance_mode compliance, int producer, + cipher_algo_t cipher, + enum gcry_cipher_modes mode) +{ + if (! initialized) + return 1; + + switch (compliance) + { + case CO_DE_VS: + switch (cipher) + { + case CIPHER_ALGO_AES: + case CIPHER_ALGO_AES192: + case CIPHER_ALGO_AES256: + case CIPHER_ALGO_3DES: + switch (module) + { + case GNUPG_MODULE_NAME_GPG: + return (mode == GCRY_CIPHER_MODE_NONE + || mode == GCRY_CIPHER_MODE_CFB); + case GNUPG_MODULE_NAME_GPGSM: + return (mode == GCRY_CIPHER_MODE_NONE + || mode == GCRY_CIPHER_MODE_CBC + || (mode == GCRY_CIPHER_MODE_GCM && !producer)); + } + log_assert (!"reached"); + + case CIPHER_ALGO_BLOWFISH: + case CIPHER_ALGO_CAMELLIA128: + case CIPHER_ALGO_CAMELLIA192: + case CIPHER_ALGO_CAMELLIA256: + case CIPHER_ALGO_CAST5: + case CIPHER_ALGO_IDEA: + case CIPHER_ALGO_TWOFISH: + return (module == GNUPG_MODULE_NAME_GPG + && (mode == GCRY_CIPHER_MODE_NONE + || mode == GCRY_CIPHER_MODE_CFB) + && ! producer); + default: + return 0; + } + log_assert (!"reached"); + + default: + /* The default policy is to allow all algorithms. */ + return 1; + } + + log_assert (!"reached"); +} + + +/* Return true if DIGEST is compliant to the given COMPLIANCE mode. */ +int +gnupg_digest_is_compliant (enum gnupg_compliance_mode compliance, + digest_algo_t digest) +{ + if (! initialized) + return 0; + + switch (compliance) + { + case CO_DE_VS: + switch (digest) + { + case DIGEST_ALGO_SHA256: + case DIGEST_ALGO_SHA384: + case DIGEST_ALGO_SHA512: + return 1; + default: + return 0; + } + log_assert (!"reached"); + + default: + return 0; + } + + log_assert (!"reached"); +} + + +/* Return true if DIGEST is allowed in the given COMPLIANCE mode. If + * PRODUCER is true, the predicate is evaluated for the producer, if + * false for the consumer. This way policies can be strict in what + * they produce, and liberal in what they accept. */ +int +gnupg_digest_is_allowed (enum gnupg_compliance_mode compliance, int producer, + digest_algo_t digest) +{ + if (! initialized) + return 1; + + switch (compliance) + { + case CO_DE_VS: + switch (digest) + { + case DIGEST_ALGO_SHA256: + case DIGEST_ALGO_SHA384: + case DIGEST_ALGO_SHA512: + return 1; + case DIGEST_ALGO_SHA1: + case DIGEST_ALGO_SHA224: + case DIGEST_ALGO_RMD160: + return ! producer; + case DIGEST_ALGO_MD5: + return ! producer && module == GNUPG_MODULE_NAME_GPGSM; + default: + return 0; + } + log_assert (!"reached"); + + default: + /* The default policy is to allow all algorithms. */ + return 1; + } + + log_assert (!"reached"); +} + + +/* Return True if the random number generator is compliant in + * COMPLIANCE mode. */ +int +gnupg_rng_is_compliant (enum gnupg_compliance_mode compliance) +{ + int *result; + int res; + + result = get_compliance_cache (compliance, 1); + + if (result && *result != -1) + res = *result; /* Use cached result. */ + else if (compliance == CO_DE_VS) + { + /* We also check whether the library is at all compliant. */ + res = gnupg_gcrypt_is_compliant (compliance); + + /* In DE_VS mode under Windows we also require that the JENT RNG + * is active. Check it here. */ +#ifdef HAVE_W32_SYSTEM + if (res == 1) + { + char *buf; + char *fields[5]; + + buf = gcry_get_config (0, "rng-type"); + if (buf + && split_fields_colon (buf, fields, DIM (fields)) >= 5 + && atoi (fields[4]) > 0) + ; /* Field 5 > 0 := Jent is active. */ + else + result = 0; /* Force non-compliance. */ + gcry_free (buf); + } +#endif /*HAVE_W32_SYSTEM*/ + } + else + res = 1; + + if (result) + *result = res; + + return res; +} + + +/* Return true if the used Libgcrypt is compliant in COMPLIANCE + * mode. */ +int +gnupg_gcrypt_is_compliant (enum gnupg_compliance_mode compliance) +{ + int *result; + int res; + + result = get_compliance_cache (compliance, 0); + + if (result && *result != -1) + res = *result; /* Use cached result. */ + else if (compliance == CO_DE_VS) + { + int is19orlater = !!gcry_check_version ("1.9.0"); + + /* A compliant version of GnuPG requires Libgcrypt >= 1.8.1 and + * less than 1.9.0. Version 1.9.0 requires a re-evaluation and + * can thus not be used for de-vs. */ + if (gcry_check_version ("1.8.1") && !is19orlater) + res = 1; /* Compliant version of Libgcrypt. */ + else if (is19orlater) + { + /* Libgcrypt might be nice enough to tell us whether it is + * compliant. */ + char *buf; + char *fields[3]; + + buf = gcry_get_config (0, "compliance"); + if (buf + && split_fields_colon (buf, fields, DIM (fields)) >= 2 + && strstr (fields[1], "de-vs")) + res = 1; /* Compliant. */ + else + res = 0; /* Non-compliant. */ + gcry_free (buf); + } + else + res = 0; /* Non-compliant version of Libgcrypt. */ + } + else + res = 1; + + if (result) + *result = res; + + return res; +} + + +const char * +gnupg_status_compliance_flag (enum gnupg_compliance_mode compliance) +{ + switch (compliance) + { + case CO_GNUPG: + return "8"; + case CO_RFC4880: + case CO_RFC2440: + case CO_PGP6: + case CO_PGP7: + case CO_PGP8: + log_assert (!"no status code assigned for this compliance mode"); + case CO_DE_VS: + return "23"; + } + log_assert (!"invalid compliance mode"); +} + + +/* Parse the value of --compliance. Returns the value corresponding + * to the given STRING according to OPTIONS of size LENGTH, or -1 + * indicating that the lookup was unsuccessful, or the list of options + * was printed. If quiet is false, an additional hint to use 'help' + * is printed on unsuccessful lookups. */ +int +gnupg_parse_compliance_option (const char *string, + struct gnupg_compliance_option options[], + size_t length, + int quiet) +{ + size_t i; + + if (! ascii_strcasecmp (string, "help")) + { + log_info (_("valid values for option '%s':\n"), "--compliance"); + for (i = 0; i < length; i++) + log_info (" %s\n", options[i].keyword); + return -1; + } + + for (i = 0; i < length; i++) + if (! ascii_strcasecmp (string, options[i].keyword)) + return options[i].value; + + log_error (_("invalid value for option '%s'\n"), "--compliance"); + if (! quiet) + log_info (_("(use \"help\" to list choices)\n")); + return -1; +} + + +/* Return the command line option for the given COMPLIANCE mode. */ +const char * +gnupg_compliance_option_string (enum gnupg_compliance_mode compliance) +{ + switch (compliance) + { + case CO_GNUPG: return "--compliance=gnupg"; + case CO_RFC4880: return "--compliance=openpgp"; + case CO_RFC2440: return "--compliance=rfc2440"; + case CO_PGP6: return "--compliance=pgp6"; + case CO_PGP7: return "--compliance=pgp7"; + case CO_PGP8: return "--compliance=pgp8"; + case CO_DE_VS: return "--compliance=de-vs"; + } + + log_assert (!"invalid compliance mode"); +} + + +/* Set additional infos for example taken from config files at startup. */ +void +gnupg_set_compliance_extra_info (unsigned int min_rsa) +{ + min_compliant_rsa_length = min_rsa; +} diff --git a/common/compliance.h b/common/compliance.h new file mode 100644 index 0000000..e29ff4e --- /dev/null +++ b/common/compliance.h @@ -0,0 +1,97 @@ +/* compliance.h - Definitions for compliance modi + * Copyright (C) 2017 g10 Code GmbH + * Copyright (C) 2017 Bundesamt für Sicherheit in der Informationstechnik + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef GNUPG_COMMON_COMPLIANCE_H +#define GNUPG_COMMON_COMPLIANCE_H + +#include <gcrypt.h> +#include "openpgpdefs.h" + +void gnupg_initialize_compliance (int gnupg_module_name); + +enum gnupg_compliance_mode + { + CO_GNUPG, CO_RFC4880, CO_RFC2440, + CO_PGP6, CO_PGP7, CO_PGP8, CO_DE_VS + }; + +enum pk_use_case + { + PK_USE_ENCRYPTION, PK_USE_DECRYPTION, + PK_USE_SIGNING, PK_USE_VERIFICATION, + }; + +/* Flags to distinguish public key algorithm variants. */ +#define PK_ALGO_FLAG_RSAPSS 1 /* Use rsaPSS padding. */ + + +int gnupg_pk_is_compliant (enum gnupg_compliance_mode compliance, int algo, + unsigned int algo_flags, + gcry_mpi_t key[], unsigned int keylength, + const char *curvename); +int gnupg_pk_is_allowed (enum gnupg_compliance_mode compliance, + enum pk_use_case use, int algo, + unsigned int algo_flags, gcry_mpi_t key[], + unsigned int keylength, const char *curvename); +int gnupg_cipher_is_compliant (enum gnupg_compliance_mode compliance, + cipher_algo_t cipher, + enum gcry_cipher_modes mode); +int gnupg_cipher_is_allowed (enum gnupg_compliance_mode compliance, + int producer, + cipher_algo_t cipher, + enum gcry_cipher_modes mode); +int gnupg_digest_is_compliant (enum gnupg_compliance_mode compliance, + digest_algo_t digest); +int gnupg_digest_is_allowed (enum gnupg_compliance_mode compliance, + int producer, + digest_algo_t digest); +int gnupg_rng_is_compliant (enum gnupg_compliance_mode compliance); +int gnupg_gcrypt_is_compliant (enum gnupg_compliance_mode compliance); + +const char *gnupg_status_compliance_flag (enum gnupg_compliance_mode + compliance); + +struct gnupg_compliance_option +{ + const char *keyword; + int value; +}; + +int gnupg_parse_compliance_option (const char *string, + struct gnupg_compliance_option options[], + size_t length, + int quiet); +const char *gnupg_compliance_option_string (enum gnupg_compliance_mode + compliance); + +void gnupg_set_compliance_extra_info (unsigned int min_rsa); + + +#endif /*GNUPG_COMMON_COMPLIANCE_H*/ diff --git a/common/convert.c b/common/convert.c new file mode 100644 index 0000000..ee2f117 --- /dev/null +++ b/common/convert.c @@ -0,0 +1,267 @@ +/* convert.c - Hex conversion functions. + * Copyright (C) 2006, 2008 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdlib.h> +#include <errno.h> +#include <ctype.h> + +#include "util.h" + + +#define tohex(n) ((n) < 10 ? ((n) + '0') : (((n) - 10) + 'A')) + + +/* Convert STRING consisting of hex characters into its binary + representation and store that at BUFFER. BUFFER needs to be of + LENGTH bytes. The function checks that the STRING will convert + exactly to LENGTH bytes. The string is delimited by either end of + string or a white space character. The function returns -1 on + error or the length of the parsed string. In-place conversion is + allowed but the Source string might be garbled on error. */ +int +hex2bin (const char *string, void *buffer, size_t length) +{ + int i; + const char *s = string; + + for (i=0; i < length; ) + { + if (!hexdigitp (s) || !hexdigitp (s+1)) + return -1; /* Invalid hex digits. */ + ((unsigned char*)buffer)[i++] = xtoi_2 (s); + s += 2; + } + if (*s && (!isascii (*s) || !isspace (*s)) ) + return -1; /* Not followed by Nul or white space. */ + if (i != length) + return -1; /* Not of expected length. */ + if (*s) + s++; /* Skip the delimiter. */ + return s - string; +} + + +/* Convert STRING consisting of hex characters into its binary representation + and store that at BUFFER. BUFFER needs to be of LENGTH bytes. The + function check that the STRING will convert exactly to LENGTH + bytes. Colons between the hex digits are allowed, if one colon + has been given a colon is expected very 2 characters. The string + is delimited by either end of string or a white space character. + The function returns -1 on error or the length of the parsed + string. */ +int +hexcolon2bin (const char *string, void *buffer, size_t length) +{ + int i; + const char *s = string; + int need_colon = 0; + + for (i=0; i < length; ) + { + if (i==1 && *s == ':') /* Skip colons between hex digits. */ + { + need_colon = 1; + s++; + } + else if (need_colon && *s == ':') + s++; + else if (need_colon) + return -1; /* Colon expected. */ + if (!hexdigitp (s) || !hexdigitp (s+1)) + return -1; /* Invalid hex digits. */ + ((unsigned char*)buffer)[i++] = xtoi_2 (s); + s += 2; + } + if (*s == ':') + return -1; /* Trailing colons are not allowed. */ + if (*s && (!isascii (*s) || !isspace (*s)) ) + return -1; /* Not followed by Nul or white space. */ + if (i != length) + return -1; /* Not of expected length. */ + if (*s) + s++; /* Skip the delimiter. */ + return s - string; +} + + + +static char * +do_bin2hex (const void *buffer, size_t length, char *stringbuf, int with_colon) +{ + const unsigned char *s; + char *p; + + if (!stringbuf) + { + /* Not really correct for with_colon but we don't care about the + one wasted byte. */ + size_t n = with_colon? 3:2; + size_t nbytes = n * length + 1; + if (length && (nbytes-1) / n != length) + { + gpg_err_set_errno (ENOMEM); + return NULL; + } + stringbuf = xtrymalloc (nbytes); + if (!stringbuf) + return NULL; + } + + for (s = buffer, p = stringbuf; length; length--, s++) + { + if (with_colon && s != buffer) + *p++ = ':'; + *p++ = tohex ((*s>>4)&15); + *p++ = tohex (*s&15); + } + *p = 0; + + return stringbuf; +} + + +/* Convert LENGTH bytes of data in BUFFER into hex encoding and store + that at the provided STRINGBUF. STRINGBUF must be allocated of at + least (2*LENGTH+1) bytes or be NULL so that the function mallocs an + appropriate buffer. Returns STRINGBUF or NULL on error (which may + only occur if STRINGBUF has been NULL and the internal malloc + failed). */ +char * +bin2hex (const void *buffer, size_t length, char *stringbuf) +{ + return do_bin2hex (buffer, length, stringbuf, 0); +} + +/* Convert LENGTH bytes of data in BUFFER into hex encoding and store + that at the provided STRINGBUF. STRINGBUF must be allocated of at + least (3*LENGTH+1) bytes or be NULL so that the function mallocs an + appropriate buffer. Returns STRINGBUF or NULL on error (which may + only occur if STRINGBUF has been NULL and the internal malloc + failed). */ +char * +bin2hexcolon (const void *buffer, size_t length, char *stringbuf) +{ + return do_bin2hex (buffer, length, stringbuf, 1); +} + + + +/* Convert HEXSTRING consisting of hex characters into string and + store that at BUFFER. HEXSTRING is either delimited by end of + string or a white space character. The function makes sure that + the resulting string in BUFFER is terminated by a Nul byte. Note + that the returned string may include embedded Nul bytes; the extra + Nul byte at the end is used to make sure tha the result can always + be used as a C-string. + + BUFSIZE is the available length of BUFFER; if the converted result + plus a possible required extra Nul character does not fit into this + buffer, the function returns NULL and won't change the existing + content of BUFFER. In-place conversion is possible as long as + BUFFER points to HEXSTRING. + + If BUFFER is NULL and BUFSIZE is 0 the function scans HEXSTRING but + does not store anything. This may be used to find the end of + HEXSTRING. + + On success the function returns a pointer to the next character + after HEXSTRING (which is either end-of-string or a the next white + space). If BUFLEN is not NULL the number of valid vytes in BUFFER + is stored there (an extra Nul byte is not counted); this will even + be done if BUFFER has been passed as NULL. */ +const char * +hex2str (const char *hexstring, char *buffer, size_t bufsize, size_t *buflen) +{ + const char *s = hexstring; + int idx, count; + int need_nul = 0; + + if (buflen) + *buflen = 0; + + for (s=hexstring, count=0; hexdigitp (s) && hexdigitp (s+1); s += 2, count++) + ; + if (*s && (!isascii (*s) || !isspace (*s)) ) + { + gpg_err_set_errno (EINVAL); + return NULL; /* Not followed by Nul or white space. */ + } + /* We need to append a nul character. However we don't want that if + the hexstring already ends with "00". */ + need_nul = ((s == hexstring) || !(s[-2] == '0' && s[-1] == '0')); + if (need_nul) + count++; + + if (buffer) + { + if (count > bufsize) + { + gpg_err_set_errno (EINVAL); + return NULL; /* Too long. */ + } + + for (s=hexstring, idx=0; hexdigitp (s) && hexdigitp (s+1); s += 2) + ((unsigned char*)buffer)[idx++] = xtoi_2 (s); + if (need_nul) + buffer[idx] = 0; + } + + if (buflen) + *buflen = count - need_nul; + return s; +} + + +/* Same as hex2str but this function allocated a new string. Returns + NULL on error. If R_COUNT is not NULL, the number of scanned bytes + will be stored there. ERRNO is set on error. */ +char * +hex2str_alloc (const char *hexstring, size_t *r_count) +{ + const char *tail; + size_t nbytes; + char *result; + + tail = hex2str (hexstring, NULL, 0, &nbytes); + if (!tail) + { + if (r_count) + *r_count = 0; + return NULL; + } + if (r_count) + *r_count = tail - hexstring; + result = xtrymalloc (nbytes+1); + if (!result) + return NULL; + if (!hex2str (hexstring, result, nbytes+1, NULL)) + BUG (); + return result; +} diff --git a/common/dotlock.c b/common/dotlock.c new file mode 100644 index 0000000..772bda3 --- /dev/null +++ b/common/dotlock.c @@ -0,0 +1,1443 @@ +/* dotlock.c - dotfile locking + * Copyright (C) 1998, 2000, 2001, 2003, 2004, + * 2005, 2006, 2008, 2010, 2011 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute and/or modify this + * part of GnuPG under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * GnuPG is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copies of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, see <https://www.gnu.org/licenses/>. + * + * ALTERNATIVELY, this file may be distributed under the terms of the + * following license, in which case the provisions of this license are + * required INSTEAD OF the GNU Lesser General License or the GNU + * General Public License. If you wish to allow use of your version of + * this file only under the terms of the GNU Lesser General License or + * the GNU General Public License, and not to allow others to use your + * version of this file under the terms of the following license, + * indicate your decision by deleting this paragraph and the license + * below. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + Overview: + ========= + + This module implements advisory file locking in a portable way. + Due to the problems with POSIX fcntl locking a separate lock file + is used. It would be possible to use fcntl locking on this lock + file and thus avoid the weird auto unlock bug of POSIX while still + having an unproved better performance of fcntl locking. However + there are still problems left, thus we resort to use a hardlink + which has the well defined property that a link call will fail if + the target file already exists. + + Given that hardlinks are also available on NTFS file systems since + Windows XP; it will be possible to enhance this module to use + hardlinks even on Windows and thus allow Windows and Posix clients + to use locking on the same directory. This is not yet implemented; + instead we use a lockfile on Windows along with W32 style file + locking. + + On FAT file systems hardlinks are not supported. Thus this method + does not work. Our solution is to use a O_EXCL locking instead. + Querying the type of the file system is not easy to do in a + portable way (e.g. Linux has a statfs, BSDs have a the same call + but using different structures and constants). What we do instead + is to check at runtime whether link(2) works for a specific lock + file. + + + How to use: + =========== + + At program initialization time, the module should be explicitly + initialized: + + dotlock_create (NULL, 0); + + This installs an atexit handler and may also initialize mutex etc. + It is optional for non-threaded applications. Only the first call + has an effect. This needs to be done before any extra threads are + started. + + To create a lock file (which prepares it but does not take the + lock) you do: + + dotlock_t h + + h = dotlock_create (fname, 0); + if (!h) + error ("error creating lock file: %s\n", strerror (errno)); + + It is important to handle the error. For example on a read-only + file system a lock can't be created (but is usually not needed). + FNAME is the file you want to lock; the actual lockfile is that + name with the suffix ".lock" appended. On success a handle to be + used with the other functions is returned or NULL on error. Note + that the handle shall only be used by one thread at a time. This + function creates a unique file temporary file (".#lk*") in the same + directory as FNAME and returns a handle for further operations. + The module keeps track of these unique files so that they will be + unlinked using the atexit handler. If you don't need the lock file + anymore, you may also explicitly remove it with a call to: + + dotlock_destroy (h); + + To actually lock the file, you use: + + if (dotlock_take (h, -1)) + error ("error taking lock: %s\n", strerror (errno)); + + This function will wait until the lock is acquired. If an + unexpected error occurs if will return non-zero and set ERRNO. If + you pass (0) instead of (-1) the function does not wait in case the + file is already locked but returns -1 and sets ERRNO to EACCES. + Any other positive value for the second parameter is considered a + timeout value in milliseconds. + + To release the lock you call: + + if (dotlock_release (h)) + error ("error releasing lock: %s\n", strerror (errno)); + + or, if the lock file is not anymore needed, you may just call + dotlock_destroy. However dotlock_release does some extra checks + before releasing the lock and prints diagnostics to help detecting + bugs. + + If you want to explicitly destroy all lock files you may call + + dotlock_remove_lockfiles (); + + which is the core of the installed atexit handler. In case your + application wants to disable locking completely it may call + + disable_locking () + + before any locks are created. + + There are two convenience functions to store an integer (e.g. a + file descriptor) value with the handle: + + void dotlock_set_fd (dotlock_t h, int fd); + int dotlock_get_fd (dotlock_t h); + + If nothing has been stored dotlock_get_fd returns -1. + + + + How to build: + ============= + + This module was originally developed for GnuPG but later changed to + allow its use without any GnuPG dependency. If you want to use it + with you application you may simply use it and it should figure out + most things automagically. + + You may use the common config.h file to pass macros, but take care + to pass -DHAVE_CONFIG_H to the compiler. Macros used by this + module are: + + DOTLOCK_USE_PTHREAD - Define if POSIX threads are in use. + + DOTLOCK_GLIB_LOGGING - Define this to use Glib logging functions. + + DOTLOCK_EXT_SYM_PREFIX - Prefix all external symbols with the + string to which this macro evaluates. + + GNUPG_MAJOR_VERSION - Defined when used by GnuPG. + + HAVE_DOSISH_SYSTEM - Defined for Windows etc. Will be + automatically defined if a the target is + Windows. + + HAVE_POSIX_SYSTEM - Internally defined to !HAVE_DOSISH_SYSTEM. + + HAVE_SIGNAL_H - Should be defined on Posix systems. If config.h + is not used defaults to defined. + + DIRSEP_C - Separation character for file name parts. + Usually not redefined. + + EXTSEP_S - Separation string for file name suffixes. + Usually not redefined. + + HAVE_W32CE_SYSTEM - Currently only used by GnuPG. + + Note that there is a test program t-dotlock which has compile + instructions at its end. At least for SMBFS and CIFS it is + important that 64 bit versions of stat are used; most programming + environments do this these days, just in case you want to compile + it on the command line, remember to pass -D_FILE_OFFSET_BITS=64 + + + Bugs: + ===== + + On Windows this module is not yet thread-safe. + + + Miscellaneous notes: + ==================== + + On hardlinks: + - Hardlinks are supported under Windows with NTFS since XP/Server2003. + - In Linux 2.6.33 both SMBFS and CIFS seem to support hardlinks. + - NFS supports hard links. But there are solvable problems. + - FAT does not support links + + On the file locking API: + - CIFS on Linux 2.6.33 supports several locking methods. + SMBFS seems not to support locking. No closer checks done. + - NFS supports Posix locks. flock is emulated in the server. + However there are a couple of problems; see below. + - FAT does not support locks. + - An advantage of fcntl locking is that R/W locks can be + implemented which is not easy with a straight lock file. + + On O_EXCL: + - Does not work reliable on NFS + - Should work on CIFS and SMBFS but how can we delete lockfiles? + + On NFS problems: + - Locks vanish if the server crashes and reboots. + - Client crashes keep the lock in the server until the client + re-connects. + - Communication problems may return unreliable error codes. The + MUA Postfix's workaround is to compare the link count after + seeing an error for link. However that gives a race. If using a + unique file to link to a lockfile and using stat to check the + link count instead of looking at the error return of link(2) is + the best solution. + - O_EXCL seems to have a race and may re-create a file anyway. + +*/ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +/* Some quick replacements for stuff we usually expect to be defined + in config.h. Define HAVE_POSIX_SYSTEM for better readability. */ +#if !defined (HAVE_DOSISH_SYSTEM) && defined(_WIN32) +# define HAVE_DOSISH_SYSTEM 1 +#endif +#if !defined (HAVE_DOSISH_SYSTEM) && !defined (HAVE_POSIX_SYSTEM) +# define HAVE_POSIX_SYSTEM 1 +#endif + +/* With no config.h assume that we have sitgnal.h. */ +#if !defined (HAVE_CONFIG_H) && defined (HAVE_POSIX_SYSTEM) +# define HAVE_SIGNAL_H 1 +#endif + +/* Standard headers. */ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <errno.h> +#include <unistd.h> +#ifdef HAVE_DOSISH_SYSTEM +# define WIN32_LEAN_AND_MEAN /* We only need the OS core stuff. */ +# include <windows.h> +#else +# include <sys/types.h> +# include <sys/stat.h> +# include <sys/utsname.h> +#endif +#include <sys/types.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <fcntl.h> +#ifdef HAVE_SIGNAL_H +# include <signal.h> +#endif +#ifdef DOTLOCK_USE_PTHREAD +# include <pthread.h> +#endif + +#ifdef DOTLOCK_GLIB_LOGGING +# include <glib.h> +#endif + +#ifdef GNUPG_MAJOR_VERSION +# include "util.h" +# include "common-defs.h" +# include "stringhelp.h" /* For stpcpy and w32_strerror. */ +#endif +#ifdef HAVE_W32CE_SYSTEM +# include "utf8conv.h" /* WindowsCE requires filename conversion. */ +#endif + +#include "dotlock.h" + + +/* Define constants for file name construction. */ +#if !defined(DIRSEP_C) && !defined(EXTSEP_S) +# ifdef HAVE_DOSISH_SYSTEM +# define DIRSEP_C '\\' +# define EXTSEP_S "." +#else +# define DIRSEP_C '/' +# define EXTSEP_S "." +# endif +#endif + +/* In GnuPG we use wrappers around the malloc functions. If they are + not defined we assume that this code is used outside of GnuPG and + fall back to the regular malloc functions. */ +#ifndef xtrymalloc +# define xtrymalloc(a) malloc ((a)) +# define xtrycalloc(a,b) calloc ((a), (b)) +# define xfree(a) free ((a)) +#endif + +/* Wrapper to set ERRNO (required for W32CE). */ +#ifdef GPG_ERROR_VERSION +# define my_set_errno(e) gpg_err_set_errno ((e)) +#else +# define my_set_errno(e) do { errno = (e); } while (0) +#endif + +/* Gettext macro replacement. */ +#ifndef _ +# define _(a) (a) +#endif + +#ifdef GNUPG_MAJOR_VERSION +# define my_info_0(a) log_info ((a)) +# define my_info_1(a,b) log_info ((a), (b)) +# define my_info_2(a,b,c) log_info ((a), (b), (c)) +# define my_info_3(a,b,c,d) log_info ((a), (b), (c), (d)) +# define my_error_0(a) log_error ((a)) +# define my_error_1(a,b) log_error ((a), (b)) +# define my_error_2(a,b,c) log_error ((a), (b), (c)) +# define my_debug_1(a,b) log_debug ((a), (b)) +# define my_fatal_0(a) log_fatal ((a)) +#elif defined (DOTLOCK_GLIB_LOGGING) +# define my_info_0(a) g_message ((a)) +# define my_info_1(a,b) g_message ((a), (b)) +# define my_info_2(a,b,c) g_message ((a), (b), (c)) +# define my_info_3(a,b,c,d) g_message ((a), (b), (c), (d)) +# define my_error_0(a) g_warning ((a)) +# define my_error_1(a,b) g_warning ((a), (b)) +# define my_error_2(a,b,c) g_warning ((a), (b), (c)) +# define my_debug_1(a,b) g_debug ((a), (b)) +# define my_fatal_0(a) g_error ((a)) +#else +# define my_info_0(a) fprintf (stderr, (a)) +# define my_info_1(a,b) fprintf (stderr, (a), (b)) +# define my_info_2(a,b,c) fprintf (stderr, (a), (b), (c)) +# define my_info_3(a,b,c,d) fprintf (stderr, (a), (b), (c), (d)) +# define my_error_0(a) fprintf (stderr, (a)) +# define my_error_1(a,b) fprintf (stderr, (a), (b)) +# define my_error_2(a,b,c) fprintf (stderr, (a), (b), (c)) +# define my_debug_1(a,b) fprintf (stderr, (a), (b)) +# define my_fatal_0(a) do { fprintf (stderr,(a)); fflush (stderr); \ + abort (); } while (0) +#endif + + + + + +/* The object describing a lock. */ +struct dotlock_handle +{ + struct dotlock_handle *next; + char *lockname; /* Name of the actual lockfile. */ + unsigned int locked:1; /* Lock status. */ + unsigned int disable:1; /* If true, locking is disabled. */ + unsigned int use_o_excl:1; /* Use open (O_EXCL) for locking. */ + + int extra_fd; /* A place for the caller to store an FD. */ + +#ifdef HAVE_DOSISH_SYSTEM + HANDLE lockhd; /* The W32 handle of the lock file. */ +#else /*!HAVE_DOSISH_SYSTEM */ + char *tname; /* Name of the lockfile template. */ + size_t nodename_off; /* Offset in TNAME of the nodename part. */ + size_t nodename_len; /* Length of the nodename part. */ +#endif /*!HAVE_DOSISH_SYSTEM */ +}; + + +/* A list of all lock handles. The volatile attribute might help + if used in an atexit handler. Note that [UN]LOCK_all_lockfiles + must not change ERRNO. */ +static volatile dotlock_t all_lockfiles; +#ifdef DOTLOCK_USE_PTHREAD +static pthread_mutex_t all_lockfiles_mutex = PTHREAD_MUTEX_INITIALIZER; +# define LOCK_all_lockfiles() do { \ + if (pthread_mutex_lock (&all_lockfiles_mutex)) \ + my_fatal_0 ("locking all_lockfiles_mutex failed\n"); \ + } while (0) +# define UNLOCK_all_lockfiles() do { \ + if (pthread_mutex_unlock (&all_lockfiles_mutex)) \ + my_fatal_0 ("unlocking all_lockfiles_mutex failed\n"); \ + } while (0) +#else /*!DOTLOCK_USE_PTHREAD*/ +# define LOCK_all_lockfiles() do { } while (0) +# define UNLOCK_all_lockfiles() do { } while (0) +#endif /*!DOTLOCK_USE_PTHREAD*/ + +/* If this has the value true all locking is disabled. */ +static int never_lock; + + + + +#ifdef HAVE_DOSISH_SYSTEM +/* FIXME: For use in GnuPG this can be replaced by + * gnupg_w32_set_errno. */ +static int +map_w32_to_errno (DWORD w32_err) +{ + switch (w32_err) + { + case 0: + return 0; + + case ERROR_FILE_NOT_FOUND: + return ENOENT; + + case ERROR_PATH_NOT_FOUND: + return ENOENT; + + case ERROR_ACCESS_DENIED: + return EPERM; + + case ERROR_INVALID_HANDLE: + case ERROR_INVALID_BLOCK: + return EINVAL; + + case ERROR_NOT_ENOUGH_MEMORY: + return ENOMEM; + + case ERROR_NO_DATA: + case ERROR_BROKEN_PIPE: + return EPIPE; + + default: + return EIO; + } +} +#endif /*HAVE_DOSISH_SYSTEM*/ + + +#ifdef HAVE_W32_SYSTEM +static int +any8bitchar (const char *string) +{ + if (string) + for ( ; *string; string++) + if ((*string & 0x80)) + return 1; + return 0; +} +#endif /*HAVE_W32_SYSTEM*/ + + + + +/* Entirely disable all locking. This function should be called + before any locking is done. It may be called right at startup of + the process as it only sets a global value. */ +void +dotlock_disable (void) +{ + never_lock = 1; +} + + +#ifdef HAVE_POSIX_SYSTEM +static int +maybe_deadlock (dotlock_t h) +{ + dotlock_t r; + int res = 0; + + LOCK_all_lockfiles (); + for (r=all_lockfiles; r; r = r->next) + { + if ( r != h && r->locked ) + { + res = 1; + break; + } + } + UNLOCK_all_lockfiles (); + return res; +} +#endif /*HAVE_POSIX_SYSTEM*/ + + +/* Read the lock file and return the pid, returns -1 on error. True + will be stored in the integer at address SAME_NODE if the lock file + has been created on the same node. */ +#ifdef HAVE_POSIX_SYSTEM +static int +read_lockfile (dotlock_t h, int *same_node, int *r_fd) +{ + char buffer_space[10+1+70+1]; /* 70 is just an estimated value; node + names are usually shorter. */ + int fd; + int pid = -1; + char *buffer, *p; + size_t expected_len; + int res, nread; + + *same_node = 0; + expected_len = 10 + 1 + h->nodename_len + 1; + if ( expected_len >= sizeof buffer_space) + { + buffer = xtrymalloc (expected_len); + if (!buffer) + return -1; + } + else + buffer = buffer_space; + + if ( (fd = open (h->lockname, O_RDONLY)) == -1 ) + { + int e = errno; + my_info_2 ("error opening lockfile '%s': %s\n", + h->lockname, strerror(errno) ); + if (buffer != buffer_space) + xfree (buffer); + my_set_errno (e); /* Need to return ERRNO here. */ + return -1; + } + + p = buffer; + nread = 0; + do + { + res = read (fd, p, expected_len - nread); + if (res == -1 && errno == EINTR) + continue; + if (res < 0) + { + int e = errno; + my_info_1 ("error reading lockfile '%s'\n", h->lockname ); + close (fd); + if (buffer != buffer_space) + xfree (buffer); + my_set_errno (e); + return -1; + } + p += res; + nread += res; + } + while (res && nread != expected_len); + + if (r_fd) + *r_fd = fd; + else + close(fd); + + if (nread < 11) + { + my_info_1 ("invalid size of lockfile '%s'\n", h->lockname); + if (buffer != buffer_space) + xfree (buffer); + my_set_errno (EINVAL); + return -1; + } + + if (buffer[10] != '\n' + || (buffer[10] = 0, pid = atoi (buffer)) == -1 + || !pid ) + { + my_error_2 ("invalid pid %d in lockfile '%s'\n", pid, h->lockname); + if (buffer != buffer_space) + xfree (buffer); + my_set_errno (EINVAL); + return -1; + } + + if (nread == expected_len + && !memcmp (h->tname+h->nodename_off, buffer+11, h->nodename_len) + && buffer[11+h->nodename_len] == '\n') + *same_node = 1; + + if (buffer != buffer_space) + xfree (buffer); + return pid; +} +#endif /*HAVE_POSIX_SYSTEM */ + + +/* Check whether the file system which stores TNAME supports + hardlinks. Instead of using the non-portable statsfs call which + differs between various Unix versions, we do a runtime test. + Returns: 0 supports hardlinks; 1 no hardlink support, -1 unknown + (test error). */ +#ifdef HAVE_POSIX_SYSTEM +static int +use_hardlinks_p (const char *tname) +{ + char *lname; + struct stat sb; + unsigned int nlink; + int res; + + if (stat (tname, &sb)) + return -1; + nlink = (unsigned int)sb.st_nlink; + + lname = xtrymalloc (strlen (tname) + 1 + 1); + if (!lname) + return -1; + strcpy (lname, tname); + strcat (lname, "x"); + + /* We ignore the return value of link() because it is unreliable. */ + (void) link (tname, lname); + + if (stat (tname, &sb)) + res = -1; /* Ooops. */ + else if (sb.st_nlink == nlink + 1) + res = 0; /* Yeah, hardlinks are supported. */ + else + res = 1; /* No hardlink support. */ + + unlink (lname); + xfree (lname); + return res; +} +#endif /*HAVE_POSIX_SYSTEM */ + + + +#ifdef HAVE_POSIX_SYSTEM +/* Locking core for Unix. It used a temporary file and the link + system call to make locking an atomic operation. */ +static dotlock_t +dotlock_create_unix (dotlock_t h, const char *file_to_lock) +{ + int fd = -1; + char pidstr[16]; + const char *nodename; + const char *dirpart; + int dirpartlen; + struct utsname utsbuf; + size_t tnamelen; + + snprintf (pidstr, sizeof pidstr, "%10d\n", (int)getpid() ); + + /* Create a temporary file. */ + if ( uname ( &utsbuf ) ) + nodename = "unknown"; + else + nodename = utsbuf.nodename; + + if ( !(dirpart = strrchr (file_to_lock, DIRSEP_C)) ) + { + dirpart = EXTSEP_S; + dirpartlen = 1; + } + else + { + dirpartlen = dirpart - file_to_lock; + dirpart = file_to_lock; + } + + LOCK_all_lockfiles (); + h->next = all_lockfiles; + all_lockfiles = h; + + tnamelen = dirpartlen + 6 + 30 + strlen(nodename) + 10 + 1; + h->tname = xtrymalloc (tnamelen + 1); + if (!h->tname) + { + all_lockfiles = h->next; + UNLOCK_all_lockfiles (); + xfree (h); + return NULL; + } + h->nodename_len = strlen (nodename); + + snprintf (h->tname, tnamelen, "%.*s/.#lk%p.", dirpartlen, dirpart, h ); + h->nodename_off = strlen (h->tname); + snprintf (h->tname+h->nodename_off, tnamelen - h->nodename_off, + "%s.%d", nodename, (int)getpid ()); + + do + { + my_set_errno (0); + fd = open (h->tname, O_WRONLY|O_CREAT|O_EXCL, + S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR ); + } + while (fd == -1 && errno == EINTR); + + if ( fd == -1 ) + { + int saveerrno = errno; + all_lockfiles = h->next; + UNLOCK_all_lockfiles (); + my_error_2 (_("failed to create temporary file '%s': %s\n"), + h->tname, strerror (errno)); + xfree (h->tname); + xfree (h); + my_set_errno (saveerrno); + return NULL; + } + if ( write (fd, pidstr, 11 ) != 11 ) + goto write_failed; + if ( write (fd, nodename, strlen (nodename) ) != strlen (nodename) ) + goto write_failed; + if ( write (fd, "\n", 1 ) != 1 ) + goto write_failed; + if ( close (fd) ) + { + if ( errno == EINTR ) + fd = -1; + goto write_failed; + } + fd = -1; + + /* Check whether we support hard links. */ + switch (use_hardlinks_p (h->tname)) + { + case 0: /* Yes. */ + break; + case 1: /* No. */ + unlink (h->tname); + h->use_o_excl = 1; + break; + default: + { + int saveerrno = errno; + my_error_2 ("can't check whether hardlinks are supported for '%s': %s\n" + , h->tname, strerror (saveerrno)); + my_set_errno (saveerrno); + } + goto write_failed; + } + + h->lockname = xtrymalloc (strlen (file_to_lock) + 6 ); + if (!h->lockname) + { + int saveerrno = errno; + all_lockfiles = h->next; + UNLOCK_all_lockfiles (); + unlink (h->tname); + xfree (h->tname); + xfree (h); + my_set_errno (saveerrno); + return NULL; + } + strcpy (stpcpy (h->lockname, file_to_lock), EXTSEP_S "lock"); + UNLOCK_all_lockfiles (); + + return h; + + write_failed: + { + int saveerrno = errno; + all_lockfiles = h->next; + UNLOCK_all_lockfiles (); + my_error_2 (_("error writing to '%s': %s\n"), h->tname, strerror (errno)); + if ( fd != -1 ) + close (fd); + unlink (h->tname); + xfree (h->tname); + xfree (h); + my_set_errno (saveerrno); + } + return NULL; +} +#endif /*HAVE_POSIX_SYSTEM*/ + + +#ifdef HAVE_DOSISH_SYSTEM +/* Locking core for Windows. This version does not need a temporary + file but uses the plain lock file along with record locking. We + create this file here so that we later only need to do the file + locking. For error reporting it is useful to keep the name of the + file in the handle. */ +static dotlock_t +dotlock_create_w32 (dotlock_t h, const char *file_to_lock) +{ + LOCK_all_lockfiles (); + h->next = all_lockfiles; + all_lockfiles = h; + + h->lockname = strconcat (file_to_lock, EXTSEP_S "lock", NULL); + if (!h->lockname) + { + all_lockfiles = h->next; + UNLOCK_all_lockfiles (); + xfree (h); + return NULL; + } + + /* If would be nice if we would use the FILE_FLAG_DELETE_ON_CLOSE + along with FILE_SHARE_DELETE but that does not work due to a race + condition: Despite the OPEN_ALWAYS flag CreateFile may return an + error and we can't reliable create/open the lock file unless we + would wait here until it works - however there are other valid + reasons why a lock file can't be created and thus the process + would not stop as expected but spin until Windows crashes. Our + solution is to keep the lock file open; that does not harm. */ + if (any8bitchar (h->lockname)) + { + wchar_t *wname = utf8_to_wchar (h->lockname); + + if (wname) + h->lockhd = CreateFileW (wname, + GENERIC_READ|GENERIC_WRITE, + FILE_SHARE_READ|FILE_SHARE_WRITE, + NULL, OPEN_ALWAYS, 0, NULL); + else + h->lockhd = INVALID_HANDLE_VALUE; + xfree (wname); + } + else + h->lockhd = CreateFileA (h->lockname, + GENERIC_READ|GENERIC_WRITE, + FILE_SHARE_READ|FILE_SHARE_WRITE, + NULL, OPEN_ALWAYS, 0, NULL); + if (h->lockhd == INVALID_HANDLE_VALUE) + { + int saveerrno = map_w32_to_errno (GetLastError ()); + all_lockfiles = h->next; + UNLOCK_all_lockfiles (); + my_error_2 (_("can't create '%s': %s\n"), h->lockname, w32_strerror (-1)); + xfree (h->lockname); + xfree (h); + my_set_errno (saveerrno); + return NULL; + } + return h; +} +#endif /*HAVE_DOSISH_SYSTEM*/ + + +/* Create a lockfile for a file name FILE_TO_LOCK and returns an + object of type dotlock_t which may be used later to actually acquire + the lock. A cleanup routine gets installed to cleanup left over + locks or other files used internally by the lock mechanism. + + Calling this function with NULL does only install the atexit + handler and may thus be used to assure that the cleanup is called + after all other atexit handlers. + + This function creates a lock file in the same directory as + FILE_TO_LOCK using that name and a suffix of ".lock". Note that on + POSIX systems a temporary file ".#lk.<hostname>.pid[.threadid] is + used. + + FLAGS must be 0. + + The function returns an new handle which needs to be released using + destroy_dotlock but gets also released at the termination of the + process. On error NULL is returned. + */ + +dotlock_t +dotlock_create (const char *file_to_lock, unsigned int flags) +{ + static int initialized; + dotlock_t h; + + if ( !initialized ) + { + atexit (dotlock_remove_lockfiles); + initialized = 1; + } + + if ( !file_to_lock ) + return NULL; /* Only initialization was requested. */ + + if (flags) + { + my_set_errno (EINVAL); + return NULL; + } + + h = xtrycalloc (1, sizeof *h); + if (!h) + return NULL; + h->extra_fd = -1; + + if (never_lock) + { + h->disable = 1; + LOCK_all_lockfiles (); + h->next = all_lockfiles; + all_lockfiles = h; + UNLOCK_all_lockfiles (); + return h; + } + +#ifdef HAVE_DOSISH_SYSTEM + return dotlock_create_w32 (h, file_to_lock); +#else /*!HAVE_DOSISH_SYSTEM */ + return dotlock_create_unix (h, file_to_lock); +#endif /*!HAVE_DOSISH_SYSTEM*/ +} + + + +/* Convenience function to store a file descriptor (or any other + integer value) in the context of handle H. */ +void +dotlock_set_fd (dotlock_t h, int fd) +{ + h->extra_fd = fd; +} + +/* Convenience function to retrieve a file descriptor (or any other + integer value) stored in the context of handle H. */ +int +dotlock_get_fd (dotlock_t h) +{ + return h->extra_fd; +} + + + +#ifdef HAVE_POSIX_SYSTEM +/* Unix specific code of destroy_dotlock. */ +static void +dotlock_destroy_unix (dotlock_t h) +{ + if (h->locked && h->lockname) + unlink (h->lockname); + if (h->tname && !h->use_o_excl) + unlink (h->tname); + xfree (h->tname); +} +#endif /*HAVE_POSIX_SYSTEM*/ + + +#ifdef HAVE_DOSISH_SYSTEM +/* Windows specific code of destroy_dotlock. */ +static void +dotlock_destroy_w32 (dotlock_t h) +{ + if (h->locked) + { + OVERLAPPED ovl; + + memset (&ovl, 0, sizeof ovl); + UnlockFileEx (h->lockhd, 0, 1, 0, &ovl); + } + CloseHandle (h->lockhd); +} +#endif /*HAVE_DOSISH_SYSTEM*/ + + +/* Destroy the lock handle H and release the lock. */ +void +dotlock_destroy (dotlock_t h) +{ + dotlock_t hprev, htmp; + + if ( !h ) + return; + + /* First remove the handle from our global list of all locks. */ + LOCK_all_lockfiles (); + for (hprev=NULL, htmp=all_lockfiles; htmp; hprev=htmp, htmp=htmp->next) + if (htmp == h) + { + if (hprev) + hprev->next = htmp->next; + else + all_lockfiles = htmp->next; + h->next = NULL; + break; + } + UNLOCK_all_lockfiles (); + + /* Then destroy the lock. */ + if (!h->disable) + { +#ifdef HAVE_DOSISH_SYSTEM + dotlock_destroy_w32 (h); +#else /* !HAVE_DOSISH_SYSTEM */ + dotlock_destroy_unix (h); +#endif /* HAVE_DOSISH_SYSTEM */ + xfree (h->lockname); + } + xfree(h); +} + + +/* Return true if H has been taken. */ +int +dotlock_is_locked (dotlock_t h) +{ + return h && !!h->locked; +} + + + +#ifdef HAVE_POSIX_SYSTEM +/* Unix specific code of make_dotlock. Returns 0 on success and -1 on + error. */ +static int +dotlock_take_unix (dotlock_t h, long timeout) +{ + int wtime = 0; + int sumtime = 0; + int pid; + int lastpid = -1; + int ownerchanged; + const char *maybe_dead=""; + int same_node; + int saveerrno; + int fd; + + again: + if (h->use_o_excl) + { + /* No hardlink support - use open(O_EXCL). */ + do + { + my_set_errno (0); + fd = open (h->lockname, O_WRONLY|O_CREAT|O_EXCL, + S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR ); + } + while (fd == -1 && errno == EINTR); + + if (fd == -1 && errno == EEXIST) + ; /* Lock held by another process. */ + else if (fd == -1) + { + saveerrno = errno; + my_error_2 ("lock not made: open(O_EXCL) of '%s' failed: %s\n", + h->lockname, strerror (saveerrno)); + my_set_errno (saveerrno); + return -1; + } + else + { + char pidstr[16]; + + snprintf (pidstr, sizeof pidstr, "%10d\n", (int)getpid()); + if (write (fd, pidstr, 11 ) == 11 + && write (fd, h->tname + h->nodename_off,h->nodename_len) + == h->nodename_len + && write (fd, "\n", 1) == 1 + && !close (fd)) + { + h->locked = 1; + return 0; + } + /* Write error. */ + saveerrno = errno; + my_error_2 ("lock not made: writing to '%s' failed: %s\n", + h->lockname, strerror (errno)); + close (fd); + unlink (h->lockname); + my_set_errno (saveerrno); + return -1; + } + } + else /* Standard method: Use hardlinks. */ + { + struct stat sb; + + /* We ignore the return value of link() because it is unreliable. */ + (void) link (h->tname, h->lockname); + + if (stat (h->tname, &sb)) + { + saveerrno = errno; + my_error_1 ("lock not made: Oops: stat of tmp file failed: %s\n", + strerror (errno)); + /* In theory this might be a severe error: It is possible + that link succeeded but stat failed due to changed + permissions. We can't do anything about it, though. */ + my_set_errno (saveerrno); + return -1; + } + + if (sb.st_nlink == 2) + { + h->locked = 1; + return 0; /* Okay. */ + } + } + + /* Check for stale lock files. */ + if ( (pid = read_lockfile (h, &same_node, &fd)) == -1 ) + { + if ( errno != ENOENT ) + { + saveerrno = errno; + my_info_0 ("cannot read lockfile\n"); + my_set_errno (saveerrno); + return -1; + } + my_info_0 ("lockfile disappeared\n"); + goto again; + } + else if ( (pid == getpid() && same_node) + || (same_node && kill (pid, 0) && errno == ESRCH) ) + /* Stale lockfile is detected. */ + { + struct stat sb; + + /* Check if it's unlocked during examining the lockfile. */ + if (fstat (fd, &sb) || sb.st_nlink == 0) + { + /* It's gone already by another process. */ + close (fd); + goto again; + } + + /* + * Here, although it's quite _rare_, we have a race condition. + * + * When multiple processes race on a stale lockfile, detecting + * AND removing should be done atomically. That is, to work + * correctly, the file to be removed should be the one which is + * examined for detection. + * + * But, when it's not atomic, consider the case for us where it + * takes some time between the detection and the removal of the + * lockfile. + * + * In this situation, it is possible that the file which was + * detected as stale is already removed by another process and + * then new lockfile is created (by that process or other one). + * + * And it is newly created valid lockfile which is going to be + * removed by us. + * + * Consider this long comment as it expresses possible (long) + * time between fstat above and unlink below; Meanwhile, the + * lockfile in question may be removed and there may be new + * valid one. + * + * In short, when you see the message of removing stale lockfile + * when there are multiple processes for the work, there is + * (very) little possibility something went wrong. + */ + + unlink (h->lockname); + my_info_1 (_("removing stale lockfile (created by %d)\n"), pid); + close (fd); + goto again; + } + + close (fd); + if (lastpid == -1) + lastpid = pid; + ownerchanged = (pid != lastpid); + + if (timeout) + { + struct timeval tv; + + /* Wait until lock has been released. We use increasing retry + intervals of 50ms, 100ms, 200ms, 400ms, 800ms, 2s, 4s and 8s + but reset it if the lock owner meanwhile changed. */ + if (!wtime || ownerchanged) + wtime = 50; + else if (wtime < 800) + wtime *= 2; + else if (wtime == 800) + wtime = 2000; + else if (wtime < 8000) + wtime *= 2; + + if (timeout > 0) + { + if (wtime > timeout) + wtime = timeout; + timeout -= wtime; + } + + sumtime += wtime; + if (sumtime >= 1500) + { + sumtime = 0; + my_info_3 (_("waiting for lock (held by %d%s) %s...\n"), + pid, maybe_dead, maybe_deadlock(h)? _("(deadlock?) "):""); + } + + + tv.tv_sec = wtime / 1000; + tv.tv_usec = (wtime % 1000) * 1000; + select (0, NULL, NULL, NULL, &tv); + goto again; + } + + my_set_errno (EACCES); + return -1; +} +#endif /*HAVE_POSIX_SYSTEM*/ + + +#ifdef HAVE_DOSISH_SYSTEM +/* Windows specific code of make_dotlock. Returns 0 on success and -1 on + error. */ +static int +dotlock_take_w32 (dotlock_t h, long timeout) +{ + int wtime = 0; + int w32err; + OVERLAPPED ovl; + + again: + /* Lock one byte at offset 0. The offset is given by OVL. */ + memset (&ovl, 0, sizeof ovl); + if (LockFileEx (h->lockhd, (LOCKFILE_EXCLUSIVE_LOCK + | LOCKFILE_FAIL_IMMEDIATELY), 0, 1, 0, &ovl)) + { + h->locked = 1; + return 0; /* okay */ + } + + w32err = GetLastError (); + if (w32err != ERROR_LOCK_VIOLATION) + { + my_error_2 (_("lock '%s' not made: %s\n"), + h->lockname, w32_strerror (w32err)); + my_set_errno (map_w32_to_errno (w32err)); + return -1; + } + + if (timeout) + { + /* Wait until lock has been released. We use retry intervals of + 50ms, 100ms, 200ms, 400ms, 800ms, 2s, 4s and 8s. */ + if (!wtime) + wtime = 50; + else if (wtime < 800) + wtime *= 2; + else if (wtime == 800) + wtime = 2000; + else if (wtime < 8000) + wtime *= 2; + + if (timeout > 0) + { + if (wtime > timeout) + wtime = timeout; + timeout -= wtime; + } + + if (wtime >= 800) + my_info_1 (_("waiting for lock %s...\n"), h->lockname); + + Sleep (wtime); + goto again; + } + + my_set_errno (EACCES); + return -1; +} +#endif /*HAVE_DOSISH_SYSTEM*/ + + +/* Take a lock on H. A value of 0 for TIMEOUT returns immediately if + the lock can't be taken, -1 waits forever (hopefully not), other + values wait for TIMEOUT milliseconds. Returns: 0 on success */ +int +dotlock_take (dotlock_t h, long timeout) +{ + int ret; + + if ( h->disable ) + { + h->locked = 1; + return 0; /* Locks are completely disabled. Return success. */ + } + + if ( h->locked ) + { + /* my_debug_1 ("'%s' is already locked (%s)\n", h->lockname); */ + return 0; + } + +#ifdef HAVE_DOSISH_SYSTEM + ret = dotlock_take_w32 (h, timeout); +#else /*!HAVE_DOSISH_SYSTEM*/ + ret = dotlock_take_unix (h, timeout); +#endif /*!HAVE_DOSISH_SYSTEM*/ + + return ret; +} + + + +#ifdef HAVE_POSIX_SYSTEM +/* Unix specific code of release_dotlock. */ +static int +dotlock_release_unix (dotlock_t h) +{ + int pid, same_node; + int saveerrno; + + pid = read_lockfile (h, &same_node, NULL); + if ( pid == -1 ) + { + saveerrno = errno; + my_error_0 ("release_dotlock: lockfile error\n"); + my_set_errno (saveerrno); + return -1; + } + if ( pid != getpid() || !same_node ) + { + my_error_1 ("release_dotlock: not our lock (pid=%d)\n", pid); + my_set_errno (EACCES); + return -1; + } + + if ( unlink( h->lockname ) ) + { + saveerrno = errno; + my_error_1 ("release_dotlock: error removing lockfile '%s'\n", + h->lockname); + my_set_errno (saveerrno); + return -1; + } + /* Fixme: As an extra check we could check whether the link count is + now really at 1. */ + return 0; +} +#endif /*HAVE_POSIX_SYSTEM */ + + +#ifdef HAVE_DOSISH_SYSTEM +/* Windows specific code of release_dotlock. */ +static int +dotlock_release_w32 (dotlock_t h) +{ + OVERLAPPED ovl; + + memset (&ovl, 0, sizeof ovl); + if (!UnlockFileEx (h->lockhd, 0, 1, 0, &ovl)) + { + int saveerrno = map_w32_to_errno (GetLastError ()); + my_error_2 ("release_dotlock: error removing lockfile '%s': %s\n", + h->lockname, w32_strerror (-1)); + my_set_errno (saveerrno); + return -1; + } + + return 0; +} +#endif /*HAVE_DOSISH_SYSTEM */ + + +/* Release a lock. Returns 0 on success. */ +int +dotlock_release (dotlock_t h) +{ + int ret; + + /* To avoid atexit race conditions we first check whether there are + any locks left. It might happen that another atexit handler + tries to release the lock while the atexit handler of this module + already ran and thus H is undefined. */ + LOCK_all_lockfiles (); + ret = !all_lockfiles; + UNLOCK_all_lockfiles (); + if (ret) + return 0; + + if ( h->disable ) + { + h->locked = 0; + return 0; + } + + if ( !h->locked ) + { + /* my_debug_1 ("Oops, '%s' is not locked (%s)\n", h->lockname); */ + return 0; + } + +#ifdef HAVE_DOSISH_SYSTEM + ret = dotlock_release_w32 (h); +#else + ret = dotlock_release_unix (h); +#endif + + if (!ret) + h->locked = 0; + return ret; +} + + + +/* Remove all lockfiles. This is called by the atexit handler + installed by this module but may also be called by other + termination handlers. */ +void +dotlock_remove_lockfiles (void) +{ + dotlock_t h, h2; + + /* First set the lockfiles list to NULL so that for example + dotlock_release is aware that this function is currently + running. */ + LOCK_all_lockfiles (); + h = all_lockfiles; + all_lockfiles = NULL; + UNLOCK_all_lockfiles (); + + while ( h ) + { + h2 = h->next; + dotlock_destroy (h); + h = h2; + } +} diff --git a/common/dotlock.h b/common/dotlock.h new file mode 100644 index 0000000..9869739 --- /dev/null +++ b/common/dotlock.h @@ -0,0 +1,113 @@ +/* dotlock.h - dotfile locking declarations + * Copyright (C) 2000, 2001, 2006, 2011 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute and/or modify this + * part of GnuPG under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * GnuPG is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copies of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, see <https://www.gnu.org/licenses/>. + * + * ALTERNATIVELY, this file may be distributed under the terms of the + * following license, in which case the provisions of this license are + * required INSTEAD OF the GNU Lesser General License or the GNU + * General Public License. If you wish to allow use of your version of + * this file only under the terms of the GNU Lesser General License or + * the GNU General Public License, and not to allow others to use your + * version of this file under the terms of the following license, + * indicate your decision by deleting this paragraph and the license + * below. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef GNUPG_COMMON_DOTLOCK_H +#define GNUPG_COMMON_DOTLOCK_H + +/* See dotlock.c for a description. */ + +#ifdef DOTLOCK_EXT_SYM_PREFIX +# ifndef _DOTLOCK_PREFIX +# define _DOTLOCK_PREFIX1(x,y) x ## y +# define _DOTLOCK_PREFIX2(x,y) _DOTLOCK_PREFIX1(x,y) +# define _DOTLOCK_PREFIX(x) _DOTLOCK_PREFIX2(DOTLOCK_EXT_SYM_PREFIX,x) +# endif /*_DOTLOCK_PREFIX*/ +# define dotlock_disable _DOTLOCK_PREFIX(dotlock_disable) +# define dotlock_create _DOTLOCK_PREFIX(dotlock_create) +# define dotlock_set_fd _DOTLOCK_PREFIX(dotlock_set_fd) +# define dotlock_get_fd _DOTLOCK_PREFIX(dotlock_get_fd) +# define dotlock_destroy _DOTLOCK_PREFIX(dotlock_destroy) +# define dotlock_take _DOTLOCK_PREFIX(dotlock_take) +# define dotlock_release _DOTLOCK_PREFIX(dotlock_release) +# define dotlock_remove_lockfiles _DOTLOCK_PREFIX(dotlock_remove_lockfiles) +#endif /*DOTLOCK_EXT_SYM_PREFIX*/ + +#ifdef __cplusplus +extern "C" +{ +#if 0 +} +#endif +#endif + + +struct dotlock_handle; +typedef struct dotlock_handle *dotlock_t; + +void dotlock_disable (void); +dotlock_t dotlock_create (const char *file_to_lock, unsigned int flags); +void dotlock_set_fd (dotlock_t h, int fd); +int dotlock_get_fd (dotlock_t h); +void dotlock_destroy (dotlock_t h); +int dotlock_is_locked (dotlock_t h); +int dotlock_take (dotlock_t h, long timeout); +int dotlock_release (dotlock_t h); +void dotlock_remove_lockfiles (void); + +#ifdef __cplusplus +} +#endif +#endif /*GNUPG_COMMON_DOTLOCK_H*/ diff --git a/common/dynload.h b/common/dynload.h new file mode 100644 index 0000000..f1dc3f0 --- /dev/null +++ b/common/dynload.h @@ -0,0 +1,100 @@ +/* dynload.h - Wrapper functions for run-time dynamic loading + * Copyright (C) 2003, 2010 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute and/or modify this + * part of GnuPG under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * GnuPG is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copies of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef GNUPG_COMMON_DYNLOAD_H +#define GNUPG_COMMON_DYNLOAD_H + +#ifndef __MINGW32__ +# include <dlfcn.h> +#else +# ifdef HAVE_WINSOCK2_H +# include <winsock2.h> /* needs to be included before windows.h */ +# endif +# include <windows.h> +# include "utf8conv.h" +# include "mischelp.h" +# define RTLD_LAZY 0 + +static inline void * +dlopen (const char *name, int flag) +{ + void *hd; +#ifdef HAVE_W32CE_SYSTEM + wchar_t *wname = utf8_to_wchar (name); + hd = wname? LoadLibrary (wname) : NULL; + xfree (wname); +#else + hd = LoadLibrary (name); +#endif + (void)flag; + return hd; +} + +static inline void * +dlsym (void *hd, const char *sym) +{ + if (hd && sym) + { +#ifdef HAVE_W32CE_SYSTEM + wchar_t *wsym = utf8_to_wchar (sym); + void *fnc = wsym? GetProcAddress (hd, wsym) : NULL; + xfree (wsym); +#else + void *fnc = GetProcAddress (hd, sym); +#endif + if (!fnc) + return NULL; + return fnc; + } + return NULL; +} + + +static inline const char * +dlerror (void) +{ + static char buf[32]; + snprintf (buf, sizeof buf, "ec=%lu", GetLastError ()); + return buf; +} + + +static inline int +dlclose (void * hd) +{ + if (hd) + { + CloseHandle (hd); + return 0; + } + return -1; +} +# endif /*__MINGW32__*/ +#endif /*GNUPG_COMMON_DYNLOAD_H*/ diff --git a/common/exaudit.awk b/common/exaudit.awk new file mode 100644 index 0000000..0d2f95e --- /dev/null +++ b/common/exaudit.awk @@ -0,0 +1,43 @@ +# exaudit.awk - Extract audit event codes from audit.h +# Copyright (C) 2007 Free Software Foundation, Inc. +# +# This file is part of GnuPG. +# +# GnuPG is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# GnuPG is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <http://www.gnu.org/licenses/>. + +BEGIN { + print "# Output of exaudit.awk - DO NOT EDIT." + topheader = 0; + okay = 0; + code = 0; +} + +topheader == 0 && /^\/\*/ { topheader = 1 } +topheader == 1 { print $0 } +topheader == 1 && /\*\// { topheader = 2; print "" } + +/AUDIT_NULL_EVENT/ { okay = 1 } +!okay { next } +/AUDIT_LAST_EVENT/ { exit } +/AUDIT_[A-Za-z_]+/ { + sub (/[,\/\*]+/, "", $1); + desc = tolower (substr($1,7)); + gsub (/_/," ",desc); + printf "%d\t%s\t%s\n", code, $1, desc; + code++; +} + +END { + print "# end of audit codes." +} diff --git a/common/exechelp-posix.c b/common/exechelp-posix.c new file mode 100644 index 0000000..e9cb7ed --- /dev/null +++ b/common/exechelp-posix.c @@ -0,0 +1,909 @@ +/* exechelp.c - Fork and exec helpers for POSIX + * Copyright (C) 2004, 2007, 2008, 2009, + * 2010 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#if defined(HAVE_W32_SYSTEM) || defined (HAVE_W32CE_SYSTEM) +#error This code is only used on POSIX +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <errno.h> +#include <assert.h> +#ifdef HAVE_SIGNAL_H +# include <signal.h> +#endif +#include <unistd.h> +#include <fcntl.h> + +#ifdef WITHOUT_NPTH /* Give the Makefile a chance to build without Pth. */ +#undef HAVE_NPTH +#undef USE_NPTH +#endif + +#ifdef HAVE_NPTH +#include <npth.h> +#endif +#include <sys/wait.h> + +#ifdef HAVE_GETRLIMIT +#include <sys/time.h> +#include <sys/resource.h> +#endif /*HAVE_GETRLIMIT*/ + +#ifdef HAVE_STAT +# include <sys/stat.h> +#endif + +#if __linux__ +# include <sys/types.h> +# include <dirent.h> +#endif /*__linux__ */ + +#include "util.h" +#include "i18n.h" +#include "sysutils.h" +#include "exechelp.h" + + +/* Helper */ +static inline gpg_error_t +my_error_from_syserror (void) +{ + return gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); +} + +static inline gpg_error_t +my_error (int errcode) +{ + return gpg_err_make (default_errsource, errcode); +} + + +/* Return the maximum number of currently allowed open file + descriptors. Only useful on POSIX systems but returns a value on + other systems too. */ +int +get_max_fds (void) +{ + int max_fds = -1; +#ifdef HAVE_GETRLIMIT + struct rlimit rl; + + /* Under Linux we can figure out the highest used file descriptor by + * reading /proc/PID/fd. This is in the common cases much fast than + * for example doing 4096 close calls where almost all of them will + * fail. On a system with a limit of 4096 files and only 8 files + * open with the highest number being 10, we speedup close_all_fds + * from 125ms to 0.4ms including readdir. + * + * Another option would be to close the file descriptors as returned + * from reading that directory - however then we need to snapshot + * that list before starting to close them. */ +#ifdef __linux__ + { + DIR *dir = NULL; + struct dirent *dir_entry; + const char *s; + int x; + + dir = opendir ("/proc/self/fd"); + if (dir) + { + while ((dir_entry = readdir (dir))) + { + s = dir_entry->d_name; + if ( *s < '0' || *s > '9') + continue; + x = atoi (s); + if (x > max_fds) + max_fds = x; + } + closedir (dir); + } + if (max_fds != -1) + return max_fds + 1; + } +#endif /* __linux__ */ + + +# ifdef RLIMIT_NOFILE + if (!getrlimit (RLIMIT_NOFILE, &rl)) + max_fds = rl.rlim_max; +# endif + +# ifdef RLIMIT_OFILE + if (max_fds == -1 && !getrlimit (RLIMIT_OFILE, &rl)) + max_fds = rl.rlim_max; + +# endif +#endif /*HAVE_GETRLIMIT*/ + +#ifdef _SC_OPEN_MAX + if (max_fds == -1) + { + long int scres = sysconf (_SC_OPEN_MAX); + if (scres >= 0) + max_fds = scres; + } +#endif + +#ifdef _POSIX_OPEN_MAX + if (max_fds == -1) + max_fds = _POSIX_OPEN_MAX; +#endif + +#ifdef OPEN_MAX + if (max_fds == -1) + max_fds = OPEN_MAX; +#endif + + if (max_fds == -1) + max_fds = 256; /* Arbitrary limit. */ + + /* AIX returns INT32_MAX instead of a proper value. We assume that + this is always an error and use an arbitrary limit. */ +#ifdef INT32_MAX + if (max_fds == INT32_MAX) + max_fds = 256; +#endif + + return max_fds; +} + + +/* Close all file descriptors starting with descriptor FIRST. If + EXCEPT is not NULL, it is expected to be a list of file descriptors + which shall not be closed. This list shall be sorted in ascending + order with the end marked by -1. */ +void +close_all_fds (int first, int *except) +{ + int max_fd = get_max_fds (); + int fd, i, except_start; + + if (except) + { + except_start = 0; + for (fd=first; fd < max_fd; fd++) + { + for (i=except_start; except[i] != -1; i++) + { + if (except[i] == fd) + { + /* If we found the descriptor in the exception list + we can start the next compare run at the next + index because the exception list is ordered. */ + except_start = i + 1; + break; + } + } + if (except[i] == -1) + close (fd); + } + } + else + { + for (fd=first; fd < max_fd; fd++) + close (fd); + } + + gpg_err_set_errno (0); +} + + +/* Returns an array with all currently open file descriptors. The end + of the array is marked by -1. The caller needs to release this + array using the *standard free* and not with xfree. This allow the + use of this function right at startup even before libgcrypt has + been initialized. Returns NULL on error and sets ERRNO + accordingly. */ +int * +get_all_open_fds (void) +{ + int *array; + size_t narray; + int fd, max_fd, idx; +#ifndef HAVE_STAT + array = calloc (1, sizeof *array); + if (array) + array[0] = -1; +#else /*HAVE_STAT*/ + struct stat statbuf; + + max_fd = get_max_fds (); + narray = 32; /* If you change this change also t-exechelp.c. */ + array = calloc (narray, sizeof *array); + if (!array) + return NULL; + + /* Note: The list we return is ordered. */ + for (idx=0, fd=0; fd < max_fd; fd++) + if (!(fstat (fd, &statbuf) == -1 && errno == EBADF)) + { + if (idx+1 >= narray) + { + int *tmp; + + narray += (narray < 256)? 32:256; + tmp = realloc (array, narray * sizeof *array); + if (!tmp) + { + free (array); + return NULL; + } + array = tmp; + } + array[idx++] = fd; + } + array[idx] = -1; +#endif /*HAVE_STAT*/ + return array; +} + + +/* The exec core used right after the fork. This will never return. */ +static void +do_exec (const char *pgmname, const char *argv[], + int fd_in, int fd_out, int fd_err, + int *except, void (*preexec)(void), unsigned int flags) +{ + char **arg_list; + int i, j; + int fds[3]; + int nodevnull[3]; + + fds[0] = fd_in; + fds[1] = fd_out; + fds[2] = fd_err; + + nodevnull[0] = !!(flags & GNUPG_SPAWN_KEEP_STDIN); + nodevnull[1] = !!(flags & GNUPG_SPAWN_KEEP_STDOUT); + nodevnull[2] = !!(flags & GNUPG_SPAWN_KEEP_STDERR); + + /* Create the command line argument array. */ + i = 0; + if (argv) + while (argv[i]) + i++; + arg_list = xcalloc (i+2, sizeof *arg_list); + arg_list[0] = strrchr (pgmname, '/'); + if (arg_list[0]) + arg_list[0]++; + else + arg_list[0] = xstrdup (pgmname); + if (argv) + for (i=0,j=1; argv[i]; i++, j++) + arg_list[j] = (char*)argv[i]; + + /* Assign /dev/null to unused FDs. */ + for (i=0; i <= 2; i++) + { + if (nodevnull[i]) + continue; + if (fds[i] == -1) + { + fds[i] = open ("/dev/null", i? O_WRONLY : O_RDONLY); + if (fds[i] == -1) + log_fatal ("failed to open '%s': %s\n", + "/dev/null", strerror (errno)); + } + } + + /* Connect the standard files. */ + for (i=0; i <= 2; i++) + { + if (nodevnull[i]) + continue; + if (fds[i] != i && dup2 (fds[i], i) == -1) + log_fatal ("dup2 std%s failed: %s\n", + i==0?"in":i==1?"out":"err", strerror (errno)); + } + + /* Close all other files. */ + close_all_fds (3, except); + + if (preexec) + preexec (); + execv (pgmname, arg_list); + /* No way to print anything, as we have closed all streams. */ + _exit (127); +} + + +static gpg_error_t +do_create_pipe (int filedes[2]) +{ + gpg_error_t err = 0; + + if (pipe (filedes) == -1) + { + err = my_error_from_syserror (); + filedes[0] = filedes[1] = -1; + } + + return err; +} + + +static gpg_error_t +create_pipe_and_estream (int filedes[2], estream_t *r_fp, + int outbound, int nonblock) +{ + gpg_error_t err; + + if (pipe (filedes) == -1) + { + err = my_error_from_syserror (); + log_error (_("error creating a pipe: %s\n"), gpg_strerror (err)); + filedes[0] = filedes[1] = -1; + *r_fp = NULL; + return err; + } + + if (!outbound) + *r_fp = es_fdopen (filedes[0], nonblock? "r,nonblock" : "r"); + else + *r_fp = es_fdopen (filedes[1], nonblock? "w,nonblock" : "w"); + if (!*r_fp) + { + err = my_error_from_syserror (); + log_error (_("error creating a stream for a pipe: %s\n"), + gpg_strerror (err)); + close (filedes[0]); + close (filedes[1]); + filedes[0] = filedes[1] = -1; + return err; + } + return 0; +} + + +/* Portable function to create a pipe. Under Windows the write end is + inheritable. If R_FP is not NULL, an estream is created for the + read end and stored at R_FP. */ +gpg_error_t +gnupg_create_inbound_pipe (int filedes[2], estream_t *r_fp, int nonblock) +{ + if (r_fp) + return create_pipe_and_estream (filedes, r_fp, 0, nonblock); + else + return do_create_pipe (filedes); +} + + +/* Portable function to create a pipe. Under Windows the read end is + inheritable. If R_FP is not NULL, an estream is created for the + write end and stored at R_FP. */ +gpg_error_t +gnupg_create_outbound_pipe (int filedes[2], estream_t *r_fp, int nonblock) +{ + if (r_fp) + return create_pipe_and_estream (filedes, r_fp, 1, nonblock); + else + return do_create_pipe (filedes); +} + + +/* Portable function to create a pipe. Under Windows both ends are + inheritable. */ +gpg_error_t +gnupg_create_pipe (int filedes[2]) +{ + return do_create_pipe (filedes); +} + + +/* Fork and exec the PGMNAME, see exechelp.h for details. */ +gpg_error_t +gnupg_spawn_process (const char *pgmname, const char *argv[], + int *except, void (*preexec)(void), unsigned int flags, + estream_t *r_infp, + estream_t *r_outfp, + estream_t *r_errfp, + pid_t *pid) +{ + gpg_error_t err; + int inpipe[2] = {-1, -1}; + int outpipe[2] = {-1, -1}; + int errpipe[2] = {-1, -1}; + estream_t infp = NULL; + estream_t outfp = NULL; + estream_t errfp = NULL; + int nonblock = !!(flags & GNUPG_SPAWN_NONBLOCK); + + if (r_infp) + *r_infp = NULL; + if (r_outfp) + *r_outfp = NULL; + if (r_errfp) + *r_errfp = NULL; + *pid = (pid_t)(-1); /* Always required. */ + + if (r_infp) + { + err = create_pipe_and_estream (inpipe, &infp, 1, nonblock); + if (err) + return err; + } + + if (r_outfp) + { + err = create_pipe_and_estream (outpipe, &outfp, 0, nonblock); + if (err) + { + if (infp) + es_fclose (infp); + else if (inpipe[1] != -1) + close (inpipe[1]); + if (inpipe[0] != -1) + close (inpipe[0]); + + return err; + } + } + + if (r_errfp) + { + err = create_pipe_and_estream (errpipe, &errfp, 0, nonblock); + if (err) + { + if (infp) + es_fclose (infp); + else if (inpipe[1] != -1) + close (inpipe[1]); + if (inpipe[0] != -1) + close (inpipe[0]); + + if (outfp) + es_fclose (outfp); + else if (outpipe[0] != -1) + close (outpipe[0]); + if (outpipe[1] != -1) + close (outpipe[1]); + + return err; + } + } + + + *pid = fork (); + if (*pid == (pid_t)(-1)) + { + err = my_error_from_syserror (); + log_error (_("error forking process: %s\n"), gpg_strerror (err)); + + if (infp) + es_fclose (infp); + else if (inpipe[1] != -1) + close (inpipe[1]); + if (inpipe[0] != -1) + close (inpipe[0]); + + if (outfp) + es_fclose (outfp); + else if (outpipe[0] != -1) + close (outpipe[0]); + if (outpipe[1] != -1) + close (outpipe[1]); + + if (errfp) + es_fclose (errfp); + else if (errpipe[0] != -1) + close (errpipe[0]); + if (errpipe[1] != -1) + close (errpipe[1]); + return err; + } + + if (!*pid) + { + /* This is the child. */ + gcry_control (GCRYCTL_TERM_SECMEM); + es_fclose (infp); + es_fclose (outfp); + es_fclose (errfp); + do_exec (pgmname, argv, inpipe[0], outpipe[1], errpipe[1], + except, preexec, flags); + /*NOTREACHED*/ + } + + /* This is the parent. */ + if (inpipe[0] != -1) + close (inpipe[0]); + if (outpipe[1] != -1) + close (outpipe[1]); + if (errpipe[1] != -1) + close (errpipe[1]); + + if (r_infp) + *r_infp = infp; + if (r_outfp) + *r_outfp = outfp; + if (r_errfp) + *r_errfp = errfp; + + return 0; +} + + + +/* Simplified version of gnupg_spawn_process. This function forks and + then execs PGMNAME, while connecting INFD to stdin, OUTFD to stdout + and ERRFD to stderr (any of them may be -1 to connect them to + /dev/null). The arguments for the process are expected in the NULL + terminated array ARGV. The program name itself should not be + included there. Calling gnupg_wait_process is required. + + Returns 0 on success or an error code. */ +gpg_error_t +gnupg_spawn_process_fd (const char *pgmname, const char *argv[], + int infd, int outfd, int errfd, pid_t *pid) +{ + gpg_error_t err; + + *pid = fork (); + if (*pid == (pid_t)(-1)) + { + err = my_error_from_syserror (); + log_error (_("error forking process: %s\n"), strerror (errno)); + return err; + } + + if (!*pid) + { + gcry_control (GCRYCTL_TERM_SECMEM); + /* Run child. */ + do_exec (pgmname, argv, infd, outfd, errfd, NULL, NULL, 0); + /*NOTREACHED*/ + } + + return 0; +} + + + + +/* Waiting for child processes. + + waitpid(2) may return information about terminated children that we + did not yet request, and there is no portable way to wait for a + specific set of children. + + As a workaround, we store the results of children for later use. + + XXX: This assumes that PIDs are not reused too quickly. */ + +struct terminated_child +{ + pid_t pid; + int exitcode; + struct terminated_child *next; +}; + +struct terminated_child *terminated_children; + + +static gpg_error_t +store_result (pid_t pid, int exitcode) +{ + struct terminated_child *c; + + c = xtrymalloc (sizeof *c); + if (c == NULL) + return gpg_err_code_from_syserror (); + + c->pid = pid; + c->exitcode = exitcode; + c->next = terminated_children; + terminated_children = c; + + return 0; +} + + +static int +get_result (pid_t pid, int *r_exitcode) +{ + struct terminated_child *c, **prevp; + + for (prevp = &terminated_children, c = terminated_children; + c; + prevp = &c->next, c = c->next) + if (c->pid == pid) + { + *prevp = c->next; + *r_exitcode = c->exitcode; + xfree (c); + return 1; + } + + return 0; +} + + +/* See exechelp.h for a description. */ +gpg_error_t +gnupg_wait_process (const char *pgmname, pid_t pid, int hang, int *r_exitcode) +{ + gpg_err_code_t ec; + int i, status; + + if (r_exitcode) + *r_exitcode = -1; + + if (pid == (pid_t)(-1)) + return gpg_error (GPG_ERR_INV_VALUE); + +#ifdef USE_NPTH + i = npth_waitpid (pid, &status, hang? 0:WNOHANG); +#else + while ((i=waitpid (pid, &status, hang? 0:WNOHANG)) == (pid_t)(-1) + && errno == EINTR); +#endif + + if (i == (pid_t)(-1)) + { + ec = gpg_err_code_from_errno (errno); + log_error (_("waiting for process %d to terminate failed: %s\n"), + (int)pid, strerror (errno)); + } + else if (!i) + { + ec = GPG_ERR_TIMEOUT; /* Still running. */ + } + else if (WIFEXITED (status) && WEXITSTATUS (status) == 127) + { + log_error (_("error running '%s': probably not installed\n"), pgmname); + ec = GPG_ERR_CONFIGURATION; + } + else if (WIFEXITED (status) && WEXITSTATUS (status)) + { + if (!r_exitcode) + log_error (_("error running '%s': exit status %d\n"), pgmname, + WEXITSTATUS (status)); + else + *r_exitcode = WEXITSTATUS (status); + ec = GPG_ERR_GENERAL; + } + else if (!WIFEXITED (status)) + { + log_error (_("error running '%s': terminated\n"), pgmname); + ec = GPG_ERR_GENERAL; + } + else + { + if (r_exitcode) + *r_exitcode = 0; + ec = 0; + } + + return gpg_err_make (GPG_ERR_SOURCE_DEFAULT, ec); +} + +/* See exechelp.h for a description. */ +gpg_error_t +gnupg_wait_processes (const char **pgmnames, pid_t *pids, size_t count, + int hang, int *r_exitcodes) +{ + gpg_err_code_t ec = 0; + size_t i, left; + int *dummy = NULL; + + if (r_exitcodes == NULL) + { + dummy = r_exitcodes = xtrymalloc (sizeof *r_exitcodes * count); + if (dummy == NULL) + return gpg_err_code_from_syserror (); + } + + for (i = 0, left = count; i < count; i++) + { + int status = -1; + + /* Skip invalid PID. */ + if (pids[i] == (pid_t)(-1)) + { + r_exitcodes[i] = -1; + left -= 1; + continue; + } + + /* See if there was a previously stored result for this pid. */ + if (get_result (pids[i], &status)) + left -= 1; + + r_exitcodes[i] = status; + } + + while (left > 0) + { + pid_t pid; + int status; + +#ifdef USE_NPTH + pid = npth_waitpid (-1, &status, hang ? 0 : WNOHANG); +#else + while ((pid = waitpid (-1, &status, hang ? 0 : WNOHANG)) == (pid_t)(-1) + && errno == EINTR); +#endif + + if (pid == (pid_t)(-1)) + { + ec = gpg_err_code_from_errno (errno); + log_error (_("waiting for processes to terminate failed: %s\n"), + strerror (errno)); + break; + } + else if (!pid) + { + ec = GPG_ERR_TIMEOUT; /* Still running. */ + break; + } + else + { + for (i = 0; i < count; i++) + if (pid == pids[i]) + break; + + if (i == count) + { + /* No match, store this result. */ + ec = store_result (pid, status); + if (ec) + break; + continue; + } + + /* Process PIDS[i] died. */ + if (r_exitcodes[i] != (pid_t) -1) + { + log_error ("PID %d was reused", pid); + ec = GPG_ERR_GENERAL; + break; + } + + left -= 1; + r_exitcodes[i] = status; + } + } + + for (i = 0; i < count; i++) + { + if (r_exitcodes[i] == -1) + continue; + + if (WIFEXITED (r_exitcodes[i]) && WEXITSTATUS (r_exitcodes[i]) == 127) + { + log_error (_("error running '%s': probably not installed\n"), + pgmnames[i]); + ec = GPG_ERR_CONFIGURATION; + } + else if (WIFEXITED (r_exitcodes[i]) && WEXITSTATUS (r_exitcodes[i])) + { + if (dummy) + log_error (_("error running '%s': exit status %d\n"), + pgmnames[i], WEXITSTATUS (r_exitcodes[i])); + else + r_exitcodes[i] = WEXITSTATUS (r_exitcodes[i]); + ec = GPG_ERR_GENERAL; + } + else if (!WIFEXITED (r_exitcodes[i])) + { + log_error (_("error running '%s': terminated\n"), pgmnames[i]); + ec = GPG_ERR_GENERAL; + } + } + + xfree (dummy); + return gpg_err_make (GPG_ERR_SOURCE_DEFAULT, ec); +} + + + +void +gnupg_release_process (pid_t pid) +{ + (void)pid; +} + + +/* Spawn a new process and immediately detach from it. The name of + the program to exec is PGMNAME and its arguments are in ARGV (the + programname is automatically passed as first argument). + Environment strings in ENVP are set. An error is returned if + pgmname is not executable; to make this work it is necessary to + provide an absolute file name. All standard file descriptors are + connected to /dev/null. */ +gpg_error_t +gnupg_spawn_process_detached (const char *pgmname, const char *argv[], + const char *envp[] ) +{ + gpg_err_code_t ec; + pid_t pid; + int i; + + if (getuid() != geteuid()) + return my_error (GPG_ERR_BUG); + + if ((ec = gnupg_access (pgmname, X_OK))) + return gpg_err_make (default_errsource, ec); + + pid = fork (); + if (pid == (pid_t)(-1)) + { + log_error (_("error forking process: %s\n"), strerror (errno)); + return my_error_from_syserror (); + } + if (!pid) + { + pid_t pid2; + + gcry_control (GCRYCTL_TERM_SECMEM); + if (setsid() == -1 || chdir ("/")) + _exit (1); + + pid2 = fork (); /* Double fork to let init take over the new child. */ + if (pid2 == (pid_t)(-1)) + _exit (1); + if (pid2) + _exit (0); /* Let the parent exit immediately. */ + + if (envp) + for (i=0; envp[i]; i++) + putenv (xstrdup (envp[i])); + + do_exec (pgmname, argv, -1, -1, -1, NULL, NULL, 0); + + /*NOTREACHED*/ + } + + if (waitpid (pid, NULL, 0) == -1) + log_error ("waitpid failed in gnupg_spawn_process_detached: %s", + strerror (errno)); + + return 0; +} + + +/* Kill a process; that is send an appropriate signal to the process. + gnupg_wait_process must be called to actually remove the process + from the system. An invalid PID is ignored. */ +void +gnupg_kill_process (pid_t pid) +{ + if (pid != (pid_t)(-1)) + { + kill (pid, SIGTERM); + } +} diff --git a/common/exechelp-w32.c b/common/exechelp-w32.c new file mode 100644 index 0000000..00cf3fc --- /dev/null +++ b/common/exechelp-w32.c @@ -0,0 +1,1043 @@ +/* exechelp-w32.c - Fork and exec helpers for W32. + * Copyright (C) 2004, 2007, 2008, 2009, + * 2010 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#if !defined(HAVE_W32_SYSTEM) || defined (HAVE_W32CE_SYSTEM) +#error This code is only used on W32. +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <assert.h> +#ifdef HAVE_SIGNAL_H +# include <signal.h> +#endif +#include <unistd.h> +#include <fcntl.h> + +#ifdef WITHOUT_NPTH /* Give the Makefile a chance to build without Pth. */ +#undef HAVE_NPTH +#undef USE_NPTH +#endif + +#ifdef HAVE_NPTH +#include <npth.h> +#endif + +#ifdef HAVE_STAT +# include <sys/stat.h> +#endif + + +#include "util.h" +#include "i18n.h" +#include "sysutils.h" +#include "exechelp.h" + +/* Define to 1 do enable debugging. */ +#define DEBUG_W32_SPAWN 0 + + +/* It seems Vista doesn't grok X_OK and so fails access() tests. + Previous versions interpreted X_OK as F_OK anyway, so we'll just + use F_OK directly. */ +#undef X_OK +#define X_OK F_OK + +/* We assume that a HANDLE can be represented by an int which should + be true for all i386 systems (HANDLE is defined as void *) and + these are the only systems for which Windows is available. Further + we assume that -1 denotes an invalid handle. */ +# define fd_to_handle(a) ((HANDLE)(a)) +# define handle_to_fd(a) ((int)(a)) +# define pid_to_handle(a) ((HANDLE)(a)) +# define handle_to_pid(a) ((int)(a)) + + +/* Helper */ +static inline gpg_error_t +my_error_from_syserror (void) +{ + return gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); +} + +static inline gpg_error_t +my_error (int errcode) +{ + return gpg_err_make (default_errsource, errcode); +} + + +/* Return the maximum number of currently allowed open file + descriptors. Only useful on POSIX systems but returns a value on + other systems too. */ +int +get_max_fds (void) +{ + int max_fds = -1; + +#ifdef OPEN_MAX + if (max_fds == -1) + max_fds = OPEN_MAX; +#endif + + if (max_fds == -1) + max_fds = 256; /* Arbitrary limit. */ + + return max_fds; +} + + +/* Under Windows this is a dummy function. */ +void +close_all_fds (int first, int *except) +{ + (void)first; + (void)except; +} + + +/* Returns an array with all currently open file descriptors. The end + * of the array is marked by -1. The caller needs to release this + * array using the *standard free* and not with xfree. This allow the + * use of this function right at startup even before libgcrypt has + * been initialized. Returns NULL on error and sets ERRNO + * accordingly. Note that fstat prints a warning to DebugView for all + * invalid fds which is a bit annoying. We actually do not need this + * function in real code (close_all_fds is a dummy anyway) but we keep + * it for use by t-exechelp.c. */ +int * +get_all_open_fds (void) +{ + int *array; + size_t narray; + int fd, max_fd, idx; +#ifndef HAVE_STAT + array = calloc (1, sizeof *array); + if (array) + array[0] = -1; +#else /*HAVE_STAT*/ + struct stat statbuf; + + max_fd = get_max_fds (); + narray = 32; /* If you change this change also t-exechelp.c. */ + array = calloc (narray, sizeof *array); + if (!array) + return NULL; + + /* Note: The list we return is ordered. */ + for (idx=0, fd=0; fd < max_fd; fd++) + if (!(fstat (fd, &statbuf) == -1 && errno == EBADF)) + { + if (idx+1 >= narray) + { + int *tmp; + + narray += (narray < 256)? 32:256; + tmp = realloc (array, narray * sizeof *array); + if (!tmp) + { + free (array); + return NULL; + } + array = tmp; + } + array[idx++] = fd; + } + array[idx] = -1; +#endif /*HAVE_STAT*/ + return array; +} + + +/* Helper function to build_w32_commandline. */ +static char * +build_w32_commandline_copy (char *buffer, const char *string) +{ + char *p = buffer; + const char *s; + + if (!*string) /* Empty string. */ + p = stpcpy (p, "\"\""); + else if (strpbrk (string, " \t\n\v\f\"")) + { + /* Need to do some kind of quoting. */ + p = stpcpy (p, "\""); + for (s=string; *s; s++) + { + *p++ = *s; + if (*s == '\"') + *p++ = *s; + } + *p++ = '\"'; + *p = 0; + } + else + p = stpcpy (p, string); + + return p; +} + +/* Build a command line for use with W32's CreateProcess. On success + CMDLINE gets the address of a newly allocated string. */ +static gpg_error_t +build_w32_commandline (const char *pgmname, const char * const *argv, + char **cmdline) +{ + int i, n; + const char *s; + char *buf, *p; + + *cmdline = NULL; + n = 0; + s = pgmname; + n += strlen (s) + 1 + 2; /* (1 space, 2 quoting */ + for (; *s; s++) + if (*s == '\"') + n++; /* Need to double inner quotes. */ + for (i=0; (s=argv[i]); i++) + { + n += strlen (s) + 1 + 2; /* (1 space, 2 quoting */ + for (; *s; s++) + if (*s == '\"') + n++; /* Need to double inner quotes. */ + } + n++; + + buf = p = xtrymalloc (n); + if (!buf) + return my_error_from_syserror (); + + p = build_w32_commandline_copy (p, pgmname); + for (i=0; argv[i]; i++) + { + *p++ = ' '; + p = build_w32_commandline_copy (p, argv[i]); + } + + *cmdline= buf; + return 0; +} + + +#define INHERIT_READ 1 +#define INHERIT_WRITE 2 +#define INHERIT_BOTH (INHERIT_READ|INHERIT_WRITE) + +/* Create pipe. FLAGS indicates which ends are inheritable. */ +static int +create_inheritable_pipe (HANDLE filedes[2], int flags) +{ + HANDLE r, w; + SECURITY_ATTRIBUTES sec_attr; + + memset (&sec_attr, 0, sizeof sec_attr ); + sec_attr.nLength = sizeof sec_attr; + sec_attr.bInheritHandle = TRUE; + + if (!CreatePipe (&r, &w, &sec_attr, 0)) + return -1; + + if ((flags & INHERIT_READ) == 0) + if (! SetHandleInformation (r, HANDLE_FLAG_INHERIT, 0)) + goto fail; + + if ((flags & INHERIT_WRITE) == 0) + if (! SetHandleInformation (w, HANDLE_FLAG_INHERIT, 0)) + goto fail; + + filedes[0] = r; + filedes[1] = w; + return 0; + + fail: + log_error ("SetHandleInformation failed: %s\n", w32_strerror (-1)); + CloseHandle (r); + CloseHandle (w); + return -1; +} + + +static HANDLE +w32_open_null (int for_write) +{ + HANDLE hfile; + + hfile = CreateFileW (L"nul", + for_write? GENERIC_WRITE : GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, 0, NULL); + if (hfile == INVALID_HANDLE_VALUE) + log_debug ("can't open 'nul': %s\n", w32_strerror (-1)); + return hfile; +} + + +static gpg_error_t +create_pipe_and_estream (int filedes[2], int flags, + estream_t *r_fp, int outbound, int nonblock) +{ + gpg_error_t err = 0; + HANDLE fds[2]; + es_syshd_t syshd; + + filedes[0] = filedes[1] = -1; + err = my_error (GPG_ERR_GENERAL); + if (!create_inheritable_pipe (fds, flags)) + { + filedes[0] = _open_osfhandle (handle_to_fd (fds[0]), O_RDONLY); + if (filedes[0] == -1) + { + log_error ("failed to translate osfhandle %p\n", fds[0]); + CloseHandle (fds[1]); + } + else + { + filedes[1] = _open_osfhandle (handle_to_fd (fds[1]), O_APPEND); + if (filedes[1] == -1) + { + log_error ("failed to translate osfhandle %p\n", fds[1]); + close (filedes[0]); + filedes[0] = -1; + CloseHandle (fds[1]); + } + else + err = 0; + } + } + + if (! err && r_fp) + { + syshd.type = ES_SYSHD_HANDLE; + if (!outbound) + { + syshd.u.handle = fds[0]; + *r_fp = es_sysopen (&syshd, nonblock? "r,nonblock" : "r"); + } + else + { + syshd.u.handle = fds[1]; + *r_fp = es_sysopen (&syshd, nonblock? "w,nonblock" : "w"); + } + if (!*r_fp) + { + err = my_error_from_syserror (); + log_error (_("error creating a stream for a pipe: %s\n"), + gpg_strerror (err)); + close (filedes[0]); + close (filedes[1]); + filedes[0] = filedes[1] = -1; + return err; + } + } + + return err; +} + +/* Portable function to create a pipe. Under Windows the write end is + inheritable. If R_FP is not NULL, an estream is created for the + read end and stored at R_FP. */ +gpg_error_t +gnupg_create_inbound_pipe (int filedes[2], estream_t *r_fp, int nonblock) +{ + return create_pipe_and_estream (filedes, INHERIT_WRITE, + r_fp, 0, nonblock); +} + + +/* Portable function to create a pipe. Under Windows the read end is + inheritable. If R_FP is not NULL, an estream is created for the + write end and stored at R_FP. */ +gpg_error_t +gnupg_create_outbound_pipe (int filedes[2], estream_t *r_fp, int nonblock) +{ + return create_pipe_and_estream (filedes, INHERIT_READ, + r_fp, 1, nonblock); +} + + +/* Portable function to create a pipe. Under Windows both ends are + inheritable. */ +gpg_error_t +gnupg_create_pipe (int filedes[2]) +{ + return create_pipe_and_estream (filedes, INHERIT_BOTH, + NULL, 0, 0); +} + + +/* Fork and exec the PGMNAME, see exechelp.h for details. */ +gpg_error_t +gnupg_spawn_process (const char *pgmname, const char *argv[], + int *except, void (*preexec)(void), unsigned int flags, + estream_t *r_infp, + estream_t *r_outfp, + estream_t *r_errfp, + pid_t *pid) +{ + gpg_error_t err; + SECURITY_ATTRIBUTES sec_attr; + PROCESS_INFORMATION pi = + { + NULL, /* Returns process handle. */ + 0, /* Returns primary thread handle. */ + 0, /* Returns pid. */ + 0 /* Returns tid. */ + }; + STARTUPINFOW si; + int cr_flags; + char *cmdline; + wchar_t *wcmdline = NULL; + wchar_t *wpgmname = NULL; + HANDLE inpipe[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}; + HANDLE outpipe[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}; + HANDLE errpipe[2] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}; + estream_t infp = NULL; + estream_t outfp = NULL; + estream_t errfp = NULL; + HANDLE nullhd[3] = {INVALID_HANDLE_VALUE, + INVALID_HANDLE_VALUE, + INVALID_HANDLE_VALUE}; + int i, rc; + es_syshd_t syshd; + gpg_err_source_t errsource = default_errsource; + int nonblock = !!(flags & GNUPG_SPAWN_NONBLOCK); + + (void)except; /* Not yet used. */ + + if (r_infp) + *r_infp = NULL; + if (r_outfp) + *r_outfp = NULL; + if (r_errfp) + *r_errfp = NULL; + *pid = (pid_t)(-1); /* Always required. */ + + if (r_infp) + { + if (create_inheritable_pipe (inpipe, INHERIT_READ)) + { + err = gpg_err_make (errsource, GPG_ERR_GENERAL); + log_error (_("error creating a pipe: %s\n"), gpg_strerror (err)); + return err; + } + + syshd.type = ES_SYSHD_HANDLE; + syshd.u.handle = inpipe[1]; + infp = es_sysopen (&syshd, nonblock? "w,nonblock" : "w"); + if (!infp) + { + err = gpg_err_make (errsource, gpg_err_code_from_syserror ()); + log_error (_("error creating a stream for a pipe: %s\n"), + gpg_strerror (err)); + CloseHandle (inpipe[0]); + CloseHandle (inpipe[1]); + inpipe[0] = inpipe[1] = INVALID_HANDLE_VALUE; + return err; + } + } + + if (r_outfp) + { + if (create_inheritable_pipe (outpipe, INHERIT_WRITE)) + { + err = gpg_err_make (errsource, GPG_ERR_GENERAL); + log_error (_("error creating a pipe: %s\n"), gpg_strerror (err)); + return err; + } + + syshd.type = ES_SYSHD_HANDLE; + syshd.u.handle = outpipe[0]; + outfp = es_sysopen (&syshd, nonblock? "r,nonblock" : "r"); + if (!outfp) + { + err = gpg_err_make (errsource, gpg_err_code_from_syserror ()); + log_error (_("error creating a stream for a pipe: %s\n"), + gpg_strerror (err)); + CloseHandle (outpipe[0]); + CloseHandle (outpipe[1]); + outpipe[0] = outpipe[1] = INVALID_HANDLE_VALUE; + if (infp) + es_fclose (infp); + else if (inpipe[1] != INVALID_HANDLE_VALUE) + CloseHandle (inpipe[1]); + if (inpipe[0] != INVALID_HANDLE_VALUE) + CloseHandle (inpipe[0]); + return err; + } + } + + if (r_errfp) + { + if (create_inheritable_pipe (errpipe, INHERIT_WRITE)) + { + err = gpg_err_make (errsource, GPG_ERR_GENERAL); + log_error (_("error creating a pipe: %s\n"), gpg_strerror (err)); + return err; + } + + syshd.type = ES_SYSHD_HANDLE; + syshd.u.handle = errpipe[0]; + errfp = es_sysopen (&syshd, nonblock? "r,nonblock" : "r"); + if (!errfp) + { + err = gpg_err_make (errsource, gpg_err_code_from_syserror ()); + log_error (_("error creating a stream for a pipe: %s\n"), + gpg_strerror (err)); + CloseHandle (errpipe[0]); + CloseHandle (errpipe[1]); + errpipe[0] = errpipe[1] = INVALID_HANDLE_VALUE; + if (outfp) + es_fclose (outfp); + else if (outpipe[0] != INVALID_HANDLE_VALUE) + CloseHandle (outpipe[0]); + if (outpipe[1] != INVALID_HANDLE_VALUE) + CloseHandle (outpipe[1]); + if (infp) + es_fclose (infp); + else if (inpipe[1] != INVALID_HANDLE_VALUE) + CloseHandle (inpipe[1]); + if (inpipe[0] != INVALID_HANDLE_VALUE) + CloseHandle (inpipe[0]); + return err; + } + } + + /* Prepare security attributes. */ + memset (&sec_attr, 0, sizeof sec_attr ); + sec_attr.nLength = sizeof sec_attr; + sec_attr.bInheritHandle = FALSE; + + /* Build the command line. */ + err = build_w32_commandline (pgmname, argv, &cmdline); + if (err) + return err; + + if (inpipe[0] == INVALID_HANDLE_VALUE) + nullhd[0] = ((flags & GNUPG_SPAWN_KEEP_STDIN)? + GetStdHandle (STD_INPUT_HANDLE) : w32_open_null (0)); + if (outpipe[1] == INVALID_HANDLE_VALUE) + nullhd[1] = ((flags & GNUPG_SPAWN_KEEP_STDOUT)? + GetStdHandle (STD_OUTPUT_HANDLE) : w32_open_null (1)); + if (errpipe[1] == INVALID_HANDLE_VALUE) + nullhd[2] = ((flags & GNUPG_SPAWN_KEEP_STDOUT)? + GetStdHandle (STD_ERROR_HANDLE) : w32_open_null (1)); + + /* Start the process. Note that we can't run the PREEXEC function + because this might change our own environment. */ + (void)preexec; + + memset (&si, 0, sizeof si); + si.cb = sizeof (si); + si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; + si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_HIDE; + si.hStdInput = inpipe[0] == INVALID_HANDLE_VALUE? nullhd[0] : inpipe[0]; + si.hStdOutput = outpipe[1] == INVALID_HANDLE_VALUE? nullhd[1] : outpipe[1]; + si.hStdError = errpipe[1] == INVALID_HANDLE_VALUE? nullhd[2] : errpipe[1]; + + cr_flags = (CREATE_DEFAULT_ERROR_MODE + | ((flags & GNUPG_SPAWN_DETACHED)? DETACHED_PROCESS : 0) + | GetPriorityClass (GetCurrentProcess ()) + | CREATE_SUSPENDED); + /* log_debug ("CreateProcess, path='%s' cmdline='%s'\n", */ + /* pgmname, cmdline); */ + /* Take care: CreateProcessW may modify wpgmname */ + if (!(wpgmname = utf8_to_wchar (pgmname))) + rc = 0; + else if (!(wcmdline = utf8_to_wchar (cmdline))) + rc = 0; + else + rc = CreateProcessW (wpgmname, /* Program to start. */ + wcmdline, /* Command line arguments. */ + &sec_attr, /* Process security attributes. */ + &sec_attr, /* Thread security attributes. */ + TRUE, /* Inherit handles. */ + cr_flags, /* Creation flags. */ + NULL, /* Environment. */ + NULL, /* Use current drive/directory. */ + &si, /* Startup information. */ + &pi /* Returns process information. */ + ); + if (!rc) + { + if (!wpgmname || !wcmdline) + log_error ("CreateProcess failed (utf8_to_wchar): %s\n", + strerror (errno)); + else + log_error ("CreateProcess failed: %s\n", w32_strerror (-1)); + xfree (wpgmname); + xfree (wcmdline); + xfree (cmdline); + if (infp) + es_fclose (infp); + else if (inpipe[1] != INVALID_HANDLE_VALUE) + CloseHandle (outpipe[1]); + if (inpipe[0] != INVALID_HANDLE_VALUE) + CloseHandle (inpipe[0]); + if (outfp) + es_fclose (outfp); + else if (outpipe[0] != INVALID_HANDLE_VALUE) + CloseHandle (outpipe[0]); + if (outpipe[1] != INVALID_HANDLE_VALUE) + CloseHandle (outpipe[1]); + if (errfp) + es_fclose (errfp); + else if (errpipe[0] != INVALID_HANDLE_VALUE) + CloseHandle (errpipe[0]); + if (errpipe[1] != INVALID_HANDLE_VALUE) + CloseHandle (errpipe[1]); + return gpg_err_make (errsource, GPG_ERR_GENERAL); + } + xfree (wpgmname); + xfree (wcmdline); + xfree (cmdline); + cmdline = NULL; + + /* Close the inherited handles to /dev/null. */ + for (i=0; i < DIM (nullhd); i++) + if (nullhd[i] != INVALID_HANDLE_VALUE) + CloseHandle (nullhd[i]); + + /* Close the inherited ends of the pipes. */ + if (inpipe[0] != INVALID_HANDLE_VALUE) + CloseHandle (inpipe[0]); + if (outpipe[1] != INVALID_HANDLE_VALUE) + CloseHandle (outpipe[1]); + if (errpipe[1] != INVALID_HANDLE_VALUE) + CloseHandle (errpipe[1]); + + /* log_debug ("CreateProcess ready: hProcess=%p hThread=%p" */ + /* " dwProcessID=%d dwThreadId=%d\n", */ + /* pi.hProcess, pi.hThread, */ + /* (int) pi.dwProcessId, (int) pi.dwThreadId); */ + /* log_debug (" outfp=%p errfp=%p\n", outfp, errfp); */ + + /* Fixme: For unknown reasons AllowSetForegroundWindow returns an + invalid argument error if we pass it the correct processID. As a + workaround we use -1 (ASFW_ANY). */ + if ((flags & GNUPG_SPAWN_RUN_ASFW)) + gnupg_allow_set_foregound_window ((pid_t)(-1)/*pi.dwProcessId*/); + + /* Process has been created suspended; resume it now. */ + ResumeThread (pi.hThread); + CloseHandle (pi.hThread); + + if (r_infp) + *r_infp = infp; + if (r_outfp) + *r_outfp = outfp; + if (r_errfp) + *r_errfp = errfp; + + *pid = handle_to_pid (pi.hProcess); + return 0; + +} + + + +/* Simplified version of gnupg_spawn_process. This function forks and + then execs PGMNAME, while connecting INFD to stdin, OUTFD to stdout + and ERRFD to stderr (any of them may be -1 to connect them to + /dev/null). The arguments for the process are expected in the NULL + terminated array ARGV. The program name itself should not be + included there. Calling gnupg_wait_process is required. + + Returns 0 on success or an error code. */ +gpg_error_t +gnupg_spawn_process_fd (const char *pgmname, const char *argv[], + int infd, int outfd, int errfd, pid_t *pid) +{ + gpg_error_t err; + SECURITY_ATTRIBUTES sec_attr; + PROCESS_INFORMATION pi = { NULL, 0, 0, 0 }; + STARTUPINFOW si; + char *cmdline; + wchar_t *wcmdline = NULL; + wchar_t *wpgmname = NULL; + int i, rc; + HANDLE stdhd[3]; + + /* Setup return values. */ + *pid = (pid_t)(-1); + + /* Prepare security attributes. */ + memset (&sec_attr, 0, sizeof sec_attr ); + sec_attr.nLength = sizeof sec_attr; + sec_attr.bInheritHandle = FALSE; + + /* Build the command line. */ + err = build_w32_commandline (pgmname, argv, &cmdline); + if (err) + return err; + + memset (&si, 0, sizeof si); + si.cb = sizeof (si); + si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; + si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_MINIMIZE; + stdhd[0] = infd == -1? w32_open_null (0) : INVALID_HANDLE_VALUE; + stdhd[1] = outfd == -1? w32_open_null (1) : INVALID_HANDLE_VALUE; + stdhd[2] = errfd == -1? w32_open_null (1) : INVALID_HANDLE_VALUE; + si.hStdInput = infd == -1? stdhd[0] : (void*)_get_osfhandle (infd); + si.hStdOutput = outfd == -1? stdhd[1] : (void*)_get_osfhandle (outfd); + si.hStdError = errfd == -1? stdhd[2] : (void*)_get_osfhandle (errfd); + +/* log_debug ("CreateProcess, path='%s' cmdline='%s'\n", pgmname, cmdline); */ + /* Take care: CreateProcessW may modify wpgmname */ + if (!(wpgmname = utf8_to_wchar (pgmname))) + rc = 0; + else if (!(wcmdline = utf8_to_wchar (cmdline))) + rc = 0; + else + rc = CreateProcessW (wpgmname, /* Program to start. */ + wcmdline, /* Command line arguments. */ + &sec_attr, /* Process security attributes. */ + &sec_attr, /* Thread security attributes. */ + TRUE, /* Inherit handles. */ + (CREATE_DEFAULT_ERROR_MODE + | GetPriorityClass (GetCurrentProcess ()) + | CREATE_SUSPENDED | DETACHED_PROCESS), + NULL, /* Environment. */ + NULL, /* Use current drive/directory. */ + &si, /* Startup information. */ + &pi /* Returns process information. */ + ); + if (!rc) + { + if (!wpgmname || !wcmdline) + log_error ("CreateProcess failed (utf8_to_wchar): %s\n", + strerror (errno)); + else + log_error ("CreateProcess failed: %s\n", w32_strerror (-1)); + err = my_error (GPG_ERR_GENERAL); + } + else + err = 0; + xfree (wpgmname); + xfree (wcmdline); + xfree (cmdline); + for (i=0; i < 3; i++) + if (stdhd[i] != INVALID_HANDLE_VALUE) + CloseHandle (stdhd[i]); + if (err) + return err; + +/* log_debug ("CreateProcess ready: hProcess=%p hThread=%p" */ +/* " dwProcessID=%d dwThreadId=%d\n", */ +/* pi.hProcess, pi.hThread, */ +/* (int) pi.dwProcessId, (int) pi.dwThreadId); */ + + /* Process has been created suspended; resume it now. */ + ResumeThread (pi.hThread); + CloseHandle (pi.hThread); + + *pid = handle_to_pid (pi.hProcess); + return 0; + +} + + +/* See exechelp.h for a description. */ +gpg_error_t +gnupg_wait_process (const char *pgmname, pid_t pid, int hang, int *r_exitcode) +{ + return gnupg_wait_processes (&pgmname, &pid, 1, hang, r_exitcode); +} + +/* See exechelp.h for a description. */ +gpg_error_t +gnupg_wait_processes (const char **pgmnames, pid_t *pids, size_t count, + int hang, int *r_exitcodes) +{ + gpg_err_code_t ec = 0; + size_t i; + HANDLE *procs; + int code; + + procs = xtrycalloc (count, sizeof *procs); + if (procs == NULL) + return my_error_from_syserror (); + + for (i = 0; i < count; i++) + { + if (r_exitcodes) + r_exitcodes[i] = -1; + + if (pids[i] == (pid_t)(-1)) + return my_error (GPG_ERR_INV_VALUE); + + procs[i] = fd_to_handle (pids[i]); + } + + /* FIXME: We should do a pth_waitpid here. However this has not yet + been implemented. A special W32 pth system call would even be + better. */ + code = WaitForMultipleObjects (count, procs, TRUE, hang? INFINITE : 0); + switch (code) + { + case WAIT_TIMEOUT: + ec = GPG_ERR_TIMEOUT; + goto leave; + + case WAIT_FAILED: + log_error (_("waiting for processes to terminate failed: %s\n"), + w32_strerror (-1)); + ec = GPG_ERR_GENERAL; + goto leave; + + case WAIT_OBJECT_0: + for (i = 0; i < count; i++) + { + DWORD exc; + + if (! GetExitCodeProcess (procs[i], &exc)) + { + log_error (_("error getting exit code of process %d: %s\n"), + (int) pids[i], w32_strerror (-1) ); + ec = GPG_ERR_GENERAL; + } + else if (exc) + { + if (!r_exitcodes) + log_error (_("error running '%s': exit status %d\n"), + pgmnames[i], (int)exc); + else + r_exitcodes[i] = (int)exc; + ec = GPG_ERR_GENERAL; + } + else + { + if (r_exitcodes) + r_exitcodes[i] = 0; + } + } + break; + + default: + log_error ("WaitForMultipleObjects returned unexpected " + "code %d\n", code); + ec = GPG_ERR_GENERAL; + break; + } + + leave: + return gpg_err_make (GPG_ERR_SOURCE_DEFAULT, ec); +} + + + +void +gnupg_release_process (pid_t pid) +{ + if (pid != (pid_t)INVALID_HANDLE_VALUE) + { + HANDLE process = (HANDLE)pid; + + CloseHandle (process); + } +} + + +/* Spawn a new process and immediately detach from it. The name of + the program to exec is PGMNAME and its arguments are in ARGV (the + programname is automatically passed as first argument). + Environment strings in ENVP are set. An error is returned if + pgmname is not executable; to make this work it is necessary to + provide an absolute file name. All standard file descriptors are + connected to /dev/null. */ +gpg_error_t +gnupg_spawn_process_detached (const char *pgmname, const char *argv[], + const char *envp[] ) +{ + gpg_error_t err; + SECURITY_ATTRIBUTES sec_attr; + PROCESS_INFORMATION pi = + { + NULL, /* Returns process handle. */ + 0, /* Returns primary thread handle. */ + 0, /* Returns pid. */ + 0 /* Returns tid. */ + }; + STARTUPINFOW si; + int cr_flags; + char *cmdline; + wchar_t *wcmdline = NULL; + wchar_t *wpgmname = NULL; + BOOL in_job = FALSE; + gpg_err_code_t ec; + int rc; + int jobdebug; + + /* We don't use ENVP. */ + (void)envp; + + cmdline = getenv ("GNUPG_EXEC_DEBUG_FLAGS"); + jobdebug = (cmdline && (atoi (cmdline) & 1)); + + if ((ec = gnupg_access (pgmname, X_OK))) + return gpg_err_make (default_errsource, ec); + + /* Prepare security attributes. */ + memset (&sec_attr, 0, sizeof sec_attr ); + sec_attr.nLength = sizeof sec_attr; + sec_attr.bInheritHandle = FALSE; + + /* Build the command line. */ + err = build_w32_commandline (pgmname, argv, &cmdline); + if (err) + return err; + + /* Start the process. */ + memset (&si, 0, sizeof si); + si.cb = sizeof (si); + si.dwFlags = STARTF_USESHOWWINDOW; + si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_MINIMIZE; + + cr_flags = (CREATE_DEFAULT_ERROR_MODE + | GetPriorityClass (GetCurrentProcess ()) + | CREATE_NEW_PROCESS_GROUP + | DETACHED_PROCESS); + + /* Check if we were spawned as part of a Job. + * In a job we need to add CREATE_BREAKAWAY_FROM_JOB + * to the cr_flags, otherwise our child processes + * are killed when we terminate. */ + if (!IsProcessInJob (GetCurrentProcess(), NULL, &in_job)) + { + log_error ("IsProcessInJob() failed: %s\n", w32_strerror (-1)); + in_job = FALSE; + } + + if (in_job) + { + /* Only try to break away from job if it is allowed, otherwise + * CreateProcess() would fail with an "Access is denied" error. */ + JOBOBJECT_EXTENDED_LIMIT_INFORMATION info; + if (!QueryInformationJobObject (NULL, JobObjectExtendedLimitInformation, + &info, sizeof info, NULL)) + { + log_error ("QueryInformationJobObject() failed: %s\n", + w32_strerror (-1)); + } + else if ((info.BasicLimitInformation.LimitFlags & + JOB_OBJECT_LIMIT_BREAKAWAY_OK)) + { + if (jobdebug) + log_debug ("Using CREATE_BREAKAWAY_FROM_JOB flag\n"); + cr_flags |= CREATE_BREAKAWAY_FROM_JOB; + } + else if ((info.BasicLimitInformation.LimitFlags & + JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK)) + { + /* The child process should automatically detach from the job. */ + if (jobdebug) + log_debug ("Not using CREATE_BREAKAWAY_FROM_JOB flag; " + "JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK is set\n"); + } + else + { + /* It seems that the child process must remain in the job. + * This is not necessarily an error, although it can cause premature + * termination of the child process when the job is closed. */ + if (jobdebug) + log_debug ("Not using CREATE_BREAKAWAY_FROM_JOB flag\n"); + } + } + else + { + if (jobdebug) + log_debug ("Process is not in a Job\n"); + } + + /* log_debug ("CreateProcess(detached), path='%s' cmdline='%s'\n", */ + /* pgmname, cmdline); */ + /* Take care: CreateProcessW may modify wpgmname */ + if (!(wpgmname = utf8_to_wchar (pgmname))) + rc = 0; + else if (!(wcmdline = utf8_to_wchar (cmdline))) + rc = 0; + else + rc = CreateProcessW (wpgmname, /* Program to start. */ + wcmdline, /* Command line arguments. */ + &sec_attr, /* Process security attributes. */ + &sec_attr, /* Thread security attributes. */ + FALSE, /* Inherit handles. */ + cr_flags, /* Creation flags. */ + NULL, /* Environment. */ + NULL, /* Use current drive/directory. */ + &si, /* Startup information. */ + &pi /* Returns process information. */ + ); + if (!rc) + { + if (!wpgmname || !wcmdline) + log_error ("CreateProcess failed (utf8_to_wchar): %s\n", + strerror (errno)); + else + log_error ("CreateProcess(detached) failed: %s\n", w32_strerror (-1)); + xfree (wpgmname); + xfree (wcmdline); + xfree (cmdline); + return my_error (GPG_ERR_GENERAL); + } + xfree (wpgmname); + xfree (wcmdline); + xfree (cmdline); + cmdline = NULL; + +/* log_debug ("CreateProcess(detached) ready: hProcess=%p hThread=%p" */ +/* " dwProcessID=%d dwThreadId=%d\n", */ +/* pi.hProcess, pi.hThread, */ +/* (int) pi.dwProcessId, (int) pi.dwThreadId); */ + + CloseHandle (pi.hThread); + CloseHandle (pi.hProcess); + + return 0; +} + + +/* Kill a process; that is send an appropriate signal to the process. + gnupg_wait_process must be called to actually remove the process + from the system. An invalid PID is ignored. */ +void +gnupg_kill_process (pid_t pid) +{ + if (pid != (pid_t) INVALID_HANDLE_VALUE) + { + HANDLE process = (HANDLE) pid; + + /* Arbitrary error code. */ + TerminateProcess (process, 1); + } +} diff --git a/common/exechelp-w32ce.c b/common/exechelp-w32ce.c new file mode 100644 index 0000000..ec9f014 --- /dev/null +++ b/common/exechelp-w32ce.c @@ -0,0 +1,886 @@ +/* exechelp-w32.c - Fork and exec helpers for W32CE. + * Copyright (C) 2004, 2007, 2008, 2009, + * 2010 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#if !defined(HAVE_W32_SYSTEM) && !defined (HAVE_W32CE_SYSTEM) +#error This code is only used on W32CE. +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <assert.h> +#ifdef HAVE_SIGNAL_H +# include <signal.h> +#endif +#include <unistd.h> +#include <fcntl.h> + +#ifdef WITHOUT_NPTH /* Give the Makefile a chance to build without Pth. */ +#undef HAVE_NPTH +#undef USE_NPTH +#endif + +#ifdef HAVE_NPTH +#include <npth.h> +#endif + +#ifdef HAVE_STAT +# include <sys/stat.h> +#endif + +#include <assuan.h> + +#include "util.h" +#include "i18n.h" +#include "sysutils.h" +#include "exechelp.h" + + +/* It seems Vista doesn't grok X_OK and so fails access() tests. + Previous versions interpreted X_OK as F_OK anyway, so we'll just + use F_OK directly. */ +#undef X_OK +#define X_OK F_OK + + +/* We assume that a HANDLE can be represented by an int which should + be true for all i386 systems (HANDLE is defined as void *) and + these are the only systems for which Windows is available. Further + we assume that -1 denotes an invalid handle. */ +#define fd_to_handle(a) ((HANDLE)(a)) +#define handle_to_fd(a) ((int)(a)) +#define pid_to_handle(a) ((HANDLE)(a)) +#define handle_to_pid(a) ((int)(a)) + + +#ifdef USE_NPTH +/* The data passed to the feeder_thread. */ +struct feeder_thread_parms +{ + estream_t stream; + volatile int stream_valid; + HANDLE hd; + int direction; +}; + + +/* The thread started by start_feede3. */ +static void * +feeder_thread (void *arg) +{ + struct feeder_thread_parms *parm = arg; + char buffer[4096]; + int rc; + + if (parm->direction) + { + size_t nread = 0; + DWORD nwritten; + + log_debug ("feeder_thread estream->pipe: stream=%p pipe=%p\n", + parm->stream, parm->hd); + while (parm->stream_valid + && !es_read (parm->stream, buffer, sizeof buffer, &nread)) + { + do + { + pth_enter (); + rc = WriteFile (parm->hd, buffer, nread, &nwritten, NULL); + pth_leave (); + if (!rc) + { + log_debug ("feeder(%p): WriteFile error: rc=%d\n", + parm->hd, (int)GetLastError ()); + goto leave; + } + nread -= nwritten; + } + while (nread); + } + if (!parm->stream_valid) + log_debug ("feeder(%p): closed by other thread\n", parm->hd); + else if (nread) + log_debug ("feeder(%p): es_read error: %s\n", + parm->hd, strerror (errno)); + } + else + { + DWORD nread = 0; + size_t nwritten; + + log_debug ("feeder_thread pipe->estream: stream=%p pipe=%p\n", + parm->stream, parm->hd); + while ( (pth_enter (), + (rc = ReadFile (parm->hd, buffer, sizeof buffer, &nread, NULL)), + pth_leave (), + rc) && nread) + { + log_debug ("feeder_thread pipe->estream: read %d bytes\n", + (int)nread); + do + { + if (parm->stream_valid + && es_write (parm->stream, buffer, nread, &nwritten)) + { + log_debug ("feeder(%p): es_write error: %s\n", + parm->hd, strerror (errno)); + goto leave; + } + log_debug ("feeder_thread pipe->estream: es_wrote %d bytes\n", + (int)nwritten); + nread -= nwritten; + } + while (nread && parm->stream_valid); + } + if (!parm->stream_valid) + log_debug ("feeder(%p): closed by other thread\n", parm->hd); + else if (nread) + log_debug ("feeder(%p): ReadFile error: rc=%d\n", + parm->hd, (int)GetLastError ()); + else + log_debug ("feeder(%p): eof\n", parm->hd); + } + +leave: + log_debug ("feeder(%p): waiting for es_fclose\n", parm->hd); + while (parm->stream_valid) + pth_yield (NULL); + log_debug ("feeder(%p): about to close the pipe handle\n", parm->hd); + CloseHandle (parm->hd); + log_debug ("feeder(%p): pipe handle closed\n", parm->hd); + xfree (parm); + return NULL; +} +#endif /*USE_NPTH*/ + +#ifdef USE_NPTH +static void +feeder_onclose_notification (estream_t stream, void *opaque) +{ + struct feeder_thread_parms *parm = opaque; + (void)stream; + log_debug ("feeder(%p): received onclose note\n", parm->hd); + parm->stream_valid = 0; +} +#endif /*USE_NPTH*/ + +/* Fire up a thread to copy data between STREAM and a pipe's + descriptor FD. With DIRECTION set to true the copy takes place + from the stream to the pipe, otherwise from the pipe to the + stream. */ +static gpg_error_t +start_feeder (estream_t stream, HANDLE hd, int direction) +{ +#ifdef USE_NPTH + gpg_error_t err; + struct feeder_thread_parms *parm; + pth_attr_t tattr; + + parm = xtrymalloc (sizeof *parm); + if (!parm) + return gpg_error_from_syserror (); + parm->stream = stream; + parm->stream_valid = 1; + parm->hd = hd; + parm->direction = direction; + + if (es_onclose (stream, 1, feeder_onclose_notification, parm)) + { + err = gpg_error_from_syserror (); + xfree (parm); + return err; + } + + tattr = pth_attr_new (); + pth_attr_set (tattr, PTH_ATTR_JOINABLE, 0); + pth_attr_set (tattr, PTH_ATTR_STACK_SIZE, 64*1024); + pth_attr_set (tattr, PTH_ATTR_NAME, "exec-feeder"); + + log_debug ("spawning new feeder(%p, %p, %d)\n", stream, hd, direction); + if(!pth_spawn (tattr, feeder_thread, parm)) + { + err = gpg_error_from_syserror (); + es_onclose (stream, 0, feeder_onclose_notification, parm); + xfree (parm); + } + else + err = 0; + pth_attr_destroy (tattr); + + return err; +#else + (void)stream; + (void)hd; + (void)direction; + return gpg_error (GPG_ERR_NOT_IMPLEMENTED); /* No Pth. */ +#endif +} + + + +/* Return the maximum number of currently allowed open file + descriptors. Only useful on POSIX systems but returns a value on + other systems too. */ +int +get_max_fds (void) +{ + int max_fds = -1; + +#ifdef OPEN_MAX + if (max_fds == -1) + max_fds = OPEN_MAX; +#endif + + if (max_fds == -1) + max_fds = 256; /* Arbitrary limit. */ + + return max_fds; +} + + +/* Under Windows this is a dummy function. */ +void +close_all_fds (int first, int *except) +{ + (void)first; + (void)except; +} + + +/* Returns an array with all currently open file descriptors. The end + of the array is marked by -1. The caller needs to release this + array using the *standard free* and not with xfree. This allow the + use of this function right at startup even before libgcrypt has + been initialized. Returns NULL on error and sets ERRNO + accordingly. */ +int * +get_all_open_fds (void) +{ + int *array; + size_t narray; + int fd, max_fd, idx; +#ifndef HAVE_STAT + array = calloc (1, sizeof *array); + if (array) + array[0] = -1; +#else /*HAVE_STAT*/ + struct stat statbuf; + + max_fd = get_max_fds (); + narray = 32; /* If you change this change also t-exechelp.c. */ + array = calloc (narray, sizeof *array); + if (!array) + return NULL; + + /* Note: The list we return is ordered. */ + for (idx=0, fd=0; fd < max_fd; fd++) + if (!(fstat (fd, &statbuf) == -1 && errno == EBADF)) + { + if (idx+1 >= narray) + { + int *tmp; + + narray += (narray < 256)? 32:256; + tmp = realloc (array, narray * sizeof *array); + if (!tmp) + { + free (array); + return NULL; + } + array = tmp; + } + array[idx++] = fd; + } + array[idx] = -1; +#endif /*HAVE_STAT*/ + return array; +} + + + +static char * +copy_quoted (char *p, const char *string) +{ + const char *s; + + if (!*string) /* Empty string. */ + p = stpcpy (p, "\"\""); + else if (strpbrk (string, " \t\n\v\f\"")) /* Need quotes. */ + { + p = stpcpy (p, "\""); + for (s = string; *s; s++) + { + *p++ = *s; + if (*s == '\"') + *p++ = *s; + } + *p++ = '\"'; + *p = 0; + } + else /* Copy verbatim. */ + p = stpcpy (p, string); + + return p; +} + + +/* Build a command line for use with W32's CreateProcess. On success + CMDLINE gets the address of a newly allocated string. */ +static int +build_w32_commandline (const char * const *argv, + int rvid0, int rvid1, int rvid2, + char **cmdline) +{ + int i, n; + const char *s; + char *buf, *p; + char fdbuf[3*30]; + + p = fdbuf; + *p = 0; + + if (rvid0) + snprintf (p, 25, "-&S0=%d ", rvid0); + else + strcpy (p, "-&S0=null "); + p += strlen (p); + + if (rvid1) + snprintf (p, 25, "-&S1=%d ", rvid1); + else + strcpy (p, "-&S1=null "); + p += strlen (p); + + if (rvid2) + snprintf (p, 25, "-&S2=%d ", rvid2); + else + strcpy (p, "-&S2=null "); + p += strlen (p); + + *cmdline = NULL; + n = strlen (fdbuf); + for (i=0; (s = argv[i]); i++) + { + n += strlen (s) + 1 + 2; /* (1 space, 2 quoting) */ + for (; *s; s++) + if (*s == '\"') + n++; /* Need to double inner quotes. */ + } + n++; + + buf = p = xtrymalloc (n); + if (! buf) + return -1; + + p = stpcpy (p, fdbuf); + for (i = 0; argv[i]; i++) + { + *p++ = ' '; + p = copy_quoted (p, argv[i]); + } + + *cmdline = buf; + return 0; +} + + +/* Create pipe where one end is inheritable: With an INHERIT_IDX of 0 + the read end is inheritable, with 1 the write end is inheritable. + Note that the inheritable ends are rendezvous ids and no file + descriptors or handles. */ +static gpg_error_t +create_inheritable_pipe (int filedes[2], int inherit_idx) +{ + HANDLE hd; + int rvid; + + filedes[0] = filedes[1] = -1; + hd = _assuan_w32ce_prepare_pipe (&rvid, !inherit_idx); + if (hd == INVALID_HANDLE_VALUE) + { + log_error ("_assuan_w32ce_prepare_pipe failed: %s\n", w32_strerror (-1)); + gpg_err_set_errno (EIO); + return gpg_error_from_syserror (); + } + + if (inherit_idx) + { + filedes[0] = handle_to_fd (hd); + filedes[1] = rvid; + } + else + { + filedes[0] = rvid; + filedes[1] = handle_to_fd (hd); + } + return 0; +} + + +/* Portable function to create a pipe. Under Windows the write end is + inheritable (i.e. an rendezvous id). */ +gpg_error_t +gnupg_create_inbound_pipe (int filedes[2], estream_t *r_fp, int nonblock) +{ + if (r_fp) + return gpg_error (GPG_ERR_NOT_IMPLEMENTED); + else + return create_inheritable_pipe (filedes, 1); +} + + +/* Portable function to create a pipe. Under Windows the read end is + inheritable (i.e. an rendezvous id). */ +gpg_error_t +gnupg_create_outbound_pipe (int filedes[2], estream_t *r_fp, int nonblock) +{ + if (r_fp) + return gpg_error (GPG_ERR_NOT_IMPLEMENTED); + else + return create_inheritable_pipe (filedes, 0); +} + + +/* Portable function to create a pipe. Under Windows both ends are + inheritable. */ +gpg_error_t +gnupg_create_pipe (int filedes[2]) +{ + return gpg_error (GPG_ERR_NOT_IMPLEMENTED); +} + + +static int +create_process (const char *pgmname, const char *cmdline, + PROCESS_INFORMATION *pi) +{ + int res; + wchar_t *wpgmname, *wcmdline; + + wpgmname = utf8_to_wchar (pgmname); + if (!wpgmname) + return 0; + wcmdline = utf8_to_wchar (cmdline); + if (!wcmdline) + { + xfree (wpgmname); + return 0; + } + res = CreateProcess (wpgmname, /* Program to start. */ + wcmdline, /* Command line arguments. */ + NULL, /* Process security attributes. */ + NULL, /* Thread security attributes. */ + FALSE, /* Inherit handles. */ + CREATE_SUSPENDED, /* Creation flags. */ + NULL, /* Environment. */ + NULL, /* Use current drive/directory. */ + NULL, /* Startup information. */ + pi); /* Returns process information. */ + xfree (wcmdline); + xfree (wpgmname); + return res; +} + + +/* Fork and exec the PGMNAME, see exechelp.h for details. */ +gpg_error_t +gnupg_spawn_process (const char *pgmname, const char *argv[], + int *except, void (*preexec)(void), unsigned int flags, + estream_t *r_infp, + estream_t *r_outfp, + estream_t *r_errfp, + pid_t *pid) +{ + gpg_error_t err; + PROCESS_INFORMATION pi = {NULL }; + char *cmdline; + es_syshd_t syshd; + struct { + HANDLE hd; + int rvid; + } inpipe = {INVALID_HANDLE_VALUE, 0}; + struct { + HANDLE hd; + int rvid; + } outpipe = {INVALID_HANDLE_VALUE, 0}; + struct { + HANDLE hd; + int rvid; + } errpipe = {INVALID_HANDLE_VALUE, 0}; + estream_t outfp = NULL; + estream_t errfp = NULL; + gpg_err_source_t errsource = default_errsource; + + (void)except; /* Not yet used. */ + (void)preexec; + (void)flags; + + /* Setup return values. */ + if (r_outfp) + *r_outfp = NULL; + if (r_errfp) + *r_errfp = NULL; + *pid = (pid_t)(-1); /* Always required. */ + + log_debug ("%s: enter\n", __func__); + if (infp) + { + es_fflush (infp); + es_rewind (infp); + + /* Create a pipe to copy our infile to the stdin of the child + process. On success inpipe.hd is owned by the feeder. */ + inpipe.hd = _assuan_w32ce_prepare_pipe (&inpipe.rvid, 1); + if (inpipe.hd == INVALID_HANDLE_VALUE) + { + log_error ("_assuan_w32ce_prepare_pipe failed: %s\n", + w32_strerror (-1)); + gpg_err_set_errno (EIO); + return gpg_error_from_syserror (); + } + log_debug ("%s: inpipe %p created; hd=%p rvid=%d\n", __func__, + infp, inpipe.hd, inpipe.rvid); + err = start_feeder (infp, inpipe.hd, 1); + if (err) + { + log_error ("error spawning feeder: %s\n", gpg_strerror (err)); + CloseHandle (inpipe.hd); + return err; + } + inpipe.hd = INVALID_HANDLE_VALUE; /* Now owned by the feeder. */ + log_debug ("%s: inpipe %p created; feeder started\n", __func__, + infp); + } + + if (r_outfp) + { + /* Create a pipe to make the stdout of the child process + available as a stream. */ + outpipe.hd = _assuan_w32ce_prepare_pipe (&outpipe.rvid, 0); + if (outpipe.hd == INVALID_HANDLE_VALUE) + { + log_error ("_assuan_w32ce_prepare_pipe failed: %s\n", + w32_strerror (-1)); + gpg_err_set_errno (EIO); + /* Fixme release other stuff/kill feeder. */ + return gpg_error_from_syserror (); + } + syshd.type = ES_SYSHD_HANDLE; + syshd.u.handle = outpipe.hd; + err = 0; + outfp = es_sysopen (&syshd, "r"); + if (!outfp) + { + err = gpg_err_make (errsource, gpg_err_code_from_syserror ()); + log_error ("error opening pipe stream: %s\n", gpg_strerror (err)); + CloseHandle (outpipe.hd); + return err; + } + log_debug ("%s: outpipe %p created; hd=%p rvid=%d\n", __func__, + outfp, outpipe.hd, outpipe.rvid); + outpipe.hd = INVALID_HANDLE_VALUE; /* Now owned by the OUTFP. */ + } + + if (r_errfp) + { + /* Create a pipe to make the stderr of the child process + available as a stream. */ + errpipe.hd = _assuan_w32ce_prepare_pipe (&errpipe.rvid, 0); + if (errpipe.hd == INVALID_HANDLE_VALUE) + { + log_error ("_assuan_w32ce_prepare_pipe failed: %s\n", + w32_strerror (-1)); + gpg_err_set_errno (EIO); + /* Fixme release other stuff/kill feeder. */ + return gpg_error_from_syserror (); + } + syshd.type = ES_SYSHD_HANDLE; + syshd.u.handle = errpipe.hd; + err = 0; + errfp = es_sysopen (&syshd, "r"); + if (!errfp) + { + err = gpg_err_make (errsource, gpg_err_code_from_syserror ()); + log_error ("error opening pipe stream: %s\n", gpg_strerror (err)); + CloseHandle (errpipe.hd); + return err; + } + log_debug ("%s: errpipe %p created; hd=%p rvid=%d\n", __func__, + errfp, errpipe.hd, errpipe.rvid); + errpipe.hd = INVALID_HANDLE_VALUE; /* Now owned by the ERRFP. */ + } + + + + /* Build the command line. */ + err = build_w32_commandline (argv, inpipe.rvid, outpipe.rvid, errpipe.rvid, + &cmdline); + if (err) + { + /* Fixme release other stuff/kill feeder. */ + CloseHandle (errpipe.hd); + return err; + } + + log_debug ("CreateProcess, path='%s' cmdline='%s'\n", pgmname, cmdline); + if (!create_process (pgmname, cmdline, &pi)) + { + log_error ("CreateProcess failed: %s\n", w32_strerror (-1)); + xfree (cmdline); + /* Fixme release other stuff/kill feeder. */ + CloseHandle (errpipe.hd); + return gpg_error (GPG_ERR_GENERAL); + } + xfree (cmdline); + cmdline = NULL; + + /* Note: The other end of the pipe is a rendezvous id and thus there + is no need for a close. */ + + log_debug ("CreateProcess ready: hProcess=%p hThread=%p" + " dwProcessID=%d dwThreadId=%d\n", + pi.hProcess, pi.hThread, + (int) pi.dwProcessId, (int) pi.dwThreadId); + + + /* Process has been created suspended; resume it now. */ + ResumeThread (pi.hThread); + CloseHandle (pi.hThread); + + if (r_outfp) + *r_outfp = outfp; + if (r_errfp) + *r_errfp = errfp; + *pid = handle_to_pid (pi.hProcess); + return 0; +} + + + +/* Simplified version of gnupg_spawn_process. This function forks and + then execs PGMNAME, while connecting INFD to stdin, OUTFD to stdout + and ERRFD to stderr (any of them may be -1 to connect them to + /dev/null). The arguments for the process are expected in the NULL + terminated array ARGV. The program name itself should not be + included there. Calling gnupg_wait_process is required. + + Returns 0 on success or an error code. */ +gpg_error_t +gnupg_spawn_process_fd (const char *pgmname, const char *argv[], + int infd, int outfd, int errfd, pid_t *pid) +{ + gpg_error_t err; + PROCESS_INFORMATION pi = {NULL}; + char *cmdline; + + /* Setup return values. */ + *pid = (pid_t)(-1); + + if (infd != -1 || outfd != -1 || errfd != -1) + return gpg_error (GPG_ERR_NOT_SUPPORTED); + + /* Build the command line. */ + err = build_w32_commandline (argv, 0, 0, 0, &cmdline); + if (err) + return err; + + log_debug ("CreateProcess, path='%s' cmdline='%s'\n", pgmname, cmdline); + if (!create_process (pgmname, cmdline, &pi)) + { + log_error ("CreateProcess(fd) failed: %s\n", w32_strerror (-1)); + xfree (cmdline); + return gpg_error (GPG_ERR_GENERAL); + } + xfree (cmdline); + cmdline = NULL; + + log_debug ("CreateProcess(fd) ready: hProcess=%p hThread=%p" + " dwProcessID=%d dwThreadId=%d\n", + pi.hProcess, pi.hThread, + (int) pi.dwProcessId, (int) pi.dwThreadId); + + /* Process has been created suspended; resume it now. */ + ResumeThread (pi.hThread); + CloseHandle (pi.hThread); + + *pid = handle_to_pid (pi.hProcess); + return 0; +} + + +/* See exechelp.h for a description. */ +gpg_error_t +gnupg_wait_process (const char *pgmname, pid_t pid, int hang, int *exitcode) +{ + gpg_err_code_t ec; + HANDLE proc = fd_to_handle (pid); + int code; + DWORD exc; + + if (exitcode) + *exitcode = -1; + + if (pid == (pid_t)(-1)) + return gpg_error (GPG_ERR_INV_VALUE); + + /* FIXME: We should do a pth_waitpid here. However this has not yet + been implemented. A special W32 pth system call would even be + better. */ + code = WaitForSingleObject (proc, hang? INFINITE : 0); + switch (code) + { + case WAIT_TIMEOUT: + ec = GPG_ERR_TIMEOUT; + break; + + case WAIT_FAILED: + log_error (_("waiting for process %d to terminate failed: %s\n"), + (int)pid, w32_strerror (-1)); + ec = GPG_ERR_GENERAL; + break; + + case WAIT_OBJECT_0: + if (!GetExitCodeProcess (proc, &exc)) + { + log_error (_("error getting exit code of process %d: %s\n"), + (int)pid, w32_strerror (-1) ); + ec = GPG_ERR_GENERAL; + } + else if (exc) + { + log_error (_("error running '%s': exit status %d\n"), + pgmname, (int)exc ); + if (exitcode) + *exitcode = (int)exc; + ec = GPG_ERR_GENERAL; + } + else + { + if (exitcode) + *exitcode = 0; + ec = 0; + } + break; + + default: + log_error ("WaitForSingleObject returned unexpected " + "code %d for pid %d\n", code, (int)pid ); + ec = GPG_ERR_GENERAL; + break; + } + + return gpg_err_make (GPG_ERR_SOURCE_DEFAULT, ec); +} + + +/* See exechelp.h for a description. */ +gpg_error_t +gnupg_wait_processes (const char **pgmnames, pid_t *pids, size_t count, + int hang, int *r_exitcodes) +{ + return gpg_error (GPG_ERR_NOT_IMPLEMENTED); +} + + +void +gnupg_release_process (pid_t pid) +{ + if (pid != (pid_t)INVALID_HANDLE_VALUE) + { + HANDLE process = (HANDLE)pid; + + CloseHandle (process); + } +} + + +/* Spawn a new process and immediately detach from it. The name of + the program to exec is PGMNAME and its arguments are in ARGV (the + programname is automatically passed as first argument). + Environment strings in ENVP are set. An error is returned if + pgmname is not executable; to make this work it is necessary to + provide an absolute file name. All standard file descriptors are + connected to /dev/null. */ +gpg_error_t +gnupg_spawn_process_detached (const char *pgmname, const char *argv[], + const char *envp[] ) +{ + gpg_error_t err; + char *cmdline; + PROCESS_INFORMATION pi = {NULL }; + + (void)envp; + + /* Build the command line. */ + err = build_w32_commandline (argv, 0, 0, 0, &cmdline); + if (err) + return err; + + /* Note: There is no detached flag under CE. */ + log_debug ("CreateProcess, path='%s' cmdline='%s'\n", pgmname, cmdline); + if (!create_process (pgmname, cmdline, &pi)) + { + log_error ("CreateProcess(detached) failed: %s\n", w32_strerror (-1)); + xfree (cmdline); + return gpg_error (GPG_ERR_GENERAL); + } + xfree (cmdline); + cmdline = NULL; + + log_debug ("CreateProcess(detached) ready: hProcess=%p hThread=%p" + " dwProcessID=%d dwThreadId=%d\n", + pi.hProcess, pi.hThread, + (int) pi.dwProcessId, (int) pi.dwThreadId); + + /* Process has been created suspended; resume it now. */ + ResumeThread (pi.hThread); + CloseHandle (pi.hThread); + + return 0; +} + + +/* Kill a process; that is send an appropriate signal to the process. + gnupg_wait_process must be called to actually remove the process + from the system. An invalid PID is ignored. */ +void +gnupg_kill_process (pid_t pid) +{ + if (pid != (pid_t) INVALID_HANDLE_VALUE) + { + HANDLE process = (HANDLE) pid; + + /* Arbitrary error code. */ + TerminateProcess (process, 1); + } +} diff --git a/common/exechelp.h b/common/exechelp.h new file mode 100644 index 0000000..1240fde --- /dev/null +++ b/common/exechelp.h @@ -0,0 +1,207 @@ +/* exechelp.h - Definitions for the fork and exec helpers + * Copyright (C) 2004, 2009, 2010 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef GNUPG_COMMON_EXECHELP_H +#define GNUPG_COMMON_EXECHELP_H + + +/* Return the maximum number of currently allowed file descriptors. + Only useful on POSIX systems. */ +int get_max_fds (void); + + +/* Close all file descriptors starting with descriptor FIRST. If + EXCEPT is not NULL, it is expected to be a list of file descriptors + which are not to close. This list shall be sorted in ascending + order with its end marked by -1. */ +void close_all_fds (int first, int *except); + + +/* Returns an array with all currently open file descriptors. The end + of the array is marked by -1. The caller needs to release this + array using the *standard free* and not with xfree. This allow the + use of this function right at startup even before libgcrypt has + been initialized. Returns NULL on error and sets ERRNO accordingly. */ +int *get_all_open_fds (void); + + +/* Portable function to create a pipe. Under Windows the write end is + inheritable. If R_FP is not NULL, an estream is created for the + write end and stored at R_FP. */ +gpg_error_t gnupg_create_inbound_pipe (int filedes[2], + estream_t *r_fp, int nonblock); + +/* Portable function to create a pipe. Under Windows the read end is + inheritable. If R_FP is not NULL, an estream is created for the + write end and stored at R_FP. */ +gpg_error_t gnupg_create_outbound_pipe (int filedes[2], + estream_t *r_fp, int nonblock); + +/* Portable function to create a pipe. Under Windows both ends are + inheritable. */ +gpg_error_t gnupg_create_pipe (int filedes[2]); + + +#define GNUPG_SPAWN_NONBLOCK 16 +#define GNUPG_SPAWN_RUN_ASFW 64 +#define GNUPG_SPAWN_DETACHED 128 +#define GNUPG_SPAWN_KEEP_STDIN 256 +#define GNUPG_SPAWN_KEEP_STDOUT 512 +#define GNUPG_SPAWN_KEEP_STDERR 1024 + +/* Fork and exec the program PGMNAME. + + If R_INFP is NULL connect stdin of the new process to /dev/null; if + it is not NULL store the address of a pointer to a new estream + there. If R_OUTFP is NULL connect stdout of the new process to + /dev/null; if it is not NULL store the address of a pointer to a + new estream there. If R_ERRFP is NULL connect stderr of the new + process to /dev/null; if it is not NULL store the address of a + pointer to a new estream there. On success the pid of the new + process is stored at PID. On error -1 is stored at PID and if + R_OUTFP or R_ERRFP are not NULL, NULL is stored there. + + The arguments for the process are expected in the NULL terminated + array ARGV. The program name itself should not be included there. + If PREEXEC is not NULL, the given function will be called right + before the exec. + + IF EXCEPT is not NULL, it is expected to be an ordered list of file + descriptors, terminated by an entry with the value (-1). These + file descriptors won't be closed before spawning a new program. + + Returns 0 on success or an error code. Calling gnupg_wait_process + and gnupg_release_process is required if the function succeeded. + + FLAGS is a bit vector: + + GNUPG_SPAWN_NONBLOCK + If set the two output streams are created in non-blocking + mode and the input stream is switched to non-blocking mode. + This is merely a convenience feature because the caller + could do the same with gpgrt_set_nonblock. Does not yet + work for Windows. + + GNUPG_SPAWN_DETACHED + If set the process will be started as a background process. + This flag is only useful under W32 (but not W32CE) systems, + so that no new console is created and pops up a console + window when starting the server. Does not work on W32CE. + + GNUPG_SPAWN_RUN_ASFW + On W32 (but not on W32CE) run AllowSetForegroundWindow for + the child. Note that due to unknown problems this actually + allows SetForegroundWindow for all children of this process. + + GNUPG_SPAWN_KEEP_STDIN + GNUPG_SPAWN_KEEP_STDOUT + GNUPG_SPAWN_KEEP_STDERR + Do not assign /dev/null to a non-required standard file + descriptor. + + */ +gpg_error_t +gnupg_spawn_process (const char *pgmname, const char *argv[], + int *execpt, void (*preexec)(void), unsigned int flags, + estream_t *r_infp, + estream_t *r_outfp, + estream_t *r_errfp, + pid_t *pid); + + +/* Simplified version of gnupg_spawn_process. This function forks and + then execs PGMNAME, while connecting INFD to stdin, OUTFD to stdout + and ERRFD to stderr (any of them may be -1 to connect them to + /dev/null). The arguments for the process are expected in the NULL + terminated array ARGV. The program name itself should not be + included there. Calling gnupg_wait_process and + gnupg_release_process is required. Returns 0 on success or an + error code. */ +gpg_error_t gnupg_spawn_process_fd (const char *pgmname, + const char *argv[], + int infd, int outfd, int errfd, + pid_t *pid); + + +/* If HANG is true, waits for the process identified by PID to exit; + if HANG is false, checks whether the process has terminated. + PGMNAME should be the same as supplied to the spawn function and is + only used for diagnostics. Return values: + + 0 + The process exited successful. 0 is stored at R_EXITCODE. + + GPG_ERR_GENERAL + The process exited without success. The exit code of process + is then stored at R_EXITCODE. An exit code of -1 indicates + that the process terminated abnormally (e.g. due to a signal). + + GPG_ERR_TIMEOUT + The process is still running (returned only if HANG is false). + + GPG_ERR_INV_VALUE + An invalid PID has been specified. + + Other error codes may be returned as well. Unless otherwise noted, + -1 will be stored at R_EXITCODE. R_EXITCODE may be passed as NULL + if the exit code is not required (in that case an error message will + be printed). Note that under Windows PID is not the process id but + the handle of the process. */ +gpg_error_t gnupg_wait_process (const char *pgmname, pid_t pid, int hang, + int *r_exitcode); + +/* Like gnupg_wait_process, but for COUNT processes. */ +gpg_error_t gnupg_wait_processes (const char **pgmnames, pid_t *pids, + size_t count, int hang, int *r_exitcodes); + + +/* Kill a process; that is send an appropriate signal to the process. + gnupg_wait_process must be called to actually remove the process + from the system. An invalid PID is ignored. */ +void gnupg_kill_process (pid_t pid); + +/* Release the process identified by PID. This function is actually + only required for Windows but it does not harm to always call it. + It is a nop if PID is invalid. */ +void gnupg_release_process (pid_t pid); + + +/* Spawn a new process and immediately detach from it. The name of + the program to exec is PGMNAME and its arguments are in ARGV (the + programname is automatically passed as first argument). + Environment strings in ENVP are set. An error is returned if + pgmname is not executable; to make this work it is necessary to + provide an absolute file name. */ +gpg_error_t gnupg_spawn_process_detached (const char *pgmname, + const char *argv[], + const char *envp[] ); + + + +#endif /*GNUPG_COMMON_EXECHELP_H*/ diff --git a/common/exectool.c b/common/exectool.c new file mode 100644 index 0000000..3458de4 --- /dev/null +++ b/common/exectool.c @@ -0,0 +1,650 @@ +/* exectool.c - Utility functions to execute a helper tool + * Copyright (C) 2015 Werner Koch + * Copyright (C) 2016 g10 Code GmbH + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include <errno.h> +#include <assert.h> +#include <gpg-error.h> + +#include <assuan.h> +#include "i18n.h" +#include "logging.h" +#include "membuf.h" +#include "mischelp.h" +#include "exechelp.h" +#include "sysutils.h" +#include "util.h" +#include "exectool.h" + +typedef struct +{ + const char *pgmname; + exec_tool_status_cb_t status_cb; + void *status_cb_value; + int cont; + size_t used; + size_t buffer_size; + char *buffer; +} read_and_log_buffer_t; + + +static inline gpg_error_t +my_error_from_syserror (void) +{ + return gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); +} + + +static void +read_and_log_stderr (read_and_log_buffer_t *state, es_poll_t *fderr) +{ + gpg_error_t err; + int c; + + if (!fderr) + { + /* Flush internal buffer. */ + if (state->used) + { + const char *pname; + int len; + + state->buffer[state->used] = 0; + state->used = 0; + + pname = strrchr (state->pgmname, '/'); + if (pname && pname != state->pgmname && pname[1]) + pname++; + else + pname = state->pgmname; + len = strlen (pname); + + if (state->status_cb + && !strncmp (state->buffer, "[GNUPG:] ", 9) + && state->buffer[9] >= 'A' && state->buffer[9] <= 'Z') + { + char *rest; + + rest = strchr (state->buffer + 9, ' '); + if (!rest) + { + /* Set REST to an empty string. */ + rest = state->buffer + strlen (state->buffer); + } + else + { + *rest++ = 0; + trim_spaces (rest); + } + state->status_cb (state->status_cb_value, + state->buffer + 9, rest); + } + else if (!state->cont + && !strncmp (state->buffer, pname, len) + && strlen (state->buffer) > strlen (pname) + && state->buffer[len] == ':' ) + { + /* PGMNAME plus colon is identical to the start of + the output: print only the output. */ + log_info ("%s\n", state->buffer); + } + else + log_info ("%s%c %s\n", + pname, state->cont? '+':':', state->buffer); + } + state->cont = 0; + return; + } + for (;;) + { + c = es_fgetc (fderr->stream); + if (c == EOF) + { + if (es_feof (fderr->stream)) + { + fderr->ignore = 1; /* Not anymore needed. */ + } + else if (es_ferror (fderr->stream)) + { + err = my_error_from_syserror (); + log_error ("error reading stderr of '%s': %s\n", + state->pgmname, gpg_strerror (err)); + fderr->ignore = 1; /* Disable. */ + } + + break; + } + else if (c == '\n') + { + read_and_log_stderr (state, NULL); + } + else + { + if (state->used >= state->buffer_size - 1) + { + if (state->status_cb) + { + /* A status callback requires that we have a full + * line. Thus we need to enlarget the buffer in + * this case. */ + char *newbuffer; + size_t newsize = state->buffer_size + 256; + + newbuffer = xtrymalloc (newsize); + if (!newbuffer) + { + log_error ("error allocating memory for status cb: %s\n", + gpg_strerror (my_error_from_syserror ())); + /* We better disable the status CB in this case. */ + state->status_cb = NULL; + read_and_log_stderr (state, NULL); + state->cont = 1; + } + else + { + memcpy (newbuffer, state->buffer, state->used); + xfree (state->buffer); + state->buffer = newbuffer; + state->buffer_size = newsize; + } + } + else + { + read_and_log_stderr (state, NULL); + state->cont = 1; + } + } + state->buffer[state->used++] = c; + } + } +} + + + +/* A buffer to copy from one stream to another. */ +struct copy_buffer +{ + char buffer[4096]; + char *writep; + size_t nread; +}; + + +/* Initialize a copy buffer. */ +static void +copy_buffer_init (struct copy_buffer *c) +{ + c->writep = c->buffer; + c->nread = 0; +} + + +/* Securely wipe a copy buffer. */ +static void +copy_buffer_shred (struct copy_buffer *c) +{ + if (c == NULL) + return; + wipememory (c->buffer, sizeof c->buffer); + c->writep = NULL; + c->nread = ~0U; +} + + +/* Copy data from SOURCE to SINK using copy buffer C. */ +static gpg_error_t +copy_buffer_do_copy (struct copy_buffer *c, estream_t source, estream_t sink) +{ + gpg_error_t err; + size_t nwritten = 0; + + if (c->nread == 0) + { + c->writep = c->buffer; + if (es_read (source, c->buffer, sizeof c->buffer, &c->nread)) + { + err = my_error_from_syserror (); + if (gpg_err_code (err) == GPG_ERR_EAGAIN) + return 0; /* We will just retry next time. */ + + return err; + } + + log_assert (c->nread <= sizeof c->buffer); + } + + if (c->nread == 0) + return 0; /* Done copying. */ + + nwritten = 0; + if (sink && es_write (sink, c->writep, c->nread, &nwritten)) + err = my_error_from_syserror (); + else + err = 0; + + log_assert (nwritten <= c->nread); + c->writep += nwritten; + c->nread -= nwritten; + log_assert (c->writep - c->buffer <= sizeof c->buffer); + + if (err) + { + if (gpg_err_code (err) == GPG_ERR_EAGAIN) + return 0; /* We will just retry next time. */ + + return err; + } + + if (sink && es_fflush (sink) && errno != EAGAIN) + err = my_error_from_syserror (); + + return err; +} + + +/* Flush the remaining data to SINK. */ +static gpg_error_t +copy_buffer_flush (struct copy_buffer *c, estream_t sink) +{ + gpg_error_t err = 0; + size_t nwritten = 0; + + if (es_write (sink, c->writep, c->nread, &nwritten)) + err = my_error_from_syserror (); + + log_assert (nwritten <= c->nread); + c->writep += nwritten; + c->nread -= nwritten; + log_assert (c->writep - c->buffer <= sizeof c->buffer); + + if (err) + return err; + + if (es_fflush (sink)) + err = my_error_from_syserror (); + + return err; +} + + + +/* Run the program PGMNAME with the command line arguments given in + * the NULL terminates array ARGV. If INPUT is not NULL it will be + * fed to stdin of the process. stderr is logged using log_info and + * the process' stdout is written to OUTPUT. If OUTPUT is NULL the + * output is discarded. If INEXTRA is given, an additional input + * stream will be passed to the child; to tell the child about this + * ARGV is scanned and the first occurrence of an argument + * "-&@INEXTRA@" is replaced by the concatenation of "-&" and the + * child's file descriptor of the pipe created for the INEXTRA stream. + * + * On error a diagnostic is printed and an error code returned. */ +gpg_error_t +gnupg_exec_tool_stream (const char *pgmname, const char *argv[], + estream_t input, estream_t inextra, + estream_t output, + exec_tool_status_cb_t status_cb, + void *status_cb_value) +{ + gpg_error_t err; + pid_t pid = (pid_t) -1; + estream_t infp = NULL; + estream_t extrafp = NULL; + estream_t outfp = NULL, errfp = NULL; + es_poll_t fds[4]; + int exceptclose[2]; + int extrapipe[2] = {-1, -1}; + char extrafdbuf[20]; + const char *argsave = NULL; + int argsaveidx; + int count; + read_and_log_buffer_t fderrstate; + struct copy_buffer *cpbuf_in = NULL, *cpbuf_out = NULL, *cpbuf_extra = NULL; + + memset (fds, 0, sizeof fds); + memset (&fderrstate, 0, sizeof fderrstate); + + cpbuf_in = xtrymalloc (sizeof *cpbuf_in); + if (cpbuf_in == NULL) + { + err = my_error_from_syserror (); + goto leave; + } + copy_buffer_init (cpbuf_in); + + cpbuf_out = xtrymalloc (sizeof *cpbuf_out); + if (cpbuf_out == NULL) + { + err = my_error_from_syserror (); + goto leave; + } + copy_buffer_init (cpbuf_out); + + cpbuf_extra = xtrymalloc (sizeof *cpbuf_extra); + if (cpbuf_extra == NULL) + { + err = my_error_from_syserror (); + goto leave; + } + copy_buffer_init (cpbuf_extra); + + fderrstate.pgmname = pgmname; + fderrstate.status_cb = status_cb; + fderrstate.status_cb_value = status_cb_value; + fderrstate.buffer_size = 256; + fderrstate.buffer = xtrymalloc (fderrstate.buffer_size); + if (!fderrstate.buffer) + { + err = my_error_from_syserror (); + goto leave; + } + + if (inextra) + { + err = gnupg_create_outbound_pipe (extrapipe, &extrafp, 1); + if (err) + { + log_error ("error running outbound pipe for extra fp: %s\n", + gpg_strerror (err)); + goto leave; + } + exceptclose[0] = extrapipe[0]; /* Do not close in child. */ + exceptclose[1] = -1; + /* Now find the argument marker and replace by the pipe's fd. + Yeah, that is an ugly non-thread safe hack but it safes us to + create a copy of the array. */ +#ifdef HAVE_W32_SYSTEM + snprintf (extrafdbuf, sizeof extrafdbuf, "-&%lu", + (unsigned long)(void*)_get_osfhandle (extrapipe[0])); +#else + snprintf (extrafdbuf, sizeof extrafdbuf, "-&%d", extrapipe[0]); +#endif + for (argsaveidx=0; argv[argsaveidx]; argsaveidx++) + if (!strcmp (argv[argsaveidx], "-&@INEXTRA@")) + { + argsave = argv[argsaveidx]; + argv[argsaveidx] = extrafdbuf; + break; + } + } + else + exceptclose[0] = -1; + + err = gnupg_spawn_process (pgmname, argv, + exceptclose, NULL, GNUPG_SPAWN_NONBLOCK, + input? &infp : NULL, + &outfp, &errfp, &pid); + if (extrapipe[0] != -1) + close (extrapipe[0]); + if (argsave) + argv[argsaveidx] = argsave; + if (err) + { + log_error ("error running '%s': %s\n", pgmname, gpg_strerror (err)); + goto leave; + } + + fds[0].stream = infp; + fds[0].want_write = 1; + if (!input) + fds[0].ignore = 1; + fds[1].stream = outfp; + fds[1].want_read = 1; + fds[2].stream = errfp; + fds[2].want_read = 1; + fds[3].stream = extrafp; + fds[3].want_write = 1; + if (!inextra) + fds[3].ignore = 1; + + /* Now read as long as we have something to poll. We continue + reading even after EOF or error on stdout so that we get the + other error messages or remaining output. */ + while (! (fds[1].ignore && fds[2].ignore)) + { + count = es_poll (fds, DIM(fds), -1); + if (count == -1) + { + err = my_error_from_syserror (); + log_error ("error polling '%s': %s\n", pgmname, gpg_strerror (err)); + goto leave; + } + if (!count) + { + log_debug ("unexpected timeout while polling '%s'\n", pgmname); + break; + } + + if (fds[0].got_write) + { + err = copy_buffer_do_copy (cpbuf_in, input, fds[0].stream); + if (err) + { + log_error ("error feeding data to '%s': %s\n", + pgmname, gpg_strerror (err)); + goto leave; + } + + if (es_feof (input)) + { + err = copy_buffer_flush (cpbuf_in, fds[0].stream); + if (gpg_err_code (err) == GPG_ERR_EAGAIN) + continue; /* Retry next time. */ + if (err) + { + log_error ("error feeding data to '%s': %s\n", + pgmname, gpg_strerror (err)); + goto leave; + } + + fds[0].ignore = 1; /* ready. */ + es_fclose (infp); infp = NULL; + } + } + + if (fds[3].got_write) + { + log_assert (inextra); + err = copy_buffer_do_copy (cpbuf_extra, inextra, fds[3].stream); + if (err) + { + log_error ("error feeding data to '%s': %s\n", + pgmname, gpg_strerror (err)); + goto leave; + } + + if (es_feof (inextra)) + { + err = copy_buffer_flush (cpbuf_extra, fds[3].stream); + if (gpg_err_code (err) == GPG_ERR_EAGAIN) + continue; /* Retry next time. */ + if (err) + { + log_error ("error feeding data to '%s': %s\n", + pgmname, gpg_strerror (err)); + goto leave; + } + + fds[3].ignore = 1; /* ready. */ + es_fclose (extrafp); extrafp = NULL; + } + } + + if (fds[1].got_read) + { + err = copy_buffer_do_copy (cpbuf_out, fds[1].stream, output); + if (err) + { + log_error ("error reading data from '%s': %s\n", + pgmname, gpg_strerror (err)); + goto leave; + } + + if (es_feof (fds[1].stream)) + { + err = copy_buffer_flush (cpbuf_out, output); + if (err) + { + log_error ("error reading data from '%s': %s\n", + pgmname, gpg_strerror (err)); + goto leave; + } + + fds[1].ignore = 1; /* ready. */ + } + } + + if (fds[2].got_read) + read_and_log_stderr (&fderrstate, fds + 2); + } + + read_and_log_stderr (&fderrstate, NULL); /* Flush. */ + es_fclose (infp); infp = NULL; + es_fclose (extrafp); extrafp = NULL; + es_fclose (outfp); outfp = NULL; + es_fclose (errfp); errfp = NULL; + + err = gnupg_wait_process (pgmname, pid, 1, NULL); + pid = (pid_t)(-1); + + leave: + if (err && pid != (pid_t) -1) + gnupg_kill_process (pid); + + es_fclose (infp); + es_fclose (extrafp); + es_fclose (outfp); + es_fclose (errfp); + if (pid != (pid_t)(-1)) + gnupg_wait_process (pgmname, pid, 1, NULL); + gnupg_release_process (pid); + + copy_buffer_shred (cpbuf_in); + xfree (cpbuf_in); + copy_buffer_shred (cpbuf_out); + xfree (cpbuf_out); + copy_buffer_shred (cpbuf_extra); + xfree (cpbuf_extra); + xfree (fderrstate.buffer); + return err; +} + + +/* A dummy free function to pass to 'es_mopen'. */ +static void +nop_free (void *ptr) +{ + (void) ptr; +} + +/* Run the program PGMNAME with the command line arguments given in + the NULL terminates array ARGV. If INPUT_STRING is not NULL it + will be fed to stdin of the process. stderr is logged using + log_info and the process' stdout is returned in a newly malloced + buffer RESULT with the length stored at RESULTLEN if not given as + NULL. A hidden Nul is appended to the output. On error NULL is + stored at RESULT, a diagnostic is printed, and an error code + returned. */ +gpg_error_t +gnupg_exec_tool (const char *pgmname, const char *argv[], + const char *input_string, + char **result, size_t *resultlen) +{ + gpg_error_t err; + estream_t input = NULL; + estream_t output; + size_t len; + size_t nread; + + *result = NULL; + if (resultlen) + *resultlen = 0; + + if (input_string) + { + len = strlen (input_string); + input = es_mopen ((char *) input_string, len, len, + 0 /* don't grow */, NULL, nop_free, "rb"); + if (! input) + return my_error_from_syserror (); + } + + output = es_fopenmem (0, "wb"); + if (! output) + { + err = my_error_from_syserror (); + goto leave; + } + + err = gnupg_exec_tool_stream (pgmname, argv, input, NULL, output, NULL, NULL); + if (err) + goto leave; + + len = es_ftello (output); + err = es_fseek (output, 0, SEEK_SET); + if (err) + goto leave; + + *result = xtrymalloc (len + 1); + if (!*result) + { + err = my_error_from_syserror (); + goto leave; + } + + if (len) + { + if (es_read (output, *result, len, &nread)) + { + err = my_error_from_syserror (); + goto leave; + } + if (nread != len) + log_fatal ("%s: short read from memstream\n", __func__); + } + (*result)[len] = 0; + + if (resultlen) + *resultlen = len; + + leave: + es_fclose (input); + es_fclose (output); + if (err) + { + xfree (*result); + *result = NULL; + } + return err; +} diff --git a/common/exectool.h b/common/exectool.h new file mode 100644 index 0000000..27bbfc9 --- /dev/null +++ b/common/exectool.h @@ -0,0 +1,69 @@ +/* sh-exectool.h - Utility functions to execute a helper tool + * Copyright (C) 2015 g10 Code GmbH + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef GNUPG_COMMON_EXECTOOL_H +#define GNUPG_COMMON_EXECTOOL_H + +#include <gpg-error.h> + +/* This callback can be used to process --status-fd outputs of GnuPG + * tools. OPAQUE can be used to communicate between the caller of the + * function and the callback. KEYWORD is the status keyword (see + * doc/DETAILS); it is never NULL. ARGS are the arguments of the + * status line and will also never be NULL; the caller may modify this + * string. */ +typedef void (*exec_tool_status_cb_t) (void *opaque, + const char *keyword, + char *args); + + +/* Run the program PGMNAME with the command line arguments given in + the NULL terminates array ARGV. If INPUT_STRING is not NULL it + will be fed to stdin of the process. stderr is logged using + log_info and the process' stdout is returned in a newly malloced + buffer RESULT with the length stored at RESULTLEN if not given as + NULL. A hidden Nul is appended to the output. On error NULL is + stored at RESULT, a diagnostic is printed, and an error code + returned. */ +gpg_error_t gnupg_exec_tool (const char *pgmname, const char *argv[], + const char *input_string, + char **result, size_t *resultlen); + +/* Run the program PGMNAME with the command line arguments given in + the NULL terminates array ARGV. If INPUT is not NULL it will be + fed to stdin of the process. stderr is logged using log_info and + the process' stdout is written to OUTPUT. On error a diagnostic is + printed, and an error code returned. INEXTRA is reserved. */ +gpg_error_t gnupg_exec_tool_stream (const char *pgmname, const char *argv[], + estream_t input, estream_t inextra, + estream_t output, + exec_tool_status_cb_t status_cb, + void *status_cb_value); + +#endif /* GNUPG_COMMON_EXECTOOL_H */ diff --git a/common/exstatus.awk b/common/exstatus.awk new file mode 100644 index 0000000..fb13819 --- /dev/null +++ b/common/exstatus.awk @@ -0,0 +1,39 @@ +# exstatus.awk - Extract status codes from status.h +# Copyright (C) 2007 Free Software Foundation, Inc. +# +# This file is part of GnuPG. +# +# GnuPG is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# GnuPG is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <http://www.gnu.org/licenses/>. + +BEGIN { + print "# Created by exstatus.awk - DO NOT EDIT." + topheader = 0; + code = 0; +} + +topheader == 0 && /^\/\*/ { topheader = 1 } +topheader == 1 { print $0 } +topheader == 1 && /\*\// { topheader = 2; print "" } + +/^[ \t]+STATUS_[A-Za-z_]+/ { + sub (/[,\/\*]+/, "", $1); + desc = substr($1,8); + printf "%d\t%s\t%s\n", code, $1, desc; + code++; +} + + +END { + print "# end of status codes." +} diff --git a/common/fwddecl.h b/common/fwddecl.h new file mode 100644 index 0000000..b945406 --- /dev/null +++ b/common/fwddecl.h @@ -0,0 +1,39 @@ +/* fwddecl.h - Forward declarations + * Copyright (C) 2015 Werner Koch + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef GNUPG_COMMON_FWDDECL_H +#define GNUPG_COMMON_FWDDECL_H + + +/*-- Forward declaration of the commonly used server control structure. */ +struct server_control_s; +typedef struct server_control_s *ctrl_t; + + +#endif /*GNUPG_COMMON_FWDDECL_H*/ diff --git a/common/gc-opt-flags.h b/common/gc-opt-flags.h new file mode 100644 index 0000000..8458aa0 --- /dev/null +++ b/common/gc-opt-flags.h @@ -0,0 +1,42 @@ +/* gc-opt-flags.h - gpgconf constants used by the backends. + * Copyright (C) 2004, 2007 Free Software Foundation, Inc. + * + * This file is free software; as a special exception the author gives + * unlimited permission to copy and/or distribute it, with or without + * modifications, as long as this notice is preserved. + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY, to the extent permitted by law; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. + */ + +#ifndef GNUPG_GC_OPT_FLAGS_H +#define GNUPG_GC_OPT_FLAGS_H + +/* Public option flags. YOU MUST NOT CHANGE THE NUMBERS OF THE + EXISTING FLAGS, AS THEY ARE PART OF THE EXTERNAL INTERFACE. See + gnupg/tools/gpgconf-comp.c for details. */ + +#define GC_OPT_FLAG_NONE 0UL + +/* The DEFAULT flag for an option indicates that the option has a + default value. */ +#define GC_OPT_FLAG_DEFAULT (1UL << 4) + +/* The DEF_DESC flag for an option indicates that the option has a + default, which is described by the value of the default field. */ +#define GC_OPT_FLAG_DEF_DESC (1UL << 5) + +/* The NO_ARG_DESC flag for an option indicates that the argument has + a default, which is described by the value of the ARGDEF field. */ +#define GC_OPT_FLAG_NO_ARG_DESC (1UL << 6) + +/* The NO_CHANGE flag for an option indicates that the user should not + be allowed to change this option using the standard gpgconf method. + Frontends using gpgconf should grey out such options, so that only + the current value is displayed. */ +#define GC_OPT_FLAG_NO_CHANGE (1UL <<7) + + +#endif /*GNUPG_GC_OPT_FLAGS_H*/ diff --git a/common/get-passphrase.c b/common/get-passphrase.c new file mode 100644 index 0000000..c24b40e --- /dev/null +++ b/common/get-passphrase.c @@ -0,0 +1,255 @@ +/* get-passphrase.c - Ask for a passphrase via the agent + * Copyright (C) 2009 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <assuan.h> + +#include "util.h" +#include "i18n.h" +#include "asshelp.h" +#include "membuf.h" +#include "sysutils.h" +#include "get-passphrase.h" + +/* The context used by this process to ask for the passphrase. */ +static assuan_context_t agent_ctx; +static struct +{ + gpg_err_source_t errsource; + int verbosity; + const char *agent_program; + const char *lc_ctype; + const char *lc_messages; + session_env_t session_env; + const char *pinentry_user_data; +} agentargs; + + +/* Set local variable to be used for a possible agent startup. Note + that the strings are just pointers and should not anymore be + modified by the caller. */ +void +gnupg_prepare_get_passphrase (gpg_err_source_t errsource, + int verbosity, + const char *agent_program, + const char *opt_lc_ctype, + const char *opt_lc_messages, + session_env_t session_env) +{ + agentargs.errsource = errsource; + agentargs.verbosity = verbosity; + agentargs.agent_program = agent_program; + agentargs.lc_ctype = opt_lc_ctype; + agentargs.lc_messages = opt_lc_messages; + agentargs.session_env = session_env; +} + + +/* Try to connect to the agent via socket or fork it off and work by + pipes. Handle the server's initial greeting. */ +static gpg_error_t +start_agent (void) +{ + gpg_error_t err; + + /* Fixme: This code is not thread safe, thus we don't build it with + pth. We will need a context for each thread or serialize the + access to the agent. */ + if (agent_ctx) + return 0; + + err = start_new_gpg_agent (&agent_ctx, + agentargs.errsource, + agentargs.agent_program, + agentargs.lc_ctype, + agentargs.lc_messages, + agentargs.session_env, + 1, agentargs.verbosity, 0, NULL, NULL); + if (!err) + { + /* Tell the agent that we support Pinentry notifications. No + error checking so that it will work with older agents. */ + assuan_transact (agent_ctx, "OPTION allow-pinentry-notify", + NULL, NULL, NULL, NULL, NULL, NULL); + } + + return err; +} + + +/* This is the default inquiry callback. It merely handles the + Pinentry notification. */ +static gpg_error_t +default_inq_cb (void *opaque, const char *line) +{ + (void)opaque; + + if (!strncmp (line, "PINENTRY_LAUNCHED", 17) && (line[17]==' '||!line[17])) + { + gnupg_allow_set_foregound_window ((pid_t)strtoul (line+17, NULL, 10)); + /* We do not return errors to avoid breaking other code. */ + } + else + log_debug ("ignoring gpg-agent inquiry '%s'\n", line); + + return 0; +} + + +/* Ask for a passphrase via gpg-agent. On success the caller needs to + free the string stored at R_PASSPHRASE. On error NULL will be + stored at R_PASSPHRASE and an appropriate gpg error code is + returned. With REPEAT set to 1, gpg-agent will ask the user to + repeat the just entered passphrase. CACHE_ID is a gpg-agent style + passphrase cache id or NULL. ERR_MSG is a error message to be + presented to the user (e.g. "bad passphrase - try again") or NULL. + PROMPT is the prompt string to label the entry box, it may be NULL + for a default one. DESC_MSG is a longer description to be + displayed above the entry box, if may be NULL for a default one. + If USE_SECMEM is true, the returned passphrase is returned in + secure memory. The length of all these strings is limited; they + need to fit in their encoded form into a standard Assuan line (i.e + less then about 950 characters). All strings shall be UTF-8. */ +gpg_error_t +gnupg_get_passphrase (const char *cache_id, + const char *err_msg, + const char *prompt, + const char *desc_msg, + int repeat, + int check_quality, + int use_secmem, + char **r_passphrase) +{ + gpg_error_t err; + char line[ASSUAN_LINELENGTH]; + const char *arg1 = NULL; + char *arg2 = NULL; + char *arg3 = NULL; + char *arg4 = NULL; + membuf_t data; + + *r_passphrase = NULL; + + err = start_agent (); + if (err) + return err; + + /* Check that the gpg-agent understands the repeat option. */ + if (assuan_transact (agent_ctx, + "GETINFO cmd_has_option GET_PASSPHRASE repeat", + NULL, NULL, NULL, NULL, NULL, NULL)) + return gpg_error (GPG_ERR_NOT_SUPPORTED); + + arg1 = cache_id && *cache_id? cache_id:NULL; + if (err_msg && *err_msg) + if (!(arg2 = percent_plus_escape (err_msg))) + goto no_mem; + if (prompt && *prompt) + if (!(arg3 = percent_plus_escape (prompt))) + goto no_mem; + if (desc_msg && *desc_msg) + if (!(arg4 = percent_plus_escape (desc_msg))) + goto no_mem; + + snprintf (line, DIM(line), + "GET_PASSPHRASE --data %s--repeat=%d -- %s %s %s %s", + check_quality? "--check ":"", + repeat, + arg1? arg1:"X", + arg2? arg2:"X", + arg3? arg3:"X", + arg4? arg4:"X"); + xfree (arg2); + xfree (arg3); + xfree (arg4); + + if (use_secmem) + init_membuf_secure (&data, 64); + else + init_membuf (&data, 64); + err = assuan_transact (agent_ctx, line, + put_membuf_cb, &data, + default_inq_cb, NULL, NULL, NULL); + + /* Older Pinentries return the old assuan error code for canceled + which gets translated by libassuan to GPG_ERR_ASS_CANCELED and + not to the code for a user cancel. Fix this here. */ + if (err && gpg_err_source (err) + && gpg_err_code (err) == GPG_ERR_ASS_CANCELED) + err = gpg_err_make (gpg_err_source (err), GPG_ERR_CANCELED); + + if (err) + { + void *p; + size_t n; + + p = get_membuf (&data, &n); + if (p) + wipememory (p, n); + xfree (p); + } + else + { + put_membuf (&data, "", 1); + *r_passphrase = get_membuf (&data, NULL); + if (!*r_passphrase) + err = gpg_error_from_syserror (); + } + return err; + no_mem: + err = gpg_error_from_syserror (); + xfree (arg2); + xfree (arg3); + xfree (arg4); + return err; +} + + +/* Flush the passphrase cache with Id CACHE_ID. */ +gpg_error_t +gnupg_clear_passphrase (const char *cache_id) +{ + gpg_error_t err; + char line[ASSUAN_LINELENGTH]; + + if (!cache_id || !*cache_id) + return 0; + + err = start_agent (); + if (err) + return err; + + snprintf (line, DIM(line), "CLEAR_PASSPHRASE %s", cache_id); + return assuan_transact (agent_ctx, line, NULL, NULL, + default_inq_cb, NULL, NULL, NULL); +} diff --git a/common/get-passphrase.h b/common/get-passphrase.h new file mode 100644 index 0000000..afdbe78 --- /dev/null +++ b/common/get-passphrase.h @@ -0,0 +1,54 @@ +/* get-passphrase.h - Definitions to ask for a passphrase via the agent. + * Copyright (C) 2009 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef GNUPG_COMMON_GET_PASSPHRASE_H +#define GNUPG_COMMON_GET_PASSPHRASE_H + +#include "session-env.h" + +void gnupg_prepare_get_passphrase (gpg_err_source_t errsource, + int verbosity, + const char *agent_program, + const char *opt_lc_ctype, + const char *opt_lc_messages, + session_env_t session_env); + +gpg_error_t gnupg_get_passphrase (const char *cache_id, + const char *err_msg, + const char *prompt, + const char *desc_msg, + int repeat, + int check_quality, + int use_secmem, + char **r_passphrase); + +gpg_error_t gnupg_clear_passphrase (const char *cache_id); + + +#endif /*GNUPG_COMMON_GET_PASSPHRASE_H*/ diff --git a/common/gettime.c b/common/gettime.c new file mode 100644 index 0000000..3fe30ce --- /dev/null +++ b/common/gettime.c @@ -0,0 +1,1064 @@ +/* gettime.c - Wrapper for time functions + * Copyright (C) 1998, 2002, 2007, 2011 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdlib.h> +#include <time.h> +#include <ctype.h> +#ifdef HAVE_LOCALE_H +#include <locale.h> +#endif +#ifdef HAVE_LANGINFO_H +#include <langinfo.h> +#endif + +#include "util.h" +#include "i18n.h" +#include "gettime.h" + +#ifdef HAVE_UNSIGNED_TIME_T +# define IS_INVALID_TIME_T(a) ((a) == (time_t)(-1)) +#else + /* Error or 32 bit time_t and value after 2038-01-19. */ +# define IS_INVALID_TIME_T(a) ((a) < 0) +#endif + + +static unsigned long timewarp; +static enum { NORMAL = 0, FROZEN, FUTURE, PAST } timemode; + +/* Correction used to map to real Julian days. */ +#define JD_DIFF 1721060L + + +/* Wrapper for the time(3). We use this here so we can fake the time + for tests */ +time_t +gnupg_get_time () +{ + time_t current = time (NULL); + if (current == (time_t)(-1)) + log_fatal ("time() failed\n"); + + if (timemode == NORMAL) + return current; + else if (timemode == FROZEN) + return timewarp; + else if (timemode == FUTURE) + return current + timewarp; + else + return current - timewarp; +} + + +/* Wrapper around gmtime_r. + + On systems without gmtime_r this implementation works within gnupg + because we use only one thread a time. FIXME: An independent + library may use gmtime in one of its own thread (or via + npth_enter/npth_leave) - in this case we run into a problem. The + solution would be to use a mutex here. */ +struct tm * +gnupg_gmtime (const time_t *timep, struct tm *result) +{ +#ifdef HAVE_GMTIME_R + return gmtime_r (timep, result); +#else + struct tm *tp; + + tp = gmtime (timep); + if (tp) + memcpy (result, tp, sizeof *result); + return tp; +#endif +} + + +/* Return the current time (possibly faked) in ISO format. */ +void +gnupg_get_isotime (gnupg_isotime_t timebuf) +{ + time_t atime = gnupg_get_time (); + struct tm *tp; + struct tm tmbuf; + + tp = gnupg_gmtime (&atime, &tmbuf); + if (!tp) + *timebuf = 0; + else + snprintf (timebuf, 16, "%04d%02d%02dT%02d%02d%02d", + 1900 + tp->tm_year, tp->tm_mon+1, tp->tm_mday, + tp->tm_hour, tp->tm_min, tp->tm_sec); +} + + +/* Set the time to NEWTIME so that gnupg_get_time returns a time + starting with this one. With FREEZE set to 1 the returned time + will never change. Just for completeness, a value of (time_t)-1 + for NEWTIME gets you back to reality. Note that this is obviously + not thread-safe but this is not required. */ +void +gnupg_set_time (time_t newtime, int freeze) +{ + time_t current = time (NULL); + + if ( newtime == (time_t)-1 || current == newtime) + { + timemode = NORMAL; + timewarp = 0; + } + else if (freeze) + { + timemode = FROZEN; + timewarp = newtime == (time_t)-1 ? current : newtime; + } + else if (newtime > current) + { + timemode = FUTURE; + timewarp = newtime - current; + } + else + { + timemode = PAST; + timewarp = current - newtime; + } +} + +/* Returns true when we are in timewarp mode */ +int +gnupg_faked_time_p (void) +{ + return timemode; +} + + +/* This function is used by gpg because OpenPGP defines the timestamp + as an unsigned 32 bit value. */ +u32 +make_timestamp (void) +{ + time_t t = gnupg_get_time (); + return (u32)t; +} + + + +/**************** + * Scan a date string and return a timestamp. + * The only supported format is "yyyy-mm-dd" + * Returns 0 for an invalid date. + */ +u32 +scan_isodatestr( const char *string ) +{ + int year, month, day; + struct tm tmbuf; + time_t stamp; + int i; + + if( strlen(string) != 10 || string[4] != '-' || string[7] != '-' ) + return 0; + for( i=0; i < 4; i++ ) + if( !digitp (string+i) ) + return 0; + if( !digitp (string+5) || !digitp(string+6) ) + return 0; + if( !digitp(string+8) || !digitp(string+9) ) + return 0; + year = atoi(string); + month = atoi(string+5); + day = atoi(string+8); + /* some basic checks */ + if( year < 1970 || month < 1 || month > 12 || day < 1 || day > 31 ) + return 0; + memset( &tmbuf, 0, sizeof tmbuf ); + tmbuf.tm_mday = day; + tmbuf.tm_mon = month-1; + tmbuf.tm_year = year - 1900; + tmbuf.tm_isdst = -1; + stamp = mktime( &tmbuf ); + if( stamp == (time_t)-1 ) + return 0; + return stamp; +} + + +int +isotime_p (const char *string) +{ + const char *s; + int i; + + if (!*string) + return 0; + for (s=string, i=0; i < 8; i++, s++) + if (!digitp (s)) + return 0; + if (*s != 'T') + return 0; + for (s++, i=9; i < 15; i++, s++) + if (!digitp (s)) + return 0; + if (*s == 'Z') + s++; + if ( !(!*s || (isascii (*s) && isspace(*s)) || *s == ':' || *s == ',')) + return 0; /* Wrong delimiter. */ + + return 1; +} + + +/* Scan a string and return true if the string represents the human + readable format of an ISO time. This format is: + yyyy-mm-dd[ hh[:mm[:ss]]] + Scanning stops at the second space or at a comma. If DATE_ONLY is + true the time part is not expected and the scanning stops at the + first space or at a comma. */ +int +isotime_human_p (const char *string, int date_only) +{ + const char *s; + int i; + + if (!*string) + return 0; + for (s=string, i=0; i < 4; i++, s++) + if (!digitp (s)) + return 0; + if (*s != '-') + return 0; + s++; + if (!digitp (s) || !digitp (s+1) || s[2] != '-') + return 0; + i = atoi_2 (s); + if (i < 1 || i > 12) + return 0; + s += 3; + if (!digitp (s) || !digitp (s+1)) + return 0; + i = atoi_2 (s); + if (i < 1 || i > 31) + return 0; + s += 2; + if (!*s || *s == ',') + return 1; /* Okay; only date given. */ + if (!spacep (s)) + return 0; + if (date_only) + return 1; /* Okay; only date was requested. */ + s++; + if (spacep (s)) + return 1; /* Okay, second space stops scanning. */ + if (!digitp (s) || !digitp (s+1)) + return 0; + i = atoi_2 (s); + if (i < 0 || i > 23) + return 0; + s += 2; + if (!*s || *s == ',') + return 1; /* Okay; only date and hour given. */ + if (*s != ':') + return 0; + s++; + if (!digitp (s) || !digitp (s+1)) + return 0; + i = atoi_2 (s); + if (i < 0 || i > 59) + return 0; + s += 2; + if (!*s || *s == ',') + return 1; /* Okay; only date, hour and minute given. */ + if (*s != ':') + return 0; + s++; + if (!digitp (s) || !digitp (s+1)) + return 0; + i = atoi_2 (s); + if (i < 0 || i > 60) + return 0; + s += 2; + if (!*s || *s == ',' || spacep (s)) + return 1; /* Okay; date, hour and minute and second given. */ + + return 0; /* Unexpected delimiter. */ +} + +/* Convert a standard isotime or a human readable variant into an + isotime structure. The allowed formats are those described by + isotime_p and isotime_human_p. The function returns 0 on failure + or the length of the scanned string on success. */ +size_t +string2isotime (gnupg_isotime_t atime, const char *string) +{ + gnupg_isotime_t dummyatime; + + if (!atime) + atime = dummyatime; + + atime[0] = 0; + if (isotime_p (string)) + { + memcpy (atime, string, 15); + atime[15] = 0; + return 15; + } + if (!isotime_human_p (string, 0)) + return 0; + atime[0] = string[0]; + atime[1] = string[1]; + atime[2] = string[2]; + atime[3] = string[3]; + atime[4] = string[5]; + atime[5] = string[6]; + atime[6] = string[8]; + atime[7] = string[9]; + atime[8] = 'T'; + memset (atime+9, '0', 6); + atime[15] = 0; + if (!spacep (string+10)) + return 10; + if (spacep (string+11)) + return 11; /* As per def, second space stops scanning. */ + atime[9] = string[11]; + atime[10] = string[12]; + if (string[13] != ':') + return 13; + atime[11] = string[14]; + atime[12] = string[15]; + if (string[16] != ':') + return 16; + atime[13] = string[17]; + atime[14] = string[18]; + return 19; +} + + +/* Scan an ISO timestamp and return an Epoch based timestamp. The + only supported format is "yyyymmddThhmmss[Z]" delimited by white + space, nul, a colon or a comma. Returns (time_t)(-1) for an + invalid string. */ +time_t +isotime2epoch (const char *string) +{ + int year, month, day, hour, minu, sec; + struct tm tmbuf; + + if (!isotime_p (string)) + return (time_t)(-1); + + year = atoi_4 (string); + month = atoi_2 (string + 4); + day = atoi_2 (string + 6); + hour = atoi_2 (string + 9); + minu = atoi_2 (string + 11); + sec = atoi_2 (string + 13); + + /* Basic checks. */ + if (year < 1970 || month < 1 || month > 12 || day < 1 || day > 31 + || hour > 23 || minu > 59 || sec > 61 ) + return (time_t)(-1); + + memset (&tmbuf, 0, sizeof tmbuf); + tmbuf.tm_sec = sec; + tmbuf.tm_min = minu; + tmbuf.tm_hour = hour; + tmbuf.tm_mday = day; + tmbuf.tm_mon = month-1; + tmbuf.tm_year = year - 1900; + tmbuf.tm_isdst = -1; + return timegm (&tmbuf); +} + + +/* Convert an Epoch time to an iso time stamp. */ +void +epoch2isotime (gnupg_isotime_t timebuf, time_t atime) +{ + if (atime == (time_t)(-1)) + *timebuf = 0; + else + { + struct tm *tp; +#ifdef HAVE_GMTIME_R + struct tm tmbuf; + + tp = gmtime_r (&atime, &tmbuf); +#else + tp = gmtime (&atime); +#endif + snprintf (timebuf, 16, "%04d%02d%02dT%02d%02d%02d", + 1900 + tp->tm_year, tp->tm_mon+1, tp->tm_mday, + tp->tm_hour, tp->tm_min, tp->tm_sec); + } +} + + +/* Parse a short ISO date string (YYYY-MM-DD) into a TM structure. + Returns 0 on success. */ +int +isodate_human_to_tm (const char *string, struct tm *t) +{ + int year, month, day; + + if (!isotime_human_p (string, 1)) + return -1; + + year = atoi_4 (string); + month = atoi_2 (string + 5); + day = atoi_2 (string + 8); + + /* Basic checks. */ + if (year < 1970 || month < 1 || month > 12 || day < 1 || day > 31) + return -1; + + memset (t, 0, sizeof *t); + t->tm_sec = 0; + t->tm_min = 0; + t->tm_hour = 0; + t->tm_mday = day; + t->tm_mon = month-1; + t->tm_year = year - 1900; + t->tm_isdst = -1; + return 0; +} + + +/* This function is a copy of gpgme/src/conversion.c:_gpgme_timegm. + If you change it, then update the other one too. */ +#ifdef HAVE_W32_SYSTEM +static time_t +_win32_timegm (struct tm *tm) +{ + /* This one is thread safe. */ + SYSTEMTIME st; + FILETIME ft; + unsigned long long cnsecs; + + st.wYear = tm->tm_year + 1900; + st.wMonth = tm->tm_mon + 1; + st.wDay = tm->tm_mday; + st.wHour = tm->tm_hour; + st.wMinute = tm->tm_min; + st.wSecond = tm->tm_sec; + st.wMilliseconds = 0; /* Not available. */ + st.wDayOfWeek = 0; /* Ignored. */ + + /* System time is UTC thus the conversion is pretty easy. */ + if (!SystemTimeToFileTime (&st, &ft)) + { + gpg_err_set_errno (EINVAL); + return (time_t)(-1); + } + + cnsecs = (((unsigned long long)ft.dwHighDateTime << 32) + | ft.dwLowDateTime); + cnsecs -= 116444736000000000ULL; /* The filetime epoch is 1601-01-01. */ + return (time_t)(cnsecs / 10000000ULL); +} +#endif + + +/* Parse the string TIMESTAMP into a time_t. The string may either be + seconds since Epoch or in the ISO 8601 format like + "20390815T143012". Returns 0 for an empty string or seconds since + Epoch. Leading spaces are skipped. If ENDP is not NULL, it will + point to the next non-parsed character in TIMESTRING. + + This function is a copy of + gpgme/src/conversion.c:_gpgme_parse_timestamp. If you change it, + then update the other one too. */ +time_t +parse_timestamp (const char *timestamp, char **endp) +{ + /* Need to skip leading spaces, because that is what strtoul does + but not our ISO 8601 checking code. */ + while (*timestamp && *timestamp== ' ') + timestamp++; + if (!*timestamp) + return 0; + + if (strlen (timestamp) >= 15 && timestamp[8] == 'T') + { + struct tm buf; + int year; + + year = atoi_4 (timestamp); + if (year < 1900) + return (time_t)(-1); + + if (endp) + *endp = (char*)(timestamp + 15); + + /* Fixme: We would better use a configure test to see whether + mktime can handle dates beyond 2038. */ + if (sizeof (time_t) <= 4 && year >= 2038) + return (time_t)2145914603; /* 2037-12-31 23:23:23 */ + + memset (&buf, 0, sizeof buf); + buf.tm_year = year - 1900; + buf.tm_mon = atoi_2 (timestamp+4) - 1; + buf.tm_mday = atoi_2 (timestamp+6); + buf.tm_hour = atoi_2 (timestamp+9); + buf.tm_min = atoi_2 (timestamp+11); + buf.tm_sec = atoi_2 (timestamp+13); + +#ifdef HAVE_W32_SYSTEM + return _win32_timegm (&buf); +#else +#ifdef HAVE_TIMEGM + return timegm (&buf); +#else + { + time_t tim; + + putenv ("TZ=UTC"); + tim = mktime (&buf); +#ifdef __GNUC__ +#warning fixme: we must somehow reset TZ here. It is not threadsafe anyway. +#endif + return tim; + } +#endif /* !HAVE_TIMEGM */ +#endif /* !HAVE_W32_SYSTEM */ + } + else + return (time_t)strtoul (timestamp, endp, 10); +} + + + +u32 +add_days_to_timestamp( u32 stamp, u16 days ) +{ + return stamp + days*86400L; +} + + +/**************** + * Return a string with a time value in the form: x Y, n D, n H + */ + +const char * +strtimevalue( u32 value ) +{ + static char buffer[30]; + unsigned int years, days, hours, minutes; + + value /= 60; + minutes = value % 60; + value /= 60; + hours = value % 24; + value /= 24; + days = value % 365; + value /= 365; + years = value; + + sprintf(buffer,"%uy%ud%uh%um", years, days, hours, minutes ); + if( years ) + return buffer; + if( days ) + return strchr( buffer, 'y' ) + 1; + return strchr( buffer, 'd' ) + 1; +} + + + +/* Return a malloced string with the time elapsed between NOW and + SINCE. May return NULL on error. */ +char * +elapsed_time_string (time_t since, time_t now) +{ + char *result; + double diff; + unsigned long value; + unsigned int days, hours, minutes, seconds; + + if (!now) + now = gnupg_get_time (); + + diff = difftime (now, since); + if (diff < 0) + return xtrystrdup ("time-warp"); + + seconds = (unsigned long)diff % 60; + value = (unsigned long)(diff / 60); + minutes = value % 60; + value /= 60; + hours = value % 24; + value /= 24; + days = value % 365; + + if (days) + result = xtryasprintf ("%ud%uh%um%us", days, hours, minutes, seconds); + else if (hours) + result = xtryasprintf ("%uh%um%us", hours, minutes, seconds); + else if (minutes) + result = xtryasprintf ("%um%us", minutes, seconds); + else + result = xtryasprintf ("%us", seconds); + + return result; +} + + +/* + * Note: this function returns GMT + */ +const char * +strtimestamp (u32 stamp) +{ + static char buffer[11+5]; + struct tm *tp; + time_t atime = stamp; + + if (IS_INVALID_TIME_T (atime)) + { + strcpy (buffer, "????" "-??" "-??"); + } + else + { + tp = gmtime( &atime ); + snprintf (buffer, sizeof buffer, "%04d-%02d-%02d", + 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday ); + } + return buffer; +} + + +/* + * Note: this function returns GMT + */ +const char * +isotimestamp (u32 stamp) +{ + static char buffer[25+5]; + struct tm *tp; + time_t atime = stamp; + + if (IS_INVALID_TIME_T (atime)) + { + strcpy (buffer, "????" "-??" "-??" " " "??" ":" "??" ":" "??"); + } + else + { + tp = gmtime ( &atime ); + snprintf (buffer, sizeof buffer, "%04d-%02d-%02d %02d:%02d:%02d", + 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday, + tp->tm_hour, tp->tm_min, tp->tm_sec); + } + return buffer; +} + + +/* Windows version of strftime returning the string as utf-8. */ +#ifdef HAVE_W32_SYSTEM + +#define strftime(a,b,c,d) w32_strftime ((a),(b),(c),(d)) + +static size_t +w32_strftime (char *s, size_t max, const char *format, const struct tm *tm) +{ + wchar_t *wformatbuf = NULL; + const wchar_t *wformat = L"%c %Z"; + wchar_t wbuf[200]; + size_t n; + char *buf; + + if (strcmp (format, "%c %Z")) + { + log_debug (" comverted\n"); + wformatbuf = utf8_to_wchar (format); + if (wformatbuf) + wformat = wformatbuf; + } + + n = wcsftime (wbuf, sizeof wbuf, wformat, tm); + xfree (wformatbuf); + if (!n) + { + /* Most likely the buffer is too short - try ISO format instead. */ + n = wcsftime (wbuf, sizeof wbuf, L"%Y-%m-%d %H:%M:%S", tm); + if (!n) + wcscpy (wbuf, L"[????" "-??" "-??]"); + } + buf = wchar_to_utf8 (wbuf); + mem2str (s, buf? buf : "[????" "-??" "-??]", max); + xfree (buf); + return strlen (s) + 1; +} +#endif /*HAVE_W32_SYSTEM*/ + + + +/**************** + * Note: this function returns local time + */ +const char * +asctimestamp (u32 stamp) +{ + static char buffer[80]; +#if defined (HAVE_STRFTIME) && defined (HAVE_NL_LANGINFO) + static char fmt[80]; +#endif + struct tm *tp; + time_t atime = stamp; + + if (IS_INVALID_TIME_T (atime)) + { + strcpy (buffer, "????" "-??" "-??"); + return buffer; + } + tp = localtime( &atime ); +#ifdef HAVE_STRFTIME +# if defined(HAVE_NL_LANGINFO) + mem2str( fmt, nl_langinfo(D_T_FMT), DIM(fmt)-3 ); + if (!strstr( fmt, "%Z" )) + strcat( fmt, " %Z"); + /* NOTE: gcc -Wformat-noliteral will complain here. I have found no + way to suppress this warning. */ + strftime (buffer, DIM(buffer)-1, fmt, tp); +# elif defined(HAVE_W32CE_SYSTEM) + /* tzset is not available but %Z nevertheless prints a default + nonsense timezone ("WILDABBR"). Thus we don't print the time + zone at all. */ + strftime (buffer, DIM(buffer)-1, "%c", tp); +# else +# if HAVE_W32_SYSTEM + { + static int done; + + if (!done) + { + /* The locale names as used by Windows are in the form + * "German_Germany.1252" or "German_Austria.1252" with + * alternate names similar to Unix, e.g. "de-DE". However + * that is the theory. On current Windows and Mingw the + * alternate names do not work. We would need a table to map + * them from the short names as provided by gpgrt to the long + * names and append some code page. For now we use "" and + * take the locale from the user's system settings. Thus the + * standard Unix envvars don't work for time and may mismatch + * with the string translations. The new UCRT available since + * 2018 has a lot of additional support but that will for sure + * break other things. We should move to ISO strings to get + * rid of such problems. */ + setlocale (LC_TIME, ""); + done = 1; + /* log_debug ("LC_ALL now '%s'\n", setlocale (LC_ALL, NULL)); */ + /* log_debug ("LC_TIME now '%s'\n", setlocale (LC_TIME, NULL)); */ + } + } +# endif + /* FIXME: we should check whether the locale appends a " %Z" These + * locales from glibc don't put the " %Z": fi_FI hr_HR ja_JP lt_LT + * lv_LV POSIX ru_RU ru_SU sv_FI sv_SE zh_CN. */ + strftime (buffer, DIM(buffer)-1, "%c %Z", tp); +# endif + buffer[DIM(buffer)-1] = 0; +#else + mem2str( buffer, asctime(tp), DIM(buffer) ); +#endif + return buffer; +} + + +/* Return the timestamp STAMP in RFC-2822 format. This is always done + * in the C locale. We return the gmtime to avoid computing the + * timezone. The caller must release the returned string. + * + * Example: "Mon, 27 Jun 2016 1:42:00 +0000". + */ +char * +rfctimestamp (u32 stamp) +{ + time_t atime = stamp; + struct tm tmbuf, *tp; + + + if (IS_INVALID_TIME_T (atime)) + { + gpg_err_set_errno (EINVAL); + return NULL; + } + + tp = gnupg_gmtime (&atime, &tmbuf); + if (!tp) + return NULL; + return xtryasprintf ("%.3s, %02d %.3s %04d %02d:%02d:%02d +0000", + &"SunMonTueWedThuFriSat"[(tp->tm_wday%7)*3], + tp->tm_mday, + &"JanFebMarAprMayJunJulAugSepOctNovDec" + [(tp->tm_mon%12)*3], + tp->tm_year + 1900, + tp->tm_hour, + tp->tm_min, + tp->tm_sec); +} + + +static int +days_per_year (int y) +{ + int s ; + + s = !(y % 4); + if ( !(y % 100)) + if ((y%400)) + s = 0; + return s ? 366 : 365; +} + +static int +days_per_month (int y, int m) +{ + int s; + + switch(m) + { + case 1: case 3: case 5: case 7: case 8: case 10: case 12: + return 31 ; + case 2: + s = !(y % 4); + if (!(y % 100)) + if ((y % 400)) + s = 0; + return s? 29 : 28 ; + case 4: case 6: case 9: case 11: + return 30; + } + BUG(); +} + + +/* Convert YEAR, MONTH and DAY into the Julian date. We assume that + it is already noon. We do not support dates before 1582-10-15. */ +static unsigned long +date2jd (int year, int month, int day) +{ + unsigned long jd; + + jd = 365L * year + 31 * (month-1) + day + JD_DIFF; + if (month < 3) + year-- ; + else + jd -= (4 * month + 23) / 10; + + jd += year / 4 - ((year / 100 + 1) *3) / 4; + + return jd ; +} + +/* Convert a Julian date back to YEAR, MONTH and DAY. Return day of + the year or 0 on error. This function uses some more or less + arbitrary limits, most important is that days before 1582 are not + supported. */ +static int +jd2date (unsigned long jd, int *year, int *month, int *day) +{ + int y, m, d; + long delta; + + if (!jd) + return 0 ; + if (jd < 1721425 || jd > 2843085) + return 0; + + y = (jd - JD_DIFF) / 366; + d = m = 1; + + while ((delta = jd - date2jd (y, m, d)) > days_per_year (y)) + y++; + + m = (delta / 31) + 1; + while( (delta = jd - date2jd (y, m, d)) > days_per_month (y,m)) + if (++m > 12) + { + m = 1; + y++; + } + + d = delta + 1 ; + if (d > days_per_month (y, m)) + { + d = 1; + m++; + } + if (m > 12) + { + m = 1; + y++; + } + + if (year) + *year = y; + if (month) + *month = m; + if (day) + *day = d ; + + return (jd - date2jd (y, 1, 1)) + 1; +} + + +/* Check that the 15 bytes in ATIME represent a valid ISO time. Note + that this function does not expect a string but a plain 15 byte + isotime buffer. */ +gpg_error_t +check_isotime (const gnupg_isotime_t atime) +{ + int i; + const char *s; + + if (!*atime) + return gpg_error (GPG_ERR_NO_VALUE); + + for (s=atime, i=0; i < 8; i++, s++) + if (!digitp (s)) + return gpg_error (GPG_ERR_INV_TIME); + if (*s != 'T') + return gpg_error (GPG_ERR_INV_TIME); + for (s++, i=9; i < 15; i++, s++) + if (!digitp (s)) + return gpg_error (GPG_ERR_INV_TIME); + return 0; +} + + +/* Dump the ISO time T to the log stream without a LF. */ +void +dump_isotime (const gnupg_isotime_t t) +{ + if (!t || !*t) + log_printf ("%s", _("[none]")); + else + log_printf ("%.4s-%.2s-%.2s %.2s:%.2s:%s", + t, t+4, t+6, t+9, t+11, t+13); +} + + +/* Copy one ISO date to another, this is inline so that we can do a + minimal sanity check. A null date (empty string) is allowed. */ +void +gnupg_copy_time (gnupg_isotime_t d, const gnupg_isotime_t s) +{ + if (*s) + { + if ((strlen (s) != 15 || s[8] != 'T')) + BUG(); + memcpy (d, s, 15); + d[15] = 0; + } + else + *d = 0; +} + + +/* Add SECONDS to ATIME. SECONDS may not be negative and is limited + to about the equivalent of 62 years which should be more then + enough for our purposes. */ +gpg_error_t +add_seconds_to_isotime (gnupg_isotime_t atime, int nseconds) +{ + gpg_error_t err; + int year, month, day, hour, minute, sec, ndays; + unsigned long jd; + + err = check_isotime (atime); + if (err) + return err; + + if (nseconds < 0 || nseconds >= (0x7fffffff - 61) ) + return gpg_error (GPG_ERR_INV_VALUE); + + year = atoi_4 (atime+0); + month = atoi_2 (atime+4); + day = atoi_2 (atime+6); + hour = atoi_2 (atime+9); + minute= atoi_2 (atime+11); + sec = atoi_2 (atime+13); + + if (year <= 1582) /* The julian date functions don't support this. */ + return gpg_error (GPG_ERR_INV_VALUE); + + sec += nseconds; + minute += sec/60; + sec %= 60; + hour += minute/60; + minute %= 60; + ndays = hour/24; + hour %= 24; + + jd = date2jd (year, month, day) + ndays; + jd2date (jd, &year, &month, &day); + + if (year > 9999 || month > 12 || day > 31 + || year < 0 || month < 1 || day < 1) + return gpg_error (GPG_ERR_INV_VALUE); + + snprintf (atime, 16, "%04d%02d%02dT%02d%02d%02d", + year, month, day, hour, minute, sec); + return 0; +} + + +gpg_error_t +add_days_to_isotime (gnupg_isotime_t atime, int ndays) +{ + gpg_error_t err; + int year, month, day, hour, minute, sec; + unsigned long jd; + + err = check_isotime (atime); + if (err) + return err; + + if (ndays < 0 || ndays >= 9999*366 ) + return gpg_error (GPG_ERR_INV_VALUE); + + year = atoi_4 (atime+0); + month = atoi_2 (atime+4); + day = atoi_2 (atime+6); + hour = atoi_2 (atime+9); + minute= atoi_2 (atime+11); + sec = atoi_2 (atime+13); + + if (year <= 1582) /* The julian date functions don't support this. */ + return gpg_error (GPG_ERR_INV_VALUE); + + jd = date2jd (year, month, day) + ndays; + jd2date (jd, &year, &month, &day); + + if (year > 9999 || month > 12 || day > 31 + || year < 0 || month < 1 || day < 1) + return gpg_error (GPG_ERR_INV_VALUE); + + snprintf (atime, 16, "%04d%02d%02dT%02d%02d%02d", + year, month, day, hour, minute, sec); + return 0; +} diff --git a/common/gettime.h b/common/gettime.h new file mode 100644 index 0000000..73f1886 --- /dev/null +++ b/common/gettime.h @@ -0,0 +1,70 @@ +/* gettime.h - Wrapper for time functions + * Copyright (C) 2010, 2012 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef GNUPG_COMMON_GETTIME_H +#define GNUPG_COMMON_GETTIME_H + +#include <time.h> /* We need time_t. */ +#include <gpg-error.h> /* We need gpg_error_t. */ + + +/* A type to hold the ISO time. Note that this is the same as + the KSBA type ksba_isotime_t. */ +typedef char gnupg_isotime_t[16]; + +time_t gnupg_get_time (void); +struct tm *gnupg_gmtime (const time_t *timep, struct tm *result); +void gnupg_get_isotime (gnupg_isotime_t timebuf); +void gnupg_set_time (time_t newtime, int freeze); +int gnupg_faked_time_p (void); +u32 make_timestamp (void); +char *elapsed_time_string (time_t since, time_t now); + +u32 scan_isodatestr (const char *string); +int isotime_p (const char *string); +int isotime_human_p (const char *string, int date_only); +size_t string2isotime (gnupg_isotime_t atime, const char *string); +time_t isotime2epoch (const char *string); +void epoch2isotime (gnupg_isotime_t timebuf, time_t atime); +int isodate_human_to_tm (const char *string, struct tm *t); +time_t parse_timestamp (const char *timestamp, char **endp); +u32 add_days_to_timestamp (u32 stamp, u16 days); +const char *strtimevalue (u32 stamp); +const char *strtimestamp (u32 stamp); /* GMT */ +const char *isotimestamp (u32 stamp); /* GMT */ +const char *asctimestamp (u32 stamp); /* localized */ +char *rfctimestamp (u32 stamp); /* RFC format, malloced. */ +gpg_error_t add_seconds_to_isotime (gnupg_isotime_t atime, int nseconds); +gpg_error_t add_days_to_isotime (gnupg_isotime_t atime, int ndays); +gpg_error_t check_isotime (const gnupg_isotime_t atime); +void dump_isotime (const gnupg_isotime_t atime); +void gnupg_copy_time (gnupg_isotime_t d, const gnupg_isotime_t s); + + +#endif /*GNUPG_COMMON_GETTIME_H*/ diff --git a/common/gnupg.ico b/common/gnupg.ico Binary files differnew file mode 100644 index 0000000..4c4bae0 --- /dev/null +++ b/common/gnupg.ico diff --git a/common/gpgrlhelp.c b/common/gpgrlhelp.c new file mode 100644 index 0000000..680d999 --- /dev/null +++ b/common/gpgrlhelp.c @@ -0,0 +1,97 @@ +/* gpgrlhelp.c - A readline wrapper. + * Copyright (C) 2006 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +/* This module may by used by applications to initializes readline + support. It is required so that we can have hooks in other parts + of libcommon without actually requiring to link against + libreadline. It works along with ttyio.c which is a proper part of + libcommon. */ + +#include <config.h> +#include <stdlib.h> +#include <stddef.h> + +#ifdef HAVE_LIBREADLINE +#define GNUPG_LIBREADLINE_H_INCLUDED +#include <stdio.h> +#include <readline/readline.h> +#include <readline/history.h> +#endif + +#include "util.h" +#include "common-defs.h" + + +#ifdef HAVE_LIBREADLINE +static void +set_completer (rl_completion_func_t *completer) +{ + rl_attempted_completion_function = completer; + rl_inhibit_completion = 0; +} + +static void +inhibit_completion (int value) +{ + rl_inhibit_completion = value; +} + +static void +cleanup_after_signal (void) +{ + rl_free_line_state (); + rl_cleanup_after_signal (); +} + +static void +init_stream (FILE *fp) +{ + rl_catch_signals = 0; + rl_instream = rl_outstream = fp; + rl_inhibit_completion = 1; +} + +#endif /*HAVE_LIBREADLINE*/ + + +/* Initialize our readline code. This should be called as early as + possible as it is actually a constructur. */ +void +gnupg_rl_initialize (void) +{ +#ifdef HAVE_LIBREADLINE + tty_private_set_rl_hooks (init_stream, + set_completer, + inhibit_completion, + cleanup_after_signal, + readline, + add_history); + rl_readline_name = GNUPG_NAME; +#endif +} diff --git a/common/helpfile.c b/common/helpfile.c new file mode 100644 index 0000000..7a7a235 --- /dev/null +++ b/common/helpfile.c @@ -0,0 +1,272 @@ +/* helpfile.c - GnuPG's helpfile feature + * Copyright (C) 2007 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdlib.h> + + +#include "util.h" +#include "i18n.h" +#include "membuf.h" + + +/* Try to find KEY in the file FNAME. */ +static char * +findkey_fname (const char *key, const char *fname) +{ + gpg_error_t err = 0; + estream_t fp; + int lnr = 0; + int c; + char *p, line[256]; + int in_item = 0; + membuf_t mb = MEMBUF_ZERO; + + fp = es_fopen (fname, "r"); + if (!fp) + { + if (errno != ENOENT) + { + err = gpg_error_from_syserror (); + log_error (_("can't open '%s': %s\n"), fname, gpg_strerror (err)); + } + return NULL; + } + + while (es_fgets (line, DIM(line)-1, fp)) + { + lnr++; + + if (!*line || line[strlen(line)-1] != '\n') + { + /* Eat until end of line. */ + while ((c = es_getc (fp)) != EOF && c != '\n') + ; + err = gpg_error (*line? GPG_ERR_LINE_TOO_LONG + : GPG_ERR_INCOMPLETE_LINE); + log_error (_("file '%s', line %d: %s\n"), + fname, lnr, gpg_strerror (err)); + } + else + line[strlen(line)-1] = 0; /* Chop the LF. */ + + again: + if (!in_item) + { + /* Allow for empty lines and spaces while not in an item. */ + for (p=line; spacep (p); p++) + ; + if (!*p || *p == '#') + continue; + if (*line != '.' || spacep(line+1)) + { + log_info (_("file '%s', line %d: %s\n"), + fname, lnr, _("ignoring garbage line")); + continue; + } + trim_trailing_spaces (line); + in_item = 1; + if (!strcmp (line+1, key)) + { + /* Found. Start collecting. */ + init_membuf (&mb, 1024); + } + continue; + } + + /* If in an item only allow for comments in the first column + and provide ". " as an escape sequence to allow for + leading dots and hash marks in the actual text. */ + if (*line == '#') + continue; + if (*line == '.') + { + if (spacep(line+1)) + p = line + 2; + else + { + trim_trailing_spaces (line); + in_item = 0; + if (is_membuf_ready (&mb)) + break; /* Yep, found and collected the item. */ + if (!line[1]) + continue; /* Just an end of text dot. */ + goto again; /* A new key line. */ + } + } + else + p = line; + + if (is_membuf_ready (&mb)) + { + put_membuf_str (&mb, p); + put_membuf (&mb, "\n", 1); + } + + } + if ( !err && es_ferror (fp) ) + { + err = gpg_error_from_syserror (); + log_error (_("error reading '%s', line %d: %s\n"), + fname, lnr, gpg_strerror (err)); + } + + es_fclose (fp); + if (is_membuf_ready (&mb)) + { + /* We have collected something. */ + if (err) + { + xfree (get_membuf (&mb, NULL)); + return NULL; + } + else + { + put_membuf (&mb, "", 1); /* Terminate string. */ + return get_membuf (&mb, NULL); + } + } + else + return NULL; +} + + +/* Try the help files depending on the locale. */ +static char * +findkey_locale (const char *key, const char *locname, + int only_current_locale, const char *dirname) +{ + const char *s; + char *fname, *ext, *p; + char *result; + + fname = xtrymalloc (strlen (dirname) + 6 + strlen (locname) + 4 + 1); + if (!fname) + return NULL; + ext = stpcpy (stpcpy (fname, dirname), "/help."); + /* Search with locale name and territory. ("help.LL_TT.txt") */ + if (strchr (locname, '_')) + { + strcpy (stpcpy (ext, locname), ".txt"); + result = findkey_fname (key, fname); + } + else + result = NULL; /* No territory. */ + + if (!result) + { + /* Search with just the locale name - if any. ("help.LL.txt") */ + if (*locname) + { + for (p=ext, s=locname; *s && *s != '_';) + *p++ = *s++; + strcpy (p, ".txt"); + result = findkey_fname (key, fname); + } + else + result = NULL; + } + + if (!result && (!only_current_locale || !*locname) ) + { + /* Last try: Search in file without any locale info. ("help.txt") */ + strcpy (ext, "txt"); + result = findkey_fname (key, fname); + } + + xfree (fname); + return result; +} + + +/* Return a malloced help text as identified by KEY. The system takes + the string from an UTF-8 encoded file to be created by an + administrator or as distributed with GnuPG. On a GNU or Unix + system the entry is searched in these files: + + /etc/gnupg/help.LL.txt + /etc/gnupg/help.txt + /usr/share/gnupg/help.LL.txt + /usr/share/gnupg/help.txt + + Here LL denotes the two digit language code of the current locale. + If ONLY_CURRENT_LOCALE is set, the function won't fallback to the + english valiant ("help.txt") unless that locale has been requested. + + The help file needs to be encoded in UTF-8, lines with a '#' in the + first column are comment lines and entirely ignored. Help keys are + identified by a key consisting of a single word with a single dot + as the first character. All key lines listed without any + intervening lines (except for comment lines) lead to the same help + text. Lines following the key lines make up the actual hep texts. + +*/ + +char * +gnupg_get_help_string (const char *key, int only_current_locale) +{ + static const char *locname; + char *result; + + if (!locname) + { + char *buffer, *p; + int count = 0; + const char *s = gnupg_messages_locale_name (); + buffer = xtrystrdup (s); + if (!buffer) + locname = ""; + else + { + for (p = buffer; *p; p++) + if (*p == '.' || *p == '@' || *p == '/' /*(safeguard)*/) + *p = 0; + else if (*p == '_') + { + if (count++) + *p = 0; /* Also cut at a underscore in the territory. */ + } + locname = buffer; + } + } + + if (!key || !*key) + return NULL; + + result = findkey_locale (key, locname, only_current_locale, + gnupg_sysconfdir ()); + if (!result) + result = findkey_locale (key, locname, only_current_locale, + gnupg_datadir ()); + + if (result) + trim_trailing_spaces (result); + + return result; +} diff --git a/common/homedir.c b/common/homedir.c new file mode 100644 index 0000000..9788c22 --- /dev/null +++ b/common/homedir.c @@ -0,0 +1,1536 @@ +/* homedir.c - Setup the home directory. + * Copyright (C) 2004, 2006, 2007, 2010 Free Software Foundation, Inc. + * Copyright (C) 2013, 2016 Werner Koch + * Copyright (C) 2021 g10 Code GmbH + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + * SPDX-License-Identifier: (LGPL-3.0-or-later OR GPL-2.0-or-later) + */ + +#include <config.h> +#include <stdlib.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> + +#ifdef HAVE_W32_SYSTEM +#include <winsock2.h> /* Due to the stupid mingw64 requirement to + include this header before windows.h which + is often implicitly included. */ +#include <shlobj.h> +#ifndef CSIDL_APPDATA +#define CSIDL_APPDATA 0x001a +#endif +#ifndef CSIDL_LOCAL_APPDATA +#define CSIDL_LOCAL_APPDATA 0x001c +#endif +#ifndef CSIDL_COMMON_APPDATA +#define CSIDL_COMMON_APPDATA 0x0023 +#endif +#ifndef CSIDL_FLAG_CREATE +#define CSIDL_FLAG_CREATE 0x8000 +#endif +#endif /*HAVE_W32_SYSTEM*/ + +#ifdef HAVE_STAT +#include <sys/stat.h> /* for stat() */ +#endif + +#include "util.h" +#include "sysutils.h" +#include "i18n.h" +#include "zb32.h" + +/* The name of the symbolic link to the file from which the process + * text was read. */ +#if __linux__ +# define MYPROC_SELF_EXE "/proc/self/exe" +#elif defined(__NetBSD__) +# define MYPROC_SELF_EXE "/proc/curproc/exe" +#elif defined(__illumos__) || defined(__sun) +# define MYPROC_SELF_EXE "/proc/self/path/a.out" +#else /* Assume other BSDs */ +# define MYPROC_SELF_EXE "/proc/curproc/file" +#endif + + +/* The GnuPG homedir. This is only accessed by the functions + * gnupg_homedir and gnupg_set_homedir. Malloced. */ +static char *the_gnupg_homedir; + +/* Flag indicating that home directory is not the default one. */ +static byte non_default_homedir; + + +#ifdef HAVE_W32_SYSTEM +/* A flag used to indicate that a control file for gpgconf has been + * detected. Under Windows the presence of this file indicates a + * portable installations and triggers several changes: + * + * - The GNUGHOME directory is fixed relative to installation + * directory. All other means to set the home directory are ignored. + * + * - All registry variables will be ignored. + * + * This flag is not used on Unix systems. + */ +static byte w32_portable_app; +#endif /*HAVE_W32_SYSTEM*/ + +#ifdef HAVE_W32_SYSTEM +/* This flag is true if this process' binary has been installed under + bin and not in the root directory as often used before GnuPG 2.1. */ +static byte w32_bin_is_bin; +#endif /*HAVE_W32_SYSTEM*/ + + +#ifdef HAVE_W32_SYSTEM +static const char *w32_rootdir (void); +#endif + + +/* This is a helper function to load and call a Windows function from + * either of one DLLs. On success an UTF-8 file name is returned. + * ERRNO is _not_ set on error. */ +#ifdef HAVE_W32_SYSTEM +static char * +w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d) +{ + static int initialized; + static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPWSTR); + wchar_t wfname[MAX_PATH]; + + if (!initialized) + { + static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL }; + void *handle; + int i; + + initialized = 1; + + for (i=0, handle = NULL; !handle && dllnames[i]; i++) + { + handle = dlopen (dllnames[i], RTLD_LAZY); + if (handle) + { + func = dlsym (handle, "SHGetFolderPathW"); + if (!func) + { + dlclose (handle); + handle = NULL; + } + } + } + } + + if (func && func (a,b,c,d,wfname) >= 0) + return wchar_to_utf8 (wfname); + else + return NULL; +} +#endif /*HAVE_W32_SYSTEM*/ + + +/* Check whether DIR is the default homedir. */ +static int +is_gnupg_default_homedir (const char *dir) +{ + int result; + char *a = make_absfilename (dir, NULL); + char *b = make_absfilename (standard_homedir (), NULL); + result = !compare_filenames (a, b); + xfree (b); + xfree (a); + return result; +} + + +/* Helper to remove trailing slashes from NEWDIR. Return a new + * allocated string if that has been done or NULL if there are no + * slashes to remove. Also inserts a missing slash after a Windows + * drive letter. */ +static char * +copy_dir_with_fixup (const char *newdir) +{ + char *result = NULL; + char *p; + + if (!*newdir) + return NULL; + +#ifdef HAVE_W32_SYSTEM + if (newdir[0] && newdir[1] == ':' + && !(newdir[2] == '/' || newdir[2] == '\\')) + { + /* Drive letter with missing leading slash. */ + p = result = xmalloc (strlen (newdir) + 1 + 1); + *p++ = newdir[0]; + *p++ = newdir[1]; + *p++ = '\\'; + strcpy (p, newdir+2); + + /* Remove trailing slashes. */ + p = result + strlen (result) - 1; + while (p > result+2 && (*p == '/' || *p == '\\')) + *p-- = 0; + } + else if (newdir[strlen (newdir)-1] == '/' + || newdir[strlen (newdir)-1] == '\\' ) + { + result = xstrdup (newdir); + p = result + strlen (result) - 1; + while (p > result + && (*p == '/' || *p == '\\') + && (p-1 > result && p[-1] != ':')) /* We keep "c:/". */ + *p-- = 0; + } + +#else /*!HAVE_W32_SYSTEM*/ + + if (newdir[strlen (newdir)-1] == '/') + { + result = xstrdup (newdir); + p = result + strlen (result) - 1; + while (p > result && *p == '/') + *p-- = 0; + } + +#endif /*!HAVE_W32_SYSTEM*/ + + return result; +} + + +/* Get the standard home directory. In general this function should + not be used as it does not consider a registry value (under W32) or + the GNUPGHOME environment variable. It is better to use + default_homedir(). */ +const char * +standard_homedir (void) +{ +#ifdef HAVE_W32_SYSTEM + static const char *dir; + + if (!dir) + { + const char *rdir; + + rdir = w32_rootdir (); + if (w32_portable_app) + { + dir = xstrconcat (rdir, DIRSEP_S "home", NULL); + } + else + { + char *path; + + path = w32_shgetfolderpath (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE, + NULL, 0); + if (path) + { + dir = xstrconcat (path, "\\gnupg", NULL); + xfree (path); + + /* Try to create the directory if it does not yet exists. */ + if (gnupg_access (dir, F_OK)) + gnupg_mkdir (dir, "-rwx"); + } + else + dir = GNUPG_DEFAULT_HOMEDIR; + } + } + return dir; +#else/*!HAVE_W32_SYSTEM*/ + return GNUPG_DEFAULT_HOMEDIR; +#endif /*!HAVE_W32_SYSTEM*/ +} + +/* Set up the default home directory. The usual --homedir option + should be parsed later. */ +const char * +default_homedir (void) +{ + const char *dir; + +#ifdef HAVE_W32_SYSTEM + /* For a portable application we only use the standard homedir. */ + w32_rootdir (); + if (w32_portable_app) + return standard_homedir (); +#endif /*HAVE_W32_SYSTEM*/ + + dir = getenv ("GNUPGHOME"); +#ifdef HAVE_W32_SYSTEM + if (!dir || !*dir) + { + static const char *saved_dir; + + if (!saved_dir) + { + if (!dir || !*dir) + { + char *tmp, *p; + + /* This is deprecated; gpgconf --list-dirs prints a + * warning if the homedir has been taken from the + * registry. */ + tmp = read_w32_registry_string (NULL, + GNUPG_REGISTRY_DIR, + "HomeDir"); + if (tmp && !*tmp) + { + xfree (tmp); + tmp = NULL; + } + if (tmp) + { + /* Strip trailing backslashes. */ + p = tmp + strlen (tmp) - 1; + while (p > tmp && *p == '\\') + *p-- = 0; + saved_dir = tmp; + } + } + + if (!saved_dir) + saved_dir = standard_homedir (); + } + dir = saved_dir; + } +#endif /*HAVE_W32_SYSTEM*/ + + if (!dir || !*dir) + dir = GNUPG_DEFAULT_HOMEDIR; + else + { + char *p; + + p = copy_dir_with_fixup (dir); + if (p) + dir = p; + + if (!is_gnupg_default_homedir (dir)) + non_default_homedir = 1; + } + + return dir; +} + + +#ifdef HAVE_W32_SYSTEM +/* Check whether gpgconf is installed and if so read the gpgconf.ctl + file. */ +static void +check_portable_app (const char *dir) +{ + char *fname; + + fname = xstrconcat (dir, DIRSEP_S "gpgconf.exe", NULL); + if (!gnupg_access (fname, F_OK)) + { + strcpy (fname + strlen (fname) - 3, "ctl"); + if (!gnupg_access (fname, F_OK)) + { + /* gpgconf.ctl file found. Record this fact. */ + w32_portable_app = 1; + { + unsigned int flags; + log_get_prefix (&flags); + log_set_prefix (NULL, (flags | GPGRT_LOG_NO_REGISTRY)); + } + /* FIXME: We should read the file to detect special flags + and print a warning if we don't understand them */ + } + } + xfree (fname); +} +#endif /*HAVE_W32_SYSTEM*/ + + +#ifdef HAVE_W32_SYSTEM +/* Determine the root directory of the gnupg installation on Windows. */ +static const char * +w32_rootdir (void) +{ + static int got_dir; + static char dir[MAX_PATH+5]; + + if (!got_dir) + { + char *p; + int rc; + wchar_t wdir [MAX_PATH+5]; + + rc = GetModuleFileNameW (NULL, wdir, MAX_PATH); + if (rc && WideCharToMultiByte (CP_UTF8, 0, wdir, -1, dir, MAX_PATH-4, + NULL, NULL) < 0) + rc = 0; + if (!rc) + { + log_debug ("GetModuleFileName failed: %s\n", w32_strerror (-1)); + *dir = 0; + } + got_dir = 1; + p = strrchr (dir, DIRSEP_C); + if (p) + { + *p = 0; + + check_portable_app (dir); + + /* If we are installed below "bin" we strip that and use + the top directory instead. */ + p = strrchr (dir, DIRSEP_C); + if (p && !strcmp (p+1, "bin")) + { + *p = 0; + w32_bin_is_bin = 1; + } + } + if (!p) + { + log_debug ("bad filename '%s' returned for this process\n", dir); + *dir = 0; + } + } + + if (*dir) + return dir; + /* Fallback to the hardwired value. */ + return GNUPG_LIBEXECDIR; +} +#endif /*HAVE_W32_SYSTEM*/ + + +#ifndef HAVE_W32_SYSTEM /* Unix */ +/* Determine the root directory of the gnupg installation on Unix. + * The standard case is that this function returns NULL so that the + * root directory as configured at build time is used. However, it + * may return a static string with a different root directory, similar + * to what we do on Windows. That second mode is triggered by the + * existence of a file gpgconf.ctl installed side-by-side to gpgconf. + * This file is parsed for keywords describing the actually to be used + * root directory. There is no solid standard on Unix to locate the + * binary used to create the process, thus we support this currently + * only on Linux and BSD where we can look this info up using the proc + * file system. If WANT_SYSCONFDIR is true the optional sysconfdir + * entry is returned. */ +static const char * +unix_rootdir (int want_sysconfdir) +{ + static int checked; + static char *dir; /* for the rootdir */ + static char *sdir; /* for the sysconfdir */ + + if (!checked) + { + char *p; + char *buffer; + size_t bufsize = 256-1; + int nread; + gpg_error_t err; + char *line; + size_t linelen; + ssize_t length; + estream_t fp; + char *rootdir; + char *sysconfdir; + const char *name; + + for (;;) + { + buffer = xmalloc (bufsize+1); + nread = readlink (MYPROC_SELF_EXE, buffer, bufsize); + if (nread < 0) + { + err = gpg_error_from_syserror (); + buffer[0] = 0; + if ((name = getenv ("GNUPG_BUILD_ROOT")) && *name == '/') + { + /* Try a fallback for systems w/o a supported /proc + * file system if we are running a regression test. */ + log_info ("error reading symlink '%s': %s\n", + MYPROC_SELF_EXE, gpg_strerror (err)); + xfree (buffer); + buffer = xstrconcat (name, "/bin/gpgconf", NULL); + log_info ("trying fallback '%s'\n", buffer); + } + break; + } + else if (nread < bufsize) + { + buffer[nread] = 0; + break; /* Got it. */ + } + else if (bufsize >= 4095) + { + buffer[0] = 0; + log_info ("error reading symlink '%s': %s\n", + MYPROC_SELF_EXE, "value too large"); + break; + } + xfree (buffer); + bufsize += 256; + } + if (!*buffer) + { + xfree (buffer); + checked = 1; + return NULL; /* Error - assume no gpgconf.ctl. */ + } + + p = strrchr (buffer, '/'); + if (!p) + { + xfree (buffer); + checked = 1; + return NULL; /* Erroneous /proc - assume no gpgconf.ctl. */ + } + *p = 0; /* BUFFER has the directory. */ + if ((p = strrchr (buffer, '/'))) + { + /* Strip one part and expect the file below a bin dir. */ + *p = 0; + p = xstrconcat (buffer, "/bin/gpgconf.ctl", NULL); + xfree (buffer); + buffer = p; + } + else /* !p */ + { + /* Installed in the root which is not a good idea. Assume + * no gpgconf.ctl. */ + xfree (buffer); + checked = 1; + return NULL; + } + + if (gnupg_access (buffer, F_OK)) + { + /* No gpgconf.ctl file. */ + xfree (buffer); + checked = 1; + return NULL; + } + /* log_info ("detected '%s'\n", buffer); */ + fp = es_fopen (buffer, "r"); + if (!fp) + { + err = gpg_error_from_syserror (); + log_info ("error opening '%s': %s\n", buffer, gpg_strerror (err)); + xfree (buffer); + checked = 1; + return NULL; + } + + line = NULL; + linelen = 0; + rootdir = NULL; + sysconfdir = NULL; + while ((length = es_read_line (fp, &line, &linelen, NULL)) > 0) + { + /* Strip NL and CR, if present. */ + while (length > 0 + && (line[length - 1] == '\n' || line[length - 1] == '\r')) + line[--length] = 0; + trim_spaces (line); + if (!strncmp (line, "rootdir=", 8)) + { + name = "rootdir"; + p = line + 8; + } + else if (!strncmp (line, "rootdir =", 9)) /* (What a kludge) */ + { + name = "rootdir"; + p = line + 9; + } + else if (!strncmp (line, "sysconfdir=", 11)) + { + name = "sysconfdir"; + p = line + 11; + } + else if (!strncmp (line, "sysconfdir =", 12)) /* (What a kludge) */ + { + name = "sysconfdir"; + p = line + 12; + } + else + continue; + trim_spaces (p); + p = substitute_envvars (p); + if (!p) + { + err = gpg_error_from_syserror (); + log_info ("error getting %s from gpgconf.ctl: %s\n", + name, gpg_strerror (err)); + } + else if (!strcmp (name, "sysconfdir")) + { + xfree (sysconfdir); + sysconfdir = p; + } + else + { + xfree (rootdir); + rootdir = p; + } + } + if (es_ferror (fp)) + { + err = gpg_error_from_syserror (); + log_info ("error reading '%s': %s\n", buffer, gpg_strerror (err)); + es_fclose (fp); + xfree (buffer); + xfree (line); + checked = 1; + return NULL; + } + es_fclose (fp); + xfree (buffer); + xfree (line); + + if (!rootdir || !*rootdir || *rootdir != '/') + { + log_info ("invalid rootdir '%s' specified in gpgconf.ctl\n", rootdir); + xfree (rootdir); + xfree (sysconfdir); + dir = NULL; + } + else if (sysconfdir && (!*sysconfdir || *sysconfdir != '/')) + { + log_info ("invalid sysconfdir '%s' specified in gpgconf.ctl\n", + sysconfdir); + xfree (rootdir); + xfree (sysconfdir); + dir = NULL; + } + else + { + while (*rootdir && rootdir[strlen (rootdir)-1] == '/') + rootdir[strlen (rootdir)-1] = 0; + dir = rootdir; + gpgrt_annotate_leaked_object (dir); + /* log_info ("want rootdir '%s'\n", dir); */ + if (sysconfdir) + { + while (*sysconfdir && sysconfdir[strlen (sysconfdir)-1] == '/') + sysconfdir[strlen (sysconfdir)-1] = 0; + sdir = sysconfdir; + gpgrt_annotate_leaked_object (sdir); + /* log_info ("want sysconfdir '%s'\n", sdir); */ + } + } + checked = 1; + } + + return want_sysconfdir? sdir : dir; +} +#endif /* Unix */ + + +#ifdef HAVE_W32_SYSTEM +static const char * +w32_commondir (void) +{ + static char *dir; + + if (!dir) + { + const char *rdir; + char *path; + + /* Make sure that w32_rootdir has been called so that we are + able to check the portable application flag. The common dir + is the identical to the rootdir. In that case there is also + no need to strdup its value. */ + rdir = w32_rootdir (); + if (w32_portable_app) + return rdir; + + path = w32_shgetfolderpath (NULL, CSIDL_COMMON_APPDATA, NULL, 0); + if (path) + { + dir = xstrconcat (path, "\\GNU", NULL); + /* No auto create of the directory. Either the installer or + * the admin has to create these directories. */ + } + else + { + /* Folder not found or defined - probably an old Windows + * version. Use the installation directory instead. */ + dir = xstrdup (rdir); + } + } + + return dir; +} +#endif /*HAVE_W32_SYSTEM*/ + + +/* Change the homedir. Some care must be taken to set this early + * enough because previous calls to gnupg_homedir may else return a + * different string. */ +void +gnupg_set_homedir (const char *newdir) +{ + char *tmp = NULL; + + if (!newdir || !*newdir) + newdir = default_homedir (); + else + { + tmp = copy_dir_with_fixup (newdir); + if (tmp) + newdir = tmp; + + if (!is_gnupg_default_homedir (newdir)) + non_default_homedir = 1; + } + xfree (the_gnupg_homedir); + the_gnupg_homedir = make_absfilename (newdir, NULL);; + xfree (tmp); +} + + +/* Create the homedir directory only if the supplied directory name is + * the same as the default one. This way we avoid to create arbitrary + * directories when a non-default home directory is used. To cope + * with HOME, we do compare only the suffix if we see that the default + * homedir does start with a tilde. If the mkdir fails the function + * terminates the process. If QUIET is set not diagnostic is printed + * on homedir creation. */ +void +gnupg_maybe_make_homedir (const char *fname, int quiet) +{ + const char *defhome = standard_homedir (); + + if ( +#ifdef HAVE_W32_SYSTEM + ( !compare_filenames (fname, defhome) ) +#else + ( *defhome == '~' + && (strlen(fname) >= strlen (defhome+1) + && !strcmp(fname+strlen(fname)-strlen(defhome+1), defhome+1 ) )) + || (*defhome != '~' && !compare_filenames( fname, defhome ) ) +#endif + ) + { + if (gnupg_mkdir (fname, "-rwx")) + log_fatal ( _("can't create directory '%s': %s\n"), + fname, strerror(errno) ); + else if (!quiet ) + log_info ( _("directory '%s' created\n"), fname ); + } +} + + +/* Return the homedir. The returned string is valid until another + * gnupg-set-homedir call. This is always an absolute directory name. + * The function replaces the former global var opt.homedir. */ +const char * +gnupg_homedir (void) +{ + /* If a homedir has not been set, set it to the default. */ + if (!the_gnupg_homedir) + the_gnupg_homedir = make_absfilename (default_homedir (), NULL); + return the_gnupg_homedir; +} + + +/* Return whether the home dir is the default one. */ +int +gnupg_default_homedir_p (void) +{ + return !non_default_homedir; +} + + +/* Return the directory name used by daemons for their current working + * directory. */ +const char * +gnupg_daemon_rootdir (void) +{ +#ifdef HAVE_W32_SYSTEM + static char *name; + + if (!name) + { + char path[MAX_PATH]; + size_t n; + + n = GetSystemDirectoryA (path, sizeof path); + if (!n || n >= sizeof path) + name = xstrdup ("/"); /* Error - use the curret top dir instead. */ + else + name = xstrdup (path); + } + + return name; + +#else /*!HAVE_W32_SYSTEM*/ + return "/"; +#endif /*!HAVE_W32_SYSTEM*/ +} + + +/* Helper for gnupg-socketdir. This is a global function, so that + * gpgconf can use it for its --create-socketdir command. If + * SKIP_CHECKS is set permission checks etc. are not done. The + * function always returns a malloced directory name and stores these + * bit flags at R_INFO: + * + * 1 := Internal error, stat failed, out of core, etc. + * 2 := No /run/user directory. + * 4 := Directory not owned by the user, not a directory + * or wrong permissions. + * 8 := Same as 4 but for the subdir. + * 16 := mkdir failed + * 32 := Non default homedir; checking subdir. + * 64 := Subdir does not exist. + * 128 := Using homedir as fallback. + */ +char * +_gnupg_socketdir_internal (int skip_checks, unsigned *r_info) +{ +#if defined(HAVE_W32_SYSTEM) + char *name; + + (void)skip_checks; + + *r_info = 0; + + /* First make sure that non_default_homedir and w32_portable_app can + * be set. */ + gnupg_homedir (); + + if (w32_portable_app) + { + name = xstrconcat (w32_rootdir (), DIRSEP_S, "gnupg", NULL); + } + else + { + char *path; + + path = w32_shgetfolderpath (NULL, + CSIDL_LOCAL_APPDATA|CSIDL_FLAG_CREATE, + NULL, 0); + if (path) + { + name = xstrconcat (path, "\\gnupg", NULL); + xfree (path); + if (gnupg_access (name, F_OK)) + gnupg_mkdir (name, "-rwx"); + } + else + { + name = xstrdup (gnupg_homedir ()); + } + } + + /* If a non default homedir is used, we check whether an + * corresponding sub directory below the socket dir is available + * and use that. We hash the non default homedir to keep the new + * subdir short enough. */ + if (non_default_homedir) + { + char sha1buf[20]; + struct stat sb; + char *suffix; + char *p; + + *r_info |= 32; /* Testing subdir. */ + + /* Canonicalize the name to avoid problems with mixed case + * names. Note that we use only 10 bytes of the hash because on + * Windows the account name is also part of the name. */ + suffix = ascii_strlwr (xstrdup (gnupg_homedir ())); + for (p=suffix; *p; p++) + if ( *p == '\\') + *p = '/'; + gcry_md_hash_buffer (GCRY_MD_SHA1, sha1buf, suffix, strlen (suffix)); + xfree (suffix); + suffix = zb32_encode (sha1buf, 8*10); + if (!suffix) + { + *r_info |= 1; /* Out of core etc. */ + goto leave_w32; + } + p = xstrconcat (name, "\\d.", suffix, NULL); + xfree (suffix); + xfree (name); + name = p; + + /* Stat that directory and check constraints. + * The command + * gpgconf --remove-socketdir + * can be used to remove that directory. */ + if (gnupg_stat (name, &sb)) + { + if (errno != ENOENT) + *r_info |= 1; /* stat failed. */ + else if (!skip_checks) + { + /* Try to create the directory and check again. */ + if (gnupg_mkdir (name, "-rwx")) + *r_info |= 16; /* mkdir failed. */ + else if (gnupg_stat (name, &sb)) + { + if (errno != ENOENT) + *r_info |= 1; /* stat failed. */ + else + *r_info |= 64; /* Subdir does not exist. */ + } + else + goto leave_w32; /* Success! */ + } + else + *r_info |= 64; /* Subdir does not exist. */ + if (!skip_checks) + { + xfree (name); + name = NULL; + goto leave_w32; + } + } + } + + leave_w32: + /* If nothing works - fall back to the homedir. */ + if (!name) + { + *r_info |= 128; /* Fallback. */ + name = xstrdup (gnupg_homedir ()); + } + +#elif !defined(HAVE_STAT) + char *name; + + (void)skip_checks; + *r_info = 0; + name = xstrdup (gnupg_homedir ()); + +#else /* Unix and stat(2) available. */ + + static const char * const bases[] = { +#ifdef USE_RUN_GNUPG_USER_SOCKET + "/run/gnupg", +#endif + "/run", +#ifdef USE_RUN_GNUPG_USER_SOCKET + "/var/run/gnupg", +#endif + "/var/run", + NULL + }; + int i; + struct stat sb; + char prefix[19 + 1 + 20 + 6 + 1]; + const char *s; + char *name = NULL; + + *r_info = 0; + + /* First make sure that non_default_homedir can be set. */ + gnupg_homedir (); + + /* It has been suggested to first check XDG_RUNTIME_DIR envvar. + * However, the specs state that the lifetime of the directory MUST + * be bound to the user being logged in. Now GnuPG may also be run + * as a background process with no (desktop) user logged in. Thus + * we better don't do that. */ + + /* Check whether we have a /run/[gnupg/]user dir. */ + for (i=0; bases[i]; i++) + { + snprintf (prefix, sizeof prefix, "%s/user/%u", + bases[i], (unsigned int)getuid ()); + if (!stat (prefix, &sb) && S_ISDIR(sb.st_mode)) + break; + } + if (!bases[i]) + { + *r_info |= 2; /* No /run/user directory. */ + goto leave; + } + + if (sb.st_uid != getuid ()) + { + *r_info |= 4; /* Not owned by the user. */ + if (!skip_checks) + goto leave; + } + + if (strlen (prefix) + 7 >= sizeof prefix) + { + *r_info |= 1; /* Ooops: Buffer too short to append "/gnupg". */ + goto leave; + } + strcat (prefix, "/gnupg"); + + /* Check whether the gnupg sub directory has proper permissions. */ + if (stat (prefix, &sb)) + { + if (errno != ENOENT) + { + *r_info |= 1; /* stat failed. */ + goto leave; + } + + /* Try to create the directory and check again. */ + if (gnupg_mkdir (prefix, "-rwx")) + { + *r_info |= 16; /* mkdir failed. */ + goto leave; + } + if (stat (prefix, &sb)) + { + *r_info |= 1; /* stat failed. */ + goto leave; + } + } + /* Check that it is a directory, owned by the user, and only the + * user has permissions to use it. */ + if (!S_ISDIR(sb.st_mode) + || sb.st_uid != getuid () + || (sb.st_mode & (S_IRWXG|S_IRWXO))) + { + *r_info |= 4; /* Bad permissions or not a directory. */ + if (!skip_checks) + goto leave; + } + + /* If a non default homedir is used, we check whether an + * corresponding sub directory below the socket dir is available + * and use that. We hash the non default homedir to keep the new + * subdir short enough. */ + if (non_default_homedir) + { + char sha1buf[20]; + char *suffix; + + *r_info |= 32; /* Testing subdir. */ + s = gnupg_homedir (); + gcry_md_hash_buffer (GCRY_MD_SHA1, sha1buf, s, strlen (s)); + suffix = zb32_encode (sha1buf, 8*15); + if (!suffix) + { + *r_info |= 1; /* Out of core etc. */ + goto leave; + } + name = strconcat (prefix, "/d.", suffix, NULL); + xfree (suffix); + if (!name) + { + *r_info |= 1; /* Out of core etc. */ + goto leave; + } + + /* Stat that directory and check constraints. + * The command + * gpgconf --remove-socketdir + * can be used to remove that directory. */ + if (stat (name, &sb)) + { + if (errno != ENOENT) + *r_info |= 1; /* stat failed. */ + else if (!skip_checks) + { + /* Try to create the directory and check again. */ + if (gnupg_mkdir (name, "-rwx")) + *r_info |= 16; /* mkdir failed. */ + else if (stat (prefix, &sb)) + { + if (errno != ENOENT) + *r_info |= 1; /* stat failed. */ + else + *r_info |= 64; /* Subdir does not exist. */ + } + else + goto leave; /* Success! */ + } + else + *r_info |= 64; /* Subdir does not exist. */ + if (!skip_checks) + { + xfree (name); + name = NULL; + goto leave; + } + } + else if (!S_ISDIR(sb.st_mode) + || sb.st_uid != getuid () + || (sb.st_mode & (S_IRWXG|S_IRWXO))) + { + *r_info |= 8; /* Bad permissions or subdir is not a directory. */ + if (!skip_checks) + { + xfree (name); + name = NULL; + goto leave; + } + } + } + else + name = xstrdup (prefix); + + leave: + /* If nothing works fall back to the homedir. */ + if (!name) + { + *r_info |= 128; /* Fallback. */ + name = xstrdup (gnupg_homedir ()); + } + +#endif /* Unix */ + + return name; +} + + +/* + * Return the name of the socket dir. That is the directory used for + * the IPC local sockets. This is an absolute directory name. + */ +const char * +gnupg_socketdir (void) +{ + static char *name; + + if (!name) + { + unsigned int dummy; + name = _gnupg_socketdir_internal (0, &dummy); + } + + return name; +} + + +/* Return the name of the sysconfdir. This is a static string. This + function is required because under Windows we can't simply compile + it in. */ +const char * +gnupg_sysconfdir (void) +{ +#ifdef HAVE_W32_SYSTEM + static char *name; + + if (!name) + { + const char *s1, *s2; + s1 = w32_commondir (); + s2 = DIRSEP_S "etc" DIRSEP_S "gnupg"; + name = xmalloc (strlen (s1) + strlen (s2) + 1); + strcpy (stpcpy (name, s1), s2); + } + return name; +#else /*!HAVE_W32_SYSTEM*/ + const char *dir = unix_rootdir (1); + if (dir) + return dir; + else + return GNUPG_SYSCONFDIR; +#endif /*!HAVE_W32_SYSTEM*/ +} + + +const char * +gnupg_bindir (void) +{ + static char *name; + const char *rdir; + +#if defined(HAVE_W32_SYSTEM) + rdir = w32_rootdir (); + if (w32_bin_is_bin) + { + if (!name) + name = xstrconcat (rdir, DIRSEP_S "bin", NULL); + return name; + } + else + return rdir; +#else /*!HAVE_W32_SYSTEM*/ + rdir = unix_rootdir (0); + if (rdir) + { + if (!name) + { + name = xstrconcat (rdir, DIRSEP_S "bin", NULL); + gpgrt_annotate_leaked_object (name); + } + return name; + } + else + return GNUPG_BINDIR; +#endif /*!HAVE_W32_SYSTEM*/ +} + + +/* Return the name of the libexec directory. The name is allocated in + a static area on the first use. This function won't fail. */ +const char * +gnupg_libexecdir (void) +{ +#ifdef HAVE_W32_SYSTEM + return gnupg_bindir (); +#else /*!HAVE_W32_SYSTEM*/ + static char *name; + const char *rdir; + + rdir = unix_rootdir (0); + if (rdir) + { + if (!name) + { + name = xstrconcat (rdir, DIRSEP_S "libexec", NULL); + gpgrt_annotate_leaked_object (name); + } + return name; + } + else + return GNUPG_LIBEXECDIR; +#endif /*!HAVE_W32_SYSTEM*/ +} + +const char * +gnupg_libdir (void) +{ + static char *name; + +#ifdef HAVE_W32_SYSTEM + if (!name) + name = xstrconcat (w32_rootdir (), DIRSEP_S "lib" DIRSEP_S "gnupg", NULL); + return name; +#else /*!HAVE_W32_SYSTEM*/ + const char *rdir; + + rdir = unix_rootdir (0); + if (rdir) + { + if (!name) + { + name = xstrconcat (rdir, DIRSEP_S "lib", DIRSEP_S, "gnupg", NULL); + gpgrt_annotate_leaked_object (name); + } + return name; + } + else + return GNUPG_LIBDIR; +#endif /*!HAVE_W32_SYSTEM*/ +} + +const char * +gnupg_datadir (void) +{ + static char *name; + +#ifdef HAVE_W32_SYSTEM + if (!name) + name = xstrconcat (w32_rootdir (), DIRSEP_S "share" DIRSEP_S "gnupg", NULL); + return name; +#else /*!HAVE_W32_SYSTEM*/ + const char *rdir; + + rdir = unix_rootdir (0); + if (rdir) + { + if (!name) + { + name = xstrconcat (rdir, DIRSEP_S "share" DIRSEP_S "gnupg", NULL); + gpgrt_annotate_leaked_object (name); + } + return name; + } + else + return GNUPG_DATADIR; +#endif /*!HAVE_W32_SYSTEM*/ +} + + +const char * +gnupg_localedir (void) +{ + static char *name; + +#ifdef HAVE_W32_SYSTEM + if (!name) + name = xstrconcat (w32_rootdir (), DIRSEP_S "share" DIRSEP_S "locale", + NULL); + return name; +#else /*!HAVE_W32_SYSTEM*/ + const char *rdir; + + rdir = unix_rootdir (0); + if (rdir) + { + if (!name) + { + name = xstrconcat (rdir, DIRSEP_S "share" DIRSEP_S "locale", NULL); + gpgrt_annotate_leaked_object (name); + } + return name; + } + else + return LOCALEDIR; +#endif /*!HAVE_W32_SYSTEM*/ +} + + +/* Return the standard socket name used by gpg-agent. */ +const char * +gpg_agent_socket_name (void) +{ + static char *name; + + if (!name) + { + name = make_filename (gnupg_socketdir (), GPG_AGENT_SOCK_NAME, NULL); + gpgrt_annotate_leaked_object (name); + } + return name; +} + + +/* Return the user socket name used by DirMngr. */ +const char * +dirmngr_socket_name (void) +{ + static char *name; + + if (!name) + name = make_filename (gnupg_socketdir (), DIRMNGR_SOCK_NAME, NULL); + return name; +} + + +/* Return the default pinentry name. If RESET is true the internal + cache is first flushed. */ +static const char * +get_default_pinentry_name (int reset) +{ + static struct { + const char *(*rfnc)(void); + const char *name; + } names[] = { + /* The first entry is what we return in case we found no + other pinentry. */ + { gnupg_bindir, DIRSEP_S "pinentry" EXEEXT_S }, +#ifdef HAVE_W32_SYSTEM + /* Try Gpg4win directory (with bin and without.) */ + { w32_rootdir, "\\..\\Gpg4win\\bin\\pinentry.exe" }, + { w32_rootdir, "\\..\\Gpg4win\\pinentry.exe" }, + /* Try a pinentry in a dir above us */ + { w32_rootdir, "\\..\\bin\\pinentry.exe" }, + /* Try old Gpgwin directory. */ + { w32_rootdir, "\\..\\GNU\\GnuPG\\pinentry.exe" }, + /* Try a Pinentry from the common GNU dir. */ + { w32_rootdir, "\\..\\GNU\\bin\\pinentry.exe" }, +#endif + /* Last chance is a pinentry-basic (which comes with the + GnuPG 2.1 Windows installer). */ + { gnupg_bindir, DIRSEP_S "pinentry-basic" EXEEXT_S } + }; + static char *name; + + if (reset) + { + xfree (name); + name = NULL; + } + + if (!name) + { + int i; + + for (i=0; i < DIM(names); i++) + { + char *name2; + + name2 = xstrconcat (names[i].rfnc (), names[i].name, NULL); + if (!gnupg_access (name2, F_OK)) + { + /* Use that pinentry. */ + xfree (name); + name = name2; + break; + } + if (!i) /* Store the first as fallback return. */ + name = name2; + else + xfree (name2); + } + } + + return name; +} + + +/* If set, 'gnupg_module_name' returns modules from that build + * directory. */ +static char *gnupg_build_directory; + +/* For sanity checks. */ +static int gnupg_module_name_called; + + +/* Set NEWDIR as the new build directory. This will make + * 'gnupg_module_name' return modules from that build directory. Must + * be called before any invocation of 'gnupg_module_name', and must + * not be called twice. It can be used by test suites to make sure + * the components from the build directory are used instead of + * potentially outdated installed ones. + * Fixme: It might be better to make use of the newer gpgconf.ctl feature + * for regression testing. + */ +void +gnupg_set_builddir (const char *newdir) +{ + log_assert (! gnupg_module_name_called); + log_assert (! gnupg_build_directory); + gnupg_build_directory = xtrystrdup (newdir); +} + + +/* If no build directory has been configured, try to set it from the + * environment. We only do this in development builds to avoid + * increasing the set of influential environment variables and hence + * the attack surface of production builds. */ +static void +gnupg_set_builddir_from_env (void) +{ +#if defined(IS_DEVELOPMENT_VERSION) || defined(ENABLE_GNUPG_BUILDDIR_ENVVAR) + if (gnupg_build_directory) + return; + + gnupg_build_directory = getenv ("GNUPG_BUILDDIR"); +#endif +} + + +/* Return the file name of a helper tool. WHICH is one of the + GNUPG_MODULE_NAME_foo constants. */ +const char * +gnupg_module_name (int which) +{ + gnupg_set_builddir_from_env (); + gnupg_module_name_called = 1; + +#define X(a,b,c) do { \ + static char *name; \ + if (!name) \ + name = gnupg_build_directory \ + ? xstrconcat (gnupg_build_directory, \ + DIRSEP_S b DIRSEP_S c EXEEXT_S, NULL) \ + : xstrconcat (gnupg_ ## a (), DIRSEP_S c EXEEXT_S, NULL); \ + return name; \ + } while (0) + + switch (which) + { + case GNUPG_MODULE_NAME_AGENT: +#ifdef GNUPG_DEFAULT_AGENT + return GNUPG_DEFAULT_AGENT; +#else + X(bindir, "agent", "gpg-agent"); +#endif + + case GNUPG_MODULE_NAME_PINENTRY: +#ifdef GNUPG_DEFAULT_PINENTRY + return GNUPG_DEFAULT_PINENTRY; /* (Set by a configure option) */ +#else + return get_default_pinentry_name (0); +#endif + + case GNUPG_MODULE_NAME_SCDAEMON: +#ifdef GNUPG_DEFAULT_SCDAEMON + return GNUPG_DEFAULT_SCDAEMON; +#else + X(libexecdir, "scd", "scdaemon"); +#endif + + case GNUPG_MODULE_NAME_DIRMNGR: +#ifdef GNUPG_DEFAULT_DIRMNGR + return GNUPG_DEFAULT_DIRMNGR; +#else + X(bindir, "dirmngr", DIRMNGR_NAME); +#endif + + case GNUPG_MODULE_NAME_PROTECT_TOOL: +#ifdef GNUPG_DEFAULT_PROTECT_TOOL + return GNUPG_DEFAULT_PROTECT_TOOL; +#else + X(libexecdir, "agent", "gpg-protect-tool"); +#endif + + case GNUPG_MODULE_NAME_DIRMNGR_LDAP: +#ifdef GNUPG_DEFAULT_DIRMNGR_LDAP + return GNUPG_DEFAULT_DIRMNGR_LDAP; +#else + X(libexecdir, "dirmngr", "dirmngr_ldap"); +#endif + + case GNUPG_MODULE_NAME_CHECK_PATTERN: + X(libexecdir, "tools", "gpg-check-pattern"); + + case GNUPG_MODULE_NAME_GPGSM: + X(bindir, "sm", "gpgsm"); + + case GNUPG_MODULE_NAME_GPG: +#if USE_GPG2_HACK + if (! gnupg_build_directory) + X(bindir, "g10", GPG_NAME "2"); + else +#endif + X(bindir, "g10", GPG_NAME); + + case GNUPG_MODULE_NAME_GPGV: +#if USE_GPG2_HACK + if (! gnupg_build_directory) + X(bindir, "g10", GPG_NAME "v2"); + else +#endif + X(bindir, "g10", GPG_NAME "v"); + + case GNUPG_MODULE_NAME_CONNECT_AGENT: + X(bindir, "tools", "gpg-connect-agent"); + + case GNUPG_MODULE_NAME_GPGCONF: + X(bindir, "tools", "gpgconf"); + + default: + BUG (); + } +#undef X +} + + +/* Flush some of the cached module names. This is for example used by + gpg-agent to allow configuring a different pinentry. */ +void +gnupg_module_name_flush_some (void) +{ + (void)get_default_pinentry_name (1); +} diff --git a/common/host2net.h b/common/host2net.h new file mode 100644 index 0000000..9eeaf24 --- /dev/null +++ b/common/host2net.h @@ -0,0 +1,112 @@ +/* host2net.h - Endian conversion macros + * Copyright (C) 1998, 2014, 2015 Werner Koch + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef GNUPG_COMMON_HOST2NET_H +#define GNUPG_COMMON_HOST2NET_H + +#include "types.h" + +#define ulongtobuf( p, a ) do { \ + ((byte*)p)[0] = a >> 24; \ + ((byte*)p)[1] = a >> 16; \ + ((byte*)p)[2] = a >> 8; \ + ((byte*)p)[3] = a ; \ + } while(0) +#define ushorttobuf( p, a ) do { \ + ((byte*)p)[0] = a >> 8; \ + ((byte*)p)[1] = a ; \ + } while(0) + + +static inline unsigned long +buf16_to_ulong (const void *buffer) +{ + const unsigned char *p = buffer; + + return (((unsigned long)p[0] << 8) | p[1]); +} + +static inline unsigned int +buf16_to_uint (const void *buffer) +{ + const unsigned char *p = buffer; + + return (((unsigned int)p[0] << 8) | p[1]); +} + +static inline unsigned short +buf16_to_ushort (const void *buffer) +{ + const unsigned char *p = buffer; + + return (((unsigned short)p[0] << 8) | p[1]); +} + +static inline u16 +buf16_to_u16 (const void *buffer) +{ + const unsigned char *p = buffer; + + return (((u16)p[0] << 8) | p[1]); +} + +static inline size_t +buf32_to_size_t (const void *buffer) +{ + const unsigned char *p = buffer; + + return (((size_t)p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]); +} + +static inline unsigned long +buf32_to_ulong (const void *buffer) +{ + const unsigned char *p = buffer; + + return (((unsigned long)p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]); +} + +static inline unsigned int +buf32_to_uint (const void *buffer) +{ + const unsigned char *p = buffer; + + return (((unsigned int)p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]); +} + +static inline u32 +buf32_to_u32 (const void *buffer) +{ + const unsigned char *p = buffer; + + return (((u32)p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]); +} + + +#endif /*GNUPG_COMMON_HOST2NET_H*/ diff --git a/common/i18n.c b/common/i18n.c new file mode 100644 index 0000000..60362ce --- /dev/null +++ b/common/i18n.c @@ -0,0 +1,237 @@ +/* i18n.c - gettext initialization + * Copyright (C) 2007, 2010 Free Software Foundation, Inc. + * Copyright (C) 2015 g10 Code GmbH + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#ifdef HAVE_LOCALE_H +#include <locale.h> +#endif +#ifdef HAVE_LANGINFO_CODESET +#include <langinfo.h> +#endif + +#include "util.h" +#include "i18n.h" + + +#undef USE_MSGCACHE +#if defined(HAVE_SETLOCALE) && defined(LC_MESSAGES) \ + && !defined(USE_SIMPLE_GETTEXT) && defined(ENABLE_NLS) +# define USE_MSGCACHE 1 +#endif + + +#ifdef USE_MSGCACHE +/* An object to store pointers to static strings and their static + translations. A linked list is not optimal but given that we only + have a few dozen messages it should be acceptable. */ +struct msg_cache_s +{ + struct msg_cache_s *next; + const char *key; + const char *value; +}; + +/* A object to store an lc_messages string and a link to the cache + object. */ +struct msg_cache_heads_s +{ + struct msg_cache_heads_s *next; + struct msg_cache_s *cache; + char lc_messages[1]; +}; + +/* Out static cache of translated messages. We need this because + there is no gettext API to return a translation depending on the + locale. Switching the locale for each access to a translatable + string seems to be too expensive. Note that this is used only for + strings in gpg-agent which are passed to Pinentry. All other + strings are using the regular gettext interface. Note that we can + never release this memory because consumers take the result as + static strings. */ +static struct msg_cache_heads_s *msgcache; + +#endif /*USE_MSGCACHE*/ + + +void +i18n_init (void) +{ +#ifdef USE_SIMPLE_GETTEXT + bindtextdomain (PACKAGE_GT, gnupg_localedir ()); + textdomain (PACKAGE_GT); +#else +# ifdef ENABLE_NLS + setlocale (LC_ALL, "" ); + bindtextdomain (PACKAGE_GT, gnupg_localedir ()); + textdomain (PACKAGE_GT); +# endif +#endif +} + + +/* The Assuan agent protocol requires us to transmit utf-8 strings + thus we need a way to temporary switch gettext from native to + utf8. */ +char * +i18n_switchto_utf8 (void) +{ +#ifdef USE_SIMPLE_GETTEXT + /* Return an arbitrary pointer as true value. */ + return gettext_use_utf8 (1) ? (char*)(-1) : NULL; +#elif defined(ENABLE_NLS) + char *orig_codeset = bind_textdomain_codeset (PACKAGE_GT, NULL); +# ifdef HAVE_LANGINFO_CODESET + if (!orig_codeset) + orig_codeset = nl_langinfo (CODESET); +# endif + if (orig_codeset) + { /* We only switch when we are able to restore the codeset later. + Note that bind_textdomain_codeset does only return on memory + errors but not if a codeset is not available. Thus we don't + bother printing a diagnostic here. */ + orig_codeset = xstrdup (orig_codeset); + if (!bind_textdomain_codeset (PACKAGE_GT, "utf-8")) + { + xfree (orig_codeset); + orig_codeset = NULL; + } + } + return orig_codeset; +#else + return NULL; +#endif +} + +/* Switch back to the saved codeset. */ +void +i18n_switchback (char *saved_codeset) +{ +#ifdef USE_SIMPLE_GETTEXT + gettext_use_utf8 (!!saved_codeset); +#elif defined(ENABLE_NLS) + if (saved_codeset) + { + bind_textdomain_codeset (PACKAGE_GT, saved_codeset); + xfree (saved_codeset); + } +#else + (void)saved_codeset; +#endif +} + + +/* Gettext variant which temporary switches to utf-8 for string. */ +const char * +i18n_utf8 (const char *string) +{ + char *saved = i18n_switchto_utf8 (); + const char *result = _(string); + i18n_switchback (saved); + return result; +} + + +/* A variant of gettext which allows the programmer to specify the + locale to use for translating the message. The function assumes + that utf-8 is used for the encoding. */ +const char * +i18n_localegettext (const char *lc_messages, const char *string) +{ +#if USE_MSGCACHE + const char *result = NULL; + char *saved = NULL; + struct msg_cache_heads_s *mh; + struct msg_cache_s *mc; + + if (!lc_messages) + goto leave; + + /* Lookup in the cache. */ + for (mh = msgcache; mh; mh = mh->next) + if (!strcmp (mh->lc_messages, lc_messages)) + break; + if (mh) + { + /* A cache entry for this local exists - find the string. + Because the system is designed for static strings it is + sufficient to compare the pointers. */ + for (mc = mh->cache; mc; mc = mc->next) + if (mc->key == string) + { + /* Cache hit. */ + result = mc->value; + goto leave; + } + } + + /* Cached miss. Change the locale, translate, reset locale. */ + saved = setlocale (LC_MESSAGES, NULL); + if (!saved) + goto leave; + saved = xtrystrdup (saved); + if (!saved) + goto leave; + if (!setlocale (LC_MESSAGES, lc_messages)) + goto leave; + + bindtextdomain (PACKAGE_GT, gnupg_localedir ()); + result = gettext (string); + setlocale (LC_MESSAGES, saved); + bindtextdomain (PACKAGE_GT, gnupg_localedir ()); + + /* Cache the result. */ + if (!mh) + { + /* First use of this locale - create an entry. */ + mh = xtrymalloc (sizeof *mh + strlen (lc_messages)); + if (!mh) + goto leave; + strcpy (mh->lc_messages, lc_messages); + mh->cache = NULL; + mh->next = msgcache; + msgcache = mh; + } + mc = xtrymalloc (sizeof *mc); + if (!mc) + goto leave; + mc->key = string; + mc->value = result; + mc->next = mh->cache; + mh->cache = mc; + + leave: + xfree (saved); + return result? result : _(string); + +#else /*!USE_MSGCACHE*/ + + (void)lc_messages; + return _(string); + +#endif /*!USE_MSGCACHE*/ +} diff --git a/common/i18n.h b/common/i18n.h new file mode 100644 index 0000000..22e8a90 --- /dev/null +++ b/common/i18n.h @@ -0,0 +1,63 @@ +/* i18n.h + * Copyright (C) 1998, 2001 Free Software Foundation, Inc. + * + * This file is free software; as a special exception the author gives + * unlimited permission to copy and/or distribute it, with or without + * modifications, as long as this notice is preserved. + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY, to the extent permitted by law; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. + */ + +#ifndef GNUPG_COMMON_I18N_H +#define GNUPG_COMMON_I18N_H + + +#ifdef USE_SIMPLE_GETTEXT +# include "../common/w32help.h" +# define _(a) gettext (a) +# define N_(a) (a) +#else +# ifdef HAVE_LOCALE_H +# include <locale.h> +# endif +# ifdef ENABLE_NLS +# include <libintl.h> +# define _(a) gettext (a) +# ifdef gettext_noop +# define N_(a) gettext_noop (a) +# else +# define N_(a) (a) +# endif +# else +# define _(a) (a) +# define N_(a) (a) +# define ngettext(a,b,c) ((c)==1? (a):(b)) +# endif +#endif /*!USE_SIMPLE_GETTEXT*/ + +#ifndef GNUPG_GCC_ATTR_FORMAT_ARG +#if __GNUC__ >= 3 /* Actually 2.8 but testing the major is easier. */ +# define GNUPG_GCC_ATTR_FORMAT_ARG(a) __attribute__ ((__format_arg__ (a))) +#else +# define GNUPG_GCC_ATTR_FORMAT_ARG(a) +#endif +#endif + +void i18n_init (void); +char *i18n_switchto_utf8 (void); +void i18n_switchback (char *saved_codeset); +const char *i18n_utf8 (const char *string); +const char *i18n_localegettext (const char *lc_messages, const char *string) + GNUPG_GCC_ATTR_FORMAT_ARG(2); + +/* If a module wants a local L_() function we define it here. */ +#ifdef LunderscoreIMPL +LunderscorePROTO +LunderscoreIMPL +#endif + + +#endif /*GNUPG_COMMON_I18N_H*/ diff --git a/common/init.c b/common/init.c new file mode 100644 index 0000000..4ae7cbc --- /dev/null +++ b/common/init.c @@ -0,0 +1,384 @@ +/* init.c - Various initializations + * Copyright (C) 2007 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#ifdef HAVE_W32_SYSTEM +# ifdef HAVE_WINSOCK2_H +# include <winsock2.h> +# endif +# include <windows.h> +#endif +#ifdef HAVE_W32CE_SYSTEM +# include <assuan.h> /* For _assuan_w32ce_finish_pipe. */ +#endif + +#include <gcrypt.h> +#include "util.h" +#include "i18n.h" +#include "w32help.h" + +/* This object is used to register memory cleanup functions. + Technically they are not needed but they can avoid frequent + questions about un-released memory. Note that we use the system + malloc and not any wrappers. */ +struct mem_cleanup_item_s; +typedef struct mem_cleanup_item_s *mem_cleanup_item_t; + +struct mem_cleanup_item_s +{ + mem_cleanup_item_t next; + void (*func) (void); +}; + +static mem_cleanup_item_t mem_cleanup_list; + + +/* The default error source of the application. This is different + from GPG_ERR_SOURCE_DEFAULT in that it does not depend on the + source file and thus is usable in code shared by applications. + Note that we need to initialize it because otherwise some linkers + (OS X at least) won't find the symbol when linking the t-*.c + files. */ +gpg_err_source_t default_errsource = 0; + + +#ifdef HAVE_W32CE_SYSTEM +static void parse_std_file_handles (int *argcp, char ***argvp); +static void +sleep_on_exit (void) +{ + /* The sshd on CE swallows some of the command output. Sleeping a + while usually helps. */ + Sleep (400); +} +#endif /*HAVE_W32CE_SYSTEM*/ + +#if HAVE_W32_SYSTEM +static void prepare_w32_commandline (int *argcp, char ***argvp); +#endif /*HAVE_W32_SYSTEM*/ + + + +static void +run_mem_cleanup (void) +{ + mem_cleanup_item_t next; + + while (mem_cleanup_list) + { + next = mem_cleanup_list->next; + mem_cleanup_list->func (); + free (mem_cleanup_list); + mem_cleanup_list = next; + } +} + + +void +register_mem_cleanup_func (void (*func)(void)) +{ + mem_cleanup_item_t item; + + for (item = mem_cleanup_list; item; item = item->next) + if (item->func == func) + return; /* Function has already been registered. */ + + item = malloc (sizeof *item); + if (item) + { + item->func = func; + item->next = mem_cleanup_list; + mem_cleanup_list = item; + } +} + + +/* If STRING is not NULL write string to es_stdout or es_stderr. MODE + must be 1 or 2. If STRING is NULL flush the respective stream. */ +static int +writestring_via_estream (int mode, const char *string) +{ + if (mode == 1 || mode == 2) + { + if (string) + return es_fputs (string, mode == 1? es_stdout : es_stderr); + else + return es_fflush (mode == 1? es_stdout : es_stderr); + } + else + return -1; +} + + +/* This function should be the first called after main. */ +void +early_system_init (void) +{ +} + + +/* This function is to be used early at program startup to make sure + that some subsystems are initialized. This is in particular + important for W32 to initialize the sockets so that our socket + emulation code used directly as well as in libassuan may be used. + It should best be called before any I/O is done so that setup + required for logging is ready. ARGCP and ARGVP are the addresses + of the parameters given to main. This function may modify them. + + This function should be called only via the macro + init_common_subsystems. + + CAUTION: This might be called while running suid(root). */ +void +_init_common_subsystems (gpg_err_source_t errsource, int *argcp, char ***argvp) +{ + /* Store the error source in a global variable. */ + default_errsource = errsource; + + atexit (run_mem_cleanup); + + /* Try to auto set the character set. */ + set_native_charset (NULL); + +#ifdef HAVE_W32_SYSTEM + /* For W32 we need to initialize the socket layer. This is because + we use recv and send in libassuan as well as at some other + places. */ + { + WSADATA wsadat; + + WSAStartup (0x202, &wsadat); + } +#endif + +#ifdef HAVE_W32CE_SYSTEM + /* Register the sleep exit function before the estream init so that + the sleep will be called after the estream registered atexit + function which flushes the left open estream streams and in + particular es_stdout. */ + atexit (sleep_on_exit); +#endif + + if (!gcry_check_version (NEED_LIBGCRYPT_VERSION)) + { + log_fatal (_("%s is too old (need %s, have %s)\n"), "libgcrypt", + NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL)); + } + + /* Initialize the Estream library. */ + gpgrt_init (); + gpgrt_set_alloc_func (gcry_realloc); + +#ifdef HAVE_W32CE_SYSTEM + /* Special hack for Windows CE: We extract some options from arg + to setup the standard handles. */ + parse_std_file_handles (argcp, argvp); +#endif + +#ifdef HAVE_W32_SYSTEM + /* We want gettext to always output UTF-8 and we put the console in + * utf-8 mode. */ + gettext_use_utf8 (1); + if (!SetConsoleCP (CP_UTF8) || !SetConsoleOutputCP (CP_UTF8)) + { + /* Don't show the error if the program does not have a console. + * This is for example the case for daemons. */ + int rc = GetLastError (); + if (rc != ERROR_INVALID_HANDLE) + { + log_info ("SetConsoleCP failed: %s\n", w32_strerror (rc)); + log_info ("Warning: Garbled console data possible\n"); + } + } +#endif + + /* Access the standard estreams as early as possible. If we don't + do this the original stdio streams may have been closed when + _es_get_std_stream is first use and in turn it would connect to + the bit bucket. */ + { + int i; + for (i=0; i < 3; i++) + (void)_gpgrt_get_std_stream (i); + } + + /* --version et al shall use estream as well. */ + gnupg_set_usage_outfnc (writestring_via_estream); + + /* Register our string mapper with gpgrt. */ + gnupg_set_fixed_string_mapper (map_static_macro_string); + + /* Logging shall use the standard socket directory as fallback. */ + log_set_socket_dir_cb (gnupg_socketdir); + +#if HAVE_W32_SYSTEM + /* For Standard Windows we use our own parser for the command line + * so that we can return an array of utf-8 encoded strings. */ + prepare_w32_commandline (argcp, argvp); +#else + (void)argcp; + (void)argvp; +#endif + +} + + + +/* WindowsCE uses a very strange way of handling the standard streams. + There is a function SetStdioPath to associate a standard stream + with a file or a device but what we really want is to use pipes as + standard streams. Despite that we implement pipes using a device, + we would have some limitations on the number of open pipes due to + the 3 character limit of device file name. Thus we don't take this + path. Another option would be to install a file system driver with + support for pipes; this would allow us to get rid of the device + name length limitation. However, with GnuPG we can get away be + redefining the standard streams and passing the handles to be used + on the command line. This has also the advantage that it makes + creating a process much easier and does not require the + SetStdioPath set and restore game. The caller needs to pass the + rendezvous ids using up to three options: + + -&S0=<rvid> -&S1=<rvid> -&S2=<rvid> + + They are all optional but they must be the first arguments on the + command line. Parsing stops as soon as an invalid option is found. + These rendezvous ids are then used to finish the pipe creation.*/ +#ifdef HAVE_W32CE_SYSTEM +static void +parse_std_file_handles (int *argcp, char ***argvp) +{ + int argc = *argcp; + char **argv = *argvp; + const char *s; + assuan_fd_t fd; + int i; + int fixup = 0; + + if (!argc) + return; + + for (argc--, argv++; argc; argc--, argv++) + { + s = *argv; + if (*s == '-' && s[1] == '&' && s[2] == 'S' + && (s[3] == '0' || s[3] == '1' || s[3] == '2') + && s[4] == '=' + && (strchr ("-01234567890", s[5]) || !strcmp (s+5, "null"))) + { + if (s[5] == 'n') + fd = ASSUAN_INVALID_FD; + else + fd = _assuan_w32ce_finish_pipe (atoi (s+5), s[3] != '0'); + _es_set_std_fd (s[3] - '0', (int)fd); + fixup++; + } + else + break; + } + + if (fixup) + { + argc = *argcp; + argc -= fixup; + *argcp = argc; + + argv = *argvp; + for (i=1; i < argc; i++) + argv[i] = argv[i + fixup]; + for (; i < argc + fixup; i++) + argv[i] = NULL; + } + + +} +#endif /*HAVE_W32CE_SYSTEM*/ + + +/* For Windows we need to parse the command line so that we can + * provide an UTF-8 encoded argv. If there is any Unicode character + * we return a new array but if there is no Unicode character we do + * nothing. */ +#ifdef HAVE_W32_SYSTEM +static void +prepare_w32_commandline (int *r_argc, char ***r_argv) +{ + const wchar_t *wcmdline, *ws; + char *cmdline; + int argc; + char **argv; + const char *s; + int i, globing, itemsalloced; + + s = strusage (95); + globing = (s && *s == '1'); + + wcmdline = GetCommandLineW (); + if (!wcmdline) + { + log_error ("GetCommandLineW failed\n"); + return; /* Ooops. */ + } + + if (!globing) + { + /* If globbing is not enabled we use our own parser only if + * there are any non-ASCII characters. */ + for (ws=wcmdline; *ws; ws++) + if (!iswascii (*ws)) + break; + if (!*ws) + return; /* No Unicode - return directly. */ + } + + cmdline = wchar_to_utf8 (wcmdline); + if (!cmdline) + { + log_error ("parsing command line failed: %s\n", strerror (errno)); + return; /* Ooops. */ + } + gpgrt_annotate_leaked_object (cmdline); + + argv = w32_parse_commandline (cmdline, globing, &argc, &itemsalloced); + if (!argv) + { + log_error ("parsing command line failed: %s\n", "internal error"); + return; /* Ooops. */ + } + gpgrt_annotate_leaked_object (argv); + if (itemsalloced) + { + for (i=0; i < argc; i++) + gpgrt_annotate_leaked_object (argv[i]); + } + *r_argv = argv; + *r_argc = argc; +} +#endif /*HAVE_W32_SYSTEM*/ diff --git a/common/init.h b/common/init.h new file mode 100644 index 0000000..3a5beca --- /dev/null +++ b/common/init.h @@ -0,0 +1,47 @@ +/* init.h - Definitions for init functions. + * Copyright (C) 2007, 2012 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef GNUPG_COMMON_INIT_H +#define GNUPG_COMMON_INIT_H + +#ifndef GPG_ERR_SOURCE_DEFAULT +# error GPG_ERR_SOURCE_DEFAULT is not defined +#elseif GPG_ERR_SOURCE_DEFAULT == GPG_ERR_SOURCE_UNKNOWN +# error GPG_ERR_SOURCE_DEFAULT has default value +#endif + +void register_mem_cleanup_func (void (*func)(void)); + +void early_system_init (void); +void _init_common_subsystems (gpg_err_source_t errsource, + int *argcp, char ***argvp); +#define init_common_subsystems(a,b) \ + _init_common_subsystems (GPG_ERR_SOURCE_DEFAULT, (a), (b)) + +#endif /*GNUPG_COMMON_INIT_H*/ diff --git a/common/iobuf.c b/common/iobuf.c new file mode 100644 index 0000000..6370efb --- /dev/null +++ b/common/iobuf.c @@ -0,0 +1,2698 @@ +/* iobuf.c - File Handling for OpenPGP. + * Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004, 2006, 2007, 2008, + * 2009, 2010, 2011 Free Software Foundation, Inc. + * Copyright (C) 2015 g10 Code GmbH + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <assert.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#ifdef HAVE_W32_SYSTEM +# ifdef HAVE_WINSOCK2_H +# include <winsock2.h> +# endif +# include <windows.h> +#endif +#ifdef __riscos__ +# include <kernel.h> +# include <swis.h> +#endif /* __riscos__ */ + +#include <assuan.h> + +#include "util.h" +#include "sysutils.h" +#include "iobuf.h" + +/*-- Begin configurable part. --*/ + +/* The size of the internal buffers. + NOTE: If you change this value you MUST also adjust the regression + test "armored_key_8192" in armor.test! */ +#define IOBUF_BUFFER_SIZE 8192 + +/* To avoid a potential DoS with compression packets we better limit + the number of filters in a chain. */ +#define MAX_NESTING_FILTER 64 + +/*-- End configurable part. --*/ + + +#ifdef HAVE_W32_SYSTEM +# ifdef HAVE_W32CE_SYSTEM +# define FD_FOR_STDIN (es_fileno (es_stdin)) +# define FD_FOR_STDOUT (es_fileno (es_stdout)) +# else +# define FD_FOR_STDIN (GetStdHandle (STD_INPUT_HANDLE)) +# define FD_FOR_STDOUT (GetStdHandle (STD_OUTPUT_HANDLE)) +# endif +#else /*!HAVE_W32_SYSTEM*/ +# define FD_FOR_STDIN (0) +# define FD_FOR_STDOUT (1) +#endif /*!HAVE_W32_SYSTEM*/ + + +/* The context used by the file filter. */ +typedef struct +{ + gnupg_fd_t fp; /* Open file pointer or handle. */ + int keep_open; + int no_cache; + int eof_seen; + int print_only_name; /* Flags indicating that fname is not a real file. */ + char fname[1]; /* Name of the file. */ +} file_filter_ctx_t; + +/* The context used by the estream filter. */ +typedef struct +{ + estream_t fp; /* Open estream handle. */ + int keep_open; + int no_cache; + int eof_seen; + int print_only_name; /* Flags indicating that fname is not a real file. */ + char fname[1]; /* Name of the file. */ +} file_es_filter_ctx_t; + + +/* Object to control the "close cache". */ +struct close_cache_s +{ + struct close_cache_s *next; + gnupg_fd_t fp; + char fname[1]; +}; +typedef struct close_cache_s *close_cache_t; +static close_cache_t close_cache; + +int iobuf_debug_mode; + + +#ifdef HAVE_W32_SYSTEM +typedef struct +{ + int sock; + int keep_open; + int no_cache; + int eof_seen; + int print_only_name; /* Flag indicating that fname is not a real file. */ + char fname[1]; /* Name of the file */ + +} sock_filter_ctx_t; +#endif /*HAVE_W32_SYSTEM*/ + +/* The first partial length header block must be of size 512 to make + * it easier (and more efficient) we use a min. block size of 512 for + * all chunks (but the last one) */ +#define OP_MIN_PARTIAL_CHUNK 512 +#define OP_MIN_PARTIAL_CHUNK_2POW 9 + +/* The context we use for the block filter (used to handle OpenPGP + length information header). */ +typedef struct +{ + int use; + size_t size; + size_t count; + int partial; /* 1 = partial header, 2 in last partial packet. */ + char *buffer; /* Used for partial header. */ + size_t buflen; /* Used size of buffer. */ + int first_c; /* First character of a partial header (which is > 0). */ + int eof; +} +block_filter_ctx_t; + + +/* Local prototypes. */ +static int underflow (iobuf_t a, int clear_pending_eof); +static int underflow_target (iobuf_t a, int clear_pending_eof, size_t target); +static int translate_file_handle (int fd, int for_write); + +/* Sends any pending data to the filter's FILTER function. Note: this + works on the filter and not on the whole pipeline. That is, + iobuf_flush doesn't necessarily cause data to be written to any + underlying file; it just causes any data buffered at the filter A + to be sent to A's filter function. + + If A is a IOBUF_OUTPUT_TEMP filter, then this also enlarges the + buffer by IOBUF_BUFFER_SIZE. + + May only be called on an IOBUF_OUTPUT or IOBUF_OUTPUT_TEMP filters. */ +static int filter_flush (iobuf_t a); + + + +/* This is a replacement for strcmp. Under W32 it does not + distinguish between backslash and slash. */ +static int +fd_cache_strcmp (const char *a, const char *b) +{ +#ifdef HAVE_DOSISH_SYSTEM + for (; *a && *b; a++, b++) + { + if (*a != *b && !((*a == '/' && *b == '\\') + || (*a == '\\' && *b == '/')) ) + break; + } + return *(const unsigned char *)a - *(const unsigned char *)b; +#else + return strcmp (a, b); +#endif +} + + +/* + * Invalidate (i.e. close) a cached iobuf + */ +static int +fd_cache_invalidate (const char *fname) +{ + close_cache_t cc; + int rc = 0; + + assert (fname); + if (DBG_IOBUF) + log_debug ("fd_cache_invalidate (%s)\n", fname); + + for (cc = close_cache; cc; cc = cc->next) + { + if (cc->fp != GNUPG_INVALID_FD && !fd_cache_strcmp (cc->fname, fname)) + { + if (DBG_IOBUF) + log_debug (" did (%s)\n", cc->fname); +#ifdef HAVE_W32_SYSTEM + if (!CloseHandle (cc->fp)) + rc = -1; +#else + rc = close (cc->fp); +#endif + cc->fp = GNUPG_INVALID_FD; + } + } + return rc; +} + + +/* Try to sync changes to the disk. This is to avoid data loss during + a system crash in write/close/rename cycle on some file + systems. */ +static int +fd_cache_synchronize (const char *fname) +{ + int err = 0; + +#ifdef HAVE_FSYNC + close_cache_t cc; + + if (DBG_IOBUF) + log_debug ("fd_cache_synchronize (%s)\n", fname); + + for (cc=close_cache; cc; cc = cc->next ) + { + if (cc->fp != GNUPG_INVALID_FD && !fd_cache_strcmp (cc->fname, fname)) + { + if (DBG_IOBUF) + log_debug (" did (%s)\n", cc->fname); + + err = fsync (cc->fp); + } + } +#else + (void)fname; +#endif /*HAVE_FSYNC*/ + + return err; +} + + +static gnupg_fd_t +direct_open (const char *fname, const char *mode, int mode700) +{ +#ifdef HAVE_W32_SYSTEM + unsigned long da, cd, sm; + HANDLE hfile; + + (void)mode700; + /* Note, that we do not handle all mode combinations */ + + /* According to the ReactOS source it seems that open() of the + * standard MSW32 crt does open the file in shared mode which is + * something new for MS applications ;-) + */ + if (strchr (mode, '+')) + { + if (fd_cache_invalidate (fname)) + return GNUPG_INVALID_FD; + da = GENERIC_READ | GENERIC_WRITE; + cd = OPEN_EXISTING; + sm = FILE_SHARE_READ | FILE_SHARE_WRITE; + } + else if (strchr (mode, 'w')) + { + if (fd_cache_invalidate (fname)) + return GNUPG_INVALID_FD; + da = GENERIC_WRITE; + cd = CREATE_ALWAYS; + sm = FILE_SHARE_WRITE; + } + else + { + da = GENERIC_READ; + cd = OPEN_EXISTING; + sm = FILE_SHARE_READ; + } + + /* We always use the Unicode version because it supports file names + * longer than MAX_PATH. (requires gpgrt 1.45) */ + if (1) + { + wchar_t *wfname = gpgrt_fname_to_wchar (fname); + if (wfname) + { + hfile = CreateFileW (wfname, da, sm, NULL, cd, + FILE_ATTRIBUTE_NORMAL, NULL); + xfree (wfname); + } + else + hfile = INVALID_HANDLE_VALUE; + } + + return hfile; + +#else /*!HAVE_W32_SYSTEM*/ + + int oflag; + int cflag = S_IRUSR | S_IWUSR; + + if (!mode700) + cflag |= S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; + + /* Note, that we do not handle all mode combinations */ + if (strchr (mode, '+')) + { + if (fd_cache_invalidate (fname)) + return GNUPG_INVALID_FD; + oflag = O_RDWR; + } + else if (strchr (mode, 'w')) + { + if (fd_cache_invalidate (fname)) + return GNUPG_INVALID_FD; + oflag = O_WRONLY | O_CREAT | O_TRUNC; + } + else + { + oflag = O_RDONLY; + } +#ifdef O_BINARY + if (strchr (mode, 'b')) + oflag |= O_BINARY; +#endif + +#ifdef __riscos__ + { + struct stat buf; + + /* Don't allow iobufs on directories */ + if (!stat (fname, &buf) && S_ISDIR (buf.st_mode) && !S_ISREG (buf.st_mode)) + return __set_errno (EISDIR); + } +#endif + return open (fname, oflag, cflag); + +#endif /*!HAVE_W32_SYSTEM*/ +} + + +/* + * Instead of closing an FD we keep it open and cache it for later reuse + * Note that this caching strategy only works if the process does not chdir. + */ +static void +fd_cache_close (const char *fname, gnupg_fd_t fp) +{ + close_cache_t cc; + + assert (fp); + if (!fname || !*fname) + { +#ifdef HAVE_W32_SYSTEM + CloseHandle (fp); +#else + close (fp); +#endif + if (DBG_IOBUF) + log_debug ("fd_cache_close (%d) real\n", (int)fp); + return; + } + /* try to reuse a slot */ + for (cc = close_cache; cc; cc = cc->next) + { + if (cc->fp == GNUPG_INVALID_FD && !fd_cache_strcmp (cc->fname, fname)) + { + cc->fp = fp; + if (DBG_IOBUF) + log_debug ("fd_cache_close (%s) used existing slot\n", fname); + return; + } + } + /* add a new one */ + if (DBG_IOBUF) + log_debug ("fd_cache_close (%s) new slot created\n", fname); + cc = xcalloc (1, sizeof *cc + strlen (fname)); + strcpy (cc->fname, fname); + cc->fp = fp; + cc->next = close_cache; + close_cache = cc; +} + +/* + * Do a direct_open on FNAME but first try to reuse one from the fd_cache + */ +static gnupg_fd_t +fd_cache_open (const char *fname, const char *mode) +{ + close_cache_t cc; + + assert (fname); + for (cc = close_cache; cc; cc = cc->next) + { + if (cc->fp != GNUPG_INVALID_FD && !fd_cache_strcmp (cc->fname, fname)) + { + gnupg_fd_t fp = cc->fp; + cc->fp = GNUPG_INVALID_FD; + if (DBG_IOBUF) + log_debug ("fd_cache_open (%s) using cached fp\n", fname); +#ifdef HAVE_W32_SYSTEM + if (SetFilePointer (fp, 0, NULL, FILE_BEGIN) == 0xffffffff) + { + log_error ("rewind file failed on handle %p: ec=%d\n", + fp, (int) GetLastError ()); + fp = GNUPG_INVALID_FD; + } +#else + if (lseek (fp, 0, SEEK_SET) == (off_t) - 1) + { + log_error ("can't rewind fd %d: %s\n", fp, strerror (errno)); + fp = GNUPG_INVALID_FD; + } +#endif + return fp; + } + } + if (DBG_IOBUF) + log_debug ("fd_cache_open (%s) not cached\n", fname); + return direct_open (fname, mode, 0); +} + + +static int +file_filter (void *opaque, int control, iobuf_t chain, byte * buf, + size_t * ret_len) +{ + file_filter_ctx_t *a = opaque; + gnupg_fd_t f = a->fp; + size_t size = *ret_len; + size_t nbytes = 0; + int rc = 0; + + (void)chain; /* Not used. */ + + if (control == IOBUFCTRL_UNDERFLOW) + { + assert (size); /* We need a buffer. */ + if (a->eof_seen) + { + rc = -1; + *ret_len = 0; + } + else + { +#ifdef HAVE_W32_SYSTEM + unsigned long nread; + + nbytes = 0; + if (!ReadFile (f, buf, size, &nread, NULL)) + { + int ec = (int) GetLastError (); + if (ec != ERROR_BROKEN_PIPE) + { + rc = gpg_error_from_errno (ec); + log_error ("%s: read error: ec=%d\n", a->fname, ec); + } + } + else if (!nread) + { + a->eof_seen = 1; + rc = -1; + } + else + { + nbytes = nread; + } + +#else + + int n; + + nbytes = 0; + do + { + n = read (f, buf, size); + } + while (n == -1 && errno == EINTR); + if (n == -1) + { /* error */ + if (errno != EPIPE) + { + rc = gpg_error_from_syserror (); + log_error ("%s: read error: %s\n", + a->fname, strerror (errno)); + } + } + else if (!n) + { /* eof */ + a->eof_seen = 1; + rc = -1; + } + else + { + nbytes = n; + } +#endif + *ret_len = nbytes; + } + } + else if (control == IOBUFCTRL_FLUSH) + { + if (size) + { +#ifdef HAVE_W32_SYSTEM + byte *p = buf; + unsigned long n; + + nbytes = size; + do + { + if (size && !WriteFile (f, p, nbytes, &n, NULL)) + { + int ec = (int) GetLastError (); + rc = gpg_error_from_errno (ec); + log_error ("%s: write error: ec=%d\n", a->fname, ec); + break; + } + p += n; + nbytes -= n; + } + while (nbytes); + nbytes = p - buf; +#else + byte *p = buf; + int n; + + nbytes = size; + do + { + do + { + n = write (f, p, nbytes); + } + while (n == -1 && errno == EINTR); + if (n > 0) + { + p += n; + nbytes -= n; + } + } + while (n != -1 && nbytes); + if (n == -1) + { + rc = gpg_error_from_syserror (); + log_error ("%s: write error: %s\n", a->fname, strerror (errno)); + } + nbytes = p - buf; +#endif + } + *ret_len = nbytes; + } + else if (control == IOBUFCTRL_INIT) + { + a->eof_seen = 0; + a->keep_open = 0; + a->no_cache = 0; + } + else if (control == IOBUFCTRL_DESC) + { + mem2str (buf, "file_filter(fd)", *ret_len); + } + else if (control == IOBUFCTRL_FREE) + { + if (f != FD_FOR_STDIN && f != FD_FOR_STDOUT) + { + if (DBG_IOBUF) + log_debug ("%s: close fd/handle %d\n", a->fname, FD2INT (f)); + if (!a->keep_open) + fd_cache_close (a->no_cache ? NULL : a->fname, f); + } + xfree (a); /* We can free our context now. */ + } + + return rc; +} + + +/* Similar to file_filter but using the estream system. */ +static int +file_es_filter (void *opaque, int control, iobuf_t chain, byte * buf, + size_t * ret_len) +{ + file_es_filter_ctx_t *a = opaque; + estream_t f = a->fp; + size_t size = *ret_len; + size_t nbytes = 0; + int rc = 0; + + (void)chain; /* Not used. */ + + if (control == IOBUFCTRL_UNDERFLOW) + { + assert (size); /* We need a buffer. */ + if (a->eof_seen) + { + rc = -1; + *ret_len = 0; + } + else + { + nbytes = 0; + rc = es_read (f, buf, size, &nbytes); + if (rc == -1) + { /* error */ + rc = gpg_error_from_syserror (); + log_error ("%s: read error: %s\n", a->fname, strerror (errno)); + } + else if (!nbytes) + { /* eof */ + a->eof_seen = 1; + rc = -1; + } + *ret_len = nbytes; + } + } + else if (control == IOBUFCTRL_FLUSH) + { + if (size) + { + byte *p = buf; + size_t nwritten; + + nbytes = size; + do + { + nwritten = 0; + if (es_write (f, p, nbytes, &nwritten)) + { + rc = gpg_error_from_syserror (); + log_error ("%s: write error: %s\n", + a->fname, strerror (errno)); + break; + } + p += nwritten; + nbytes -= nwritten; + } + while (nbytes); + nbytes = p - buf; + } + *ret_len = nbytes; + } + else if (control == IOBUFCTRL_INIT) + { + a->eof_seen = 0; + a->no_cache = 0; + } + else if (control == IOBUFCTRL_DESC) + { + mem2str (buf, "estream_filter", *ret_len); + } + else if (control == IOBUFCTRL_FREE) + { + if (f != es_stdin && f != es_stdout) + { + if (DBG_IOBUF) + log_debug ("%s: es_fclose %p\n", a->fname, f); + if (!a->keep_open) + es_fclose (f); + } + f = NULL; + xfree (a); /* We can free our context now. */ + } + + return rc; +} + + +#ifdef HAVE_W32_SYSTEM +/* Because network sockets are special objects under Lose32 we have to + use a dedicated filter for them. */ +static int +sock_filter (void *opaque, int control, iobuf_t chain, byte * buf, + size_t * ret_len) +{ + sock_filter_ctx_t *a = opaque; + size_t size = *ret_len; + size_t nbytes = 0; + int rc = 0; + + (void)chain; + + if (control == IOBUFCTRL_UNDERFLOW) + { + assert (size); /* need a buffer */ + if (a->eof_seen) + { + rc = -1; + *ret_len = 0; + } + else + { + int nread; + + nread = recv (a->sock, buf, size, 0); + if (nread == SOCKET_ERROR) + { + int ec = (int) WSAGetLastError (); + rc = gpg_error_from_errno (ec); + log_error ("socket read error: ec=%d\n", ec); + } + else if (!nread) + { + a->eof_seen = 1; + rc = -1; + } + else + { + nbytes = nread; + } + *ret_len = nbytes; + } + } + else if (control == IOBUFCTRL_FLUSH) + { + if (size) + { + byte *p = buf; + int n; + + nbytes = size; + do + { + n = send (a->sock, p, nbytes, 0); + if (n == SOCKET_ERROR) + { + int ec = (int) WSAGetLastError (); + rc = gpg_error_from_errno (ec); + log_error ("socket write error: ec=%d\n", ec); + break; + } + p += n; + nbytes -= n; + } + while (nbytes); + nbytes = p - buf; + } + *ret_len = nbytes; + } + else if (control == IOBUFCTRL_INIT) + { + a->eof_seen = 0; + a->keep_open = 0; + a->no_cache = 0; + } + else if (control == IOBUFCTRL_DESC) + { + mem2str (buf, "sock_filter", *ret_len); + } + else if (control == IOBUFCTRL_FREE) + { + if (!a->keep_open) + closesocket (a->sock); + xfree (a); /* we can free our context now */ + } + return rc; +} +#endif /*HAVE_W32_SYSTEM*/ + +/**************** + * This is used to implement the block write mode. + * Block reading is done on a byte by byte basis in readbyte(), + * without a filter + */ +static int +block_filter (void *opaque, int control, iobuf_t chain, byte * buffer, + size_t * ret_len) +{ + block_filter_ctx_t *a = opaque; + char *buf = (char *)buffer; + size_t size = *ret_len; + int c, needed, rc = 0; + char *p; + + if (control == IOBUFCTRL_UNDERFLOW) + { + size_t n = 0; + + p = buf; + assert (size); /* need a buffer */ + if (a->eof) /* don't read any further */ + rc = -1; + while (!rc && size) + { + if (!a->size) + { /* get the length bytes */ + if (a->partial == 2) + { + a->eof = 1; + if (!n) + rc = -1; + break; + } + else if (a->partial) + { + /* These OpenPGP introduced huffman like encoded length + * bytes are really a mess :-( */ + if (a->first_c) + { + c = a->first_c; + a->first_c = 0; + } + else if ((c = iobuf_get (chain)) == -1) + { + log_error ("block_filter: 1st length byte missing\n"); + rc = GPG_ERR_BAD_DATA; + break; + } + if (c < 192) + { + a->size = c; + a->partial = 2; + if (!a->size) + { + a->eof = 1; + if (!n) + rc = -1; + break; + } + } + else if (c < 224) + { + a->size = (c - 192) * 256; + if ((c = iobuf_get (chain)) == -1) + { + log_error + ("block_filter: 2nd length byte missing\n"); + rc = GPG_ERR_BAD_DATA; + break; + } + a->size += c + 192; + a->partial = 2; + if (!a->size) + { + a->eof = 1; + if (!n) + rc = -1; + break; + } + } + else if (c == 255) + { + a->size = iobuf_get_noeof (chain) << 24; + a->size |= iobuf_get_noeof (chain) << 16; + a->size |= iobuf_get_noeof (chain) << 8; + if ((c = iobuf_get (chain)) == -1) + { + log_error ("block_filter: invalid 4 byte length\n"); + rc = GPG_ERR_BAD_DATA; + break; + } + a->size |= c; + a->partial = 2; + if (!a->size) + { + a->eof = 1; + if (!n) + rc = -1; + break; + } + } + else + { /* Next partial body length. */ + a->size = 1 << (c & 0x1f); + } + /* log_debug("partial: ctx=%p c=%02x size=%u\n", a, c, a->size); */ + } + else + BUG (); + } + + while (!rc && size && a->size) + { + needed = size < a->size ? size : a->size; + c = iobuf_read (chain, p, needed); + if (c < needed) + { + if (c == -1) + c = 0; + log_error + ("block_filter %p: read error (size=%lu,a->size=%lu)\n", + a, (ulong) size + c, (ulong) a->size + c); + rc = GPG_ERR_BAD_DATA; + } + else + { + size -= c; + a->size -= c; + p += c; + n += c; + } + } + } + *ret_len = n; + } + else if (control == IOBUFCTRL_FLUSH) + { + if (a->partial) + { /* the complicated openpgp scheme */ + size_t blen, n, nbytes = size + a->buflen; + + assert (a->buflen <= OP_MIN_PARTIAL_CHUNK); + if (nbytes < OP_MIN_PARTIAL_CHUNK) + { + /* not enough to write a partial block out; so we store it */ + if (!a->buffer) + a->buffer = xmalloc (OP_MIN_PARTIAL_CHUNK); + memcpy (a->buffer + a->buflen, buf, size); + a->buflen += size; + } + else + { /* okay, we can write out something */ + /* do this in a loop to use the most efficient block lengths */ + p = buf; + do + { + /* find the best matching block length - this is limited + * by the size of the internal buffering */ + for (blen = OP_MIN_PARTIAL_CHUNK * 2, + c = OP_MIN_PARTIAL_CHUNK_2POW + 1; blen <= nbytes; + blen *= 2, c++) + ; + blen /= 2; + c--; + /* write the partial length header */ + assert (c <= 0x1f); /*;-) */ + c |= 0xe0; + iobuf_put (chain, c); + if ((n = a->buflen)) + { /* write stuff from the buffer */ + assert (n == OP_MIN_PARTIAL_CHUNK); + if (iobuf_write (chain, a->buffer, n)) + rc = gpg_error_from_syserror (); + a->buflen = 0; + nbytes -= n; + } + if ((n = nbytes) > blen) + n = blen; + if (n && iobuf_write (chain, p, n)) + rc = gpg_error_from_syserror (); + p += n; + nbytes -= n; + } + while (!rc && nbytes >= OP_MIN_PARTIAL_CHUNK); + /* store the rest in the buffer */ + if (!rc && nbytes) + { + assert (!a->buflen); + assert (nbytes < OP_MIN_PARTIAL_CHUNK); + if (!a->buffer) + a->buffer = xmalloc (OP_MIN_PARTIAL_CHUNK); + memcpy (a->buffer, p, nbytes); + a->buflen = nbytes; + } + } + } + else + BUG (); + } + else if (control == IOBUFCTRL_INIT) + { + if (DBG_IOBUF) + log_debug ("init block_filter %p\n", a); + if (a->partial) + a->count = 0; + else if (a->use == IOBUF_INPUT) + a->count = a->size = 0; + else + a->count = a->size; /* force first length bytes */ + a->eof = 0; + a->buffer = NULL; + a->buflen = 0; + } + else if (control == IOBUFCTRL_DESC) + { + mem2str (buf, "block_filter", *ret_len); + } + else if (control == IOBUFCTRL_FREE) + { + if (a->use == IOBUF_OUTPUT) + { /* write the end markers */ + if (a->partial) + { + u32 len; + /* write out the remaining bytes without a partial header + * the length of this header may be 0 - but if it is + * the first block we are not allowed to use a partial header + * and frankly we can't do so, because this length must be + * a power of 2. This is _really_ complicated because we + * have to check the possible length of a packet prior + * to it's creation: a chain of filters becomes complicated + * and we need a lot of code to handle compressed packets etc. + * :-((((((( + */ + /* construct header */ + len = a->buflen; + /*log_debug("partial: remaining length=%u\n", len ); */ + if (len < 192) + rc = iobuf_put (chain, len); + else if (len < 8384) + { + if (!(rc = iobuf_put (chain, ((len - 192) / 256) + 192))) + rc = iobuf_put (chain, ((len - 192) % 256)); + } + else + { /* use a 4 byte header */ + if (!(rc = iobuf_put (chain, 0xff))) + if (!(rc = iobuf_put (chain, (len >> 24) & 0xff))) + if (!(rc = iobuf_put (chain, (len >> 16) & 0xff))) + if (!(rc = iobuf_put (chain, (len >> 8) & 0xff))) + rc = iobuf_put (chain, len & 0xff); + } + if (!rc && len) + rc = iobuf_write (chain, a->buffer, len); + if (rc) + { + log_error ("block_filter: write error: %s\n", + strerror (errno)); + rc = gpg_error_from_syserror (); + } + xfree (a->buffer); + a->buffer = NULL; + a->buflen = 0; + } + else + BUG (); + } + else if (a->size) + { + log_error ("block_filter: pending bytes!\n"); + } + if (DBG_IOBUF) + log_debug ("free block_filter %p\n", a); + xfree (a); /* we can free our context now */ + } + + return rc; +} + +#define MAX_IOBUF_DESC 32 +/* + * Fill the buffer by the description of iobuf A. + * The buffer size should be MAX_IOBUF_DESC (or larger). + * Returns BUF as (const char *). + */ +static const char * +iobuf_desc (iobuf_t a, byte *buf) +{ + size_t len = MAX_IOBUF_DESC; + + if (! a || ! a->filter) + memcpy (buf, "?", 2); + else + a->filter (a->filter_ov, IOBUFCTRL_DESC, NULL, buf, &len); + + return buf; +} + +static void +print_chain (iobuf_t a) +{ + if (!DBG_IOBUF) + return; + for (; a; a = a->chain) + { + byte desc[MAX_IOBUF_DESC]; + + log_debug ("iobuf chain: %d.%d '%s' filter_eof=%d start=%d len=%d\n", + a->no, a->subno, iobuf_desc (a, desc), a->filter_eof, + (int) a->d.start, (int) a->d.len); + } +} + +int +iobuf_print_chain (iobuf_t a) +{ + print_chain (a); + return 0; +} + +iobuf_t +iobuf_alloc (int use, size_t bufsize) +{ + iobuf_t a; + static int number = 0; + + assert (use == IOBUF_INPUT || use == IOBUF_INPUT_TEMP + || use == IOBUF_OUTPUT || use == IOBUF_OUTPUT_TEMP); + if (bufsize == 0) + { + log_bug ("iobuf_alloc() passed a bufsize of 0!\n"); + bufsize = IOBUF_BUFFER_SIZE; + } + + a = xcalloc (1, sizeof *a); + a->use = use; + a->d.buf = xmalloc (bufsize); + a->d.size = bufsize; + a->no = ++number; + a->subno = 0; + a->real_fname = NULL; + return a; +} + +int +iobuf_close (iobuf_t a) +{ + iobuf_t a_chain; + size_t dummy_len = 0; + int rc = 0; + + for (; a; a = a_chain) + { + byte desc[MAX_IOBUF_DESC]; + int rc2 = 0; + + a_chain = a->chain; + + if (a->use == IOBUF_OUTPUT && (rc = filter_flush (a))) + log_error ("filter_flush failed on close: %s\n", gpg_strerror (rc)); + + if (DBG_IOBUF) + log_debug ("iobuf-%d.%d: close '%s'\n", + a->no, a->subno, iobuf_desc (a, desc)); + + if (a->filter && (rc2 = a->filter (a->filter_ov, IOBUFCTRL_FREE, + a->chain, NULL, &dummy_len))) + log_error ("IOBUFCTRL_FREE failed on close: %s\n", gpg_strerror (rc)); + if (! rc && rc2) + /* Whoops! An error occurred. Save it in RC if we haven't + already recorded an error. */ + rc = rc2; + + xfree (a->real_fname); + if (a->d.buf) + { + memset (a->d.buf, 0, a->d.size); /* erase the buffer */ + xfree (a->d.buf); + } + xfree (a); + } + return rc; +} + +int +iobuf_cancel (iobuf_t a) +{ + const char *s; + iobuf_t a2; + int rc; +#if defined(HAVE_W32_SYSTEM) || defined(__riscos__) + char *remove_name = NULL; +#endif + + if (a && a->use == IOBUF_OUTPUT) + { + s = iobuf_get_real_fname (a); + if (s && *s) + { +#if defined(HAVE_W32_SYSTEM) || defined(__riscos__) + remove_name = xstrdup (s); +#else + remove (s); +#endif + } + } + + /* send a cancel message to all filters */ + for (a2 = a; a2; a2 = a2->chain) + { + size_t dummy; + if (a2->filter) + a2->filter (a2->filter_ov, IOBUFCTRL_CANCEL, a2->chain, NULL, &dummy); + } + + rc = iobuf_close (a); +#if defined(HAVE_W32_SYSTEM) || defined(__riscos__) + if (remove_name) + { + /* Argg, MSDOS does not allow removing open files. So + * we have to do it here */ + gnupg_remove (remove_name); + xfree (remove_name); + } +#endif + return rc; +} + + +iobuf_t +iobuf_temp (void) +{ + return iobuf_alloc (IOBUF_OUTPUT_TEMP, IOBUF_BUFFER_SIZE); +} + +iobuf_t +iobuf_temp_with_content (const char *buffer, size_t length) +{ + iobuf_t a; + int i; + + a = iobuf_alloc (IOBUF_INPUT_TEMP, length); + assert (length == a->d.size); + /* memcpy (a->d.buf, buffer, length); */ + for (i=0; i < length; i++) + a->d.buf[i] = buffer[i]; + a->d.len = length; + + return a; +} + + +int +iobuf_is_pipe_filename (const char *fname) +{ + if (!fname || (*fname=='-' && !fname[1]) ) + return 1; + return check_special_filename (fname, 0, 1) != -1; +} + + +static iobuf_t +do_open (const char *fname, int special_filenames, + int use, const char *opentype, int mode700) +{ + iobuf_t a; + gnupg_fd_t fp; + file_filter_ctx_t *fcx; + size_t len = 0; + int print_only = 0; + int fd; + byte desc[MAX_IOBUF_DESC]; + + assert (use == IOBUF_INPUT || use == IOBUF_OUTPUT); + + if (special_filenames + /* NULL or '-'. */ + && (!fname || (*fname == '-' && !fname[1]))) + { + if (use == IOBUF_INPUT) + { + fp = FD_FOR_STDIN; + fname = "[stdin]"; + } + else + { + fp = FD_FOR_STDOUT; + fname = "[stdout]"; + } + print_only = 1; + } + else if (!fname) + return NULL; + else if (special_filenames + && (fd = check_special_filename (fname, 0, 1)) != -1) + return iobuf_fdopen (translate_file_handle (fd, use == IOBUF_INPUT ? 0 : 1), + opentype); + else + { + if (use == IOBUF_INPUT) + fp = fd_cache_open (fname, opentype); + else + fp = direct_open (fname, opentype, mode700); + if (fp == GNUPG_INVALID_FD) + return NULL; + } + + a = iobuf_alloc (use, IOBUF_BUFFER_SIZE); + fcx = xmalloc (sizeof *fcx + strlen (fname)); + fcx->fp = fp; + fcx->print_only_name = print_only; + strcpy (fcx->fname, fname); + if (!print_only) + a->real_fname = xstrdup (fname); + a->filter = file_filter; + a->filter_ov = fcx; + file_filter (fcx, IOBUFCTRL_INIT, NULL, NULL, &len); + if (DBG_IOBUF) + log_debug ("iobuf-%d.%d: open '%s' desc=%s fd=%d\n", + a->no, a->subno, fname, iobuf_desc (a, desc), FD2INT (fcx->fp)); + + return a; +} + +iobuf_t +iobuf_open (const char *fname) +{ + return do_open (fname, 1, IOBUF_INPUT, "rb", 0); +} + +iobuf_t +iobuf_create (const char *fname, int mode700) +{ + return do_open (fname, 1, IOBUF_OUTPUT, "wb", mode700); +} + +iobuf_t +iobuf_openrw (const char *fname) +{ + return do_open (fname, 0, IOBUF_OUTPUT, "r+b", 0); +} + + +static iobuf_t +do_iobuf_fdopen (int fd, const char *mode, int keep_open) +{ + iobuf_t a; + gnupg_fd_t fp; + file_filter_ctx_t *fcx; + size_t len; + + fp = INT2FD (fd); + + a = iobuf_alloc (strchr (mode, 'w') ? IOBUF_OUTPUT : IOBUF_INPUT, + IOBUF_BUFFER_SIZE); + fcx = xmalloc (sizeof *fcx + 20); + fcx->fp = fp; + fcx->print_only_name = 1; + fcx->keep_open = keep_open; + sprintf (fcx->fname, "[fd %d]", fd); + a->filter = file_filter; + a->filter_ov = fcx; + file_filter (fcx, IOBUFCTRL_INIT, NULL, NULL, &len); + if (DBG_IOBUF) + log_debug ("iobuf-%d.%d: fdopen%s '%s'\n", + a->no, a->subno, keep_open? "_nc":"", fcx->fname); + iobuf_ioctl (a, IOBUF_IOCTL_NO_CACHE, 1, NULL); + return a; +} + + +iobuf_t +iobuf_fdopen (int fd, const char *mode) +{ + return do_iobuf_fdopen (fd, mode, 0); +} + +iobuf_t +iobuf_fdopen_nc (int fd, const char *mode) +{ + return do_iobuf_fdopen (fd, mode, 1); +} + + +iobuf_t +iobuf_esopen (estream_t estream, const char *mode, int keep_open) +{ + iobuf_t a; + file_es_filter_ctx_t *fcx; + size_t len = 0; + + a = iobuf_alloc (strchr (mode, 'w') ? IOBUF_OUTPUT : IOBUF_INPUT, + IOBUF_BUFFER_SIZE); + fcx = xtrymalloc (sizeof *fcx + 30); + fcx->fp = estream; + fcx->print_only_name = 1; + fcx->keep_open = keep_open; + sprintf (fcx->fname, "[fd %p]", estream); + a->filter = file_es_filter; + a->filter_ov = fcx; + file_es_filter (fcx, IOBUFCTRL_INIT, NULL, NULL, &len); + if (DBG_IOBUF) + log_debug ("iobuf-%d.%d: esopen%s '%s'\n", + a->no, a->subno, keep_open? "_nc":"", fcx->fname); + return a; +} + + +iobuf_t +iobuf_sockopen (int fd, const char *mode) +{ + iobuf_t a; +#ifdef HAVE_W32_SYSTEM + sock_filter_ctx_t *scx; + size_t len; + + a = iobuf_alloc (strchr (mode, 'w') ? IOBUF_OUTPUT : IOBUF_INPUT, + IOBUF_BUFFER_SIZE); + scx = xmalloc (sizeof *scx + 25); + scx->sock = fd; + scx->print_only_name = 1; + sprintf (scx->fname, "[sock %d]", fd); + a->filter = sock_filter; + a->filter_ov = scx; + sock_filter (scx, IOBUFCTRL_INIT, NULL, NULL, &len); + if (DBG_IOBUF) + log_debug ("iobuf-%d.%d: sockopen '%s'\n", a->no, a->subno, scx->fname); + iobuf_ioctl (a, IOBUF_IOCTL_NO_CACHE, 1, NULL); +#else + a = iobuf_fdopen (fd, mode); +#endif + return a; +} + +int +iobuf_ioctl (iobuf_t a, iobuf_ioctl_t cmd, int intval, void *ptrval) +{ + byte desc[MAX_IOBUF_DESC]; + + if (cmd == IOBUF_IOCTL_KEEP_OPEN) + { + /* Keep system filepointer/descriptor open. This was used in + the past by http.c; this ioctl is not directly used + anymore. */ + if (DBG_IOBUF) + log_debug ("iobuf-%d.%d: ioctl '%s' keep_open=%d\n", + a ? a->no : -1, a ? a->subno : -1, iobuf_desc (a, desc), + intval); + for (; a; a = a->chain) + if (!a->chain && a->filter == file_filter) + { + file_filter_ctx_t *b = a->filter_ov; + b->keep_open = intval; + return 0; + } +#ifdef HAVE_W32_SYSTEM + else if (!a->chain && a->filter == sock_filter) + { + sock_filter_ctx_t *b = a->filter_ov; + b->keep_open = intval; + return 0; + } +#endif + } + else if (cmd == IOBUF_IOCTL_INVALIDATE_CACHE) + { + if (DBG_IOBUF) + log_debug ("iobuf-*.*: ioctl '%s' invalidate\n", + ptrval ? (char *) ptrval : "?"); + if (!a && !intval && ptrval) + { + if (fd_cache_invalidate (ptrval)) + return -1; + return 0; + } + } + else if (cmd == IOBUF_IOCTL_NO_CACHE) + { + if (DBG_IOBUF) + log_debug ("iobuf-%d.%d: ioctl '%s' no_cache=%d\n", + a ? a->no : -1, a ? a->subno : -1, iobuf_desc (a, desc), + intval); + for (; a; a = a->chain) + if (!a->chain && a->filter == file_filter) + { + file_filter_ctx_t *b = a->filter_ov; + b->no_cache = intval; + return 0; + } +#ifdef HAVE_W32_SYSTEM + else if (!a->chain && a->filter == sock_filter) + { + sock_filter_ctx_t *b = a->filter_ov; + b->no_cache = intval; + return 0; + } +#endif + } + else if (cmd == IOBUF_IOCTL_FSYNC) + { + /* Do a fsync on the open fd and return any errors to the caller + of iobuf_ioctl. Note that we work on a file name here. */ + if (DBG_IOBUF) + log_debug ("iobuf-*.*: ioctl '%s' fsync\n", + ptrval? (const char*)ptrval:"<null>"); + + if (!a && !intval && ptrval) + { + return fd_cache_synchronize (ptrval); + } + } + + + return -1; +} + + +/**************** + * Register an i/o filter. + */ +int +iobuf_push_filter (iobuf_t a, + int (*f) (void *opaque, int control, + iobuf_t chain, byte * buf, size_t * len), + void *ov) +{ + return iobuf_push_filter2 (a, f, ov, 0); +} + +int +iobuf_push_filter2 (iobuf_t a, + int (*f) (void *opaque, int control, + iobuf_t chain, byte * buf, size_t * len), + void *ov, int rel_ov) +{ + iobuf_t b; + size_t dummy_len = 0; + int rc = 0; + + if (a->use == IOBUF_OUTPUT && (rc = filter_flush (a))) + return rc; + + if (a->subno >= MAX_NESTING_FILTER) + { + log_error ("i/o filter too deeply nested - corrupted data?\n"); + return GPG_ERR_BAD_DATA; + } + + /* We want to create a new filter and put it in front of A. A + simple implementation would do: + + b = iobuf_alloc (...); + b->chain = a; + return a; + + This is a bit problematic: A is the head of the pipeline and + there are potentially many pointers to it. Requiring the caller + to update all of these pointers is a burden. + + An alternative implementation would add a level of indirection. + For instance, we could use a pipeline object, which contains a + pointer to the first filter in the pipeline. This is not what we + do either. + + Instead, we allocate a new buffer (B) and copy the first filter's + state into that and use the initial buffer (A) for the new + filter. One limitation of this approach is that it is not + practical to maintain a pointer to a specific filter's state. + + Before: + + A + | + v 0x100 0x200 + +----------+ +----------+ + | filter x |--------->| filter y |---->.... + +----------+ +----------+ + + After: B + | + v 0x300 + +----------+ + A | filter x | + | +----------+ + v 0x100 ^ v 0x200 + +----------+ +----------+ + | filter w | | filter y |---->.... + +----------+ +----------+ + + Note: filter x's address changed from 0x100 to 0x300, but A still + points to the head of the pipeline. + */ + + b = xmalloc (sizeof *b); + memcpy (b, a, sizeof *b); + /* fixme: it is stupid to keep a copy of the name at every level + * but we need the name somewhere because the name known by file_filter + * may have been released when we need the name of the file */ + b->real_fname = a->real_fname ? xstrdup (a->real_fname) : NULL; + /* remove the filter stuff from the new stream */ + a->filter = NULL; + a->filter_ov = NULL; + a->filter_ov_owner = 0; + a->filter_eof = 0; + if (a->use == IOBUF_OUTPUT_TEMP) + /* A TEMP filter buffers any data sent to it; it does not forward + any data down the pipeline. If we add a new filter to the + pipeline, it shouldn't also buffer data. It should send it + downstream to be buffered. Thus, the correct type for a filter + added in front of an IOBUF_OUTPUT_TEMP filter is IOBUF_OUPUT, not + IOBUF_OUTPUT_TEMP. */ + { + a->use = IOBUF_OUTPUT; + + /* When pipeline is written to, the temp buffer's size is + increased accordingly. We don't need to allocate a 10 MB + buffer for a non-terminal filter. Just use the default + size. */ + a->d.size = IOBUF_BUFFER_SIZE; + } + else if (a->use == IOBUF_INPUT_TEMP) + /* Same idea as above. */ + { + a->use = IOBUF_INPUT; + a->d.size = IOBUF_BUFFER_SIZE; + } + + /* The new filter (A) gets a new buffer. + + If the pipeline is an output or temp pipeline, then giving the + buffer to the new filter means that data that was written before + the filter was pushed gets sent to the filter. That's clearly + wrong. + + If the pipeline is an input pipeline, then giving the buffer to + the new filter (A) means that data that has read from (B), but + not yet read from the pipeline won't be processed by the new + filter (A)! That's certainly not what we want. */ + a->d.buf = xmalloc (a->d.size); + a->d.len = 0; + a->d.start = 0; + + /* disable nlimit for the new stream */ + a->ntotal = b->ntotal + b->nbytes; + a->nlimit = a->nbytes = 0; + a->nofast = 0; + /* make a link from the new stream to the original stream */ + a->chain = b; + + /* setup the function on the new stream */ + a->filter = f; + a->filter_ov = ov; + a->filter_ov_owner = rel_ov; + + a->subno = b->subno + 1; + + if (DBG_IOBUF) + { + byte desc[MAX_IOBUF_DESC]; + log_debug ("iobuf-%d.%d: push '%s'\n", + a->no, a->subno, iobuf_desc (a, desc)); + print_chain (a); + } + + /* now we can initialize the new function if we have one */ + if (a->filter && (rc = a->filter (a->filter_ov, IOBUFCTRL_INIT, a->chain, + NULL, &dummy_len))) + log_error ("IOBUFCTRL_INIT failed: %s\n", gpg_strerror (rc)); + return rc; +} + +/**************** + * Remove an i/o filter. + */ +int +iobuf_pop_filter (iobuf_t a, int (*f) (void *opaque, int control, + iobuf_t chain, byte * buf, size_t * len), + void *ov) +{ + iobuf_t b; + size_t dummy_len = 0; + int rc = 0; + byte desc[MAX_IOBUF_DESC]; + + if (DBG_IOBUF) + log_debug ("iobuf-%d.%d: pop '%s'\n", + a->no, a->subno, iobuf_desc (a, desc)); + if (a->use == IOBUF_INPUT_TEMP || a->use == IOBUF_OUTPUT_TEMP) + { + /* This should be the last filter in the pipeline. */ + assert (! a->chain); + return 0; + } + if (!a->filter) + { /* this is simple */ + b = a->chain; + assert (b); + xfree (a->d.buf); + xfree (a->real_fname); + memcpy (a, b, sizeof *a); + xfree (b); + return 0; + } + for (b = a; b; b = b->chain) + if (b->filter == f && (!ov || b->filter_ov == ov)) + break; + if (!b) + log_bug ("iobuf_pop_filter(): filter function not found\n"); + + /* flush this stream if it is an output stream */ + if (a->use == IOBUF_OUTPUT && (rc = filter_flush (b))) + { + log_error ("filter_flush failed in iobuf_pop_filter: %s\n", + gpg_strerror (rc)); + return rc; + } + /* and tell the filter to free it self */ + if (b->filter && (rc = b->filter (b->filter_ov, IOBUFCTRL_FREE, b->chain, + NULL, &dummy_len))) + { + log_error ("IOBUFCTRL_FREE failed: %s\n", gpg_strerror (rc)); + return rc; + } + if (b->filter_ov && b->filter_ov_owner) + { + xfree (b->filter_ov); + b->filter_ov = NULL; + } + + + /* and see how to remove it */ + if (a == b && !b->chain) + log_bug ("can't remove the last filter from the chain\n"); + else if (a == b) + { /* remove the first iobuf from the chain */ + /* everything from b is copied to a. This is save because + * a flush has been done on the to be removed entry + */ + b = a->chain; + xfree (a->d.buf); + xfree (a->real_fname); + memcpy (a, b, sizeof *a); + xfree (b); + if (DBG_IOBUF) + log_debug ("iobuf-%d.%d: popped filter\n", a->no, a->subno); + } + else if (!b->chain) + { /* remove the last iobuf from the chain */ + log_bug ("Ohh jeee, trying to remove a head filter\n"); + } + else + { /* remove an intermediate iobuf from the chain */ + log_bug ("Ohh jeee, trying to remove an intermediate filter\n"); + } + + return rc; +} + + +/**************** + * read underflow: read at least one byte into the buffer and return + * the first byte or -1 on EOF. + */ +static int +underflow (iobuf_t a, int clear_pending_eof) +{ + return underflow_target (a, clear_pending_eof, 1); +} + + +/**************** + * read underflow: read TARGET bytes into the buffer and return + * the first byte or -1 on EOF. + */ +static int +underflow_target (iobuf_t a, int clear_pending_eof, size_t target) +{ + size_t len; + int rc; + + if (DBG_IOBUF) + log_debug ("iobuf-%d.%d: underflow: buffer size: %d; still buffered: %d => space for %d bytes\n", + a->no, a->subno, + (int) a->d.size, (int) (a->d.len - a->d.start), + (int) (a->d.size - (a->d.len - a->d.start))); + + if (a->use == IOBUF_INPUT_TEMP) + /* By definition, there isn't more data to read into the + buffer. */ + return -1; + + assert (a->use == IOBUF_INPUT); + + /* If there is still some buffered data, then move it to the start + of the buffer and try to fill the end of the buffer. (This is + useful if we are called from iobuf_peek().) */ + assert (a->d.start <= a->d.len); + a->d.len -= a->d.start; + memmove (a->d.buf, &a->d.buf[a->d.start], a->d.len); + a->d.start = 0; + + if (a->d.len < target && a->filter_eof) + /* The last time we tried to read from this filter, we got an EOF. + We couldn't return the EOF, because there was buffered data. + Since there is no longer any buffered data, return the + error. */ + { + if (DBG_IOBUF) + log_debug ("iobuf-%d.%d: underflow: eof (pending eof)\n", + a->no, a->subno); + if (! clear_pending_eof) + return -1; + + if (a->chain) + /* A filter follows this one. Free this filter. */ + { + iobuf_t b = a->chain; + if (DBG_IOBUF) + log_debug ("iobuf-%d.%d: filter popped (pending EOF returned)\n", + a->no, a->subno); + xfree (a->d.buf); + xfree (a->real_fname); + memcpy (a, b, sizeof *a); + xfree (b); + print_chain (a); + } + else + a->filter_eof = 0; /* for the top level filter */ + return -1; /* return one(!) EOF */ + } + + if (a->d.len == 0 && a->error) + /* The last time we tried to read from this filter, we got an + error. We couldn't return the error, because there was + buffered data. Since there is no longer any buffered data, + return the error. */ + { + if (DBG_IOBUF) + log_debug ("iobuf-%d.%d: pending error (%s) returned\n", + a->no, a->subno, gpg_strerror (a->error)); + return -1; + } + + if (a->filter && ! a->filter_eof && ! a->error) + /* We have a filter function and the last time we tried to read we + didn't get an EOF or an error. Try to fill the buffer. */ + { + /* Be careful to account for any buffered data. */ + len = a->d.size - a->d.len; + if (DBG_IOBUF) + log_debug ("iobuf-%d.%d: underflow: A->FILTER (%lu bytes)\n", + a->no, a->subno, (ulong) len); + if (len == 0) + /* There is no space for more data. Don't bother calling + A->FILTER. */ + rc = 0; + else + rc = a->filter (a->filter_ov, IOBUFCTRL_UNDERFLOW, a->chain, + &a->d.buf[a->d.len], &len); + a->d.len += len; + + if (DBG_IOBUF) + log_debug ("iobuf-%d.%d: A->FILTER() returned rc=%d (%s), read %lu bytes\n", + a->no, a->subno, + rc, rc == 0 ? "ok" : rc == -1 ? "EOF" : gpg_strerror (rc), + (ulong) len); +/* if( a->no == 1 ) */ +/* log_hexdump (" data:", a->d.buf, len); */ + + if (rc == -1) + /* EOF. */ + { + size_t dummy_len = 0; + + /* Tell the filter to free itself */ + if ((rc = a->filter (a->filter_ov, IOBUFCTRL_FREE, a->chain, + NULL, &dummy_len))) + log_error ("IOBUFCTRL_FREE failed: %s\n", gpg_strerror (rc)); + + /* Free everything except for the internal buffer. */ + if (a->filter_ov && a->filter_ov_owner) + xfree (a->filter_ov); + a->filter_ov = NULL; + a->filter = NULL; + a->filter_eof = 1; + + if (clear_pending_eof && a->d.len == 0 && a->chain) + /* We don't need to keep this filter around at all: + + - we got an EOF + - we have no buffered data + - a filter follows this one. + + Unlink this filter. */ + { + iobuf_t b = a->chain; + if (DBG_IOBUF) + log_debug ("iobuf-%d.%d: pop in underflow (nothing buffered, got EOF)\n", + a->no, a->subno); + xfree (a->d.buf); + xfree (a->real_fname); + memcpy (a, b, sizeof *a); + xfree (b); + + print_chain (a); + + return -1; + } + else if (a->d.len == 0) + /* We can't unlink this filter (it is the only one in the + pipeline), but we can immediately return EOF. */ + return -1; + } + else if (rc) + /* Record the error. */ + { + a->error = rc; + + if (a->d.len == 0) + /* There is no buffered data. Immediately return EOF. */ + return -1; + } + } + + assert (a->d.start <= a->d.len); + if (a->d.start < a->d.len) + return a->d.buf[a->d.start++]; + + /* EOF. */ + return -1; +} + + +static int +filter_flush (iobuf_t a) +{ + size_t len; + int rc; + + if (a->use == IOBUF_OUTPUT_TEMP) + { /* increase the temp buffer */ + size_t newsize = a->d.size + IOBUF_BUFFER_SIZE; + + if (DBG_IOBUF) + log_debug ("increasing temp iobuf from %lu to %lu\n", + (ulong) a->d.size, (ulong) newsize); + + a->d.buf = xrealloc (a->d.buf, newsize); + a->d.size = newsize; + return 0; + } + else if (a->use != IOBUF_OUTPUT) + log_bug ("flush on non-output iobuf\n"); + else if (!a->filter) + log_bug ("filter_flush: no filter\n"); + len = a->d.len; + rc = a->filter (a->filter_ov, IOBUFCTRL_FLUSH, a->chain, a->d.buf, &len); + if (!rc && len != a->d.len) + { + log_info ("filter_flush did not write all!\n"); + rc = GPG_ERR_INTERNAL; + } + else if (rc) + a->error = rc; + a->d.len = 0; + + return rc; +} + + +int +iobuf_readbyte (iobuf_t a) +{ + int c; + + if (a->use == IOBUF_OUTPUT || a->use == IOBUF_OUTPUT_TEMP) + { + log_bug ("iobuf_readbyte called on a non-INPUT pipeline!\n"); + return -1; + } + + assert (a->d.start <= a->d.len); + + if (a->nlimit && a->nbytes >= a->nlimit) + return -1; /* forced EOF */ + + if (a->d.start < a->d.len) + { + c = a->d.buf[a->d.start++]; + } + else if ((c = underflow (a, 1)) == -1) + return -1; /* EOF */ + + assert (a->d.start <= a->d.len); + + /* Note: if underflow doesn't return EOF, then it returns the first + byte that was read and advances a->d.start appropriately. */ + + a->nbytes++; + return c; +} + + +int +iobuf_read (iobuf_t a, void *buffer, unsigned int buflen) +{ + unsigned char *buf = (unsigned char *)buffer; + int c, n; + + if (a->use == IOBUF_OUTPUT || a->use == IOBUF_OUTPUT_TEMP) + { + log_bug ("iobuf_read called on a non-INPUT pipeline!\n"); + return -1; + } + + if (a->nlimit) + { + /* Handle special cases. */ + for (n = 0; n < buflen; n++) + { + if ((c = iobuf_readbyte (a)) == -1) + { + if (!n) + return -1; /* eof */ + break; + } + + if (buf) + { + *buf = c; + buf++; + } + } + return n; + } + + n = 0; + do + { + if (n < buflen && a->d.start < a->d.len) + /* Drain the buffer. */ + { + unsigned size = a->d.len - a->d.start; + if (size > buflen - n) + size = buflen - n; + if (buf) + memcpy (buf, a->d.buf + a->d.start, size); + n += size; + a->d.start += size; + if (buf) + buf += size; + } + if (n < buflen) + /* Draining the internal buffer didn't fill BUFFER. Call + underflow to read more data into the filter's internal + buffer. */ + { + if ((c = underflow (a, 1)) == -1) + /* EOF. If we managed to read something, don't return EOF + now. */ + { + a->nbytes += n; + return n ? n : -1 /*EOF*/; + } + if (buf) + *buf++ = c; + n++; + } + } + while (n < buflen); + a->nbytes += n; + return n; +} + + + +int +iobuf_peek (iobuf_t a, byte * buf, unsigned buflen) +{ + int n = 0; + + assert (buflen > 0); + assert (a->use == IOBUF_INPUT || a->use == IOBUF_INPUT_TEMP); + + if (buflen > a->d.size) + /* We can't peek more than we can buffer. */ + buflen = a->d.size; + + /* Try to fill the internal buffer with enough data to satisfy the + request. */ + while (buflen > a->d.len - a->d.start) + { + if (underflow_target (a, 0, buflen) == -1) + /* EOF. We can't read any more. */ + break; + + /* Underflow consumes the first character (it's the return + value). unget() it by resetting the "file position". */ + assert (a->d.start == 1); + a->d.start = 0; + } + + n = a->d.len - a->d.start; + if (n > buflen) + n = buflen; + + if (n == 0) + /* EOF. */ + return -1; + + memcpy (buf, &a->d.buf[a->d.start], n); + + return n; +} + + + + +int +iobuf_writebyte (iobuf_t a, unsigned int c) +{ + int rc; + + if (a->use == IOBUF_INPUT || a->use == IOBUF_INPUT_TEMP) + { + log_bug ("iobuf_writebyte called on an input pipeline!\n"); + return -1; + } + + if (a->d.len == a->d.size) + if ((rc=filter_flush (a))) + return rc; + + assert (a->d.len < a->d.size); + a->d.buf[a->d.len++] = c; + return 0; +} + + +int +iobuf_write (iobuf_t a, const void *buffer, unsigned int buflen) +{ + const unsigned char *buf = (const unsigned char *)buffer; + int rc; + + if (a->use == IOBUF_INPUT || a->use == IOBUF_INPUT_TEMP) + { + log_bug ("iobuf_write called on an input pipeline!\n"); + return -1; + } + + do + { + if (buflen && a->d.len < a->d.size) + { + unsigned size = a->d.size - a->d.len; + if (size > buflen) + size = buflen; + memcpy (a->d.buf + a->d.len, buf, size); + buflen -= size; + buf += size; + a->d.len += size; + } + if (buflen) + { + rc = filter_flush (a); + if (rc) + return rc; + } + } + while (buflen); + return 0; +} + + +int +iobuf_writestr (iobuf_t a, const char *buf) +{ + if (a->use == IOBUF_INPUT || a->use == IOBUF_INPUT_TEMP) + { + log_bug ("iobuf_writestr called on an input pipeline!\n"); + return -1; + } + + return iobuf_write (a, buf, strlen (buf)); +} + + + +int +iobuf_write_temp (iobuf_t dest, iobuf_t source) +{ + assert (source->use == IOBUF_OUTPUT || source->use == IOBUF_OUTPUT_TEMP); + assert (dest->use == IOBUF_OUTPUT || dest->use == IOBUF_OUTPUT_TEMP); + + iobuf_flush_temp (source); + return iobuf_write (dest, source->d.buf, source->d.len); +} + +size_t +iobuf_temp_to_buffer (iobuf_t a, byte * buffer, size_t buflen) +{ + byte desc[MAX_IOBUF_DESC]; + size_t n; + + while (1) + { + int rc = filter_flush (a); + if (rc) + log_bug ("Flushing iobuf %d.%d (%s) from iobuf_temp_to_buffer failed. Ignoring.\n", + a->no, a->subno, iobuf_desc (a, desc)); + if (! a->chain) + break; + a = a->chain; + } + + n = a->d.len; + if (n > buflen) + n = buflen; + memcpy (buffer, a->d.buf, n); + return n; +} + +/* Copies the data from the input iobuf SOURCE to the output iobuf + DEST until either an error is encountered or EOF is reached. + Returns the number of bytes copies or (size_t)(-1) on error. */ +size_t +iobuf_copy (iobuf_t dest, iobuf_t source) +{ + char *temp; + /* Use a 32 KB buffer. */ + const size_t temp_size = 32 * 1024; + + size_t nread; + size_t nwrote = 0; + size_t max_read = 0; + int err; + + log_assert (source->use == IOBUF_INPUT || source->use == IOBUF_INPUT_TEMP); + log_assert (dest->use == IOBUF_OUTPUT || source->use == IOBUF_OUTPUT_TEMP); + + if (iobuf_error (dest)) + return (size_t)(-1); + + temp = xmalloc (temp_size); + while (1) + { + nread = iobuf_read (source, temp, temp_size); + if (nread == -1) + /* EOF. */ + break; + + if (nread > max_read) + max_read = nread; + + err = iobuf_write (dest, temp, nread); + if (err) + break; + nwrote += nread; + } + + /* Burn the buffer. */ + if (max_read) + wipememory (temp, max_read); + xfree (temp); + + return nwrote; +} + + +void +iobuf_flush_temp (iobuf_t temp) +{ + if (temp->use == IOBUF_INPUT || temp->use == IOBUF_INPUT_TEMP) + log_bug ("iobuf_flush_temp called on an input pipeline!\n"); + while (temp->chain) + iobuf_pop_filter (temp, temp->filter, NULL); +} + + +void +iobuf_set_limit (iobuf_t a, off_t nlimit) +{ + if (nlimit) + a->nofast = 1; + else + a->nofast = 0; + a->nlimit = nlimit; + a->ntotal += a->nbytes; + a->nbytes = 0; +} + + + +off_t +iobuf_get_filelength (iobuf_t a, int *overflow) +{ + if (overflow) + *overflow = 0; + + /* Hmmm: file_filter may have already been removed */ + for ( ; a->chain; a = a->chain ) + ; + + if (a->filter != file_filter) + return 0; + + { + file_filter_ctx_t *b = a->filter_ov; + gnupg_fd_t fp = b->fp; + +#if defined(HAVE_W32_SYSTEM) + ulong size; + static int (* __stdcall get_file_size_ex) (void *handle, + LARGE_INTEGER *r_size); + static int get_file_size_ex_initialized; + + if (!get_file_size_ex_initialized) + { + void *handle; + + handle = dlopen ("kernel32.dll", RTLD_LAZY); + if (handle) + { + get_file_size_ex = dlsym (handle, "GetFileSizeEx"); + if (!get_file_size_ex) + dlclose (handle); + } + get_file_size_ex_initialized = 1; + } + + if (get_file_size_ex) + { + /* This is a newer system with GetFileSizeEx; we use this + then because it seem that GetFileSize won't return a + proper error in case a file is larger than 4GB. */ + LARGE_INTEGER exsize; + + if (get_file_size_ex (fp, &exsize)) + { + if (!exsize.u.HighPart) + return exsize.u.LowPart; + if (overflow) + *overflow = 1; + return 0; + } + } + else + { + if ((size=GetFileSize (fp, NULL)) != 0xffffffff) + return size; + } + log_error ("GetFileSize for handle %p failed: %s\n", + fp, w32_strerror (-1)); +#else /*!HAVE_W32_SYSTEM*/ + { + struct stat st; + + if ( !fstat (FD2INT (fp), &st) ) + return st.st_size; + log_error("fstat() failed: %s\n", strerror(errno) ); + } +#endif /*!HAVE_W32_SYSTEM*/ + } + + return 0; +} + + +int +iobuf_get_fd (iobuf_t a) +{ + for (; a->chain; a = a->chain) + ; + + if (a->filter != file_filter) + return -1; + + { + file_filter_ctx_t *b = a->filter_ov; + gnupg_fd_t fp = b->fp; + + return FD2INT (fp); + } +} + + +off_t +iobuf_tell (iobuf_t a) +{ + return a->ntotal + a->nbytes; +} + + +#if !defined(HAVE_FSEEKO) && !defined(fseeko) + +#ifdef HAVE_LIMITS_H +# include <limits.h> +#endif +#ifndef LONG_MAX +# define LONG_MAX ((long) ((unsigned long) -1 >> 1)) +#endif +#ifndef LONG_MIN +# define LONG_MIN (-1 - LONG_MAX) +#endif + +/**************** + * A substitute for fseeko, for hosts that don't have it. + */ +static int +fseeko (FILE * stream, off_t newpos, int whence) +{ + while (newpos != (long) newpos) + { + long pos = newpos < 0 ? LONG_MIN : LONG_MAX; + if (fseek (stream, pos, whence) != 0) + return -1; + newpos -= pos; + whence = SEEK_CUR; + } + return fseek (stream, (long) newpos, whence); +} +#endif + +int +iobuf_seek (iobuf_t a, off_t newpos) +{ + file_filter_ctx_t *b = NULL; + + if (a->use == IOBUF_OUTPUT || a->use == IOBUF_INPUT) + { + /* Find the last filter in the pipeline. */ + for (; a->chain; a = a->chain) + ; + + if (a->filter != file_filter) + return -1; + + b = a->filter_ov; + +#ifdef HAVE_W32_SYSTEM + if (SetFilePointer (b->fp, newpos, NULL, FILE_BEGIN) == 0xffffffff) + { + log_error ("SetFilePointer failed on handle %p: ec=%d\n", + b->fp, (int) GetLastError ()); + return -1; + } +#else + if (lseek (b->fp, newpos, SEEK_SET) == (off_t) - 1) + { + log_error ("can't lseek: %s\n", strerror (errno)); + return -1; + } +#endif + /* Discard the buffer it is not a temp stream. */ + a->d.len = 0; + } + a->d.start = 0; + a->nbytes = 0; + a->nlimit = 0; + a->nofast = 0; + a->ntotal = newpos; + a->error = 0; + + /* It is impossible for A->CHAIN to be non-NULL. If A is an INPUT + or OUTPUT buffer, then we find the last filter, which is defined + as A->CHAIN being NULL. If A is a TEMP filter, then A must be + the only filter in the pipe: when iobuf_push_filter adds a filter + to the front of a pipeline, it sets the new filter to be an + OUTPUT filter if the pipeline is an OUTPUT or TEMP pipeline and + to be an INPUT filter if the pipeline is an INPUT pipeline. + Thus, only the last filter in a TEMP pipeline can be a */ + + /* remove filters, but the last */ + if (a->chain) + log_debug ("iobuf_pop_filter called in iobuf_seek - please report\n"); + while (a->chain) + iobuf_pop_filter (a, a->filter, NULL); + + return 0; +} + + +const char * +iobuf_get_real_fname (iobuf_t a) +{ + if (a->real_fname) + return a->real_fname; + + /* the old solution */ + for (; a; a = a->chain) + if (!a->chain && a->filter == file_filter) + { + file_filter_ctx_t *b = a->filter_ov; + return b->print_only_name ? NULL : b->fname; + } + + return NULL; +} + +const char * +iobuf_get_fname (iobuf_t a) +{ + for (; a; a = a->chain) + if (!a->chain && a->filter == file_filter) + { + file_filter_ctx_t *b = a->filter_ov; + return b->fname; + } + return NULL; +} + +const char * +iobuf_get_fname_nonnull (iobuf_t a) +{ + const char *fname; + + fname = iobuf_get_fname (a); + return fname? fname : "[?]"; +} + + +/**************** + * Enable or disable partial body length mode (RFC 4880 4.2.2.4). + * + * If LEN is 0, this disables partial block mode by popping the + * partial body length filter, which must be the most recently + * added filter. + * + * If LEN is non-zero, it pushes a partial body length filter. If + * this is a read filter, LEN must be the length byte from the first + * chunk and A should be position just after this first partial body + * length header. + */ +void +iobuf_set_partial_body_length_mode (iobuf_t a, size_t len) +{ + if (!len) + /* Disable partial body length mode. */ + { + if (a->use == IOBUF_INPUT) + log_debug ("iobuf_pop_filter called in set_partial_block_mode" + " - please report\n"); + + log_assert (a->filter == block_filter); + iobuf_pop_filter (a, block_filter, NULL); + } + else + /* Enabled partial body length mode. */ + { + block_filter_ctx_t *ctx = xcalloc (1, sizeof *ctx); + ctx->use = a->use; + ctx->partial = 1; + ctx->size = 0; + ctx->first_c = len; + iobuf_push_filter (a, block_filter, ctx); + } +} + + + +unsigned int +iobuf_read_line (iobuf_t a, byte ** addr_of_buffer, + unsigned *length_of_buffer, unsigned *max_length) +{ + int c; + char *buffer = (char *)*addr_of_buffer; + unsigned length = *length_of_buffer; + unsigned nbytes = 0; + unsigned maxlen = *max_length; + char *p; + + /* The code assumes that we have space for at least a newline and a + NUL character in the buffer. This requires at least 2 bytes. We + don't complicate the code by handling the stupid corner case, but + simply assert that it can't happen. */ + assert (!buffer || length >= 2 || maxlen >= 2); + + if (!buffer || length <= 1) + /* must allocate a new buffer */ + { + length = 256 <= maxlen ? 256 : maxlen; + buffer = xrealloc (buffer, length); + *addr_of_buffer = (unsigned char *)buffer; + *length_of_buffer = length; + } + + p = buffer; + while ((c = iobuf_get (a)) != -1) + { + *p++ = c; + nbytes++; + if (c == '\n') + break; + + if (nbytes == length - 1) + /* We don't have enough space to add a \n and a \0. Increase + the buffer size. */ + { + if (length == maxlen) + /* We reached the buffer's size limit! */ + { + /* Skip the rest of the line. */ + while (c != '\n' && (c = iobuf_get (a)) != -1) + ; + + /* p is pointing at the last byte in the buffer. We + always terminate the line with "\n\0" so overwrite + the previous byte with a \n. */ + assert (p > buffer); + p[-1] = '\n'; + + /* Indicate truncation. */ + *max_length = 0; + break; + } + + length += length < 1024 ? 256 : 1024; + if (length > maxlen) + length = maxlen; + + buffer = xrealloc (buffer, length); + *addr_of_buffer = (unsigned char *)buffer; + *length_of_buffer = length; + p = buffer + nbytes; + } + } + /* Add the terminating NUL. */ + *p = 0; + + /* Return the number of characters written to the buffer including + the newline, but not including the terminating NUL. */ + return nbytes; +} + +static int +translate_file_handle (int fd, int for_write) +{ +#if defined(HAVE_W32CE_SYSTEM) + /* This is called only with one of the special filenames. Under + W32CE the FD here is not a file descriptor but a rendezvous id, + thus we need to finish the pipe first. */ + fd = _assuan_w32ce_finish_pipe (fd, for_write); +#elif defined(HAVE_W32_SYSTEM) + { + int x; + + (void)for_write; + + if (fd == 0) + x = (int) GetStdHandle (STD_INPUT_HANDLE); + else if (fd == 1) + x = (int) GetStdHandle (STD_OUTPUT_HANDLE); + else if (fd == 2) + x = (int) GetStdHandle (STD_ERROR_HANDLE); + else + x = fd; + + if (x == -1) + log_debug ("GetStdHandle(%d) failed: ec=%d\n", + fd, (int) GetLastError ()); + + fd = x; + } +#else + (void)for_write; +#endif + return fd; +} + + +void +iobuf_skip_rest (iobuf_t a, unsigned long n, int partial) +{ + if ( partial ) + { + for (;;) + { + if (a->nofast || a->d.start >= a->d.len) + { + if (iobuf_readbyte (a) == -1) + { + break; + } + } + else + { + unsigned long count = a->d.len - a->d.start; + a->nbytes += count; + a->d.start = a->d.len; + } + } + } + else + { + unsigned long remaining = n; + while (remaining > 0) + { + if (a->nofast || a->d.start >= a->d.len) + { + if (iobuf_readbyte (a) == -1) + { + break; + } + --remaining; + } + else + { + unsigned long count = a->d.len - a->d.start; + if (count > remaining) + { + count = remaining; + } + a->nbytes += count; + a->d.start += count; + remaining -= count; + } + } + } +} diff --git a/common/iobuf.h b/common/iobuf.h new file mode 100644 index 0000000..624e154 --- /dev/null +++ b/common/iobuf.h @@ -0,0 +1,615 @@ +/* iobuf.h - I/O buffer + * Copyright (C) 1998, 1999, 2000, 2001, 2003, + * 2010 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef GNUPG_COMMON_IOBUF_H +#define GNUPG_COMMON_IOBUF_H + +/* An iobuf is basically a filter in a pipeline. + + Consider the following command, which consists of three filters + that are chained together: + + $ cat file | base64 --decode | gunzip + + The first filter reads the file from the file system and sends that + data to the second filter. The second filter decodes + base64-encoded data and sends the data to the third and last + filter. The last filter decompresses the data and the result is + displayed on the terminal. The iobuf system works in the same way + where each iobuf is a filter and the individual iobufs can be + chained together. + + There are number of predefined filters. iobuf_open(), for + instance, creates a filter that reads from a specified file. And, + iobuf_temp_with_content() creates a filter that returns some + specified contents. There are also filters for writing content. + iobuf_openrw opens a file for writing. iobuf_temp creates a filter + that writes data to a fixed-sized buffer. + + To chain filters together, you use the iobuf_push_filter() + function. The filters are chained together using the chain field + in the iobuf_t. + + A pipeline can only be used for reading (IOBUF_INPUT) or for + writing (IOBUF_OUTPUT / IOBUF_OUTPUT_TEMP). When reading, data + flows from the last filter towards the first. That is, the user + calls iobuf_read(), the module reads from the first filter, which + gets its input from the second filter, etc. When writing, data + flows from the first filter towards the last. In this case, when + the user calls iobuf_write(), the data is written to the first + filter, which writes the transformed data to the second filter, + etc. + + An iobuf_t contains some state about the filter. For instance, it + indicates if the filter has already returned EOF (filter_eof) and + the next filter in the pipeline, if any (chain). It also contains + a function pointer, filter. This is a generic function. It is + called when input is needed or output is available. In this case + it is passed a pointer to some filter-specific persistent state + (filter_ov), the actual operation, the next filter in the chain, if + any, and a buffer that either contains the contents to write, if + the pipeline is setup to write data, or is the place to store data, + if the pipeline is setup to read data. + + + Unlike a Unix pipeline, an IOBUF pipeline can return EOF multiple + times. This is similar to the following: + + { cat file1; cat file2; } | grep foo + + However, instead of grep seeing a single stream, grep would see + each byte stream followed by an EOF marker. (When a filter returns + EOF, the EOF is returned to the user exactly once and then the + filter is removed from the pipeline.) */ + +/* For estream_t. */ +#include <gpg-error.h> + +#include "../common/types.h" +#include "../common/sysutils.h" + +#define DBG_IOBUF iobuf_debug_mode + +/* Filter control modes. */ +enum + { + IOBUFCTRL_INIT = 1, + IOBUFCTRL_FREE = 2, + IOBUFCTRL_UNDERFLOW = 3, + IOBUFCTRL_FLUSH = 4, + IOBUFCTRL_DESC = 5, + IOBUFCTRL_CANCEL = 6, + IOBUFCTRL_USER = 16 + }; + + +/* Command codes for iobuf_ioctl. */ +typedef enum + { + IOBUF_IOCTL_KEEP_OPEN = 1, /* Uses intval. */ + IOBUF_IOCTL_INVALIDATE_CACHE = 2, /* Uses ptrval. */ + IOBUF_IOCTL_NO_CACHE = 3, /* Uses intval. */ + IOBUF_IOCTL_FSYNC = 4 /* Uses ptrval. */ + } iobuf_ioctl_t; + +enum iobuf_use + { + /* Pipeline is in input mode. The data flows from the end to the + beginning. That is, when reading from the pipeline, the first + filter gets its input from the second filter, etc. */ + IOBUF_INPUT, + /* Pipeline is in input mode. The last filter in the pipeline is + a temporary buffer from which the data is "read". */ + IOBUF_INPUT_TEMP, + /* Pipeline is in output mode. The data flows from the beginning + to the end. That is, when writing to the pipeline, the user + writes to the first filter, which transforms the data and sends + it to the second filter, etc. */ + IOBUF_OUTPUT, + /* Pipeline is in output mode. The last filter in the pipeline is + a temporary buffer that grows as necessary. */ + IOBUF_OUTPUT_TEMP + }; + + +typedef struct iobuf_struct *iobuf_t; +typedef struct iobuf_struct *IOBUF; /* Compatibility with gpg 1.4. */ + +/* fixme: we should hide most of this stuff */ +struct iobuf_struct +{ + /* The type of filter. Either IOBUF_INPUT, IOBUF_OUTPUT or + IOBUF_OUTPUT_TEMP. */ + enum iobuf_use use; + + /* nlimit can be changed using iobuf_set_limit. If non-zero, it is + the number of additional bytes that can be read from the filter + before EOF is forcefully returned. */ + off_t nlimit; + /* nbytes if the number of bytes that have been read (using + iobuf_get / iobuf_readbyte / iobuf_read) since the last call to + iobuf_set_limit. */ + off_t nbytes; + + /* The number of bytes read prior to the last call to + iobuf_set_limit. Thus, the total bytes read (i.e., the position + of stream) is ntotal + nbytes. */ + off_t ntotal; + + /* Whether we need to read from the filter one byte at a time or + whether we can do bulk reads. We need to read one byte at a time + if a limit (set via iobuf_set_limit) is active. */ + int nofast; + + /* A buffer for unread/unwritten data. + + For an output pipeline (IOBUF_OUTPUT), this is the data that has + not yet been written to the filter. Consider a simple pipeline + consisting of a single stage, which writes to a file. When you + write to the pipeline (iobuf_writebyte or iobuf_write), the data + is first stored in this buffer. Only when the buffer is full or + you call iobuf_flush() is FILTER actually called and the data + written to the file. + + For an input pipeline (IOBUF_INPUT), this is the data that has + been read from this filter, but not yet been read from the + preceding filter (or the user, if this filter is the head of the + pipeline). Again, consider a simple pipeline consisting of a + single stage. This stage reads from a file. If you read a + single byte (iobuf_get) and the buffer is empty, then FILTER is + called to fill the buffer. In this case, a single byte is not + requested, but the whole buffer is filled (if possible). */ + struct + { + /* Size of the buffer. */ + size_t size; + /* Number of bytes at the beginning of the buffer that have + already been consumed. (In other words: the index of the first + byte that hasn't been consumed.) This is only non-zero for + input filters. */ + size_t start; + /* The number of bytes in the buffer including any bytes that have + been consumed. */ + size_t len; + /* The buffer itself. */ + byte *buf; + } d; + + /* When FILTER is called to read some data, it may read some data + and then return EOF. We can't return the EOF immediately. + Instead, we note that we observed the EOF and when the buffer is + finally empty, we return the EOF. */ + int filter_eof; + /* Like filter_eof, when FILTER is called to read some data, it may + read some data and then return an error. We can't return the + error (in the form of an EOF) immediately. Instead, we note that + we observed the error and when the buffer is finally empty, we + return the EOF. */ + int error; + + /* The callback function to read data from the filter, etc. See + iobuf_filter_push for details. */ + int (*filter) (void *opaque, int control, + iobuf_t chain, byte * buf, size_t * len); + /* An opaque pointer that can be used for local filter state. This + is passed as the first parameter to FILTER. */ + void *filter_ov; + /* Whether the iobuf code should free(filter_ov) when destroying the + filter. */ + int filter_ov_owner; + + /* When using iobuf_open, iobuf_create, iobuf_openrw to open a file, + the file's name is saved here. This is used to delete the file + when an output pipeline (IOBUF_OUPUT) is canceled + (iobuf_cancel). */ + char *real_fname; + + /* The next filter in the pipeline. */ + iobuf_t chain; + + /* This field is for debugging. Each time a filter is allocated + (via iobuf_alloc()), a monotonically increasing counter is + incremented and this field is set to the new value. This field + should only be accessed via the iobuf_io macro. */ + int no; + + /* The number of filters in the pipeline following (not including) + this one. When you call iobuf_push_filter or iobuf_push_filter2, + this value is used to check the length of the pipeline if the + pipeline already contains 65 stages then these functions fail. + This amount of nesting typically indicates corrupted data or an + active denial of service attack. */ + int subno; +}; + +extern int iobuf_debug_mode; + + +/* Returns whether the specified filename corresponds to a pipe. In + particular, this function checks if FNAME is "-" and, if special + filenames are enabled (see check_special_filename), whether + FNAME is a special filename. */ +int iobuf_is_pipe_filename (const char *fname); + +/* Allocate a new filter. This filter doesn't have a function + assigned to it. Thus you need to manually set IOBUF->FILTER and + IOBUF->FILTER_OV, if required. This function is intended to help + create a new primary source or primary sink, i.e., the last filter + in the pipeline. + + USE is IOBUF_INPUT, IOBUF_INPUT_TEMP, IOBUF_OUTPUT or + IOBUF_OUTPUT_TEMP. + + BUFSIZE is the desired internal buffer size (that is, the size of + the typical read / write request). */ +iobuf_t iobuf_alloc (int use, size_t bufsize); + +/* Create an output filter that simply buffers data written to it. + This is useful for collecting data for later processing. The + buffer can be written to in the usual way (iobuf_write, etc.). The + data can later be extracted using iobuf_write_temp() or + iobuf_temp_to_buffer(). */ +iobuf_t iobuf_temp (void); + +/* Create an input filter that contains some data for reading. */ +iobuf_t iobuf_temp_with_content (const char *buffer, size_t length); + +/* Create an input file filter that reads from a file. If FNAME is + '-', reads from stdin. If special filenames are enabled + (iobuf_enable_special_filenames), then interprets special + filenames. */ +iobuf_t iobuf_open (const char *fname); + +/* Create an output file filter that writes to a file. If FNAME is + NULL or '-', writes to stdout. If special filenames are enabled + (iobuf_enable_special_filenames), then interprets special + filenames. If FNAME is not NULL, '-' or a special filename, the + file is opened for writing. If the file exists, it is truncated. + If MODE700 is TRUE, the file is created with mode 600. Otherwise, + mode 666 is used. */ +iobuf_t iobuf_create (const char *fname, int mode700); + +/* Create an output file filter that writes to a specified file. + Neither '-' nor special file names are recognized. */ +iobuf_t iobuf_openrw (const char *fname); + +/* Create a file filter using an existing file descriptor. If MODE + contains the letter 'w', creates an output filter. Otherwise, + creates an input filter. Note: MODE must reflect the file + descriptors actual mode! When the filter is destroyed, the file + descriptor is closed. */ +iobuf_t iobuf_fdopen (int fd, const char *mode); + +/* Like iobuf_fdopen, but doesn't close the file descriptor when the + filter is destroyed. */ +iobuf_t iobuf_fdopen_nc (int fd, const char *mode); + +/* Create a filter using an existing estream. If MODE contains the + letter 'w', creates an output filter. Otherwise, creates an input + filter. If KEEP_OPEN is TRUE, then the stream is not closed when + the filter is destroyed. Otherwise, the stream is closed when the + filter is destroyed. */ +iobuf_t iobuf_esopen (estream_t estream, const char *mode, int keep_open); + +/* Create a filter using an existing socket. On Windows creates a + special socket filter. On non-Windows systems simply, this simply + calls iobuf_fdopen. */ +iobuf_t iobuf_sockopen (int fd, const char *mode); + +/* Set various options / perform different actions on a PIPELINE. See + the IOBUF_IOCTL_* macros above. */ +int iobuf_ioctl (iobuf_t a, iobuf_ioctl_t cmd, int intval, void *ptrval); + +/* Close a pipeline. The filters in the pipeline are first flushed + using iobuf_flush, if they are output filters, and then + IOBUFCTRL_FREE is called on each filter. + + If any filter returns a non-zero value in response to the + IOBUFCTRL_FREE, that first such non-zero value is returned. Note: + processing is not aborted in this case. If all filters are freed + successfully, 0 is returned. */ +int iobuf_close (iobuf_t iobuf); + +/* Calls IOBUFCTRL_CANCEL on each filter in the pipeline. Then calls + io_close() on the pipeline. Finally, if the pipeline is an output + pipeline, deletes the file. Returns the result of calling + iobuf_close on the pipeline. */ +int iobuf_cancel (iobuf_t iobuf); + +/* Add a new filter to the front of a pipeline. A is the head of the + pipeline. F is the filter implementation. OV is an opaque pointer + that is passed to F and is normally used to hold any internal + state, such as a file pointer. + + Note: you may only maintain a reference to an iobuf_t as a + reference to the head of the pipeline. That is, don't think about + setting a pointer in OV to point to the filter's iobuf_t. This is + because when we add a new filter to a pipeline, we memcpy the state + in A into new buffer. This has the advantage that there is no need + to update any references to the pipeline when a filter is added or + removed, but it also means that a filter's state moves around in + memory. + + The behavior of the filter function is determined by the value of + the control parameter: + + IOBUFCTRL_INIT: Called this value just before the filter is + linked into the pipeline. This can be used to initialize + internal data structures. + + IOBUFCTRL_FREE: Called with this value just before the filter is + removed from the pipeline. Normally used to release internal + data structures, close a file handle, etc. + + IOBUFCTRL_UNDERFLOW: Called with this value to fill the passed + buffer with more data. *LEN is the size of the buffer. Before + returning, it should be set to the number of bytes which were + written into the buffer. The function must return 0 to + indicate success, -1 on EOF and a GPG_ERR_xxxxx code for any + error. + + Note: this function may both return data and indicate an error + or EOF. In this case, it simply writes the data to BUF, sets + *LEN and returns the appropriate return code. The implication + is that if an error occurs and no data has yet been written, it + is essential that *LEN be set to 0! + + IOBUFCTRL_FLUSH: Called with this value to write out any + collected data. *LEN is the number of bytes in BUF that need + to be written out. Returns 0 on success and a GPG_ERR_* code + otherwise. *LEN must be set to the number of bytes that were + written out. + + IOBUFCTRL_CANCEL: Called with this value when iobuf_cancel() is + called on the pipeline. + + IOBUFCTRL_DESC: Called with this value to get a human-readable + description of the filter. *LEN is the size of the buffer. + The description is filled into BUF, NUL-terminated. Always + returns 0. + */ +int iobuf_push_filter (iobuf_t a, int (*f) (void *opaque, int control, + iobuf_t chain, byte * buf, + size_t * len), void *ov); +/* This variant of iobuf_push_filter allows the called to indicate + that OV should be freed when this filter is freed. That is, if + REL_OV is TRUE, then when the filter is popped or freed OV will be + freed after the filter function is called with control set to + IOBUFCTRL_FREE. */ +int iobuf_push_filter2 (iobuf_t a, + int (*f) (void *opaque, int control, iobuf_t chain, + byte * buf, size_t * len), void *ov, + int rel_ov); + +/* Pop the top filter. The top filter must have the filter function F + and the cookie OV. The cookie check is ignored if OV is NULL. */ +int iobuf_pop_filter (iobuf_t a, + int (*f) (void *opaque, int control, + iobuf_t chain, byte * buf, size_t * len), + void *ov); + +/* Used for debugging. Prints out the chain using log_debug if + IOBUF_DEBUG_MODE is not 0. */ +int iobuf_print_chain (iobuf_t a); + +/* Indicate that some error occurred on the specified filter. */ +#define iobuf_set_error(a) do { (a)->error = 1; } while(0) + +/* Return any pending error on filter A. */ +#define iobuf_error(a) ((a)->error) + +/* Limit the amount of additional data that may be read from the + filter. That is, if you've already read 100 bytes from A and you + set the limit to 50, then you can read up to an additional 50 bytes + (i.e., a total of 150 bytes) before EOF is forcefully returned. + Setting NLIMIT to 0 removes any active limit. + + Note: using iobuf_seek removes any currently enforced limit! */ +void iobuf_set_limit (iobuf_t a, off_t nlimit); + +/* Returns the number of bytes that have been read from the pipeline. + Note: the result is undefined for IOBUF_OUTPUT and IOBUF_OUTPUT_TEMP + pipelines! */ +off_t iobuf_tell (iobuf_t a); + +/* There are two cases: + + - If A is an INPUT or OUTPUT pipeline, then the last filter in the + pipeline is found. If that is not a file filter, -1 is returned. + Otherwise, an fseek(..., SEEK_SET) is performed on the file + descriptor. + + - If A is a TEMP pipeline and the *first* (and thus only filter) is + a TEMP filter, then the "file position" is effectively unchanged. + That is, data is appended to the buffer and the seek does not + cause the size of the buffer to grow. + + If no error occurred, then any limit previous set by + iobuf_set_limit() is cleared. Further, any error on the filter + (the file filter or the temp filter) is cleared. + + Returns 0 on success and -1 if an error occurs. */ +int iobuf_seek (iobuf_t a, off_t newpos); + +/* Read a single byte. If a filter has no more data, returns -1 to + indicate the EOF. Generally, you don't want to use this function, + but instead prefer the iobuf_get macro, which is faster if there is + data in the internal buffer. */ +int iobuf_readbyte (iobuf_t a); + +/* Get a byte from the iobuf; must check for eof prior to this + function. This function returns values in the range 0 .. 255 or -1 + to indicate EOF. iobuf_get_noeof() does not return -1 to indicate + EOF, but masks the returned value to be in the range 0 .. 255. */ +#define iobuf_get(a) \ + ( ((a)->nofast || (a)->d.start >= (a)->d.len )? \ + iobuf_readbyte((a)) : ( (a)->nbytes++, (a)->d.buf[(a)->d.start++] ) ) +#define iobuf_get_noeof(a) (iobuf_get((a))&0xff) + +/* Fill BUF with up to BUFLEN bytes. If a filter has no more data, + returns -1 to indicate the EOF. Otherwise returns the number of + bytes read. */ +int iobuf_read (iobuf_t a, void *buf, unsigned buflen); + +/* Read a line of input (including the '\n') from the pipeline. + + The semantics are the same as for fgets(), but if the buffer is too + short a larger one will be allocated up to *MAX_LENGTH and the end + of the line except the trailing '\n' discarded. (Thus, + *ADDR_OF_BUFFER must be allocated using malloc().) If the buffer + is enlarged, then *LENGTH_OF_BUFFER will be updated to reflect the + new size. If the line is truncated, then *MAX_LENGTH will be set + to 0. If *ADDR_OF_BUFFER is NULL, a buffer is allocated using + malloc(). + + A line is considered a byte stream ending in a '\n'. Returns the + number of characters written to the buffer (i.e., excluding any + discarded characters due to truncation). Thus, use this instead of + strlen(buffer) to determine the length of the string as this is + unreliable if the input contains NUL characters. + + EOF is indicated by a line of length zero. + + The last LF may be missing due to an EOF. */ +unsigned iobuf_read_line (iobuf_t a, byte ** addr_of_buffer, + unsigned *length_of_buffer, unsigned *max_length); + +/* Read up to BUFLEN bytes from pipeline A. Note: this function can't + return more than the pipeline's internal buffer size. The return + value is the number of bytes actually written to BUF. If the + filter returns EOF, then this function returns -1. + + This function does not clear any pending EOF. That is, if the + pipeline consists of two filters and the first one returns EOF + during the peek, then the subsequent iobuf_read* will still return + EOF before returning the data from the second filter. */ +int iobuf_peek (iobuf_t a, byte * buf, unsigned buflen); + +/* Write a byte to the pipeline. Returns 0 on success and an error + code otherwise. */ +int iobuf_writebyte (iobuf_t a, unsigned c); + +/* Alias for iobuf_writebyte. */ +#define iobuf_put(a,c) iobuf_writebyte(a,c) + +/* Write a sequence of bytes to the pipeline. Returns 0 on success + and an error code otherwise. */ +int iobuf_write (iobuf_t a, const void *buf, unsigned buflen); + +/* Write a string (not including the NUL terminator) to the pipeline. + Returns 0 on success and an error code otherwise. */ +int iobuf_writestr (iobuf_t a, const char *buf); + +/* Flushes the pipeline removing all filters but the sink (the last + filter) in the process. */ +void iobuf_flush_temp (iobuf_t temp); + +/* Flushes the pipeline SOURCE removing all filters but the sink (the + last filter) in the process (i.e., it calls + iobuf_flush_temp(source)) and then writes the data to the pipeline + DEST. Note: this doesn't free (iobuf_close()) SOURCE. Both SOURCE + and DEST must be output pipelines. */ +int iobuf_write_temp (iobuf_t dest, iobuf_t source); + +/* Flushes each filter in the pipeline (i.e., sends any buffered data + to the filter by calling IOBUFCTRL_FLUSH). Then, copies up to the + first BUFLEN bytes from the last filter's internal buffer (which + will only be non-empty if it is a temp filter) to the buffer + BUFFER. Returns the number of bytes actually copied. */ +size_t iobuf_temp_to_buffer (iobuf_t a, byte * buffer, size_t buflen); + +/* Copies the data from the input iobuf SOURCE to the output iobuf + DEST until either an error is encountered or EOF is reached. + Returns the number of bytes successfully written. If an error + occurred, then any buffered bytes are not returned to SOURCE and are + effectively lost. To check if an error occurred, use + iobuf_error. */ +size_t iobuf_copy (iobuf_t dest, iobuf_t source); + +/* Return the size of any underlying file. This only works with + file_filter based pipelines. + + On Win32, it is sometimes not possible to determine the size of + files larger than 4GB. In this case, *OVERFLOW (if not NULL) is + set to 1. Otherwise, *OVERFLOW is set to 0. */ +off_t iobuf_get_filelength (iobuf_t a, int *overflow); +#define IOBUF_FILELENGTH_LIMIT 0xffffffff + +/* Return the file descriptor designating the underlying file. This + only works with file_filter based pipelines. */ +int iobuf_get_fd (iobuf_t a); + +/* Return the real filename, if available. This only supports + pipelines that end in file filters. Returns NULL if not + available. */ +const char *iobuf_get_real_fname (iobuf_t a); + +/* Return the filename or a description thereof. For instance, for + iobuf_open("-"), this will return "[stdin]". This only supports + pipelines that end in file filters. Returns NULL if not + available. */ +const char *iobuf_get_fname (iobuf_t a); + +/* Like iobuf_getfname, but instead of returning NULL if no + description is available, return "[?]". */ +const char *iobuf_get_fname_nonnull (iobuf_t a); + +/* Pushes a filter on the pipeline that interprets the datastream as + an OpenPGP data block whose length is encoded using partial body + length headers (see Section 4.2.2.4 of RFC 4880). Concretely, it + just returns / writes the data and finishes the packet with an + EOF. */ +void iobuf_set_partial_body_length_mode (iobuf_t a, size_t len); + +/* If PARTIAL is set, then read from the pipeline until the first EOF + is returned. + + If PARTIAL is 0, then read up to N bytes or until the first EOF is + returned. + + Recall: a filter can return EOF. In this case, it and all + preceding filters are popped from the pipeline and the next read is + from the following filter (which may or may not return EOF). */ +void iobuf_skip_rest (iobuf_t a, unsigned long n, int partial); + +#define iobuf_where(a) "[don't know]" + +/* Each time a filter is allocated (via iobuf_alloc()), a + monotonically increasing counter is incremented and this field is + set to the new value. This macro returns that number. */ +#define iobuf_id(a) ((a)->no) + +#define iobuf_get_temp_buffer(a) ( (a)->d.buf ) +#define iobuf_get_temp_length(a) ( (a)->d.len ) + +/* Whether the filter uses an in-memory buffer. */ +#define iobuf_is_temp(a) ( (a)->use == IOBUF_OUTPUT_TEMP ) + +#endif /*GNUPG_COMMON_IOBUF_H*/ diff --git a/common/ksba-io-support.c b/common/ksba-io-support.c new file mode 100644 index 0000000..2832a4f --- /dev/null +++ b/common/ksba-io-support.c @@ -0,0 +1,762 @@ +/* kska-io-support.c - Supporting functions for ksba reader and writer + * Copyright (C) 2001-2005, 2007, 2010-2011, 2017 Werner Koch + * Copyright (C) 2006 g10 Code GmbH + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <time.h> +#include <assert.h> +#include <ksba.h> + +#include "util.h" +#include "i18n.h" +#include "ksba-io-support.h" + + +#ifdef HAVE_DOSISH_SYSTEM + #define LF "\r\n" +#else + #define LF "\n" +#endif + + +/* Data used by the reader callbacks. */ +struct reader_cb_parm_s +{ + estream_t fp; + + unsigned char line[1024]; + int linelen; + int readpos; + int have_lf; + unsigned long line_counter; + + int allow_multi_pem; /* Allow processing of multiple PEM objects. */ + int autodetect; /* Try to detect the input encoding. */ + int assume_pem; /* Assume input encoding is PEM. */ + int assume_base64; /* Assume input is base64 encoded. */ + + int identified; + int is_pem; + int is_base64; + int stop_seen; + int might_be_smime; + + int eof_seen; + + struct { + int idx; + unsigned char val; + int stop_seen; + } base64; +}; + + +/* Data used by the writer callbacks. */ +struct writer_cb_parm_s +{ + estream_t stream; /* Output stream. */ + + char *pem_name; /* Malloced. */ + + int wrote_begin; + int did_finish; + + struct { + int idx; + int quad_count; + unsigned char radbuf[4]; + } base64; + +}; + + +/* Context for this module's functions. */ +struct gnupg_ksba_io_s { + union { + struct reader_cb_parm_s rparm; + struct writer_cb_parm_s wparm; + } u; + + union { + ksba_reader_t reader; + ksba_writer_t writer; + } u2; +}; + + +/* The base-64 character list */ +static char bintoasc[64] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; +/* The reverse base-64 list */ +static unsigned char asctobin[256] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f, + 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, + 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, + 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, + 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, + 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff +}; + + +static int +has_only_base64 (const unsigned char *line, int linelen) +{ + if (linelen < 20) + return 0; + for (; linelen; line++, linelen--) + { + if (*line == '\n' || (linelen > 1 && *line == '\r' && line[1] == '\n')) + break; + if ( !strchr (bintoasc, *line) ) + return 0; + } + return 1; /* yes */ +} + +static int +is_empty_line (const unsigned char *line, int linelen) +{ + if (linelen >= 2 && *line == '\r' && line[1] == '\n') + return 1; + if (linelen >= 1 && *line == '\n') + return 1; + return 0; +} + + +static int +base64_reader_cb (void *cb_value, char *buffer, size_t count, size_t *nread) +{ + struct reader_cb_parm_s *parm = cb_value; + size_t n; + int c, c2; + + *nread = 0; + if (!buffer) + return -1; /* not supported */ + + next: + if (!parm->linelen) + { + /* read an entire line or up to the size of the buffer */ + parm->line_counter++; + parm->have_lf = 0; + for (n=0; n < DIM(parm->line);) + { + c = es_getc (parm->fp); + if (c == EOF) + { + parm->eof_seen = 1; + if (es_ferror (parm->fp)) + return -1; + break; + } + parm->line[n++] = c; + if (c == '\n') + { + parm->have_lf = 1; + /* Fixme: we need to skip overlong lines while detecting + the dashed lines */ + break; + } + } + parm->linelen = n; + if (!n) + return -1; /* eof */ + parm->readpos = 0; + } + + if (!parm->identified) + { + if (!parm->autodetect) + { + if (parm->assume_pem) + { + /* wait for the header line */ + parm->linelen = parm->readpos = 0; + if (!parm->have_lf + || strncmp ((char*)parm->line, "-----BEGIN ", 11) + || !strncmp ((char*)parm->line+11, "PGP ", 4)) + goto next; + parm->is_pem = 1; + } + else if (parm->assume_base64) + parm->is_base64 = 1; + } + else if (parm->line_counter == 1 && !parm->have_lf) + { + /* first line too long - assume DER encoding */ + parm->is_pem = 0; + } + else if (parm->line_counter == 1 && parm->linelen && *parm->line == 0x30) + { + /* the very first byte does pretty much look like a SEQUENCE tag*/ + parm->is_pem = 0; + } + else if ( parm->have_lf + && !strncmp ((char*)parm->line, "-----BEGIN ", 11) + && strncmp ((char *)parm->line+11, "PGP ", 4) ) + { + /* Fixme: we must only compare if the line really starts at + the beginning */ + parm->is_pem = 1; + parm->linelen = parm->readpos = 0; + } + else if ( parm->have_lf && parm->line_counter == 1 + && parm->linelen >= 13 + && !ascii_memcasecmp (parm->line, "Content-Type:", 13)) + { /* might be a S/MIME body */ + parm->might_be_smime = 1; + parm->linelen = parm->readpos = 0; + goto next; + } + else if (parm->might_be_smime == 1 + && is_empty_line (parm->line, parm->linelen)) + { + parm->might_be_smime = 2; + parm->linelen = parm->readpos = 0; + goto next; + } + else if (parm->might_be_smime == 2) + { + parm->might_be_smime = 0; + if ( !has_only_base64 (parm->line, parm->linelen)) + { + parm->linelen = parm->readpos = 0; + goto next; + } + parm->is_pem = 1; + } + else + { + parm->linelen = parm->readpos = 0; + goto next; + } + parm->identified = 1; + parm->base64.stop_seen = 0; + parm->base64.idx = 0; + } + + + n = 0; + if (parm->is_pem || parm->is_base64) + { + if (parm->is_pem && parm->have_lf + && !strncmp ((char*)parm->line, "-----END ", 9)) + { + parm->identified = 0; + parm->linelen = parm->readpos = 0; + + /* If the caller want to read multiple PEM objects from one + file, we have to reset our internal state and return a + EOF immediately. The caller is the expected to use + ksba_reader_clear to clear the EOF condition and continue + to read. If we don't want to do that we just return 0 + bytes which will force the ksba_reader to skip until + EOF. */ + if (parm->allow_multi_pem) + { + parm->identified = 0; + parm->autodetect = 0; + parm->assume_pem = 1; + parm->stop_seen = 0; + return -1; /* Send EOF now. */ + } + } + else if (parm->stop_seen) + { /* skip the rest of the line */ + parm->linelen = parm->readpos = 0; + } + else + { + int idx = parm->base64.idx; + unsigned char val = parm->base64.val; + + while (n < count && parm->readpos < parm->linelen ) + { + c = parm->line[parm->readpos++]; + if (c == '\n' || c == ' ' || c == '\r' || c == '\t') + continue; + if ((c = asctobin[(c2=c)]) == 255) + { + if (c2 == '=') + { /* pad character: stop */ + if (idx == 1) + buffer[n++] = val; + parm->stop_seen = 1; + break; + } + else if (c2 == '-' + && parm->readpos == 1 + && parm->readpos-1+9 < parm->linelen + && !strncmp ((char*)parm->line + parm->readpos-1, + "-----END ", 9)) + { /* END line seen (padding was not needed). */ + parm->stop_seen = 1; + break; + } + log_error (_("invalid radix64 character %02x skipped\n"), + c2); + continue; + } + switch (idx) + { + case 0: + val = c << 2; + break; + case 1: + val |= (c>>4)&3; + buffer[n++] = val; + val = (c<<4)&0xf0; + break; + case 2: + val |= (c>>2)&15; + buffer[n++] = val; + val = (c<<6)&0xc0; + break; + case 3: + val |= c&0x3f; + buffer[n++] = val; + break; + } + idx = (idx+1) % 4; + } + if (parm->readpos == parm->linelen) + parm->linelen = parm->readpos = 0; + + parm->base64.idx = idx; + parm->base64.val = val; + } + } + else + { /* DER encoded */ + while (n < count && parm->readpos < parm->linelen) + buffer[n++] = parm->line[parm->readpos++]; + if (parm->readpos == parm->linelen) + parm->linelen = parm->readpos = 0; + } + + *nread = n; + return 0; +} + + + +static int +simple_reader_cb (void *cb_value, char *buffer, size_t count, size_t *nread) +{ + struct reader_cb_parm_s *parm = cb_value; + size_t n; + int c = 0; + + *nread = 0; + if (!buffer) + return -1; /* not supported */ + + for (n=0; n < count; n++) + { + c = es_getc (parm->fp); + if (c == EOF) + { + parm->eof_seen = 1; + if (es_ferror (parm->fp)) + return -1; + if (n) + break; /* Return what we have before an EOF. */ + return -1; + } + *(byte *)buffer++ = c; + } + + *nread = n; + return 0; +} + + + + +static int +base64_writer_cb (void *cb_value, const void *buffer, size_t count) +{ + struct writer_cb_parm_s *parm = cb_value; + unsigned char radbuf[4]; + int i, c, idx, quad_count; + const unsigned char *p; + estream_t stream = parm->stream; + + if (!count) + return 0; + + if (!parm->wrote_begin) + { + if (parm->pem_name) + { + es_fputs ("-----BEGIN ", stream); + es_fputs (parm->pem_name, stream); + es_fputs ("-----\n", stream); + } + parm->wrote_begin = 1; + parm->base64.idx = 0; + parm->base64.quad_count = 0; + } + + idx = parm->base64.idx; + quad_count = parm->base64.quad_count; + for (i=0; i < idx; i++) + radbuf[i] = parm->base64.radbuf[i]; + + for (p=buffer; count; p++, count--) + { + radbuf[idx++] = *p; + if (idx > 2) + { + idx = 0; + c = bintoasc[(*radbuf >> 2) & 077]; + es_putc (c, stream); + c = bintoasc[(((*radbuf<<4)&060)|((radbuf[1] >> 4)&017))&077]; + es_putc (c, stream); + c = bintoasc[(((radbuf[1]<<2)&074)|((radbuf[2]>>6)&03))&077]; + es_putc (c, stream); + c = bintoasc[radbuf[2]&077]; + es_putc (c, stream); + if (++quad_count >= (64/4)) + { + es_fputs (LF, stream); + quad_count = 0; + } + } + } + for (i=0; i < idx; i++) + parm->base64.radbuf[i] = radbuf[i]; + parm->base64.idx = idx; + parm->base64.quad_count = quad_count; + + return es_ferror (stream)? gpg_error_from_syserror () : 0; +} + + +/* This callback is only used in stream mode. However, we don't + restrict it to this. */ +static int +plain_writer_cb (void *cb_value, const void *buffer, size_t count) +{ + struct writer_cb_parm_s *parm = cb_value; + estream_t stream = parm->stream; + + if (!count) + return 0; + + es_write (stream, buffer, count, NULL); + + return es_ferror (stream)? gpg_error_from_syserror () : 0; +} + + +static int +base64_finish_write (struct writer_cb_parm_s *parm) +{ + unsigned char *radbuf; + int c, idx, quad_count; + estream_t stream = parm->stream; + + if (!parm->wrote_begin) + return 0; /* Nothing written or we are not called in base-64 mode. */ + + /* flush the base64 encoding */ + idx = parm->base64.idx; + quad_count = parm->base64.quad_count; + if (idx) + { + radbuf = parm->base64.radbuf; + + c = bintoasc[(*radbuf>>2)&077]; + es_putc (c, stream); + if (idx == 1) + { + c = bintoasc[((*radbuf << 4) & 060) & 077]; + es_putc (c, stream); + es_putc ('=', stream); + es_putc ('=', stream); + } + else + { + c = bintoasc[(((*radbuf<<4)&060)|((radbuf[1]>>4)&017))&077]; + es_putc (c, stream); + c = bintoasc[((radbuf[1] << 2) & 074) & 077]; + es_putc (c, stream); + es_putc ('=', stream); + + } + if (++quad_count >= (64/4)) + { + es_fputs (LF, stream); + quad_count = 0; + } + } + + if (quad_count) + es_fputs (LF, stream); + + if (parm->pem_name) + { + es_fputs ("-----END ", stream); + es_fputs (parm->pem_name, stream); + es_fputs ("-----\n", stream); + } + + return es_ferror (stream)? gpg_error_from_syserror () : 0; +} + + + + +/* Create a reader for the stream FP. FLAGS can be used to specify + * the expected input encoding. + * + * The function returns a gnupg_ksba_io_t object which must be passed to + * the gpgme_destroy_reader function. The created ksba_reader_t + * object is stored at R_READER - the caller must not call the + * ksba_reader_release function on. + * + * The supported flags are: + * + * GNUPG_KSBA_IO_PEM - Assume the input is PEM encoded + * GNUPG_KSBA_IO_BASE64 - Assume the input is Base64 encoded. + * GNUPG_KSBA_IO_AUTODETECT - The reader tries to detect the encoding. + * GNUPG_KSBA_IO_MULTIPEM - The reader expects that the caller uses + * ksba_reader_clear after EOF until no more + * objects were found. + * + * Note that the PEM flag has a higher priority than the BASE64 flag + * which in turn has a gight priority than the AUTODETECT flag. + */ +gpg_error_t +gnupg_ksba_create_reader (gnupg_ksba_io_t *ctx, + unsigned int flags, estream_t fp, + ksba_reader_t *r_reader) +{ + int rc; + ksba_reader_t r; + + *r_reader = NULL; + *ctx = xtrycalloc (1, sizeof **ctx); + if (!*ctx) + return out_of_core (); + (*ctx)->u.rparm.allow_multi_pem = !!(flags & GNUPG_KSBA_IO_MULTIPEM); + + rc = ksba_reader_new (&r); + if (rc) + { + xfree (*ctx); *ctx = NULL; + return rc; + } + + (*ctx)->u.rparm.fp = fp; + if ((flags & GNUPG_KSBA_IO_PEM)) + { + (*ctx)->u.rparm.assume_pem = 1; + (*ctx)->u.rparm.assume_base64 = 1; + rc = ksba_reader_set_cb (r, base64_reader_cb, &(*ctx)->u.rparm); + } + else if ((flags & GNUPG_KSBA_IO_BASE64)) + { + (*ctx)->u.rparm.assume_base64 = 1; + rc = ksba_reader_set_cb (r, base64_reader_cb, &(*ctx)->u.rparm); + } + else if ((flags & GNUPG_KSBA_IO_AUTODETECT)) + { + (*ctx)->u.rparm.autodetect = 1; + rc = ksba_reader_set_cb (r, base64_reader_cb, &(*ctx)->u.rparm); + } + else + rc = ksba_reader_set_cb (r, simple_reader_cb, &(*ctx)->u.rparm); + + if (rc) + { + ksba_reader_release (r); + xfree (*ctx); *ctx = NULL; + return rc; + } + + (*ctx)->u2.reader = r; + *r_reader = r; + return 0; +} + + +/* Return True if an EOF as been seen. */ +int +gnupg_ksba_reader_eof_seen (gnupg_ksba_io_t ctx) +{ + return ctx && ctx->u.rparm.eof_seen; +} + + +/* Destroy a reader object. */ +void +gnupg_ksba_destroy_reader (gnupg_ksba_io_t ctx) +{ + if (!ctx) + return; + + ksba_reader_release (ctx->u2.reader); + xfree (ctx); +} + + + +/* Create a writer for the given STREAM. Depending on FLAGS an output + * encoding is chosen. In PEM mode PEM_NAME is used for the header + * and footer lines; if PEM_NAME is NULL the string "CMS OBJECT" is + * used. + * + * The function returns a gnupg_ksba_io_t object which must be passed to + * the gpgme_destroy_writer function. The created ksba_writer_t + * object is stored at R_WRITER - the caller must not call the + * ksba_reader_release function on it. + * + * The supported flags are: + * + * GNUPG_KSBA_IO_PEM - Write output as PEM + * GNUPG_KSBA_IO_BASE64 - Write output as plain Base64; note that the PEM + * flag overrides this flag. + * + */ +gpg_error_t +gnupg_ksba_create_writer (gnupg_ksba_io_t *ctx, unsigned int flags, + const char *pem_name, estream_t stream, + ksba_writer_t *r_writer) +{ + int rc; + ksba_writer_t w; + + *r_writer = NULL; + *ctx = xtrycalloc (1, sizeof **ctx); + if (!*ctx) + return gpg_error_from_syserror (); + + rc = ksba_writer_new (&w); + if (rc) + { + xfree (*ctx); *ctx = NULL; + return rc; + } + + if ((flags & GNUPG_KSBA_IO_PEM) || (flags & GNUPG_KSBA_IO_BASE64)) + { + (*ctx)->u.wparm.stream = stream; + if ((flags & GNUPG_KSBA_IO_PEM)) + { + (*ctx)->u.wparm.pem_name = xtrystrdup (pem_name + ? pem_name + : "CMS OBJECT"); + if (!(*ctx)->u.wparm.pem_name) + { + rc = gpg_error_from_syserror (); + ksba_writer_release (w); + xfree (*ctx); *ctx = NULL; + return rc; + } + } + rc = ksba_writer_set_cb (w, base64_writer_cb, &(*ctx)->u.wparm); + } + else if (stream) + { + (*ctx)->u.wparm.stream = stream; + rc = ksba_writer_set_cb (w, plain_writer_cb, &(*ctx)->u.wparm); + } + else + rc = gpg_error (GPG_ERR_INV_ARG); + + if (rc) + { + ksba_writer_release (w); + xfree (*ctx); *ctx = NULL; + return rc; + } + + (*ctx)->u2.writer = w; + *r_writer = w; + return 0; +} + + +/* Flush a writer. This is for example required to write the padding + * or the PEM footer. */ +gpg_error_t +gnupg_ksba_finish_writer (gnupg_ksba_io_t ctx) +{ + struct writer_cb_parm_s *parm; + + if (!ctx) + return gpg_error (GPG_ERR_INV_VALUE); + parm = &ctx->u.wparm; + if (parm->did_finish) + return 0; /* Already done. */ + parm->did_finish = 1; + if (!parm->stream) + return 0; /* Callback was not used. */ + return base64_finish_write (parm); +} + + +/* Destroy a writer object. */ +void +gnupg_ksba_destroy_writer (gnupg_ksba_io_t ctx) +{ + if (!ctx) + return; + + ksba_writer_release (ctx->u2.writer); + xfree (ctx->u.wparm.pem_name); + xfree (ctx); +} diff --git a/common/ksba-io-support.h b/common/ksba-io-support.h new file mode 100644 index 0000000..e33e0ed --- /dev/null +++ b/common/ksba-io-support.h @@ -0,0 +1,66 @@ +/* ksba-io-support.h - Supporting functions for ksba reader and writer + * Copyright (C) 2017 Werner Koch + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef GNUPG_KSBA_IO_SUPPORT_H +#define GNUPG_KSBA_IO_SUPPORT_H + +/* Flags used with gnupg_ksba_create_reader and + * gnupg_ksba_create_writer. */ +#define GNUPG_KSBA_IO_PEM 1 /* X.509 PEM format. */ +#define GNUPG_KSBA_IO_BASE64 2 /* Plain Base64 format. */ +#define GNUPG_KSBA_IO_AUTODETECT 4 /* Try to autodetect the format. */ +#define GNUPG_KSBA_IO_MULTIPEM 8 /* Allow more than one PEM chunk. */ + + +/* Context object. */ +typedef struct gnupg_ksba_io_s *gnupg_ksba_io_t; + + + +gpg_error_t gnupg_ksba_create_reader (gnupg_ksba_io_t *ctx, + unsigned int flags, + estream_t fp, + ksba_reader_t *r_reader); + +int gnupg_ksba_reader_eof_seen (gnupg_ksba_io_t ctx); +void gnupg_ksba_destroy_reader (gnupg_ksba_io_t ctx); + +gpg_error_t gnupg_ksba_create_writer (gnupg_ksba_io_t *ctx, + unsigned int flags, + const char *pem_name, + estream_t stream, + ksba_writer_t *r_writer); + +gpg_error_t gnupg_ksba_finish_writer (gnupg_ksba_io_t ctx); +void gnupg_ksba_destroy_writer (gnupg_ksba_io_t ctx); + + + + +#endif /*GNUPG_KSBA_IO_SUPPORT_H*/ diff --git a/common/localename.c b/common/localename.c new file mode 100644 index 0000000..b620a74 --- /dev/null +++ b/common/localename.c @@ -0,0 +1,127 @@ +/* localename.c - Determine the current selected locale. + * Copyright (C) 1995-1999, 2000-2003, 2007, + * 2008 Free Software Foundation, Inc. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, see <https://www.gnu.org/licenses/>. + */ +/* Written by Ulrich Drepper <drepper@gnu.org>, 1995. */ +/* Win32 code written by Tor Lillqvist <tml@iki.fi>. */ +/* Modified for GpgOL use by Werner Koch <wk@gnupg.org>, 2005. */ +/* Modified for GnuPG use by Werner Koch <wk@gnupg.org>, 2007 */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdlib.h> +#include <string.h> +#ifdef HAVE_LOCALE_H +#include <locale.h> +#endif +#include <gpg-error.h> /* We need gettext_localename for W32. */ + +#include "../common/w32help.h" + +/* XPG3 defines the result of 'setlocale (category, NULL)' as: + "Directs 'setlocale()' to query 'category' and return the current + setting of 'local'." + However it does not specify the exact format. Neither do SUSV2 and + ISO C 99. So we can use this feature only on selected systems (e.g. + those using GNU C Library). */ +#if defined _LIBC || (defined __GNU_LIBRARY__ && __GNU_LIBRARY__ >= 2) +# define HAVE_LOCALE_NULL +#endif + +/* Use a dummy value for LC_MESSAGES in case it is not defined. This + works because we always test for HAVE_LC_MESSAGES and the core + function takes the category as a string as well. */ +#ifndef HAVE_LC_MESSAGES +#define LC_MESSAGES 0 +#endif + + +/* Determine the current locale's name, and canonicalize it into XPG syntax + language[_territory[.codeset]][@modifier] + The codeset part in the result is not reliable; the locale_charset() + should be used for codeset information instead. + The result must not be freed; it is statically allocated. */ + +#ifndef HAVE_W32_SYSTEM +static const char * +do_nl_locale_name (int category, const char *categoryname) +{ + const char *retval; + + /* Use the POSIX methods of looking to 'LC_ALL', 'LC_xxx', and 'LANG'. + On some systems this can be done by the 'setlocale' function itself. */ +# if defined HAVE_SETLOCALE && defined HAVE_LC_MESSAGES && defined HAVE_LOCALE_NULL + (void)categoryname; + retval = setlocale (category, NULL); +# else + (void)category; + /* Setting of LC_ALL overwrites all other. */ + retval = getenv ("LC_ALL"); + if (retval == NULL || retval[0] == '\0') + { + /* Next comes the name of the desired category. */ + retval = getenv (categoryname); + if (retval == NULL || retval[0] == '\0') + { + /* Last possibility is the LANG environment variable. */ + retval = getenv ("LANG"); + if (retval == NULL || retval[0] == '\0') + /* We use C as the default domain. POSIX says this is + implementation defined. */ + retval = "C"; + } + } +# endif + + return retval; +} +#endif /* HAVE_W32_SYSTEM */ + + + +/* Return the locale used for translatable messages. The standard C + and POSIX are locale names are mapped to an empty string. If a + locale can't be found an empty string will be returned. */ +const char * +gnupg_messages_locale_name (void) +{ + const char *s; + +#ifdef HAVE_W32_SYSTEM + /* We use the localename function libgpg-error. */ + s = gettext_localename (); +#else + s = do_nl_locale_name (LC_MESSAGES, "LC_MESSAGES"); +#endif + if (!s) + s = ""; + else if (!strcmp (s, "C") || !strcmp (s, "POSIX")) + s = ""; + + return s; +} diff --git a/common/logging.c b/common/logging.c new file mode 100644 index 0000000..7fe8b74 --- /dev/null +++ b/common/logging.c @@ -0,0 +1,1110 @@ +/* logging.c - Useful logging functions + * Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006, + * 2009, 2010 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute and/or modify this + * part of GnuPG under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * GnuPG is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copies of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, see <https://www.gnu.org/licenses/>. + */ + + +#include <config.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <stddef.h> +#include <errno.h> +#include <time.h> +#include <sys/types.h> +#include <sys/stat.h> +#ifdef HAVE_W32_SYSTEM +# ifdef HAVE_WINSOCK2_H +# include <winsock2.h> +# endif +# include <windows.h> +#else /*!HAVE_W32_SYSTEM*/ +# include <sys/socket.h> +# include <sys/un.h> +# include <netinet/in.h> +# include <arpa/inet.h> +#endif /*!HAVE_W32_SYSTEM*/ +#include <unistd.h> +#include <fcntl.h> +#include <assert.h> +/* #include <execinfo.h> */ + +#define GNUPG_COMMON_NEED_AFLOCAL 1 +#include "util.h" +#include "i18n.h" +#include "common-defs.h" +#include "logging.h" +#include "sysutils.h" + +#ifdef HAVE_W32_SYSTEM +# ifndef S_IRWXG +# define S_IRGRP S_IRUSR +# define S_IWGRP S_IWUSR +# endif +# ifndef S_IRWXO +# define S_IROTH S_IRUSR +# define S_IWOTH S_IWUSR +# endif +#endif + + +#ifdef HAVE_W32CE_SYSTEM +# define isatty(a) (0) +#endif + +#undef WITH_IPV6 +#if defined (AF_INET6) && defined(PF_INET) \ + && defined (INET6_ADDRSTRLEN) && defined(HAVE_INET_PTON) +# define WITH_IPV6 1 +#endif + +#ifndef EAFNOSUPPORT +# define EAFNOSUPPORT EINVAL +#endif +#ifndef INADDR_NONE /* Slowaris is missing that. */ +#define INADDR_NONE ((unsigned long)(-1)) +#endif /*INADDR_NONE*/ + +#ifdef HAVE_W32_SYSTEM +#define sock_close(a) closesocket(a) +#else +#define sock_close(a) close(a) +#endif + + +static estream_t logstream; +static int log_socket = -1; +static char prefix_buffer[80]; +static int with_time; +static int with_prefix; +static int with_pid; +#ifdef HAVE_W32_SYSTEM +static int no_registry; +#endif +static int (*get_pid_suffix_cb)(unsigned long *r_value); +static const char * (*socket_dir_cb)(void); +static int running_detached; +static int force_prefixes; + +static int missing_lf; +static int errorcount; + + +int +log_get_errorcount (int clear) +{ + int n = errorcount; + if( clear ) + errorcount = 0; + return n; +} + +void +log_inc_errorcount (void) +{ + /* Protect against counter overflow. */ + if (errorcount < 30000) + errorcount++; +} + + +/* The following 3 functions are used by es_fopencookie to write logs + to a socket. */ +struct fun_cookie_s +{ + int fd; + int quiet; + int want_socket; + int is_socket; +#ifdef HAVE_W32CE_SYSTEM + int use_writefile; +#endif + char name[1]; +}; + + +/* Write NBYTES of BUFFER to file descriptor FD. */ +static int +writen (int fd, const void *buffer, size_t nbytes, int is_socket) +{ + const char *buf = buffer; + size_t nleft = nbytes; + int nwritten; +#ifndef HAVE_W32_SYSTEM + (void)is_socket; /* Not required. */ +#endif + + while (nleft > 0) + { +#ifdef HAVE_W32_SYSTEM + if (is_socket) + nwritten = send (fd, buf, nleft, 0); + else +#endif + nwritten = write (fd, buf, nleft); + + if (nwritten < 0 && errno == EINTR) + continue; + if (nwritten < 0) + return -1; + nleft -= nwritten; + buf = buf + nwritten; + } + + return 0; +} + + +/* Returns true if STR represents a valid port number in decimal + notation and no garbage is following. */ +static int +parse_portno (const char *str, unsigned short *r_port) +{ + unsigned int value; + + for (value=0; *str && (*str >= '0' && *str <= '9'); str++) + { + value = value * 10 + (*str - '0'); + if (value > 65535) + return 0; + } + if (*str || !value) + return 0; + + *r_port = value; + return 1; +} + + +static gpgrt_ssize_t +fun_writer (void *cookie_arg, const void *buffer, size_t size) +{ + struct fun_cookie_s *cookie = cookie_arg; + + /* FIXME: Use only estream with a callback for socket writing. This + avoids the ugly mix of fd and estream code. */ + + /* Note that we always try to reconnect to the socket but print + error messages only the first time an error occurred. If + RUNNING_DETACHED is set we don't fall back to stderr and even do + not print any error messages. This is needed because detached + processes often close stderr and by writing to file descriptor 2 + we might send the log message to a file not intended for logging + (e.g. a pipe or network connection). */ + if (cookie->want_socket && cookie->fd == -1) + { +#ifdef WITH_IPV6 + struct sockaddr_in6 srvr_addr_in6; +#endif + struct sockaddr_in srvr_addr_in; +#ifndef HAVE_W32_SYSTEM + struct sockaddr_un srvr_addr_un; +#endif + const char *name_for_err = ""; + size_t addrlen; + struct sockaddr *srvr_addr = NULL; + unsigned short port = 0; + int af = AF_LOCAL; + int pf = PF_LOCAL; + const char *name = cookie->name; + + /* Not yet open or meanwhile closed due to an error. */ + cookie->is_socket = 0; + + /* Check whether this is a TCP socket or a local socket. */ + if (!strncmp (name, "tcp://", 6) && name[6]) + { + name += 6; + af = AF_INET; + pf = PF_INET; + } +#ifndef HAVE_W32_SYSTEM + else if (!strncmp (name, "socket://", 9)) + name += 9; +#endif + + if (af == AF_LOCAL) + { + addrlen = 0; +#ifndef HAVE_W32_SYSTEM + memset (&srvr_addr, 0, sizeof srvr_addr); + srvr_addr_un.sun_family = af; + if (!*name && (name = socket_dir_cb ()) && *name) + { + if (strlen (name) + 7 < sizeof (srvr_addr_un.sun_path)-1) + { + strncpy (srvr_addr_un.sun_path, + name, sizeof (srvr_addr_un.sun_path)-1); + strcat (srvr_addr_un.sun_path, "/S.log"); + srvr_addr_un.sun_path[sizeof (srvr_addr_un.sun_path)-1] = 0; + srvr_addr = (struct sockaddr *)&srvr_addr_un; + addrlen = SUN_LEN (&srvr_addr_un); + name_for_err = srvr_addr_un.sun_path; + } + } + else + { + if (*name && strlen (name) < sizeof (srvr_addr_un.sun_path)-1) + { + strncpy (srvr_addr_un.sun_path, + name, sizeof (srvr_addr_un.sun_path)-1); + srvr_addr_un.sun_path[sizeof (srvr_addr_un.sun_path)-1] = 0; + srvr_addr = (struct sockaddr *)&srvr_addr_un; + addrlen = SUN_LEN (&srvr_addr_un); + } + } +#endif /*!HAVE_W32SYSTEM*/ + } + else + { + char *addrstr, *p; +#ifdef HAVE_INET_PTON + void *addrbuf = NULL; +#endif /*HAVE_INET_PTON*/ + + addrstr = xtrymalloc (strlen (name) + 1); + if (!addrstr) + addrlen = 0; /* This indicates an error. */ + else if (*name == '[') + { + /* Check for IPv6 literal address. */ + strcpy (addrstr, name+1); + p = strchr (addrstr, ']'); + if (!p || p[1] != ':' || !parse_portno (p+2, &port)) + { + gpg_err_set_errno (EINVAL); + addrlen = 0; + } + else + { + *p = 0; +#ifdef WITH_IPV6 + af = AF_INET6; + pf = PF_INET6; + memset (&srvr_addr_in6, 0, sizeof srvr_addr_in6); + srvr_addr_in6.sin6_family = af; + srvr_addr_in6.sin6_port = htons (port); +#ifdef HAVE_INET_PTON + addrbuf = &srvr_addr_in6.sin6_addr; +#endif /*HAVE_INET_PTON*/ + srvr_addr = (struct sockaddr *)&srvr_addr_in6; + addrlen = sizeof srvr_addr_in6; +#else + gpg_err_set_errno (EAFNOSUPPORT); + addrlen = 0; +#endif + } + } + else + { + /* Check for IPv4 literal address. */ + strcpy (addrstr, name); + p = strchr (addrstr, ':'); + if (!p || !parse_portno (p+1, &port)) + { + gpg_err_set_errno (EINVAL); + addrlen = 0; + } + else + { + *p = 0; + memset (&srvr_addr_in, 0, sizeof srvr_addr_in); + srvr_addr_in.sin_family = af; + srvr_addr_in.sin_port = htons (port); +#ifdef HAVE_INET_PTON + addrbuf = &srvr_addr_in.sin_addr; +#endif /*HAVE_INET_PTON*/ + srvr_addr = (struct sockaddr *)&srvr_addr_in; + addrlen = sizeof srvr_addr_in; + } + } + + if (addrlen) + { +#ifdef HAVE_INET_PTON + if (inet_pton (af, addrstr, addrbuf) != 1) + addrlen = 0; +#else /*!HAVE_INET_PTON*/ + /* We need to use the old function. If we are here v6 + support isn't enabled anyway and thus we can do fine + without. Note that Windows has a compatible inet_pton + function named inetPton, but only since Vista. */ + srvr_addr_in.sin_addr.s_addr = inet_addr (addrstr); + if (srvr_addr_in.sin_addr.s_addr == INADDR_NONE) + addrlen = 0; +#endif /*!HAVE_INET_PTON*/ + } + + xfree (addrstr); + } + + cookie->fd = addrlen? socket (pf, SOCK_STREAM, 0) : -1; + if (cookie->fd == -1) + { + if (!cookie->quiet && !running_detached + && isatty (es_fileno (es_stderr))) + es_fprintf (es_stderr, "failed to create socket for logging: %s\n", + strerror(errno)); + } + else + { + if (connect (cookie->fd, srvr_addr, addrlen) == -1) + { + if (!cookie->quiet && !running_detached + && isatty (es_fileno (es_stderr))) + es_fprintf (es_stderr, "can't connect to '%s%s': %s\n", + cookie->name, name_for_err, strerror(errno)); + sock_close (cookie->fd); + cookie->fd = -1; + } + } + + if (cookie->fd == -1) + { + if (!running_detached) + { + /* Due to all the problems with apps not running + detached but being called with stderr closed or used + for a different purposes, it does not make sense to + switch to stderr. We therefore disable it. */ + if (!cookie->quiet) + { + /* fputs ("switching logging to stderr\n", stderr);*/ + cookie->quiet = 1; + } + cookie->fd = -1; /*fileno (stderr);*/ + } + } + else /* Connection has been established. */ + { + cookie->quiet = 0; + cookie->is_socket = 1; + } + } + + log_socket = cookie->fd; + if (cookie->fd != -1) + { +#ifdef HAVE_W32CE_SYSTEM + if (cookie->use_writefile) + { + DWORD nwritten; + + WriteFile ((HANDLE)cookie->fd, buffer, size, &nwritten, NULL); + return (gpgrt_ssize_t)size; /* Okay. */ + } +#endif + if (!writen (cookie->fd, buffer, size, cookie->is_socket)) + return (gpgrt_ssize_t)size; /* Okay. */ + } + + if (!running_detached && cookie->fd != -1 + && isatty (es_fileno (es_stderr))) + { + if (*cookie->name) + es_fprintf (es_stderr, "error writing to '%s': %s\n", + cookie->name, strerror(errno)); + else + es_fprintf (es_stderr, "error writing to file descriptor %d: %s\n", + cookie->fd, strerror(errno)); + } + if (cookie->is_socket && cookie->fd != -1) + { + sock_close (cookie->fd); + cookie->fd = -1; + log_socket = -1; + } + + return (gpgrt_ssize_t)size; +} + + +static int +fun_closer (void *cookie_arg) +{ + struct fun_cookie_s *cookie = cookie_arg; + + if (cookie->fd != -1 && cookie->fd != 2) + sock_close (cookie->fd); + xfree (cookie); + log_socket = -1; + return 0; +} + + +/* Common function to either set the logging to a file or a file + descriptor. */ +static void +set_file_fd (const char *name, int fd) +{ + estream_t fp; + int want_socket; +#ifdef HAVE_W32CE_SYSTEM + int use_writefile = 0; +#endif + struct fun_cookie_s *cookie; + + /* Close an open log stream. */ + if (logstream) + { + if (logstream != es_stderr) + es_fclose (logstream); + logstream = NULL; + } + + /* Figure out what kind of logging we want. */ + if (name && !strcmp (name, "-")) + { + name = NULL; + fd = es_fileno (es_stderr); + } + + want_socket = 0; + if (name && !strncmp (name, "tcp://", 6) && name[6]) + want_socket = 1; +#ifndef HAVE_W32_SYSTEM + else if (name && !strncmp (name, "socket://", 9)) + want_socket = 2; +#endif /*HAVE_W32_SYSTEM*/ +#ifdef HAVE_W32CE_SYSTEM + else if (name && !strcmp (name, "GPG2:")) + { + HANDLE hd; + + ActivateDevice (L"Drivers\\"GNUPG_NAME"_Log", 0); + /* Ignore a filename and write the debug output to the GPG2: + device. */ + hd = CreateFile (L"GPG2:", GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + fd = (hd == INVALID_HANDLE_VALUE)? -1 : (int)hd; + name = NULL; + force_prefixes = 1; + use_writefile = 1; + } +#endif /*HAVE_W32CE_SYSTEM*/ + + /* Setup a new stream. */ + + /* The xmalloc below is justified because we can expect that this + function is called only during initialization and there is no + easy way out of this error condition. */ + cookie = xmalloc (sizeof *cookie + (name? strlen (name):0)); + strcpy (cookie->name, name? name:""); + cookie->quiet = 0; + cookie->is_socket = 0; + cookie->want_socket = want_socket; +#ifdef HAVE_W32CE_SYSTEM + cookie->use_writefile = use_writefile; +#endif + if (!name) + cookie->fd = fd; + else if (want_socket) + cookie->fd = -1; + else + { + do + cookie->fd = open (name, O_WRONLY|O_APPEND|O_CREAT, + (S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR|S_IWGRP|S_IWOTH)); + while (cookie->fd == -1 && errno == EINTR); + } + log_socket = cookie->fd; + + { + es_cookie_io_functions_t io = { NULL }; + io.func_write = fun_writer; + io.func_close = fun_closer; + + fp = es_fopencookie (cookie, "w", io); + } + + /* On error default to a stderr based estream. */ + if (!fp) + fp = es_stderr; + + es_setvbuf (fp, NULL, _IOLBF, 0); + + logstream = fp; + + /* We always need to print the prefix and the pid for socket mode, + so that the server reading the socket can do something + meaningful. */ + force_prefixes = want_socket; + + missing_lf = 0; +} + + +/* Set the file to write log to. The special names NULL and "-" may + be used to select stderr and names formatted like + "socket:///home/foo/mylogs" may be used to write the logging to the + socket "/home/foo/mylogs". If the connection to the socket fails + or a write error is detected, the function writes to stderr and + tries the next time again to connect the socket. + */ +void +log_set_file (const char *name) +{ + set_file_fd (name? name: "-", -1); +} + +void +log_set_fd (int fd) +{ + if (! gnupg_fd_valid (fd)) + log_fatal ("logger-fd is invalid: %s\n", strerror (errno)); + + set_file_fd (NULL, fd); +} + + +/* Set a function to retrieve the directory name of a socket if + * only "socket://" has been given to log_set_file. */ +void +log_set_socket_dir_cb (const char *(*fnc)(void)) +{ + socket_dir_cb = fnc; +} + + +void +log_set_pid_suffix_cb (int (*cb)(unsigned long *r_value)) +{ + get_pid_suffix_cb = cb; +} + + +void +log_set_prefix (const char *text, unsigned int flags) +{ + if (text) + { + strncpy (prefix_buffer, text, sizeof (prefix_buffer)-1); + prefix_buffer[sizeof (prefix_buffer)-1] = 0; + } + + with_prefix = (flags & GPGRT_LOG_WITH_PREFIX); + with_time = (flags & GPGRT_LOG_WITH_TIME); + with_pid = (flags & GPGRT_LOG_WITH_PID); + running_detached = (flags & GPGRT_LOG_RUN_DETACHED); +#ifdef HAVE_W32_SYSTEM + no_registry = (flags & GPGRT_LOG_NO_REGISTRY); +#endif +} + + +const char * +log_get_prefix (unsigned int *flags) +{ + if (flags) + { + *flags = 0; + if (with_prefix) + *flags |= GPGRT_LOG_WITH_PREFIX; + if (with_time) + *flags |= GPGRT_LOG_WITH_TIME; + if (with_pid) + *flags |= GPGRT_LOG_WITH_PID; + if (running_detached) + *flags |= GPGRT_LOG_RUN_DETACHED; +#ifdef HAVE_W32_SYSTEM + if (no_registry) + *flags |= GPGRT_LOG_NO_REGISTRY; +#endif + } + return prefix_buffer; +} + +/* This function returns true if the file descriptor FD is in use for + logging. This is preferable over a test using log_get_fd in that + it allows the logging code to use more then one file descriptor. */ +int +log_test_fd (int fd) +{ + if (logstream) + { + int tmp = es_fileno (logstream); + if ( tmp != -1 && tmp == fd) + return 1; + } + if (log_socket != -1 && log_socket == fd) + return 1; + return 0; +} + +int +log_get_fd () +{ + return logstream? es_fileno(logstream) : -1; +} + +estream_t +log_get_stream () +{ + if (!logstream) + { + log_set_file (NULL); /* Make sure a log stream has been set. */ + assert (logstream); + } + return logstream; +} + + +static void +print_prefix (int level, int leading_backspace) +{ + if (level != GPGRT_LOG_CONT) + { /* Note this does not work for multiple line logging as we would + * need to print to a buffer first */ + if (with_time && !force_prefixes) + { + struct tm *tp; + time_t atime = time (NULL); + + tp = localtime (&atime); + es_fprintf_unlocked (logstream, "%04d-%02d-%02d %02d:%02d:%02d ", + 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday, + tp->tm_hour, tp->tm_min, tp->tm_sec ); + } + if (with_prefix || force_prefixes) + es_fputs_unlocked (prefix_buffer, logstream); + if (with_pid || force_prefixes) + { + unsigned long pidsuf; + int pidfmt; + + if (get_pid_suffix_cb && (pidfmt=get_pid_suffix_cb (&pidsuf))) + es_fprintf_unlocked (logstream, pidfmt == 1? "[%u.%lu]":"[%u.%lx]", + (unsigned int)getpid (), pidsuf); + else + es_fprintf_unlocked (logstream, "[%u]", (unsigned int)getpid ()); + } + if ((!with_time && (with_prefix || with_pid)) || force_prefixes) + es_putc_unlocked (':', logstream); + /* A leading backspace suppresses the extra space so that we can + correctly output, programname, filename and linenumber. */ + if (!leading_backspace + && (with_time || with_prefix || with_pid || force_prefixes)) + es_putc_unlocked (' ', logstream); + } + + switch (level) + { + case GPGRT_LOG_BEGIN: break; + case GPGRT_LOG_CONT: break; + case GPGRT_LOG_INFO: break; + case GPGRT_LOG_WARN: break; + case GPGRT_LOG_ERROR: break; + case GPGRT_LOG_FATAL: es_fputs_unlocked ("Fatal: ",logstream ); break; + case GPGRT_LOG_BUG: es_fputs_unlocked ("Ohhhh jeeee: ", logstream); break; + case GPGRT_LOG_DEBUG: es_fputs_unlocked ("DBG: ", logstream ); break; + default: + es_fprintf_unlocked (logstream,"[Unknown log level %d]: ", level); + break; + } +} + + +static void +do_logv (int level, int ignore_arg_ptr, const char *extrastring, + const char *prefmt, const char *fmt, va_list arg_ptr) +{ + int leading_backspace = (fmt && *fmt == '\b'); + + if (!logstream) + { +#ifdef HAVE_W32_SYSTEM + char *tmp; + + tmp = (no_registry + ? NULL + : read_w32_registry_string (NULL, GNUPG_REGISTRY_DIR, + "DefaultLogFile")); + log_set_file (tmp && *tmp? tmp : NULL); + xfree (tmp); +#else + log_set_file (NULL); /* Make sure a log stream has been set. */ +#endif + assert (logstream); + } + + es_flockfile (logstream); + if (missing_lf && level != GPGRT_LOG_CONT) + es_putc_unlocked ('\n', logstream ); + missing_lf = 0; + + print_prefix (level, leading_backspace); + if (leading_backspace) + fmt++; + + if (fmt) + { + if (prefmt) + es_fputs_unlocked (prefmt, logstream); + + if (ignore_arg_ptr) + { /* This is used by log_string and comes with the extra + * feature that after a LF the next line is indent at the + * length of the prefix. Note that we do not yet include + * the length of the timestamp and pid in the indent + * computation. */ + const char *p, *pend; + + for (p = fmt; (pend = strchr (p, '\n')); p = pend+1) + es_fprintf_unlocked (logstream, "%*s%.*s", + (int)((p != fmt + && (with_prefix || force_prefixes)) + ?strlen (prefix_buffer)+2:0), "", + (int)(pend - p)+1, p); + es_fputs_unlocked (p, logstream); + } + else + es_vfprintf_unlocked (logstream, fmt, arg_ptr); + if (*fmt && fmt[strlen(fmt)-1] != '\n') + missing_lf = 1; + } + + /* If we have an EXTRASTRING print it now while we still hold the + * lock on the logstream. */ + if (extrastring) + { + int c; + + if (missing_lf) + { + es_putc_unlocked ('\n', logstream); + missing_lf = 0; + } + print_prefix (level, leading_backspace); + es_fputs_unlocked (">> ", logstream); + missing_lf = 1; + while ((c = *extrastring++)) + { + missing_lf = 1; + if (c == '\\') + es_fputs_unlocked ("\\\\", logstream); + else if (c == '\r') + es_fputs_unlocked ("\\r", logstream); + else if (c == '\n') + { + es_fputs_unlocked ("\\n\n", logstream); + if (*extrastring) + { + print_prefix (level, leading_backspace); + es_fputs_unlocked (">> ", logstream); + } + else + missing_lf = 0; + } + else + es_putc_unlocked (c, logstream); + } + if (missing_lf) + { + es_putc_unlocked ('\n', logstream); + missing_lf = 0; + } + } + + if (level == GPGRT_LOG_FATAL) + { + if (missing_lf) + es_putc_unlocked ('\n', logstream); + es_funlockfile (logstream); + exit (2); + } + else if (level == GPGRT_LOG_BUG) + { + if (missing_lf) + es_putc_unlocked ('\n', logstream ); + es_funlockfile (logstream); + /* Using backtrace requires a configure test and to pass + * -rdynamic to gcc. Thus we do not enable it now. */ + /* { */ + /* void *btbuf[20]; */ + /* int btidx, btlen; */ + /* char **btstr; */ + + /* btlen = backtrace (btbuf, DIM (btbuf)); */ + /* btstr = backtrace_symbols (btbuf, btlen); */ + /* if (btstr) */ + /* for (btidx=0; btidx < btlen; btidx++) */ + /* log_debug ("[%d] %s\n", btidx, btstr[btidx]); */ + /* } */ + abort (); + } + else + es_funlockfile (logstream); +} + + +void +log_log (int level, const char *fmt, ...) +{ + va_list arg_ptr ; + + va_start (arg_ptr, fmt) ; + do_logv (level, 0, NULL, NULL, fmt, arg_ptr); + va_end (arg_ptr); +} + + +void +log_logv (int level, const char *fmt, va_list arg_ptr) +{ + do_logv (level, 0, NULL, NULL, fmt, arg_ptr); +} + + +/* Same as log_logv but PREFIX is printed immediately before FMT. + * Note that PREFIX is an additional string and independent of the + * prefix set by log_set_prefix. */ +void +log_logv_with_prefix (int level, const char *prefix, + const char *fmt, va_list arg_ptr) +{ + do_logv (level, 0, NULL, prefix, fmt, arg_ptr); +} + + +static void +do_log_ignore_arg (int level, const char *str, ...) +{ + va_list arg_ptr; + va_start (arg_ptr, str); + do_logv (level, 1, NULL, NULL, str, arg_ptr); + va_end (arg_ptr); +} + + +/* Log STRING at LEVEL but indent from the second line on by the + * length of the prefix. */ +void +log_string (int level, const char *string) +{ + /* We need a dummy arg_ptr, but there is no portable way to create + * one. So we call the do_logv function through a variadic wrapper. */ + do_log_ignore_arg (level, string); +} + + +void +log_info (const char *fmt, ...) +{ + va_list arg_ptr ; + + va_start (arg_ptr, fmt); + do_logv (GPGRT_LOG_INFO, 0, NULL, NULL, fmt, arg_ptr); + va_end (arg_ptr); +} + + +void +log_error (const char *fmt, ...) +{ + va_list arg_ptr ; + + va_start (arg_ptr, fmt); + do_logv (GPGRT_LOG_ERROR, 0, NULL, NULL, fmt, arg_ptr); + va_end (arg_ptr); + log_inc_errorcount (); +} + + +void +log_fatal (const char *fmt, ...) +{ + va_list arg_ptr ; + + va_start (arg_ptr, fmt); + do_logv (GPGRT_LOG_FATAL, 0, NULL, NULL, fmt, arg_ptr); + va_end (arg_ptr); + abort (); /* Never called; just to make the compiler happy. */ +} + + +void +log_bug (const char *fmt, ...) +{ + va_list arg_ptr ; + + va_start (arg_ptr, fmt); + do_logv (GPGRT_LOG_BUG, 0, NULL, NULL, fmt, arg_ptr); + va_end (arg_ptr); + abort (); /* Never called; just to make the compiler happy. */ +} + + +void +log_debug (const char *fmt, ...) +{ + va_list arg_ptr ; + + va_start (arg_ptr, fmt); + do_logv (GPGRT_LOG_DEBUG, 0, NULL, NULL, fmt, arg_ptr); + va_end (arg_ptr); +} + + +/* The same as log_debug but at the end of the output STRING is + * printed with LFs expanded to include the prefix and a final --end-- + * marker. */ +void +log_debug_with_string (const char *string, const char *fmt, ...) +{ + va_list arg_ptr ; + + va_start (arg_ptr, fmt); + do_logv (GPGRT_LOG_DEBUG, 0, string, NULL, fmt, arg_ptr); + va_end (arg_ptr); +} + + +void +log_printf (const char *fmt, ...) +{ + va_list arg_ptr; + + va_start (arg_ptr, fmt); + do_logv (fmt ? GPGRT_LOG_CONT : GPGRT_LOG_BEGIN, 0, NULL, NULL, fmt, arg_ptr); + va_end (arg_ptr); +} + + +/* Flush the log - this is useful to make sure that the trailing + linefeed has been printed. */ +void +log_flush (void) +{ + do_log_ignore_arg (GPGRT_LOG_CONT, NULL); +} + + +/* Print a hexdump of BUFFER. With TEXT of NULL print just the raw + dump, with TEXT just an empty string, print a trailing linefeed, + otherwise print an entire debug line. */ +void +log_printhex (const void *buffer, size_t length, const char *fmt, ...) +{ + if (fmt && *fmt) + { + va_list arg_ptr ; + + va_start (arg_ptr, fmt); + do_logv (GPGRT_LOG_DEBUG, 0, NULL, NULL, fmt, arg_ptr); + va_end (arg_ptr); + log_printf (" "); + } + if (length) + { + const unsigned char *p = buffer; + log_printf ("%02X", *p); + for (length--, p++; length--; p++) + log_printf (" %02X", *p); + } + if (fmt) + log_printf ("\n"); +} + + +/* +void +log_printcanon () {} +is found in sexputils.c +*/ + +/* +void +log_printsexp () {} +is found in sexputils.c +*/ + + +void +log_clock (const char *string) +{ +#if 0 + static unsigned long long initial; + struct timespec tv; + unsigned long long now; + + if (clock_gettime (CLOCK_REALTIME, &tv)) + { + log_debug ("error getting the realtime clock value\n"); + return; + } + now = tv.tv_sec * 1000000000ull; + now += tv.tv_nsec; + + if (!initial) + initial = now; + + log_debug ("[%6llu] %s", (now - initial)/1000, string); +#else + /* You need to link with -ltr to enable the above code. */ + log_debug ("[not enabled in the source] %s", string); +#endif +} + + +#ifdef GPGRT_HAVE_MACRO_FUNCTION +void +bug_at( const char *file, int line, const char *func ) +{ + log_log (GPGRT_LOG_BUG, "... this is a bug (%s:%d:%s)\n", file, line, func); + abort (); /* Never called; just to make the compiler happy. */ +} +#else /*!GPGRT_HAVE_MACRO_FUNCTION*/ +void +bug_at( const char *file, int line ) +{ + log_log (GPGRT_LOG_BUG, "you found a bug ... (%s:%d)\n", file, line); + abort (); /* Never called; just to make the compiler happy. */ +} +#endif /*!GPGRT_HAVE_MACRO_FUNCTION*/ + + +#ifdef GPGRT_HAVE_MACRO_FUNCTION +void +_log_assert (const char *expr, const char *file, int line, const char *func) +{ + log_log (GPGRT_LOG_BUG, "Assertion \"%s\" in %s failed (%s:%d)\n", + expr, func, file, line); + abort (); /* Never called; just to make the compiler happy. */ +} +#else /*!GPGRT_HAVE_MACRO_FUNCTION*/ +void +_log_assert (const char *expr, const char *file, int line) +{ + log_log (GPGRT_LOG_BUG, "Assertion \"%s\" failed (%s:%d)\n", + expr, file, line); + abort (); /* Never called; just to make the compiler happy. */ +} +#endif /*!GPGRT_HAVE_MACRO_FUNCTION*/ diff --git a/common/logging.h b/common/logging.h new file mode 100644 index 0000000..cb1ec11 --- /dev/null +++ b/common/logging.h @@ -0,0 +1,142 @@ +/* logging.h + * Copyright (C) 1999, 2000, 2001, 2004, 2006, + * 2010 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute and/or modify this + * part of GnuPG under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * GnuPG is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copies of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef GNUPG_COMMON_LOGGING_H +#define GNUPG_COMMON_LOGGING_H + +#include <stdio.h> +#include <stdarg.h> +#include <gpg-error.h> +#include "mischelp.h" +#include "w32help.h" + +int log_get_errorcount (int clear); +void log_inc_errorcount (void); +void log_set_file( const char *name ); +void log_set_fd (int fd); +void log_set_socket_dir_cb (const char *(*fnc)(void)); +void log_set_pid_suffix_cb (int (*cb)(unsigned long *r_value)); +void log_set_prefix (const char *text, unsigned int flags); +const char *log_get_prefix (unsigned int *flags); +int log_test_fd (int fd); +int log_get_fd(void); +estream_t log_get_stream (void); + +#ifdef GPGRT_HAVE_MACRO_FUNCTION + void bug_at (const char *file, int line, const char *func) + GPGRT_ATTR_NORETURN; + void _log_assert (const char *expr, const char *file, int line, + const char *func) GPGRT_ATTR_NORETURN; +# define BUG() bug_at( __FILE__ , __LINE__, __FUNCTION__) +# define log_assert(expr) \ + ((expr) \ + ? (void) 0 \ + : _log_assert (#expr, __FILE__, __LINE__, __FUNCTION__)) +#else /*!GPGRT_HAVE_MACRO_FUNCTION*/ + void bug_at (const char *file, int line); + void _log_assert (const char *expr, const char *file, int line); +# define BUG() bug_at( __FILE__ , __LINE__ ) +# define log_assert(expr) \ + ((expr) \ + ? (void) 0 \ + : _log_assert (#expr, __FILE__, __LINE__)) +#endif /*!GPGRT_HAVE_MACRO_FUNCTION*/ + +/* Flag values for log_set_prefix. */ +#define GPGRT_LOG_WITH_PREFIX 1 +#define GPGRT_LOG_WITH_TIME 2 +#define GPGRT_LOG_WITH_PID 4 +#define GPGRT_LOG_RUN_DETACHED 256 +#define GPGRT_LOG_NO_REGISTRY 512 + +/* Log levels as used by log_log. */ +enum jnlib_log_levels { + GPGRT_LOG_BEGIN, + GPGRT_LOG_CONT, + GPGRT_LOG_INFO, + GPGRT_LOG_WARN, + GPGRT_LOG_ERROR, + GPGRT_LOG_FATAL, + GPGRT_LOG_BUG, + GPGRT_LOG_DEBUG +}; +void log_log (int level, const char *fmt, ...) GPGRT_ATTR_PRINTF(2,3); +void log_logv (int level, const char *fmt, va_list arg_ptr); +void log_logv_with_prefix (int level, const char *prefix, + const char *fmt, va_list arg_ptr); +void log_string (int level, const char *string); +void log_bug (const char *fmt, ...) GPGRT_ATTR_NR_PRINTF(1,2); +void log_fatal (const char *fmt, ...) GPGRT_ATTR_NR_PRINTF(1,2); +void log_error (const char *fmt, ...) GPGRT_ATTR_PRINTF(1,2); +void log_info (const char *fmt, ...) GPGRT_ATTR_PRINTF(1,2); +void log_debug (const char *fmt, ...) GPGRT_ATTR_PRINTF(1,2); +void log_debug_with_string (const char *string, const char *fmt, + ...) GPGRT_ATTR_PRINTF(2,3); +void log_printf (const char *fmt, ...) GPGRT_ATTR_PRINTF(1,2); +void log_flush (void); + +/* Print a hexdump of BUFFER. With FMT passed as NULL print just the + * raw dump, with FMT being an empty string, print a trailing + * linefeed, otherwise print an entire debug line with expanded FMT + * followed by the hexdump and a final LF. */ +void log_printhex (const void *buffer, size_t length, + const char *fmt, ...) GPGRT_ATTR_PRINTF(3,4); + +void log_clock (const char *string); + + +/* Some handy assertion macros which don't abort. */ + +#define return_if_fail(expr) do { \ + if (!(expr)) { \ + log_debug ("%s:%d: assertion '%s' failed\n", \ + __FILE__, __LINE__, #expr ); \ + return; \ + } } while (0) +#define return_null_if_fail(expr) do { \ + if (!(expr)) { \ + log_debug ("%s:%d: assertion '%s' failed\n", \ + __FILE__, __LINE__, #expr ); \ + return NULL; \ + } } while (0) +#define return_val_if_fail(expr,val) do { \ + if (!(expr)) { \ + log_debug ("%s:%d: assertion '%s' failed\n", \ + __FILE__, __LINE__, #expr ); \ + return (val); \ + } } while (0) +#define never_reached() do { \ + log_debug ("%s:%d: oops - should never get here\n", \ + __FILE__, __LINE__ ); \ + } while (0) + + +#endif /*GNUPG_COMMON_LOGGING_H*/ diff --git a/common/mapstrings.c b/common/mapstrings.c new file mode 100644 index 0000000..16da48e --- /dev/null +++ b/common/mapstrings.c @@ -0,0 +1,216 @@ +/* mapstrings.c - Static string mapping + * Copyright (C) 2014 Werner Koch + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdlib.h> +#include <errno.h> + +#include "util.h" +#include "stringhelp.h" +#include "membuf.h" + + +static struct { + const char *name; + const char *value; +} macros[] = { +#ifdef PACKAGE_BUGREPORT + { "EMAIL", PACKAGE_BUGREPORT }, +#else + { "EMAIL", "bug@example.org" }, +#endif + { "GNUPG", GNUPG_NAME }, + { "GPG", GPG_NAME }, + { "GPGSM", GPGSM_NAME }, + { "GPG_AGENT", GPG_AGENT_NAME }, + { "SCDAEMON", SCDAEMON_NAME }, + { "DIRMNGR", DIRMNGR_NAME }, + { "G13", G13_NAME }, + { "GPGCONF", GPGCONF_NAME }, + { "GPGTAR", GPGTAR_NAME } +}; + + + +/* A list to remember already done mappings. */ +struct mapping_s +{ + struct mapping_s *next; + const char *key; + const char *value; +}; +static struct mapping_s *mappings; + + +/* Similar to above but using two integers and a domain as key. */ +struct intmapping_s +{ + struct intmapping_s *next; + int key1; + int key2; + const char *string; + char domain[1]; +}; +static struct intmapping_s *intmappings; + + +/* If STRING has already been mapped, return the mapped string. If + not return NULL. */ +static const char * +already_mapped (const char *string) +{ + struct mapping_s *m; + + for (m=mappings; m; m = m->next) + if (m->key == string && !strcmp (m->key, string)) + return m->value; + return NULL; +} + + +/* Store NEWSTRING under key STRING and return NEWSTRING. */ +static const char * +store_mapping (const char *string, char *newstring) +{ + struct mapping_s *m; + + m = xmalloc (sizeof *m); + m->key = string; + m->value = newstring; + m->next = mappings; + mappings = m; + return newstring; +} + + +/* Find the first macro in STRING. Return a pointer to the + replacement value, set BEGPTR to the leading '@', and set ENDPTR to + the terminating '@'. If no macro is found return NULL. */ +const char * +find_macro (const char *string, const char **begptr, + const char **endptr) +{ + const char *s, *s2, *s3; + int idx; + + s = string; + if (!s) + return NULL; + + for (; (s2 = strchr (s, '@')); s = s2) + { + s2++; + if (*s2 >= 'A' && *s2 <= 'Z' && (s3 = (strchr (s2, '@')))) + { + for (idx=0; idx < DIM (macros); idx++) + if (strlen (macros[idx].name) == (s3 - s2) + && !memcmp (macros[idx].name, s2, (s3 - s2))) + { + *begptr = s2 - 1; + *endptr = s3; + return macros[idx].value; + } + } + } + return NULL; +} + + +/* If STRING includes known @FOO@ macros, replace these macros and + return a new static string. Warning: STRING must have been + allocated statically. Note that this function allocates memory + which will not be released (similar to gettext). */ +const char * +map_static_macro_string (const char *string) +{ + const char *s, *s2, *s3, *value; + membuf_t mb; + char *p; + + if ((s = already_mapped (string))) + return s; + s = string; + value = find_macro (s, &s2, &s3); + if (!value) + return string; /* No macros at all. */ + + init_membuf (&mb, strlen (string) + 100); + do + { + put_membuf (&mb, s, s2 - s); + put_membuf_str (&mb, value); + s = s3 + 1; + } + while ((value = find_macro (s, &s2, &s3))); + put_membuf_str (&mb, s); + put_membuf (&mb, "", 1); + + p = get_membuf_shrink (&mb, NULL); + if (!p) + log_fatal ("map_static_macro_string failed: %s\n", strerror (errno)); + + return store_mapping (string, p); +} + + +/* If a list of strings has already been mapped to a the tuple + * (DOMAIN,KEY1,KEY2) return that string. If not, create a mapping + * made up of the concatenation of the given strings. */ +const char * +map_static_strings (const char *domain, int key1, int key2, + const char *string1, ...) +{ + va_list arg_ptr; + struct intmapping_s *m; + + if (!string1 || !domain) + return ""; + + for (m = intmappings; m; m = m->next) + if (m->key1 == key1 && m->key2 == key2 && !strcmp (domain, m->domain)) + return m->string; + + m = xmalloc (sizeof *m + strlen (domain)); + strcpy (m->domain, domain); + m->key1 = key1; + m->key2 = key2; + + va_start (arg_ptr, string1); + m->string = vstrconcat (string1, arg_ptr); + va_end (arg_ptr); + if (!m->string) + log_fatal ("map_static_strings failed: %s\n", strerror (errno)); + + gpgrt_annotate_leaked_object (m->string); + gpgrt_annotate_leaked_object (m); + + m->next = intmappings; + intmappings = m; + return m->string; +} diff --git a/common/mbox-util.c b/common/mbox-util.c new file mode 100644 index 0000000..76255ba --- /dev/null +++ b/common/mbox-util.c @@ -0,0 +1,282 @@ +/* mbox-util.c - Mail address helper functions + * Copyright (C) 1998-2010 Free Software Foundation, Inc. + * Copyright (C) 1998-2015 Werner Koch + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +/* NB: GPGME uses the same code to reflect our idea on how to extract + * a mail address from a user id. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> + +#include "util.h" +#include "mbox-util.h" + + +static int +string_count_chr (const char *string, int c) +{ + int count; + + for (count=0; *string; string++ ) + if ( *string == c ) + count++; + return count; +} + +static int +mem_count_chr (const void *buffer, int c, size_t length) +{ + const char *s = buffer; + int count; + + for (count=0; length; length--, s++) + if (*s == c) + count++; + return count; +} + + +/* This is a case-sensitive version of our memistr. I wonder why no + standard function memstr exists but I better do not use the name + memstr to avoid future conflicts. */ +static const char * +my_memstr (const void *buffer, size_t buflen, const char *sub) +{ + const unsigned char *buf = buffer; + const unsigned char *t = (const unsigned char *)buf; + const unsigned char *s = (const unsigned char *)sub; + size_t n = buflen; + + for ( ; n ; t++, n-- ) + { + if (*t == *s) + { + for (buf = t++, buflen = n--, s++; n && *t ==*s; t++, s++, n--) + ; + if (!*s) + return (const char*)buf; + t = (const unsigned char *)buf; + s = (const unsigned char *)sub ; + n = buflen; + } + } + return NULL; +} + + + +static int +string_has_ctrl_or_space (const char *string) +{ + for (; *string; string++ ) + if (!(*string & 0x80) && *string <= 0x20) + return 1; + return 0; +} + + +/* Return true if STRING has two consecutive '.' after an '@' + sign. */ +static int +has_dotdot_after_at (const char *string) +{ + string = strchr (string, '@'); + if (!string) + return 0; /* No at-sign. */ + string++; + return !!strstr (string, ".."); +} + + +/* Check whether BUFFER has characters not valid in an RFC-822 + address. LENGTH gives the length of BUFFER. + + To cope with OpenPGP we ignore non-ascii characters so that for + example umlauts are legal in an email address. An OpenPGP user ID + must be utf-8 encoded but there is no strict requirement for + RFC-822. Thus to avoid IDNA encoding we put the address verbatim + as utf-8 into the user ID under the assumption that mail programs + handle IDNA at a lower level and take OpenPGP user IDs as utf-8. + Note that we can't do an utf-8 encoding checking here because in + keygen.c this function is called with the native encoding and + native to utf-8 encoding is only done later. */ +int +has_invalid_email_chars (const void *buffer, size_t length) +{ + const unsigned char *s = buffer; + int at_seen=0; + const char *valid_chars= + "01234567890_-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + for ( ; length && *s; length--, s++ ) + { + if ((*s & 0x80)) + continue; /* We only care about ASCII. */ + if (*s == '@') + at_seen=1; + else if (!at_seen && !(strchr (valid_chars, *s) + || strchr ("!#$%&'*+/=?^`{|}~", *s))) + return 1; + else if (at_seen && !strchr (valid_chars, *s)) + return 1; + } + return 0; +} + + +/* Same as is_valid_mailbox (see below) but operates on non-nul + terminated buffer. */ +int +is_valid_mailbox_mem (const void *name_arg, size_t namelen) +{ + const char *name = name_arg; + + return !( !name + || !namelen + || has_invalid_email_chars (name, namelen) + || mem_count_chr (name, '@', namelen) != 1 + || *name == '@' + || name[namelen-1] == '@' + || name[namelen-1] == '.' + || my_memstr (name, namelen, "..")); +} + + +/* Check whether NAME represents a valid mailbox according to + RFC822. Returns true if so. */ +int +is_valid_mailbox (const char *name) +{ + return name? is_valid_mailbox_mem (name, strlen (name)) : 0; +} + + +/* Return the mailbox (local-part@domain) form a standard user id. + All plain ASCII characters in the result are converted to + lowercase. Caller must free the result. Returns NULL if no valid + mailbox was found (or we are out of memory). */ +char * +mailbox_from_userid (const char *userid) +{ + const char *s, *s_end; + size_t len; + char *result = NULL; + + s = strchr (userid, '<'); + if (s) + { + /* Seems to be a standard user id. */ + s++; + s_end = strchr (s, '>'); + if (s_end && s_end > s) + { + len = s_end - s; + result = xtrymalloc (len + 1); + if (!result) + return NULL; /* Ooops - out of core. */ + strncpy (result, s, len); + result[len] = 0; + /* Apply some basic checks on the address. We do not use + is_valid_mailbox because those checks are too strict. */ + if (string_count_chr (result, '@') != 1 /* Need exactly one '@. */ + || *result == '@' /* local-part missing. */ + || result[len-1] == '@' /* domain missing. */ + || result[len-1] == '.' /* ends with a dot. */ + || string_has_ctrl_or_space (result) + || has_dotdot_after_at (result)) + { + xfree (result); + result = NULL; + errno = EINVAL; + } + } + else + errno = EINVAL; + } + else if (is_valid_mailbox (userid)) + { + /* The entire user id is a mailbox. Return that one. Note that + this fallback method has some restrictions on the valid + syntax of the mailbox. However, those who want weird + addresses should know about it and use the regular <...> + syntax. */ + result = xtrystrdup (userid); + } + else + errno = EINVAL; + + return result? ascii_strlwr (result): NULL; +} + + +/* Check whether UID is a valid standard user id of the form + "Heinrich Heine <heinrichh@duesseldorf.de>" + and return true if this is the case. */ +int +is_valid_user_id (const char *uid) +{ + if (!uid || !*uid) + return 0; + + return 1; +} + + +/* Returns true if STRING is a valid domain name according to the LDH + * rule. */ +int +is_valid_domain_name (const char *string) +{ + static char const ldh_chars[] = + "01234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-"; + const char *s; + + /* Note that we do not check the length limit of a label or the + * entire name */ + + for (s=string; *s; s++) + if (*s == '.') + { + if (string == s) + return 0; /* Dot at the start of the string. */ + /* (may also be at the end like in ".") */ + if (s[1] == '.') + return 0; /* No - double dot. */ + } + else if (!strchr (ldh_chars, *s)) + return 0; + else if (*s == '-') + { + if (string == s) + return 0; /* Leading hyphen. */ + if (s[-1] == '.') + return 0; /* Hyphen at begin of a label. */ + if (s[1] == '.') + return 0; /* Hyphen at start of a label. */ + if (!s[1]) + return 0; /* Trailing hyphen. */ + } + + return !!*string; +} diff --git a/common/mbox-util.h b/common/mbox-util.h new file mode 100644 index 0000000..7355cee --- /dev/null +++ b/common/mbox-util.h @@ -0,0 +1,30 @@ +/* mbox-util.h - Defs for mail address helper functions + * Copyright (C) 2015 Werner Koch + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ +#ifndef GNUPG_COMMON_MBOX_UTIL_H +#define GNUPG_COMMON_MBOX_UTIL_H + +int has_invalid_email_chars (const void *buffer, size_t length); +int is_valid_mailbox (const char *name); +int is_valid_mailbox_mem (const void *buffer, size_t length); +char *mailbox_from_userid (const char *userid); +int is_valid_user_id (const char *uid); +int is_valid_domain_name (const char *string); + + +#endif /*GNUPG_COMMON_MBOX_UTIL_H*/ diff --git a/common/membuf.c b/common/membuf.c new file mode 100644 index 0000000..009fbc3 --- /dev/null +++ b/common/membuf.c @@ -0,0 +1,233 @@ +/* membuf.c - A simple implementation of a dynamic buffer. + * Copyright (C) 2001, 2003, 2009, 2011 Free Software Foundation, Inc. + * Copyright (C) 2013 Werner Koch + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdlib.h> +#include <errno.h> +#include <stdarg.h> + +#include "util.h" +#include "membuf.h" + + +/* A simple implementation of a dynamic buffer. Use init_membuf() to + create a buffer, put_membuf to append bytes and get_membuf to + release and return the buffer. Allocation errors are detected but + only returned at the final get_membuf(), this helps not to clutter + the code with out of core checks. */ + +void +init_membuf (membuf_t *mb, int initiallen) +{ + mb->len = 0; + mb->size = initiallen; + mb->out_of_core = 0; + mb->buf = xtrymalloc (initiallen); + if (!mb->buf) + mb->out_of_core = errno; +} + +/* Same as init_membuf but allocates the buffer in secure memory. */ +void +init_membuf_secure (membuf_t *mb, int initiallen) +{ + mb->len = 0; + mb->size = initiallen; + mb->out_of_core = 0; + mb->buf = xtrymalloc_secure (initiallen); + if (!mb->buf) + mb->out_of_core = errno; +} + + +/* Shift the content of the membuf MB by AMOUNT bytes. The next + operation will then behave as if AMOUNT bytes had not been put into + the buffer. If AMOUNT is greater than the actual accumulated + bytes, the membuf is basically reset to its initial state. */ +void +clear_membuf (membuf_t *mb, size_t amount) +{ + /* No need to clear if we are already out of core. */ + if (mb->out_of_core) + return; + if (amount >= mb->len) + mb->len = 0; + else + { + mb->len -= amount; + memmove (mb->buf, mb->buf+amount, mb->len); + } +} + + +void +put_membuf (membuf_t *mb, const void *buf, size_t len) +{ + if (mb->out_of_core || !len) + return; + + if (mb->len + len >= mb->size) + { + char *p; + + mb->size += len + 1024; + p = xtryrealloc (mb->buf, mb->size); + if (!p) + { + mb->out_of_core = errno ? errno : ENOMEM; + /* Wipe out what we already accumulated. This is required + in case we are storing sensitive data here. The membuf + API does not provide another way to cleanup after an + error. */ + wipememory (mb->buf, mb->len); + return; + } + mb->buf = p; + } + if (buf) + memcpy (mb->buf + mb->len, buf, len); + else + memset (mb->buf + mb->len, 0, len); + mb->len += len; +} + + +/* A variant of put_membuf accepting a void * and returning a + gpg_error_t (which will always return 0) to be used as a generic + callback handler. This function also allows buffer to be NULL. */ +gpg_error_t +put_membuf_cb (void *opaque, const void *buf, size_t len) +{ + membuf_t *data = opaque; + + if (buf) + put_membuf (data, buf, len); + return 0; +} + + +void +put_membuf_str (membuf_t *mb, const char *string) +{ + put_membuf (mb, string, strlen (string)); +} + + +void +put_membuf_printf (membuf_t *mb, const char *format, ...) +{ + int rc; + va_list arg_ptr; + char *buf; + + va_start (arg_ptr, format); + rc = gpgrt_vasprintf (&buf, format, arg_ptr); + if (rc < 0) + mb->out_of_core = errno ? errno : ENOMEM; + va_end (arg_ptr); + if (rc >= 0) + { + put_membuf (mb, buf, strlen (buf)); + xfree (buf); + } +} + + +void * +get_membuf (membuf_t *mb, size_t *len) +{ + char *p; + + if (mb->out_of_core) + { + if (mb->buf) + { + wipememory (mb->buf, mb->len); + xfree (mb->buf); + mb->buf = NULL; + } + gpg_err_set_errno (mb->out_of_core); + return NULL; + } + + p = mb->buf; + if (len) + *len = mb->len; + mb->buf = NULL; + mb->out_of_core = ENOMEM; /* hack to make sure it won't get reused. */ + return p; +} + + +/* Same as get_membuf but shrinks the reallocated space to the + required size. */ +void * +get_membuf_shrink (membuf_t *mb, size_t *len) +{ + void *p, *pp; + size_t dummylen; + + if (!len) + len = &dummylen; + + p = get_membuf (mb, len); + if (!p) + return NULL; + if (*len) + { + pp = xtryrealloc (p, *len); + if (pp) + p = pp; + } + + return p; +} + + +/* Peek at the membuf MB. On success a pointer to the buffer is + returned which is valid until the next operation on MB. If LEN is + not NULL the current LEN of the buffer is stored there. On error + NULL is returned and ERRNO is set. */ +const void * +peek_membuf (membuf_t *mb, size_t *len) +{ + const char *p; + + if (mb->out_of_core) + { + gpg_err_set_errno (mb->out_of_core); + return NULL; + } + + p = mb->buf; + if (len) + *len = mb->len; + return p; +} diff --git a/common/membuf.h b/common/membuf.h new file mode 100644 index 0000000..1497bcd --- /dev/null +++ b/common/membuf.h @@ -0,0 +1,64 @@ +/* membuf.h - A simple implementation of a dynamic buffer + * Copyright (C) 2001, 2003 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef GNUPG_COMMON_MEMBUF_H +#define GNUPG_COMMON_MEMBUF_H + +#include "mischelp.h" + +/* The definition of the structure is private, we only need it here, + so it can be allocated on the stack. */ +struct private_membuf_s +{ + size_t len; + size_t size; + char *buf; + int out_of_core; +}; + +typedef struct private_membuf_s membuf_t; + +/* Return the current length of the membuf. */ +#define get_membuf_len(a) ((a)->len) +#define is_membuf_ready(a) ((a)->buf || (a)->out_of_core) +#define MEMBUF_ZERO { 0, 0, NULL, 0} + +void init_membuf (membuf_t *mb, int initiallen); +void init_membuf_secure (membuf_t *mb, int initiallen); +void clear_membuf (membuf_t *mb, size_t amount); +void put_membuf (membuf_t *mb, const void *buf, size_t len); +gpg_error_t put_membuf_cb (void *opaque, const void *buf, size_t len); +void put_membuf_str (membuf_t *mb, const char *string); +void put_membuf_printf (membuf_t *mb, const char *format, + ...) GPGRT_ATTR_PRINTF(2,3); +void *get_membuf (membuf_t *mb, size_t *len); +void *get_membuf_shrink (membuf_t *mb, size_t *len); +const void *peek_membuf (membuf_t *mb, size_t *len); + +#endif /*GNUPG_COMMON_MEMBUF_H*/ diff --git a/common/miscellaneous.c b/common/miscellaneous.c new file mode 100644 index 0000000..c377554 --- /dev/null +++ b/common/miscellaneous.c @@ -0,0 +1,842 @@ +/* miscellaneous.c - Stuff not fitting elsewhere + * Copyright (C) 2003, 2006 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdlib.h> +#include <limits.h> +#include <errno.h> + +#include "util.h" +#include "iobuf.h" +#include "i18n.h" + +/* Used by libgcrypt for logging. */ +static void +my_gcry_logger (void *dummy, int level, const char *fmt, va_list arg_ptr) +{ + (void)dummy; + + /* Map the log levels. */ + switch (level) + { + case GCRY_LOG_CONT: level = GPGRT_LOG_CONT; break; + case GCRY_LOG_INFO: level = GPGRT_LOG_INFO; break; + case GCRY_LOG_WARN: level = GPGRT_LOG_WARN; break; + case GCRY_LOG_ERROR:level = GPGRT_LOG_ERROR; break; + case GCRY_LOG_FATAL:level = GPGRT_LOG_FATAL; break; + case GCRY_LOG_BUG: level = GPGRT_LOG_BUG; break; + case GCRY_LOG_DEBUG:level = GPGRT_LOG_DEBUG; break; + default: level = GPGRT_LOG_ERROR; break; + } + log_logv (level, fmt, arg_ptr); +} + + +/* This function is called by libgcrypt on a fatal error. */ +static void +my_gcry_fatalerror_handler (void *opaque, int rc, const char *text) +{ + (void)opaque; + + log_fatal ("libgcrypt problem: %s\n", text ? text : gpg_strerror (rc)); + abort (); +} + + +/* This function is called by libgcrypt if it ran out of core and + there is no way to return that error to the caller. We do our own + function here to make use of our logging functions. */ +static int +my_gcry_outofcore_handler (void *opaque, size_t req_n, unsigned int flags) +{ + static int been_here; /* Used to protect against recursive calls. */ + + (void)opaque; + + if (!been_here) + { + been_here = 1; + if ( (flags & 1) ) + log_fatal (_("out of core in secure memory " + "while allocating %lu bytes"), (unsigned long)req_n); + else + log_fatal (_("out of core while allocating %lu bytes"), + (unsigned long)req_n); + } + return 0; /* Let libgcrypt call its own fatal error handler. + Actually this will turn out to be + my_gcry_fatalerror_handler. */ +} + + +/* Setup libgcrypt to use our own logging functions. Should be used + early at startup. */ +void +setup_libgcrypt_logging (void) +{ + gcry_set_log_handler (my_gcry_logger, NULL); + gcry_set_fatalerror_handler (my_gcry_fatalerror_handler, NULL); + gcry_set_outofcore_handler (my_gcry_outofcore_handler, NULL); +} + + +/* Print an out of core message and let the process die. The printed + * error is taken from ERRNO. */ +void +xoutofcore (void) +{ + gpg_error_t err = gpg_error_from_syserror (); + log_fatal (_("error allocating enough memory: %s\n"), gpg_strerror (err)); + abort (); /* Never called; just to make the compiler happy. */ +} + + +/* This is safe version of realloc useful for reallocing a calloced + * array. There are two ways to call it: The first example + * reallocates the array A to N elements each of SIZE but does not + * clear the newly allocated elements: + * + * p = gpgrt_reallocarray (a, n, n, nsize); + * + * Note that when NOLD is larger than N no cleaning is needed anyway. + * The second example reallocates an array of size NOLD to N elements + * each of SIZE but clear the newly allocated elements: + * + * p = gpgrt_reallocarray (a, nold, n, nsize); + * + * Note that gnupg_reallocarray (NULL, 0, n, nsize) is equivalent to + * gcry_calloc (n, nsize). + */ +void * +gnupg_reallocarray (void *a, size_t oldnmemb, size_t nmemb, size_t size) +{ + size_t oldbytes, bytes; + char *p; + + bytes = nmemb * size; /* size_t is unsigned so the behavior on overflow + * is defined. */ + if (size && bytes / size != nmemb) + { + gpg_err_set_errno (ENOMEM); + return NULL; + } + + p = gcry_realloc (a, bytes); + if (p && oldnmemb < nmemb) + { + /* OLDNMEMBS is lower than NMEMB thus the user asked for a + calloc. Clear all newly allocated members. */ + oldbytes = oldnmemb * size; + if (size && oldbytes / size != oldnmemb) + { + xfree (p); + gpg_err_set_errno (ENOMEM); + return NULL; + } + memset (p + oldbytes, 0, bytes - oldbytes); + } + return p; +} + + +/* Die-on-error version of gnupg_reallocarray. */ +void * +xreallocarray (void *a, size_t oldnmemb, size_t nmemb, size_t size) +{ + void *p = gnupg_reallocarray (a, oldnmemb, nmemb, size); + if (!p) + xoutofcore (); + return p; +} + + +/* A wrapper around gcry_cipher_algo_name to return the string + "AES-128" instead of "AES". Given that we have an alias in + libgcrypt for it, it does not harm to too much to return this other + string. Some users complained that we print "AES" but "AES192" + and "AES256". We can't fix that in libgcrypt but it is pretty + safe to do it in an application. */ +const char * +gnupg_cipher_algo_name (int algo) +{ + const char *s; + + s = gcry_cipher_algo_name (algo); + if (!strcmp (s, "AES")) + s = "AES128"; + return s; +} + + +void +obsolete_option (const char *configname, unsigned int configlineno, + const char *name) +{ + if (configname) + log_info (_("%s:%u: obsolete option \"%s\" - it has no effect\n"), + configname, configlineno, name); + else + log_info (_("WARNING: \"%s%s\" is an obsolete option - it has no effect\n"), + "--", name); +} + + +/* Decide whether the filename is stdout or a real filename and return + * an appropriate string. */ +const char * +print_fname_stdout (const char *s) +{ + if( !s || (*s == '-' && !s[1]) ) + return "[stdout]"; + return s; +} + + +/* Decide whether the filename is stdin or a real filename and return + * an appropriate string. */ +const char * +print_fname_stdin (const char *s) +{ + if( !s || (*s == '-' && !s[1]) ) + return "[stdin]"; + return s; +} + + +static int +do_print_utf8_buffer (estream_t stream, + const void *buffer, size_t length, + const char *delimiters, size_t *bytes_written) +{ + const char *p = buffer; + size_t i; + + /* We can handle plain ascii simpler, so check for it first. */ + for (i=0; i < length; i++ ) + { + if ( (p[i] & 0x80) ) + break; + } + if (i < length) + { + int delim = delimiters? *delimiters : 0; + char *buf; + int ret; + + /*(utf8 conversion already does the control character quoting). */ + buf = utf8_to_native (p, length, delim); + if (bytes_written) + *bytes_written = strlen (buf); + ret = es_fputs (buf, stream); + xfree (buf); + return ret == EOF? ret : (int)i; + } + else + return es_write_sanitized (stream, p, length, delimiters, bytes_written); +} + + +void +print_utf8_buffer3 (estream_t stream, const void *p, size_t n, + const char *delim) +{ + do_print_utf8_buffer (stream, p, n, delim, NULL); +} + + +void +print_utf8_buffer2 (estream_t stream, const void *p, size_t n, int delim) +{ + char tmp[2]; + + tmp[0] = delim; + tmp[1] = 0; + do_print_utf8_buffer (stream, p, n, tmp, NULL); +} + + +void +print_utf8_buffer (estream_t stream, const void *p, size_t n) +{ + do_print_utf8_buffer (stream, p, n, NULL, NULL); +} + + +void +print_utf8_string (estream_t stream, const char *p) +{ + if (!p) + p = ""; + do_print_utf8_buffer (stream, p, strlen (p), NULL, NULL); +} + + +/* Write LENGTH bytes of BUFFER to FP as a hex encoded string. + RESERVED must be 0. */ +void +print_hexstring (FILE *fp, const void *buffer, size_t length, int reserved) +{ +#define tohex(n) ((n) < 10 ? ((n) + '0') : (((n) - 10) + 'A')) + const unsigned char *s; + + (void)reserved; + + for (s = buffer; length; s++, length--) + { + putc ( tohex ((*s>>4)&15), fp); + putc ( tohex (*s&15), fp); + } +#undef tohex +} + + +/* Create a string from the buffer P_ARG of length N which is suitable + * for printing. Caller must release the created string using xfree. + * On error ERRNO is set and NULL returned. Errors are only possible + * due to malloc failure. */ +char * +try_make_printable_string (const void *p_arg, size_t n, int delim) +{ + const unsigned char *p = p_arg; + size_t save_n, buflen; + const unsigned char *save_p; + char *buffer, *d; + + /* First count length. */ + for (save_n = n, save_p = p, buflen=1 ; n; n--, p++ ) + { + if ( *p < 0x20 || *p == 0x7f || *p == delim || (delim && *p=='\\')) + { + if ( *p=='\n' || *p=='\r' || *p=='\f' + || *p=='\v' || *p=='\b' || !*p ) + buflen += 2; + else + buflen += 5; + } + else + buflen++; + } + p = save_p; + n = save_n; + /* And now make the string */ + d = buffer = xtrymalloc (buflen); + for ( ; n; n--, p++ ) + { + if (*p < 0x20 || *p == 0x7f || *p == delim || (delim && *p=='\\')) { + *d++ = '\\'; + if( *p == '\n' ) + *d++ = 'n'; + else if( *p == '\r' ) + *d++ = 'r'; + else if( *p == '\f' ) + *d++ = 'f'; + else if( *p == '\v' ) + *d++ = 'v'; + else if( *p == '\b' ) + *d++ = 'b'; + else if( !*p ) + *d++ = '0'; + else { + sprintf(d, "x%02x", *p ); + d += 3; + } + } + else + *d++ = *p; + } + *d = 0; + return buffer; +} + + +/* Same as try_make_printable_string but terminates the process on + * memory shortage. */ +char * +make_printable_string (const void *p, size_t n, int delim ) +{ + char *string = try_make_printable_string (p, n, delim); + if (!string) + xoutofcore (); + return string; +} + + +/* Decode the C formatted string SRC and return the result in a newly + * allocated buffer. In error returns NULL and sets ERRNO. */ +char * +decode_c_string (const char *src) +{ + char *buffer, *dst; + int val; + + /* The converted string will never be larger than the original + string. */ + buffer = dst = xtrymalloc (strlen (src) + 1); + if (!buffer) + return NULL; + + while (*src) + { + if (*src != '\\') + { + *dst++ = *src++; + continue; + } + +#define DECODE_ONE(_m,_r) case _m: src += 2; *dst++ = _r; break; + + switch (src[1]) + { + DECODE_ONE ('n', '\n'); + DECODE_ONE ('r', '\r'); + DECODE_ONE ('f', '\f'); + DECODE_ONE ('v', '\v'); + DECODE_ONE ('b', '\b'); + DECODE_ONE ('t', '\t'); + DECODE_ONE ('\\', '\\'); + DECODE_ONE ('\'', '\''); + DECODE_ONE ('\"', '\"'); + + case 'x': + val = hextobyte (src+2); + if (val == -1) /* Bad coding, keep as is. */ + { + *dst++ = *src++; + *dst++ = *src++; + if (*src) + *dst++ = *src++; + if (*src) + *dst++ = *src++; + } + else if (!val) + { + /* A binary zero is not representable in a C string thus + * we keep the C-escaping. Note that this will also + * never be larger than the source string. */ + *dst++ = '\\'; + *dst++ = '0'; + src += 4; + } + else + { + *(unsigned char *)dst++ = val; + src += 4; + } + break; + + default: /* Bad coding; keep as is.. */ + *dst++ = *src++; + *dst++ = *src++; + break; + } +#undef DECODE_ONE + } + *dst++ = 0; + + return buffer; +} + + +/* Check whether (BUF,LEN) is valid header for an OpenPGP compressed + * packet. LEN should be at least 6. */ +static int +is_openpgp_compressed_packet (unsigned char *buf, size_t len) +{ + int c, ctb, pkttype; + int lenbytes; + + ctb = *buf++; len--; + if (!(ctb & 0x80)) + return 0; /* Invalid packet. */ + + if ((ctb & 0x40)) /* New style (OpenPGP) CTB. */ + { + pkttype = (ctb & 0x3f); + if (!len) + return 0; /* Expected first length octet missing. */ + c = *buf++; len--; + if (c < 192) + ; + else if (c < 224) + { + if (!len) + return 0; /* Expected second length octet missing. */ + } + else if (c == 255) + { + if (len < 4) + return 0; /* Expected length octets missing */ + } + } + else /* Old style CTB. */ + { + pkttype = (ctb>>2)&0xf; + lenbytes = ((ctb&3)==3)? 0 : (1<<(ctb & 3)); + if (len < lenbytes) + return 0; /* Not enough length bytes. */ + } + + return (pkttype == 8); +} + + + +/* + * Check if the file is compressed. + */ +int +is_file_compressed (const char *s, int *ret_rc) +{ + iobuf_t a; + byte buf[6]; + int i; + int rc = 0; + int overflow; + + struct magic_compress_s { + size_t len; + byte magic[4]; + } magic[] = { + { 3, { 0x42, 0x5a, 0x68, 0x00 } }, /* bzip2 */ + { 3, { 0x1f, 0x8b, 0x08, 0x00 } }, /* gzip */ + { 4, { 0x50, 0x4b, 0x03, 0x04 } }, /* (pk)zip */ + }; + + if ( iobuf_is_pipe_filename (s) || !ret_rc ) + return 0; /* We can't check stdin or no file was given */ + + a = iobuf_open( s ); + if ( a == NULL ) { + *ret_rc = gpg_error_from_syserror (); + return 0; + } + iobuf_ioctl (a, IOBUF_IOCTL_NO_CACHE, 1, NULL); + + if ( iobuf_get_filelength( a, &overflow ) < 6 && !overflow) { + *ret_rc = 0; + goto leave; + } + + if ( iobuf_read( a, buf, 6 ) == -1 ) { + *ret_rc = a->error; + goto leave; + } + + for ( i = 0; i < DIM( magic ); i++ ) { + if ( !memcmp( buf, magic[i].magic, magic[i].len ) ) { + *ret_rc = 0; + rc = 1; + goto leave; + } + } + + if (is_openpgp_compressed_packet (buf, 6)) + { + *ret_rc = 0; + rc = 1; + } + + leave: + iobuf_close( a ); + return rc; +} + + +/* Try match against each substring of multistr, delimited by | */ +int +match_multistr (const char *multistr,const char *match) +{ + do + { + size_t seglen = strcspn (multistr,"|"); + if (!seglen) + break; + /* Using the localized strncasecmp! */ + if (strncasecmp(multistr,match,seglen)==0) + return 1; + multistr += seglen; + if (*multistr == '|') + multistr++; + } + while (*multistr); + + return 0; +} + + + +/* Parse the first portion of the version number S and store it at + NUMBER. On success, the function returns a pointer into S starting + with the first character, which is not part of the initial number + portion; on failure, NULL is returned. */ +static const char* +parse_version_number (const char *s, int *number) +{ + int val = 0; + + if (*s == '0' && digitp (s+1)) + return NULL; /* Leading zeros are not allowed. */ + for (; digitp (s); s++ ) + { + val *= 10; + val += *s - '0'; + } + *number = val; + return val < 0? NULL : s; +} + +/* Break up the complete string representation of the version number S, + which is expected to have this format: + + <major number>.<minor number>.<micro number><patch level>. + + The major, minor and micro number components will be stored at + MAJOR, MINOR and MICRO. On success, a pointer to the last + component, the patch level, will be returned; on failure, NULL will + be returned. */ +static const char * +parse_version_string (const char *s, int *major, int *minor, int *micro) +{ + s = parse_version_number (s, major); + if (!s || *s != '.') + return NULL; + s++; + s = parse_version_number (s, minor); + if (!s || *s != '.') + return NULL; + s++; + s = parse_version_number (s, micro); + if (!s) + return NULL; + return s; /* Patchlevel. */ +} + +/* Return true if version string is at least version B. */ +int +gnupg_compare_version (const char *a, const char *b) +{ + int a_major, a_minor, a_micro; + int b_major, b_minor, b_micro; + const char *a_plvl, *b_plvl; + + if (!a || !b) + return 0; + + /* Parse version A. */ + a_plvl = parse_version_string (a, &a_major, &a_minor, &a_micro); + if (!a_plvl ) + return 0; /* Invalid version number. */ + + /* Parse version B. */ + b_plvl = parse_version_string (b, &b_major, &b_minor, &b_micro); + if (!b_plvl ) + return 0; /* Invalid version number. */ + + /* Compare version numbers. */ + return (a_major > b_major + || (a_major == b_major && a_minor > b_minor) + || (a_major == b_major && a_minor == b_minor + && a_micro > b_micro) + || (a_major == b_major && a_minor == b_minor + && a_micro == b_micro + && strcmp (a_plvl, b_plvl) >= 0)); +} + + + +/* Parse an --debug style argument. We allow the use of number values + * in the usual C notation or a string with comma separated keywords. + * + * Returns: 0 on success or -1 and ERRNO set on error. On success the + * supplied variable is updated by the parsed flags. + * + * If STRING is NULL the enabled debug flags are printed. + * + * See doc/DETAILS for a summary of used debug options. + */ +int +parse_debug_flag (const char *string, unsigned int *debugvar, + const struct debug_flags_s *flags) + +{ + unsigned long result = 0; + int i, j; + + if (!string) + { + if (debugvar) + { + log_info ("enabled debug flags:"); + for (i=0; flags[i].name; i++) + if ((*debugvar & flags[i].flag)) + log_printf (" %s", flags[i].name); + log_printf ("\n"); + } + return 0; + } + + while (spacep (string)) + string++; + if (*string == '-') + { + errno = EINVAL; + return -1; + } + + if (!strcmp (string, "?") || !strcmp (string, "help")) + { + log_info ("available debug flags:\n"); + for (i=0; flags[i].name; i++) + log_info (" %5u %s\n", flags[i].flag, flags[i].name); + if (flags[i].flag != 77) + exit (0); + } + else if (digitp (string)) + { + errno = 0; + result = strtoul (string, NULL, 0); + if (result == ULONG_MAX && errno == ERANGE) + return -1; + } + else + { + char **words; + words = strtokenize (string, ","); + if (!words) + return -1; + for (i=0; words[i]; i++) + { + if (*words[i]) + { + for (j=0; flags[j].name; j++) + if (!strcmp (words[i], flags[j].name)) + { + result |= flags[j].flag; + break; + } + if (!flags[j].name) + { + if (!strcmp (words[i], "none")) + { + *debugvar = 0; + result = 0; + } + else if (!strcmp (words[i], "all")) + result = ~0; + else + log_info (_("unknown debug flag '%s' ignored\n"), words[i]); + } + } + } + xfree (words); + } + + *debugvar |= result; + return 0; +} + + + +/* Parse an --comaptibility_flags style argument consisting of comma + * separated strings. + * + * Returns: 0 on success or -1 and ERRNO set on error. On success the + * supplied variable is updated by the parsed flags. + * + * If STRING is NULL the enabled flags are printed. + */ +int +parse_compatibility_flags (const char *string, unsigned int *flagvar, + const struct compatibility_flags_s *flags) + +{ + unsigned long result = 0; + int i, j; + + if (!string) + { + if (flagvar) + { + log_info ("enabled compatibility flags:"); + for (i=0; flags[i].name; i++) + if ((*flagvar & flags[i].flag)) + log_printf (" %s", flags[i].name); + log_printf ("\n"); + } + return 0; + } + + while (spacep (string)) + string++; + + if (!strcmp (string, "?") || !strcmp (string, "help")) + { + log_info ("available compatibility flags:\n"); + for (i=0; flags[i].name; i++) + log_info (" %s\n", flags[i].name); + if (flags[i].flag != 77) + exit (0); + } + else + { + char **words; + words = strtokenize (string, ","); + if (!words) + return -1; + for (i=0; words[i]; i++) + { + if (*words[i]) + { + for (j=0; flags[j].name; j++) + if (!strcmp (words[i], flags[j].name)) + { + result |= flags[j].flag; + break; + } + if (!flags[j].name) + { + if (!strcmp (words[i], "none")) + { + *flagvar = 0; + result = 0; + } + else if (!strcmp (words[i], "all")) + result = ~0; + else + log_info ("unknown compatibility flag '%s' ignored\n", + words[i]); + } + } + } + xfree (words); + } + + *flagvar |= result; + return 0; +} diff --git a/common/mischelp.c b/common/mischelp.c new file mode 100644 index 0000000..ee85002 --- /dev/null +++ b/common/mischelp.c @@ -0,0 +1,205 @@ +/* mischelp.c - Miscellaneous helper functions + * Copyright (C) 1998, 2000, 2001, 2006, 2007 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute and/or modify this + * part of GnuPG under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * GnuPG is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copies of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#ifdef HAVE_W32_SYSTEM +# define WIN32_LEAN_AND_MEAN +# include <windows.h> +#else /*!HAVE_W32_SYSTEM*/ +# include <sys/types.h> +# include <sys/stat.h> +# include <unistd.h> +#endif /*!HAVE_W32_SYSTEM*/ +#include <errno.h> + +#include "util.h" +#include "common-defs.h" +#include "stringhelp.h" +#include "utf8conv.h" +#include "mischelp.h" + + +void +wipememory (void *ptr, size_t len) +{ +#if defined(HAVE_W32_SYSTEM) && defined(SecureZeroMemory) + SecureZeroMemory (ptr, len); +#elif defined(HAVE_EXPLICIT_BZERO) + explicit_bzero (ptr, len); +#else + /* Prevent compiler from optimizing away the call to memset by accessing + memset through volatile pointer. */ + static void *(*volatile memset_ptr)(void *, int, size_t) = (void *)memset; + memset_ptr (ptr, 0, len); +#endif +} + + +/* Check whether the files NAME1 and NAME2 are identical. This is for + example achieved by comparing the inode numbers of the files. */ +int +same_file_p (const char *name1, const char *name2) +{ + int yes; + + /* First try a shortcut. */ + if (!compare_filenames (name1, name2)) + yes = 1; + else + { +#ifdef HAVE_W32_SYSTEM + HANDLE file1, file2; + BY_HANDLE_FILE_INFORMATION info1, info2; + wchar_t *wname; + + wname = gpgrt_fname_to_wchar (name1); + if (wname) + { + file1 = CreateFileW (wname, 0, 0, NULL, OPEN_EXISTING, 0, NULL); + xfree (wname); + } + else + file1 = INVALID_HANDLE_VALUE; + + if (file1 == INVALID_HANDLE_VALUE) + yes = 0; /* If we can't open the file, it is not the same. */ + else + { + wname = gpgrt_fname_to_wchar (name2); + if (wname) + { + file2 = CreateFileW (wname, 0, 0, NULL, OPEN_EXISTING, 0, NULL); + xfree (wname); + } + else + file2 = INVALID_HANDLE_VALUE; + + if (file2 == INVALID_HANDLE_VALUE) + yes = 0; /* If we can't open the file, it is not the same. */ + else + { + yes = (GetFileInformationByHandle (file1, &info1) + && GetFileInformationByHandle (file2, &info2) + && info1.dwVolumeSerialNumber==info2.dwVolumeSerialNumber + && info1.nFileIndexHigh == info2.nFileIndexHigh + && info1.nFileIndexLow == info2.nFileIndexLow); + CloseHandle (file2); + } + CloseHandle (file1); + } +#else /*!HAVE_W32_SYSTEM*/ + struct stat info1, info2; + + yes = (!stat (name1, &info1) && !stat (name2, &info2) + && info1.st_dev == info2.st_dev && info1.st_ino == info2.st_ino); +#endif /*!HAVE_W32_SYSTEM*/ + } + return yes; +} + + +/* + timegm() is a GNU function that might not be available everywhere. + It's basically the inverse of gmtime() - you give it a struct tm, + and get back a time_t. It differs from mktime() in that it handles + the case where the struct tm is UTC and the local environment isn't. + + Note, that this replacement implementation might not be thread-safe! + + Some BSDs don't handle the putenv("foo") case properly, so we use + unsetenv if the platform has it to remove environment variables. +*/ +#ifndef HAVE_TIMEGM +time_t +timegm (struct tm *tm) +{ +#ifdef HAVE_W32_SYSTEM + /* This one is thread safe. */ + SYSTEMTIME st; + FILETIME ft; + unsigned long long cnsecs; + + st.wYear = tm->tm_year + 1900; + st.wMonth = tm->tm_mon + 1; + st.wDay = tm->tm_mday; + st.wHour = tm->tm_hour; + st.wMinute = tm->tm_min; + st.wSecond = tm->tm_sec; + st.wMilliseconds = 0; /* Not available. */ + st.wDayOfWeek = 0; /* Ignored. */ + + /* System time is UTC thus the conversion is pretty easy. */ + if (!SystemTimeToFileTime (&st, &ft)) + { + gpg_err_set_errno (EINVAL); + return (time_t)(-1); + } + + cnsecs = (((unsigned long long)ft.dwHighDateTime << 32) + | ft.dwLowDateTime); + cnsecs -= 116444736000000000ULL; /* The filetime epoch is 1601-01-01. */ + return (time_t)(cnsecs / 10000000ULL); + +#else /* (Non thread safe implementation!) */ + + time_t answer; + char *zone; + + zone=getenv("TZ"); + putenv("TZ=UTC"); + tzset(); + answer=mktime(tm); + if(zone) + { + static char *old_zone; + + if (!old_zone) + { + old_zone = malloc(3+strlen(zone)+1); + if (old_zone) + { + strcpy(old_zone,"TZ="); + strcat(old_zone,zone); + } + } + if (old_zone) + putenv (old_zone); + } + else + gnupg_unsetenv("TZ"); + + tzset(); + return answer; +#endif +} +#endif /*!HAVE_TIMEGM*/ diff --git a/common/mischelp.h b/common/mischelp.h new file mode 100644 index 0000000..bdee5a4 --- /dev/null +++ b/common/mischelp.h @@ -0,0 +1,92 @@ +/* mischelp.h - Miscellaneous helper macros and functions + * Copyright (C) 1999, 2000, 2001, 2002, 2003, + * 2006, 2007, 2009 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute and/or modify this + * part of GnuPG under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * GnuPG is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copies of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef GNUPG_COMMON_MISCHELP_H +#define GNUPG_COMMON_MISCHELP_H + + +/* Check whether the files NAME1 and NAME2 are identical. This is for + example achieved by comparing the inode numbers of the files. */ +int same_file_p (const char *name1, const char *name2); + + +#ifndef HAVE_TIMEGM +#include <time.h> +time_t timegm (struct tm *tm); +#endif /*!HAVE_TIMEGM*/ + + +#define DIM(v) (sizeof(v)/sizeof((v)[0])) +#define DIMof(type,member) DIM(((type *)0)->member) + +/* To avoid that a compiler optimizes certain memset calls away, + wipememory function may be used instead. */ +void wipememory(void *ptr, size_t len); + +/* Include hacks which are mainly required for Slowaris. */ +#ifdef GNUPG_COMMON_NEED_AFLOCAL +#ifndef HAVE_W32_SYSTEM +# include <sys/socket.h> +# include <sys/un.h> +#else +# ifdef HAVE_WINSOCK2_H +# include <winsock2.h> +# endif +# include <windows.h> +#endif + +#ifndef PF_LOCAL +# ifdef PF_UNIX +# define PF_LOCAL PF_UNIX +# else +# define PF_LOCAL AF_UNIX +# endif +#endif /*PF_LOCAL*/ +#ifndef AF_LOCAL +# define AF_LOCAL AF_UNIX +#endif /*AF_UNIX*/ + +/* We used to avoid this macro in GnuPG and inlined the AF_LOCAL name + length computation directly with the little twist of adding 1 extra + byte. It seems that this was needed once on an old HP/UX box and + there are also rumours that 4.3 Reno and DEC systems need it. This + one-off buglet did not harm any current system until it came to Mac + OS X where the kernel (as of May 2009) exhibited a strange bug: The + systems basically froze in the connect call if the passed name + contained an invalid directory part. Ignore the old Unices. */ +#ifndef SUN_LEN +# define SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path) \ + + strlen ((ptr)->sun_path)) +#endif /*SUN_LEN*/ +#endif /*GNUPG_COMMON_NEED_AFLOCAL*/ + + +#endif /*GNUPG_COMMON_MISCHELP_H*/ diff --git a/common/mkdir_p.c b/common/mkdir_p.c new file mode 100644 index 0000000..6fb98c3 --- /dev/null +++ b/common/mkdir_p.c @@ -0,0 +1,187 @@ +/* mkdir_p.c - Create a directory and any missing parents. + * Copyright (C) 2015 g10 Code GmbH + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <sys/stat.h> +#include <errno.h> +#include <assert.h> +#include <stdarg.h> + +#include "util.h" +#include "stringhelp.h" +#include "logging.h" +#include "sysutils.h" +#include "mkdir_p.h" + + +gpg_error_t +gnupg_amkdir_p (const char **directory_components) +{ + gpg_error_t err = 0; + int count; + char **dirs; + int i; + + for (count = 0; directory_components[count]; count ++) + ; + + /* log_debug ("%s: %d directory components.\n", __func__, count); */ + + dirs = xtrycalloc (count, sizeof *dirs); + if (!dirs) + return gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); + + for (i = 0; directory_components[i]; i ++) + { + if (i == 0) + dirs[i] = make_filename_try (directory_components[i], NULL); + else + dirs[i] = make_filename_try (dirs[i-1], directory_components[i], NULL); + if (!dirs[i]) + { + err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); + goto out; + } + + /* log_debug ("%s: Directory %d: `%s'.\n", __func__, i, dirs[i]); */ + } + + for (i = count - 1; i >= 0; i --) + { + struct stat s; + + /* log_debug ("%s: stat(%s)\n", __func__, dirs[i]); */ + + if (!gnupg_stat (dirs[i], &s)) + { + if ( ! S_ISDIR (s.st_mode)) + { + /* log_debug ("%s: %s exists, but is not a directory!\n", */ + /* __func__, dirs[i]); */ + err = gpg_err_make (default_errsource, GPG_ERR_ENOTDIR); + goto out; + } + else + { + /* Got a directory. */ + /* log_debug ("%s: %s exists and is a directory!\n", */ + /* __func__, dirs[i]); */ + err = 0; + break; + } + } + else if (errno == ENOENT) + /* This directory does not exist yet. Continue walking up the + hierarchy. */ + { + /* log_debug ("%s: %s does not exist!\n", */ + /* __func__, dirs[i]); */ + continue; + } + else + /* Some other error code. Die. Note: this could be ENOTDIR + (we return this above), which means that a component of the + path prefix is not a directory. */ + { + /* log_debug ("%s: stat(%s) => %s!\n", */ + /* __func__, dirs[i], strerror (errno)); */ + err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); + goto out; + } + } + + assert (i >= -1); + /* DIRS[I] exists. Start with the following entry. */ + i ++; + + for (; i < count; i ++) + { + /* log_debug ("Creating directory: %s\n", dirs[i]); */ + + if (gnupg_mkdir (dirs[i], "-rwx")) + { + err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); + goto out; + } + } + + out: + for (i = 0; i < count; i ++) + xfree (dirs[i]); + xfree (dirs); + + /* log_debug ("%s: Returning %s\n", __func__, gpg_strerror (rc)); */ + + return err; +} + + +gpg_error_t +gnupg_mkdir_p (const char *directory_component, ...) +{ + va_list ap; + gpg_error_t err = 0; + int i; + int space = 1; + const char **dirs; + + dirs = xtrymalloc (space * sizeof (char *)); + if (!dirs) + return gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); + + dirs[0] = directory_component; + + va_start (ap, directory_component); + for (i = 1; dirs[i - 1]; i ++) + { + if (i == space) + { + const char **tmp_dirs; + + space = 2 * space; + tmp_dirs = xtryrealloc (dirs, space * sizeof (char *)); + if (!tmp_dirs) + { + err = gpg_err_make (default_errsource, + gpg_err_code_from_syserror ()); + break; + } + dirs = tmp_dirs; + } + dirs[i] = va_arg (ap, char *); + } + va_end (ap); + + if (!err) + err = gnupg_amkdir_p (dirs); + + xfree (dirs); + + return err; +} diff --git a/common/mkdir_p.h b/common/mkdir_p.h new file mode 100644 index 0000000..1e939b3 --- /dev/null +++ b/common/mkdir_p.h @@ -0,0 +1,52 @@ +/* mkdir_p.h - Create a directory and any missing parents. + * Copyright (C) 2015 g10 Code GmbH + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef MKDIR_P_H +#define MKDIR_P_H + +#include "types.h" + +/* Create a directory as well as any missing parents. + + The arguments must be NULL termianted. If DIRECTORY_COMPONENTS... + consists of two elements, "foo/bar" and "xyzzy", this function will + first try to create the directory "foo/bar" and then the directory + "foo/bar/xyzzy". On success returns 0, otherwise an error code is + returned. */ +gpg_error_t gnupg_mkdir_p (const char *directory_component, ...) GPGRT_ATTR_SENTINEL(0); + +/* Like mkdir_p, but DIRECTORY_COMPONENTS is a NULL terminated + array, e.g.: + + char **dirs = { "foo", "bar", NULL }; + amkdir_p (dirs); + */ +gpg_error_t gnupg_amkdir_p (const char **directory_components); + +#endif diff --git a/common/mkstrtable.awk b/common/mkstrtable.awk new file mode 100644 index 0000000..60efce8 --- /dev/null +++ b/common/mkstrtable.awk @@ -0,0 +1,185 @@ +# mkstrtable.awk +# Copyright (C) 2003, 2004 g10 Code GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <http://www.gnu.org/licenses/>. +# +# As a special exception, g10 Code GmbH gives unlimited permission to +# copy, distribute and modify the C source files that are the output +# of mkstrtable.awk. You need not follow the terms of the GNU General +# Public License when using or distributing such scripts, even though +# portions of the text of mkstrtable.awk appear in them. The GNU +# General Public License (GPL) does govern all other use of the material +# that constitutes the mkstrtable.awk program. +# +# Certain portions of the mkstrtable.awk source text are designed to be +# copied (in certain cases, depending on the input) into the output of +# mkstrtable.awk. We call these the "data" portions. The rest of the +# mkstrtable.awk source text consists of comments plus executable code +# that decides which of the data portions to output in any given case. +# We call these comments and executable code the "non-data" portions. +# mkstrtable.h never copies any of the non-data portions into its output. +# +# This special exception to the GPL applies to versions of mkstrtable.awk +# released by g10 Code GmbH. When you make and distribute a modified version +# of mkstrtable.awk, you may extend this special exception to the GPL to +# apply to your modified version as well, *unless* your modified version +# has the potential to copy into its output some of the text that was the +# non-data portion of the version that you started with. (In other words, +# unless your change moves or copies text from the non-data portions to the +# data portions.) If your modification has such potential, you must delete +# any notice of this special exception to the GPL from your modified version. + +# This script outputs a source file that does define the following +# symbols: +# +# static const char msgstr[]; +# A string containing all messages in the list. +# +# static const int msgidx[]; +# A list of index numbers, one for each message, that points to the +# beginning of the string in msgstr. +# +# msgidxof (code); +# A macro that maps code numbers to idx numbers. If a DEFAULT MESSAGE +# is provided (see below), its index will be returned for unknown codes. +# Otherwise -1 is returned for codes that do not appear in the list. +# You can lookup the message with code CODE with: +# msgstr + msgidx[msgidxof (code)]. +# +# The input file has the following format: +# CODE1 ... MESSAGE1 (code nr, <tab>, something, <tab>, msg) +# CODE2 ... MESSAGE2 (code nr, <tab>, something, <tab>, msg) +# ... +# CODEn ... MESSAGEn (code nr, <tab>, something, <tab>, msg) +# ... DEFAULT-MESSAGE (<tab>, something, <tab>, fall-back msg) +# +# Comments (starting with # and ending at the end of the line) are removed, +# as is trailing whitespace. The last line is optional; if no DEFAULT +# MESSAGE is given, msgidxof will return the number -1 for unknown +# index numbers. +# +# The field to be used is specified with the variable "textidx" on +# the command line. It defaults to 2. +# +# The variable nogettext can be set to 1 to suppress gettext markers. +# +# The variable prefix can be used to prepend a string to each message. +# +# The variable pkg_namespace can be used to prepend a string to each +# variable and macro name. + +BEGIN { + FS = "[\t]+"; +# cpos holds the current position in the message string. + cpos = 0; +# msg holds the number of messages. + msg = 0; + print "/* Output of mkstrtable.awk. DO NOT EDIT. */"; + print ""; + header = 1; + if (textidx == 0) + textidx = 2; +# nogettext can be set to 1 to suppress gettext noop markers. +} + +/^#/ { next; } + +header { + if ($1 ~ /^[0123456789]+$/) + { + print "/* The purpose of this complex string table is to produce"; + print " optimal code with a minimum of relocations. */"; + print ""; + print "static const char " pkg_namespace "msgstr[] = "; + header = 0; + } + else + print; +} + +!header { + sub (/#.+/, ""); + sub (/[ ]+$/, ""); # Strip trailing space and tab characters. + + if (/^$/) + next; + +# Print the string msgstr line by line. We delay output by one line to be able +# to treat the last line differently (see END). + if (last_msgstr) + { + if (nogettext) + print " \"" last_msgstr "\" \"\\0\""; + else + print " gettext_noop (\"" last_msgstr "\") \"\\0\""; + } + last_msgstr = prefix $textidx; + +# Remember the error code and msgidx of each error message. + code[msg] = $1; + pos[msg] = cpos; + cpos += length (last_msgstr) + 1; + msg++; + + if ($1 == "") + { + has_default = 1; + exit; + } +} +END { + if (has_default) + coded_msgs = msg - 1; + else + coded_msgs = msg; + + if (nogettext) + print " \"" prefix last_msgstr "\";"; + else + print " gettext_noop (\"" prefix last_msgstr "\");"; + print ""; + print "static const int " pkg_namespace "msgidx[] ="; + print " {"; + for (i = 0; i < coded_msgs; i++) + print " " pos[i] ","; + print " " pos[coded_msgs]; + print " };"; + print ""; + print "#define " pkg_namespace "msgidxof(code) (0 ? -1 \\"; + +# Gather the ranges. + skip = code[0]; + start = code[0]; + stop = code[0]; + for (i = 1; i < coded_msgs; i++) + { + if (code[i] == stop + 1) + stop++; + else + { + print " : ((code >= " start ") && (code <= " stop ")) ? (code - " \ + skip ") \\"; + skip += code[i] - stop - 1; + start = code[i]; + stop = code[i]; + } + } + print " : ((code >= " start ") && (code <= " stop ")) ? (code - " \ + skip ") \\"; + if (has_default) + print " : " stop + 1 " - " skip ")"; + else + print " : -1)"; + + } diff --git a/common/name-value.c b/common/name-value.c new file mode 100644 index 0000000..b9b13d1 --- /dev/null +++ b/common/name-value.c @@ -0,0 +1,908 @@ +/* name-value.c - Parser and writer for a name-value format. + * Copyright (C) 2016 g10 Code GmbH + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +/* + * This module aso provides features for the extended private key + * format of gpg-agent. + */ + +#include <config.h> +#include <assert.h> +#include <gcrypt.h> +#include <gpg-error.h> +#include <string.h> + +#include "mischelp.h" +#include "strlist.h" +#include "util.h" +#include "name-value.h" + +struct name_value_container +{ + struct name_value_entry *first; + struct name_value_entry *last; + unsigned int private_key_mode:1; +}; + + +struct name_value_entry +{ + struct name_value_entry *prev; + struct name_value_entry *next; + + /* The name. Comments and blank lines have NAME set to NULL. */ + char *name; + + /* The value as stored in the file. We store it when we parse + a file so that we can reproduce it. */ + strlist_t raw_value; + + /* The decoded value. */ + char *value; +}; + + +/* Helper */ +static inline gpg_error_t +my_error_from_syserror (void) +{ + return gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); +} + + +static inline gpg_error_t +my_error (gpg_err_code_t ec) +{ + return gpg_err_make (default_errsource, ec); +} + + + + +/* Allocation and deallocation. */ + +/* Allocate a private key container structure. */ +nvc_t +nvc_new (void) +{ + return xtrycalloc (1, sizeof (struct name_value_container)); +} + + +/* Allocate a private key container structure for use with private keys. */ +nvc_t +nvc_new_private_key (void) +{ + nvc_t nvc = nvc_new (); + if (nvc) + nvc->private_key_mode = 1; + return nvc; +} + + +static void +nve_release (nve_t entry, int private_key_mode) +{ + if (entry == NULL) + return; + + xfree (entry->name); + if (entry->value && private_key_mode) + wipememory (entry->value, strlen (entry->value)); + xfree (entry->value); + if (private_key_mode) + free_strlist_wipe (entry->raw_value); + else + free_strlist (entry->raw_value); + xfree (entry); +} + + +/* Release a private key container structure. */ +void +nvc_release (nvc_t pk) +{ + nve_t e, next; + + if (pk == NULL) + return; + + for (e = pk->first; e; e = next) + { + next = e->next; + nve_release (e, pk->private_key_mode); + } + + xfree (pk); +} + + + +/* Dealing with names and values. */ + +/* Check whether the given name is valid. Valid names start with a + letter, end with a colon, and contain only alphanumeric characters + and the hyphen. */ +static int +valid_name (const char *name) +{ + size_t i, len = strlen (name); + + if (! alphap (name) || len == 0 || name[len - 1] != ':') + return 0; + + for (i = 1; i < len - 1; i++) + if (! alnump (&name[i]) && name[i] != '-') + return 0; + + return 1; +} + + +/* Makes sure that ENTRY has a RAW_VALUE. */ +static gpg_error_t +assert_raw_value (nve_t entry) +{ + gpg_error_t err = 0; + size_t len, offset; +#define LINELEN 70 + char buf[LINELEN+3]; + + if (entry->raw_value) + return 0; + + len = strlen (entry->value); + offset = 0; + while (len) + { + size_t amount, linelen = LINELEN; + + /* On the first line we need to subtract space for the name. */ + if (entry->raw_value == NULL && strlen (entry->name) < linelen) + linelen -= strlen (entry->name); + + /* See if the rest of the value fits in this line. */ + if (len <= linelen) + amount = len; + else + { + size_t i; + + /* Find a suitable space to break on. */ + for (i = linelen - 1; linelen - i < 30; i--) + if (ascii_isspace (entry->value[offset+i])) + break; + + if (ascii_isspace (entry->value[offset+i])) + { + /* Found one. */ + amount = i; + } + else + { + /* Just induce a hard break. */ + amount = linelen; + } + } + + snprintf (buf, sizeof buf, " %.*s\n", (int) amount, + &entry->value[offset]); + if (append_to_strlist_try (&entry->raw_value, buf) == NULL) + { + err = my_error_from_syserror (); + goto leave; + } + + offset += amount; + len -= amount; + } + + leave: + if (err) + { + free_strlist_wipe (entry->raw_value); + entry->raw_value = NULL; + } + + return err; +#undef LINELEN +} + + +/* Computes the length of the value encoded as continuation. If + *SWALLOW_WS is set, all whitespace at the beginning of S is + swallowed. If START is given, a pointer to the beginning of the + value is stored there. */ +static size_t +continuation_length (const char *s, int *swallow_ws, const char **start) +{ + size_t len; + + if (*swallow_ws) + { + /* The previous line was a blank line and we inserted a newline. + Swallow all whitespace at the beginning of this line. */ + while (ascii_isspace (*s)) + s++; + } + else + { + /* Iff a continuation starts with more than one space, it + encodes a space. */ + if (ascii_isspace (*s)) + s++; + } + + /* Strip whitespace at the end. */ + len = strlen (s); + while (len > 0 && ascii_isspace (s[len-1])) + len--; + + if (len == 0) + { + /* Blank lines encode newlines. */ + len = 1; + s = "\n"; + *swallow_ws = 1; + } + else + *swallow_ws = 0; + + if (start) + *start = s; + + return len; +} + + +/* Makes sure that ENTRY has a VALUE. */ +static gpg_error_t +assert_value (nve_t entry) +{ + size_t len; + int swallow_ws; + strlist_t s; + char *p; + + if (entry->value) + return 0; + + len = 0; + swallow_ws = 0; + for (s = entry->raw_value; s; s = s->next) + len += continuation_length (s->d, &swallow_ws, NULL); + + /* Add one for the terminating zero. */ + len += 1; + + entry->value = p = xtrymalloc (len); + if (entry->value == NULL) + return my_error_from_syserror (); + + swallow_ws = 0; + for (s = entry->raw_value; s; s = s->next) + { + const char *start; + size_t l = continuation_length (s->d, &swallow_ws, &start); + + memcpy (p, start, l); + p += l; + } + + *p++ = 0; + assert (p - entry->value == len); + + return 0; +} + + +/* Get the name. */ +char * +nve_name (nve_t pke) +{ + return pke->name; +} + + +/* Get the value. */ +char * +nve_value (nve_t pke) +{ + if (assert_value (pke)) + return NULL; + return pke->value; +} + + + +/* Adding and modifying values. */ + +/* Add (NAME, VALUE, RAW_VALUE) to PK. NAME may be NULL for comments + and blank lines. At least one of VALUE and RAW_VALUE must be + given. If PRESERVE_ORDER is not given, entries with the same name + are grouped. NAME, VALUE and RAW_VALUE is consumed. */ +static gpg_error_t +_nvc_add (nvc_t pk, char *name, char *value, strlist_t raw_value, + int preserve_order) +{ + gpg_error_t err = 0; + nve_t e; + + assert (value || raw_value); + + if (name && ! valid_name (name)) + { + err = my_error (GPG_ERR_INV_NAME); + goto leave; + } + + if (name + && pk->private_key_mode + && !ascii_strcasecmp (name, "Key:") + && nvc_lookup (pk, "Key:")) + { + err = my_error (GPG_ERR_INV_NAME); + goto leave; + } + + e = xtrycalloc (1, sizeof *e); + if (e == NULL) + { + err = my_error_from_syserror (); + goto leave; + } + + e->name = name; + e->value = value; + e->raw_value = raw_value; + + if (pk->first) + { + nve_t last; + + if (preserve_order || name == NULL) + last = pk->last; + else + { + /* See if there is already an entry with NAME. */ + last = nvc_lookup (pk, name); + + /* If so, find the last in that block. */ + if (last) + { + while (last->next) + { + nve_t next = last->next; + + if (next->name && ascii_strcasecmp (next->name, name) == 0) + last = next; + else + break; + } + } + else /* Otherwise, just find the last entry. */ + last = pk->last; + } + + if (last->next) + { + e->prev = last; + e->next = last->next; + last->next = e; + e->next->prev = e; + } + else + { + e->prev = last; + last->next = e; + pk->last = e; + } + } + else + pk->first = pk->last = e; + + leave: + if (err) + { + xfree (name); + if (value) + wipememory (value, strlen (value)); + xfree (value); + free_strlist_wipe (raw_value); + } + + return err; +} + + +/* Add (NAME, VALUE) to PK. If an entry with NAME already exists, it + is not updated but the new entry is appended. */ +gpg_error_t +nvc_add (nvc_t pk, const char *name, const char *value) +{ + char *k, *v; + + k = xtrystrdup (name); + if (k == NULL) + return my_error_from_syserror (); + + v = xtrystrdup (value); + if (v == NULL) + { + xfree (k); + return my_error_from_syserror (); + } + + return _nvc_add (pk, k, v, NULL, 0); +} + + +/* Add (NAME, VALUE) to PK. If an entry with NAME already exists, it + is updated with VALUE. If multiple entries with NAME exist, the + first entry is updated. */ +gpg_error_t +nvc_set (nvc_t pk, const char *name, const char *value) +{ + nve_t e; + + if (! valid_name (name)) + return GPG_ERR_INV_NAME; + + e = nvc_lookup (pk, name); + if (e) + return nve_set (e, value); + else + return nvc_add (pk, name, value); +} + + +/* Update entry E to VALUE. */ +gpg_error_t +nve_set (nve_t e, const char *value) +{ + char *v; + + if (!e) + return GPG_ERR_INV_ARG; + + v = xtrystrdup (value? value:""); + if (!v) + return my_error_from_syserror (); + + free_strlist_wipe (e->raw_value); + e->raw_value = NULL; + if (e->value) + wipememory (e->value, strlen (e->value)); + xfree (e->value); + e->value = v; + + return 0; +} + + +/* Delete the given entry from PK. */ +void +nvc_delete (nvc_t pk, nve_t entry) +{ + if (entry->prev) + entry->prev->next = entry->next; + else + pk->first = entry->next; + + if (entry->next) + entry->next->prev = entry->prev; + else + pk->last = entry->prev; + + nve_release (entry, pk->private_key_mode); +} + +/* Delete the entries with NAME from PK. */ +void +nvc_delete_named (nvc_t pk, const char *name) +{ + nve_t e; + + if (!valid_name (name)) + return; + + while ((e = nvc_lookup (pk, name))) + nvc_delete (pk, e); +} + + + + +/* Lookup and iteration. */ + +/* Get the first non-comment entry. */ +nve_t +nvc_first (nvc_t pk) +{ + nve_t entry; + + if (!pk) + return NULL; + + for (entry = pk->first; entry; entry = entry->next) + if (entry->name) + return entry; + + return NULL; +} + + +/* Get the first entry with the given name. Return NULL if it does + * not exist. */ +nve_t +nvc_lookup (nvc_t pk, const char *name) +{ + nve_t entry; + + if (!pk) + return NULL; + + for (entry = pk->first; entry; entry = entry->next) + if (entry->name && ascii_strcasecmp (entry->name, name) == 0) + return entry; + + return NULL; +} + + +/* Get the next non-comment entry. */ +nve_t +nve_next (nve_t entry) +{ + for (entry = entry->next; entry; entry = entry->next) + if (entry->name) + return entry; + return NULL; +} + + +/* Get the next entry with the given name. */ +nve_t +nve_next_value (nve_t entry, const char *name) +{ + for (entry = entry->next; entry; entry = entry->next) + if (entry->name && ascii_strcasecmp (entry->name, name) == 0) + return entry; + return NULL; +} + + +/* Return the string for the first entry in NVC with NAME. If an + * entry with NAME is missing in NVC or its value is the empty string + * NULL is returned. Note that the returned string is a pointer + * into NVC. */ +const char * +nvc_get_string (nvc_t nvc, const char *name) +{ + nve_t item; + + if (!nvc) + return NULL; + item = nvc_lookup (nvc, name); + if (!item) + return NULL; + return nve_value (item); +} + + +/* Return true if NAME exists and its value is true; that is either + * "yes", "true", or a decimal value unequal to 0. */ +int +nvc_get_boolean (nvc_t nvc, const char *name) +{ + nve_t item; + const char *s; + + if (!nvc) + return 0; + item = nvc_lookup (nvc, name); + if (!item) + return 0; + s = nve_value (item); + if (s && (atoi (s) + || !ascii_strcasecmp (s, "yes") + || !ascii_strcasecmp (s, "true"))) + return 1; + return 0; +} + + + +/* Private key handling. */ + +/* Get the private key. */ +gpg_error_t +nvc_get_private_key (nvc_t pk, gcry_sexp_t *retsexp) +{ + gpg_error_t err; + nve_t e; + + e = pk->private_key_mode? nvc_lookup (pk, "Key:") : NULL; + if (e == NULL) + return my_error (GPG_ERR_MISSING_KEY); + + err = assert_value (e); + if (err) + return err; + + return gcry_sexp_sscan (retsexp, NULL, e->value, strlen (e->value)); +} + + +/* Set the private key. */ +gpg_error_t +nvc_set_private_key (nvc_t pk, gcry_sexp_t sexp) +{ + gpg_error_t err; + char *raw, *clean, *p; + size_t len, i; + + if (!pk->private_key_mode) + return my_error (GPG_ERR_MISSING_KEY); + + len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, NULL, 0); + + raw = xtrymalloc (len); + if (raw == NULL) + return my_error_from_syserror (); + + clean = xtrymalloc (len); + if (clean == NULL) + { + xfree (raw); + return my_error_from_syserror (); + } + + gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, raw, len); + + /* Strip any whitespace at the end. */ + i = strlen (raw) - 1; + while (i && ascii_isspace (raw[i])) + { + raw[i] = 0; + i--; + } + + /* Replace any newlines with spaces, remove superfluous whitespace. */ + len = strlen (raw); + for (p = clean, i = 0; i < len; i++) + { + char c = raw[i]; + + /* Collapse contiguous and superfluous spaces. */ + if (ascii_isspace (c) && i > 0 + && (ascii_isspace (raw[i-1]) || raw[i-1] == '(' || raw[i-1] == ')')) + continue; + + if (c == '\n') + c = ' '; + + *p++ = c; + } + *p = 0; + + err = nvc_set (pk, "Key:", clean); + xfree (raw); + xfree (clean); + return err; +} + + + +/* Parsing and serialization. */ + +static gpg_error_t +do_nvc_parse (nvc_t *result, int *errlinep, estream_t stream, + int for_private_key) +{ + gpg_error_t err = 0; + gpgrt_ssize_t len; + char *buf = NULL; + size_t buf_len = 0; + char *name = NULL; + strlist_t raw_value = NULL; + + *result = for_private_key? nvc_new_private_key () : nvc_new (); + if (*result == NULL) + return my_error_from_syserror (); + + if (errlinep) + *errlinep = 0; + while ((len = es_read_line (stream, &buf, &buf_len, NULL)) > 0) + { + char *p; + if (errlinep) + *errlinep += 1; + + /* Skip any whitespace. */ + for (p = buf; *p && ascii_isspace (*p); p++) + /* Do nothing. */; + + if (name && (spacep (buf) || *p == 0)) + { + /* A continuation. */ + if (append_to_strlist_try (&raw_value, buf) == NULL) + { + err = my_error_from_syserror (); + goto leave; + } + continue; + } + + /* No continuation. Add the current entry if any. */ + if (raw_value) + { + err = _nvc_add (*result, name, NULL, raw_value, 1); + name = NULL; + if (err) + goto leave; + } + + /* And prepare for the next one. */ + name = NULL; + raw_value = NULL; + + if (*p != 0 && *p != '#') + { + char *colon, *value, tmp; + + colon = strchr (buf, ':'); + if (colon == NULL) + { + err = my_error (GPG_ERR_INV_VALUE); + goto leave; + } + + value = colon + 1; + tmp = *value; + *value = 0; + name = xtrystrdup (p); + *value = tmp; + + if (name == NULL) + { + err = my_error_from_syserror (); + goto leave; + } + + if (append_to_strlist_try (&raw_value, value) == NULL) + { + err = my_error_from_syserror (); + goto leave; + } + continue; + } + + if (append_to_strlist_try (&raw_value, buf) == NULL) + { + err = my_error_from_syserror (); + goto leave; + } + } + if (len < 0) + { + err = gpg_error_from_syserror (); + goto leave; + } + + /* Add the final entry. */ + if (raw_value) + err = _nvc_add (*result, name, NULL, raw_value, 1); + + leave: + gpgrt_free (buf); + if (err) + { + nvc_release (*result); + *result = NULL; + } + + return err; +} + + +/* Parse STREAM and return a newly allocated name value container + structure in RESULT. If ERRLINEP is given, the line number the + parser was last considering is stored there. */ +gpg_error_t +nvc_parse (nvc_t *result, int *errlinep, estream_t stream) +{ + return do_nvc_parse (result, errlinep, stream, 0); +} + + +/* Parse STREAM and return a newly allocated name value container + structure in RESULT - assuming the extended private key format. If + ERRLINEP is given, the line number the parser was last considering + is stored there. */ +gpg_error_t +nvc_parse_private_key (nvc_t *result, int *errlinep, estream_t stream) +{ + return do_nvc_parse (result, errlinep, stream, 1); +} + + +/* Helper fpr nvc_write. */ +static gpg_error_t +write_one_entry (nve_t entry, estream_t stream) +{ + gpg_error_t err; + strlist_t sl; + + if (entry->name) + es_fputs (entry->name, stream); + + err = assert_raw_value (entry); + if (err) + return err; + + for (sl = entry->raw_value; sl; sl = sl->next) + es_fputs (sl->d, stream); + + if (es_ferror (stream)) + return my_error_from_syserror (); + + return 0; +} + + +/* Write a representation of PK to STREAM. */ +gpg_error_t +nvc_write (nvc_t pk, estream_t stream) +{ + gpg_error_t err = 0; + nve_t entry; + nve_t keyentry = NULL; + + for (entry = pk->first; entry; entry = entry->next) + { + if (pk->private_key_mode + && entry->name && !ascii_strcasecmp (entry->name, "Key:")) + { + if (!keyentry) + keyentry = entry; + continue; + } + + err = write_one_entry (entry, stream); + if (err) + return err; + } + + /* In private key mode we write the Key always last. */ + if (keyentry) + err = write_one_entry (keyentry, stream); + + return err; +} diff --git a/common/name-value.h b/common/name-value.h new file mode 100644 index 0000000..fd0a98c --- /dev/null +++ b/common/name-value.h @@ -0,0 +1,132 @@ +/* name-value.h - Parser and writer for a name-value format. + * Copyright (C) 2016 g10 Code GmbH + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef GNUPG_COMMON_NAME_VALUE_H +#define GNUPG_COMMON_NAME_VALUE_H + +struct name_value_container; +typedef struct name_value_container *nvc_t; + +struct name_value_entry; +typedef struct name_value_entry *nve_t; + + + +/* Memory management, and dealing with entries. */ + +/* Allocate a name value container structure. */ +nvc_t nvc_new (void); + +/* Allocate a name value container structure for use with the extended + * private key format. */ +nvc_t nvc_new_private_key (void); + +/* Release a name value container structure. */ +void nvc_release (nvc_t pk); + +/* Get the name. */ +char *nve_name (nve_t pke); + +/* Get the value. */ +char *nve_value (nve_t pke); + + + +/* Lookup and iteration. */ + +/* Get the first non-comment entry. */ +nve_t nvc_first (nvc_t pk); + +/* Get the first entry with the given name. */ +nve_t nvc_lookup (nvc_t pk, const char *name); + +/* Get the next non-comment entry. */ +nve_t nve_next (nve_t entry); + +/* Get the next entry with the given name. */ +nve_t nve_next_value (nve_t entry, const char *name); + +/* Return the string for the first entry in NVC with NAME or NULL. */ +const char *nvc_get_string (nvc_t nvc, const char *name); + +/* Return a boolean value for the first entry in NVC with NAME. */ +int nvc_get_boolean (nvc_t nvc, const char *name); + + + +/* Adding and modifying values. */ + +/* Add (NAME, VALUE) to PK. If an entry with NAME already exists, it + is not updated but the new entry is appended. */ +gpg_error_t nvc_add (nvc_t pk, const char *name, const char *value); + +/* Add (NAME, VALUE) to PK. If an entry with NAME already exists, it + is updated with VALUE. If multiple entries with NAME exist, the + first entry is updated. */ +gpg_error_t nvc_set (nvc_t pk, const char *name, const char *value); + +/* Update entry E to VALUE. */ +gpg_error_t nve_set (nve_t e, const char *value); + +/* Delete the given entry from PK. */ +void nvc_delete (nvc_t pk, nve_t pke); + +/* Delete the entries with NAME from PK. */ +void nvc_delete_named (nvc_t pk, const char *name); + + + +/* Private key handling. */ + +/* Get the private key. */ +gpg_error_t nvc_get_private_key (nvc_t pk, gcry_sexp_t *retsexp); + +/* Set the private key. */ +gpg_error_t nvc_set_private_key (nvc_t pk, gcry_sexp_t sexp); + + + +/* Parsing and serialization. */ + +/* Parse STREAM and return a newly allocated private key container + structure in RESULT. If ERRLINEP is given, the line number the + parser was last considering is stored there. */ +gpg_error_t nvc_parse (nvc_t *result, int *errlinep, estream_t stream); + +/* Parse STREAM and return a newly allocated name value container + structure in RESULT - assuming the extended private key format. If + ERRLINEP is given, the line number the parser was last considering + is stored there. */ +gpg_error_t nvc_parse_private_key (nvc_t *result, int *errlinep, + estream_t stream); + +/* Write a representation of PK to STREAM. */ +gpg_error_t nvc_write (nvc_t pk, estream_t stream); + +#endif /* GNUPG_COMMON_NAME_VALUE_H */ diff --git a/common/openpgp-fpr.c b/common/openpgp-fpr.c new file mode 100644 index 0000000..7b11008 --- /dev/null +++ b/common/openpgp-fpr.c @@ -0,0 +1,283 @@ +/* openpgp-fpr.c - OpenPGP Fingerprint computation + * Copyright (C) 2021 g10 Code GmbH + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + * SPDX-License-Identifier: (LGPL-3.0-or-later OR GPL-2.0-or-later) + */ + +#include <config.h> +#include <stdlib.h> +#include <errno.h> +#include <ctype.h> + +#include "util.h" +#include "openpgpdefs.h" + +/* Count the number of bits, assuming the A represents an unsigned big + * integer of length LEN bytes. */ +static unsigned int +count_bits (const unsigned char *a, size_t len) +{ + unsigned int n = len * 8; + int i; + + for (; len && !*a; len--, a++, n -=8) + ; + if (len) + { + for (i=7; i && !(*a & (1<<i)); i--) + n--; + } + return n; +} + +/* Variant of count_bits for simple octet strings. */ +static unsigned int +count_sos_bits (const unsigned char *a, size_t len) +{ + unsigned int n = len * 8; + int i; + + if (len == 0 || *a == 0) + return n; + + for (i=7; i && !(*a & (1<<i)); i--) + n--; + + return n; +} + + +gpg_error_t +compute_openpgp_fpr (int keyversion, int pgpalgo, unsigned long timestamp, + gcry_buffer_t *iov, int iovcnt, + unsigned char *result, unsigned int *r_resultlen) +{ + gpg_error_t err; + int hashalgo; + unsigned char prefix[15]; + size_t n; + int i; + + if (r_resultlen) + *r_resultlen = 0; + + if (iovcnt < 2) + return gpg_error (GPG_ERR_INV_ARG); + + /* Note that iov[0] is reserved. */ + for (n=0, i=1; i < iovcnt; i++) + n += iov[i].len; + + i = 0; + if (keyversion == 5) + { + hashalgo = GCRY_MD_SHA256; + n += 10; /* Add the prefix length. */ + prefix[i++] = 0x9a; + prefix[i++] = (n >> 24); + prefix[i++] = (n >> 16); + } + else if (keyversion == 4) + { + hashalgo = GCRY_MD_SHA1; + n += 6; /* Add the prefix length. */ + prefix[i++] = 0x99; + } + else + return gpg_error (GPG_ERR_UNKNOWN_VERSION); + + prefix[i++] = (n >> 8); + prefix[i++] = n; + prefix[i++] = keyversion; + prefix[i++] = (timestamp >> 24); + prefix[i++] = (timestamp >> 16); + prefix[i++] = (timestamp >> 8); + prefix[i++] = (timestamp); + prefix[i++] = pgpalgo; + if (keyversion == 5) + { + prefix[i++] = ((n-10) >> 24); + prefix[i++] = ((n-10) >> 16); + prefix[i++] = ((n-10) >> 8); + prefix[i++] = (n-10); + } + log_assert (i <= sizeof prefix); + /* The first element is reserved for our use; set it. */ + iov[0].size = 0; + iov[0].off = 0; + iov[0].len = i; + iov[0].data = prefix; + + /* for (i=0; i < iovcnt; i++) */ + /* log_printhex (iov[i].data, iov[i].len, "cmpfpr i=%d: ", i); */ + + err = gcry_md_hash_buffers (hashalgo, 0, result, iov, iovcnt); + /* log_printhex (result, 20, "fingerpint: "); */ + + /* Better clear the first element because it was set by us. */ + iov[0].size = 0; + iov[0].off = 0; + iov[0].len = 0; + iov[0].data = NULL; + + if (!err && r_resultlen) + *r_resultlen = (hashalgo == GCRY_MD_SHA1)? 20 : 32; + + return err; +} + + +gpg_error_t +compute_openpgp_fpr_rsa (int keyversion, unsigned long timestamp, + const unsigned char *m, unsigned int mlen, + const unsigned char *e, unsigned int elen, + unsigned char *result, unsigned int *r_resultlen) +{ + gcry_buffer_t iov[5] = { {0} }; + unsigned char nbits_m[2], nbits_e[2]; + unsigned int n; + + /* Strip leading zeroes. */ + for (; mlen && !*m; mlen--, m++) + ; + for (; elen && !*e; elen--, e++) + ; + + /* Count bits. */ + n = count_bits (m, mlen); + nbits_m[0] = n >> 8; + nbits_m[1] = n; + + n = count_bits (e, elen); + nbits_e[0] = n >> 8; + nbits_e[1] = n; + + /* Put parms into the array. Note that iov[0] is reserved. */ + iov[1].len = 2; + iov[1].data = nbits_m; + iov[2].len = mlen; + iov[2].data = (void*)m; + iov[3].len = 2; + iov[3].data = nbits_e; + iov[4].len = elen; + iov[4].data = (void*)e; + + return compute_openpgp_fpr (keyversion, PUBKEY_ALGO_RSA, timestamp, + iov, 5, result, r_resultlen); +} + + +/* Determine KDF hash algorithm and KEK encryption algorithm by CURVE. + * The returned buffer has a length of 4. + * Note: This needs to be kept in sync with the table in g10/ecdh.c */ +static const unsigned char* +default_ecdh_params (unsigned int nbits) +{ + /* See RFC-6637 for those constants. + 0x03: Number of bytes + 0x01: Version for this parameter format + KEK digest algorithm + KEK cipher algorithm + */ + if (nbits <= 256) + return (const unsigned char*)"\x03\x01\x08\x07"; + else if (nbits <= 384) + return (const unsigned char*)"\x03\x01\x09\x09"; + else + return (const unsigned char*)"\x03\x01\x0a\x09"; +} + + +gpg_error_t +compute_openpgp_fpr_ecc (int keyversion, unsigned long timestamp, + const char *curvename, int for_encryption, + const unsigned char *q, unsigned int qlen, + const unsigned char *kdf, unsigned int kdflen, + unsigned char *result, unsigned int *r_resultlen) +{ + gpg_error_t err; + const char *curveoidstr; + gcry_mpi_t curveoid = NULL; + unsigned int curvebits; + int pgpalgo; + const unsigned char *oidraw; + size_t oidrawlen; + gcry_buffer_t iov[5] = { {0} }; + unsigned int iovlen; + unsigned char nbits_q[2]; + unsigned int n; + + curveoidstr = openpgp_curve_to_oid (curvename, &curvebits, &pgpalgo); + err = openpgp_oid_from_str (curveoidstr, &curveoid); + if (err) + goto leave; + oidraw = gcry_mpi_get_opaque (curveoid, &n); + if (!oidraw) + { + err = gpg_error_from_syserror (); + goto leave; + } + oidrawlen = (n+7)/8; + + /* If the curve does not enforce a certain algorithm, we use the + * for_encryption flag to decide which algo to use. */ + if (!pgpalgo) + pgpalgo = for_encryption? PUBKEY_ALGO_ECDH : PUBKEY_ALGO_ECDSA; + + /* Count bits. */ + n = count_sos_bits (q, qlen); + nbits_q[0] = n >> 8; + nbits_q[1] = n; + + /* Put parms into the array. Note that iov[0] is reserved. */ + iov[1].len = oidrawlen; + iov[1].data = (void*)oidraw; + iov[2].len = 2; + iov[2].data = nbits_q; + iov[3].len = qlen; + iov[3].data = (void*)q; + iovlen = 4; + if (pgpalgo == PUBKEY_ALGO_ECDH) + { + if (!kdf || !kdflen || !kdf[0]) + { + /* No KDF given - use the default. */ + kdflen = 4; + kdf = default_ecdh_params (curvebits); + } + iov[4].len = kdflen; + iov[4].data = (void*)kdf; + iovlen++; + } + + err = compute_openpgp_fpr (keyversion, pgpalgo, timestamp, + iov, iovlen, result, r_resultlen); + + leave: + gcry_mpi_release (curveoid); + return err; +} diff --git a/common/openpgp-oid.c b/common/openpgp-oid.c new file mode 100644 index 0000000..943fe3e --- /dev/null +++ b/common/openpgp-oid.c @@ -0,0 +1,472 @@ +/* openpgp-oids.c - OID helper for OpenPGP + * Copyright (C) 2011 Free Software Foundation, Inc. + * Copyright (C) 2013 Werner Koch + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdlib.h> +#include <errno.h> +#include <ctype.h> +#include <assert.h> + +#include "util.h" +#include "openpgpdefs.h" + +/* A table with all our supported OpenPGP curves. */ +static struct { + const char *name; /* Standard name. */ + const char *oidstr; /* IETF formatted OID. */ + unsigned int nbits; /* Nominal bit length of the curve. */ + const char *alias; /* NULL or alternative name of the curve. */ + int pubkey_algo; /* Required OpenPGP algo or 0 for ECDSA/ECDH. */ +} oidtable[] = { + + { "Curve25519", "1.3.6.1.4.1.3029.1.5.1", 255, "cv25519", PUBKEY_ALGO_ECDH }, + { "Ed25519", "1.3.6.1.4.1.11591.15.1", 255, "ed25519", PUBKEY_ALGO_EDDSA }, + + { "NIST P-256", "1.2.840.10045.3.1.7", 256, "nistp256" }, + { "NIST P-384", "1.3.132.0.34", 384, "nistp384" }, + { "NIST P-521", "1.3.132.0.35", 521, "nistp521" }, + + { "brainpoolP256r1", "1.3.36.3.3.2.8.1.1.7", 256 }, + { "brainpoolP384r1", "1.3.36.3.3.2.8.1.1.11", 384 }, + { "brainpoolP512r1", "1.3.36.3.3.2.8.1.1.13", 512 }, + + { "secp256k1", "1.3.132.0.10", 256 }, + + { NULL, NULL, 0} +}; + + +/* The OID for Curve Ed25519 in OpenPGP format. */ +static const char oid_ed25519[] = + { 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xda, 0x47, 0x0f, 0x01 }; + +/* The OID for Curve25519 in OpenPGP format. */ +static const char oid_cv25519[] = + { 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x97, 0x55, 0x01, 0x05, 0x01 }; + + +/* Helper for openpgp_oid_from_str. */ +static size_t +make_flagged_int (unsigned long value, char *buf, size_t buflen) +{ + int more = 0; + int shift; + + /* fixme: figure out the number of bits in an ulong and start with + that value as shift (after making it a multiple of 7) a more + straigtforward implementation is to do it in reverse order using + a temporary buffer - saves a lot of compares */ + for (more=0, shift=28; shift > 0; shift -= 7) + { + if (more || value >= (1<<shift)) + { + buf[buflen++] = 0x80 | (value >> shift); + value -= (value >> shift) << shift; + more = 1; + } + } + buf[buflen++] = value; + return buflen; +} + + +/* Convert the OID given in dotted decimal form in STRING to an DER + * encoding and store it as an opaque value at R_MPI. The format of + * the DER encoded is not a regular ASN.1 object but the modified + * format as used by OpenPGP for the ECC curve description. On error + * the function returns and error code an NULL is stored at R_BUG. + * Note that scanning STRING stops at the first white space + * character. */ +gpg_error_t +openpgp_oid_from_str (const char *string, gcry_mpi_t *r_mpi) +{ + unsigned char *buf; + size_t buflen; + unsigned long val1, val; + const char *endp; + int arcno; + + *r_mpi = NULL; + + if (!string || !*string) + return gpg_error (GPG_ERR_INV_VALUE); + + /* We can safely assume that the encoded OID is shorter than the string. */ + buf = xtrymalloc (1 + strlen (string) + 2); + if (!buf) + return gpg_error_from_syserror (); + /* Save the first byte for the length. */ + buflen = 1; + + val1 = 0; /* Avoid compiler warning. */ + arcno = 0; + do { + arcno++; + val = strtoul (string, (char**)&endp, 10); + if (!digitp (string) || !(*endp == '.' || !*endp)) + { + xfree (buf); + return gpg_error (GPG_ERR_INV_OID_STRING); + } + if (*endp == '.') + string = endp+1; + + if (arcno == 1) + { + if (val > 2) + break; /* Not allowed, error caught below. */ + val1 = val; + } + else if (arcno == 2) + { /* Need to combine the first two arcs in one octet. */ + if (val1 < 2) + { + if (val > 39) + { + xfree (buf); + return gpg_error (GPG_ERR_INV_OID_STRING); + } + buf[buflen++] = val1*40 + val; + } + else + { + val += 80; + buflen = make_flagged_int (val, buf, buflen); + } + } + else + { + buflen = make_flagged_int (val, buf, buflen); + } + } while (*endp == '.'); + + if (arcno == 1 || buflen < 2 || buflen > 254 ) + { /* It is not possible to encode only the first arc. */ + xfree (buf); + return gpg_error (GPG_ERR_INV_OID_STRING); + } + + *buf = buflen - 1; + *r_mpi = gcry_mpi_set_opaque (NULL, buf, buflen * 8); + if (!*r_mpi) + { + xfree (buf); + return gpg_error_from_syserror (); + } + return 0; +} + + +/* Return a malloced string representation of the OID in the buffer + * (BUF,LEN). In case of an error NULL is returned and ERRNO is set. + * As per OpenPGP spec the first byte of the buffer is the length of + * the rest; the function performs a consistency check. */ +char * +openpgp_oidbuf_to_str (const unsigned char *buf, size_t len) +{ + char *string, *p; + int n = 0; + unsigned long val, valmask; + + valmask = (unsigned long)0xfe << (8 * (sizeof (valmask) - 1)); + /* The first bytes gives the length; check consistency. */ + + if (!len || buf[0] != len -1) + { + gpg_err_set_errno (EINVAL); + return NULL; + } + /* Skip length byte. */ + len--; + buf++; + + /* To calculate the length of the string we can safely assume an + upper limit of 3 decimal characters per byte. Two extra bytes + account for the special first octect */ + string = p = xtrymalloc (len*(1+3)+2+1); + if (!string) + return NULL; + if (!len) + { + *p = 0; + return string; + } + + if (buf[0] < 40) + p += sprintf (p, "0.%d", buf[n]); + else if (buf[0] < 80) + p += sprintf (p, "1.%d", buf[n]-40); + else { + val = buf[n] & 0x7f; + while ( (buf[n]&0x80) && ++n < len ) + { + if ( (val & valmask) ) + goto badoid; /* Overflow. */ + val <<= 7; + val |= buf[n] & 0x7f; + } + if (val < 80) + goto badoid; + val -= 80; + sprintf (p, "2.%lu", val); + p += strlen (p); + } + for (n++; n < len; n++) + { + val = buf[n] & 0x7f; + while ( (buf[n]&0x80) && ++n < len ) + { + if ( (val & valmask) ) + goto badoid; /* Overflow. */ + val <<= 7; + val |= buf[n] & 0x7f; + } + sprintf (p, ".%lu", val); + p += strlen (p); + } + + *p = 0; + return string; + + badoid: + /* Return a special OID (gnu.gnupg.badoid) to indicate the error + case. The OID is broken and thus we return one which can't do + any harm. Formally this does not need to be a bad OID but an OID + with an arc that can't be represented in a 32 bit word is more + than likely corrupt. */ + xfree (string); + return xtrystrdup ("1.3.6.1.4.1.11591.2.12242973"); +} + + +/* Return a malloced string representation of the OID in the opaque + * MPI A. In case of an error NULL is returned and ERRNO is set. */ +char * +openpgp_oid_to_str (gcry_mpi_t a) +{ + const unsigned char *buf; + unsigned int lengthi; + + if (!a + || !gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE) + || !(buf = gcry_mpi_get_opaque (a, &lengthi))) + { + gpg_err_set_errno (EINVAL); + return NULL; + } + + buf = gcry_mpi_get_opaque (a, &lengthi); + return openpgp_oidbuf_to_str (buf, (lengthi+7)/8); +} + + +/* Return true if (BUF,LEN) represents the OID for Ed25519. */ +int +openpgp_oidbuf_is_ed25519 (const void *buf, size_t len) +{ + return (buf && len == DIM (oid_ed25519) + && !memcmp (buf, oid_ed25519, DIM (oid_ed25519))); +} + + +/* Return true if A represents the OID for Ed25519. */ +int +openpgp_oid_is_ed25519 (gcry_mpi_t a) +{ + const unsigned char *buf; + unsigned int nbits; + + if (!a || !gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE)) + return 0; + + buf = gcry_mpi_get_opaque (a, &nbits); + return openpgp_oidbuf_is_ed25519 (buf, (nbits+7)/8); +} + + +/* Return true if (BUF,LEN) represents the OID for Curve25519. */ +int +openpgp_oidbuf_is_cv25519 (const void *buf, size_t len) +{ + return (buf && len == DIM (oid_cv25519) + && !memcmp (buf, oid_cv25519, DIM (oid_cv25519))); +} + + +/* Return true if the MPI A represents the OID for Curve25519. */ +int +openpgp_oid_is_cv25519 (gcry_mpi_t a) +{ + const unsigned char *buf; + unsigned int nbits; + + if (!a || !gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE)) + return 0; + + buf = gcry_mpi_get_opaque (a, &nbits); + return openpgp_oidbuf_is_cv25519 (buf, (nbits+7)/8); +} + + +/* Map the Libgcrypt ECC curve NAME to an OID. If R_NBITS is not NULL + store the bit size of the curve there. Returns NULL for unknown + curve names. If R_ALGO is not NULL and a specific ECC algorithm is + required for this curve its OpenPGP algorithm number is stored + there; otherwise 0 is stored which indicates that ECDSA or ECDH can + be used. */ +const char * +openpgp_curve_to_oid (const char *name, unsigned int *r_nbits, int *r_algo) +{ + int i; + unsigned int nbits = 0; + const char *oidstr = NULL; + int algo = 0; + + if (name) + { + for (i=0; oidtable[i].name; i++) + if (!strcmp (oidtable[i].name, name) + || (oidtable[i].alias && !strcmp (oidtable[i].alias, name))) + { + oidstr = oidtable[i].oidstr; + nbits = oidtable[i].nbits; + algo = oidtable[i].pubkey_algo; + break; + } + if (!oidtable[i].name) + { + /* If not found assume the input is already an OID and check + whether we support it. */ + for (i=0; oidtable[i].name; i++) + if (!strcmp (name, oidtable[i].oidstr)) + { + oidstr = oidtable[i].oidstr; + nbits = oidtable[i].nbits; + algo = oidtable[i].pubkey_algo; + break; + } + } + } + + if (r_nbits) + *r_nbits = nbits; + if (r_algo) + *r_algo = algo; + return oidstr; +} + + +/* Map an OpenPGP OID to the Libgcrypt curve NAME. Returns NULL for + unknown curve names. Unless CANON is set we prefer an alias name + here which is more suitable for printing. */ +const char * +openpgp_oid_to_curve (const char *oidstr, int canon) +{ + int i; + + if (!oidstr) + return NULL; + + for (i=0; oidtable[i].name; i++) + if (!strcmp (oidtable[i].oidstr, oidstr)) + return !canon && oidtable[i].alias? oidtable[i].alias : oidtable[i].name; + + return NULL; +} + + +/* Return true if the curve with NAME is supported. */ +static int +curve_supported_p (const char *name) +{ + int result = 0; + gcry_sexp_t keyparms; + + if (!gcry_sexp_build (&keyparms, NULL, "(public-key(ecc(curve %s)))", name)) + { + result = !!gcry_pk_get_curve (keyparms, 0, NULL); + gcry_sexp_release (keyparms); + } + return result; +} + + +/* Enumerate available and supported OpenPGP curves. The caller needs + to set the integer variable at ITERP to zero and keep on calling + this function until NULL is returned. */ +const char * +openpgp_enum_curves (int *iterp) +{ + int idx = *iterp; + + while (idx >= 0 && idx < DIM (oidtable) && oidtable[idx].name) + { + if (curve_supported_p (oidtable[idx].name)) + { + *iterp = idx + 1; + return oidtable[idx].alias? oidtable[idx].alias : oidtable[idx].name; + } + idx++; + } + *iterp = idx; + return NULL; +} + + +/* Return the Libgcrypt name for the gpg curve NAME if supported. If + * R_ALGO is not NULL the required OpenPGP public key algo or 0 is + * stored at that address. If R_NBITS is not NULL the nominal bitsize + * of the curves is stored there. NULL is returned if the curve is + * not supported. */ +const char * +openpgp_is_curve_supported (const char *name, int *r_algo, + unsigned int *r_nbits) +{ + int idx; + + if (r_algo) + *r_algo = 0; + if (r_nbits) + *r_nbits = 0; + for (idx = 0; idx < DIM (oidtable) && oidtable[idx].name; idx++) + { + if ((!strcmp (name, oidtable[idx].name) + || (oidtable[idx].alias && !strcmp (name, (oidtable[idx].alias)))) + && curve_supported_p (oidtable[idx].name)) + { + if (r_algo) + *r_algo = oidtable[idx].pubkey_algo; + if (r_nbits) + *r_nbits = oidtable[idx].nbits; + return oidtable[idx].name; + } + } + return NULL; +} diff --git a/common/openpgpdefs.h b/common/openpgpdefs.h new file mode 100644 index 0000000..05f3621 --- /dev/null +++ b/common/openpgpdefs.h @@ -0,0 +1,230 @@ +/* openpgpdefs.h - Constants from the OpenPGP standard (rfc2440) + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, + * 2006 Free Software Foundation, Inc. + * Copyright (C) 2014 Werner Koch + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef GNUPG_COMMON_OPENPGPDEFS_H +#define GNUPG_COMMON_OPENPGPDEFS_H + +typedef enum + { + PKT_NONE = 0, + PKT_PUBKEY_ENC = 1, /* Public key encrypted packet. */ + PKT_SIGNATURE = 2, /* Secret key encrypted packet. */ + PKT_SYMKEY_ENC = 3, /* Session key packet. */ + PKT_ONEPASS_SIG = 4, /* One pass sig packet. */ + PKT_SECRET_KEY = 5, /* Secret key. */ + PKT_PUBLIC_KEY = 6, /* Public key. */ + PKT_SECRET_SUBKEY = 7, /* Secret subkey. */ + PKT_COMPRESSED = 8, /* Compressed data packet. */ + PKT_ENCRYPTED = 9, /* Conventional encrypted data. */ + PKT_MARKER = 10, /* Marker packet. */ + PKT_PLAINTEXT = 11, /* Literal data packet. */ + PKT_RING_TRUST = 12, /* Keyring trust packet. */ + PKT_USER_ID = 13, /* User id packet. */ + PKT_PUBLIC_SUBKEY = 14, /* Public subkey. */ + PKT_OLD_COMMENT = 16, /* Comment packet from an OpenPGP draft. */ + PKT_ATTRIBUTE = 17, /* PGP's attribute packet. */ + PKT_ENCRYPTED_MDC = 18, /* Integrity protected encrypted data. */ + PKT_MDC = 19, /* Manipulation detection code packet. */ + PKT_ENCRYPTED_AEAD= 20, /* AEAD encrypted data packet. */ + PKT_COMMENT = 61, /* new comment packet (GnuPG specific). */ + PKT_GPG_CONTROL = 63 /* internal control packet (GnuPG specific). */ + } +pkttype_t; + +static inline const char * +pkttype_str (pkttype_t type) +{ + switch (type) + { + case PKT_PUBKEY_ENC: return "PUBKEY_ENC"; + case PKT_SIGNATURE: return "SIGNATURE"; + case PKT_SYMKEY_ENC: return "SYMKEY_ENC"; + case PKT_ONEPASS_SIG: return "ONEPASS_SIG"; + case PKT_SECRET_KEY: return "SECRET_KEY"; + case PKT_PUBLIC_KEY: return "PUBLIC_KEY"; + case PKT_SECRET_SUBKEY: return "SECRET_SUBKEY"; + case PKT_COMPRESSED: return "COMPRESSED"; + case PKT_ENCRYPTED: return "ENCRYPTED"; + case PKT_MARKER: return "MARKER"; + case PKT_PLAINTEXT: return "PLAINTEXT"; + case PKT_RING_TRUST: return "RING_TRUST"; + case PKT_USER_ID: return "USER_ID"; + case PKT_PUBLIC_SUBKEY: return "PUBLIC_SUBKEY"; + case PKT_OLD_COMMENT: return "OLD_COMMENT"; + case PKT_ATTRIBUTE: return "ATTRIBUTE"; + case PKT_ENCRYPTED_MDC: return "ENCRYPTED_MDC"; + case PKT_MDC: return "MDC"; + case PKT_COMMENT: return "COMMENT"; + case PKT_GPG_CONTROL: return "GPG_CONTROL"; + default: return "unknown packet type"; + } +} + +typedef enum + { + SIGSUBPKT_TEST_CRITICAL = -3, + SIGSUBPKT_LIST_UNHASHED = -2, + SIGSUBPKT_LIST_HASHED = -1, + SIGSUBPKT_NONE = 0, + SIGSUBPKT_SIG_CREATED = 2, /* Signature creation time. */ + SIGSUBPKT_SIG_EXPIRE = 3, /* Signature expiration time. */ + SIGSUBPKT_EXPORTABLE = 4, /* Exportable. */ + SIGSUBPKT_TRUST = 5, /* Trust signature. */ + SIGSUBPKT_REGEXP = 6, /* Regular expression. */ + SIGSUBPKT_REVOCABLE = 7, /* Revocable. */ + SIGSUBPKT_KEY_EXPIRE = 9, /* Key expiration time. */ + SIGSUBPKT_ARR = 10, /* Additional recipient request. */ + SIGSUBPKT_PREF_SYM = 11, /* Preferred symmetric algorithms. */ + SIGSUBPKT_REV_KEY = 12, /* Revocation key. */ + SIGSUBPKT_ISSUER = 16, /* Issuer key ID. */ + SIGSUBPKT_NOTATION = 20, /* Notation data. */ + SIGSUBPKT_PREF_HASH = 21, /* Preferred hash algorithms. */ + SIGSUBPKT_PREF_COMPR = 22, /* Preferred compression algorithms. */ + SIGSUBPKT_KS_FLAGS = 23, /* Key server preferences. */ + SIGSUBPKT_PREF_KS = 24, /* Preferred keyserver. */ + SIGSUBPKT_PRIMARY_UID = 25, /* Primary user id. */ + SIGSUBPKT_POLICY = 26, /* Policy URL. */ + SIGSUBPKT_KEY_FLAGS = 27, /* Key flags. */ + SIGSUBPKT_SIGNERS_UID = 28, /* Signer's user id. */ + SIGSUBPKT_REVOC_REASON = 29, /* Reason for revocation. */ + SIGSUBPKT_FEATURES = 30, /* Feature flags. */ + + SIGSUBPKT_SIGNATURE = 32, /* Embedded signature. */ + SIGSUBPKT_ISSUER_FPR = 33, /* Issuer fingerprint. */ + SIGSUBPKT_PREF_AEAD = 34, /* Preferred AEAD algorithms. */ + + SIGSUBPKT_KEY_BLOCK = 38, /* Entire key used. */ + + SIGSUBPKT_FLAG_CRITICAL = 128 + } +sigsubpkttype_t; + + +/* Note that we encode the AEAD algo in a 3 bit field at some places. */ +typedef enum + { + AEAD_ALGO_NONE = 0, + AEAD_ALGO_EAX = 1, + AEAD_ALGO_OCB = 2 + } +aead_algo_t; + + +typedef enum + { + CIPHER_ALGO_NONE = 0, + CIPHER_ALGO_IDEA = 1, + CIPHER_ALGO_3DES = 2, + CIPHER_ALGO_CAST5 = 3, + CIPHER_ALGO_BLOWFISH = 4, /* 128 bit */ + /* 5 & 6 are reserved */ + CIPHER_ALGO_AES = 7, + CIPHER_ALGO_AES192 = 8, + CIPHER_ALGO_AES256 = 9, + CIPHER_ALGO_TWOFISH = 10, /* 256 bit */ + CIPHER_ALGO_CAMELLIA128 = 11, + CIPHER_ALGO_CAMELLIA192 = 12, + CIPHER_ALGO_CAMELLIA256 = 13, + CIPHER_ALGO_PRIVATE10 = 110 + } +cipher_algo_t; + + +typedef enum + { + PUBKEY_ALGO_RSA = 1, + PUBKEY_ALGO_RSA_E = 2, /* RSA encrypt only (legacy). */ + PUBKEY_ALGO_RSA_S = 3, /* RSA sign only (legacy). */ + PUBKEY_ALGO_ELGAMAL_E = 16, /* Elgamal encrypt only. */ + PUBKEY_ALGO_DSA = 17, + PUBKEY_ALGO_ECDH = 18, /* RFC-6637 */ + PUBKEY_ALGO_ECDSA = 19, /* RFC-6637 */ + PUBKEY_ALGO_ELGAMAL = 20, /* Elgamal encrypt+sign (legacy). */ + /* 21 reserved by OpenPGP. */ + PUBKEY_ALGO_EDDSA = 22, /* EdDSA (not yet assigned). */ + PUBKEY_ALGO_PRIVATE10 = 110 + } +pubkey_algo_t; + + +typedef enum + { + DIGEST_ALGO_MD5 = 1, + DIGEST_ALGO_SHA1 = 2, + DIGEST_ALGO_RMD160 = 3, + /* 4, 5, 6, and 7 are reserved. */ + DIGEST_ALGO_SHA256 = 8, + DIGEST_ALGO_SHA384 = 9, + DIGEST_ALGO_SHA512 = 10, + DIGEST_ALGO_SHA224 = 11, + DIGEST_ALGO_PRIVATE10 = 110 + } +digest_algo_t; + + +typedef enum + { + COMPRESS_ALGO_NONE = 0, + COMPRESS_ALGO_ZIP = 1, + COMPRESS_ALGO_ZLIB = 2, + COMPRESS_ALGO_BZIP2 = 3, + COMPRESS_ALGO_PRIVATE10 = 110 + } +compress_algo_t; + +/* Limits to be used for static arrays. */ +#define OPENPGP_MAX_NPKEY 5 /* Maximum number of public key parameters. */ +#define OPENPGP_MAX_NSKEY 7 /* Maximum number of secret key parameters. */ +#define OPENPGP_MAX_NSIG 2 /* Maximum number of signature parameters. */ +#define OPENPGP_MAX_NENC 2 /* Maximum number of encryption parameters. */ + + +/*-- openpgp-fpr.c --*/ +gpg_error_t compute_openpgp_fpr (int keyversion, int pgpalgo, + unsigned long timestamp, + gcry_buffer_t *iov, int iovcnt, + unsigned char *result, + unsigned int *r_resultlen); +gpg_error_t compute_openpgp_fpr_rsa (int keyversion, + unsigned long timestamp, + const unsigned char *m, unsigned int mlen, + const unsigned char *e, unsigned int elen, + unsigned char *result, + unsigned int *r_resultlen); +gpg_error_t compute_openpgp_fpr_ecc (int keyversion, + unsigned long timestamp, + const char *curvename, int for_encryption, + const unsigned char *q, unsigned int qlen, + const unsigned char *kdf, + unsigned int kdflen, + unsigned char *result, + unsigned int *r_resultlen); + + +#endif /*GNUPG_COMMON_OPENPGPDEFS_H*/ diff --git a/common/percent.c b/common/percent.c new file mode 100644 index 0000000..224de78 --- /dev/null +++ b/common/percent.c @@ -0,0 +1,321 @@ +/* percent.c - Percent escaping + * Copyright (C) 2008, 2009 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdlib.h> +#include <errno.h> +#include <ctype.h> +#include <assert.h> + +#include "util.h" + + +/* Create a newly alloced string from STRING with all spaces and + control characters converted to plus signs or %xx sequences. The + function returns the new string or NULL in case of a malloc + failure. + + Note that we also escape the quote character to work around a bug + in the mingw32 runtime which does not correcty handle command line + quoting. We correctly double the quote mark when calling a program + (i.e. gpg-protect-tool), but the pre-main code does not notice the + double quote as an escaped quote. We do this also on POSIX systems + for consistency. */ +char * +percent_plus_escape (const char *string) +{ + char *buffer, *p; + const char *s; + size_t length; + + for (length=1, s=string; *s; s++) + { + if (*s == '+' || *s == '\"' || *s == '%' + || *(const unsigned char *)s < 0x20) + length += 3; + else + length++; + } + + buffer = p = xtrymalloc (length); + if (!buffer) + return NULL; + + for (s=string; *s; s++) + { + if (*s == '+' || *s == '\"' || *s == '%' + || *(const unsigned char *)s < 0x20) + { + snprintf (p, 4, "%%%02X", *(unsigned char *)s); + p += 3; + } + else if (*s == ' ') + *p++ = '+'; + else + *p++ = *s; + } + *p = 0; + + return buffer; + +} + + +/* Create a newly malloced string from (DATA,DATALEN) with embedded + * nuls quoted as %00. The standard percent unescaping can be used to + * reverse this encoding. With PLUS_ESCAPE set plus-escaping (spaces + * are replaced by a '+') and escaping of characters with values less + * than 0x20 is used. If PREFIX is not NULL it will be prepended to + * the output in standard escape format; that is PLUS_ESCAPING is + * ignored for PREFIX. */ +char * +percent_data_escape (int plus_escape, const char *prefix, + const void *data, size_t datalen) +{ + char *buffer, *p; + const unsigned char *s; + size_t n; + size_t length = 1; + + if (prefix) + { + for (s = prefix; *s; s++) + { + if (*s == '%' || *s < 0x20) + length += 3; + else + length++; + } + } + + for (s=data, n=datalen; n; s++, n--) + { + if (!*s || *s == '%' || (plus_escape && (*s < ' ' || *s == '+'))) + length += 3; + else + length++; + } + + buffer = p = xtrymalloc (length); + if (!buffer) + return NULL; + + if (prefix) + { + for (s = prefix; *s; s++) + { + if (*s == '%' || *s < 0x20) + { + snprintf (p, 4, "%%%02X", *s); + p += 3; + } + else + *p++ = *s; + } + } + + for (s=data, n=datalen; n; s++, n--) + { + if (!*s) + { + memcpy (p, "%00", 3); + p += 3; + } + else if (*s == '%') + { + memcpy (p, "%25", 3); + p += 3; + } + else if (plus_escape && *s == ' ') + { + *p++ = '+'; + } + else if (plus_escape && (*s < ' ' || *s == '+')) + { + snprintf (p, 4, "%%%02X", *s); + p += 3; + } + else + *p++ = *s; + } + *p = 0; + + return buffer; +} + + +/* Do the percent and plus/space unescaping from STRING to BUFFER and + return the length of the valid buffer. Plus unescaping is only + done if WITHPLUS is true. An escaped Nul character will be + replaced by NULREPL. */ +static size_t +do_unescape (unsigned char *buffer, const unsigned char *string, + int withplus, int nulrepl) +{ + unsigned char *p = buffer; + + while (*string) + { + if (*string == '%' && string[1] && string[2]) + { + string++; + *p = xtoi_2 (string); + if (!*p) + *p = nulrepl; + string++; + } + else if (*string == '+' && withplus) + *p = ' '; + else + *p = *string; + p++; + string++; + } + + return (p - buffer); +} + + +/* Count space required after unescaping STRING. Note that this will + never be larger than strlen (STRING). */ +static size_t +count_unescape (const unsigned char *string) +{ + size_t n = 0; + + while (*string) + { + if (*string == '%' && string[1] && string[2]) + { + string++; + string++; + } + string++; + n++; + } + + return n; +} + + +/* Helper. */ +static char * +do_plus_or_plain_unescape (const char *string, int withplus, int nulrepl) +{ + size_t nbytes, n; + char *newstring; + + nbytes = count_unescape (string); + newstring = xtrymalloc (nbytes+1); + if (newstring) + { + n = do_unescape (newstring, string, withplus, nulrepl); + assert (n == nbytes); + newstring[n] = 0; + } + return newstring; +} + + +/* Create a new allocated string from STRING with all "%xx" sequences + decoded and all plus signs replaced by a space. Embedded Nul + characters are replaced by the value of NULREPL. The function + returns the new string or NULL in case of a malloc failure. */ +char * +percent_plus_unescape (const char *string, int nulrepl) +{ + return do_plus_or_plain_unescape (string, 1, nulrepl); +} + + +/* Create a new allocated string from STRING with all "%xx" sequences + decoded. Embedded Nul characters are replaced by the value of + NULREPL. The function returns the new string or NULL in case of a + malloc failure. */ +char * +percent_unescape (const char *string, int nulrepl) +{ + return do_plus_or_plain_unescape (string, 0, nulrepl); +} + + +static size_t +do_unescape_inplace (char *string, int withplus, int nulrepl) +{ + unsigned char *p, *p0; + + p = p0 = string; + while (*string) + { + if (*string == '%' && string[1] && string[2]) + { + string++; + *p = xtoi_2 (string); + if (!*p) + *p = nulrepl; + string++; + } + else if (*string == '+' && withplus) + *p = ' '; + else + *p = *string; + p++; + string++; + } + + return (p - p0); +} + + +/* Perform percent and plus unescaping in STRING and return the new + valid length of the string. Embedded Nul characters are replaced + by the value of NULREPL. A terminating Nul character is not + inserted; the caller might want to call this function this way: + + foo[percent_plus_unescape_inplace (foo, 0)] = 0; + */ +size_t +percent_plus_unescape_inplace (char *string, int nulrepl) +{ + return do_unescape_inplace (string, 1, nulrepl); +} + + +/* Perform percent unescaping in STRING and return the new valid + length of the string. Embedded Nul characters are replaced by the + value of NULREPL. A terminating Nul character is not inserted; the + caller might want to call this function this way: + + foo[percent_unescape_inplace (foo, 0)] = 0; + */ +size_t +percent_unescape_inplace (char *string, int nulrepl) +{ + return do_unescape_inplace (string, 0, nulrepl); +} diff --git a/common/recsel.c b/common/recsel.c new file mode 100644 index 0000000..df77b57 --- /dev/null +++ b/common/recsel.c @@ -0,0 +1,632 @@ +/* recsel.c - Record selection + * Copyright (C) 2014, 2016 Werner Koch + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> + +#include "util.h" +#include "recsel.h" + +/* Select operators. */ +typedef enum + { + SELECT_SAME, + SELECT_SUB, + SELECT_NONEMPTY, + SELECT_ISTRUE, + SELECT_EQ, /* Numerically equal. */ + SELECT_LE, + SELECT_GE, + SELECT_LT, + SELECT_GT, + SELECT_STRLE, /* String is less or equal. */ + SELECT_STRGE, + SELECT_STRLT, + SELECT_STRGT + } select_op_t; + + +/* Definition for a select expression. */ +struct recsel_expr_s +{ + recsel_expr_t next; + select_op_t op; /* Operation code. */ + unsigned int not:1; /* Negate operators. */ + unsigned int disjun:1;/* Start of a disjunction. */ + unsigned int xcase:1; /* String match is case sensitive. */ + const char *value; /* (Points into NAME.) */ + long numvalue; /* strtol of VALUE. */ + char name[1]; /* Name of the property. */ +}; + + +/* Helper */ +static inline gpg_error_t +my_error_from_syserror (void) +{ + return gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); +} + +/* Helper */ +static inline gpg_error_t +my_error (gpg_err_code_t ec) +{ + return gpg_err_make (default_errsource, ec); +} + + +/* This is a case-sensitive version of our memistr. I wonder why no + * standard function memstr exists but I better do not use the name + * memstr to avoid future conflicts. + * + * FIXME: Move this to a stringhelp.c + */ +static const char * +my_memstr (const void *buffer, size_t buflen, const char *sub) +{ + const unsigned char *buf = buffer; + const unsigned char *t = (const unsigned char *)buf; + const unsigned char *s = (const unsigned char *)sub; + size_t n = buflen; + + for ( ; n ; t++, n-- ) + { + if (*t == *s) + { + for (buf = t++, buflen = n--, s++; n && *t ==*s; t++, s++, n--) + ; + if (!*s) + return (const char*)buf; + t = (const unsigned char *)buf; + s = (const unsigned char *)sub ; + n = buflen; + } + } + return NULL; +} + + +/* Return a pointer to the next logical connection operator or NULL if + * none. */ +static char * +find_next_lc (char *string) +{ + char *p1, *p2; + + p1 = strchr (string, '&'); + if (p1 && p1[1] != '&') + p1 = NULL; + p2 = strchr (string, '|'); + if (p2 && p2[1] != '|') + p2 = NULL; + if (p1 && !p2) + return p1; + if (!p1) + return p2; + return p1 < p2 ? p1 : p2; +} + + +/* Parse an expression. The expression syntax is: + * + * [<lc>] {{<flag>} PROPNAME <op> VALUE [<lc>]} + * + * A [] indicates an optional part, a {} a repetition. PROPNAME and + * VALUE may not be the empty string. White space between the + * elements is ignored. Numerical values are computed as long int; + * standard C notation applies. <lc> is the logical connection + * operator; either "&&" for a conjunction or "||" for a disjunction. + * A conjunction is assumed at the begin of an expression and + * conjunctions have higher precedence than disjunctions. If VALUE + * starts with one of the characters used in any <op> a space after + * the <op> is required. A VALUE is terminated by an <lc> unless the + * "--" <flag> is used in which case the VALUE spans to the end of the + * expression. <op> may be any of + * + * =~ Substring must match + * !~ Substring must not match + * = The full string must match + * <> The full string must not match + * == The numerical value must match + * != The numerical value must not match + * <= The numerical value of the field must be LE than the value. + * < The numerical value of the field must be LT than the value. + * >= The numerical value of the field must be GT than the value. + * >= The numerical value of the field must be GE than the value. + * -n True if value is not empty (no VALUE parameter allowed). + * -z True if value is empty (no VALUE parameter allowed). + * -t Alias for "PROPNAME != 0" (no VALUE parameter allowed). + * -f Alias for "PROPNAME == 0" (no VALUE parameter allowed). + * + * Values for <flag> must be space separated and any of: + * + * -- VALUE spans to the end of the expression. + * -c The string match in this part is done case-sensitive. + * -t Do not trim leading and trailing spaces from VALUE. + * Note that a space after <op> is here required. + * + * For example four calls to recsel_parse_expr() with these values for + * EXPR + * + * "uid =~ Alfa" + * "&& uid !~ Test" + * "|| uid =~ Alpha" + * "uid !~ Test" + * + * or the equivalent expression + * + * "uid =~ Alfa" && uid !~ Test" || uid =~ Alpha" && "uid !~ Test" + * + * are making a selector for records where the "uid" property contains + * the strings "Alfa" or "Alpha" but not the String "test". + * + * The caller must pass the address of a selector variable to this + * function and initialize the value of the function to NULL before + * the first call. recset_release needs to be called to free the + * selector. + */ +gpg_error_t +recsel_parse_expr (recsel_expr_t *selector, const char *expression) +{ + recsel_expr_t se_head = NULL; + recsel_expr_t se, se2; + char *expr_buffer; + char *expr; + char *s0, *s; + int toend = 0; + int xcase = 0; + int notrim = 0; + int disjun = 0; + char *next_lc = NULL; + + while (*expression == ' ' || *expression == '\t') + expression++; + + expr_buffer = xtrystrdup (expression); + if (!expr_buffer) + return my_error_from_syserror (); + expr = expr_buffer; + + if (*expr == '|' && expr[1] == '|') + { + disjun = 1; + expr += 2; + } + else if (*expr == '&' && expr[1] == '&') + expr += 2; + + next_term: + while (*expr == ' ' || *expr == '\t') + expr++; + + while (*expr == '-') + { + switch (*++expr) + { + case '-': toend = 1; break; + case 'c': xcase = 1; break; + case 't': notrim = 1; break; + default: + log_error ("invalid flag '-%c' in expression\n", *expr); + recsel_release (se_head); + xfree (expr_buffer); + return my_error (GPG_ERR_INV_FLAG); + } + expr++; + while (*expr == ' ' || *expr == '\t') + expr++; + } + + next_lc = toend? NULL : find_next_lc (expr); + if (next_lc) + *next_lc = 0; /* Terminate this term. */ + + se = xtrymalloc (sizeof *se + strlen (expr)); + if (!se) + return my_error_from_syserror (); + strcpy (se->name, expr); + se->next = NULL; + se->not = 0; + se->disjun = disjun; + se->xcase = xcase; + + if (!se_head) + se_head = se; + else + { + for (se2 = se_head; se2->next; se2 = se2->next) + ; + se2->next = se; + } + + + s = strpbrk (expr, "=<>!~-"); + if (!s || s == expr ) + { + log_error ("no field name given in expression\n"); + recsel_release (se_head); + xfree (expr_buffer); + return my_error (GPG_ERR_NO_NAME); + } + s0 = s; + + if (!strncmp (s, "=~", 2)) + { + se->op = SELECT_SUB; + s += 2; + } + else if (!strncmp (s, "!~", 2)) + { + se->op = SELECT_SUB; + se->not = 1; + s += 2; + } + else if (!strncmp (s, "<>", 2)) + { + se->op = SELECT_SAME; + se->not = 1; + s += 2; + } + else if (!strncmp (s, "==", 2)) + { + se->op = SELECT_EQ; + s += 2; + } + else if (!strncmp (s, "!=", 2)) + { + se->op = SELECT_EQ; + se->not = 1; + s += 2; + } + else if (!strncmp (s, "<=", 2)) + { + se->op = SELECT_LE; + s += 2; + } + else if (!strncmp (s, ">=", 2)) + { + se->op = SELECT_GE; + s += 2; + } + else if (!strncmp (s, "<", 1)) + { + se->op = SELECT_LT; + s += 1; + } + else if (!strncmp (s, ">", 1)) + { + se->op = SELECT_GT; + s += 1; + } + else if (!strncmp (s, "=", 1)) + { + se->op = SELECT_SAME; + s += 1; + } + else if (!strncmp (s, "-z", 2)) + { + se->op = SELECT_NONEMPTY; + se->not = 1; + s += 2; + } + else if (!strncmp (s, "-n", 2)) + { + se->op = SELECT_NONEMPTY; + s += 2; + } + else if (!strncmp (s, "-f", 2)) + { + se->op = SELECT_ISTRUE; + se->not = 1; + s += 2; + } + else if (!strncmp (s, "-t", 2)) + { + se->op = SELECT_ISTRUE; + s += 2; + } + else if (!strncmp (s, "-le", 3)) + { + se->op = SELECT_STRLE; + s += 3; + } + else if (!strncmp (s, "-ge", 3)) + { + se->op = SELECT_STRGE; + s += 3; + } + else if (!strncmp (s, "-lt", 3)) + { + se->op = SELECT_STRLT; + s += 3; + } + else if (!strncmp (s, "-gt", 3)) + { + se->op = SELECT_STRGT; + s += 3; + } + else + { + log_error ("invalid operator in expression\n"); + recsel_release (se_head); + xfree (expr_buffer); + return my_error (GPG_ERR_INV_OP); + } + + /* We require that a space is used if the value starts with any of + the operator characters. */ + if (se->op == SELECT_NONEMPTY || se->op == SELECT_ISTRUE) + ; + else if (strchr ("=<>!~", *s)) + { + log_error ("invalid operator in expression\n"); + recsel_release (se_head); + xfree (expr_buffer); + return my_error (GPG_ERR_INV_OP); + } + + if (*s == ' ' || *s == '\t') + s++; + if (!notrim) + while (*s == ' ' || *s == '\t') + s++; + + if (se->op == SELECT_NONEMPTY || se->op == SELECT_ISTRUE) + { + if (*s) + { + log_error ("value given for -n or -z\n"); + recsel_release (se_head); + xfree (expr_buffer); + return my_error (GPG_ERR_SYNTAX); + } + } + else + { + if (!*s) + { + log_error ("no value given in expression\n"); + recsel_release (se_head); + xfree (expr_buffer); + return my_error (GPG_ERR_MISSING_VALUE); + } + } + + se->name[s0 - expr] = 0; + trim_spaces (se->name); + if (!se->name[0]) + { + log_error ("no field name given in expression\n"); + recsel_release (se_head); + xfree (expr_buffer); + return my_error (GPG_ERR_NO_NAME); + } + + if (!notrim) + trim_spaces (se->name + (s - expr)); + se->value = se->name + (s - expr); + if (!se->value[0] && !(se->op == SELECT_NONEMPTY || se->op == SELECT_ISTRUE)) + { + log_error ("no value given in expression\n"); + recsel_release (se_head); + xfree (expr_buffer); + return my_error (GPG_ERR_MISSING_VALUE); + } + + se->numvalue = strtol (se->value, NULL, 0); + + if (next_lc) + { + disjun = next_lc[1] == '|'; + expr = next_lc + 2; + goto next_term; + } + + /* Read:y Append to passes last selector. */ + if (!*selector) + *selector = se_head; + else + { + for (se2 = *selector; se2->next; se2 = se2->next) + ; + se2->next = se_head; + } + + xfree (expr_buffer); + return 0; +} + + +void +recsel_release (recsel_expr_t a) +{ + while (a) + { + recsel_expr_t tmp = a->next; + xfree (a); + a = tmp; + } +} + + +void +recsel_dump (recsel_expr_t selector) +{ + recsel_expr_t se; + + log_debug ("--- Begin selectors ---\n"); + for (se = selector; se; se = se->next) + { + log_debug ("%s %s %s %s '%s'\n", + se==selector? " ": (se->disjun? "||":"&&"), + se->xcase? "-c":" ", + se->name, + se->op == SELECT_SAME? (se->not? "<>":"= "): + se->op == SELECT_SUB? (se->not? "!~":"=~"): + se->op == SELECT_NONEMPTY?(se->not? "-z":"-n"): + se->op == SELECT_ISTRUE? (se->not? "-f":"-t"): + se->op == SELECT_EQ? (se->not? "!=":"=="): + se->op == SELECT_LT? "< ": + se->op == SELECT_LE? "<=": + se->op == SELECT_GT? "> ": + se->op == SELECT_GE? ">=": + se->op == SELECT_STRLT? "-lt": + se->op == SELECT_STRLE? "-le": + se->op == SELECT_STRGT? "-gt": + se->op == SELECT_STRGE? "-ge": + /**/ "[oops]", + se->value); + } + log_debug ("--- End selectors ---\n"); +} + + +/* Return true if the record RECORD has been selected. The GETVAL + * function is called with COOKIE and the NAME of a property used in + * the expression. */ +int +recsel_select (recsel_expr_t selector, + const char *(*getval)(void *cookie, const char *propname), + void *cookie) +{ + recsel_expr_t se; + const char *value; + size_t selen, valuelen; + long numvalue; + int result = 1; + + se = selector; + while (se) + { + value = getval? getval (cookie, se->name) : NULL; + if (!value) + value = ""; + + if (!*value) + { + /* Field is empty. */ + result = 0; + } + else /* Field has a value. */ + { + valuelen = strlen (value); + numvalue = strtol (value, NULL, 0); + selen = strlen (se->value); + + switch (se->op) + { + case SELECT_SAME: + if (se->xcase) + result = (valuelen==selen && !memcmp (value,se->value,selen)); + else + result = (valuelen==selen && !memicmp (value,se->value,selen)); + break; + case SELECT_SUB: + if (se->xcase) + result = !!my_memstr (value, valuelen, se->value); + else + result = !!memistr (value, valuelen, se->value); + break; + case SELECT_NONEMPTY: + result = !!valuelen; + break; + case SELECT_ISTRUE: + result = !!numvalue; + break; + case SELECT_EQ: + result = (numvalue == se->numvalue); + break; + case SELECT_GT: + result = (numvalue > se->numvalue); + break; + case SELECT_GE: + result = (numvalue >= se->numvalue); + break; + case SELECT_LT: + result = (numvalue < se->numvalue); + break; + case SELECT_LE: + result = (numvalue <= se->numvalue); + break; + case SELECT_STRGT: + if (se->xcase) + result = strcmp (value, se->value) > 0; + else + result = strcasecmp (value, se->value) > 0; + break; + case SELECT_STRGE: + if (se->xcase) + result = strcmp (value, se->value) >= 0; + else + result = strcasecmp (value, se->value) >= 0; + break; + case SELECT_STRLT: + if (se->xcase) + result = strcmp (value, se->value) < 0; + else + result = strcasecmp (value, se->value) < 0; + break; + case SELECT_STRLE: + if (se->xcase) + result = strcmp (value, se->value) <= 0; + else + result = strcasecmp (value, se->value) <= 0; + break; + } + } + + if (se->not) + result = !result; + + if (result) + { + /* This expression evaluated to true. See whether there are + remaining expressions in this conjunction. */ + if (!se->next || se->next->disjun) + break; /* All expressions are true. Return True. */ + se = se->next; /* Test the next. */ + } + else + { + /* This expression evaluated to false and thus the + * conjunction evaluates to false. We skip over the + * remaining expressions of this conjunction and continue + * with the next disjunction if any. */ + do + se = se->next; + while (se && !se->disjun); + } + } + + return result; +} diff --git a/common/recsel.h b/common/recsel.h new file mode 100644 index 0000000..0e0a792 --- /dev/null +++ b/common/recsel.h @@ -0,0 +1,43 @@ +/* recsel.c - Record selection + * Copyright (C) 2016 Werner Koch + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ +#ifndef GNUPG_COMMON_RECSEL_H +#define GNUPG_COMMON_RECSEL_H + +struct recsel_expr_s; +typedef struct recsel_expr_s *recsel_expr_t; + +gpg_error_t recsel_parse_expr (recsel_expr_t *selector, const char *expr); +void recsel_release (recsel_expr_t a); +void recsel_dump (recsel_expr_t selector); +int recsel_select (recsel_expr_t selector, + const char *(*getval)(void *cookie, const char *propname), + void *cookie); + + +#endif /*GNUPG_COMMON_RECSEL_H*/ diff --git a/common/server-help.c b/common/server-help.c new file mode 100644 index 0000000..e5a69e0 --- /dev/null +++ b/common/server-help.c @@ -0,0 +1,185 @@ +/* server-help.h - Helper functions for writing Assuan servers. + * Copyright (C) 2003, 2009, 2010 g10 Code GmbH + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <string.h> + +#include "util.h" +#include "server-help.h" + + +static GPGRT_INLINE gpg_error_t +my_error (int e) +{ + return gpg_err_make (default_errsource, (e)); +} + +static GPGRT_INLINE gpg_error_t +my_error_from_syserror (void) +{ + return gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); +} + + +/* Skip over options in LINE. + + Blanks after the options are also removed. Options are indicated + by two leading dashes followed by a string consisting of non-space + characters. The special option "--" indicates an explicit end of + options; all what follows will not be considered an option. The + first no-option string also indicates the end of option parsing. */ +char * +skip_options (const char *line) +{ + while (spacep (line)) + line++; + while (*line == '-' && line[1] == '-') + { + while (*line && !spacep (line)) + line++; + while (spacep (line)) + line++; + } + return (char*) line; +} + + +/* Check whether the option NAME appears in LINE. */ +int +has_option (const char *line, const char *name) +{ + const char *s; + int n = strlen (name); + + s = strstr (line, name); + if (s && s >= skip_options (line)) + return 0; + return (s && (s == line || spacep (s-1)) && (!s[n] || spacep (s+n))); +} + + +/* Same as has_option but only considers options at the begin of the + line. This is useful for commands which allow arbitrary strings on + the line. */ +int +has_leading_option (const char *line, const char *name) +{ + const char *s; + int n; + + if (name[0] != '-' || name[1] != '-' || !name[2] || spacep (name+2)) + return 0; + n = strlen (name); + while ( *line == '-' && line[1] == '-' ) + { + s = line; + while (*line && !spacep (line)) + line++; + if (n == (line - s) && !strncmp (s, name, n)) + return 1; + while (spacep (line)) + line++; + } + return 0; +} + + +/* Same as has_option but does only test for the name of the option + and ignores an argument, i.e. with NAME being "--hash" it would + return a pointer for "--hash" as well as for "--hash=foo". If + there is no such option NULL is returned. The pointer returned + points right behind the option name, this may be an equal sign, Nul + or a space. */ +const char * +has_option_name (const char *line, const char *name) +{ + const char *s; + int n = strlen (name); + + s = strstr (line, name); + return (s && (s == line || spacep (s-1)) + && (!s[n] || spacep (s+n) || s[n] == '=')) ? (s+n) : NULL; +} + + +/* Parse an option with the format "--NAME=VALUE" which must occur in + * LINE before a double-dash. LINE is written to but not modified by + * this function. If the option is found and has a value the value is + * stored as a malloced string at R_VALUE. If the option was not + * found or an error occurred NULL is stored there. Note that + * currently the value must be a string without any space; we may + * eventually update this function to allow for a quoted value. */ +gpg_error_t +get_option_value (char *line, const char *name, char **r_value) +{ + char *p, *pend; + int c; + + *r_value = NULL; + + p = (char*)has_option_name (line, name); + if (!p || p >= skip_options (line)) + return 0; + + if (*p != '=' || !p[1] || spacep (p+1)) + return my_error (GPG_ERR_INV_ARG); + p++; + for (pend = p; *pend && !spacep (pend); pend++) + ; + c = *pend; + *pend = 0; + *r_value = xtrystrdup (p); + *pend = c; + if (!p) + return my_error_from_syserror (); + return 0; +} + + +/* Return a pointer to the argument of the option with NAME. If such + an option is not given, NULL is returned. */ +char * +option_value (const char *line, const char *name) +{ + char *s; + int n = strlen (name); + + s = strstr (line, name); + if (s && s >= skip_options (line)) + return NULL; + if (s && (s == line || spacep (s-1)) + && s[n] && (spacep (s+n) || s[n] == '=')) + { + s += n + 1; + s += strspn (s, " "); + if (*s && !spacep(s)) + return s; + } + return NULL; +} diff --git a/common/server-help.h b/common/server-help.h new file mode 100644 index 0000000..9d2f4cf --- /dev/null +++ b/common/server-help.h @@ -0,0 +1,70 @@ +/* server-help.h - Helper functions for writing Assuan servers. + * Copyright (C) 2003, 2009, 2010 g10 Code GmbH + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef GNUPG_COMMON_SERVER_HELP_H +#define GNUPG_COMMON_SERVER_HELP_H + +/* Skip over options in LINE. + + Blanks after the options are also removed. Options are indicated + by two leading dashes followed by a string consisting of non-space + characters. The special option "--" indicates an explicit end of + options; all what follows will not be considered an option. The + first no-option string also indicates the end of option parsing. */ +char *skip_options (const char *line); + +/* Check whether the option NAME appears in LINE. */ +int has_option (const char *line, const char *name); + +/* Same as has_option but only considers options at the begin of the + line. This is useful for commands which allow arbitrary strings on + the line. */ +int has_leading_option (const char *line, const char *name); + +/* Same as has_option but does only test for the name of the option + and ignores an argument, i.e. with NAME being "--hash" it would + return a pointer for "--hash" as well as for "--hash=foo". If + there is no such option NULL is returned. The pointer returned + points right behind the option name, this may be an equal sign, Nul + or a space. */ +const char *has_option_name (const char *line, const char *name); + +/* Same as has_option_name but ignores all options after a "--" and + * does not return a const char ptr. */ +char *has_leading_option_name (char *line, const char *name); + +/* Parse an option with the format "--NAME=VALUE" and return the value + * as a malloced string. */ +gpg_error_t get_option_value (char *line, const char *name, char **r_value); + +/* Return a pointer to the argument of the option with NAME. If such + an option is not given, NULL is returned. */ +char *option_value (const char *line, const char *name); + +#endif /* GNUPG_COMMON_SERVER_HELP_H */ diff --git a/common/session-env.c b/common/session-env.c new file mode 100644 index 0000000..c46529a --- /dev/null +++ b/common/session-env.c @@ -0,0 +1,406 @@ +/* session-env.c - Session environment helper functions. + * Copyright (C) 2009 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdlib.h> +#include <errno.h> +#include <ctype.h> +#include <assert.h> +#include <unistd.h> + +#include "util.h" +#include "session-env.h" + + +struct variable_s +{ + char *value; /* Pointer into NAME to the Nul terminated value. */ + int is_default; /* The value is a default one. */ + char name[1]; /* Nul terminated Name and space for the value. */ +}; + + + +/* The session environment object. */ +struct session_environment_s +{ + size_t arraysize; /* Allocated size or ARRAY. */ + size_t arrayused; /* Used size of ARRAY. */ + struct variable_s **array; /* Array of variables. NULL slots are unused. */ +}; + + +/* A list of environment variables we pass from the actual user + (e.g. gpgme) down to the pinentry. We do not handle the locale + settings because they do not only depend on envvars. */ +static struct +{ + const char *name; + const char *assname; /* Name used by Assuan or NULL. */ +} stdenvnames[] = { + { "GPG_TTY", "ttyname" }, /* GnuPG specific envvar. */ + { "TERM", "ttytype" }, /* Used to set ttytype. */ + { "DISPLAY", "display" }, /* The X-Display. */ + { "XAUTHORITY","xauthority"}, /* Xlib Authentication. */ + { "XMODIFIERS" }, /* Used by Xlib to select X input + modules (eg "@im=SCIM"). */ + { "WAYLAND_DISPLAY" }, /* For the Wayland display engine. */ + { "XDG_SESSION_TYPE" }, /* Used by Qt and other non-GTK toolkits + to check for x11 or wayland. */ + { "QT_QPA_PLATFORM" }, /* Used by Qt to explicitly request + x11 or wayland; in particular, needed + to make Qt use Wayland on Gnome. */ + { "GTK_IM_MODULE" }, /* Used by gtk to select gtk input + modules (eg "scim-bridge"). */ + { "DBUS_SESSION_BUS_ADDRESS" },/* Used by GNOME3 to talk to gcr over + dbus */ + { "QT_IM_MODULE" }, /* Used by Qt to select qt input + modules (eg "xim"). */ + { "INSIDE_EMACS" }, /* Set by Emacs before running a + process. */ + { "PINENTRY_USER_DATA", "pinentry-user-data"} + /* Used for communication with + non-standard Pinentries. */ +}; + + +/* Track last allocated arraysize of all objects ever created. If + nothing has ever been allocated we use INITIAL_ARRAYSIZE and we + will never use more than MAXDEFAULT_ARRAYSIZE for initial + allocation. Note that this is not reentrant if used with a + preemptive thread model. */ +static size_t lastallocatedarraysize; +#define INITIAL_ARRAYSIZE 8 /* Let's use the number of stdenvnames. */ +#define CHUNK_ARRAYSIZE 10 +#define MAXDEFAULT_ARRAYSIZE (INITIAL_ARRAYSIZE + CHUNK_ARRAYSIZE * 5) + + +/* Return the names of standard environment variables one after the + other. The caller needs to set the value at the address of + ITERATOR initially to 0 and then call this function until it returns + NULL. */ +const char * +session_env_list_stdenvnames (int *iterator, const char **r_assname) +{ + int idx = *iterator; + + if (idx < 0 || idx >= DIM (stdenvnames)) + return NULL; + *iterator = idx + 1; + if (r_assname) + *r_assname = stdenvnames[idx].assname; + return stdenvnames[idx].name; +} + + +/* Create a new session environment object. Return NULL and sets + ERRNO on failure. */ +session_env_t +session_env_new (void) +{ + session_env_t se; + + se = xtrycalloc (1, sizeof *se); + if (se) + { + se->arraysize = (lastallocatedarraysize? + lastallocatedarraysize : INITIAL_ARRAYSIZE); + se->array = xtrycalloc (se->arraysize, sizeof *se->array); + if (!se->array) + { + xfree (se); + se = NULL; + } + } + + return se; +} + + +/* Release a session environment object. */ +void +session_env_release (session_env_t se) +{ + int idx; + + if (!se) + return; + + if (se->arraysize > INITIAL_ARRAYSIZE + && se->arraysize <= MAXDEFAULT_ARRAYSIZE + && se->arraysize > lastallocatedarraysize) + lastallocatedarraysize = se->arraysize; + + for (idx=0; idx < se->arrayused; idx++) + if (se->array[idx]) + xfree (se->array[idx]); + xfree (se->array); + xfree (se); +} + + +static gpg_error_t +delete_var (session_env_t se, const char *name) +{ + int idx; + + for (idx=0; idx < se->arrayused; idx++) + if (se->array[idx] && !strcmp (se->array[idx]->name, name)) + { + xfree (se->array[idx]); + se->array[idx] = NULL; + } + return 0; +} + + +static gpg_error_t +update_var (session_env_t se, const char *string, size_t namelen, + const char *explicit_value, int set_default) +{ + int idx; + int freeidx = -1; + const char *value; + size_t valuelen; + struct variable_s *var; + + if (explicit_value) + value = explicit_value; + else + value = string + namelen + 1; + valuelen = strlen (value); + + for (idx=0; idx < se->arrayused; idx++) + { + if (!se->array[idx]) + freeidx = idx; + else if (!strncmp (se->array[idx]->name, string, namelen) + && strlen (se->array[idx]->name) == namelen) + { + if (strlen (se->array[idx]->value) == valuelen) + { + /* The new value has the same length. We can update it + in-place. */ + memcpy (se->array[idx]->value, value, valuelen); + se->array[idx]->is_default = !!set_default; + return 0; + } + /* Prepare for update. */ + freeidx = idx; + } + } + + if (freeidx == -1) + { + if (se->arrayused == se->arraysize) + { + /* Reallocate the array. */ + size_t newsize; + struct variable_s **newarray; + + newsize = se->arraysize + CHUNK_ARRAYSIZE; + newarray = xtrycalloc (newsize, sizeof *newarray); + if (!newarray) + return gpg_error_from_syserror (); + for (idx=0; idx < se->arrayused; idx++) + newarray[idx] = se->array[idx]; + se->arraysize = newsize; + xfree (se->array); + se->array = newarray; + } + freeidx = se->arrayused++; + } + + /* Allocate new memory and return an error if that didn't worked. + Allocating it first allows us to keep the old value; it doesn't + matter that arrayused has already been incremented in case of a + new entry - it will then pint to a NULL slot. */ + var = xtrymalloc (sizeof *var + namelen + 1 + valuelen); + if (!var) + return gpg_error_from_syserror (); + var->is_default = !!set_default; + memcpy (var->name, string, namelen); + var->name[namelen] = '\0'; + var->value = var->name + namelen + 1; + strcpy (var->value, value); + + xfree (se->array[freeidx]); + se->array[freeidx] = var; + return 0; +} + + +/* Set or update an environment variable of the session environment. + String is similar to the putval(3) function but it is reentrant and + takes a copy. In particular it exhibits this behaviour: + + <NAME> Delete envvar NAME + <KEY>= Set envvar NAME to the empty string + <KEY>=<VALUE> Set envvar NAME to VALUE + + On success 0 is returned; on error an gpg-error code. */ +gpg_error_t +session_env_putenv (session_env_t se, const char *string) +{ + const char *s; + + if (!string || !*string) + return gpg_error (GPG_ERR_INV_VALUE); + s = strchr (string, '='); + if (s == string) + return gpg_error (GPG_ERR_INV_VALUE); + if (!s) + return delete_var (se, string); + else + return update_var (se, string, s - string, NULL, 0); +} + + +/* Same as session_env_putenv but with name and value given as distict + values. */ +gpg_error_t +session_env_setenv (session_env_t se, const char *name, const char *value) +{ + if (!name || !*name) + return gpg_error (GPG_ERR_INV_VALUE); + if (!value) + return delete_var (se, name); + else + return update_var (se, name, strlen (name), value, 0); +} + + + + +/* Return the value of the environment variable NAME from the SE + object. If the variable does not exist, NULL is returned. The + returned value is valid as long as SE is valid and as long it has + not been removed or updated by a call to session_env_putenv. The + caller MUST not change the returned value. */ +char * +session_env_getenv (session_env_t se, const char *name) +{ + int idx; + + if (!se || !name || !*name) + return NULL; + + for (idx=0; idx < se->arrayused; idx++) + if (se->array[idx] && !strcmp (se->array[idx]->name, name)) + return se->array[idx]->is_default? NULL : se->array[idx]->value; + return NULL; +} + + +/* Return the value of the environment variable NAME from the SE + object. The returned value is valid as long as SE is valid and as + long it has not been removed or updated by a call to + session_env_putenv. If the variable does not exist, the function + tries to return the value trough a call to getenv; if that returns + a value, this value is recorded and used. If no value could be + found, returns NULL. The caller must not change the returned + value. */ +char * +session_env_getenv_or_default (session_env_t se, const char *name, + int *r_default) +{ + int idx; + char *defvalue; + + if (r_default) + *r_default = 0; + if (!se || !name || !*name) + return NULL; + + for (idx=0; idx < se->arrayused; idx++) + if (se->array[idx] && !strcmp (se->array[idx]->name, name)) + { + if (r_default && se->array[idx]->is_default) + *r_default = 1; + return se->array[idx]->value; + } + + /* Get the default value with an additional fallback for GPG_TTY. */ + defvalue = getenv (name); + if ((!defvalue || !*defvalue) && !strcmp (name, "GPG_TTY") + && gnupg_ttyname (0)) + { + defvalue = gnupg_ttyname (0); + } + if (defvalue) + { + /* Record the default value for later use so that we are safe + from later modifications of the environment. We need to take + a copy to better cope with the rules of putenv(3). We ignore + the error of the update function because we can't return an + explicit error anyway and the following scan would then fail + anyway. */ + update_var (se, name, strlen (name), defvalue, 1); + + for (idx=0; idx < se->arrayused; idx++) + if (se->array[idx] && !strcmp (se->array[idx]->name, name)) + { + if (r_default && se->array[idx]->is_default) + *r_default = 1; + return se->array[idx]->value; + } + } + + return NULL; +} + + +/* List the entire environment stored in SE. The caller initially + needs to set the value of ITERATOR to 0 and then call this function + until it returns NULL. The value is returned at R_VALUE. If + R_DEFAULT is not NULL, the default flag is stored on return. The + default flag indicates that the value has been taken from the + process' environment. The caller must not change the returned + name or value. */ +char * +session_env_listenv (session_env_t se, int *iterator, + const char **r_value, int *r_default) +{ + int idx = *iterator; + + if (!se || idx < 0) + return NULL; + + for (; idx < se->arrayused; idx++) + if (se->array[idx]) + { + *iterator = idx+1; + if (r_default) + *r_default = se->array[idx]->is_default; + if (r_value) + *r_value = se->array[idx]->value; + return se->array[idx]->name; + } + return NULL; +} diff --git a/common/session-env.h b/common/session-env.h new file mode 100644 index 0000000..8709e22 --- /dev/null +++ b/common/session-env.h @@ -0,0 +1,53 @@ +/* session-env.h - Definitions for session environment functions + * Copyright (C) 2009 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef GNUPG_COMMON_SESSION_ENV_H +#define GNUPG_COMMON_SESSION_ENV_H + +struct session_environment_s; +typedef struct session_environment_s *session_env_t; + +const char *session_env_list_stdenvnames (int *iterator, + const char **r_assname); + +session_env_t session_env_new (void); +void session_env_release (session_env_t se); + +gpg_error_t session_env_putenv (session_env_t se, const char *string); +gpg_error_t session_env_setenv (session_env_t se, + const char *name, const char *value); + +char *session_env_getenv (session_env_t se, const char *name); +char *session_env_getenv_or_default (session_env_t se, const char *name, + int *r_default); +char *session_env_listenv (session_env_t se, int *iterator, + const char **r_value, int *r_default); + + +#endif /*GNUPG_COMMON_SESSION_ENV_H*/ diff --git a/common/sexp-parse.h b/common/sexp-parse.h new file mode 100644 index 0000000..4f77f14 --- /dev/null +++ b/common/sexp-parse.h @@ -0,0 +1,137 @@ +/* sexp-parse.h - S-expression helper functions + * Copyright (C) 2002, 2003, 2007 Free Software Foundation, Inc. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef SEXP_PARSE_H +#define SEXP_PARSE_H + +#include <gpg-error.h> + + +/* Return the length of the next S-Exp part and update the pointer to + the first data byte. 0 is returned on error */ +static inline size_t +snext (unsigned char const **buf) +{ + const unsigned char *s; + int n; + + s = *buf; + for (n=0; *s && *s != ':' && (*s >= '0' && *s <= '9'); s++) + n = n*10 + (*s - '0'); + if (!n || *s != ':') + return 0; /* we don't allow empty lengths */ + *buf = s+1; + return n; +} + +/* Skip over the S-Expression BUF points to and update BUF to point to + the character right behind. DEPTH gives the initial number of open + lists and may be passed as a positive number to skip over the + remainder of an S-Expression if the current position is somewhere + in an S-Expression. The function may return an error code if it + encounters an impossible condition. */ +static inline gpg_error_t +sskip (unsigned char const **buf, int *depth) +{ + const unsigned char *s = *buf; + size_t n; + int d = *depth; + + while (d > 0) + { + if (*s == '(') + { + d++; + s++; + } + else if (*s == ')') + { + d--; + s++; + } + else + { + if (!d) + return gpg_error (GPG_ERR_INV_SEXP); + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + s += n; + } + } + *buf = s; + *depth = d; + return 0; +} + + +/* Check whether the string at the address BUF points to matches + the token. Return true on match and update BUF to point behind the + token. Return false and do not update the buffer if it does not + match. */ +static inline int +smatch (unsigned char const **buf, size_t buflen, const char *token) +{ + size_t toklen = strlen (token); + + if (buflen != toklen || memcmp (*buf, token, toklen)) + return 0; + *buf += toklen; + return 1; +} + +/* Format VALUE for use as the length indicatior of an S-expression. + The caller needs to provide a buffer HELP_BUFFER wth a length of + HELP_BUFLEN. The return value is a pointer into HELP_BUFFER with + the formatted length string. The colon and a trailing nul are + appended. HELP_BUFLEN must be at least 3 - a more useful value is + 15. If LENGTH is not NULL, the LENGTH of the resulting string + (excluding the terminating nul) is stored at that address. */ +static inline char * +smklen (char *help_buffer, size_t help_buflen, size_t value, size_t *length) +{ + char *p = help_buffer + help_buflen; + + if (help_buflen >= 3) + { + *--p = 0; + *--p = ':'; + do + { + *--p = '0' + (value % 10); + value /= 10; + } + while (value && p > help_buffer); + } + + if (length) + *length = (help_buffer + help_buflen) - p; + return p; +} + + +#endif /*SEXP_PARSE_H*/ diff --git a/common/sexputil.c b/common/sexputil.c new file mode 100644 index 0000000..68388e1 --- /dev/null +++ b/common/sexputil.c @@ -0,0 +1,1188 @@ +/* sexputil.c - Utility functions for S-expressions. + * Copyright (C) 2005, 2007, 2009 Free Software Foundation, Inc. + * Copyright (C) 2013 Werner Koch + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +/* This file implements a few utility functions useful when working + with canonical encrypted S-expressions (i.e. not the S-exprssion + objects from libgcrypt). */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#ifdef HAVE_LOCALE_H +#include <locale.h> +#endif + +#include "util.h" +#include "tlv.h" +#include "sexp-parse.h" +#include "openpgpdefs.h" /* for pubkey_algo_t */ + + +/* Return a malloced string with the S-expression CANON in advanced + format. Returns NULL on error. */ +static char * +sexp_to_string (gcry_sexp_t sexp) +{ + size_t n; + char *result; + + if (!sexp) + return NULL; + n = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, NULL, 0); + if (!n) + return NULL; + result = xtrymalloc (n); + if (!result) + return NULL; + n = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, result, n); + if (!n) + BUG (); + + return result; +} + + +/* Return a malloced string with the S-expression CANON in advanced + format. Returns NULL on error. */ +char * +canon_sexp_to_string (const unsigned char *canon, size_t canonlen) +{ + size_t n; + gcry_sexp_t sexp; + char *result; + + n = gcry_sexp_canon_len (canon, canonlen, NULL, NULL); + if (!n) + return NULL; + if (gcry_sexp_sscan (&sexp, NULL, canon, n)) + return NULL; + result = sexp_to_string (sexp); + gcry_sexp_release (sexp); + return result; +} + + +/* Print the canonical encoded S-expression in SEXP in advanced + format. SEXPLEN may be passed as 0 is SEXP is known to be valid. + With TEXT of NULL print just the raw S-expression, with TEXT just + an empty string, print a trailing linefeed, otherwise print an + entire debug line. */ +void +log_printcanon (const char *text, const unsigned char *sexp, size_t sexplen) +{ + if (text && *text) + log_debug ("%s ", text); + if (sexp) + { + char *buf = canon_sexp_to_string (sexp, sexplen); + log_printf ("%s", buf? buf : "[invalid S-expression]"); + xfree (buf); + } + if (text) + log_printf ("\n"); +} + + +/* Print the gcryp S-expression in SEXP in advanced format. With TEXT + of NULL print just the raw S-expression, with TEXT just an empty + string, print a trailing linefeed, otherwise print an entire debug + line. */ +void +log_printsexp (const char *text, gcry_sexp_t sexp) +{ + if (text && *text) + log_debug ("%s ", text); + if (sexp) + { + char *buf = sexp_to_string (sexp); + log_printf ("%s", buf? buf : "[invalid S-expression]"); + xfree (buf); + } + if (text) + log_printf ("\n"); +} + + +/* Helper function to create a canonical encoded S-expression from a + Libgcrypt S-expression object. The function returns 0 on success + and the malloced canonical S-expression is stored at R_BUFFER and + the allocated length at R_BUFLEN. On error an error code is + returned and (NULL, 0) stored at R_BUFFER and R_BUFLEN. If the + allocated buffer length is not required, NULL by be used for + R_BUFLEN. */ +gpg_error_t +make_canon_sexp (gcry_sexp_t sexp, unsigned char **r_buffer, size_t *r_buflen) +{ + size_t len; + unsigned char *buf; + + *r_buffer = NULL; + if (r_buflen) + *r_buflen = 0;; + + len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_CANON, NULL, 0); + if (!len) + return gpg_error (GPG_ERR_BUG); + buf = xtrymalloc (len); + if (!buf) + return gpg_error_from_syserror (); + len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_CANON, buf, len); + if (!len) + return gpg_error (GPG_ERR_BUG); + + *r_buffer = buf; + if (r_buflen) + *r_buflen = len; + + return 0; +} + + +/* Same as make_canon_sexp but pad the buffer to multiple of 64 + bits. If SECURE is set, secure memory will be allocated. */ +gpg_error_t +make_canon_sexp_pad (gcry_sexp_t sexp, int secure, + unsigned char **r_buffer, size_t *r_buflen) +{ + size_t len; + unsigned char *buf; + + *r_buffer = NULL; + if (r_buflen) + *r_buflen = 0;; + + len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_CANON, NULL, 0); + if (!len) + return gpg_error (GPG_ERR_BUG); + len += (8 - len % 8) % 8; + buf = secure? xtrycalloc_secure (1, len) : xtrycalloc (1, len); + if (!buf) + return gpg_error_from_syserror (); + if (!gcry_sexp_sprint (sexp, GCRYSEXP_FMT_CANON, buf, len)) + return gpg_error (GPG_ERR_BUG); + + *r_buffer = buf; + if (r_buflen) + *r_buflen = len; + + return 0; +} + +/* Return the so called "keygrip" which is the SHA-1 hash of the + public key parameters expressed in a way depended on the algorithm. + + KEY is expected to be an canonical encoded S-expression with a + public or private key. KEYLEN is the length of that buffer. + + GRIP must be at least 20 bytes long. On success 0 is returned, on + error an error code. */ +gpg_error_t +keygrip_from_canon_sexp (const unsigned char *key, size_t keylen, + unsigned char *grip) +{ + gpg_error_t err; + gcry_sexp_t sexp; + + if (!grip) + return gpg_error (GPG_ERR_INV_VALUE); + err = gcry_sexp_sscan (&sexp, NULL, (const char *)key, keylen); + if (err) + return err; + if (!gcry_pk_get_keygrip (sexp, grip)) + err = gpg_error (GPG_ERR_INTERNAL); + gcry_sexp_release (sexp); + return err; +} + + +/* Compare two simple S-expressions like "(3:foo)". Returns 0 if they + are identical or !0 if they are not. Note that this function can't + be used for sorting. */ +int +cmp_simple_canon_sexp (const unsigned char *a_orig, + const unsigned char *b_orig) +{ + const char *a = (const char *)a_orig; + const char *b = (const char *)b_orig; + unsigned long n1, n2; + char *endp; + + if (!a && !b) + return 0; /* Both are NULL, they are identical. */ + if (!a || !b) + return 1; /* One is NULL, they are not identical. */ + if (*a != '(' || *b != '(') + log_bug ("invalid S-exp in cmp_simple_canon_sexp\n"); + + a++; + n1 = strtoul (a, &endp, 10); + a = endp; + b++; + n2 = strtoul (b, &endp, 10); + b = endp; + + if (*a != ':' || *b != ':' ) + log_bug ("invalid S-exp in cmp_simple_canon_sexp\n"); + if (n1 != n2) + return 1; /* Not the same. */ + + for (a++, b++; n1; n1--, a++, b++) + if (*a != *b) + return 1; /* Not the same. */ + return 0; +} + + + +/* Helper for cmp_canon_sexp. */ +static int +cmp_canon_sexp_def_tcmp (void *ctx, int depth, + const unsigned char *aval, size_t alen, + const unsigned char *bval, size_t blen) +{ + (void)ctx; + (void)depth; + + if (alen > blen) + return 1; + else if (alen < blen) + return -1; + else + return memcmp (aval, bval, alen); +} + + +/* Compare the two canonical encoded s-expressions A with maximum + * length ALEN and B with maximum length BLEN. + * + * Returns 0 if they match. + * + * If TCMP is NULL, this is not different really different from a + * memcmp but does not consider any garbage after the last closing + * parentheses. + * + * If TCMP is not NULL, it is expected to be a function to compare the + * values of each token. TCMP is called for each token while parsing + * the s-expressions until TCMP return a non-zero value. Here the CTX + * receives the provided value TCMPCTX, DEPTH is the number of + * currently open parentheses and (AVAL,ALEN) and (BVAL,BLEN) the + * values of the current token. TCMP needs to return zero to indicate + * that the tokens match. */ +int +cmp_canon_sexp (const unsigned char *a, size_t alen, + const unsigned char *b, size_t blen, + int (*tcmp)(void *ctx, int depth, + const unsigned char *aval, size_t avallen, + const unsigned char *bval, size_t bvallen), + void *tcmpctx) +{ + const unsigned char *a_buf, *a_tok; + const unsigned char *b_buf, *b_tok; + size_t a_buflen, a_toklen; + size_t b_buflen, b_toklen; + int a_depth, b_depth, ret; + + if ((!a && !b) || (!alen && !blen)) + return 0; /* Both are NULL, they are identical. */ + if (!a || !b) + return !!a - !!b; /* One is NULL, they are not identical. */ + if (*a != '(' || *b != '(') + log_bug ("invalid S-exp in %s\n", __func__); + + if (!tcmp) + tcmp = cmp_canon_sexp_def_tcmp; + + a_depth = 0; + a_buf = a; + a_buflen = alen; + b_depth = 0; + b_buf = b; + b_buflen = blen; + + for (;;) + { + if (parse_sexp (&a_buf, &a_buflen, &a_depth, &a_tok, &a_toklen)) + return -1; /* A is invalid. */ + if (parse_sexp (&b_buf, &b_buflen, &b_depth, &b_tok, &b_toklen)) + return -1; /* B is invalid. */ + if (!a_depth && !b_depth) + return 0; /* End of both expressions - they match. */ + if (a_depth != b_depth) + return a_depth - b_depth; /* Not the same structure */ + if (!a_tok && !b_tok) + ; /* parens */ + else if (a_tok && b_tok) + { + ret = tcmp (tcmpctx, a_depth, a_tok, a_toklen, b_tok, b_toklen); + if (ret) + return ret; /* Mismatch */ + } + else /* One has a paren other has not. */ + return !!a_tok - !!b_tok; + } +} + + +/* Create a simple S-expression from the hex string at LINE. Returns + a newly allocated buffer with that canonical encoded S-expression + or NULL in case of an error. On return the number of characters + scanned in LINE will be stored at NSCANNED. This functions stops + converting at the first character not representing a hexdigit. Odd + numbers of hex digits are allowed; a leading zero is then + assumed. If no characters have been found, NULL is returned.*/ +unsigned char * +make_simple_sexp_from_hexstr (const char *line, size_t *nscanned) +{ + size_t n, len; + const char *s; + unsigned char *buf; + unsigned char *p; + char numbuf[50], *numbufp; + size_t numbuflen; + + for (n=0, s=line; hexdigitp (s); s++, n++) + ; + if (nscanned) + *nscanned = n; + if (!n) + return NULL; + len = ((n+1) & ~0x01)/2; + numbufp = smklen (numbuf, sizeof numbuf, len, &numbuflen); + buf = xtrymalloc (1 + numbuflen + len + 1 + 1); + if (!buf) + return NULL; + buf[0] = '('; + p = (unsigned char *)stpcpy ((char *)buf+1, numbufp); + s = line; + if ((n&1)) + { + *p++ = xtoi_1 (s); + s++; + n--; + } + for (; n > 1; n -=2, s += 2) + *p++ = xtoi_2 (s); + *p++ = ')'; + *p = 0; /* (Not really neaded.) */ + + return buf; +} + + +/* Return the hash algorithm from a KSBA sig-val. SIGVAL is a + canonical encoded S-expression. Return 0 if the hash algorithm is + not encoded in SIG-VAL or it is not supported by libgcrypt. */ +int +hash_algo_from_sigval (const unsigned char *sigval) +{ + const unsigned char *s = sigval; + size_t n; + int depth; + char buffer[50]; + + if (!s || *s != '(') + return 0; /* Invalid S-expression. */ + s++; + n = snext (&s); + if (!n) + return 0; /* Invalid S-expression. */ + if (!smatch (&s, n, "sig-val")) + return 0; /* Not a sig-val. */ + if (*s != '(') + return 0; /* Invalid S-expression. */ + s++; + /* Skip over the algo+parameter list. */ + depth = 1; + if (sskip (&s, &depth) || depth) + return 0; /* Invalid S-expression. */ + if (*s != '(') + return 0; /* No further list. */ + /* Check whether this is (hash ALGO). */ + s++; + n = snext (&s); + if (!n) + return 0; /* Invalid S-expression. */ + if (!smatch (&s, n, "hash")) + return 0; /* Not a "hash" keyword. */ + n = snext (&s); + if (!n || n+1 >= sizeof (buffer)) + return 0; /* Algorithm string is missing or too long. */ + memcpy (buffer, s, n); + buffer[n] = 0; + + return gcry_md_map_name (buffer); +} + + +/* Create a public key S-expression for an RSA public key from the + modulus M with length MLEN and the public exponent E with length + ELEN. Returns a newly allocated buffer of NULL in case of a memory + allocation problem. If R_LEN is not NULL, the length of the + canonical S-expression is stored there. */ +unsigned char * +make_canon_sexp_from_rsa_pk (const void *m_arg, size_t mlen, + const void *e_arg, size_t elen, + size_t *r_len) +{ + const unsigned char *m = m_arg; + const unsigned char *e = e_arg; + int m_extra = 0; + int e_extra = 0; + char mlen_str[35]; + char elen_str[35]; + unsigned char *keybuf, *p; + const char part1[] = "(10:public-key(3:rsa(1:n"; + const char part2[] = ")(1:e"; + const char part3[] = ")))"; + + /* Remove leading zeroes. */ + for (; mlen && !*m; mlen--, m++) + ; + for (; elen && !*e; elen--, e++) + ; + + /* Insert a leading zero if the number would be zero or interpreted + as negative. */ + if (!mlen || (m[0] & 0x80)) + m_extra = 1; + if (!elen || (e[0] & 0x80)) + e_extra = 1; + + /* Build the S-expression. */ + snprintf (mlen_str, sizeof mlen_str, "%u:", (unsigned int)mlen+m_extra); + snprintf (elen_str, sizeof elen_str, "%u:", (unsigned int)elen+e_extra); + + keybuf = xtrymalloc (strlen (part1) + strlen (mlen_str) + mlen + m_extra + + strlen (part2) + strlen (elen_str) + elen + e_extra + + strlen (part3) + 1); + if (!keybuf) + return NULL; + + p = stpcpy (keybuf, part1); + p = stpcpy (p, mlen_str); + if (m_extra) + *p++ = 0; + memcpy (p, m, mlen); + p += mlen; + p = stpcpy (p, part2); + p = stpcpy (p, elen_str); + if (e_extra) + *p++ = 0; + memcpy (p, e, elen); + p += elen; + p = stpcpy (p, part3); + + if (r_len) + *r_len = p - keybuf; + + return keybuf; +} + + +/* Return the parameters of a public RSA key expressed as an + canonical encoded S-expression. */ +gpg_error_t +get_rsa_pk_from_canon_sexp (const unsigned char *keydata, size_t keydatalen, + unsigned char const **r_n, size_t *r_nlen, + unsigned char const **r_e, size_t *r_elen) +{ + gpg_error_t err; + const unsigned char *buf, *tok; + size_t buflen, toklen; + int depth, last_depth1, last_depth2; + const unsigned char *rsa_n = NULL; + const unsigned char *rsa_e = NULL; + size_t rsa_n_len, rsa_e_len; + + *r_n = NULL; + *r_nlen = 0; + *r_e = NULL; + *r_elen = 0; + + buf = keydata; + buflen = keydatalen; + depth = 0; + if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) + return err; + if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) + return err; + if (!tok || toklen != 10 || memcmp ("public-key", tok, toklen)) + return gpg_error (GPG_ERR_BAD_PUBKEY); + if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) + return err; + if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) + return err; + if (!tok || toklen != 3 || memcmp ("rsa", tok, toklen)) + return gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO); + + last_depth1 = depth; + while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)) + && depth && depth >= last_depth1) + { + if (tok) + return gpg_error (GPG_ERR_UNKNOWN_SEXP); + if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) + return err; + if (tok && toklen == 1) + { + const unsigned char **mpi; + size_t *mpi_len; + + switch (*tok) + { + case 'n': mpi = &rsa_n; mpi_len = &rsa_n_len; break; + case 'e': mpi = &rsa_e; mpi_len = &rsa_e_len; break; + default: mpi = NULL; mpi_len = NULL; break; + } + if (mpi && *mpi) + return gpg_error (GPG_ERR_DUP_VALUE); + + if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) + return err; + if (tok && mpi) + { + /* Strip off leading zero bytes and save. */ + for (;toklen && !*tok; toklen--, tok++) + ; + *mpi = tok; + *mpi_len = toklen; + } + } + + /* Skip to the end of the list. */ + last_depth2 = depth; + while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)) + && depth && depth >= last_depth2) + ; + if (err) + return err; + } + + if (err) + return err; + + if (!rsa_n || !rsa_n_len || !rsa_e || !rsa_e_len) + return gpg_error (GPG_ERR_BAD_PUBKEY); + + *r_n = rsa_n; + *r_nlen = rsa_n_len; + *r_e = rsa_e; + *r_elen = rsa_e_len; + return 0; +} + + +/* Return the public key parameter Q of a public RSA or ECC key + * expressed as an canonical encoded S-expression. */ +gpg_error_t +get_ecc_q_from_canon_sexp (const unsigned char *keydata, size_t keydatalen, + unsigned char const **r_q, size_t *r_qlen) +{ + gpg_error_t err; + const unsigned char *buf, *tok; + size_t buflen, toklen; + int depth, last_depth1, last_depth2; + const unsigned char *ecc_q = NULL; + size_t ecc_q_len; + + *r_q = NULL; + *r_qlen = 0; + + buf = keydata; + buflen = keydatalen; + depth = 0; + if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) + return err; + if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) + return err; + if (!tok || toklen != 10 || memcmp ("public-key", tok, toklen)) + return gpg_error (GPG_ERR_BAD_PUBKEY); + if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) + return err; + if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) + return err; + if (tok && toklen == 3 && !memcmp ("ecc", tok, toklen)) + ; + else if (tok && toklen == 5 && (!memcmp ("ecdsa", tok, toklen) + || !memcmp ("eddsa", tok, toklen))) + ; + else + return gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO); + + last_depth1 = depth; + while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)) + && depth && depth >= last_depth1) + { + if (tok) + return gpg_error (GPG_ERR_UNKNOWN_SEXP); + if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) + return err; + if (tok && toklen == 1) + { + const unsigned char **mpi; + size_t *mpi_len; + + switch (*tok) + { + case 'q': mpi = &ecc_q; mpi_len = &ecc_q_len; break; + default: mpi = NULL; mpi_len = NULL; break; + } + if (mpi && *mpi) + return gpg_error (GPG_ERR_DUP_VALUE); + + if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) + return err; + if (tok && mpi) + { + *mpi = tok; + *mpi_len = toklen; + } + } + + /* Skip to the end of the list. */ + last_depth2 = depth; + while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)) + && depth && depth >= last_depth2) + ; + if (err) + return err; + } + + if (err) + return err; + + if (!ecc_q || !ecc_q_len) + return gpg_error (GPG_ERR_BAD_PUBKEY); + + *r_q = ecc_q; + *r_qlen = ecc_q_len; + return 0; +} + + +/* Return an uncompressed point (X,Y) in P at R_BUF as a malloced + * buffer with its byte length stored at R_BUFLEN. May not be used + * for sensitive data. */ +static gpg_error_t +ec2os (gcry_mpi_t x, gcry_mpi_t y, gcry_mpi_t p, + unsigned char **r_buf, unsigned int *r_buflen) +{ + gpg_error_t err; + int pbytes = (mpi_get_nbits (p)+7)/8; + size_t n; + unsigned char *buf, *ptr; + + *r_buf = NULL; + *r_buflen = 0; + + buf = xtrymalloc (1 + 2*pbytes); + if (!buf) + return gpg_error_from_syserror (); + *buf = 04; /* Uncompressed point. */ + ptr = buf+1; + err = gcry_mpi_print (GCRYMPI_FMT_USG, ptr, pbytes, &n, x); + if (err) + { + xfree (buf); + return err; + } + if (n < pbytes) + { + memmove (ptr+(pbytes-n), ptr, n); + memset (ptr, 0, (pbytes-n)); + } + ptr += pbytes; + err = gcry_mpi_print (GCRYMPI_FMT_USG, ptr, pbytes, &n, y); + if (err) + { + xfree (buf); + return err; + } + if (n < pbytes) + { + memmove (ptr+(pbytes-n), ptr, n); + memset (ptr, 0, (pbytes-n)); + } + + *r_buf = buf; + *r_buflen = 1 + 2*pbytes; + return 0; +} + + +/* Convert the ECC parameter Q in the canonical s-expression + * (KEYDATA,KEYDATALEN) to uncompressed form. On success and if a + * conversion was done, the new canonical encoded s-expression is + * returned at (R_NEWKEYDAT,R_NEWKEYDATALEN); if a conversion was not + * required (NULL,0) is stored there. On error an error code is + * returned. The function may take any kind of key but will only do + * the conversion for ECC curves where compression is supported. */ +gpg_error_t +uncompress_ecc_q_in_canon_sexp (const unsigned char *keydata, + size_t keydatalen, + unsigned char **r_newkeydata, + size_t *r_newkeydatalen) +{ + gpg_error_t err; + const unsigned char *buf, *tok; + size_t buflen, toklen, n; + int depth, last_depth1, last_depth2; + const unsigned char *q_ptr; /* Points to the value of "q". */ + size_t q_ptrlen; /* Remaining length in KEYDATA. */ + size_t q_toklen; /* Q's length including prefix. */ + const unsigned char *curve_ptr; /* Points to the value of "curve". */ + size_t curve_ptrlen; /* Remaining length in KEYDATA. */ + gcry_mpi_t x, y; /* Point Q */ + gcry_mpi_t p, a, b; /* Curve parameters. */ + gcry_mpi_t x3, t, p1_4; /* Helper */ + int y_bit; + unsigned char *qvalue; /* Q in uncompressed form. */ + unsigned int qvaluelen; + unsigned char *dst; /* Helper */ + char lenstr[35]; /* Helper for a length prefix. */ + + *r_newkeydata = NULL; + *r_newkeydatalen = 0; + + buf = keydata; + buflen = keydatalen; + depth = 0; + if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) + return err; + if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) + return err; + if (!tok) + return gpg_error (GPG_ERR_BAD_PUBKEY); + else if (toklen == 10 || !memcmp ("public-key", tok, toklen)) + ; + else if (toklen == 11 || !memcmp ("private-key", tok, toklen)) + ; + else if (toklen == 20 || !memcmp ("shadowed-private-key", tok, toklen)) + ; + else + return gpg_error (GPG_ERR_BAD_PUBKEY); + + if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) + return err; + if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) + return err; + + if (tok && toklen == 3 && !memcmp ("ecc", tok, toklen)) + ; + else if (tok && toklen == 5 && !memcmp ("ecdsa", tok, toklen)) + ; + else + return 0; /* Other algo - no need for conversion. */ + + last_depth1 = depth; + q_ptr = curve_ptr = NULL; + q_ptrlen = 0; /*(silence cc warning)*/ + while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)) + && depth && depth >= last_depth1) + { + if (tok) + return gpg_error (GPG_ERR_UNKNOWN_SEXP); + if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) + return err; + if (tok && toklen == 1 && *tok == 'q' && !q_ptr) + { + q_ptr = buf; + q_ptrlen = buflen; + } + else if (tok && toklen == 5 && !memcmp (tok, "curve", 5) && !curve_ptr) + { + curve_ptr = buf; + curve_ptrlen = buflen; + } + + if (q_ptr && curve_ptr) + break; /* We got all what we need. */ + + /* Skip to the end of the list. */ + last_depth2 = depth; + while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)) + && depth && depth >= last_depth2) + ; + if (err) + return err; + } + if (err) + return err; + + if (!q_ptr) + return 0; /* No Q - nothing to do. */ + + /* Get Q's value and check whether uncompressing is at all required. */ + buf = q_ptr; + buflen = q_ptrlen; + if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) + return err; + if (toklen < 2 || !(*tok == 0x02 || *tok == 0x03)) + return 0; /* Invalid length or not compressed. */ + q_toklen = buf - q_ptr; /* We want the length with the prefix. */ + + /* Put the x-coordinate of q into X and remember the y bit */ + y_bit = (*tok == 0x03); + err = gcry_mpi_scan (&x, GCRYMPI_FMT_USG, tok+1, toklen-1, NULL); + if (err) + return err; + + /* For uncompressing we need to know the curve. */ + if (!curve_ptr) + { + gcry_mpi_release (x); + return gpg_error (GPG_ERR_INV_CURVE); + } + buf = curve_ptr; + buflen = curve_ptrlen; + if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) + { + gcry_mpi_release (x); + return err; + } + + { + char name[50]; + gcry_sexp_t curveparam; + + if (toklen + 1 > sizeof name) + { + gcry_mpi_release (x); + return gpg_error (GPG_ERR_TOO_LARGE); + } + mem2str (name, tok, toklen+1); + curveparam = gcry_pk_get_param (GCRY_PK_ECC, name); + if (!curveparam) + { + gcry_mpi_release (x); + return gpg_error (GPG_ERR_UNKNOWN_CURVE); + } + + err = gcry_sexp_extract_param (curveparam, NULL, "pab", &p, &a, &b, NULL); + gcry_sexp_release (curveparam); + if (err) + { + gcry_mpi_release (x); + return gpg_error (GPG_ERR_INTERNAL); + } + } + + if (!mpi_test_bit (p, 1)) + { + /* No support for point compression for this curve. */ + gcry_mpi_release (x); + gcry_mpi_release (p); + gcry_mpi_release (a); + gcry_mpi_release (b); + return gpg_error (GPG_ERR_NOT_IMPLEMENTED); + } + + /* + * Recover Y. The Weierstrass curve: y^2 = x^3 + a*x + b + */ + + x3 = mpi_new (0); + t = mpi_new (0); + p1_4 = mpi_new (0); + y = mpi_new (0); + + /* Compute right hand side. */ + mpi_powm (x3, x, GCRYMPI_CONST_THREE, p); + mpi_mul (t, a, x); + mpi_mod (t, t, p); + mpi_add (t, t, b); + mpi_mod (t, t, p); + mpi_add (t, t, x3); + mpi_mod (t, t, p); + + /* + * When p mod 4 = 3, modular square root of A can be computed by + * A^((p+1)/4) mod p + */ + + /* Compute (p+1)/4 into p1_4 */ + mpi_rshift (p1_4, p, 2); + mpi_add_ui (p1_4, p1_4, 1); + + mpi_powm (y, t, p1_4, p); + + if (y_bit != mpi_test_bit (y, 0)) + mpi_sub (y, p, y); + + gcry_mpi_release (p1_4); + gcry_mpi_release (t); + gcry_mpi_release (x3); + gcry_mpi_release (a); + gcry_mpi_release (b); + + err = ec2os (x, y, p, &qvalue, &qvaluelen); + gcry_mpi_release (x); + gcry_mpi_release (y); + gcry_mpi_release (p); + if (err) + return err; + + snprintf (lenstr, sizeof lenstr, "%u:", (unsigned int)qvaluelen); + /* Note that for simplicity we do not subtract the old length of Q + * for the new buffer. */ + *r_newkeydata = xtrymalloc (qvaluelen + strlen(lenstr) + qvaluelen); + if (!*r_newkeydata) + return gpg_error_from_syserror (); + dst = *r_newkeydata; + + n = q_ptr - keydata; + memcpy (dst, keydata, n); /* Copy first part of original data. */ + dst += n; + + n = strlen (lenstr); + memcpy (dst, lenstr, n); /* Copy new prefix of Q's value. */ + dst += n; + + memcpy (dst, qvalue, qvaluelen); /* Copy new value of Q. */ + dst += qvaluelen; + + log_assert (q_toklen < q_ptrlen); + n = q_ptrlen - q_toklen; + memcpy (dst, q_ptr + q_toklen, n);/* Copy rest of original data. */ + dst += n; + + *r_newkeydatalen = dst - *r_newkeydata; + + xfree (qvalue); + + return 0; +} + + +/* Return the algo of a public KEY of SEXP. */ +int +get_pk_algo_from_key (gcry_sexp_t key) +{ + gcry_sexp_t list; + const char *s; + size_t n; + char algoname[6]; + int algo = 0; + + list = gcry_sexp_nth (key, 1); + if (!list) + goto out; + s = gcry_sexp_nth_data (list, 0, &n); + if (!s) + goto out; + if (n >= sizeof (algoname)) + goto out; + memcpy (algoname, s, n); + algoname[n] = 0; + + algo = gcry_pk_map_name (algoname); + if (algo == GCRY_PK_ECC) + { + gcry_sexp_t l1 = gcry_sexp_find_token (list, "flags", 0); + int i; + + for (i = l1 ? gcry_sexp_length (l1)-1 : 0; i > 0; i--) + { + s = gcry_sexp_nth_data (l1, i, &n); + if (!s) + continue; /* Not a data element. */ + + if (n == 5 && !memcmp (s, "eddsa", 5)) + { + algo = GCRY_PK_EDDSA; + break; + } + } + gcry_sexp_release (l1); + } + + out: + gcry_sexp_release (list); + + return algo; +} + + +/* This is a variant of get_pk_algo_from_key but takes an canonical + * encoded S-expression as input. Returns a GCRYPT public key + * identiier or 0 on error. */ +int +get_pk_algo_from_canon_sexp (const unsigned char *keydata, size_t keydatalen) +{ + gcry_sexp_t sexp; + int algo; + + if (gcry_sexp_sscan (&sexp, NULL, keydata, keydatalen)) + return 0; + + algo = get_pk_algo_from_key (sexp); + gcry_sexp_release (sexp); + return algo; +} + + +/* Given the public key S_PKEY, return a new buffer with a descriptive + * string for its algorithm. This function may return NULL on memory + * error. If R_ALGOID is not NULL the gcrypt algo id is stored there. */ +char * +pubkey_algo_string (gcry_sexp_t s_pkey, enum gcry_pk_algos *r_algoid) +{ + const char *prefix; + gcry_sexp_t l1; + char *algoname; + int algo; + char *result; + + if (r_algoid) + *r_algoid = 0; + + l1 = gcry_sexp_find_token (s_pkey, "public-key", 0); + if (!l1) + return xtrystrdup ("E_no_key"); + { + gcry_sexp_t l_tmp = gcry_sexp_cadr (l1); + gcry_sexp_release (l1); + l1 = l_tmp; + } + algoname = gcry_sexp_nth_string (l1, 0); + gcry_sexp_release (l1); + if (!algoname) + return xtrystrdup ("E_no_algo"); + + algo = gcry_pk_map_name (algoname); + switch (algo) + { + case GCRY_PK_RSA: prefix = "rsa"; break; + case GCRY_PK_ELG: prefix = "elg"; break; + case GCRY_PK_DSA: prefix = "dsa"; break; + case GCRY_PK_ECC: prefix = ""; break; + default: prefix = NULL; break; + } + + if (prefix && *prefix) + result = xtryasprintf ("%s%u", prefix, gcry_pk_get_nbits (s_pkey)); + else if (prefix) + { + const char *curve = gcry_pk_get_curve (s_pkey, 0, NULL); + const char *name = openpgp_oid_to_curve + (openpgp_curve_to_oid (curve, NULL, NULL), 0); + + if (name) + result = xtrystrdup (name); + else if (curve) + result = xtryasprintf ("X_%s", curve); + else + result = xtrystrdup ("E_unknown"); + } + else + result = xtryasprintf ("X_algo_%d", algo); + + if (r_algoid) + *r_algoid = algo; + xfree (algoname); + return result; +} + + +/* Map a pubkey algo id from gcrypt to a string. This is the same as + * gcry_pk_algo_name but makes sure that the ECC algo identifiers are + * not all mapped to "ECC". */ +const char * +pubkey_algo_to_string (int algo) +{ + if (algo == GCRY_PK_ECDSA) + return "ECDSA"; + else if (algo == GCRY_PK_ECDH) + return "ECDH"; + else if (algo == GCRY_PK_EDDSA) + return "EdDSA"; + else + return gcry_pk_algo_name (algo); +} + + +/* Map a hash algo id from gcrypt to a string. This is the same as + * gcry_md_algo_name but the returned string is lower case, as + * expected by libksba and it avoids some overhead. */ +const char * +hash_algo_to_string (int algo) +{ + static const struct + { + const char *name; + int algo; + } hashnames[] = + { + { "sha256", GCRY_MD_SHA256 }, + { "sha512", GCRY_MD_SHA512 }, + { "sha1", GCRY_MD_SHA1 }, + { "sha384", GCRY_MD_SHA384 }, + { "sha224", GCRY_MD_SHA224 }, + { "sha3-224", GCRY_MD_SHA3_224 }, + { "sha3-256", GCRY_MD_SHA3_256 }, + { "sha3-384", GCRY_MD_SHA3_384 }, + { "sha3-512", GCRY_MD_SHA3_512 }, + { "ripemd160", GCRY_MD_RMD160 }, + { "rmd160", GCRY_MD_RMD160 }, + { "md2", GCRY_MD_MD2 }, + { "md4", GCRY_MD_MD4 }, + { "tiger", GCRY_MD_TIGER }, + { "haval", GCRY_MD_HAVAL }, +#if GCRYPT_VERSION_NUMBER >= 0x010900 + { "sm3", GCRY_MD_SM3 }, +#endif + { "md5", GCRY_MD_MD5 } + }; + int i; + + for (i=0; i < DIM (hashnames); i++) + if (algo == hashnames[i].algo) + return hashnames[i].name; + return "?"; +} + + +/* Map cipher modes to a string. */ +const char * +cipher_mode_to_string (int mode) +{ + switch (mode) + { + case GCRY_CIPHER_MODE_CFB: return "CFB"; + case GCRY_CIPHER_MODE_CBC: return "CBC"; + case GCRY_CIPHER_MODE_GCM: return "GCM"; + case GCRY_CIPHER_MODE_OCB: return "OCB"; + case 14: return "EAX"; /* Only in gcrypt 1.9 */ + default: return "[?]"; + } +} diff --git a/common/shareddefs.h b/common/shareddefs.h new file mode 100644 index 0000000..4b14421 --- /dev/null +++ b/common/shareddefs.h @@ -0,0 +1,61 @@ +/* shareddefs.h - Constants and helpers useful for all modules + * Copyright (C) 2013 Free Software Foundation, Inc. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef GNUPG_COMMON_SHAREDDEFS_H +#define GNUPG_COMMON_SHAREDDEFS_H + +/* Values for the pinentry mode. */ +typedef enum + { + PINENTRY_MODE_ASK = 0, /* Ask via pinentry (default). */ + PINENTRY_MODE_CANCEL, /* Always return a cancel error. */ + PINENTRY_MODE_ERROR, /* Return error code for no pinentry. */ + PINENTRY_MODE_LOOPBACK /* Use an inquiry to get the value. */ + } +pinentry_mode_t; + + +/* Values for the request origin. */ +typedef enum + { + REQUEST_ORIGIN_LOCAL = 0, + REQUEST_ORIGIN_REMOTE, + REQUEST_ORIGIN_BROWSER + } +request_origin_t; + + +/*-- agent-opt.c --*/ +int parse_pinentry_mode (const char *value); +const char *str_pinentry_mode (pinentry_mode_t mode); + +int parse_request_origin (const char *value); +const char *str_request_origin (request_origin_t mode); + + + +#endif /*GNUPG_COMMON_SHAREDDEFS_H*/ diff --git a/common/signal.c b/common/signal.c new file mode 100644 index 0000000..92925fd --- /dev/null +++ b/common/signal.c @@ -0,0 +1,251 @@ +/* signal.c - signal handling + * Copyright (C) 1998, 1999, 2000, 2001, 2002, + * 2005 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#ifdef HAVE_SIGNAL_H +# include <signal.h> +#endif +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <assert.h> + +#include "util.h" + + +#ifndef HAVE_DOSISH_SYSTEM +static volatile int caught_fatal_sig; +static volatile int caught_sigusr1; +#endif +static void (*cleanup_fnc)(void); + + +#ifndef HAVE_DOSISH_SYSTEM +static void +init_one_signal (int sig, RETSIGTYPE (*handler)(int), int check_ign ) +{ +# ifdef HAVE_SIGACTION + struct sigaction oact, nact; + + if (check_ign) + { + /* we don't want to change an IGN handler */ + sigaction (sig, NULL, &oact ); + if (oact.sa_handler == SIG_IGN ) + return; + } + + nact.sa_handler = handler; + sigemptyset (&nact.sa_mask); + nact.sa_flags = 0; + sigaction ( sig, &nact, NULL); +# else + RETSIGTYPE (*ohandler)(int); + + ohandler = signal (sig, handler); + if (check_ign && ohandler == SIG_IGN) + { + /* Change it back if it was already set to IGN */ + signal (sig, SIG_IGN); + } +# endif +} +#endif /*!HAVE_DOSISH_SYSTEM*/ + +#ifndef HAVE_DOSISH_SYSTEM +static const char * +get_signal_name( int signum ) +{ + /* Note that we can't use strsignal(), because it is not + reentrant. */ +#if HAVE_SIGDESCR_NP + return sigdescr_np (signum); +#elif HAVE_DECL_SYS_SIGLIST && defined(NSIG) + return (signum >= 0 && signum < NSIG) ? sys_siglist[signum] : "?"; +#else + return NULL; +#endif +} +#endif /*!HAVE_DOSISH_SYSTEM*/ + +#ifndef HAVE_DOSISH_SYSTEM +static RETSIGTYPE +got_fatal_signal (int sig) +{ + const char *s; + + if (caught_fatal_sig) + raise (sig); + caught_fatal_sig = 1; + + if (cleanup_fnc) + cleanup_fnc (); + /* Better don't translate these messages. */ + (void)write (2, "\n", 1 ); + s = log_get_prefix (NULL); + if (s) + (void)write(2, s, strlen (s)); + (void)write (2, ": signal ", 9 ); + s = get_signal_name(sig); + if (s) + (void) write (2, s, strlen(s) ); + else + { + /* We are in a signal handler so we can't use any kind of printf + even not sprintf. So we use a straightforward algorithm. We + got a report that on one particular system, raising a signal + while in this handler, the parameter SIG get sclobbered and + things are messed up because we modify its value. Although + this is a bug in that system, we will protect against it. */ + if (sig < 0 || sig >= 100000) + (void)write (2, "?", 1); + else + { + int i, value, any=0; + + for (value=sig,i=10000; i; i /= 10) + { + if (value >= i || ((any || i==1) && !(value/i))) + { + (void)write (2, &"0123456789"[value/i], 1); + if ((value/i)) + any = 1; + value %= i; + } + } + } + } + (void)write (2, " caught ... exiting\n", 20); + + /* Reset action to default action and raise signal again */ + init_one_signal (sig, SIG_DFL, 0); + /* Fixme: remove_lockfiles ();*/ +#ifdef __riscos__ + close_fds (); +#endif /* __riscos__ */ + raise( sig ); +} +#endif /*!HAVE_DOSISH_SYSTEM*/ + +#ifndef HAVE_DOSISH_SYSTEM +static RETSIGTYPE +got_usr_signal (int sig) +{ + (void)sig; + caught_sigusr1 = 1; +} +#endif /*!HAVE_DOSISH_SYSTEM*/ + +void +gnupg_init_signals (int mode, void (*fast_cleanup)(void)) +{ + assert (!mode); + + cleanup_fnc = fast_cleanup; +#ifndef HAVE_DOSISH_SYSTEM + init_one_signal (SIGINT, got_fatal_signal, 1 ); + init_one_signal (SIGHUP, got_fatal_signal, 1 ); + init_one_signal (SIGTERM, got_fatal_signal, 1 ); + init_one_signal (SIGQUIT, got_fatal_signal, 1 ); + init_one_signal (SIGSEGV, got_fatal_signal, 1 ); + init_one_signal (SIGUSR1, got_usr_signal, 0 ); + init_one_signal (SIGPIPE, SIG_IGN, 0 ); +#endif +} + + +static void +do_block (int block) +{ +#ifdef HAVE_DOSISH_SYSTEM + (void)block; +#else /*!HAVE_DOSISH_SYSTEM*/ + static int is_blocked; +#ifdef HAVE_SIGPROCMASK + static sigset_t oldmask; + + if (block) + { + sigset_t newmask; + + if (is_blocked) + log_bug ("signals are already blocked\n"); + sigfillset( &newmask ); + sigprocmask( SIG_BLOCK, &newmask, &oldmask ); + is_blocked = 1; + } + else + { + if (!is_blocked) + log_bug("signals are not blocked\n"); + sigprocmask (SIG_SETMASK, &oldmask, NULL); + is_blocked = 0; + } +#else /*!HAVE_SIGPROCMASK*/ + static void (*disposition[MAXSIG])(); + int sig; + + if (block) + { + if (is_blocked) + log_bug("signals are already blocked\n"); + for (sig=1; sig < MAXSIG; sig++) + { + disposition[sig] = sigset (sig, SIG_HOLD); + } + is_blocked = 1; + } + else + { + if (!is_blocked) + log_bug ("signals are not blocked\n"); + for (sig=1; sig < MAXSIG; sig++) { + sigset (sig, disposition[sig]); + } + is_blocked = 0; + } +#endif /*!HAVE_SIGPROCMASK*/ +#endif /*!HAVE_DOSISH_SYSTEM*/ +} + + +void +gnupg_block_all_signals () +{ + do_block(1); +} + +void +gnupg_unblock_all_signals () +{ + do_block(0); +} diff --git a/common/simple-pwquery.c b/common/simple-pwquery.c new file mode 100644 index 0000000..b8ada42 --- /dev/null +++ b/common/simple-pwquery.c @@ -0,0 +1,495 @@ +/* simple-pwquery.c - A simple password query client for gpg-agent + * Copyright (C) 2002, 2004, 2007 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +/* This module is intended as a simple client implementation to + gpg-agent's GET_PASSPHRASE command. It can only cope with an + already running gpg-agent. Some stuff is configurable in the + header file. */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include <stdlib.h> +#include <stddef.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <assuan.h> +#ifdef HAVE_W32_SYSTEM +#include <winsock2.h> +#else +#include <sys/socket.h> +#include <sys/un.h> +#endif +#ifdef HAVE_LOCALE_H +#include <locale.h> +#endif + +#define GNUPG_COMMON_NEED_AFLOCAL +#include "../common/mischelp.h" +#include "sysutils.h" +#include "membuf.h" + + +#define SIMPLE_PWQUERY_IMPLEMENTATION 1 +#include "simple-pwquery.h" + +#define SPWQ_OUT_OF_CORE gpg_error_from_errno (ENOMEM) +#define SPWQ_IO_ERROR gpg_error_from_errno (EIO) +#define SPWQ_PROTOCOL_ERROR gpg_error (GPG_ERR_PROTOCOL_VIOLATION) +#define SPWQ_ERR_RESPONSE gpg_error (GPG_ERR_INV_RESPONSE) +#define SPWQ_NO_AGENT gpg_error (GPG_ERR_NO_AGENT) +#define SPWQ_SYS_ERROR gpg_error_from_syserror () +#define SPWQ_GENERAL_ERROR gpg_error (GPG_ERR_GENERAL) +#define SPWQ_NO_PIN_ENTRY gpg_error (GPG_ERR_NO_PIN_ENTRY) + +#ifndef _ +#define _(a) (a) +#endif + +#if !defined (hexdigitp) && !defined (xtoi_2) +#define digitp(p) (*(p) >= '0' && *(p) <= '9') +#define hexdigitp(a) (digitp (a) \ + || (*(a) >= 'A' && *(a) <= 'F') \ + || (*(a) >= 'a' && *(a) <= 'f')) +#define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \ + *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10)) +#define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1)) +#endif + + +/* Name of the socket to be used. This is a kludge to keep on using + the existsing code despite that we only support a standard socket. */ +static char *default_gpg_agent_info; + + + + + +#ifndef HAVE_STPCPY +static char * +my_stpcpy(char *a,const char *b) +{ + while( *b ) + *a++ = *b++; + *a = 0; + + return (char*)a; +} +#define stpcpy(a,b) my_stpcpy((a), (b)) +#endif + + +/* Send an option to the agent */ +static int +agent_send_option (assuan_context_t ctx, const char *name, const char *value) +{ + int err; + char *line; + + line = spwq_malloc (7 + strlen (name) + 1 + strlen (value) + 2); + if (!line) + return SPWQ_OUT_OF_CORE; + strcpy (stpcpy (stpcpy (stpcpy ( + stpcpy (line, "OPTION "), name), "="), value), "\n"); + + err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); + + spwq_free (line); + return err; +} + + +/* Send all available options to the agent. */ +static int +agent_send_all_options (assuan_context_t ctx) +{ + char *dft_display = NULL; + char *dft_ttyname = NULL; + char *dft_ttytype = NULL; + char *dft_xauthority = NULL; + char *dft_pinentry_user_data = NULL; + int rc = 0; + + dft_display = getenv ("DISPLAY"); + if (dft_display) + { + if ((rc = agent_send_option (ctx, "display", dft_display))) + return rc; + } + + dft_ttyname = getenv ("GPG_TTY"); +#if !defined(HAVE_W32_SYSTEM) && !defined(HAVE_BROKEN_TTYNAME) + if ((!dft_ttyname || !*dft_ttyname) && ttyname (0)) + dft_ttyname = ttyname (0); +#endif + if (dft_ttyname && *dft_ttyname) + { + if ((rc=agent_send_option (ctx, "ttyname", dft_ttyname))) + return rc; + } + + dft_ttytype = getenv ("TERM"); + if (dft_ttyname && dft_ttytype) + { + if ((rc = agent_send_option (ctx, "ttytype", dft_ttytype))) + return rc; + } + +#if defined(HAVE_SETLOCALE) + { + char *old_lc = NULL; + char *dft_lc = NULL; + +#if defined(LC_CTYPE) + old_lc = setlocale (LC_CTYPE, NULL); + if (old_lc) + { + char *p = spwq_malloc (strlen (old_lc)+1); + if (!p) + return SPWQ_OUT_OF_CORE; + strcpy (p, old_lc); + old_lc = p; + } + dft_lc = setlocale (LC_CTYPE, ""); + if (dft_ttyname && dft_lc) + rc = agent_send_option (ctx, "lc-ctype", dft_lc); + if (old_lc) + { + setlocale (LC_CTYPE, old_lc); + spwq_free (old_lc); + } + if (rc) + return rc; +#endif + +#if defined(LC_MESSAGES) + old_lc = setlocale (LC_MESSAGES, NULL); + if (old_lc) + { + char *p = spwq_malloc (strlen (old_lc)+1); + if (!p) + return SPWQ_OUT_OF_CORE; + strcpy (p, old_lc); + old_lc = p; + } + dft_lc = setlocale (LC_MESSAGES, ""); + if (dft_ttyname && dft_lc) + rc = agent_send_option (ctx, "lc-messages", dft_lc); + if (old_lc) + { + setlocale (LC_MESSAGES, old_lc); + spwq_free (old_lc); + } + if (rc) + return rc; +#endif + } +#endif /*HAVE_SETLOCALE*/ + + /* Send the XAUTHORITY variable. */ + dft_xauthority = getenv ("XAUTHORITY"); + if (dft_xauthority) + { + /* We ignore errors here because older gpg-agents don't support + this option. */ + agent_send_option (ctx, "xauthority", dft_xauthority); + } + + /* Send the PINENTRY_USER_DATA variable. */ + dft_pinentry_user_data = getenv ("PINENTRY_USER_DATA"); + if (dft_pinentry_user_data) + { + /* We ignore errors here because older gpg-agents don't support + this option. */ + agent_send_option (ctx, "pinentry-user-data", dft_pinentry_user_data); + } + + /* Tell the agent that we support Pinentry notifications. No + error checking so that it will work with older agents. */ + assuan_transact (ctx, "OPTION allow-pinentry-notify", + NULL, NULL, NULL, NULL, NULL, NULL); + + return 0; +} + + + +/* Try to open a connection to the agent, send all options and return + the file descriptor for the connection. Return -1 in case of + error. */ +static int +agent_open (assuan_context_t *ctx) +{ + int rc; + char *infostr; + + infostr = default_gpg_agent_info; + if ( !infostr || !*infostr ) + { +#ifdef SPWQ_USE_LOGGING + log_error (_("no gpg-agent running in this session\n")); +#endif + *ctx = NULL; + return SPWQ_NO_AGENT; + } + + rc = assuan_new (ctx); + if (rc) + return rc; + + rc = assuan_socket_connect (*ctx, infostr, 0, 0); + if (rc) + { +#ifdef SPWQ_USE_LOGGING + log_error (_("can't connect to '%s': %s\n"), + infostr, gpg_strerror (rc)); +#endif + goto errout; + } + + rc = agent_send_all_options (*ctx); + if (rc) + { +#ifdef SPWQ_USE_LOGGING + log_error (_("problem setting the gpg-agent options\n")); +#endif + goto errout; + } + + return 0; + + errout: + assuan_release (*ctx); + *ctx = NULL; + return rc; +} + + +/* Copy text to BUFFER and escape as required. Return a pointer to + the end of the new buffer. Note that BUFFER must be large enough + to keep the entire text; allocataing it 3 times the size of TEXT + is sufficient. */ +static char * +copy_and_escape (char *buffer, const char *text) +{ + int i; + const unsigned char *s = (unsigned char *)text; + char *p = buffer; + + + for (i=0; s[i]; i++) + { + if (s[i] < ' ' || s[i] == '+') + { + sprintf (p, "%%%02X", s[i]); + p += 3; + } + else if (s[i] == ' ') + *p++ = '+'; + else + *p++ = s[i]; + } + return p; +} + + +/* Set the name of the default socket to NAME. */ +int +simple_pw_set_socket (const char *name) +{ + spwq_free (default_gpg_agent_info); + default_gpg_agent_info = NULL; + if (name) + { + default_gpg_agent_info = spwq_malloc (strlen (name) + 1); + if (!default_gpg_agent_info) + return SPWQ_OUT_OF_CORE; + strcpy (default_gpg_agent_info, name); + } + + return 0; +} + + +/* This is the default inquiry callback. It merely handles the + Pinentry notification. */ +static gpg_error_t +default_inq_cb (void *opaque, const char *line) +{ + (void)opaque; + + if (!strncmp (line, "PINENTRY_LAUNCHED", 17) && (line[17]==' '||!line[17])) + { + gnupg_allow_set_foregound_window ((pid_t)strtoul (line+17, NULL, 10)); + /* We do not return errors to avoid breaking other code. */ + } + else + { +#ifdef SPWQ_USE_LOGGING + log_debug ("ignoring gpg-agent inquiry '%s'\n", line); +#endif + } + + return 0; +} + + +/* Ask the gpg-agent for a passphrase and present the user with a + DESCRIPTION, a PROMPT and optionally with a TRYAGAIN extra text. + If a CACHEID is not NULL it is used to locate the passphrase in + the cache and store it under this ID. If OPT_CHECK is true + gpg-agent is asked to apply some checks on the passphrase security. + If ERRORCODE is not NULL it should point a variable receiving an + errorcode; this error code might be 0 if the user canceled the + operation. The function returns NULL to indicate an error. */ +char * +simple_pwquery (const char *cacheid, + const char *tryagain, + const char *prompt, + const char *description, + int opt_check, + int *errorcode) +{ + int rc; + assuan_context_t ctx; + membuf_t data; + char *result = NULL; + char *pw = NULL; + char *p; + size_t n; + + + rc = agent_open (&ctx); + if (rc) + goto leave; + + if (!cacheid) + cacheid = "X"; + if (!tryagain) + tryagain = "X"; + if (!prompt) + prompt = "X"; + if (!description) + description = "X"; + + { + char *line; + /* We allocate 3 times the needed space so that there is enough + space for escaping. */ + line = spwq_malloc (15 + 10 + + 3*strlen (cacheid) + 1 + + 3*strlen (tryagain) + 1 + + 3*strlen (prompt) + 1 + + 3*strlen (description) + 1 + + 2); + if (!line) + { + rc = SPWQ_OUT_OF_CORE; + goto leave; + } + strcpy (line, "GET_PASSPHRASE "); + p = line+15; + if (opt_check) + p = stpcpy (p, "--check "); + p = copy_and_escape (p, cacheid); + *p++ = ' '; + p = copy_and_escape (p, tryagain); + *p++ = ' '; + p = copy_and_escape (p, prompt); + *p++ = ' '; + p = copy_and_escape (p, description); + *p++ = '\n'; + + init_membuf_secure (&data, 64); + + rc = assuan_transact (ctx, line, put_membuf_cb, &data, + default_inq_cb, NULL, NULL, NULL); + spwq_free (line); + + /* Older Pinentries return the old assuan error code for canceled + which gets translated by libassuan to GPG_ERR_ASS_CANCELED and + not to the code for a user cancel. Fix this here. */ + if (rc && gpg_err_source (rc) + && gpg_err_code (rc) == GPG_ERR_ASS_CANCELED) + rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_CANCELED); + + if (rc) + { + p = get_membuf (&data, &n); + if (p) + wipememory (p, n); + spwq_free (p); + } + else + { + put_membuf (&data, "", 1); + result = get_membuf (&data, NULL); + if (pw == NULL) + rc = gpg_error_from_syserror (); + } + } + + leave: + if (errorcode) + *errorcode = rc; + assuan_release (ctx); + return result; +} + + +/* Ask the gpg-agent to clear the passphrase for the cache ID CACHEID. */ +int +simple_pwclear (const char *cacheid) +{ + char line[500]; + char *p; + + /* We need not more than 50 characters for the command and the + terminating nul. */ + if (strlen (cacheid) * 3 > sizeof (line) - 50) + return SPWQ_PROTOCOL_ERROR; + + strcpy (line, "CLEAR_PASSPHRASE "); + p = line + 17; + p = copy_and_escape (p, cacheid); + *p++ = '\n'; + *p++ = '\0'; + + return simple_query (line); +} + + +/* Perform the simple query QUERY (which must be new-line and 0 + terminated) and return the error code. */ +int +simple_query (const char *query) +{ + assuan_context_t ctx; + int rc; + + rc = agent_open (&ctx); + if (rc) + return rc; + + rc = assuan_transact (ctx, query, NULL, NULL, NULL, NULL, NULL, NULL); + + assuan_release (ctx); + return rc; +} diff --git a/common/simple-pwquery.h b/common/simple-pwquery.h new file mode 100644 index 0000000..9bbc53f --- /dev/null +++ b/common/simple-pwquery.h @@ -0,0 +1,70 @@ +/* simple-pwquery.c - A simple password query client for gpg-agent + * Copyright (C) 2002 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef SIMPLE_PWQUERY_H +#define SIMPLE_PWQUERY_H + +#ifdef SIMPLE_PWQUERY_IMPLEMENTATION /* Begin configuration stuff. */ + +/* Include whatever files you need. */ +#include <gcrypt.h> +#include "../common/logging.h" + +/* Try to write error message using the standard gnupg log mechanism. */ +#define SPWQ_USE_LOGGING 1 + +/* Memory allocation functions used by the implementation. Note, that + the returned value is expected to be freed with + spwq_secure_free. */ +#define spwq_malloc(a) gcry_malloc (a) +#define spwq_free(a) gcry_free (a) +#define spwq_secure_malloc(a) gcry_malloc_secure (a) +#define spwq_secure_free(a) gcry_free (a) + +#endif /*SIMPLE_PWQUERY_IMPLEMENTATION*/ /* End configuration stuff. */ + + +/* Ask the gpg-agent for a passphrase and present the user with a + DESCRIPTION, a PROMPT and optiaonlly with a TRYAGAIN extra text. + If a CACHEID is not NULL it is used to locate the passphrase in + the cache and store it under this ID. If OPT_CHECK is true + gpg-agent is asked to apply some checks on the passphrase security. + If ERRORCODE is not NULL it should point a variable receiving an + errorcode; this errocode might be 0 if the user canceled the + operation. The function returns NULL to indicate an error. */ +char *simple_pwquery (const char *cacheid, + const char *tryagain, + const char *prompt, + const char *description, + int opt_check, + int *errorcode); + +/* Ask the gpg-agent to clear the passphrase for the cache ID CACHEID. */ +int simple_pwclear (const char *cacheid); + +/* Perform the simple query QUERY (which must be new-line and 0 + terminated) and return the error code. */ +int simple_query (const char *query); + +/* Set the name of the standard socket to be used if GPG_AGENT_INFO is + not defined. The use of this function is optional but if it needs + to be called before any other function. Returns 0 on success. */ +int simple_pw_set_socket (const char *name); + +#endif /*SIMPLE_PWQUERY_H*/ diff --git a/common/ssh-utils.c b/common/ssh-utils.c new file mode 100644 index 0000000..013b28e --- /dev/null +++ b/common/ssh-utils.c @@ -0,0 +1,354 @@ +/* ssh-utils.c - Secure Shell helper functions + * Copyright (C) 2011 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdlib.h> +#include <errno.h> +#include <ctype.h> +#include <assert.h> + +#include "util.h" +#include "ssh-utils.h" + + +/* Return true if KEYPARMS holds an EdDSA key. */ +static int +is_eddsa (gcry_sexp_t keyparms) +{ + int result = 0; + gcry_sexp_t list; + const char *s; + size_t n; + int i; + + list = gcry_sexp_find_token (keyparms, "flags", 0); + for (i = list ? gcry_sexp_length (list)-1 : 0; i > 0; i--) + { + s = gcry_sexp_nth_data (list, i, &n); + if (!s) + continue; /* Not a data element. */ + + if (n == 5 && !memcmp (s, "eddsa", 5)) + { + result = 1; + break; + } + } + gcry_sexp_release (list); + return result; +} + +/* Dummy functions for es_mopen. */ +static void *dummy_realloc (void *mem, size_t size) { (void) size; return mem; } +static void dummy_free (void *mem) { (void) mem; } + +/* Return the Secure Shell type fingerprint for KEY using digest ALGO. + The length of the fingerprint is returned at R_LEN and the + fingerprint itself at R_FPR. In case of a error code is returned + and NULL stored at R_FPR. */ +static gpg_error_t +get_fingerprint (gcry_sexp_t key, int algo, + void **r_fpr, size_t *r_len, int as_string) +{ + gpg_error_t err; + gcry_sexp_t list = NULL; + gcry_sexp_t l2 = NULL; + const char *s; + char *name = NULL; + int idx; + const char *elems; + gcry_md_hd_t md = NULL; + int blobmode = 0; + + *r_fpr = NULL; + *r_len = 0; + + /* Check that the first element is valid. */ + list = gcry_sexp_find_token (key, "public-key", 0); + if (!list) + list = gcry_sexp_find_token (key, "private-key", 0); + if (!list) + list = gcry_sexp_find_token (key, "protected-private-key", 0); + if (!list) + list = gcry_sexp_find_token (key, "shadowed-private-key", 0); + if (!list) + { + err = gpg_err_make (default_errsource, GPG_ERR_UNKNOWN_SEXP); + goto leave; + } + + l2 = gcry_sexp_cadr (list); + gcry_sexp_release (list); + list = l2; + l2 = NULL; + + name = gcry_sexp_nth_string (list, 0); + if (!name) + { + err = gpg_err_make (default_errsource, GPG_ERR_INV_SEXP); + goto leave; + } + + err = gcry_md_open (&md, algo, 0); + if (err) + goto leave; + + switch (gcry_pk_map_name (name)) + { + case GCRY_PK_RSA: + elems = "en"; + gcry_md_write (md, "\0\0\0\x07ssh-rsa", 11); + break; + + case GCRY_PK_DSA: + elems = "pqgy"; + gcry_md_write (md, "\0\0\0\x07ssh-dss", 11); + break; + + case GCRY_PK_ECC: + if (is_eddsa (list)) + { + elems = "q"; + blobmode = 1; + /* For now there is just one curve, thus no need to switch + on it. */ + gcry_md_write (md, "\0\0\0\x0b" "ssh-ed25519", 15); + } + else + { + /* We only support the 3 standard curves for now. It is + just a quick hack. */ + elems = "q"; + gcry_md_write (md, "\0\0\0\x13" "ecdsa-sha2-nistp", 20); + l2 = gcry_sexp_find_token (list, "curve", 0); + if (!l2) + elems = ""; + else + { + gcry_free (name); + name = gcry_sexp_nth_string (l2, 1); + gcry_sexp_release (l2); + l2 = NULL; + if (!name) + elems = ""; + else if (!strcmp (name, "NIST P-256")||!strcmp (name, "nistp256")) + gcry_md_write (md, "256\0\0\0\x08nistp256", 15); + else if (!strcmp (name, "NIST P-384")||!strcmp (name, "nistp384")) + gcry_md_write (md, "384\0\0\0\x08nistp384", 15); + else if (!strcmp (name, "NIST P-521")||!strcmp (name, "nistp521")) + gcry_md_write (md, "521\0\0\0\x08nistp521", 15); + else + elems = ""; + } + if (!*elems) + err = gpg_err_make (default_errsource, GPG_ERR_UNKNOWN_CURVE); + } + break; + + default: + elems = ""; + err = gpg_err_make (default_errsource, GPG_ERR_PUBKEY_ALGO); + break; + } + if (err) + goto leave; + + + for (idx = 0, s = elems; *s; s++, idx++) + { + l2 = gcry_sexp_find_token (list, s, 1); + if (!l2) + { + err = gpg_err_make (default_errsource, GPG_ERR_INV_SEXP); + goto leave; + } + if (blobmode) + { + const char *blob; + size_t bloblen; + unsigned char lenbuf[4]; + + blob = gcry_sexp_nth_data (l2, 1, &bloblen); + if (!blob) + { + err = gpg_err_make (default_errsource, GPG_ERR_INV_SEXP); + goto leave; + } + blob++; + bloblen--; + lenbuf[0] = bloblen >> 24; + lenbuf[1] = bloblen >> 16; + lenbuf[2] = bloblen >> 8; + lenbuf[3] = bloblen; + gcry_md_write (md, lenbuf, 4); + gcry_md_write (md, blob, bloblen); + } + else + { + gcry_mpi_t a; + unsigned char *buf; + size_t buflen; + + a = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG); + gcry_sexp_release (l2); + l2 = NULL; + if (!a) + { + err = gpg_err_make (default_errsource, GPG_ERR_INV_SEXP); + goto leave; + } + + err = gcry_mpi_aprint (GCRYMPI_FMT_SSH, &buf, &buflen, a); + gcry_mpi_release (a); + if (err) + goto leave; + gcry_md_write (md, buf, buflen); + gcry_free (buf); + } + } + + if (as_string) + { + const char *algo_name; + char *fpr; + + /* Prefix string with the algorithm name and a colon. */ + algo_name = gcry_md_algo_name (algo); + *r_fpr = xtrymalloc (strlen (algo_name) + 1 + 3 * gcry_md_get_algo_dlen (algo) + 1); + if (*r_fpr == NULL) + { + err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); + goto leave; + } + + memcpy (*r_fpr, algo_name, strlen (algo_name)); + fpr = (char *) *r_fpr + strlen (algo_name); + *fpr++ = ':'; + + if (algo == GCRY_MD_MD5) + { + bin2hexcolon (gcry_md_read (md, algo), gcry_md_get_algo_dlen (algo), fpr); + strlwr (fpr); + } + else + { + struct b64state b64s; + estream_t stream; + char *p; + long int len; + + /* Write the base64-encoded hash to fpr. */ + stream = es_mopen (fpr, 3 * gcry_md_get_algo_dlen (algo) + 1, 0, + 0, dummy_realloc, dummy_free, "w"); + if (stream == NULL) + { + err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); + goto leave; + } + + err = b64enc_start_es (&b64s, stream, ""); + if (err) + { + es_fclose (stream); + goto leave; + } + + err = b64enc_write (&b64s, + gcry_md_read (md, algo), gcry_md_get_algo_dlen (algo)); + if (err) + { + es_fclose (stream); + goto leave; + } + + /* Finish, get the length, and close the stream. */ + err = b64enc_finish (&b64s); + len = es_ftell (stream); + es_fclose (stream); + if (err) + goto leave; + + /* Terminate. */ + fpr[len] = 0; + + /* Strip the trailing padding characters. */ + for (p = fpr + len - 1; p > fpr && *p == '='; p--) + *p = 0; + } + + *r_len = strlen (*r_fpr) + 1; + } + else + { + *r_len = gcry_md_get_algo_dlen (algo); + *r_fpr = xtrymalloc (*r_len); + if (!*r_fpr) + { + err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); + goto leave; + } + memcpy (*r_fpr, gcry_md_read (md, algo), *r_len); + } + err = 0; + + leave: + gcry_free (name); + gcry_sexp_release (l2); + gcry_md_close (md); + gcry_sexp_release (list); + return err; +} + +/* Return the Secure Shell type fingerprint for KEY using digest ALGO. + The length of the fingerprint is returned at R_LEN and the + fingerprint itself at R_FPR. In case of an error an error code is + returned and NULL stored at R_FPR. */ +gpg_error_t +ssh_get_fingerprint (gcry_sexp_t key, int algo, + void **r_fpr, size_t *r_len) +{ + return get_fingerprint (key, algo, r_fpr, r_len, 0); +} + + +/* Return the Secure Shell type fingerprint for KEY using digest ALGO + as a string. The fingerprint is mallcoed and stored at R_FPRSTR. + In case of an error an error code is returned and NULL stored at + R_FPRSTR. */ +gpg_error_t +ssh_get_fingerprint_string (gcry_sexp_t key, int algo, char **r_fprstr) +{ + gpg_error_t err; + size_t dummy; + void *string; + + err = get_fingerprint (key, algo, &string, &dummy, 1); + *r_fprstr = string; + return err; +} diff --git a/common/ssh-utils.h b/common/ssh-utils.h new file mode 100644 index 0000000..53d9f55 --- /dev/null +++ b/common/ssh-utils.h @@ -0,0 +1,41 @@ +/* ssh-utils.c - Secure Shell helper function definitions + * Copyright (C) 2011 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef GNUPG_COMMON_SSH_UTILS_H +#define GNUPG_COMMON_SSH_UTILS_H + + +gpg_error_t ssh_get_fingerprint (gcry_sexp_t key, int algo, + void **r_fpr, size_t *r_len); + +gpg_error_t ssh_get_fingerprint_string (gcry_sexp_t key, int algo, + char **r_fprstr); + + +#endif /*GNUPG_COMMON_SSH_UTILS_H*/ diff --git a/common/status-codes.h b/common/status-codes.h new file mode 100644 index 0000000..0d1e6be --- /dev/null +++ b/common/status-codes.h @@ -0,0 +1,248 @@ +/* Output of mkstrtable.awk. DO NOT EDIT. */ + +/* status.h - Status codes + * Copyright (C) 2007 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +/* The purpose of this complex string table is to produce + optimal code with a minimum of relocations. */ + +static const char statusstr_msgstr[] = + "ENTER" "\0" + "LEAVE" "\0" + "ABORT" "\0" + "CANCELED_BY_USER" "\0" + "GOODSIG" "\0" + "BADSIG" "\0" + "ERRSIG" "\0" + "BADARMOR" "\0" + "TRUST_UNDEFINED" "\0" + "TRUST_NEVER" "\0" + "TRUST_MARGINAL" "\0" + "TRUST_FULLY" "\0" + "TRUST_ULTIMATE" "\0" + "NEED_PASSPHRASE" "\0" + "VALIDSIG" "\0" + "SIG_ID" "\0" + "ENC_TO" "\0" + "NODATA" "\0" + "BAD_PASSPHRASE" "\0" + "NO_PUBKEY" "\0" + "NO_SECKEY" "\0" + "NEED_PASSPHRASE_SYM" "\0" + "DECRYPTION_KEY" "\0" + "DECRYPTION_INFO" "\0" + "DECRYPTION_FAILED" "\0" + "DECRYPTION_OKAY" "\0" + "MISSING_PASSPHRASE" "\0" + "GOOD_PASSPHRASE" "\0" + "GOODMDC" "\0" + "BADMDC" "\0" + "ERRMDC" "\0" + "IMPORTED" "\0" + "IMPORT_OK" "\0" + "IMPORT_PROBLEM" "\0" + "IMPORT_RES" "\0" + "IMPORT_CHECK" "\0" + "EXPORTED" "\0" + "EXPORT_RES" "\0" + "FILE_START" "\0" + "FILE_DONE" "\0" + "FILE_ERROR" "\0" + "BEGIN_DECRYPTION" "\0" + "END_DECRYPTION" "\0" + "BEGIN_ENCRYPTION" "\0" + "END_ENCRYPTION" "\0" + "BEGIN_SIGNING" "\0" + "DELETE_PROBLEM" "\0" + "GET_BOOL" "\0" + "GET_LINE" "\0" + "GET_HIDDEN" "\0" + "GOT_IT" "\0" + "PROGRESS" "\0" + "SIG_CREATED" "\0" + "SESSION_KEY" "\0" + "NOTATION_NAME" "\0" + "NOTATION_FLAGS" "\0" + "NOTATION_DATA" "\0" + "POLICY_URL" "\0" + "KEY_CREATED" "\0" + "USERID_HINT" "\0" + "UNEXPECTED" "\0" + "INV_RECP" "\0" + "INV_SGNR" "\0" + "NO_RECP" "\0" + "NO_SGNR" "\0" + "KEY_CONSIDERED" "\0" + "ALREADY_SIGNED" "\0" + "KEYEXPIRED" "\0" + "KEYREVOKED" "\0" + "EXPSIG" "\0" + "EXPKEYSIG" "\0" + "ATTRIBUTE" "\0" + "REVKEYSIG" "\0" + "NEWSIG" "\0" + "SIG_SUBPACKET" "\0" + "PLAINTEXT" "\0" + "PLAINTEXT_LENGTH" "\0" + "KEY_NOT_CREATED" "\0" + "NEED_PASSPHRASE_PIN" "\0" + "CARDCTRL" "\0" + "SC_OP_FAILURE" "\0" + "SC_OP_SUCCESS" "\0" + "BACKUP_KEY_CREATED" "\0" + "PKA_TRUST_BAD" "\0" + "PKA_TRUST_GOOD" "\0" + "TOFU_USER" "\0" + "TOFU_STATS" "\0" + "TOFU_STATS_SHORT" "\0" + "TOFU_STATS_LONG" "\0" + "ENCRYPTION_COMPLIANCE_MODE" "\0" + "DECRYPTION_COMPLIANCE_MODE" "\0" + "VERIFICATION_COMPLIANCE_MODE" "\0" + "TRUNCATED" "\0" + "MOUNTPOINT" "\0" + "BLOCKDEV" "\0" + "PINENTRY_LAUNCHED" "\0" + "PLAINTEXT_FOLLOWS" "\0" + "ERROR" "\0" + "WARNING" "\0" + "SUCCESS" "\0" + "FAILURE" "\0" + "INQUIRE_MAXLEN"; + +static const int statusstr_msgidx[] = + { + 0, + 6, + 12, + 18, + 35, + 43, + 50, + 57, + 66, + 82, + 94, + 109, + 121, + 136, + 152, + 161, + 168, + 175, + 182, + 197, + 207, + 217, + 237, + 252, + 268, + 286, + 302, + 321, + 337, + 345, + 352, + 359, + 368, + 378, + 393, + 404, + 417, + 426, + 437, + 448, + 458, + 469, + 486, + 501, + 518, + 533, + 547, + 562, + 571, + 580, + 591, + 598, + 607, + 619, + 631, + 645, + 660, + 674, + 685, + 697, + 709, + 720, + 729, + 738, + 746, + 754, + 769, + 784, + 795, + 806, + 813, + 823, + 833, + 843, + 850, + 864, + 874, + 891, + 907, + 927, + 936, + 950, + 964, + 983, + 997, + 1012, + 1022, + 1033, + 1050, + 1066, + 1093, + 1120, + 1149, + 1159, + 1170, + 1179, + 1197, + 1215, + 1221, + 1229, + 1237, + 1245, + + }; + +#define statusstr_msgidxof(code) (0 ? -1 \ + : ((code >= 0) && (code <= 101)) ? (code - 0) \ + : -1) diff --git a/common/status.c b/common/status.c new file mode 100644 index 0000000..50afce4 --- /dev/null +++ b/common/status.c @@ -0,0 +1,76 @@ +/* status.c - status code helper functions + * Copyright (C) 2007 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdlib.h> + +#include "util.h" +#include "status.h" +#include "status-codes.h" + + +/* Return the status string for code NO. */ +const char * +get_status_string ( int no ) +{ + int idx = statusstr_msgidxof (no); + if (idx == -1) + return "?"; + else + return statusstr_msgstr + statusstr_msgidx[idx]; +} + + +const char * +get_inv_recpsgnr_code (gpg_error_t err) +{ + const char *errstr; + + switch (gpg_err_code (err)) + { + case GPG_ERR_NO_PUBKEY: errstr = "1"; break; + case GPG_ERR_AMBIGUOUS_NAME: errstr = "2"; break; + case GPG_ERR_WRONG_KEY_USAGE: errstr = "3"; break; + case GPG_ERR_CERT_REVOKED: errstr = "4"; break; + case GPG_ERR_CERT_EXPIRED: errstr = "5"; break; + case GPG_ERR_NO_CRL_KNOWN: errstr = "6"; break; + case GPG_ERR_CRL_TOO_OLD: errstr = "7"; break; + case GPG_ERR_NO_POLICY_MATCH: errstr = "8"; break; + + case GPG_ERR_UNUSABLE_SECKEY: + case GPG_ERR_NO_SECKEY: errstr = "9"; break; + + case GPG_ERR_NOT_TRUSTED: errstr = "10"; break; + case GPG_ERR_MISSING_CERT: errstr = "11"; break; + case GPG_ERR_MISSING_ISSUER_CERT: errstr = "12"; break; + default: errstr = "0"; break; + } + + return errstr; +} diff --git a/common/status.h b/common/status.h new file mode 100644 index 0000000..d5564e4 --- /dev/null +++ b/common/status.h @@ -0,0 +1,170 @@ +/* status.h - Status codes + * Copyright (C) 2007 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef GNUPG_COMMON_STATUS_H +#define GNUPG_COMMON_STATUS_H + +enum + { + STATUS_ENTER, + STATUS_LEAVE, + STATUS_ABORT, + STATUS_CANCELED_BY_USER, + + STATUS_GOODSIG, + STATUS_BADSIG, + STATUS_ERRSIG, + + STATUS_BADARMOR, + + STATUS_TRUST_UNDEFINED, + STATUS_TRUST_NEVER, + STATUS_TRUST_MARGINAL, + STATUS_TRUST_FULLY, + STATUS_TRUST_ULTIMATE, + + STATUS_NEED_PASSPHRASE, + STATUS_VALIDSIG, + STATUS_SIG_ID, + STATUS_ENC_TO, + STATUS_NODATA, + STATUS_BAD_PASSPHRASE, + STATUS_NO_PUBKEY, + STATUS_NO_SECKEY, + STATUS_NEED_PASSPHRASE_SYM, + STATUS_DECRYPTION_KEY, + STATUS_DECRYPTION_INFO, + STATUS_DECRYPTION_FAILED, + STATUS_DECRYPTION_OKAY, + STATUS_MISSING_PASSPHRASE, + STATUS_GOOD_PASSPHRASE, + STATUS_GOODMDC, + STATUS_BADMDC, + STATUS_ERRMDC, + + STATUS_IMPORTED, + STATUS_IMPORT_OK, + STATUS_IMPORT_PROBLEM, + STATUS_IMPORT_RES, + STATUS_IMPORT_CHECK, + + STATUS_EXPORTED, + STATUS_EXPORT_RES, + + STATUS_FILE_START, + STATUS_FILE_DONE, + STATUS_FILE_ERROR, + + STATUS_BEGIN_DECRYPTION, + STATUS_END_DECRYPTION, + STATUS_BEGIN_ENCRYPTION, + STATUS_END_ENCRYPTION, + STATUS_BEGIN_SIGNING, + + STATUS_DELETE_PROBLEM, + + STATUS_GET_BOOL, + STATUS_GET_LINE, + STATUS_GET_HIDDEN, + STATUS_GOT_IT, + + STATUS_PROGRESS, + STATUS_SIG_CREATED, + STATUS_SESSION_KEY, + STATUS_NOTATION_NAME, + STATUS_NOTATION_FLAGS, + STATUS_NOTATION_DATA, + STATUS_POLICY_URL, + STATUS_KEY_CREATED, + STATUS_USERID_HINT, + STATUS_UNEXPECTED, + STATUS_INV_RECP, + STATUS_INV_SGNR, + STATUS_NO_RECP, + STATUS_NO_SGNR, + STATUS_KEY_CONSIDERED, + + STATUS_ALREADY_SIGNED, + STATUS_KEYEXPIRED, + STATUS_KEYREVOKED, + STATUS_EXPSIG, + STATUS_EXPKEYSIG, + + STATUS_ATTRIBUTE, + + STATUS_REVKEYSIG, + + STATUS_NEWSIG, + STATUS_SIG_SUBPACKET, + + STATUS_PLAINTEXT, + STATUS_PLAINTEXT_LENGTH, + STATUS_KEY_NOT_CREATED, + STATUS_NEED_PASSPHRASE_PIN, + + STATUS_CARDCTRL, + STATUS_SC_OP_FAILURE, + STATUS_SC_OP_SUCCESS, + + STATUS_BACKUP_KEY_CREATED, + + STATUS_PKA_TRUST_BAD, + STATUS_PKA_TRUST_GOOD, + + STATUS_TOFU_USER, + STATUS_TOFU_STATS, + STATUS_TOFU_STATS_SHORT, + STATUS_TOFU_STATS_LONG, + + STATUS_ENCRYPTION_COMPLIANCE_MODE, + STATUS_DECRYPTION_COMPLIANCE_MODE, + STATUS_VERIFICATION_COMPLIANCE_MODE, + + STATUS_TRUNCATED, + STATUS_MOUNTPOINT, + STATUS_BLOCKDEV, + + STATUS_PINENTRY_LAUNCHED, + + STATUS_PLAINTEXT_FOLLOWS, /* Used by g13-syshelp */ + + STATUS_ERROR, + STATUS_WARNING, + STATUS_SUCCESS, + STATUS_FAILURE, + + STATUS_INQUIRE_MAXLEN + }; + + +const char *get_status_string (int code); +const char *get_inv_recpsgnr_code (gpg_error_t err); + + +#endif /*GNUPG_COMMON_STATUS_H*/ diff --git a/common/stringhelp.c b/common/stringhelp.c new file mode 100644 index 0000000..5baaa1b --- /dev/null +++ b/common/stringhelp.c @@ -0,0 +1,1795 @@ +/* stringhelp.c - standard string helper functions + * Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006, 2007, + * 2008, 2009, 2010 Free Software Foundation, Inc. + * Copyright (C) 2014 Werner Koch + * Copyright (C) 2015, 2021 g10 Code GmbH + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute and/or modify this + * part of GnuPG under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * GnuPG is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copies of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, see <https://www.gnu.org/licenses/>. + * SPDX-License-Identifier: (LGPL-3.0-or-later OR GPL-2.0-or-later) + */ + +#include <config.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include <ctype.h> +#include <errno.h> +#ifdef HAVE_PWD_H +# include <pwd.h> +#endif +#include <unistd.h> +#include <sys/types.h> +#ifdef HAVE_W32_SYSTEM +# ifdef HAVE_WINSOCK2_H +# include <winsock2.h> +# endif +# include <windows.h> +#endif +#include <limits.h> + +#include "util.h" +#include "common-defs.h" +#include "utf8conv.h" +#include "sysutils.h" +#include "stringhelp.h" + +#define tohex_lower(n) ((n) < 10 ? ((n) + '0') : (((n) - 10) + 'a')) + + +/* Sometimes we want to avoid mixing slashes and backslashes on W32 + and prefer backslashes. There is usual no problem with mixing + them, however a very few W32 API calls can't grok plain slashes. + Printing filenames with mixed slashes also looks a bit strange. + This function has no effext on POSIX. */ +static inline char * +change_slashes (char *name) +{ +#ifdef HAVE_DOSISH_SYSTEM + char *p; + + if (strchr (name, '\\')) + { + for (p=name; *p; p++) + if (*p == '/') + *p = '\\'; + } +#endif /*HAVE_DOSISH_SYSTEM*/ + return name; +} + + +/* + * Check whether STRING starts with KEYWORD. The keyword is + * delimited by end of string, a space or a tab. Returns NULL if not + * found or a pointer into STRING to the next non-space character + * after the KEYWORD (which may be end of string). + */ +char * +has_leading_keyword (const char *string, const char *keyword) +{ + size_t n = strlen (keyword); + + if (!strncmp (string, keyword, n) + && (!string[n] || string[n] == ' ' || string[n] == '\t')) + { + string += n; + while (*string == ' ' || *string == '\t') + string++; + return (char*)string; + } + return NULL; +} + + +/* + * Look for the substring SUB in buffer and return a pointer to that + * substring in BUFFER or NULL if not found. + * Comparison is case-insensitive. + */ +const char * +memistr (const void *buffer, size_t buflen, const char *sub) +{ + const unsigned char *buf = buffer; + const unsigned char *t = (const unsigned char *)buffer; + const unsigned char *s = (const unsigned char *)sub; + size_t n = buflen; + + for ( ; n ; t++, n-- ) + { + if ( toupper (*t) == toupper (*s) ) + { + for ( buf=t++, buflen = n--, s++; + n && toupper (*t) == toupper (*s); t++, s++, n-- ) + ; + if (!*s) + return (const char*)buf; + t = buf; + s = (const unsigned char *)sub ; + n = buflen; + } + } + return NULL; +} + +const char * +ascii_memistr ( const void *buffer, size_t buflen, const char *sub ) +{ + const unsigned char *buf = buffer; + const unsigned char *t = (const unsigned char *)buf; + const unsigned char *s = (const unsigned char *)sub; + size_t n = buflen; + + for ( ; n ; t++, n-- ) + { + if (ascii_toupper (*t) == ascii_toupper (*s) ) + { + for ( buf=t++, buflen = n--, s++; + n && ascii_toupper (*t) == ascii_toupper (*s); t++, s++, n-- ) + ; + if (!*s) + return (const char*)buf; + t = (const unsigned char *)buf; + s = (const unsigned char *)sub ; + n = buflen; + } + } + return NULL; +} + +/* This function is similar to strncpy(). However it won't copy more + than N - 1 characters and makes sure that a '\0' is appended. With + N given as 0, nothing will happen. With DEST given as NULL, memory + will be allocated using xmalloc (i.e. if it runs out of core + the function terminates). Returns DES or a pointer to the + allocated memory. + */ +char * +mem2str( char *dest , const void *src , size_t n ) +{ + char *d; + const char *s; + + if( n ) { + if( !dest ) + dest = xmalloc( n ) ; + d = dest; + s = src ; + for(n--; n && *s; n-- ) + *d++ = *s++; + *d = '\0' ; + } + + return dest ; +} + + +/**************** + * remove leading and trailing white spaces + */ +char * +trim_spaces( char *str ) +{ + char *string, *p, *mark; + + string = str; + /* find first non space character */ + for( p=string; *p && isspace( *(byte*)p ) ; p++ ) + ; + /* move characters */ + for( (mark = NULL); (*string = *p); string++, p++ ) + if( isspace( *(byte*)p ) ) { + if( !mark ) + mark = string ; + } + else + mark = NULL ; + if( mark ) + *mark = '\0' ; /* remove trailing spaces */ + + return str ; +} + + +/* Same as trim_spaces but only consider, space, tab, cr and lf as space. */ +char * +ascii_trim_spaces (char *str) +{ + char *string, *p, *mark; + + string = str; + + /* Find first non-ascii space character. */ + for (p=string; *p && ascii_isspace (*p); p++) + ; + /* Move characters. */ + for (mark=NULL; (*string = *p); string++, p++ ) + { + if (ascii_isspace (*p)) + { + if (!mark) + mark = string; + } + else + mark = NULL ; + } + if (mark) + *mark = '\0' ; /* Remove trailing spaces. */ + + return str ; +} + + +/**************** + * remove trailing white spaces + */ +char * +trim_trailing_spaces( char *string ) +{ + char *p, *mark; + + for( mark = NULL, p = string; *p; p++ ) { + if( isspace( *(byte*)p ) ) { + if( !mark ) + mark = p; + } + else + mark = NULL; + } + if( mark ) + *mark = '\0' ; + + return string ; +} + + +unsigned +trim_trailing_chars( byte *line, unsigned len, const char *trimchars ) +{ + byte *p, *mark; + unsigned n; + + for(mark=NULL, p=line, n=0; n < len; n++, p++ ) { + if( strchr(trimchars, *p ) ) { + if( !mark ) + mark = p; + } + else + mark = NULL; + } + + if( mark ) { + *mark = 0; + return mark - line; + } + return len; +} + +/**************** + * remove trailing white spaces and return the length of the buffer + */ +unsigned +trim_trailing_ws( byte *line, unsigned len ) +{ + return trim_trailing_chars( line, len, " \t\r\n" ); +} + +size_t +length_sans_trailing_chars (const unsigned char *line, size_t len, + const char *trimchars ) +{ + const unsigned char *p, *mark; + size_t n; + + for( mark=NULL, p=line, n=0; n < len; n++, p++ ) + { + if (strchr (trimchars, *p )) + { + if( !mark ) + mark = p; + } + else + mark = NULL; + } + + if (mark) + return mark - line; + return len; +} + +/* + * Return the length of line ignoring trailing white-space. + */ +size_t +length_sans_trailing_ws (const unsigned char *line, size_t len) +{ + return length_sans_trailing_chars (line, len, " \t\r\n"); +} + + + +/* + * Extract from a given path the filename component. This function + * terminates the process on memory shortage. + */ +char * +make_basename(const char *filepath, const char *inputpath) +{ +#ifdef __riscos__ + return riscos_make_basename(filepath, inputpath); +#else + char *p; + + (void)inputpath; /* Only required for riscos. */ + + if ( !(p=strrchr(filepath, '/')) ) +#ifdef HAVE_DOSISH_SYSTEM + if ( !(p=strrchr(filepath, '\\')) ) +#endif +#ifdef HAVE_DRIVE_LETTERS + if ( !(p=strrchr(filepath, ':')) ) +#endif + { + return xstrdup(filepath); + } + + return xstrdup(p+1); +#endif +} + + + +/* + * Extract from a given filename the path prepended to it. If there + * isn't a path prepended to the filename, a dot is returned ('.'). + * This function terminates the process on memory shortage. + */ +char * +make_dirname(const char *filepath) +{ + char *dirname; + int dirname_length; + char *p; + + if ( !(p=strrchr(filepath, '/')) ) +#ifdef HAVE_DOSISH_SYSTEM + if ( !(p=strrchr(filepath, '\\')) ) +#endif +#ifdef HAVE_DRIVE_LETTERS + if ( !(p=strrchr(filepath, ':')) ) +#endif + { + return xstrdup("."); + } + + dirname_length = p-filepath; + dirname = xmalloc(dirname_length+1); + strncpy(dirname, filepath, dirname_length); + dirname[dirname_length] = 0; + + return dirname; +} + + + +static char * +get_pwdir (int xmode, const char *name) +{ + char *result = NULL; +#ifdef HAVE_PWD_H + struct passwd *pwd = NULL; + + if (name) + { +#ifdef HAVE_GETPWNAM + /* Fixme: We should use getpwnam_r if available. */ + pwd = getpwnam (name); +#endif + } + else + { +#ifdef HAVE_GETPWUID + /* Fixme: We should use getpwuid_r if available. */ + pwd = getpwuid (getuid()); +#endif + } + if (pwd) + { + if (xmode) + result = xstrdup (pwd->pw_dir); + else + result = xtrystrdup (pwd->pw_dir); + } +#else /*!HAVE_PWD_H*/ + /* No support at all. */ + (void)xmode; + (void)name; +#endif /*HAVE_PWD_H*/ + return result; +} + + +/* xmode 0 := Return NULL on error + 1 := Terminate on error + 2 := Make sure that name is absolute; return NULL on error + 3 := Make sure that name is absolute; terminate on error + */ +static char * +do_make_filename (int xmode, const char *first_part, va_list arg_ptr) +{ + const char *argv[32]; + int argc; + size_t n; + int skip = 1; + char *home_buffer = NULL; + char *name, *home, *p; + int want_abs; + + want_abs = !!(xmode & 2); + xmode &= 1; + + n = strlen (first_part) + 1; + argc = 0; + while ( (argv[argc] = va_arg (arg_ptr, const char *)) ) + { + n += strlen (argv[argc]) + 1; + if (argc >= DIM (argv)-1) + { + if (xmode) + BUG (); + gpg_err_set_errno (EINVAL); + return NULL; + } + argc++; + } + n++; + + home = NULL; + if (*first_part == '~') + { + if (first_part[1] == '/' || !first_part[1]) + { + /* This is the "~/" or "~" case. */ + home = getenv("HOME"); + if (!home) + home = home_buffer = get_pwdir (xmode, NULL); + if (home && *home) + n += strlen (home); + } + else + { + /* This is the "~username/" or "~username" case. */ + char *user; + + if (xmode) + user = xstrdup (first_part+1); + else + { + user = xtrystrdup (first_part+1); + if (!user) + return NULL; + } + p = strchr (user, '/'); + if (p) + *p = 0; + skip = 1 + strlen (user); + + home = home_buffer = get_pwdir (xmode, user); + xfree (user); + if (home) + n += strlen (home); + else + skip = 1; + } + } + + if (xmode) + name = xmalloc (n); + else + { + name = xtrymalloc (n); + if (!name) + { + xfree (home_buffer); + return NULL; + } + } + + if (home) + p = stpcpy (stpcpy (name, home), first_part + skip); + else + p = stpcpy (name, first_part); + + xfree (home_buffer); + for (argc=0; argv[argc]; argc++) + { + /* Avoid a leading double slash if the first part was "/". */ + if (!argc && name[0] == '/' && !name[1]) + p = stpcpy (p, argv[argc]); + else + p = stpcpy (stpcpy (p, "/"), argv[argc]); + } + + if (want_abs) + { +#ifdef HAVE_DRIVE_LETTERS + p = strchr (name, ':'); + if (p) + p++; + else + p = name; +#else + p = name; +#endif + if (*p != '/' +#ifdef HAVE_DRIVE_LETTERS + && *p != '\\' +#endif + ) + { + home = gnupg_getcwd (); + if (!home) + { + if (xmode) + { + fprintf (stderr, "\nfatal: getcwd failed: %s\n", + strerror (errno)); + exit(2); + } + xfree (name); + return NULL; + } + n = strlen (home) + 1 + strlen (name) + 1; + if (xmode) + home_buffer = xmalloc (n); + else + { + home_buffer = xtrymalloc (n); + if (!home_buffer) + { + xfree (home); + xfree (name); + return NULL; + } + } + if (p == name) + p = home_buffer; + else /* Windows case. */ + { + memcpy (home_buffer, p, p - name + 1); + p = home_buffer + (p - name + 1); + } + + /* Avoid a leading double slash if the cwd is "/". */ + if (home[0] == '/' && !home[1]) + strcpy (stpcpy (p, "/"), name); + else + strcpy (stpcpy (stpcpy (p, home), "/"), name); + + xfree (home); + xfree (name); + name = home_buffer; + /* Let's do a simple compression to catch the most common + case of using "." for gpg's --homedir option. */ + n = strlen (name); + if (n > 2 && name[n-2] == '/' && name[n-1] == '.') + name[n-2] = 0; + } + } + return change_slashes (name); +} + +/* Construct a filename from the NULL terminated list of parts. Tilde + expansion is done for the first argument. This function terminates + the process on memory shortage. */ +char * +make_filename (const char *first_part, ... ) +{ + va_list arg_ptr; + char *result; + + va_start (arg_ptr, first_part); + result = do_make_filename (1, first_part, arg_ptr); + va_end (arg_ptr); + return result; +} + +/* Construct a filename from the NULL terminated list of parts. Tilde + expansion is done for the first argument. This function may return + NULL on error. */ +char * +make_filename_try (const char *first_part, ... ) +{ + va_list arg_ptr; + char *result; + + va_start (arg_ptr, first_part); + result = do_make_filename (0, first_part, arg_ptr); + va_end (arg_ptr); + return result; +} + +/* Construct an absolute filename from the NULL terminated list of + parts. Tilde expansion is done for the first argument. This + function terminates the process on memory shortage. */ +char * +make_absfilename (const char *first_part, ... ) +{ + va_list arg_ptr; + char *result; + + va_start (arg_ptr, first_part); + result = do_make_filename (3, first_part, arg_ptr); + va_end (arg_ptr); + return result; +} + +/* Construct an absolute filename from the NULL terminated list of + parts. Tilde expansion is done for the first argument. This + function may return NULL on error. */ +char * +make_absfilename_try (const char *first_part, ... ) +{ + va_list arg_ptr; + char *result; + + va_start (arg_ptr, first_part); + result = do_make_filename (2, first_part, arg_ptr); + va_end (arg_ptr); + return result; +} + + + +/* Compare whether the filenames are identical. This is a + special version of strcmp() taking the semantics of filenames in + account. Note that this function works only on the supplied names + without considering any context like the current directory. See + also same_file_p(). */ +int +compare_filenames (const char *a, const char *b) +{ +#ifdef HAVE_DOSISH_SYSTEM + for ( ; *a && *b; a++, b++ ) + { + if (*a != *b + && (toupper (*(const unsigned char*)a) + != toupper (*(const unsigned char*)b) ) + && !((*a == '/' && *b == '\\') || (*a == '\\' && *b == '/'))) + break; + } + if ((*a == '/' && *b == '\\') || (*a == '\\' && *b == '/')) + return 0; + else + return (toupper (*(const unsigned char*)a) + - toupper (*(const unsigned char*)b)); +#else + return strcmp(a,b); +#endif +} + + +/* Convert a base-10 number in STRING into a 64 bit unsigned int + * value. Leading white spaces are skipped but no error checking is + * done. Thus it is similar to atoi(). */ +uint64_t +string_to_u64 (const char *string) +{ + uint64_t val = 0; + + while (spacep (string)) + string++; + for (; digitp (string); string++) + { + val *= 10; + val += *string - '0'; + } + return val; +} + + +/* Convert 2 hex characters at S to a byte value. Return this value + or -1 if there is an error. */ +int +hextobyte (const char *s) +{ + int c; + + if ( *s >= '0' && *s <= '9' ) + c = 16 * (*s - '0'); + else if ( *s >= 'A' && *s <= 'F' ) + c = 16 * (10 + *s - 'A'); + else if ( *s >= 'a' && *s <= 'f' ) + c = 16 * (10 + *s - 'a'); + else + return -1; + s++; + if ( *s >= '0' && *s <= '9' ) + c += *s - '0'; + else if ( *s >= 'A' && *s <= 'F' ) + c += 10 + *s - 'A'; + else if ( *s >= 'a' && *s <= 'f' ) + c += 10 + *s - 'a'; + else + return -1; + return c; +} + +/* Given a string containing an UTF-8 encoded text, return the number + of characters in this string. It differs from strlen in that it + only counts complete UTF-8 characters. SIZE is the maximum length + of the string in bytes. If SIZE is -1, then a NUL character is + taken to be the end of the string. Note, that this function does + not take combined characters into account. */ +size_t +utf8_charcount (const char *s, int len) +{ + size_t n; + + if (len == 0) + return 0; + + for (n=0; *s; s++) + { + if ( (*s&0xc0) != 0x80 ) /* Exclude continuation bytes: 10xxxxxx */ + n++; + + if (len != -1) + { + len --; + if (len == 0) + break; + } + } + + return n; +} + + +/**************************************************** + ********** W32 specific functions **************** + ****************************************************/ + +#ifdef HAVE_W32_SYSTEM +const char * +w32_strerror (int ec) +{ + static char strerr[256]; + + if (ec == -1) + ec = (int)GetLastError (); +#ifdef HAVE_W32CE_SYSTEM + /* There is only a wchar_t FormatMessage. It does not make much + sense to play the conversion game; we print only the code. */ + snprintf (strerr, sizeof strerr, "ec=%d", (int)GetLastError ()); +#else + FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, ec, + MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), + strerr, DIM (strerr)-1, NULL); + { + /* Strip the CR,LF - we want just the string. */ + size_t n = strlen (strerr); + if (n > 2 && strerr[n-2] == '\r' && strerr[n-1] == '\n' ) + strerr[n-2] = 0; + } +#endif + return strerr; +} +#endif /*HAVE_W32_SYSTEM*/ + + +/**************************************************** + ******** Locale insensitive ctype functions ******** + ****************************************************/ +/* FIXME: replace them by a table lookup and macros */ +int +ascii_isupper (int c) +{ + return c >= 'A' && c <= 'Z'; +} + +int +ascii_islower (int c) +{ + return c >= 'a' && c <= 'z'; +} + +int +ascii_toupper (int c) +{ + if (c >= 'a' && c <= 'z') + c &= ~0x20; + return c; +} + +int +ascii_tolower (int c) +{ + if (c >= 'A' && c <= 'Z') + c |= 0x20; + return c; +} + +/* Lowercase all ASCII characters in S. */ +char * +ascii_strlwr (char *s) +{ + char *p = s; + + for (p=s; *p; p++ ) + if (isascii (*p) && *p >= 'A' && *p <= 'Z') + *p |= 0x20; + + return s; +} + +/* Upcase all ASCII characters in S. */ +char * +ascii_strupr (char *s) +{ + char *p = s; + + for (p=s; *p; p++ ) + if (isascii (*p) && *p >= 'a' && *p <= 'z') + *p &= ~0x20; + + return s; +} + +int +ascii_strcasecmp( const char *a, const char *b ) +{ + if (a == b) + return 0; + + for (; *a && *b; a++, b++) { + if (*a != *b && ascii_toupper(*a) != ascii_toupper(*b)) + break; + } + return *a == *b? 0 : (ascii_toupper (*a) - ascii_toupper (*b)); +} + +int +ascii_strncasecmp (const char *a, const char *b, size_t n) +{ + const unsigned char *p1 = (const unsigned char *)a; + const unsigned char *p2 = (const unsigned char *)b; + unsigned char c1, c2; + + if (p1 == p2 || !n ) + return 0; + + do + { + c1 = ascii_tolower (*p1); + c2 = ascii_tolower (*p2); + + if ( !--n || c1 == '\0') + break; + + ++p1; + ++p2; + } + while (c1 == c2); + + return c1 - c2; +} + + +int +ascii_memcasecmp (const void *a_arg, const void *b_arg, size_t n ) +{ + const char *a = a_arg; + const char *b = b_arg; + + if (a == b) + return 0; + for ( ; n; n--, a++, b++ ) + { + if( *a != *b && ascii_toupper (*a) != ascii_toupper (*b) ) + return *a == *b? 0 : (ascii_toupper (*a) - ascii_toupper (*b)); + } + return 0; +} + +int +ascii_strcmp( const char *a, const char *b ) +{ + if (a == b) + return 0; + + for (; *a && *b; a++, b++) { + if (*a != *b ) + break; + } + return *a == *b? 0 : (*(signed char *)a - *(signed char *)b); +} + + +void * +ascii_memcasemem (const void *haystack, size_t nhaystack, + const void *needle, size_t nneedle) +{ + + if (!nneedle) + return (void*)haystack; /* finding an empty needle is really easy */ + if (nneedle <= nhaystack) + { + const char *a = haystack; + const char *b = a + nhaystack - nneedle; + + for (; a <= b; a++) + { + if ( !ascii_memcasecmp (a, needle, nneedle) ) + return (void *)a; + } + } + return NULL; +} + +/********************************************* + ********** missing string functions ********* + *********************************************/ + +#ifndef HAVE_STPCPY +char * +stpcpy(char *a,const char *b) +{ + while( *b ) + *a++ = *b++; + *a = 0; + + return (char*)a; +} +#endif + +#ifndef HAVE_STRPBRK +/* Find the first occurrence in S of any character in ACCEPT. + Code taken from glibc-2.6/string/strpbrk.c (LGPLv2.1+) and modified. */ +char * +strpbrk (const char *s, const char *accept) +{ + while (*s != '\0') + { + const char *a = accept; + while (*a != '\0') + if (*a++ == *s) + return (char *) s; + ++s; + } + + return NULL; +} +#endif /*!HAVE_STRPBRK*/ + + +#ifndef HAVE_STRSEP +/* Code taken from glibc-2.2.1/sysdeps/generic/strsep.c. */ +char * +strsep (char **stringp, const char *delim) +{ + char *begin, *end; + + begin = *stringp; + if (begin == NULL) + return NULL; + + /* A frequent case is when the delimiter string contains only one + character. Here we don't need to call the expensive 'strpbrk' + function and instead work using 'strchr'. */ + if (delim[0] == '\0' || delim[1] == '\0') + { + char ch = delim[0]; + + if (ch == '\0') + end = NULL; + else + { + if (*begin == ch) + end = begin; + else if (*begin == '\0') + end = NULL; + else + end = strchr (begin + 1, ch); + } + } + else + /* Find the end of the token. */ + end = strpbrk (begin, delim); + + if (end) + { + /* Terminate the token and set *STRINGP past NUL character. */ + *end++ = '\0'; + *stringp = end; + } + else + /* No more delimiters; this is the last token. */ + *stringp = NULL; + + return begin; +} +#endif /*HAVE_STRSEP*/ + + +#ifndef HAVE_STRLWR +char * +strlwr(char *s) +{ + char *p; + for(p=s; *p; p++ ) + *p = tolower(*p); + return s; +} +#endif + + +#ifndef HAVE_STRCASECMP +int +strcasecmp( const char *a, const char *b ) +{ + for( ; *a && *b; a++, b++ ) { + if( *a != *b && toupper(*a) != toupper(*b) ) + break; + } + return *(const byte*)a - *(const byte*)b; +} +#endif + + +/**************** + * mingw32/cpd has a memicmp() + */ +#ifndef HAVE_MEMICMP +int +memicmp( const char *a, const char *b, size_t n ) +{ + for( ; n; n--, a++, b++ ) + if( *a != *b && toupper(*(const byte*)a) != toupper(*(const byte*)b) ) + return *(const byte *)a - *(const byte*)b; + return 0; +} +#endif + + +#ifndef HAVE_MEMRCHR +void * +memrchr (const void *buffer, int c, size_t n) +{ + const unsigned char *p = buffer; + + for (p += n; n ; n--) + if (*--p == c) + return (void *)p; + return NULL; +} +#endif /*HAVE_MEMRCHR*/ + + +/* Percent-escape the string STR by replacing colons with '%3a'. If + EXTRA is not NULL all characters in EXTRA are also escaped. */ +static char * +do_percent_escape (const char *str, const char *extra, int die) +{ + int i, j; + char *ptr; + + if (!str) + return NULL; + + for (i=j=0; str[i]; i++) + if (str[i] == ':' || str[i] == '%' || str[i] == '\n' + || (extra && strchr (extra, str[i]))) + j++; + if (die) + ptr = xmalloc (i + 2 * j + 1); + else + { + ptr = xtrymalloc (i + 2 * j + 1); + if (!ptr) + return NULL; + } + i = 0; + while (*str) + { + if (*str == ':') + { + ptr[i++] = '%'; + ptr[i++] = '3'; + ptr[i++] = 'a'; + } + else if (*str == '%') + { + ptr[i++] = '%'; + ptr[i++] = '2'; + ptr[i++] = '5'; + } + else if (*str == '\n') + { + /* The newline is problematic in a line-based format. */ + ptr[i++] = '%'; + ptr[i++] = '0'; + ptr[i++] = 'a'; + } + else if (extra && strchr (extra, *str)) + { + ptr[i++] = '%'; + ptr[i++] = tohex_lower ((*str>>4)&15); + ptr[i++] = tohex_lower (*str&15); + } + else + ptr[i++] = *str; + str++; + } + ptr[i] = '\0'; + + return ptr; +} + +/* Percent-escape the string STR by replacing colons with '%3a'. If + EXTRA is not NULL all characters in EXTRA are also escaped. This + function terminates the process on memory shortage. */ +char * +percent_escape (const char *str, const char *extra) +{ + return do_percent_escape (str, extra, 1); +} + +/* Same as percent_escape but return NULL instead of exiting on memory + error. */ +char * +try_percent_escape (const char *str, const char *extra) +{ + return do_percent_escape (str, extra, 0); +} + + +/* Same as strconcat but takes a va_list. Returns EINVAL if the list + * is too long, all other errors are due to an ENOMEM condition. */ +char * +vstrconcat (const char *s1, va_list arg_ptr) +{ + const char *argv[48]; + size_t argc; + size_t needed; + char *buffer, *p; + + argc = 0; + argv[argc++] = s1; + needed = strlen (s1); + while (((argv[argc] = va_arg (arg_ptr, const char *)))) + { + needed += strlen (argv[argc]); + if (argc >= DIM (argv)-1) + { + gpg_err_set_errno (EINVAL); + return NULL; + } + argc++; + } + needed++; + buffer = xtrymalloc (needed); + if (buffer) + { + for (p = buffer, argc=0; argv[argc]; argc++) + p = stpcpy (p, argv[argc]); + } + return buffer; +} + + +/* Concatenate the string S1 with all the following strings up to a + NULL. Returns a malloced buffer with the new string or NULL on a + malloc error or if too many arguments are given. */ +char * +strconcat (const char *s1, ...) +{ + va_list arg_ptr; + char *result; + + if (!s1) + result = xtrystrdup (""); + else + { + va_start (arg_ptr, s1); + result = vstrconcat (s1, arg_ptr); + va_end (arg_ptr); + } + return result; +} + +/* Same as strconcat but terminate the process with an error message + if something goes wrong. */ +char * +xstrconcat (const char *s1, ...) +{ + va_list arg_ptr; + char *result; + + if (!s1) + result = xstrdup (""); + else + { + va_start (arg_ptr, s1); + result = vstrconcat (s1, arg_ptr); + va_end (arg_ptr); + } + if (!result) + { + if (errno == EINVAL) + fputs ("\nfatal: too many args for xstrconcat\n", stderr); + else + fputs ("\nfatal: out of memory\n", stderr); + exit (2); + } + return result; +} + +/* Split a string into fields at DELIM. REPLACEMENT is the character + to replace the delimiter with (normally: '\0' so that each field is + NUL terminated). The caller is responsible for freeing the result. + Note: this function modifies STRING! If you need the original + value, then you should pass a copy to this function. + + If malloc fails, this function returns NULL. */ +char ** +strsplit (char *string, char delim, char replacement, int *count) +{ + int fields = 1; + char *t; + char **result; + + /* First, count the number of fields. */ + for (t = strchr (string, delim); t; t = strchr (t + 1, delim)) + fields ++; + + result = xtrycalloc ((fields + 1), sizeof (*result)); + if (! result) + return NULL; + + result[0] = string; + fields = 1; + for (t = strchr (string, delim); t; t = strchr (t + 1, delim)) + { + result[fields ++] = t + 1; + *t = replacement; + } + + if (count) + *count = fields; + + return result; +} + + +/* Tokenize STRING using the set of delimiters in DELIM. Leading + * spaces and tabs are removed from all tokens. The caller must xfree + * the result. + * + * Returns: A malloced and NULL delimited array with the tokens. On + * memory error NULL is returned and ERRNO is set. + */ +static char ** +do_strtokenize (const char *string, const char *delim, int trim) +{ + const char *s; + size_t fields; + size_t bytes, n; + char *buffer; + char *p, *px, *pend; + char **result; + + /* Count the number of fields. */ + for (fields = 1, s = strpbrk (string, delim); s; s = strpbrk (s + 1, delim)) + fields++; + fields++; /* Add one for the terminating NULL. */ + + /* Allocate an array for all fields, a terminating NULL, and space + for a copy of the string. */ + bytes = fields * sizeof *result; + if (bytes / sizeof *result != fields) + { + gpg_err_set_errno (ENOMEM); + return NULL; + } + n = strlen (string) + 1; + bytes += n; + if (bytes < n) + { + gpg_err_set_errno (ENOMEM); + return NULL; + } + result = xtrymalloc (bytes); + if (!result) + return NULL; + buffer = (char*)(result + fields); + + /* Copy and parse the string. */ + strcpy (buffer, string); + for (n = 0, p = buffer; (pend = strpbrk (p, delim)); p = pend + 1) + { + *pend = 0; + if (trim) + { + while (spacep (p)) + p++; + for (px = pend - 1; px >= p && spacep (px); px--) + *px = 0; + } + result[n++] = p; + } + if (trim) + { + while (spacep (p)) + p++; + for (px = p + strlen (p) - 1; px >= p && spacep (px); px--) + *px = 0; + } + result[n++] = p; + result[n] = NULL; + + log_assert ((char*)(result + n + 1) == buffer); + + return result; +} + +/* Tokenize STRING using the set of delimiters in DELIM. Leading + * spaces and tabs are removed from all tokens. The caller must xfree + * the result. + * + * Returns: A malloced and NULL delimited array with the tokens. On + * memory error NULL is returned and ERRNO is set. + */ +char ** +strtokenize (const char *string, const char *delim) +{ + return do_strtokenize (string, delim, 1); +} + +/* Same as strtokenize but does not trim leading and trailing spaces + * from the fields. */ +char ** +strtokenize_nt (const char *string, const char *delim) +{ + return do_strtokenize (string, delim, 0); +} + + +/* Split a string into space delimited fields and remove leading and + * trailing spaces from each field. A pointer to each field is stored + * in ARRAY. Stop splitting at ARRAYSIZE fields. The function + * modifies STRING. The number of parsed fields is returned. + * Example: + * + * char *fields[2]; + * if (split_fields (string, fields, DIM (fields)) < 2) + * return // Not enough args. + * foo (fields[0]); + * foo (fields[1]); + */ +int +split_fields (char *string, char **array, int arraysize) +{ + int n = 0; + char *p, *pend; + + for (p = string; *p == ' '; p++) + ; + do + { + if (n == arraysize) + break; + array[n++] = p; + pend = strchr (p, ' '); + if (!pend) + break; + *pend++ = 0; + for (p = pend; *p == ' '; p++) + ; + } + while (*p); + + return n; +} + + +/* Split a string into colon delimited fields A pointer to each field + * is stored in ARRAY. Stop splitting at ARRAYSIZE fields. The + * function modifies STRING. The number of parsed fields is returned. + * Note that leading and trailing spaces are not removed from the fields. + * Example: + * + * char *fields[2]; + * if (split_fields (string, fields, DIM (fields)) < 2) + * return // Not enough args. + * foo (fields[0]); + * foo (fields[1]); + */ +int +split_fields_colon (char *string, char **array, int arraysize) +{ + int n = 0; + char *p, *pend; + + p = string; + do + { + if (n == arraysize) + break; + array[n++] = p; + pend = strchr (p, ':'); + if (!pend) + break; + *pend++ = 0; + p = pend; + } + while (*p); + + return n; +} + + + +/* Version number parsing. */ + +/* This function parses the first portion of the version number S and + stores it in *NUMBER. On success, this function returns a pointer + into S starting with the first character, which is not part of the + initial number portion; on failure, NULL is returned. */ +static const char* +parse_version_number (const char *s, int *number) +{ + int val = 0; + + if (*s == '0' && digitp (s+1)) + return NULL; /* Leading zeros are not allowed. */ + for (; digitp (s); s++) + { + val *= 10; + val += *s - '0'; + } + *number = val; + return val < 0 ? NULL : s; +} + + +/* This function breaks up the complete string-representation of the + version number S, which is of the following struture: <major + number>.<minor number>[.<micro number>]<patch level>. The major, + minor, and micro number components will be stored in *MAJOR, *MINOR + and *MICRO. If MICRO is not given 0 is used instead. + + On success, the last component, the patch level, will be returned; + in failure, NULL will be returned. */ +static const char * +parse_version_string (const char *s, int *major, int *minor, int *micro) +{ + s = parse_version_number (s, major); + if (!s || *s != '.') + return NULL; + s++; + s = parse_version_number (s, minor); + if (!s) + return NULL; + if (*s == '.') + { + s++; + s = parse_version_number (s, micro); + if (!s) + return NULL; + } + else + *micro = 0; + return s; /* Patchlevel. */ +} + + +/* Compare the version string MY_VERSION to the version string + * REQ_VERSION. Returns -1, 0, or 1 if MY_VERSION is found, + * respectively, to be less than, to match, or be greater than + * REQ_VERSION. This function works for three and two part version + * strings; for a two part version string the micro part is assumed to + * be 0. Patch levels are compared as strings. If a version number + * is invalid INT_MIN is returned. If REQ_VERSION is given as NULL + * the function returns 0 if MY_VERSION is parsable version string. */ +int +compare_version_strings (const char *my_version, const char *req_version) +{ + int my_major, my_minor, my_micro; + int rq_major, rq_minor, rq_micro; + const char *my_patch, *rq_patch; + int result; + + if (!my_version) + return INT_MIN; + + my_patch = parse_version_string (my_version, &my_major, &my_minor, &my_micro); + if (!my_patch) + return INT_MIN; + if (!req_version) + return 0; /* MY_VERSION can be parsed. */ + rq_patch = parse_version_string (req_version, &rq_major, &rq_minor,&rq_micro); + if (!rq_patch) + return INT_MIN; + + if (my_major == rq_major) + { + if (my_minor == rq_minor) + { + if (my_micro == rq_micro) + result = strcmp (my_patch, rq_patch); + else + result = my_micro - rq_micro; + } + else + result = my_minor - rq_minor; + } + else + result = my_major - rq_major; + + return !result? 0 : result < 0 ? -1 : 1; +} + + + +/* Format a string so that it fits within about TARGET_COLS columns. + * TEXT_IN is copied to a new buffer, which is returned. Normally, + * target_cols will be 72 and max_cols is 80. On error NULL is + * returned and ERRNO is set. */ +char * +format_text (const char *text_in, int target_cols, int max_cols) +{ + /* const int do_debug = 0; */ + + /* The character under consideration. */ + char *p; + /* The start of the current line. */ + char *line; + /* The last space that we saw. */ + char *last_space = NULL; + int last_space_cols = 0; + int copied_last_space = 0; + char *text; + + text = xtrystrdup (text_in); + if (!text) + return NULL; + + p = line = text; + while (1) + { + /* The number of columns including any trailing space. */ + int cols; + + p = p + strcspn (p, "\n "); + if (! p) + /* P now points to the NUL character. */ + p = &text[strlen (text)]; + + if (*p == '\n') + /* Pass through any newlines. */ + { + p ++; + line = p; + last_space = NULL; + last_space_cols = 0; + copied_last_space = 1; + continue; + } + + /* Have a space or a NUL. Note: we don't count the trailing + space. */ + cols = utf8_charcount (line, (uintptr_t) p - (uintptr_t) line); + if (cols < target_cols) + { + if (! *p) + /* Nothing left to break. */ + break; + + last_space = p; + last_space_cols = cols; + p ++; + /* Skip any immediately following spaces. If we break: + "... foo bar ..." between "foo" and "bar" then we want: + "... foo\nbar ...", which means that the left space has + to be the first space after foo, not the last space + before bar. */ + while (*p == ' ') + p ++; + } + else + { + int cols_with_left_space; + int cols_with_right_space; + int left_penalty; + int right_penalty; + + cols_with_left_space = last_space_cols; + cols_with_right_space = cols; + + /* if (do_debug) */ + /* log_debug ("Breaking: '%.*s'\n", */ + /* (int) ((uintptr_t) p - (uintptr_t) line), line); */ + + /* The number of columns away from TARGET_COLS. We prefer + to underflow than to overflow. */ + left_penalty = target_cols - cols_with_left_space; + right_penalty = 2 * (cols_with_right_space - target_cols); + + if (cols_with_right_space > max_cols) + /* Add a large penalty for each column that exceeds + max_cols. */ + right_penalty += 4 * (cols_with_right_space - max_cols); + + /* if (do_debug) */ + /* log_debug ("Left space => %d cols (penalty: %d); " */ + /* "right space => %d cols (penalty: %d)\n", */ + /* cols_with_left_space, left_penalty, */ + /* cols_with_right_space, right_penalty); */ + if (last_space_cols && left_penalty <= right_penalty) + { + /* Prefer the left space. */ + /* if (do_debug) */ + /* log_debug ("Breaking at left space.\n"); */ + p = last_space; + } + else + { + /* if (do_debug) */ + /* log_debug ("Breaking at right space.\n"); */ + } + + if (! *p) + break; + + *p = '\n'; + p ++; + if (*p == ' ') + { + int spaces; + for (spaces = 1; p[spaces] == ' '; spaces ++) + ; + memmove (p, &p[spaces], strlen (&p[spaces]) + 1); + } + line = p; + last_space = NULL; + last_space_cols = 0; + copied_last_space = 0; + } + } + + /* Chop off any trailing space. */ + trim_trailing_chars (text, strlen (text), " "); + /* If we inserted the trailing newline, then remove it. */ + if (! copied_last_space && *text && text[strlen (text) - 1] == '\n') + text[strlen (text) - 1] = '\0'; + + return text; +} + + +/* Substitute environment variables in STRING and return a new string. + * On error the function returns NULL. */ +char * +substitute_envvars (const char *string) +{ + char *line, *p, *pend; + const char *value; + size_t valuelen, n; + char *result = NULL; + + result = line = xtrystrdup (string); + if (!result) + return NULL; /* Ooops */ + + while (*line) + { + p = strchr (line, '$'); + if (!p) + goto leave; /* No or no more variables. */ + + if (p[1] == '$') /* Escaped dollar sign. */ + { + memmove (p, p+1, strlen (p+1)+1); + line = p + 1; + continue; + } + + if (p[1] == '{') + { + int count = 0; + + for (pend=p+2; *pend; pend++) + { + if (*pend == '{') + count++; + else if (*pend == '}') + { + if (--count < 0) + break; + } + } + if (!*pend) + goto leave; /* Unclosed - don't substitute. */ + } + else + { + for (pend = p+1; *pend && (alnump (pend) || *pend == '_'); pend++) + ; + } + + if (p[1] == '{' && *pend == '}') + { + int save = *pend; + *pend = 0; + value = getenv (p+2); + *pend++ = save; + } + else + { + int save = *pend; + *pend = 0; + value = getenv (p+1); + *pend = save; + } + + if (!value) + value = ""; + valuelen = strlen (value); + if (valuelen <= pend - p) + { + memcpy (p, value, valuelen); + p += valuelen; + n = pend - p; + if (n) + memmove (p, p+n, strlen (p+n)+1); + line = p; + } + else + { + char *src = result; + char *dst; + + dst = xtrymalloc (strlen (src) + valuelen + 1); + if (!dst) + { + xfree (result); + return NULL; + } + n = p - src; + memcpy (dst, src, n); + memcpy (dst + n, value, valuelen); + n += valuelen; + strcpy (dst + n, pend); + line = dst + n; + xfree (result); + result = dst; + } + } + + leave: + return result; +} diff --git a/common/stringhelp.h b/common/stringhelp.h new file mode 100644 index 0000000..84215c7 --- /dev/null +++ b/common/stringhelp.h @@ -0,0 +1,181 @@ +/* stringhelp.h + * Copyright (C) 1998, 1999, 2000, 2001, 2003, + * 2006, 2007, 2009 Free Software Foundation, Inc. + * 2015 g10 Code GmbH + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute and/or modify this + * part of GnuPG under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * GnuPG is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copies of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef GNUPG_COMMON_STRINGHELP_H +#define GNUPG_COMMON_STRINGHELP_H + +#include <stdint.h> +#include "types.h" + +/*-- stringhelp.c --*/ +char *has_leading_keyword (const char *string, const char *keyword); + +const char *memistr (const void *buf, size_t buflen, const char *sub); +char *mem2str( char *, const void *, size_t); +char *trim_spaces( char *string ); +char *ascii_trim_spaces (char *string); +char *trim_trailing_spaces( char *string ); +unsigned int trim_trailing_chars( unsigned char *line, unsigned len, + const char *trimchars); +unsigned int trim_trailing_ws( unsigned char *line, unsigned len ); +size_t length_sans_trailing_chars (const unsigned char *line, size_t len, + const char *trimchars ); +size_t length_sans_trailing_ws (const unsigned char *line, size_t len); + + +char *make_basename(const char *filepath, const char *inputpath); +char *make_dirname(const char *filepath); +char *make_filename( const char *first_part, ... ) GPGRT_ATTR_SENTINEL(0); +char *make_filename_try (const char *first_part, ... ) GPGRT_ATTR_SENTINEL(0); +char *make_absfilename (const char *first_part, ...) GPGRT_ATTR_SENTINEL(0); +char *make_absfilename_try (const char *first_part, + ...) GPGRT_ATTR_SENTINEL(0); +int compare_filenames( const char *a, const char *b ); + +uint64_t string_to_u64 (const char *string); +int hextobyte (const char *s); + +size_t utf8_charcount (const char *s, int len); + + +#ifdef HAVE_W32_SYSTEM +const char *w32_strerror (int ec); +#endif + + +int ascii_isupper (int c); +int ascii_islower (int c); +int ascii_toupper (int c); +int ascii_tolower (int c); +char *ascii_strlwr (char *s); +char *ascii_strupr (char *s); +int ascii_strcasecmp( const char *a, const char *b ); +int ascii_strncasecmp (const char *a, const char *b, size_t n); +int ascii_memcasecmp( const void *a, const void *b, size_t n ); +const char *ascii_memistr ( const void *buf, size_t buflen, const char *sub); +void *ascii_memcasemem (const void *haystack, size_t nhaystack, + const void *needle, size_t nneedle); + + +#ifndef HAVE_MEMICMP +int memicmp( const char *a, const char *b, size_t n ); +#endif +#ifndef HAVE_STPCPY +char *stpcpy(char *a,const char *b); +#endif +#ifndef HAVE_STRPBRK +char *strpbrk (const char *s, const char *accept); +#endif +#ifndef HAVE_STRSEP +char *strsep (char **stringp, const char *delim); +#endif +#ifndef HAVE_STRLWR +char *strlwr(char *a); +#endif +#ifndef HAVE_STRTOUL +# define strtoul(a,b,c) ((unsigned long)strtol((a),(b),(c))) +#endif +#ifndef HAVE_MEMMOVE +# define memmove(d, s, n) bcopy((s), (d), (n)) +#endif +#ifndef HAVE_STRICMP +# define stricmp(a,b) strcasecmp( (a), (b) ) +#endif +#ifndef HAVE_MEMRCHR +void *memrchr (const void *buffer, int c, size_t n); +#endif + + +#ifndef HAVE_ISASCII +static inline int +isascii (int c) +{ + return (((c) & ~0x7f) == 0); +} +#endif /* !HAVE_ISASCII */ + + +#ifndef STR +# define STR(v) #v +#endif +#define STR2(v) STR(v) + +/* Percent-escape the string STR by replacing colons with '%3a'. If + EXTRA is not NULL, also replace all characters given in EXTRA. The + "try_" variant fails with NULL if not enough memory can be + allocated. */ +char *percent_escape (const char *str, const char *extra); +char *try_percent_escape (const char *str, const char *extra); + + +/* Concatenate the string S1 with all the following strings up to a + NULL. Returns a malloced buffer with the new string or NULL on a + malloc error or if too many arguments are given. */ +char *strconcat (const char *s1, ...) GPGRT_ATTR_SENTINEL(0); +/* Same but taking a va_list. */ +char *vstrconcat (const char *s1, va_list arg_ptr); +/* Ditto, but die on error. */ +char *xstrconcat (const char *s1, ...) GPGRT_ATTR_SENTINEL(0); + + +char **strsplit (char *string, char delim, char replacement, int *count); + +/* Tokenize STRING using the set of delimiters in DELIM. */ +char **strtokenize (const char *string, const char *delim); +/* Tokenize STRING using the set of delimiters in DELIM but do not + * trim the tokens. */ +char **strtokenize_nt (const char *string, const char *delim); + +/* Split STRING into space delimited fields and store them in the + * provided ARRAY. */ +int split_fields (char *string, char **array, int arraysize); + +/* Split STRING into colon delimited fields and store them in the + * provided ARRAY. */ +int split_fields_colon (char *string, char **array, int arraysize); + +/* Return True if MYVERSION is greater or equal than REQ_VERSION. */ +int compare_version_strings (const char *my_version, const char *req_version); + +/* Format a string so that it fits within about TARGET_COLS columns. */ +char *format_text (const char *text, int target_cols, int max_cols); + +/* Substitute environmen variabales in STRING. */ +char *substitute_envvars (const char *string); + + +/*-- mapstrings.c --*/ +const char *map_static_macro_string (const char *string); +const char *map_static_strings (const char *domain, int key1, int key2, + const char *string1, ...); + +#endif /*GNUPG_COMMON_STRINGHELP_H*/ diff --git a/common/strlist.c b/common/strlist.c new file mode 100644 index 0000000..6feb3a4 --- /dev/null +++ b/common/strlist.c @@ -0,0 +1,281 @@ +/* strlist.c - string helpers + * Copyright (C) 1998, 2000, 2001, 2006 Free Software Foundation, Inc. + * Copyright (C) 2015 g10 Code GmbH + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute and/or modify this + * part of GnuPG under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * GnuPG is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copies of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include <ctype.h> + +#include "util.h" +#include "common-defs.h" +#include "strlist.h" +#include "utf8conv.h" +#include "mischelp.h" + +void +free_strlist( strlist_t sl ) +{ + strlist_t sl2; + + for(; sl; sl = sl2 ) { + sl2 = sl->next; + xfree(sl); + } +} + + +void +free_strlist_wipe (strlist_t sl) +{ + strlist_t sl2; + + for(; sl; sl = sl2 ) { + sl2 = sl->next; + wipememory (sl, sizeof *sl + strlen (sl->d)); + xfree(sl); + } +} + + +/* Add STRING to the LIST at the front. This function terminates the + process on memory shortage. */ +strlist_t +add_to_strlist( strlist_t *list, const char *string ) +{ + strlist_t sl; + + sl = xmalloc( sizeof *sl + strlen(string)); + sl->flags = 0; + strcpy(sl->d, string); + sl->next = *list; + *list = sl; + return sl; +} + + +/* Add STRING to the LIST at the front. This function returns NULL + and sets ERRNO on memory shortage. */ +strlist_t +add_to_strlist_try (strlist_t *list, const char *string) +{ + strlist_t sl; + + sl = xtrymalloc (sizeof *sl + strlen (string)); + if (sl) + { + sl->flags = 0; + strcpy (sl->d, string); + sl->next = *list; + *list = sl; + } + return sl; +} + + +/* Same as add_to_strlist() but if IS_UTF8 is *not* set, a conversion + to UTF-8 is done. This function terminates the process on memory + shortage. */ +strlist_t +add_to_strlist2( strlist_t *list, const char *string, int is_utf8 ) +{ + strlist_t sl; + + if (is_utf8) + sl = add_to_strlist( list, string ); + else + { + char *p = native_to_utf8( string ); + sl = add_to_strlist( list, p ); + xfree ( p ); + } + return sl; +} + + +/* Add STRING to the LIST at the end. This function terminates the + process on memory shortage. */ +strlist_t +append_to_strlist( strlist_t *list, const char *string ) +{ + strlist_t sl; + sl = append_to_strlist_try (list, string); + if (!sl) + xoutofcore (); + return sl; +} + + +/* Add STRING to the LIST at the end. */ +strlist_t +append_to_strlist_try (strlist_t *list, const char *string) +{ + strlist_t r, sl; + + sl = xtrymalloc( sizeof *sl + strlen(string)); + if (sl == NULL) + return NULL; + + sl->flags = 0; + strcpy(sl->d, string); + sl->next = NULL; + if( !*list ) + *list = sl; + else { + for( r = *list; r->next; r = r->next ) + ; + r->next = sl; + } + return sl; +} + + +strlist_t +append_to_strlist2( strlist_t *list, const char *string, int is_utf8 ) +{ + strlist_t sl; + + if( is_utf8 ) + sl = append_to_strlist( list, string ); + else + { + char *p = native_to_utf8 (string); + sl = append_to_strlist( list, p ); + xfree( p ); + } + return sl; +} + + +/* Return a copy of LIST. This function terminates the process on + memory shortage.*/ +strlist_t +strlist_copy (strlist_t list) +{ + strlist_t newlist = NULL, sl, *last; + + last = &newlist; + for (; list; list = list->next) + { + sl = xmalloc (sizeof *sl + strlen (list->d)); + sl->flags = list->flags; + strcpy(sl->d, list->d); + sl->next = NULL; + *last = sl; + last = &sl; + } + return newlist; +} + + + +strlist_t +strlist_prev( strlist_t head, strlist_t node ) +{ + strlist_t n; + + for(n=NULL; head && head != node; head = head->next ) + n = head; + return n; +} + +strlist_t +strlist_last( strlist_t node ) +{ + if( node ) + for( ; node->next ; node = node->next ) + ; + return node; +} + + +/* Remove the first item from LIST and return its content in an + allocated buffer. This function terminates the process on memory + shortage. */ +char * +strlist_pop (strlist_t *list) +{ + char *str=NULL; + strlist_t sl=*list; + + if(sl) + { + str = xmalloc(strlen(sl->d)+1); + strcpy(str,sl->d); + + *list=sl->next; + xfree(sl); + } + + return str; +} + +/* Return the first element of the string list HAYSTACK whose string + matches NEEDLE. If no elements match, return NULL. */ +strlist_t +strlist_find (strlist_t haystack, const char *needle) +{ + for (; + haystack; + haystack = haystack->next) + if (strcmp (haystack->d, needle) == 0) + return haystack; + return NULL; +} + +int +strlist_length (strlist_t list) +{ + int i; + for (i = 0; list; list = list->next) + i ++; + + return i; +} + +/* Reverse the list *LIST in place. */ +strlist_t +strlist_rev (strlist_t *list) +{ + strlist_t l = *list; + strlist_t lrev = NULL; + + while (l) + { + strlist_t tail = l->next; + l->next = lrev; + lrev = l; + l = tail; + } + + *list = lrev; + return lrev; +} diff --git a/common/strlist.h b/common/strlist.h new file mode 100644 index 0000000..641ea06 --- /dev/null +++ b/common/strlist.h @@ -0,0 +1,69 @@ +/* strlist.h + * Copyright (C) 1998, 2000, 2001, 2006 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute and/or modify this + * part of GnuPG under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * GnuPG is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copies of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef GNUPG_COMMON_STRLIST_H +#define GNUPG_COMMON_STRLIST_H + +struct string_list +{ + struct string_list *next; + unsigned int flags; + char d[1]; +}; +typedef struct string_list *strlist_t; + +void free_strlist (strlist_t sl); +void free_strlist_wipe (strlist_t sl); + +strlist_t add_to_strlist (strlist_t *list, const char *string); +strlist_t add_to_strlist_try (strlist_t *list, const char *string); + +strlist_t add_to_strlist2( strlist_t *list, const char *string, int is_utf8); + +strlist_t append_to_strlist (strlist_t *list, const char *string); +strlist_t append_to_strlist_try (strlist_t *list, const char *string); +strlist_t append_to_strlist2 (strlist_t *list, const char *string, + int is_utf8); + +strlist_t strlist_copy (strlist_t list); + +strlist_t strlist_prev (strlist_t head, strlist_t node); +strlist_t strlist_last (strlist_t node); +char * strlist_pop (strlist_t *list); + +strlist_t strlist_find (strlist_t haystack, const char *needle); +int strlist_length (strlist_t list); + +strlist_t strlist_rev (strlist_t *haystack); + +#define FREE_STRLIST(a) do { free_strlist((a)); (a) = NULL ; } while(0) + + +#endif /*GNUPG_COMMON_STRLIST_H*/ diff --git a/common/sysutils.c b/common/sysutils.c new file mode 100644 index 0000000..5f54ae1 --- /dev/null +++ b/common/sysutils.c @@ -0,0 +1,1951 @@ +/* sysutils.c - system helpers + * Copyright (C) 1991-2001, 2003-2004, + * 2006-2008 Free Software Foundation, Inc. + * Copyright (C) 2013-2016 Werner Koch + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#ifdef WITHOUT_NPTH /* Give the Makefile a chance to build without Pth. */ +# undef HAVE_NPTH +# undef USE_NPTH +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#ifdef HAVE_PWD_H +# include <pwd.h> +#endif +#include <unistd.h> +#include <errno.h> +#ifdef HAVE_STAT +# include <sys/stat.h> +#endif +#if defined(__linux__) && defined(__alpha__) && __GLIBC__ < 2 +# include <asm/sysinfo.h> +# include <asm/unistd.h> +#endif +#include <time.h> +#ifdef HAVE_SETRLIMIT +# include <sys/time.h> +# include <sys/resource.h> +#endif +#ifdef HAVE_W32_SYSTEM +# if WINVER < 0x0500 +# define WINVER 0x0500 /* Required for AllowSetForegroundWindow. */ +# endif +# ifdef HAVE_WINSOCK2_H +# include <winsock2.h> +# endif +# include <windows.h> +#else /*!HAVE_W32_SYSTEM*/ +# include <sys/socket.h> +# include <sys/un.h> +#endif +#ifdef HAVE_INOTIFY_INIT +# include <sys/inotify.h> +#endif /*HAVE_INOTIFY_INIT*/ +#ifdef HAVE_NPTH +# include <npth.h> +#endif +#include <fcntl.h> +#include <dirent.h> + +#include <assuan.h> + +#include "util.h" +#include "i18n.h" + +#include "sysutils.h" + +#define tohex(n) ((n) < 10 ? ((n) + '0') : (((n) - 10) + 'A')) + + +/* The object used with our opendir functions. We need to define our + * own so that we can properly handle Unicode on Windows. */ +struct gnupg_dir_s +{ +#ifdef HAVE_W32_SYSTEM + _WDIR *dir; /* The system's DIR pointer. */ +#else + DIR *dir; /* The system's DIR pointer. */ +#endif + struct gnupg_dirent_s dirent; /* The current dirent. */ + size_t namesize; /* If not 0 the allocated size of dirent.d_name. */ + char name[256]; /* Only used if NAMESIZE is 0. */ +}; + + +/* Flag to tell whether special file names are enabled. See gpg.c for + * an explanation of these file names. */ +static int allow_special_filenames; + + +static GPGRT_INLINE gpg_error_t +my_error_from_syserror (void) +{ + return gpg_err_make (default_errsource, gpg_err_code_from_syserror ()); +} + +static GPGRT_INLINE gpg_error_t +my_error (int e) +{ + return gpg_err_make (default_errsource, (e)); +} + + + +#if defined(__linux__) && defined(__alpha__) && __GLIBC__ < 2 +#warning using trap_unaligned +static int +setsysinfo(unsigned long op, void *buffer, unsigned long size, + int *start, void *arg, unsigned long flag) +{ + return syscall(__NR_osf_setsysinfo, op, buffer, size, start, arg, flag); +} + +void +trap_unaligned(void) +{ + unsigned int buf[2]; + + buf[0] = SSIN_UACPROC; + buf[1] = UAC_SIGBUS | UAC_NOPRINT; + setsysinfo(SSI_NVPAIRS, buf, 1, 0, 0, 0); +} +#else +void +trap_unaligned(void) +{ /* dummy */ +} +#endif + + +int +disable_core_dumps (void) +{ +#ifdef HAVE_DOSISH_SYSTEM + return 0; +#else +# ifdef HAVE_SETRLIMIT + struct rlimit limit; + + /* We only set the current limit unless we were not able to + retrieve the old value. */ + if (getrlimit (RLIMIT_CORE, &limit)) + limit.rlim_max = 0; + limit.rlim_cur = 0; + if( !setrlimit (RLIMIT_CORE, &limit) ) + return 0; + if( errno != EINVAL && errno != ENOSYS ) + log_fatal (_("can't disable core dumps: %s\n"), strerror(errno) ); +#endif + return 1; +#endif +} + +int +enable_core_dumps (void) +{ +#ifdef HAVE_DOSISH_SYSTEM + return 0; +#else +# ifdef HAVE_SETRLIMIT + struct rlimit limit; + + if (getrlimit (RLIMIT_CORE, &limit)) + return 1; + limit.rlim_cur = limit.rlim_max; + setrlimit (RLIMIT_CORE, &limit); + return 1; /* We always return true because this function is + merely a debugging aid. */ +# endif + return 1; +#endif +} + +#ifdef HAVE_W32_SYSTEM +static int +any8bitchar (const char *string) +{ + if (string) + for ( ; *string; string++) + if ((*string & 0x80)) + return 1; + return 0; +} +#endif /*HAVE_W32_SYSTEM*/ + + +/* Helper for gnupg_w32_set_errno. */ +#ifdef HAVE_W32_SYSTEM +static int +map_w32_to_errno (DWORD w32_err) +{ + switch (w32_err) + { + case 0: + return 0; + + case ERROR_FILE_NOT_FOUND: + return ENOENT; + + case ERROR_PATH_NOT_FOUND: + return ENOENT; + + case ERROR_ACCESS_DENIED: + return EPERM; /* ReactOS uses EACCES ("Permission denied") and + * is likely right because they used an + * undocumented function to associate the error + * codes. However we have always used EPERM + * ("Operation not permitted", e.g. function is + * required to be called by root) and we better + * stick to that to avoid surprising bugs. */ + + case ERROR_INVALID_HANDLE: + return EBADF; + + case ERROR_INVALID_BLOCK: + return ENOMEM; + + case ERROR_NOT_ENOUGH_MEMORY: + return ENOMEM; + + case ERROR_NO_DATA: + return EPIPE; + + case ERROR_ALREADY_EXISTS: + return EEXIST; + + /* This mapping has been taken from reactOS. */ + case ERROR_TOO_MANY_OPEN_FILES: return EMFILE; + case ERROR_ARENA_TRASHED: return ENOMEM; + case ERROR_BAD_ENVIRONMENT: return E2BIG; + case ERROR_BAD_FORMAT: return ENOEXEC; + case ERROR_INVALID_DRIVE: return ENOENT; + case ERROR_CURRENT_DIRECTORY: return EACCES; + case ERROR_NOT_SAME_DEVICE: return EXDEV; + case ERROR_NO_MORE_FILES: return ENOENT; + case ERROR_WRITE_PROTECT: return EACCES; + case ERROR_BAD_UNIT: return EACCES; + case ERROR_NOT_READY: return EACCES; + case ERROR_BAD_COMMAND: return EACCES; + case ERROR_CRC: return EACCES; + case ERROR_BAD_LENGTH: return EACCES; + case ERROR_SEEK: return EACCES; + case ERROR_NOT_DOS_DISK: return EACCES; + case ERROR_SECTOR_NOT_FOUND: return EACCES; + case ERROR_OUT_OF_PAPER: return EACCES; + case ERROR_WRITE_FAULT: return EACCES; + case ERROR_READ_FAULT: return EACCES; + case ERROR_GEN_FAILURE: return EACCES; + case ERROR_SHARING_VIOLATION: return EACCES; + case ERROR_LOCK_VIOLATION: return EACCES; + case ERROR_WRONG_DISK: return EACCES; + case ERROR_SHARING_BUFFER_EXCEEDED: return EACCES; + case ERROR_BAD_NETPATH: return ENOENT; + case ERROR_NETWORK_ACCESS_DENIED: return EACCES; + case ERROR_BAD_NET_NAME: return ENOENT; + case ERROR_FILE_EXISTS: return EEXIST; + case ERROR_CANNOT_MAKE: return EACCES; + case ERROR_FAIL_I24: return EACCES; + case ERROR_NO_PROC_SLOTS: return EAGAIN; + case ERROR_DRIVE_LOCKED: return EACCES; + case ERROR_BROKEN_PIPE: return EPIPE; + case ERROR_DISK_FULL: return ENOSPC; + case ERROR_INVALID_TARGET_HANDLE: return EBADF; + case ERROR_WAIT_NO_CHILDREN: return ECHILD; + case ERROR_CHILD_NOT_COMPLETE: return ECHILD; + case ERROR_DIRECT_ACCESS_HANDLE: return EBADF; + case ERROR_SEEK_ON_DEVICE: return EACCES; + case ERROR_DIR_NOT_EMPTY: return ENOTEMPTY; + case ERROR_NOT_LOCKED: return EACCES; + case ERROR_BAD_PATHNAME: return ENOENT; + case ERROR_MAX_THRDS_REACHED: return EAGAIN; + case ERROR_LOCK_FAILED: return EACCES; + case ERROR_INVALID_STARTING_CODESEG: return ENOEXEC; + case ERROR_INVALID_STACKSEG: return ENOEXEC; + case ERROR_INVALID_MODULETYPE: return ENOEXEC; + case ERROR_INVALID_EXE_SIGNATURE: return ENOEXEC; + case ERROR_EXE_MARKED_INVALID: return ENOEXEC; + case ERROR_BAD_EXE_FORMAT: return ENOEXEC; + case ERROR_ITERATED_DATA_EXCEEDS_64k: return ENOEXEC; + case ERROR_INVALID_MINALLOCSIZE: return ENOEXEC; + case ERROR_DYNLINK_FROM_INVALID_RING: return ENOEXEC; + case ERROR_IOPL_NOT_ENABLED: return ENOEXEC; + case ERROR_INVALID_SEGDPL: return ENOEXEC; + case ERROR_AUTODATASEG_EXCEEDS_64k: return ENOEXEC; + case ERROR_RING2SEG_MUST_BE_MOVABLE: return ENOEXEC; + case ERROR_RELOC_CHAIN_XEEDS_SEGLIM: return ENOEXEC; + case ERROR_INFLOOP_IN_RELOC_CHAIN: return ENOEXEC; + case ERROR_FILENAME_EXCED_RANGE: return ENOENT; + case ERROR_NESTING_NOT_ALLOWED: return EAGAIN; + case ERROR_NOT_ENOUGH_QUOTA: return ENOMEM; + + default: + return EIO; + } +} +#endif /*HAVE_W32_SYSTEM*/ + + +/* Set ERRNO from the Windows error. EC may be -1 to use the last error. */ +#ifdef HAVE_W32_SYSTEM +void +gnupg_w32_set_errno (int ec) +{ + /* FIXME: Replace by gpgrt_w32_set_errno. */ + if (ec == -1) + ec = GetLastError (); + _set_errno (map_w32_to_errno (ec)); +} +#endif /*HAVE_W32_SYSTEM*/ + + + +/* Allow the use of special "-&nnn" style file names. */ +void +enable_special_filenames (void) +{ + allow_special_filenames = 1; +} + + +/* Return a string which is used as a kind of process ID. */ +const byte * +get_session_marker (size_t *rlen) +{ + static byte marker[SIZEOF_UNSIGNED_LONG*2]; + static int initialized; + + if (!initialized) + { + gcry_create_nonce (marker, sizeof marker); + initialized = 1; + } + *rlen = sizeof (marker); + return marker; +} + +/* Return a random number in an unsigned int. */ +unsigned int +get_uint_nonce (void) +{ + unsigned int value; + + gcry_create_nonce (&value, sizeof value); + return value; +} + + + +#if 0 /* not yet needed - Note that this will require inclusion of + cmacros.am in Makefile.am */ +int +check_permissions(const char *path,int extension,int checkonly) +{ +#if defined(HAVE_STAT) && !defined(HAVE_DOSISH_SYSTEM) + char *tmppath; + struct stat statbuf; + int ret=1; + int isdir=0; + + if(opt.no_perm_warn) + return 0; + + if(extension && path[0]!=DIRSEP_C) + { + if(strchr(path,DIRSEP_C)) + tmppath=make_filename(path,NULL); + else + tmppath=make_filename(GNUPG_LIBDIR,path,NULL); + } + else + tmppath=m_strdup(path); + + /* It's okay if the file doesn't exist */ + if(stat(tmppath,&statbuf)!=0) + { + ret=0; + goto end; + } + + isdir=S_ISDIR(statbuf.st_mode); + + /* Per-user files must be owned by the user. Extensions must be + owned by the user or root. */ + if((!extension && statbuf.st_uid != getuid()) || + (extension && statbuf.st_uid!=0 && statbuf.st_uid!=getuid())) + { + if(!checkonly) + log_info(_("Warning: unsafe ownership on %s \"%s\"\n"), + isdir?"directory":extension?"extension":"file",path); + goto end; + } + + /* This works for both directories and files - basically, we don't + care what the owner permissions are, so long as the group and + other permissions are 0 for per-user files, and non-writable for + extensions. */ + if((extension && (statbuf.st_mode & (S_IWGRP|S_IWOTH)) !=0) || + (!extension && (statbuf.st_mode & (S_IRWXG|S_IRWXO)) != 0)) + { + char *dir; + + /* However, if the directory the directory/file is in is owned + by the user and is 700, then this is not a problem. + Theoretically, we could walk this test up to the root + directory /, but for the sake of sanity, I'm stopping at one + level down. */ + + dir= make_dirname (tmppath); + if(stat(dir,&statbuf)==0 && statbuf.st_uid==getuid() && + S_ISDIR(statbuf.st_mode) && (statbuf.st_mode & (S_IRWXG|S_IRWXO))==0) + { + xfree (dir); + ret=0; + goto end; + } + + m_free(dir); + + if(!checkonly) + log_info(_("Warning: unsafe permissions on %s \"%s\"\n"), + isdir?"directory":extension?"extension":"file",path); + goto end; + } + + ret=0; + + end: + m_free(tmppath); + + return ret; + +#endif /* HAVE_STAT && !HAVE_DOSISH_SYSTEM */ + + return 0; +} +#endif + + +/* Wrapper around the usual sleep function. This one won't wake up + before the sleep time has really elapsed. When build with Pth it + merely calls pth_sleep and thus suspends only the current + thread. */ +void +gnupg_sleep (unsigned int seconds) +{ +#ifdef USE_NPTH + npth_sleep (seconds); +#else + /* Fixme: make sure that a sleep won't wake up to early. */ +# ifdef HAVE_W32_SYSTEM + Sleep (seconds*1000); +# else + sleep (seconds); +# endif +#endif +} + + +/* Wrapper around the platforms usleep function. This one won't wake + * up before the sleep time has really elapsed. When build with nPth + * it merely calls npth_usleep and thus suspends only the current + * thread. */ +void +gnupg_usleep (unsigned int usecs) +{ +#if defined(USE_NPTH) + + npth_usleep (usecs); + +#elif defined(HAVE_W32_SYSTEM) + + Sleep ((usecs + 999) / 1000); + +#elif defined(HAVE_NANOSLEEP) + + if (usecs) + { + struct timespec req; + struct timespec rem; + + req.tv_sec = usecs / 1000000; + req.tv_nsec = (usecs % 1000000) * 1000; + while (nanosleep (&req, &rem) < 0 && errno == EINTR) + req = rem; + } + +#else /*Standard Unix*/ + + if (usecs) + { + struct timeval tv; + + tv.tv_sec = usecs / 1000000; + tv.tv_usec = usecs % 1000000; + select (0, NULL, NULL, NULL, &tv); + } + +#endif +} + + +/* This function is a NOP for POSIX systems but required under Windows + as the file handles as returned by OS calls (like CreateFile) are + different from the libc file descriptors (like open). This function + translates system file handles to libc file handles. FOR_WRITE + gives the direction of the handle. */ +int +translate_sys2libc_fd (gnupg_fd_t fd, int for_write) +{ +#if defined(HAVE_W32CE_SYSTEM) + (void)for_write; + return (int) fd; +#elif defined(HAVE_W32_SYSTEM) + int x; + + if (fd == GNUPG_INVALID_FD) + return -1; + + /* Note that _open_osfhandle is currently defined to take and return + a long. */ + x = _open_osfhandle ((long)fd, for_write ? 1 : 0); + if (x == -1) + log_error ("failed to translate osfhandle %p\n", (void *) fd); + return x; +#else /*!HAVE_W32_SYSTEM */ + (void)for_write; + return fd; +#endif +} + +/* This is the same as translate_sys2libc_fd but takes an integer + which is assumed to be such an system handle. On WindowsCE the + passed FD is a rendezvous ID and the function finishes the pipe + creation. */ +int +translate_sys2libc_fd_int (int fd, int for_write) +{ +#if HAVE_W32CE_SYSTEM + fd = (int) _assuan_w32ce_finish_pipe (fd, for_write); + return translate_sys2libc_fd ((void*)fd, for_write); +#elif HAVE_W32_SYSTEM + if (fd <= 2) + return fd; /* Do not do this for error, stdin, stdout, stderr. */ + + return translate_sys2libc_fd ((void*)fd, for_write); +#else + (void)for_write; + return fd; +#endif +} + + +/* Check whether FNAME has the form "-&nnnn", where N is a non-zero + * number. Returns this number or -1 if it is not the case. If the + * caller wants to use the file descriptor for writing FOR_WRITE shall + * be set to 1. If NOTRANSLATE is set the Windows specific mapping is + * not done. */ +int +check_special_filename (const char *fname, int for_write, int notranslate) +{ + if (allow_special_filenames + && fname && *fname == '-' && fname[1] == '&') + { + int i; + + fname += 2; + for (i=0; digitp (fname+i); i++ ) + ; + if (!fname[i]) + return notranslate? atoi (fname) + /**/ : translate_sys2libc_fd_int (atoi (fname), for_write); + } + return -1; +} + + +/* Replacement for tmpfile(). This is required because the tmpfile + function of Windows' runtime library is broken, insecure, ignores + TMPDIR and so on. In addition we create a file with an inheritable + handle. */ +FILE * +gnupg_tmpfile (void) +{ +#ifdef HAVE_W32_SYSTEM + int attempts, n; +#ifdef HAVE_W32CE_SYSTEM + wchar_t buffer[MAX_PATH+7+12+1]; +# define mystrlen(a) wcslen (a) + wchar_t *name, *p; +#else + char buffer[MAX_PATH+7+12+1]; +# define mystrlen(a) strlen (a) + char *name, *p; +#endif + HANDLE file; + int pid = GetCurrentProcessId (); + unsigned int value; + int i; + SECURITY_ATTRIBUTES sec_attr; + + memset (&sec_attr, 0, sizeof sec_attr ); + sec_attr.nLength = sizeof sec_attr; + sec_attr.bInheritHandle = TRUE; + + n = GetTempPath (MAX_PATH+1, buffer); + if (!n || n > MAX_PATH || mystrlen (buffer) > MAX_PATH) + { + gpg_err_set_errno (ENOENT); + return NULL; + } + p = buffer + mystrlen (buffer); +#ifdef HAVE_W32CE_SYSTEM + wcscpy (p, L"_gnupg"); + p += 7; +#else + p = stpcpy (p, "_gnupg"); +#endif + /* We try to create the directory but don't care about an error as + it may already exist and the CreateFile would throw an error + anyway. */ + CreateDirectory (buffer, NULL); + *p++ = '\\'; + name = p; + for (attempts=0; attempts < 10; attempts++) + { + p = name; + value = (GetTickCount () ^ ((pid<<16) & 0xffff0000)); + for (i=0; i < 8; i++) + { + *p++ = tohex (((value >> 28) & 0x0f)); + value <<= 4; + } +#ifdef HAVE_W32CE_SYSTEM + wcscpy (p, L".tmp"); +#else + strcpy (p, ".tmp"); +#endif + file = CreateFile (buffer, + GENERIC_READ | GENERIC_WRITE, + 0, + &sec_attr, + CREATE_NEW, + FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, + NULL); + if (file != INVALID_HANDLE_VALUE) + { + FILE *fp; +#ifdef HAVE_W32CE_SYSTEM + int fd = (int)file; + fp = _wfdopen (fd, L"w+b"); +#else + int fd = _open_osfhandle ((long)file, 0); + if (fd == -1) + { + CloseHandle (file); + return NULL; + } + fp = fdopen (fd, "w+b"); +#endif + if (!fp) + { + int save = errno; + close (fd); + gpg_err_set_errno (save); + return NULL; + } + return fp; + } + Sleep (1); /* One ms as this is the granularity of GetTickCount. */ + } + gpg_err_set_errno (ENOENT); + return NULL; +#undef mystrlen +#else /*!HAVE_W32_SYSTEM*/ + return tmpfile (); +#endif /*!HAVE_W32_SYSTEM*/ +} + + +/* Make sure that the standard file descriptors are opened. Obviously + some folks close them before an exec and the next file we open will + get one of them assigned and thus any output (i.e. diagnostics) end + up in that file (e.g. the trustdb). Not actually a gpg problem as + this will happen with almost all utilities when called in a wrong + way. However we try to minimize the damage here and raise + awareness of the problem. + + Must be called before we open any files! */ +void +gnupg_reopen_std (const char *pgmname) +{ +#ifdef F_GETFD + int did_stdin = 0; + int did_stdout = 0; + int did_stderr = 0; + FILE *complain; + + if (fcntl (STDIN_FILENO, F_GETFD) == -1 && errno ==EBADF) + { + if (open ("/dev/null",O_RDONLY) == STDIN_FILENO) + did_stdin = 1; + else + did_stdin = 2; + } + + if (fcntl (STDOUT_FILENO, F_GETFD) == -1 && errno == EBADF) + { + if (open ("/dev/null",O_WRONLY) == STDOUT_FILENO) + did_stdout = 1; + else + did_stdout = 2; + } + + if (fcntl (STDERR_FILENO, F_GETFD)==-1 && errno==EBADF) + { + if (open ("/dev/null", O_WRONLY) == STDERR_FILENO) + did_stderr = 1; + else + did_stderr = 2; + } + + /* It's hard to log this sort of thing since the filehandle we would + complain to may be closed... */ + if (!did_stderr) + complain = stderr; + else if (!did_stdout) + complain = stdout; + else + complain = NULL; + + if (complain) + { + if (did_stdin == 1) + fprintf (complain, "%s: WARNING: standard input reopened\n", pgmname); + if (did_stdout == 1) + fprintf (complain, "%s: WARNING: standard output reopened\n", pgmname); + if (did_stderr == 1) + fprintf (complain, "%s: WARNING: standard error reopened\n", pgmname); + + if (did_stdin == 2 || did_stdout == 2 || did_stderr == 2) + fprintf(complain,"%s: fatal: unable to reopen standard input," + " output, or error\n", pgmname); + } + + if (did_stdin == 2 || did_stdout == 2 || did_stderr == 2) + exit (3); +#else /* !F_GETFD */ + (void)pgmname; +#endif +} + + +/* Hack required for Windows. */ +void +gnupg_allow_set_foregound_window (pid_t pid) +{ + if (!pid) + log_info ("%s called with invalid pid %lu\n", + "gnupg_allow_set_foregound_window", (unsigned long)pid); +#if defined(HAVE_W32_SYSTEM) && !defined(HAVE_W32CE_SYSTEM) + else if (!AllowSetForegroundWindow ((pid_t)pid == (pid_t)(-1)?ASFW_ANY:pid)) + { + char *flags = getenv ("GNUPG_EXEC_DEBUG_FLAGS"); + if (flags && (atoi (flags) & 2)) + log_info ("AllowSetForegroundWindow(%lu) failed: %s\n", + (unsigned long)pid, w32_strerror (-1)); + } +#endif +} + +int +gnupg_remove (const char *fname) +{ +#ifdef HAVE_W32_SYSTEM + int rc; + wchar_t *wfname; + + wfname = utf8_to_wchar (fname); + if (!wfname) + rc = 0; + else + { + rc = DeleteFileW (wfname); + if (!rc) + gnupg_w32_set_errno (-1); + xfree (wfname); + } + if (!rc) + return -1; + return 0; +#else + return remove (fname); +#endif +} + + +/* Helper for gnupg_rename_file. */ +#ifdef HAVE_W32_SYSTEM +static int +w32_rename (const char *oldname, const char *newname) +{ + if (any8bitchar (oldname) || any8bitchar (newname)) + { + wchar_t *woldname, *wnewname; + int ret; + + woldname = utf8_to_wchar (oldname); + if (!woldname) + return -1; + wnewname = utf8_to_wchar (newname); + if (!wnewname) + { + xfree (wnewname); + return -1; + } + ret = _wrename (woldname, wnewname); + xfree (wnewname); + xfree (woldname); + return ret; + } + else + return rename (oldname, newname); +} +#endif /*HAVE_W32_SYSTEM*/ + + +/* Wrapper for rename(2) to handle Windows peculiarities. If + * BLOCK_SIGNALS is not NULL and points to a variable set to true, all + * signals will be blocked by calling gnupg_block_all_signals; the + * caller needs to call gnupg_unblock_all_signals if that variable is + * still set to true on return. */ +gpg_error_t +gnupg_rename_file (const char *oldname, const char *newname, int *block_signals) +{ + gpg_error_t err = 0; + + if (block_signals && *block_signals) + gnupg_block_all_signals (); + +#ifdef HAVE_DOSISH_SYSTEM + { + int wtime = 0; + + gnupg_remove (newname); + again: + if (w32_rename (oldname, newname)) + { + if (GetLastError () == ERROR_SHARING_VIOLATION) + { + /* Another process has the file open. We do not use a + * lock for read but instead we wait until the other + * process has closed the file. This may take long but + * that would also be the case with a dotlock approach for + * read and write. Note that we don't need this on Unix + * due to the inode concept. + * + * So let's wait until the rename has worked. The retry + * intervals are 50, 100, 200, 400, 800, 50ms, ... */ + if (!wtime || wtime >= 800) + wtime = 50; + else + wtime *= 2; + + if (wtime >= 800) + log_info (_("waiting for file '%s' to become accessible ...\n"), + oldname); + + Sleep (wtime); + goto again; + } + err = my_error_from_syserror (); + } + } +#else /* Unix */ + { +#ifdef __riscos__ + gnupg_remove (newname); +#endif + if (rename (oldname, newname) ) + err = my_error_from_syserror (); + } +#endif /* Unix */ + + if (block_signals && *block_signals && err) + { + gnupg_unblock_all_signals (); + *block_signals = 0; + } + + if (err) + log_error (_("renaming '%s' to '%s' failed: %s\n"), + oldname, newname, gpg_strerror (err)); + return err; +} + + +#ifndef HAVE_W32_SYSTEM +static mode_t +modestr_to_mode (const char *modestr, mode_t oldmode) +{ + static struct { + char letter; + mode_t value; + } table[] = { { '-', 0 }, + { 'r', S_IRUSR }, { 'w', S_IWUSR }, { 'x', S_IXUSR }, + { 'r', S_IRGRP }, { 'w', S_IWGRP }, { 'x', S_IXGRP }, + { 'r', S_IROTH }, { 'w', S_IWOTH }, { 'x', S_IXOTH } }; + int idx; + mode_t mode = 0; + + /* For now we only support a string as used by ls(1) and no octal + * numbers. The first character must be a dash. */ + for (idx=0; idx < 10 && *modestr; idx++, modestr++) + { + if (*modestr == table[idx].letter) + mode |= table[idx].value; + else if (*modestr == '.') + { + if (!idx) + ; /* Skip the dummy. */ + else if ((oldmode & table[idx].value)) + mode |= (oldmode & table[idx].value); + else + mode &= ~(oldmode & table[idx].value); + } + else if (*modestr != '-') + break; + } + + + return mode; +} +#endif + + +/* A wrapper around mkdir which takes a string for the mode argument. + This makes it easier to handle the mode argument which is not + defined on all systems. The format of the modestring is + + "-rwxrwxrwx" + + '-' is a don't care or not set. 'r', 'w', 'x' are read allowed, + write allowed, execution allowed with the first group for the user, + the second for the group and the third for all others. If the + string is shorter than above the missing mode characters are meant + to be not set. */ +int +gnupg_mkdir (const char *name, const char *modestr) +{ +#if GPG_ERROR_VERSION_NUMBER < 0x011c00 /* 1.28 */ + #ifdef HAVE_W32CE_SYSTEM + wchar_t *wname; + (void)modestr; + + wname = utf8_to_wchar (name); + if (!wname) + return -1; + if (!CreateDirectoryW (wname, NULL)) + { + xfree (wname); + return -1; /* ERRNO is automagically provided by gpg-error.h. */ + } + xfree (wname); + return 0; + #elif MKDIR_TAKES_ONE_ARG + (void)modestr; + /* Note: In the case of W32 we better use CreateDirectory and try to + set appropriate permissions. However using mkdir is easier + because this sets ERRNO. */ + return mkdir (name); + #else + return mkdir (name, modestr_to_mode (modestr, 0)); + #endif +#else + /* Note that gpgrt_mkdir also sets ERRNO in addition to returing an + * gpg-error style error code. */ + return gpgrt_mkdir (name, modestr); +#endif +} + + +/* A simple wrapper around chdir. NAME is expected to be utf8 + * encoded. */ +int +gnupg_chdir (const char *name) +{ +#if GPG_ERROR_VERSION_NUMBER < 0x011c00 /* 1.28 */ + return chdir (name); +#else /* Use the improved version from libgpg_error. */ + /* Note that gpgrt_chdir also sets ERRNO in addition to returning a + * gpg-error style error code. */ + return gpgrt_chdir (name); +#endif +} + + +/* A wrapper around rmdir. NAME is expected to be utf8 encoded. */ +int +gnupg_rmdir (const char *name) +{ +#ifdef HAVE_W32_SYSTEM + int rc; + wchar_t *wfname; + + wfname = utf8_to_wchar (name); + if (!wfname) + rc = 0; + else + { + rc = RemoveDirectoryW (wfname); + if (!rc) + gnupg_w32_set_errno (-1); + xfree (wfname); + } + if (!rc) + return -1; + return 0; +#else + return rmdir (name); +#endif +} + + +/* A wrapper around chmod which takes a string for the mode argument. + This makes it easier to handle the mode argument which is not + defined on all systems. The format of the modestring is the same + as for gnupg_mkdir with extra feature that a '.' keeps the original + mode bit. */ +int +gnupg_chmod (const char *name, const char *modestr) +{ +#ifdef HAVE_W32_SYSTEM + (void)name; + (void)modestr; + return 0; +#else + mode_t oldmode; + if (strchr (modestr, '.')) + { + /* Get the old mode so that a '.' can copy that bit. */ + struct stat st; + + if (stat (name, &st)) + return -1; + oldmode = st.st_mode; + } + else + oldmode = 0; + return chmod (name, modestr_to_mode (modestr, oldmode)); +#endif +} + + +/* Our version of mkdtemp. The API is identical to POSIX.1-2008 + version. We do not use a system provided mkdtemp because we have a + good RNG instantly available and this way we don't have diverging + versions. */ +char * +gnupg_mkdtemp (char *tmpl) +{ + /* A lower bound on the number of temporary files to attempt to + generate. The maximum total number of temporary file names that + can exist for a given template is 62**6 (5*36**3 for Windows). + It should never be necessary to try all these combinations. + Instead if a reasonable number of names is tried (we define + reasonable as 62**3 or 5*36**3) fail to give the system + administrator the chance to remove the problems. */ +#ifdef HAVE_W32_SYSTEM + static const char letters[] = + "abcdefghijklmnopqrstuvwxyz0123456789"; +# define NUMBER_OF_LETTERS 36 +# define ATTEMPTS_MIN (5 * 36 * 36 * 36) +#else + static const char letters[] = + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; +# define NUMBER_OF_LETTERS 62 +# define ATTEMPTS_MIN (62 * 62 * 62) +#endif + int len; + char *XXXXXX; + uint64_t value; + unsigned int count; + int save_errno = errno; + /* The number of times to attempt to generate a temporary file. To + conform to POSIX, this must be no smaller than TMP_MAX. */ +#if ATTEMPTS_MIN < TMP_MAX + unsigned int attempts = TMP_MAX; +#else + unsigned int attempts = ATTEMPTS_MIN; +#endif + + len = strlen (tmpl); + if (len < 6 || strcmp (&tmpl[len - 6], "XXXXXX")) + { + gpg_err_set_errno (EINVAL); + return NULL; + } + + /* This is where the Xs start. */ + XXXXXX = &tmpl[len - 6]; + + /* Get a random start value. */ + gcry_create_nonce (&value, sizeof value); + + /* Loop until a directory was created. */ + for (count = 0; count < attempts; value += 7777, ++count) + { + uint64_t v = value; + + /* Fill in the random bits. */ + XXXXXX[0] = letters[v % NUMBER_OF_LETTERS]; + v /= NUMBER_OF_LETTERS; + XXXXXX[1] = letters[v % NUMBER_OF_LETTERS]; + v /= NUMBER_OF_LETTERS; + XXXXXX[2] = letters[v % NUMBER_OF_LETTERS]; + v /= NUMBER_OF_LETTERS; + XXXXXX[3] = letters[v % NUMBER_OF_LETTERS]; + v /= NUMBER_OF_LETTERS; + XXXXXX[4] = letters[v % NUMBER_OF_LETTERS]; + v /= NUMBER_OF_LETTERS; + XXXXXX[5] = letters[v % NUMBER_OF_LETTERS]; + + if (!gnupg_mkdir (tmpl, "-rwx")) + { + gpg_err_set_errno (save_errno); + return tmpl; + } + if (errno != EEXIST) + return NULL; + } + + /* We got out of the loop because we ran out of combinations to try. */ + gpg_err_set_errno (EEXIST); + return NULL; +} + + +int +gnupg_setenv (const char *name, const char *value, int overwrite) +{ +#ifdef HAVE_W32CE_SYSTEM + (void)name; + (void)value; + (void)overwrite; + return 0; +#else /*!W32CE*/ +# ifdef HAVE_W32_SYSTEM + /* Windows maintains (at least) two sets of environment variables. + One set can be accessed by GetEnvironmentVariable and + SetEnvironmentVariable. This set is inherited by the children. + The other set is maintained in the C runtime, and is accessed + using getenv and putenv. We try to keep them in sync by + modifying both sets. */ + { + int exists; + char tmpbuf[10]; + exists = GetEnvironmentVariable (name, tmpbuf, sizeof tmpbuf); + + if ((! exists || overwrite) && !SetEnvironmentVariable (name, value)) + { + gpg_err_set_errno (EINVAL); /* (Might also be ENOMEM.) */ + return -1; + } + } +# endif /*W32*/ + +# ifdef HAVE_SETENV + return setenv (name, value, overwrite); +# else /*!HAVE_SETENV*/ + if (! getenv (name) || overwrite) + { + char *buf; + + (void)overwrite; + if (!name || !value) + { + gpg_err_set_errno (EINVAL); + return -1; + } + buf = strconcat (name, "=", value, NULL); + if (!buf) + return -1; +# if __GNUC__ +# warning no setenv - using putenv but leaking memory. +# endif + return putenv (buf); + } + return 0; +# endif /*!HAVE_SETENV*/ +#endif /*!W32CE*/ +} + + +int +gnupg_unsetenv (const char *name) +{ +#ifdef HAVE_W32CE_SYSTEM + (void)name; + return 0; +#else /*!W32CE*/ +# ifdef HAVE_W32_SYSTEM + /* Windows maintains (at least) two sets of environment variables. + One set can be accessed by GetEnvironmentVariable and + SetEnvironmentVariable. This set is inherited by the children. + The other set is maintained in the C runtime, and is accessed + using getenv and putenv. We try to keep them in sync by + modifying both sets. */ + if (!SetEnvironmentVariable (name, NULL)) + { + gpg_err_set_errno (EINVAL); /* (Might also be ENOMEM.) */ + return -1; + } +# endif /*W32*/ + +# ifdef HAVE_UNSETENV + return unsetenv (name); +# else /*!HAVE_UNSETENV*/ + { + char *buf; + + if (!name) + { + gpg_err_set_errno (EINVAL); + return -1; + } + buf = xtrystrdup (name); + if (!buf) + return -1; +# if __GNUC__ +# warning no unsetenv - trying putenv but leaking memory. +# endif + return putenv (buf); + } +# endif /*!HAVE_UNSETENV*/ +#endif /*!W32CE*/ +} + + +/* Return the current working directory as a malloced string. Return + NULL and sets ERRNO on error. */ +char * +gnupg_getcwd (void) +{ +#if GPGRT_VERSION_NUMBER < 0x012800 /* 1.40 */ +# ifdef HAVE_W32_SYSTEM + wchar_t wbuffer[MAX_PATH + sizeof(wchar_t)]; + DWORD wlen; + char *buf, *p; + + wlen = GetCurrentDirectoryW (MAX_PATH, wbuffer); + if (!wlen) + { + gpg_err_set_errno (EINVAL); + return NULL; + + } + else if (wlen > MAX_PATH) + { + gpg_err_set_errno (ENAMETOOLONG); + return NULL; + } + buf = wchar_to_utf8 (wbuffer); + if (buf) + { + for (p=buf; *p; p++) + if (*p == '\\') + *p = '/'; + } + return buf; + +# else /*Unix*/ + char *buffer; + size_t size = 100; + + for (;;) + { + buffer = xtrymalloc (size+1); + if (!buffer) + return NULL; + if (getcwd (buffer, size) == buffer) + return buffer; + xfree (buffer); + if (errno != ERANGE) + return NULL; + size *= 2; + } +# endif /*Unix*/ +#else + return gpgrt_getcwd (); +#endif +} + + +/* A simple wrapper around access. NAME is expected to be utf8 + * encoded. This function returns an error code and sets ERRNO. */ +gpg_err_code_t +gnupg_access (const char *name, int mode) +{ +#if GPGRT_VERSION_NUMBER < 0x012800 /* 1.40 */ +# ifdef HAVE_W32_SYSTEM + wchar_t *wfname; + gpg_err_code_t ec; + + wfname = utf8_to_wchar (name); + if (!wfname) + ec = gpg_err_code_from_syserror (); + else + { + ec = _waccess (wfname, mode)? gpg_err_code_from_syserror () : 0; + xfree (wfname); + } + return ec; +# else + return access (name, mode)? gpg_err_code_from_syserror () : 0; +# endif +#else /* gpgrt 1.40 or newer. */ + return gpgrt_access (name, mode); +#endif +} + + +/* A wrapper around stat to handle Unicode file names under Windows. */ +#ifdef HAVE_STAT +int +gnupg_stat (const char *name, struct stat *statbuf) +{ +# ifdef HAVE_W32_SYSTEM + if (any8bitchar (name)) + { + wchar_t *wname; + struct _stat32 st32; + int ret; + + wname = utf8_to_wchar (name); + if (!wname) + return -1; + ret = _wstat (wname, &st32); + xfree (wname); + if (!ret) + { + statbuf->st_dev = st32.st_dev; + statbuf->st_ino = st32.st_ino; + statbuf->st_mode = st32.st_mode; + statbuf->st_nlink = st32.st_nlink; + statbuf->st_uid = st32.st_uid; + statbuf->st_gid = st32.st_gid; + statbuf->st_rdev = st32.st_rdev; + statbuf->st_size = st32.st_size; + statbuf->st_atime = st32.st_atime; + statbuf->st_mtime = st32.st_mtime; + statbuf->st_ctime = st32.st_ctime; + } + return ret; + } + else + return stat (name, statbuf); +# else + return stat (name, statbuf); +# endif +} +#endif /*HAVE_STAT*/ + + +/* Wrapper around fopen for the cases where we have not yet switched + * to es_fopen. Note that for convenience the prototype is in util.h */ +FILE * +gnupg_fopen (const char *fname, const char *mode) +{ +#ifdef HAVE_W32_SYSTEM + if (any8bitchar (fname)) + { + wchar_t *wfname; + const wchar_t *wmode; + wchar_t *wmodebuf = NULL; + FILE *ret; + + wfname = utf8_to_wchar (fname); + if (!wfname) + return NULL; + if (!strcmp (mode, "r")) + wmode = L"r"; + else if (!strcmp (mode, "rb")) + wmode = L"rb"; + else if (!strcmp (mode, "w")) + wmode = L"w"; + else if (!strcmp (mode, "wb")) + wmode = L"wb"; + else + { + wmodebuf = utf8_to_wchar (mode); + if (!wmodebuf) + { + xfree (wfname); + return NULL; + } + wmode = wmodebuf; + } + ret = _wfopen (wfname, wmode); + xfree (wfname); + xfree (wmodebuf); + return ret; + } + else + return fopen (fname, mode); + +#else /*Unix*/ + return fopen (fname, mode); +#endif /*Unix*/ +} + + + +/* A wrapper around open to handle Unicode file names under Windows. */ +int +gnupg_open (const char *name, int flags, unsigned int mode) +{ +#ifdef HAVE_W32_SYSTEM + if (any8bitchar (name)) + { + wchar_t *wname; + int ret; + + wname = utf8_to_wchar (name); + if (!wname) + return -1; + ret = _wopen (wname, flags, mode); + xfree (wname); + return ret; + } + else + return open (name, flags, mode); +#else + return open (name, flags, mode); +#endif +} + + +/* A wrapper around opendir to handle Unicode file names under + * Windows. This assumes the mingw toolchain. */ +gnupg_dir_t +gnupg_opendir (const char *name) +{ +#ifdef HAVE_W32_SYSTEM + _WDIR *dir; + wchar_t *wname; +#else + DIR *dir; +#endif + gnupg_dir_t gdir; + +#ifdef HAVE_W32_SYSTEM + /* Note: See gpgtar-create for an alternative implementation which + * could be used here to avoid a mingw dependency. */ + wname = utf8_to_wchar (name); + if (!wname) + return NULL; + dir = _wopendir (wname); + xfree (wname); +#else + dir = opendir (name); +#endif + + if (!dir) + return NULL; + + gdir = xtrymalloc (sizeof *gdir); + if (!gdir) + { + int save_errno = errno; +#ifdef HAVE_W32_SYSTEM + _wclosedir (dir); +#else + closedir (dir); +#endif + gpg_err_set_errno (save_errno); + return NULL; + } + gdir->dir = dir; + gdir->namesize = 0; + gdir->dirent.d_name = gdir->name; + + return gdir; +} + + +gnupg_dirent_t +gnupg_readdir (gnupg_dir_t gdir) +{ +#ifdef HAVE_W32_SYSTEM + char *namebuffer = NULL; + struct _wdirent *de; +#else + struct dirent *de; +#endif + size_t n; + gnupg_dirent_t gde; + const char *name; + + if (!gdir) + { + gpg_err_set_errno (EINVAL); + return 0; + } + +#ifdef HAVE_W32_SYSTEM + de = _wreaddir (gdir->dir); + if (!de) + return NULL; + namebuffer = wchar_to_utf8 (de->d_name); + if (!namebuffer) + return NULL; + name = namebuffer; +#else + de = readdir (gdir->dir); + if (!de) + return NULL; + name = de->d_name; +#endif + + gde = &gdir->dirent; + n = strlen (name); + if (gdir->namesize) + { + /* Use allocated buffer. */ + if (n+1 >= gdir->namesize || !gde->d_name) + { + gdir->namesize = n + 256; + xfree (gde->d_name); + gde->d_name = xtrymalloc (gdir->namesize); + if (!gde->d_name) + return NULL; /* ERRNO is already set. */ + } + strcpy (gde->d_name, name); + } + else if (n+1 >= sizeof (gdir->name)) + { + /* Switch to allocated buffer. */ + gdir->namesize = n + 256; + gde->d_name = xtrymalloc (gdir->namesize); + if (!gde->d_name) + return NULL; /* ERRNO is already set. */ + strcpy (gde->d_name, name); + } + else + { + /* Use static buffer. */ + gde->d_name = gdir->name; + strcpy (gde->d_name, name); + } + +#ifdef HAVE_W32_SYSTEM + xfree (namebuffer); +#endif + + return gde; +} + + +int +gnupg_closedir (gnupg_dir_t gdir) +{ +#ifdef HAVE_W32_SYSTEM + _WDIR *dir; +#else + DIR *dir; +#endif + + if (!gdir) + return 0; + dir = gdir->dir; + if (gdir->namesize) + xfree (gdir->dirent.d_name); + xfree (gdir); + +#ifdef HAVE_W32_SYSTEM + return _wclosedir (dir); +#else + return closedir (dir); +#endif +} + + + +#ifdef HAVE_W32CE_SYSTEM +/* There is a isatty function declaration in cegcc but it does not + make sense, thus we redefine it. */ +int +_gnupg_isatty (int fd) +{ + (void)fd; + return 0; +} +#endif + + +#ifdef HAVE_W32CE_SYSTEM +/* Replacement for getenv which takes care of the our use of getenv. + The code is not thread safe but we expect it to work in all cases + because it is called for the first time early enough. */ +char * +_gnupg_getenv (const char *name) +{ + static int initialized; + static char *assuan_debug; + + if (!initialized) + { + assuan_debug = read_w32_registry_string (NULL, + "\\Software\\GNU\\libassuan", + "debug"); + initialized = 1; + } + + if (!strcmp (name, "ASSUAN_DEBUG")) + return assuan_debug; + else + return NULL; +} + +#endif /*HAVE_W32CE_SYSTEM*/ + + +#ifdef HAVE_W32_SYSTEM +/* Return the user's security identifier from the current process. */ +PSID +w32_get_user_sid (void) +{ + int okay = 0; + HANDLE proc = NULL; + HANDLE token = NULL; + TOKEN_USER *user = NULL; + PSID sid = NULL; + DWORD tokenlen, sidlen; + + proc = OpenProcess (PROCESS_QUERY_INFORMATION, FALSE, GetCurrentProcessId()); + if (!proc) + goto leave; + + if (!OpenProcessToken (proc, TOKEN_QUERY, &token)) + goto leave; + + if (!GetTokenInformation (token, TokenUser, NULL, 0, &tokenlen) + && GetLastError() != ERROR_INSUFFICIENT_BUFFER) + goto leave; + + user = xtrymalloc (tokenlen); + if (!user) + goto leave; + + if (!GetTokenInformation (token, TokenUser, user, tokenlen, &tokenlen)) + goto leave; + if (!IsValidSid (user->User.Sid)) + goto leave; + sidlen = GetLengthSid (user->User.Sid); + sid = xtrymalloc (sidlen); + if (!sid) + goto leave; + if (!CopySid (sidlen, sid, user->User.Sid)) + goto leave; + okay = 1; + + leave: + xfree (user); + if (token) + CloseHandle (token); + if (proc) + CloseHandle (proc); + + if (!okay) + { + xfree (sid); + sid = NULL; + } + return sid; +} +#endif /*HAVE_W32_SYSTEM*/ + + + +/* Support for inotify under Linux. */ + +/* Store a new inotify file handle for FNAME at R_FD or return an + * error code. This file descriptor watch the removal of FNAME. */ +gpg_error_t +gnupg_inotify_watch_delete_self (int *r_fd, const char *fname) +{ +#if HAVE_INOTIFY_INIT + gpg_error_t err; + int fd; + + *r_fd = -1; + + if (!fname) + return my_error (GPG_ERR_INV_VALUE); + + fd = inotify_init (); + if (fd == -1) + return my_error_from_syserror (); + + if (inotify_add_watch (fd, fname, IN_DELETE_SELF) == -1) + { + err = my_error_from_syserror (); + close (fd); + return err; + } + + *r_fd = fd; + return 0; +#else /*!HAVE_INOTIFY_INIT*/ + + (void)fname; + *r_fd = -1; + return my_error (GPG_ERR_NOT_SUPPORTED); + +#endif /*!HAVE_INOTIFY_INIT*/ +} + + +/* Store a new inotify file handle for SOCKET_NAME at R_FD or return + * an error code. */ +gpg_error_t +gnupg_inotify_watch_socket (int *r_fd, const char *socket_name) +{ +#if HAVE_INOTIFY_INIT + gpg_error_t err; + char *fname; + int fd; + char *p; + + *r_fd = -1; + + if (!socket_name) + return my_error (GPG_ERR_INV_VALUE); + + fname = xtrystrdup (socket_name); + if (!fname) + return my_error_from_syserror (); + + fd = inotify_init (); + if (fd == -1) + { + err = my_error_from_syserror (); + xfree (fname); + return err; + } + + /* We need to watch the directory for the file because there won't + * be an IN_DELETE_SELF for a socket file. To handle a removal of + * the directory we also watch the directory itself. */ + p = strrchr (fname, '/'); + if (p) + *p = 0; + if (inotify_add_watch (fd, fname, + (IN_DELETE|IN_DELETE_SELF|IN_EXCL_UNLINK)) == -1) + { + err = my_error_from_syserror (); + close (fd); + xfree (fname); + return err; + } + + xfree (fname); + + *r_fd = fd; + return 0; +#else /*!HAVE_INOTIFY_INIT*/ + + (void)socket_name; + *r_fd = -1; + return my_error (GPG_ERR_NOT_SUPPORTED); + +#endif /*!HAVE_INOTIFY_INIT*/ +} + + +/* Read an inotify event and return true if it matches NAME or if it + * sees an IN_DELETE_SELF event for the directory of NAME. */ +int +gnupg_inotify_has_name (int fd, const char *name) +{ +#if USE_NPTH && HAVE_INOTIFY_INIT +#define BUFSIZE_FOR_INOTIFY (sizeof (struct inotify_event) + 255 + 1) + union { + struct inotify_event ev; + char _buf[sizeof (struct inotify_event) + 255 + 1]; + } buf; + struct inotify_event *evp; + int n; + + n = npth_read (fd, &buf, sizeof buf); + /* log_debug ("notify read: n=%d\n", n); */ + evp = &buf.ev; + while (n >= sizeof (struct inotify_event)) + { + /* log_debug (" mask=%x len=%u name=(%s)\n", */ + /* evp->mask, (unsigned int)evp->len, evp->len? evp->name:""); */ + if ((evp->mask & IN_UNMOUNT)) + { + /* log_debug (" found (dir unmounted)\n"); */ + return 3; /* Directory was unmounted. */ + } + if ((evp->mask & IN_DELETE_SELF)) + { + /* log_debug (" found (dir removed)\n"); */ + return 2; /* Directory was removed. */ + } + if ((evp->mask & IN_DELETE)) + { + if (evp->len >= strlen (name) && !strcmp (evp->name, name)) + { + /* log_debug (" found (file removed)\n"); */ + return 1; /* File was removed. */ + } + } + n -= sizeof (*evp) + evp->len; + evp = (struct inotify_event *)(void *) + ((char *)evp + sizeof (*evp) + evp->len); + } + +#else /*!(USE_NPTH && HAVE_INOTIFY_INIT)*/ + + (void)fd; + (void)name; + +#endif /*!(USE_NPTH && HAVE_INOTIFY_INIT)*/ + + return 0; /* Not found. */ +} + + +/* Return a malloc'ed string that is the path to the passed + * unix-domain socket (or return NULL if this is not a valid + * unix-domain socket). We use a plain int here because it is only + * used on Linux. + * + * FIXME: This function needs to be moved to libassuan. */ +#ifndef HAVE_W32_SYSTEM +char * +gnupg_get_socket_name (int fd) +{ + struct sockaddr_un un; + socklen_t len = sizeof(un); + char *name = NULL; + + if (getsockname (fd, (struct sockaddr*)&un, &len) != 0) + log_error ("could not getsockname(%d): %s\n", fd, + gpg_strerror (my_error_from_syserror ())); + else if (un.sun_family != AF_UNIX) + log_error ("file descriptor %d is not a unix-domain socket\n", fd); + else if (len <= offsetof (struct sockaddr_un, sun_path)) + log_error ("socket name not present for file descriptor %d\n", fd); + else if (len > sizeof(un)) + log_error ("socket name for file descriptor %d was truncated " + "(passed %zu bytes, wanted %u)\n", fd, sizeof(un), len); + else + { + size_t namelen = len - offsetof (struct sockaddr_un, sun_path); + + /* log_debug ("file descriptor %d has path %s (%zu octets)\n", fd, */ + /* un.sun_path, namelen); */ + name = xtrymalloc (namelen + 1); + if (!name) + log_error ("failed to allocate memory for name of fd %d: %s\n", + fd, gpg_strerror (my_error_from_syserror ())); + else + { + memcpy (name, un.sun_path, namelen); + name[namelen] = 0; + } + } + + return name; +} +#endif /*!HAVE_W32_SYSTEM*/ + +/* Check whether FD is valid. */ +int +gnupg_fd_valid (int fd) +{ + int d = dup (fd); + if (d < 0) + return 0; + close (d); + return 1; +} + + +/* Return a malloced copy of the current user's account name; this may + * return NULL on memory failure. Note that this should eventually be + * replaced by a gpgrt function. */ +char * +gnupg_getusername (void) +{ + char *result = NULL; + +#ifdef HAVE_W32_SYSTEM + wchar_t wtmp[1]; + wchar_t *wbuf; + DWORD wsize = 1; + + GetUserNameW (wtmp, &wsize); + wbuf = xtrymalloc (wsize * sizeof *wbuf); + if (!wbuf) + { + gpg_err_set_errno (ENOMEM); + return NULL; + } + if (!GetUserNameW (wbuf, &wsize)) + { + gpg_err_set_errno (EINVAL); + xfree (wbuf); + return NULL; + } + result= wchar_to_utf8 (wbuf); + xfree (wbuf); + +#else /* !HAVE_W32_SYSTEM */ + +# if defined(HAVE_PWD_H) && defined(HAVE_GETPWUID) + struct passwd *pwd; + + pwd = getpwuid (getuid()); + if (pwd) + result = xtrystrdup (pwd->pw_name); + +# endif /*HAVE_PWD_H*/ + +#endif /* !HAVE_W32_SYSTEM */ + + return result; +} diff --git a/common/sysutils.h b/common/sysutils.h new file mode 100644 index 0000000..e22156b --- /dev/null +++ b/common/sysutils.h @@ -0,0 +1,117 @@ +/* sysutils.h - System utility functions for Gnupg + * Copyright (C) 2002 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef GNUPG_COMMON_SYSUTILS_H +#define GNUPG_COMMON_SYSUTILS_H + +/* Because we use system handles and not libc low level file + descriptors on W32, we need to declare them as HANDLE (which + actually is a plain pointer). This is required to eventually + support 64 bits Windows systems. */ +#ifdef HAVE_W32_SYSTEM +typedef void *gnupg_fd_t; +#define GNUPG_INVALID_FD ((void*)(-1)) +#define INT2FD(s) ((void *)(s)) +#define FD2INT(h) ((unsigned int)(h)) +#else +typedef int gnupg_fd_t; +#define GNUPG_INVALID_FD (-1) +#define INT2FD(s) (s) +#define FD2INT(h) (h) +#endif + +#ifdef HAVE_STAT +# include <sys/stat.h> +#endif + +struct gnupg_dir_s; +typedef struct gnupg_dir_s *gnupg_dir_t; +struct gnupg_dirent_s +{ + /* We don't have a d_ino because that can't be used on Windows + * anyway. D_NAME is a pointer into the gnupg_dir_s which has a + * static buffer or allocates sufficient space as needed. This is + * only valid after gnupg_readdir. */ + char *d_name; +}; +typedef struct gnupg_dirent_s *gnupg_dirent_t; + + +void trap_unaligned (void); +int disable_core_dumps (void); +int enable_core_dumps (void); +void enable_special_filenames (void); +const unsigned char *get_session_marker (size_t *rlen); +unsigned int get_uint_nonce (void); +/*int check_permissions (const char *path,int extension,int checkonly);*/ +void gnupg_sleep (unsigned int seconds); +void gnupg_usleep (unsigned int usecs); +int translate_sys2libc_fd (gnupg_fd_t fd, int for_write); +int translate_sys2libc_fd_int (int fd, int for_write); +int check_special_filename (const char *fname, int for_write, int notranslate); +FILE *gnupg_tmpfile (void); +void gnupg_reopen_std (const char *pgmname); +void gnupg_allow_set_foregound_window (pid_t pid); +int gnupg_remove (const char *fname); +gpg_error_t gnupg_rename_file (const char *oldname, const char *newname, + int *block_signals); +int gnupg_mkdir (const char *name, const char *modestr); +int gnupg_chdir (const char *name); +int gnupg_rmdir (const char *name); +int gnupg_chmod (const char *name, const char *modestr); +char *gnupg_mkdtemp (char *template); +int gnupg_setenv (const char *name, const char *value, int overwrite); +int gnupg_unsetenv (const char *name); +char *gnupg_getcwd (void); +gpg_err_code_t gnupg_access (const char *name, int mode); +#ifdef HAVE_STAT +int gnupg_stat (const char *name, struct stat *statbuf); +#endif /*HAVE_STAT*/ +int gnupg_open (const char *name, int flags, unsigned int mode); +gnupg_dir_t gnupg_opendir (const char *name); +gnupg_dirent_t gnupg_readdir (gnupg_dir_t gdir); +int gnupg_closedir (gnupg_dir_t gdir); +char *gnupg_get_socket_name (int fd); +int gnupg_fd_valid (int fd); +char *gnupg_getusername (void); + +gpg_error_t gnupg_inotify_watch_delete_self (int *r_fd, const char *fname); +gpg_error_t gnupg_inotify_watch_socket (int *r_fd, const char *socket_name); +int gnupg_inotify_has_name (int fd, const char *name); + + +#ifdef HAVE_W32_SYSTEM +void gnupg_w32_set_errno (int ec); +void *w32_get_user_sid (void); + +#include "../common/w32help.h" + +#endif /*HAVE_W32_SYSTEM*/ + +#endif /*GNUPG_COMMON_SYSUTILS_H*/ diff --git a/common/t-b64.c b/common/t-b64.c new file mode 100644 index 0000000..3b63872 --- /dev/null +++ b/common/t-b64.c @@ -0,0 +1,181 @@ +/* t-b64.c - Module tests for b64enc.c and b64dec.c + * Copyright (C) 2008 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +/* + + As of now this is only a test program for manual tests. + + */ + + + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> + +#include "util.h" + +#define pass() do { ; } while(0) +#define fail(a) do { fprintf (stderr, "%s:%d: test %d failed\n",\ + __FILE__,__LINE__, (a)); \ + errcount++; \ + } while(0) + +static int verbose; +static int errcount; + +static void +test_b64enc_pgp (const char *string) +{ + gpg_error_t err; + struct b64state state; + + if (!string) + string = "a"; + + err = b64enc_start (&state, stdout, "PGP MESSAGE"); + if (err) + fail (1); + + err = b64enc_write (&state, string, strlen (string)); + if (err) + fail (2); + + err = b64enc_finish (&state); + if (err) + fail (3); + + pass (); +} + + +static void +test_b64enc_file (const char *fname) +{ + gpg_error_t err; + struct b64state state; + FILE *fp; + char buffer[50]; + size_t nread; + + fp = fname ? fopen (fname, "r") : stdin; + if (!fp) + { + fprintf (stderr, "%s:%d: can't open '%s': %s\n", + __FILE__, __LINE__, fname? fname:"[stdin]", strerror (errno)); + fail (0); + } + + err = b64enc_start (&state, stdout, "DATA"); + if (err) + fail (1); + + while ( (nread = fread (buffer, 1, sizeof buffer, fp)) ) + { + err = b64enc_write (&state, buffer, nread); + if (err) + fail (2); + } + + err = b64enc_finish (&state); + if (err) + fail (3); + + fclose (fp); + pass (); +} + +static void +test_b64dec_file (const char *fname) +{ + gpg_error_t err; + struct b64state state; + FILE *fp; + char buffer[50]; + size_t nread, nbytes; + + fp = fname ? fopen (fname, "r") : stdin; + if (!fp) + { + fprintf (stderr, "%s:%d: can't open '%s': %s\n", + __FILE__, __LINE__, fname? fname:"[stdin]", strerror (errno)); + fail (0); + } + + err = b64dec_start (&state, ""); + if (err) + fail (1); + + while ( (nread = fread (buffer, 1, sizeof buffer, fp)) ) + { + err = b64dec_proc (&state, buffer, nread, &nbytes); + if (err) + { + if (gpg_err_code (err) == GPG_ERR_EOF) + break; + fail (2); + } + else if (nbytes) + fwrite (buffer, 1, nbytes, stdout); + } + + err = b64dec_finish (&state); + if (err) + fail (3); + + fclose (fp); + pass (); +} + + + +int +main (int argc, char **argv) +{ + int do_encode = 0; + int do_decode = 0; + + if (argc) + { argc--; argv++; } + if (argc && !strcmp (argv[0], "--verbose")) + { + verbose = 1; + argc--; argv++; + } + + if (argc && !strcmp (argv[0], "--encode")) + { + do_encode = 1; + argc--; argv++; + } + else if (argc && !strcmp (argv[0], "--decode")) + { + do_decode = 1; + argc--; argv++; + } + + if (do_encode) + test_b64enc_file (argc? *argv: NULL); + else if (do_decode) + test_b64dec_file (argc? *argv: NULL); + else + test_b64enc_pgp (argc? *argv: NULL); + + return !!errcount; +} diff --git a/common/t-ccparray.c b/common/t-ccparray.c new file mode 100644 index 0000000..eb96526 --- /dev/null +++ b/common/t-ccparray.c @@ -0,0 +1,93 @@ +/* t-ccparray.c - Module test for ccparray.c + * Copyright (C) 2016 g10 Code GmbH + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "util.h" +#include "ccparray.h" + +#define pass() do { ; } while(0) +#define fail(a) do { fprintf (stderr, "%s:%d: test %d failed\n",\ + __FILE__,__LINE__, (a)); \ + exit (1); \ + } while(0) + + +static void +run_test_1 (void) +{ + ccparray_t ccp; + const char **argv; + size_t nelem; + + ccparray_init (&ccp, 0); + ccparray_put (&ccp, "First arg"); + ccparray_put (&ccp, "Second arg"); + ccparray_put (&ccp, NULL); + ccparray_put (&ccp, "Fourth arg"); + argv = ccparray_get (&ccp, &nelem); + if (!argv) + { + fprintf (stderr, "error building array: %s\n", strerror (errno)); + exit (1); + } + + if (nelem != 4) + fail (1); + + /* for (i=0; argv[i]; i++) */ + /* printf ("[%d] = '%s'\n", i, argv[i]); */ + xfree (argv); +} + + +static void +run_test_var (int count) +{ + ccparray_t ccp; + size_t nelem; + int i; + + ccparray_init (&ccp, 0); + for (i=0; i < count; i++) + ccparray_put (&ccp, "An arg"); + xfree (ccparray_get (&ccp, &nelem)); + if (nelem != i) + fail (2); +} + + +int +main (int argc, char **argv) +{ + (void)argc; + (void)argv; + + run_test_1 (); + run_test_var (0); + run_test_var (7); + run_test_var (8); + run_test_var (9); + run_test_var (4096); + + return 0; +} diff --git a/common/t-convert.c b/common/t-convert.c new file mode 100644 index 0000000..e25de90 --- /dev/null +++ b/common/t-convert.c @@ -0,0 +1,463 @@ +/* t-convert.c - Module test for convert.c + * Copyright (C) 2006, 2008 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> + +#include "util.h" + +#define pass() do { ; } while(0) +#define fail(a) do { fprintf (stderr, "%s:%d: test %d failed\n",\ + __FILE__,__LINE__, (a)); \ + /*exit (1)*/; \ + } while(0) + + +static void +test_hex2bin (void) +{ + static const char *valid[] = { + "00112233445566778899aabbccddeeff11223344", + "00112233445566778899AABBCCDDEEFF11223344", + "00112233445566778899AABBCCDDEEFF11223344 blah", + "00112233445566778899AABBCCDDEEFF11223344\tblah", + "00112233445566778899AABBCCDDEEFF11223344\nblah", + NULL + }; + static const char *invalid[] = { + "00112233445566778899aabbccddeeff1122334", + "00112233445566778899AABBCCDDEEFF1122334", + "00112233445566778899AABBCCDDEEFG11223344", + "00 112233445566778899aabbccddeeff11223344", + "00:112233445566778899aabbccddeeff11223344", + ":00112233445566778899aabbccddeeff11223344", + "0:0112233445566778899aabbccddeeff11223344", + "00112233445566778899aabbccddeeff11223344:", + "00112233445566778899aabbccddeeff112233445", + "00112233445566778899aabbccddeeff1122334455", + "00112233445566778899aabbccddeeff11223344blah", + NULL + }; + static const char *valid2[] = { + "00", + "00 x", + NULL + }; + static const char *invalid2[] = { + "", + "0", + "00:", + "00x", + " 00", + NULL + }; + unsigned char buffer[20]; + int len; + int i; + + + for (i=0; valid[i]; i++) + { + len = hex2bin (valid[i], buffer, sizeof buffer); + if (len < 0) + fail (i); + if (memcmp (buffer, ("\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa" + "\xbb\xcc\xdd\xee\xff\x11\x22\x33\x44"), 20)) + fail (i); + } + if (hex2bin (valid[0], buffer, sizeof buffer) != 40) + fail (0); + if (hex2bin (valid[2], buffer, sizeof buffer) != 41) + fail (0); + + for (i=0; invalid[i]; i++) + { + len = hex2bin (invalid[i], buffer, sizeof buffer); + if (!(len < 0)) + fail (i); + } + + for (i=0; valid2[i]; i++) + { + len = hex2bin (valid2[i], buffer, 1); + if (len < 0) + fail (i); + if (memcmp (buffer, "\x00", 1)) + fail (i); + } + if (hex2bin (valid2[0], buffer, 1) != 2) + fail (0); + if (hex2bin (valid2[1], buffer, 1) != 3) + fail (0); + + for (i=0; invalid2[i]; i++) + { + len = hex2bin (invalid2[i], buffer, 1); + if (!(len < 0)) + fail (i); + } +} + + + +static void +test_hexcolon2bin (void) +{ + static const char *valid[] = { + "00112233445566778899aabbccddeeff11223344", + "00112233445566778899AABBCCDDEEFF11223344", + "00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:11:22:33:44", + "00112233445566778899AABBCCDDEEFF11223344 blah", + "00112233445566778899AABBCCDDEEFF11223344\tblah", + "00112233445566778899AABBCCDDEEFF11223344\nblah", + NULL + }; + static const char *invalid[] = { + "00112233445566778899aabbccddeeff1122334", + "00112233445566778899AABBCCDDEEFF1122334", + "00112233445566778899AABBCCDDEEFG11223344", + ":00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:11:22:33:44", + "00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:11:22:33:44:", + "00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:11:22:3344", + "00:1122:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:11:22:33:44", + "0011:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:11:22:33:44", + "00 11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:11:22:33:44", + "00:11 22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:11:22:33:44", + "00112233445566778899aabbccddeeff112233445", + "00112233445566778899aabbccddeeff1122334455", + "00112233445566778899aabbccddeeff11223344blah", + NULL + }; + static const char *valid2[] = { + "00", + "00 x", + NULL + }; + static const char *invalid2[] = { + "", + "0", + "00:", + ":00", + "0:0", + "00x", + " 00", + NULL + }; + unsigned char buffer[20]; + int len; + int i; + + + for (i=0; valid[i]; i++) + { + len = hexcolon2bin (valid[i], buffer, sizeof buffer); + if (len < 0) + fail (i); + if (memcmp (buffer, ("\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa" + "\xbb\xcc\xdd\xee\xff\x11\x22\x33\x44"), 20)) + fail (i); + } + if (hexcolon2bin (valid[0], buffer, sizeof buffer) != 40) + fail (0); + if (hexcolon2bin (valid[3], buffer, sizeof buffer) != 41) + fail (0); + + for (i=0; invalid[i]; i++) + { + len = hexcolon2bin (invalid[i], buffer, sizeof buffer); + if (!(len < 0)) + fail (i); + } + + for (i=0; valid2[i]; i++) + { + len = hexcolon2bin (valid2[i], buffer, 1); + if (len < 0) + fail (i); + if (memcmp (buffer, "\x00", 1)) + fail (i); + } + if (hexcolon2bin (valid2[0], buffer, 1) != 2) + fail (0); + if (hexcolon2bin (valid2[1], buffer, 1) != 3) + fail (0); + + for (i=0; invalid2[i]; i++) + { + len = hexcolon2bin (invalid2[i], buffer, 1); + if (!(len < 0)) + fail (i); + } + + +} + + + +static void +test_bin2hex (void) +{ + char stuff[20+1] = ("\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa" + "\xbb\xcc\xdd\xee\xff\x01\x10\x02\xa3"); + char hexstuff[] = "00112233445566778899AABBCCDDEEFF011002A3"; + char buffer[2*20+1]; + char *p; + + p = bin2hex (stuff, 20, buffer); + if (!p) + fail (0); + if (p != buffer) + fail (0); + if (strcmp (buffer, hexstuff)) + fail (0); + + p = bin2hex (stuff, 20, NULL); + if (!p) + fail (0); + else if (strcmp (p, hexstuff)) + fail (0); + xfree (p); + + p = bin2hex (stuff, (size_t)(-1), NULL); + if (p) + fail (0); + else if (errno != ENOMEM) + fail (1); +} + + +static void +test_bin2hexcolon (void) +{ + char stuff[20+1] = ("\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa" + "\xbb\xcc\xdd\xee\xff\x01\x10\x02\xa3"); + char hexstuff[] = ("00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF" + ":01:10:02:A3"); + char buffer[3*20+1]; + char *p; + + p = bin2hexcolon (stuff, 20, buffer); + if (!p) + fail (0); + if (p != buffer) + fail (0); + if (strcmp (buffer, hexstuff)) + fail (0); + + p = bin2hexcolon (stuff, 20, NULL); + if (!p) + fail (0); + else if (strcmp (p, hexstuff)) + fail (0); + xfree (p); + + p = bin2hexcolon (stuff, (size_t)(-1), NULL); + if (p) + fail (0); + else if (errno != ENOMEM) + fail (1); +} + + + +static void +test_hex2str (void) +{ + static struct { + const char *hex; + const char *str; + int len; /* Length of STR. This may included embedded nuls. */ + int off; + int no_alloc_test; + } tests[] = { + /* Simple tests. */ + { "112233445566778899aabbccddeeff1122", + "\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff\x11\x22", + 17, 34 }, + { "112233445566778899aabbccddeeff1122 blah", + "\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff\x11\x22", + 17, 34 }, + { "112233445566778899aabbccddeeff1122\tblah", + "\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff\x11\x22", + 17, 34 }, + { "112233445566778899aabbccddeeff1122\nblah", + "\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff\x11\x22", + 17, 34 }, + /* Valid tests yielding an empty string. */ + { "00", + "", + 1, 2 }, + { "00 x", + "", + 1, 2 }, + { "", + "", + 0, 0 }, + { " ", + "", + 0, 0 }, + /* Test trailing Nul feature. */ + { "112233445566778899aabbccddeeff1100", + "\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff\x11\x00", + 17, 34 }, + { "112233445566778899aabbccddeeff1100 ", + "\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff\x11\x00", + 17, 34 }, + /* Test buffer size. (buffer is of length 20) */ + { "6162636465666768696A6b6c6D6e6f70717273", + "abcdefghijklmnopqrs", + 19, 38 }, + { "6162636465666768696A6b6c6D6e6f7071727300", + "abcdefghijklmnopqrs", + 20, 40 }, + { "6162636465666768696A6b6c6D6e6f7071727374", + NULL, + 0, 0, 1 }, + { "6162636465666768696A6b6c6D6e6f707172737400", + NULL, + 0, 0, 1 }, + { "6162636465666768696A6b6c6D6e6f707172737475", + NULL, + 0, 0, 1 }, + + /* Invalid tests. */ + { "112233445566778899aabbccddeeff1122334", NULL, 0, 0 }, + { "112233445566778899AABBCCDDEEFF1122334", NULL, 0, 0 }, + { "112233445566778899AABBCCDDEEFG11223344", NULL, 0, 0 }, + { "0:0112233445566778899aabbccddeeff11223344", NULL, 0, 0 }, + { "112233445566778899aabbccddeeff11223344:", NULL, 0, 0 }, + { "112233445566778899aabbccddeeff112233445", NULL, 0, 0 }, + { "112233445566778899aabbccddeeff1122334455", NULL, 0, 0, 1 }, + { "112233445566778899aabbccddeeff11223344blah", NULL, 0, 0 }, + { "0", NULL, 0, 0 }, + { "00:", NULL, 0, 0 }, + { "00x", NULL, 0, 0 }, + + { NULL, NULL, 0, 0 } + }; + + int idx; + char buffer[20]; + const char *tail; + size_t count; + char *result; + + for (idx=0; tests[idx].hex; idx++) + { + tail = hex2str (tests[idx].hex, buffer, sizeof buffer, &count); + if (tests[idx].str) + { + /* Good case test. */ + if (!tail) + fail (idx); + else if (strcmp (tests[idx].str, buffer)) + fail (idx); + else if (tail - tests[idx].hex != tests[idx].off) + fail (idx); + else if (tests[idx].len != count) + fail (idx); + } + else + { + /* Bad case test. */ + if (tail) + fail (idx); + } + } + + /* Same tests again using in-place conversion. */ + for (idx=0; tests[idx].hex; idx++) + { + char tmpbuf[100]; + + assert (strlen (tests[idx].hex)+1 < sizeof tmpbuf); + strcpy (tmpbuf, tests[idx].hex); + + /* Note: we still need to use 20 as buffer length because our + tests assume that. */ + tail = hex2str (tmpbuf, tmpbuf, 20, &count); + if (tests[idx].str) + { + /* Good case test. */ + if (!tail) + fail (idx); + else if (strcmp (tests[idx].str, tmpbuf)) + fail (idx); + else if (tail - tmpbuf != tests[idx].off) + fail (idx); + else if (tests[idx].len != count) + fail (idx); + } + else + { + /* Bad case test. */ + if (tail) + fail (idx); + if (strcmp (tmpbuf, tests[idx].hex)) + fail (idx); /* Buffer was modified. */ + } + } + + /* Test the allocation variant. */ + for (idx=0; tests[idx].hex; idx++) + { + if (tests[idx].no_alloc_test) + continue; + + result = hex2str_alloc (tests[idx].hex, &count); + if (tests[idx].str) + { + /* Good case test. */ + if (!result) + fail (idx); + else if (strcmp (tests[idx].str, result)) + fail (idx); + else if (count != tests[idx].off) + fail (idx); + } + else + { + /* Bad case test. */ + if (result) + fail (idx); + } + xfree (result); + } +} + + + + + +int +main (int argc, char **argv) +{ + (void)argc; + (void)argv; + + test_hex2bin (); + test_hexcolon2bin (); + test_bin2hex (); + test_bin2hexcolon (); + test_hex2str (); + + return 0; +} diff --git a/common/t-exechelp.c b/common/t-exechelp.c new file mode 100644 index 0000000..cf967fc --- /dev/null +++ b/common/t-exechelp.c @@ -0,0 +1,188 @@ +/* t-exechelp.c - Module test for exechelp.c + * Copyright (C) 2009 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <assert.h> +#include <unistd.h> + +#include "util.h" +#include "exechelp.h" + +static int verbose; + + +static void +print_open_fds (int *array) +{ + int n; + + if (!verbose) + return; + + for (n=0; array[n] != -1; n++) + ; + printf ("open file descriptors: %d", n); + putchar (' '); + putchar (' '); + putchar ('('); + for (n=0; array[n] != -1; n++) + printf ("%d%s", array[n], array[n+1] == -1?"":" "); + putchar (')'); + putchar ('\n'); +} + + +static int * +xget_all_open_fds (void) +{ + int *array; + + array = get_all_open_fds (); + if (!array) + { + fprintf (stderr, "%s:%d: get_all_open_fds failed: %s\n", + __FILE__, __LINE__, strerror (errno)); + exit (1); + } + return array; +} + + +/* That is a very crude test. To do a proper test we would need to + fork a test process and best return information by some other means + than file descriptors. */ +static void +test_close_all_fds (void) +{ + int max_fd = get_max_fds (); + int *array; + int fd; + int initial_count, count, n; +#if 0 + char buffer[100]; + + snprintf (buffer, sizeof buffer, "/bin/ls -l /proc/%d/fd", (int)getpid ()); + system (buffer); +#endif + + if (verbose) + printf ("max. file descriptors: %d\n", max_fd); + array = xget_all_open_fds (); + print_open_fds (array); + for (initial_count=n=0; array[n] != -1; n++) + initial_count++; + free (array); + + /* Some dups to get more file descriptors and close one. */ + dup (1); + dup (1); + fd = dup (1); + dup (1); + close (fd); + + array = xget_all_open_fds (); + if (verbose) + print_open_fds (array); + for (count=n=0; array[n] != -1; n++) + count++; + if (count != initial_count+3) + { + fprintf (stderr, "%s:%d: dup or close failed\n", + __FILE__, __LINE__); + exit (1); + } + free (array); + + /* Close the non standard ones. */ + close_all_fds (3, NULL); + + /* Get a list to check whether they are all closed. */ + array = xget_all_open_fds (); + if (verbose) + print_open_fds (array); + for (count=n=0; array[n] != -1; n++) + count++; + if (count > initial_count) + { + fprintf (stderr, "%s:%d: not all files were closed\n", + __FILE__, __LINE__); + exit (1); + } + initial_count = count; + free (array); + + /* Now let's check the realloc we use. We do this and the next + tests only if we are allowed to open enought descriptors. */ + if (get_max_fds () > 32) + { + int except[] = { 20, 23, 24, -1 }; + + for (n=initial_count; n < 31; n++) + dup (1); + array = xget_all_open_fds (); + if (verbose) + print_open_fds (array); + free (array); + for (n=0; n < 5; n++) + { + dup (1); + array = xget_all_open_fds (); + if (verbose) + print_open_fds (array); + free (array); + } + + /* Check whether the except list works. */ + close_all_fds (3, except); + array = xget_all_open_fds (); + if (verbose) + print_open_fds (array); + for (count=n=0; array[n] != -1; n++) + count++; + free (array); + + if (count != initial_count + DIM(except)-1) + { + fprintf (stderr, "%s:%d: close_all_fds failed\n", + __FILE__, __LINE__); + exit (1); + } + } + +} + + +int +main (int argc, char **argv) +{ + if (argc) + { argc--; argv++; } + if (argc && !strcmp (argv[0], "--verbose")) + { + verbose = 1; + argc--; argv++; + } + + test_close_all_fds (); + + return 0; +} diff --git a/common/t-exectool.c b/common/t-exectool.c new file mode 100644 index 0000000..e1fffdc --- /dev/null +++ b/common/t-exectool.c @@ -0,0 +1,237 @@ +/* t-exectool.c - Module test for exectool.c + * Copyright (C) 2016 g10 Code GmbH + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <assert.h> +#include <unistd.h> + +#include "util.h" +#include "exectool.h" + +static int verbose; + +#define fail(msg, err) \ + do { fprintf (stderr, "%s:%d: %s failed: %s\n", \ + __FILE__,__LINE__, (msg), gpg_strerror (err)); \ + exit (1); \ + } while(0) + +static void +test_executing_true (void) +{ + gpg_error_t err; + const char *pgmname = "/bin/true"; + const char *alt_pgmname = "/usr/bin/true"; + const char *argv[] = { NULL, NULL }; + char *result; + size_t len; + + /* Fixme: We should use gpgrt_access here. */ + if (access (pgmname, X_OK)) + { + if (access (alt_pgmname, X_OK)) + { + fprintf (stderr, "skipping test: %s not executable: %s\n", + pgmname, strerror (errno)); + return; + } + pgmname = alt_pgmname; + } + + if (verbose) + fprintf (stderr, "Executing %s...\n", pgmname); + + err = gnupg_exec_tool (pgmname, argv, "", &result, &len); + if (err) + fail ("gnupg_exec_tool", err); + + assert (result); + assert (len == 0); + free (result); +} + +static void +test_executing_false (void) +{ + gpg_error_t err; + const char *pgmname = "/bin/false"; + const char *alt_pgmname = "/usr/bin/false"; + const char *argv[] = { NULL, NULL }; + char *result; + size_t len; + + if (access (pgmname, X_OK)) + { + if (access (alt_pgmname, X_OK)) + { + fprintf (stderr, "skipping test: %s not executable: %s\n", + pgmname, strerror (errno)); + return; + } + pgmname = alt_pgmname; + } + + if (verbose) + fprintf (stderr, "Executing %s...\n", pgmname); + + err = gnupg_exec_tool (pgmname, argv, "", &result, &len); + assert (err == GPG_ERR_GENERAL); +} + + +static void +test_executing_cat (const char *vector) +{ + gpg_error_t err; + const char *argv[] = { "/bin/cat", NULL }; + char *result; + size_t len; + + if (access (argv[0], X_OK)) + { + fprintf (stderr, "skipping test: %s not executable: %s\n", + argv[0], strerror (errno)); + return; + } + + if (verbose) + fprintf (stderr, "Executing %s...\n", argv[0]); + + err = gnupg_exec_tool (argv[0], &argv[1], vector, &result, &len); + if (err) + fail ("gnupg_exec_tool", err); + + assert (result); + + /* gnupg_exec_tool returns the correct length... */ + assert (len == strlen (vector)); + /* ... but 0-terminates data for ease of use. */ + assert (result[len] == 0); + + assert (strcmp (result, vector) == 0); + free (result); +} + + +static void +test_catting_cat (void) +{ + gpg_error_t err; + const char *argv[] = { "/bin/cat", "/bin/cat", NULL }; + char *result; + size_t len; + estream_t in; + char *reference, *p; + size_t reference_len; + + if (access (argv[0], X_OK)) + { + fprintf (stderr, "skipping test: %s not executable: %s\n", + argv[0], strerror (errno)); + return; + } + + in = es_fopen (argv[1], "r"); + if (in == NULL) + { + fprintf (stderr, "skipping test: could not open %s: %s\n", + argv[1], strerror (errno)); + return; + } + + err = es_fseek (in, 0L, SEEK_END); + if (err) + { + fprintf (stderr, "skipping test: could not seek in %s: %s\n", + argv[1], gpg_strerror (err)); + return; + } + + reference_len = es_ftell (in); + err = es_fseek (in, 0L, SEEK_SET); + assert (!err || !"rewinding failed"); + + reference = malloc (reference_len); + assert (reference || !"allocating reference buffer failed"); + + for (p = reference; p - reference < reference_len; ) + { + size_t bytes_read, left; + left = reference_len - (p - reference); + if (left > 4096) + left = 4096; + err = es_read (in, p, left, &bytes_read); + if (err) + { + fprintf (stderr, "error reading %s: %s", + argv[1], gpg_strerror (err)); + exit (1); + } + + p += bytes_read; + } + es_fclose (in); + + if (verbose) + fprintf (stderr, "Executing %s %s...\n", argv[0], argv[1]); + + err = gnupg_exec_tool (argv[0], &argv[1], "", &result, &len); + if (err) + fail ("gnupg_exec_tool", err); + + assert (result); + + /* gnupg_exec_tool returns the correct length... */ + assert (len == reference_len); + assert (memcmp (result, reference, reference_len) == 0); + free (reference); + free (result); +} + + +int +main (int argc, char **argv) +{ + int i; + char binjunk[256]; + + if (argc) + { argc--; argv++; } + if (argc && !strcmp (argv[0], "--verbose")) + { + verbose = 1; + argc--; argv++; + } + + test_executing_true (); + test_executing_false (); + test_executing_cat ("Talking to myself here..."); + + for (i = 0; i < 255 /* one less */; i++) + binjunk[i] = i + 1; /* avoid 0 */ + binjunk[255] = 0; + + test_executing_cat (binjunk); + test_catting_cat (); + + return 0; +} diff --git a/common/t-gettime.c b/common/t-gettime.c new file mode 100644 index 0000000..13cb1a2 --- /dev/null +++ b/common/t-gettime.c @@ -0,0 +1,277 @@ +/* t-gettime.c - Module test for gettime.c + * Copyright (C) 2007, 2011 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#ifdef HAVE_STDINT_H +# include <stdint.h> +#endif + +#include "util.h" + +/* In case we do not have stdint.h and no other version of that + * conversion macro provide shortcut it. */ +#ifndef UINTMAX_C +#define UINTMAX_C (c) (c) +#endif + +#define pass() do { ; } while(0) +#define fail(a) do { fprintf (stderr, "%s:%d: test %d failed\n",\ + __FILE__,__LINE__, (a)); \ + errcount++; \ + } while(0) + +static int verbose; +static int errcount; +#define INVALID ((time_t)(-1)) + + +static void +test_isotime2epoch (void) +{ + struct { const char *string; time_t expected; } array [] = { + { "19700101T000001", 1 }, + { "19700101T235959", 86399 }, + { "19980815T143712", 903191832 }, + { "19700101T000000", 0 }, + { "19691231T235959", INVALID }, + { "19000101T000000", INVALID }, + { "", INVALID }, + { "19000101T00000", INVALID }, + { "20010101t123456", INVALID }, + { "20010101T123456", 978352496 }, + { "20070629T160000", 1183132800 }, + { "20070629T160000:", 1183132800 }, + { "20070629T160000,", 1183132800 }, + { "20070629T160000 ", 1183132800 }, + { "20070629T160000\n", 1183132800 }, + { "20070629T160000.", INVALID }, +#if SIZEOF_TIME_T > 4 + { "21060207T062815", (time_t)UINTMAX_C(0x0ffffffff) }, + { "21060207T062816", (time_t)UINTMAX_C(0x100000000) }, + { "21060207T062817", (time_t)UINTMAX_C(0x100000001) }, + { "21060711T120001", (time_t)UINTMAX_C(4308292801) }, +#endif /*SIZEOF_TIME_T > 4*/ + { NULL, 0 } + }; + int idx; + time_t val; + gnupg_isotime_t tbuf; + + for (idx=0; array[idx].string; idx++) + { + val = isotime2epoch (array[idx].string); + if (val != array[idx].expected ) + { + fail (idx); + if (verbose) + fprintf (stderr, "string '%s' exp: %ld got: %ld\n", + array[idx].string, (long)array[idx].expected, + (long)val); + } + if (array[idx].expected != INVALID) + { + epoch2isotime (tbuf, val); + if (strlen (tbuf) != 15) + { + if (verbose) + fprintf (stderr, "string '%s', time-t %ld, revert: '%s'\n", + array[idx].string, (long)val, tbuf); + fail (idx); + } + if (strncmp (array[idx].string, tbuf, 15)) + fail (idx); + } + } +} + + + +static void +test_string2isotime (void) +{ + struct { + const char *string; + size_t result; + const char *expected; + } array [] = { + { "19700101T000001", 15, "19700101T000001" }, + { "19700101T235959", 15, "19700101T235959" }, + { "19980815T143712", 15, "19980815T143712" }, + { "19700101T000000", 15, "19700101T000000" }, + { "19691231T235959", 15, "19691231T235959" }, + { "19000101T000000", 15, "19000101T000000" }, + { "", 0, "" }, + { "19000101T00000", 0, "" }, + { "20010101t123456", 0, "" }, + { "20010101T123456", 15, "20010101T123456" }, + { "20070629T160000", 15, "20070629T160000" }, + { "20070629T160000:", 15, "20070629T160000" }, + { "20070629T160000,", 15, "20070629T160000" }, + { "20070629T160000 ", 15, "20070629T160000" }, + { "20070629T160000\n", 15,"20070629T160000" }, + { "20070629T160000.", 0, "" }, + { "1066-03-20", 10, "10660320T000000" }, + { "1066-03-20,", 10, "10660320T000000" }, + { "1066-03-20:", 0, "" }, + { "1066-03-20 00", 13, "10660320T000000" }, + { "1066-03-20 01", 13, "10660320T010000" }, + { "1066-03-20 23", 13, "10660320T230000" }, + { "1066-03-20 24", 0, "" }, + { "1066-03-20 00:", 0, "" }, + { "1066-03-20 00:3", 0, "" }, + { "1066-03-20 00:31", 16, "10660320T003100" }, + { "1066-03-20 00:31:47", 19, "10660320T003147" }, + { "1066-03-20 00:31:47 ", 19, "10660320T003147" }, + { "1066-03-20 00:31:47,", 19, "10660320T003147" }, + { "1066-03-20 00:31:47:", 0, "" }, + { "1-03-20 00:31:47:", 0, "" }, + { "10-03-20 00:31:47:", 0, "" }, + { "106-03-20 00:31:47:", 0, "" }, + { "1066-23-20 00:31:47:", 0, "" }, + { "1066-00-20 00:31:47:", 0, "" }, + { "1066-0-20 00:31:47:", 0, "" }, + { "1066-01-2 00:31:47:", 0, "" }, + { "1066-01-2 00:31:47:", 0, "" }, + { "1066-01-32 00:31:47:", 0, "" }, + { "1066-01-00 00:31:47:", 0, "" }, + { "1066-03-20 00:31:47:",11, "10660320T000000" }, + { "1066-03-2000:31:47:", 0, "" }, + { "10666-03-20 00:31:47:", 0, "" }, + { NULL, 0 } + }; + int idx; + size_t result; + gnupg_isotime_t tbuf; + + for (idx=0; array[idx].string; idx++) + { + result = string2isotime (tbuf, array[idx].string); + if (result != array[idx].result) + { + fail (idx); + if (verbose) + fprintf (stderr, "string '%s' expected: %d, got: %d\n", + array[idx].string, (int)array[idx].result, (int)result); + } + else if (result && strlen (tbuf) != 15) + { + fail (idx); + if (verbose) + fprintf (stderr, "string '%s' invalid isotime returned\n", + array[idx].string); + } + else if (result && strcmp (array[idx].expected, tbuf)) + { + fail (idx); + if (verbose) + fprintf (stderr, "string '%s' bad isotime '%s' returned\n", + array[idx].string, tbuf); + } + } +} + + +static void +test_isodate_human_to_tm (void) +{ + struct { + const char *string; + int okay; + int year, mon, mday; + } array [] = { + { "1970-01-01", 1, 1970, 1, 1 }, + { "1970-02-01", 1, 1970, 2, 1 }, + { "1970-12-31", 1, 1970, 12, 31 }, + { "1971-01-01", 1, 1971, 1, 1 }, + { "1998-08-15", 1, 1998, 8, 15 }, + { "2015-04-10", 1, 2015, 4, 10 }, + { "2015-04-10 11:30",1, 2015, 4, 10 }, + { "1969-12-31", 0, 0, 0, 0 }, + { "1900-01-01", 0, 0, 0, 0 }, + { "", 0, 0, 0, 0 }, + { "1970-12-32", 0, 0, 0, 0 }, + { "1970-13-01", 0, 0, 0, 0 }, + { "1970-01-00", 0, 0, 0, 0 }, + { "1970-00-01", 0, 0, 0, 0 }, + { "1970-00-01", 0, 0, 0, 0 }, + { "1970", 0, 0, 0, 0 }, + { "1970-01", 0, 0, 0, 0 }, + { "1970-01-1", 0, 0, 0, 0 }, + { "1970-1--01", 0, 0, 0, 0 }, + { "1970-01-01,", 1, 1970, 1, 1 }, + { "1970-01-01 ", 1, 1970, 1, 1 }, + { "1970-01-01\t", 1, 1970, 1, 1 }, + { "1970-01-01;", 0, 0, 0, 0 }, + { "1970-01-01:", 0, 0, 0, 0 }, + { "1970_01-01", 0, 0, 0, 0 }, + { "1970-01_01", 0, 0, 0, 0 }, + { NULL, 0 } + }; + int idx; + int okay; + struct tm tmbuf; + + for (idx=0; array[idx].string; idx++) + { + okay = !isodate_human_to_tm (array[idx].string, &tmbuf); + if (okay != array[idx].okay) + { + fail (idx); + if (verbose) + fprintf (stderr, "string '%s' expected: %d, got: %d\n", + array[idx].string, (int)array[idx].okay, okay); + } + else if (!okay) + ; + else if (tmbuf.tm_year + 1900 != array[idx].year + || tmbuf.tm_mon +1 != array[idx].mon + || tmbuf.tm_mday != array[idx].mday) + { + fail (idx); + if (verbose) + fprintf (stderr, "string '%s' returned %04d-%02d-%02d\n", + array[idx].string, + tmbuf.tm_year + 1900, tmbuf.tm_mon + 1, tmbuf.tm_mday); + } + else if (tmbuf.tm_sec || tmbuf.tm_min || tmbuf.tm_hour + || tmbuf.tm_isdst != -1) + { + fail (idx); + if (verbose) + fprintf (stderr, "string '%s' returned bad time part\n", + array[idx].string); + } + } +} + + +int +main (int argc, char **argv) +{ + if (argc > 1 && !strcmp (argv[1], "--verbose")) + verbose = 1; + + test_isotime2epoch (); + test_string2isotime (); + test_isodate_human_to_tm (); + + return !!errcount; +} diff --git a/common/t-helpfile.c b/common/t-helpfile.c new file mode 100644 index 0000000..0e2c79f --- /dev/null +++ b/common/t-helpfile.c @@ -0,0 +1,66 @@ +/* t-helpfile.c - Module test for helpfile.c + * Copyright (C) 2007 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> + +#include "util.h" +#include "i18n.h" + +/* #define pass() do { ; } while(0) */ +/* #define fail(a) do { fprintf (stderr, "%s:%d: test %d failed\n",\ */ +/* __FILE__,__LINE__, (a)); \ */ +/* errcount++; \ */ +/* } while(0) */ + +static int verbose; +static int errcount; + + + +int +main (int argc, char **argv) +{ + char *result; + + if (argc) + { argc--; argv++; } + i18n_init (); + if (argc && !strcmp (argv[0], "--verbose")) + { + verbose = 1; + argc--; argv++; + } + + result = gnupg_get_help_string (argc? argv[0]:NULL, 0); + if (!result) + { + fprintf (stderr, + "Error: nothing found for '%s'\n", argc?argv[0]:"(null)"); + errcount++; + } + else + { + printf ("key '%s' result='%s'\n", argc?argv[0]:"(null)", result); + xfree (result); + } + + return !!errcount; +} diff --git a/common/t-iobuf.c b/common/t-iobuf.c new file mode 100644 index 0000000..bdeab99 --- /dev/null +++ b/common/t-iobuf.c @@ -0,0 +1,394 @@ +#include <config.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> +#include <stdlib.h> + +#include "iobuf.h" +#include "stringhelp.h" + +/* Return every other byte. In particular, reads two bytes, returns + the second one. */ +static int +every_other_filter (void *opaque, int control, + iobuf_t chain, byte *buf, size_t *len) +{ + (void) opaque; + + if (control == IOBUFCTRL_DESC) + { + mem2str (buf, "every_other_filter", *len); + } + if (control == IOBUFCTRL_UNDERFLOW) + { + int c = iobuf_readbyte (chain); + int c2; + if (c == -1) + c2 = -1; + else + c2 = iobuf_readbyte (chain); + + /* printf ("Discarding %d (%c); return %d (%c)\n", c, c, c2, c2); */ + + if (c2 == -1) + { + *len = 0; + return -1; + } + + *buf = c2; + *len = 1; + + return 0; + } + + return 0; +} + +static int +double_filter (void *opaque, int control, + iobuf_t chain, byte *buf, size_t *len) +{ + (void) opaque; + + if (control == IOBUFCTRL_DESC) + { + mem2str (buf, "double_filter", *len); + } + if (control == IOBUFCTRL_FLUSH) + { + int i; + + for (i = 0; i < *len; i ++) + { + int rc; + + rc = iobuf_writebyte (chain, buf[i]); + if (rc) + return rc; + rc = iobuf_writebyte (chain, buf[i]); + if (rc) + return rc; + } + } + + return 0; +} + +struct content_filter_state +{ + int pos; + int len; + const char *buffer; +}; + +static struct content_filter_state * +content_filter_new (const char *buffer) +{ + struct content_filter_state *state + = malloc (sizeof (struct content_filter_state)); + + state->pos = 0; + state->len = strlen (buffer); + state->buffer = buffer; + + return state; +} + +static int +content_filter (void *opaque, int control, + iobuf_t chain, byte *buf, size_t *len) +{ + struct content_filter_state *state = opaque; + + (void) chain; + + if (control == IOBUFCTRL_UNDERFLOW) + { + int remaining = state->len - state->pos; + int toread = *len; + assert (toread > 0); + + if (toread > remaining) + toread = remaining; + + memcpy (buf, &state->buffer[state->pos], toread); + + state->pos += toread; + + *len = toread; + + if (toread == 0) + return -1; + return 0; + } + + return 0; +} + +int +main (int argc, char *argv[]) +{ + (void) argc; + (void) argv; + + /* A simple test to make sure filters work. We use a static buffer + and then add a filter in front of it that returns every other + character. */ + { + char *content = "0123456789abcdefghijklm"; + iobuf_t iobuf; + int c; + int n; + int rc; + + iobuf = iobuf_temp_with_content (content, strlen (content)); + rc = iobuf_push_filter (iobuf, every_other_filter, NULL); + assert (rc == 0); + + n = 0; + while ((c = iobuf_readbyte (iobuf)) != -1) + { + /* printf ("%d: %c\n", n + 1, (char) c); */ + assert (content[2 * n + 1] == c); + n ++; + } + /* printf ("Got EOF after reading %d bytes (content: %d)\n", */ + /* n, strlen (content)); */ + assert (n == strlen (content) / 2); + + iobuf_close (iobuf); + } + + /* A simple test to check buffering. Make sure that when we add a + filter to a pipeline, any buffered data gets processed by the */ + { + char *content = "0123456789abcdefghijklm"; + iobuf_t iobuf; + int c; + int n; + int rc; + int i; + + iobuf = iobuf_temp_with_content (content, strlen (content)); + + n = 0; + for (i = 0; i < 10; i ++) + { + c = iobuf_readbyte (iobuf); + assert (content[i] == c); + n ++; + } + + rc = iobuf_push_filter (iobuf, every_other_filter, NULL); + assert (rc == 0); + + while ((c = iobuf_readbyte (iobuf)) != -1) + { + /* printf ("%d: %c\n", n + 1, (char) c); */ + assert (content[2 * (n - 5) + 1] == c); + n ++; + } + assert (n == 10 + (strlen (content) - 10) / 2); + + iobuf_close (iobuf); + } + + + /* A simple test to check that iobuf_read_line works. */ + { + /* - 3 characters plus new line + - 4 characters plus new line + - 5 characters plus new line + - 5 characters, no new line + */ + char *content = "abc\ndefg\nhijkl\nmnopq"; + iobuf_t iobuf; + byte *buffer; + unsigned size; + unsigned max_len; + int n; + + iobuf = iobuf_temp_with_content (content, strlen(content)); + + /* We read a line with 3 characters plus a newline. If we + allocate a buffer that is 5 bytes long, then no reallocation + should be required. */ + size = 5; + buffer = malloc (size); + assert (buffer); + max_len = 100; + n = iobuf_read_line (iobuf, &buffer, &size, &max_len); + assert (n == 4); + assert (strcmp (buffer, "abc\n") == 0); + assert (size == 5); + assert (max_len == 100); + free (buffer); + + /* We now read a line with 4 characters plus a newline. This + requires 6 bytes of storage. We pass a buffer that is 5 bytes + large and we allow the buffer to be grown. */ + size = 5; + buffer = malloc (size); + max_len = 100; + n = iobuf_read_line (iobuf, &buffer, &size, &max_len); + assert (n == 5); + assert (strcmp (buffer, "defg\n") == 0); + assert (size >= 6); + /* The string shouldn't have been truncated (max_len == 0). */ + assert (max_len == 100); + free (buffer); + + /* We now read a line with 5 characters plus a newline. This + requires 7 bytes of storage. We pass a buffer that is 5 bytes + large and we don't allow the buffer to be grown. */ + size = 5; + buffer = malloc (size); + max_len = 5; + n = iobuf_read_line (iobuf, &buffer, &size, &max_len); + assert (n == 4); + /* Note: the string should still have a trailing \n. */ + assert (strcmp (buffer, "hij\n") == 0); + assert (size == 5); + /* The string should have been truncated (max_len == 0). */ + assert (max_len == 0); + free (buffer); + + /* We now read a line with 6 characters without a newline. This + requires 7 bytes of storage. We pass a NULL buffer and we + don't allow the buffer to be grown larger than 5 bytes. */ + size = 5; + buffer = NULL; + max_len = 5; + n = iobuf_read_line (iobuf, &buffer, &size, &max_len); + assert (n == 4); + /* Note: the string should still have a trailing \n. */ + assert (strcmp (buffer, "mno\n") == 0); + assert (size == 5); + /* The string should have been truncated (max_len == 0). */ + assert (max_len == 0); + free (buffer); + + iobuf_close (iobuf); + } + + { + /* - 10 characters, EOF + - 17 characters, EOF + */ + char *content = "abcdefghijklmnopq"; + char *content2 = "0123456789"; + iobuf_t iobuf; + int rc; + int c; + int n; + int lastc = 0; + struct content_filter_state *state; + + iobuf = iobuf_temp_with_content (content, strlen(content)); + rc = iobuf_push_filter (iobuf, + content_filter, + state=content_filter_new (content2)); + assert (rc == 0); + + n = 0; + while (1) + { + c = iobuf_readbyte (iobuf); + if (c == -1 && lastc == -1) + { + /* printf("Two EOFs in a row. Done.\n"); */ + assert (n == 27); + break; + } + + lastc = c; + + if (c == -1) + { + /* printf("After %d bytes, got EOF.\n", n); */ + assert (n == 10 || n == 27); + } + else + { + n ++; + /* printf ("%d: '%c' (%d)\n", n, c, c); */ + } + } + + iobuf_close (iobuf); + free (state); + } + + /* Write some data to a temporary filter. Push a new filter. The + already written data should not be processed by the new + filter. */ + { + iobuf_t iobuf; + int rc; + char *content = "0123456789"; + char *content2 = "abc"; + char buffer[4096]; + int n; + + iobuf = iobuf_temp (); + assert (iobuf); + + rc = iobuf_write (iobuf, content, strlen (content)); + assert (rc == 0); + + rc = iobuf_push_filter (iobuf, double_filter, NULL); + assert (rc == 0); + + /* Include a NUL. */ + rc = iobuf_write (iobuf, content2, strlen (content2) + 1); + assert (rc == 0); + + n = iobuf_temp_to_buffer (iobuf, buffer, sizeof (buffer)); +#if 0 + printf ("Got %d bytes\n", n); + printf ("buffer: `"); + fwrite (buffer, n, 1, stdout); + fputc ('\'', stdout); + fputc ('\n', stdout); +#endif + + assert (n == strlen (content) + 2 * (strlen (content2) + 1)); + assert (strcmp (buffer, "0123456789aabbcc") == 0); + + iobuf_close (iobuf); + } + + { + iobuf_t iobuf; + int rc; + char content[] = "0123456789"; + int n; + int c; + char buffer[10]; + + assert (sizeof buffer == sizeof content - 1); + + iobuf = iobuf_temp_with_content (content, strlen (content)); + assert (iobuf); + + rc = iobuf_push_filter (iobuf, every_other_filter, NULL); + assert (rc == 0); + rc = iobuf_push_filter (iobuf, every_other_filter, NULL); + assert (rc == 0); + + for (n = 0; (c = iobuf_get (iobuf)) != -1; n ++) + { + /* printf ("%d: `%c'\n", n, c); */ + buffer[n] = c; + } + + assert (n == 2); + assert (buffer[0] == '3'); + assert (buffer[1] == '7'); + + iobuf_close (iobuf); + } + + return 0; +} diff --git a/common/t-mapstrings.c b/common/t-mapstrings.c new file mode 100644 index 0000000..7dc6221 --- /dev/null +++ b/common/t-mapstrings.c @@ -0,0 +1,126 @@ +/* t-mapstrings.c - Regression tests for mapstrings.c + * Copyright (C) 2014 Werner Koch + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "t-support.h" +#include "stringhelp.h" + +static void +test_map_static_macro_string (void) +{ + static struct { + const char *string; + const char *expected; + const char *lastresult; + } tests[] = { + { "@GPG@ (@GNUPG@)", + GPG_NAME " (" GNUPG_NAME ")" }, + { "@GPG@(@GNUPG@)", + GPG_NAME "(" GNUPG_NAME ")" }, + { "@GPG@@GNUPG@", + GPG_NAME GNUPG_NAME }, + { " @GPG@@GNUPG@", + " " GPG_NAME GNUPG_NAME }, + { " @GPG@@GNUPG@ ", + " " GPG_NAME GNUPG_NAME " " }, + { " @GPG@GNUPG@ ", + " " GPG_NAME "GNUPG@ " }, + { " @ GPG@GNUPG@ ", + " @ GPG" GNUPG_NAME " " }, + { "--@GPGTAR@", + "--" GPGTAR_NAME } + }; + int testno; + const char *result; + + for (testno=0; testno < DIM(tests); testno++) + { + result = map_static_macro_string (tests[testno].string); + if (!result) + fail (testno); + else if (strcmp (result, tests[testno].expected)) + fail (testno); + if (!tests[testno].lastresult) + tests[testno].lastresult = result; + } + + /* A second time to check that the same string is been returned. */ + for (testno=0; testno < DIM(tests); testno++) + { + result = map_static_macro_string (tests[testno].string); + if (!result) + fail (testno); + else if (strcmp (result, tests[testno].expected)) + fail (testno); + if (result != tests[testno].lastresult) + fail (testno); + } +} + + +static void +test_map_static_strings (void) +{ + const char *s, *s1; + + s1 = map_static_strings ("mydomain", 0, 42, + "This", " ", "is", " ", "my"," ","string", NULL); + if (strcmp (s1, "This is my string")) + fail (1); + s = map_static_strings ("mydomain", 0, 42, + "This", " ", "is", " ", "my"," ","string", NULL); + if (strcmp (s1, s)) + fail (2); + s = map_static_strings ("mydomain", 0, 42, "foo", NULL); + if (strcmp (s1, s)) + fail (3); + s = map_static_strings ("mydomain", 1, 42, "foo 1.42", NULL); + if (!strcmp (s1, s)) + fail (4); + s = map_static_strings ("xdomain", 1, 42, "foo 1.42 other domain", NULL); + if (!strcmp (s1, s)) + fail (5); +} + + +int +main (int argc, char **argv) +{ + (void)argc; + (void)argv; + + test_map_static_macro_string (); + test_map_static_strings (); + + return 0; +} diff --git a/common/t-mbox-util.c b/common/t-mbox-util.c new file mode 100644 index 0000000..fb1ac12 --- /dev/null +++ b/common/t-mbox-util.c @@ -0,0 +1,156 @@ +/* t-mbox-util.c - Module test for mbox-util.c + * Copyright (C) 2015 Werner Koch + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "util.h" +#include "mbox-util.h" + +#define pass() do { ; } while(0) +#define fail(a) do { fprintf (stderr, "%s:%d: test %d failed\n",\ + __FILE__,__LINE__, (a)); \ + exit (1); \ + } while(0) + + +static void +run_mbox_test (void) +{ + static struct + { + const char *userid; + const char *mbox; + } testtbl[] = + { + { "Werner Koch <wk@gnupg.org>", "wk@gnupg.org" }, + { "<wk@gnupg.org>", "wk@gnupg.org" }, + { "wk@gnupg.org", "wk@gnupg.org" }, + { "wk@gnupg.org ", NULL }, + { " wk@gnupg.org", NULL }, + { "Werner Koch (test) <wk@gnupg.org>", "wk@gnupg.org" }, + { "Werner Koch <wk@gnupg.org> (test)", "wk@gnupg.org" }, + { "Werner Koch <wk@gnupg.org (test)", NULL }, + { "Werner Koch <wk@gnupg.org >", NULL }, + { "Werner Koch <wk@gnupg.org", NULL }, + { "", NULL }, + { "@", NULL }, + { "bar <>", NULL }, + { "<foo@example.org>", "foo@example.org" }, + { "<foo.@example.org>", "foo.@example.org" }, + { "<.foo.@example.org>", ".foo.@example.org" }, + { "<foo..@example.org>", "foo..@example.org" }, + { "<foo..bar@example.org>", "foo..bar@example.org" }, + { "<foo@example.org.>", NULL }, + { "<foo@example..org>", NULL }, + { "<foo@.>", NULL }, + { "<@example.org>", NULL }, + { "<foo@@example.org>", NULL }, + { "<@foo@example.org>", NULL }, + { "<foo@example.org> ()", "foo@example.org" }, + { "<fo()o@example.org> ()", "fo()o@example.org" }, + { "<fo()o@example.org> ()", "fo()o@example.org" }, + { "fo()o@example.org", NULL}, + { "Mr. Foo <foo@example.org><bar@example.net>", "foo@example.org"}, + { NULL, NULL } + }; + int idx; + + for (idx=0; testtbl[idx].userid; idx++) + { + char *mbox = mailbox_from_userid (testtbl[idx].userid); + + if (!testtbl[idx].mbox) + { + if (mbox) + fail (idx); + } + else if (!mbox) + fail (idx); + else if (strcmp (mbox, testtbl[idx].mbox)) + fail (idx); + + xfree (mbox); + } +} + + +static void +run_dns_test (void) +{ + static struct + { + const char *name; + int valid; + } testtbl[] = + { + { "", 0 }, + { ".", 0 }, + { "-", 0 }, + { "a", 1 }, + { "ab", 1 }, + { "a.b", 1 }, + { "a.b.", 1 }, + { ".a.b.", 0 }, + { ".a.b", 0 }, + { "-a.b", 0 }, + { "a-.b", 0 }, + { "a.-b", 0 }, + { "a.b-", 0 }, + { "a.b-.", 0 }, + { "a..b", 0 }, + { "ab.c", 1 }, + { "a-b.c", 1 }, + { "a-b-.c", 0 }, + { "-a-b.c", 0 }, + { "example.org", 1 }, + { "x.example.org", 1 }, + { "xy.example.org", 1 }, + { "Xy.example.org", 1 }, + { "-Xy.example.org", 0 }, + { "Xy.example-.org", 0 }, + { "foo.example.org..", 0 }, + { "foo.example.org.", 1 }, + { ".foo.example.org.", 0 }, + { "..foo.example.org.", 0 }, + { NULL, 0 } + }; + int idx; + + for (idx=0; testtbl[idx].name; idx++) + { + if (is_valid_domain_name (testtbl[idx].name) != testtbl[idx].valid) + fail (idx); + } +} + + +int +main (int argc, char **argv) +{ + (void)argc; + (void)argv; + + run_mbox_test (); + run_dns_test (); + + return 0; +} diff --git a/common/t-name-value.c b/common/t-name-value.c new file mode 100644 index 0000000..57f685f --- /dev/null +++ b/common/t-name-value.c @@ -0,0 +1,593 @@ +/* t-name-value.c - Module test for name-value.c + * Copyright (C) 2016 g10 Code GmbH + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <assert.h> +#include <unistd.h> +#include <sys/stat.h> + +#include "util.h" +#include "name-value.h" + +static int verbose; +static int private_key_mode; + + +static nvc_t +my_nvc_new (void) +{ + if (private_key_mode) + return nvc_new_private_key (); + else + return nvc_new (); +} + + +void +test_getting_values (nvc_t pk) +{ + nve_t e; + + e = nvc_lookup (pk, "Comment:"); + assert (e); + + /* Names are case-insensitive. */ + e = nvc_lookup (pk, "comment:"); + assert (e); + e = nvc_lookup (pk, "COMMENT:"); + assert (e); + + e = nvc_lookup (pk, "SomeOtherName:"); + assert (e); +} + + +void +test_key_extraction (nvc_t pk) +{ + gpg_error_t err; + gcry_sexp_t key; + + if (private_key_mode) + { + err = nvc_get_private_key (pk, &key); + assert (err == 0); + assert (key); + + if (verbose) + gcry_sexp_dump (key); + + gcry_sexp_release (key); + } + else + { + err = nvc_get_private_key (pk, &key); + assert (gpg_err_code (err) == GPG_ERR_MISSING_KEY); + } +} + + +void +test_iteration (nvc_t pk) +{ + int i; + nve_t e; + + i = 0; + for (e = nvc_first (pk); e; e = nve_next (e)) + i++; + assert (i == 4); + + i = 0; + for (e = nvc_lookup (pk, "Comment:"); + e; + e = nve_next_value (e, "Comment:")) + i++; + assert (i == 3); +} + + +void +test_whitespace (nvc_t pk) +{ + nve_t e; + + e = nvc_lookup (pk, "One:"); + assert (e); + assert (strcmp (nve_value (e), "WithoutWhitespace") == 0); + + e = nvc_lookup (pk, "Two:"); + assert (e); + assert (strcmp (nve_value (e), "With Whitespace") == 0); + + e = nvc_lookup (pk, "Three:"); + assert (e); + assert (strcmp (nve_value (e), + "Blank lines in continuations encode newlines.\n" + "Next paragraph.") == 0); +} + + +struct +{ + char *value; + void (*test_func) (nvc_t); +} tests[] = + { + { + "# This is a comment followed by an empty line\n" + "\n", + NULL, + }, + { + "# This is a comment followed by two empty lines, Windows style\r\n" + "\r\n" + "\r\n", + NULL, + }, + { + "# Some name,value pairs\n" + "Comment: Some comment.\n" + "SomeOtherName: Some value.\n", + test_getting_values, + }, + { + " # Whitespace is preserved as much as possible\r\n" + "Comment:Some comment.\n" + "SomeOtherName: Some value. \n", + test_getting_values, + }, + { + "# Values may be continued in the next line as indicated by leading\n" + "# space\n" + "Comment: Some rather long\n" + " comment that is continued in the next line.\n" + "\n" + " Blank lines with or without whitespace are allowed within\n" + " continuations to allow paragraphs.\n" + "SomeOtherName: Some value.\n", + test_getting_values, + }, + { + "# Names may be given multiple times forming an array of values\n" + "Comment: Some comment, element 0.\n" + "Comment: Some comment, element 1.\n" + "Comment: Some comment, element 2.\n" + "SomeOtherName: Some value.\n", + test_iteration, + }, + { + "# One whitespace at the beginning of a continuation is swallowed.\n" + "One: Without\n" + " Whitespace\n" + "Two: With\n" + " Whitespace\n" + "Three: Blank lines in continuations encode newlines.\n" + "\n" + " Next paragraph.\n", + test_whitespace, + }, + { + "Description: Key to sign all GnuPG released tarballs.\n" + " The key is actually stored on a smart card.\n" + "Use-for-ssh: yes\n" + "OpenSSH-cert: long base64 encoded string wrapped so that this\n" + " key file can be easily edited with a standard editor.\n" + "Key: (shadowed-private-key\n" + " (rsa\n" + " (n #00AA1AD2A55FD8C8FDE9E1941772D9CC903FA43B268CB1B5A1BAFDC900\n" + " 2961D8AEA153424DC851EF13B83AC64FBE365C59DC1BD3E83017C90D4365B4\n" + " 83E02859FC13DB5842A00E969480DB96CE6F7D1C03600392B8E08EF0C01FC7\n" + " 19F9F9086B25AD39B4F1C2A2DF3E2BE317110CFFF21D4A11455508FE407997\n" + " 601260816C8422297C0637BB291C3A079B9CB38A92CE9E551F80AA0EBF4F0E\n" + " 72C3F250461E4D31F23A7087857FC8438324A013634563D34EFDDCBF2EA80D\n" + " F9662C9CCD4BEF2522D8BDFED24CEF78DC6B309317407EAC576D889F88ADA0\n" + " 8C4FFB480981FB68C5C6CA27503381D41018E6CDC52AAAE46B166BDC10637A\n" + " E186A02BA2497FDC5D1221#)\n" + " (e #00010001#)\n" + " (shadowed t1-v1\n" + " (#D2760001240102000005000011730000# OPENPGP.1)\n" + " )))\n", + test_key_extraction, + }, + }; + + +static char * +nvc_to_string (nvc_t pk) +{ + gpg_error_t err; + char *buf; + size_t len; + estream_t sink; + + sink = es_fopenmem (0, "rw"); + assert (sink); + + err = nvc_write (pk, sink); + assert (err == 0); + + len = es_ftell (sink); + buf = xmalloc (len+1); + assert (buf); + + es_fseek (sink, 0, SEEK_SET); + es_read (sink, buf, len, NULL); + buf[len] = 0; + + es_fclose (sink); + return buf; +} + + +void dummy_free (void *p) { (void) p; } +void *dummy_realloc (void *p, size_t s) { (void) s; return p; } + +void +run_tests (void) +{ + gpg_error_t err; + nvc_t pk; + + int i; + for (i = 0; i < DIM (tests); i++) + { + estream_t source; + char *buf; + size_t len; + + len = strlen (tests[i].value); + source = es_mopen (tests[i].value, len, len, + 0, dummy_realloc, dummy_free, "r"); + assert (source); + + if (private_key_mode) + err = nvc_parse_private_key (&pk, NULL, source); + else + err = nvc_parse (&pk, NULL, source); + assert (err == 0); + assert (pk); + + if (verbose) + { + err = nvc_write (pk, es_stderr); + assert (err == 0); + } + + buf = nvc_to_string (pk); + assert (memcmp (tests[i].value, buf, len) == 0); + + es_fclose (source); + xfree (buf); + + if (tests[i].test_func) + tests[i].test_func (pk); + + nvc_release (pk); + } +} + + +void +run_modification_tests (void) +{ + gpg_error_t err; + nvc_t pk; + gcry_sexp_t key; + char *buf; + + pk = my_nvc_new (); + assert (pk); + + nvc_set (pk, "Foo:", "Bar"); + buf = nvc_to_string (pk); + assert (strcmp (buf, "Foo: Bar\n") == 0); + xfree (buf); + + nvc_set (pk, "Foo:", "Baz"); + buf = nvc_to_string (pk); + assert (strcmp (buf, "Foo: Baz\n") == 0); + xfree (buf); + + nvc_set (pk, "Bar:", "Bazzel"); + buf = nvc_to_string (pk); + assert (strcmp (buf, "Foo: Baz\nBar: Bazzel\n") == 0); + xfree (buf); + + nvc_add (pk, "Foo:", "Bar"); + buf = nvc_to_string (pk); + assert (strcmp (buf, "Foo: Baz\nFoo: Bar\nBar: Bazzel\n") == 0); + xfree (buf); + + nvc_add (pk, "DontExistYet:", "Bar"); + buf = nvc_to_string (pk); + assert (strcmp (buf, "Foo: Baz\nFoo: Bar\nBar: Bazzel\nDontExistYet: Bar\n") + == 0); + xfree (buf); + + nvc_delete (pk, nvc_lookup (pk, "DontExistYet:")); + buf = nvc_to_string (pk); + assert (strcmp (buf, "Foo: Baz\nFoo: Bar\nBar: Bazzel\n") == 0); + xfree (buf); + + nvc_delete (pk, nve_next_value (nvc_lookup (pk, "Foo:"), "Foo:")); + buf = nvc_to_string (pk); + assert (strcmp (buf, "Foo: Baz\nBar: Bazzel\n") == 0); + xfree (buf); + + nvc_delete (pk, nvc_lookup (pk, "Foo:")); + buf = nvc_to_string (pk); + assert (strcmp (buf, "Bar: Bazzel\n") == 0); + xfree (buf); + + nvc_delete (pk, nvc_first (pk)); + buf = nvc_to_string (pk); + assert (strcmp (buf, "") == 0); + xfree (buf); + + nvc_set (pk, "Foo:", "A really long value spanning across multiple lines" + " that has to be wrapped at a convenient space."); + buf = nvc_to_string (pk); + assert (strcmp (buf, "Foo: A really long value spanning across multiple" + " lines that has to be\n wrapped at a convenient space.\n") + == 0); + xfree (buf); + + nvc_set (pk, "Foo:", "XA really long value spanning across multiple lines" + " that has to be wrapped at a convenient space."); + buf = nvc_to_string (pk); + assert (strcmp (buf, "Foo: XA really long value spanning across multiple" + " lines that has to\n be wrapped at a convenient space.\n") + == 0); + xfree (buf); + + nvc_set (pk, "Foo:", "XXXXA really long value spanning across multiple lines" + " that has to be wrapped at a convenient space."); + buf = nvc_to_string (pk); + assert (strcmp (buf, "Foo: XXXXA really long value spanning across multiple" + " lines that has\n to be wrapped at a convenient space.\n") + == 0); + xfree (buf); + + nvc_set (pk, "Foo:", "Areallylongvaluespanningacrossmultiplelines" + "thathastobewrappedataconvenientspacethatisnotthere."); + buf = nvc_to_string (pk); + assert (strcmp (buf, "Foo: Areallylongvaluespanningacrossmultiplelinesthat" + "hastobewrappedataco\n nvenientspacethatisnotthere.\n") + == 0); + xfree (buf); + nvc_release (pk); + + pk = my_nvc_new (); + assert (pk); + + err = gcry_sexp_build (&key, NULL, "(hello world)"); + assert (err == 0); + assert (key); + + if (private_key_mode) + { + err = nvc_set_private_key (pk, key); + assert (err == 0); + + buf = nvc_to_string (pk); + assert (strcmp (buf, "Key: (hello world)\n") == 0); + xfree (buf); + } + else + { + err = nvc_set_private_key (pk, key); + assert (gpg_err_code (err) == GPG_ERR_MISSING_KEY); + } + gcry_sexp_release (key); + nvc_release (pk); +} + + +void +convert (const char *fname) +{ + gpg_error_t err; + estream_t source; + gcry_sexp_t key; + char *buf; + size_t buflen; + struct stat st; + nvc_t pk; + + source = es_fopen (fname, "rb"); + if (source == NULL) + goto leave; + + if (fstat (es_fileno (source), &st)) + goto leave; + + buflen = st.st_size; + buf = xtrymalloc (buflen+1); + assert (buf); + + if (es_fread (buf, buflen, 1, source) != 1) + goto leave; + + err = gcry_sexp_sscan (&key, NULL, buf, buflen); + if (err) + { + fprintf (stderr, "malformed s-expression in %s\n", fname); + exit (1); + } + + pk = my_nvc_new (); + assert (pk); + + err = nvc_set_private_key (pk, key); + assert (err == 0); + + err = nvc_write (pk, es_stdout); + assert (err == 0); + + return; + + leave: + perror (fname); + exit (1); +} + + +void +parse (const char *fname) +{ + gpg_error_t err; + estream_t source; + char *buf; + nvc_t pk_a, pk_b; + nve_t e; + int line; + + source = es_fopen (fname, "rb"); + if (source == NULL) + { + perror (fname); + exit (1); + } + + if (private_key_mode) + err = nvc_parse_private_key (&pk_a, &line, source); + else + err = nvc_parse (&pk_a, &line, source); + if (err) + { + fprintf (stderr, "failed to parse %s line %d: %s\n", + fname, line, gpg_strerror (err)); + exit (1); + } + + buf = nvc_to_string (pk_a); + xfree (buf); + + pk_b = my_nvc_new (); + assert (pk_b); + + for (e = nvc_first (pk_a); e; e = nve_next (e)) + { + gcry_sexp_t key = NULL; + + if (private_key_mode && !strcasecmp (nve_name (e), "Key:")) + { + err = nvc_get_private_key (pk_a, &key); + if (err) + key = NULL; + } + + if (key) + { + err = nvc_set_private_key (pk_b, key); + assert (err == 0); + } + else + { + err = nvc_add (pk_b, nve_name (e), nve_value (e)); + assert (err == 0); + } + } + + buf = nvc_to_string (pk_b); + if (verbose) + fprintf (stdout, "%s", buf); + xfree (buf); +} + + +void +print_usage (void) +{ + fprintf (stderr, + "usage: t-private-keys [--verbose]" + " [--convert <private-key-file>" + " || --parse-key <extended-private-key-file>" + " || --parse <file> ]\n"); + exit (2); +} + + +int +main (int argc, char **argv) +{ + enum { TEST, CONVERT, PARSE, PARSEKEY } command = TEST; + + if (argc) + { argc--; argv++; } + if (argc && !strcmp (argv[0], "--verbose")) + { + verbose = 1; + argc--; argv++; + } + + if (argc && !strcmp (argv[0], "--convert")) + { + command = CONVERT; + argc--; argv++; + if (argc != 1) + print_usage (); + } + + if (argc && !strcmp (argv[0], "--parse-key")) + { + command = PARSEKEY; + argc--; argv++; + if (argc != 1) + print_usage (); + } + + if (argc && !strcmp (argv[0], "--parse")) + { + command = PARSE; + argc--; argv++; + if (argc != 1) + print_usage (); + } + + switch (command) + { + case TEST: + run_tests (); + run_modification_tests (); + private_key_mode = 1; + run_tests (); + run_modification_tests (); + break; + + case CONVERT: + convert (*argv); + break; + + case PARSEKEY: + private_key_mode = 1; + parse (*argv); + break; + + case PARSE: + parse (*argv); + break; + } + + return 0; +} diff --git a/common/t-openpgp-oid.c b/common/t-openpgp-oid.c new file mode 100644 index 0000000..fd9de5d --- /dev/null +++ b/common/t-openpgp-oid.c @@ -0,0 +1,246 @@ +/* t-openpgp-oid.c - Module test for openpgp-oid.c + * Copyright (C) 2011 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> + +#include "util.h" + +#define pass() do { ; } while(0) +#define fail(a,e) \ + do { fprintf (stderr, "%s:%d: test %d failed (%s)\n", \ + __FILE__,__LINE__, (a), gpg_strerror (e)); \ + exit (1); \ + } while(0) + + +#define BADOID "1.3.6.1.4.1.11591.2.12242973" + + +static int verbose; + + + +static void +test_openpgp_oid_from_str (void) +{ + static char *sample_oids[] = + { + "0.0", + "1.0", + "1.2.3", + "1.2.840.10045.3.1.7", + "1.3.132.0.34", + "1.3.132.0.35", + NULL + }; + gpg_error_t err; + gcry_mpi_t a; + int idx; + char *string; + unsigned char *p; + unsigned int nbits; + size_t length; + + err = openpgp_oid_from_str ("", &a); + if (gpg_err_code (err) != GPG_ERR_INV_VALUE) + fail (0, err); + gcry_mpi_release (a); + + err = openpgp_oid_from_str (".", &a); + if (gpg_err_code (err) != GPG_ERR_INV_OID_STRING) + fail (0, err); + gcry_mpi_release (a); + + err = openpgp_oid_from_str ("0", &a); + if (gpg_err_code (err) != GPG_ERR_INV_OID_STRING) + fail (0, err); + gcry_mpi_release (a); + + for (idx=0; sample_oids[idx]; idx++) + { + err = openpgp_oid_from_str (sample_oids[idx], &a); + if (err) + fail (idx, err); + + string = openpgp_oid_to_str (a); + if (!string) + fail (idx, gpg_error_from_syserror ()); + if (strcmp (string, sample_oids[idx])) + fail (idx, 0); + xfree (string); + + p = gcry_mpi_get_opaque (a, &nbits); + length = (nbits+7)/8; + if (!p || !length || p[0] != length - 1) + fail (idx, 0); + + gcry_mpi_release (a); + } + +} + + +static void +test_openpgp_oid_to_str (void) +{ + static struct { + const char *string; + unsigned char der[10]; + } samples[] = { + { "1.2.840.10045.3.1.7", + {8, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07 }}, + + { "1.3.132.0.34", + {5, 0x2B, 0x81, 0x04, 0x00, 0x22 }}, + + { "1.3.132.0.35", + { 5, 0x2B, 0x81, 0x04, 0x00, 0x23 }}, + + { BADOID, + { 9, 0x80, 0x02, 0x70, 0x50, 0x25, 0x46, 0xfd, 0x0c, 0xc0 }}, + + { BADOID, + { 1, 0x80 }}, + + { NULL }}; + gcry_mpi_t a; + int idx; + char *string; + unsigned char *p; + + for (idx=0; samples[idx].string; idx++) + { + p = xmalloc (samples[idx].der[0]+1); + memcpy (p, samples[idx].der, samples[idx].der[0]+1); + a = gcry_mpi_set_opaque (NULL, p, (samples[idx].der[0]+1)*8); + if (!a) + fail (idx, gpg_error_from_syserror ()); + + string = openpgp_oid_to_str (a); + if (!string) + fail (idx, gpg_error_from_syserror ()); + if (strcmp (string, samples[idx].string)) + fail (idx, 0); + xfree (string); + gcry_mpi_release (a); + + /* Again using the buffer variant. */ + string = openpgp_oidbuf_to_str (samples[idx].der, samples[idx].der[0]+1); + if (!string) + fail (idx, gpg_error_from_syserror ()); + if (strcmp (string, samples[idx].string)) + fail (idx, 0); + xfree (string); +} + +} + + +static void +test_openpgp_oid_is_ed25519 (void) +{ + static struct + { + int yes; + const char *oidstr; + } samples[] = { + { 0, "0.0" }, + { 0, "1.3.132.0.35" }, + { 0, "1.3.6.1.4.1.3029.1.5.0" }, + { 0, "1.3.6.1.4.1.3029.1.5.1" }, /* Used during Libgcrypt development. */ + { 0, "1.3.6.1.4.1.3029.1.5.2" }, + { 0, "1.3.6.1.4.1.3029.1.5.1.0" }, + { 0, "1.3.6.1.4.1.3029.1.5" }, + { 0, "1.3.6.1.4.1.11591.15.0" }, + { 1, "1.3.6.1.4.1.11591.15.1" }, /* Your the one we want. */ + { 0, "1.3.6.1.4.1.11591.15.2" }, + { 0, "1.3.6.1.4.1.11591.15.1.0" }, + { 0, "1.3.6.1.4.1.11591.15" }, + { 0, NULL }, + }; + gpg_error_t err; + gcry_mpi_t a; + int idx; + + for (idx=0; samples[idx].oidstr; idx++) + { + err = openpgp_oid_from_str (samples[idx].oidstr, &a); + if (err) + fail (idx, err); + + if (openpgp_oid_is_ed25519 (a) != samples[idx].yes) + fail (idx, 0); + + gcry_mpi_release (a); + } + +} + + +static void +test_openpgp_enum_curves (void) +{ + int iter = 0; + const char *name; + int p256 = 0; + int p384 = 0; + int p521 = 0; + + while ((name = openpgp_enum_curves (&iter))) + { + if (verbose) + printf ("curve: %s\n", name); + if (!strcmp (name, "nistp256")) + p256++; + else if (!strcmp (name, "nistp384")) + p384++; + else if (!strcmp (name, "nistp521")) + p521++; + } + + if (p256 != 1 || p384 != 1 || p521 != 1) + { + /* We can only check the basic RFC-6637 requirements. */ + fputs ("standard ECC curve missing\n", stderr); + exit (1); + } +} + + +int +main (int argc, char **argv) +{ + if (argc) + { argc--; argv++; } + if (argc && !strcmp (argv[0], "--verbose")) + { + verbose = 1; + argc--; argv++; + } + + test_openpgp_oid_from_str (); + test_openpgp_oid_to_str (); + test_openpgp_oid_is_ed25519 (); + test_openpgp_enum_curves (); + + return 0; +} diff --git a/common/t-percent.c b/common/t-percent.c new file mode 100644 index 0000000..145a89b --- /dev/null +++ b/common/t-percent.c @@ -0,0 +1,114 @@ +/* t-percent.c - Module test for percent.c + * Copyright (C) 2008 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> + +#include "util.h" + +#define pass() do { ; } while(0) +#define fail(a) do { fprintf (stderr, "%s:%d: test %d failed\n",\ + __FILE__,__LINE__, (a)); \ + exit (1); \ + } while(0) + +static void +test_percent_plus_escape (void) +{ + static struct { + const char *string; + const char *expect; + } tbl[] = { + { + "", + "" + }, { + "a", + "a", + }, { + " ", + "+", + }, { + " ", + "++" + }, { + "+ +", + "%2B+%2B" + }, { + "\" \"", + "%22+%22" + }, { + "%22", + "%2522" + }, { + "%% ", + "%25%25+" + }, { + "\n ABC\t", + "%0A+ABC%09" + }, { NULL, NULL } + }; + char *buf, *buf2; + int i; + size_t len; + + for (i=0; tbl[i].string; i++) + { + buf = percent_plus_escape (tbl[i].string); + if (!buf) + { + fprintf (stderr, "out of core: %s\n", strerror (errno)); + exit (2); + } + if (strcmp (buf, tbl[i].expect)) + fail (i); + buf2 = percent_plus_unescape (buf, 0); + if (!buf2) + { + fprintf (stderr, "out of core: %s\n", strerror (errno)); + exit (2); + } + if (strcmp (buf2, tbl[i].string)) + fail (i); + xfree (buf2); + /* Now test the inplace conversion. */ + len = percent_plus_unescape_inplace (buf, 0); + buf[len] = 0; + if (strcmp (buf, tbl[i].string)) + fail (i); + xfree (buf); + } +} + + + +int +main (int argc, char **argv) +{ + (void)argc; + (void)argv; + + /* FIXME: We escape_unescape is not tested - only + percent_plus_unescape. */ + test_percent_plus_escape (); + + return 0; +} diff --git a/common/t-recsel.c b/common/t-recsel.c new file mode 100644 index 0000000..f52d085 --- /dev/null +++ b/common/t-recsel.c @@ -0,0 +1,438 @@ +/* t-recsel.c - Module test for recsel.c + * Copyright (C) 2016 Werner Koch + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "util.h" +#include "init.h" +#include "recsel.h" + +#define PGM "t-recsel" + +#define pass() do { ; } while(0) +#define fail(a,e) do { log_error ("line %d: test %d failed: %s\n", \ + __LINE__, (a), gpg_strerror ((e))); \ + exit (1); \ + } while(0) + +static int verbose; +static int debug; + + +#define FREEEXPR() do { recsel_release (se); se = NULL; } while (0) +#define ADDEXPR(a) do { \ + err = recsel_parse_expr (&se, (a)); \ + if (err) \ + fail (0, err); \ + } while (0) + + +static const char * +test_1_getval (void *cookie, const char *name) +{ + if (strcmp (name, "uid")) + fail (0, 0); + return cookie; +} + +static void +run_test_1 (void) +{ + static const char *expr[] = { + "uid =~ Alfa", + "&& uid !~ Test ", + "|| uid =~ Alpha", + " uid !~ Test" + }; + gpg_error_t err; + recsel_expr_t se = NULL; + int i; + + for (i=0; i < DIM (expr); i++) + { + err = recsel_parse_expr (&se, expr[i]); + if (err) + fail (i, err); + } + + if (debug) + recsel_dump (se); + + /* The example from recsel.c in several variants. */ + if (!recsel_select (se, test_1_getval, "Alfa")) + fail (0, 0); + if (!recsel_select (se, test_1_getval, "Alpha")) + fail (0, 0); + if (recsel_select (se, test_1_getval, "Alfa Test")) + fail (0, 0); + if (recsel_select (se, test_1_getval, "Alpha Test")) + fail (0, 0); + + /* Some modified versions from above. */ + if (!recsel_select (se, test_1_getval, " AlfA Tes")) + fail (0, 0); + if (!recsel_select (se, test_1_getval, " AlfA Tes ")) + fail (0, 0); + if (!recsel_select (se, test_1_getval, " Tes AlfA")) + fail (0, 0); + if (!recsel_select (se, test_1_getval, "TesAlfA")) + fail (0, 0); + + /* Simple cases. */ + if (recsel_select (se, NULL, NULL)) + fail (0, 0); + if (recsel_select (se, test_1_getval, NULL)) + fail (0, 0); + if (recsel_select (se, test_1_getval, "")) + fail (0, 0); + + FREEEXPR(); +} + + +/* Same as test1 but using a combined expression.. */ +static void +run_test_1b (void) +{ + gpg_error_t err; + recsel_expr_t se = NULL; + + err = recsel_parse_expr + (&se, "uid =~ Alfa && uid !~ Test || uid =~ Alpha && uid !~ Test" ); + if (err) + fail (0, err); + + if (debug) + recsel_dump (se); + + /* The example from recsel.c in several variants. */ + if (!recsel_select (se, test_1_getval, "Alfa")) + fail (0, 0); + if (!recsel_select (se, test_1_getval, "Alpha")) + fail (0, 0); + if (recsel_select (se, test_1_getval, "Alfa Test")) + fail (0, 0); + if (recsel_select (se, test_1_getval, "Alpha Test")) + fail (0, 0); + + /* Some modified versions from above. */ + if (!recsel_select (se, test_1_getval, " AlfA Tes")) + fail (0, 0); + if (!recsel_select (se, test_1_getval, " AlfA Tes ")) + fail (0, 0); + if (!recsel_select (se, test_1_getval, " Tes AlfA")) + fail (0, 0); + if (!recsel_select (se, test_1_getval, "TesAlfA")) + fail (0, 0); + + /* Simple cases. */ + if (recsel_select (se, NULL, NULL)) + fail (0, 0); + if (recsel_select (se, test_1_getval, NULL)) + fail (0, 0); + if (recsel_select (se, test_1_getval, "")) + fail (0, 0); + + FREEEXPR(); +} + + +static const char * +test_2_getval (void *cookie, const char *name) +{ + if (!strcmp (name, "uid")) + return "foo@example.org"; + else if (!strcmp (name, "keyid")) + return "0x12345678"; + else if (!strcmp (name, "zero")) + return "0"; + else if (!strcmp (name, "one")) + return "1"; + else if (!strcmp (name, "blanks")) + return " "; + else if (!strcmp (name, "letters")) + return "abcde"; + else if (!strcmp (name, "str1")) + return "aaa"; + else + return cookie; +} + +static void +run_test_2 (void) +{ + gpg_error_t err; + recsel_expr_t se = NULL; + + ADDEXPR ("uid = foo@example.org"); + if (!recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + FREEEXPR(); + ADDEXPR ("uid = Foo@example.org"); + if (!recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + FREEEXPR(); + ADDEXPR ("-c uid = Foo@example.org"); + if (recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + + FREEEXPR(); + ADDEXPR ("uid =~ foo@example.org"); + if (!recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + FREEEXPR(); + ADDEXPR ("uid =~ Foo@example.org"); + if (!recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + FREEEXPR(); + ADDEXPR ("-c uid =~ Foo@example.org"); + if (recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + + FREEEXPR(); + ADDEXPR ("uid !~ foo@example.org"); + if (recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + FREEEXPR(); + ADDEXPR ("uid !~ Foo@example.org"); + if (recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + FREEEXPR(); + ADDEXPR ("-c uid !~ Foo@example.org"); + if (!recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + + FREEEXPR(); + ADDEXPR ("uid =~ @"); + if (!recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + FREEEXPR(); + ADDEXPR ("uid =~ @"); + if (!recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + + FREEEXPR(); + ADDEXPR ("keyid == 0x12345678"); + if (!recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + FREEEXPR(); + ADDEXPR ("keyid != 0x12345678"); + if (recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + FREEEXPR(); + ADDEXPR ("keyid >= 0x12345678"); + if (!recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + FREEEXPR(); + ADDEXPR ("keyid <= 0x12345678"); + if (!recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + FREEEXPR(); + ADDEXPR ("keyid > 0x12345677"); + if (!recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + FREEEXPR(); + ADDEXPR ("keyid < 0x12345679"); + if (!recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + + FREEEXPR(); + ADDEXPR ("keyid > 0x12345678"); + if (recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + FREEEXPR(); + ADDEXPR ("keyid < 0x12345678"); + if (recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + + + FREEEXPR(); + ADDEXPR ("str1 -gt aa"); + if (!recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + FREEEXPR(); + ADDEXPR ("str1 -gt aaa"); + if (recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + FREEEXPR(); + ADDEXPR ("str1 -ge aaa"); + if (!recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + FREEEXPR(); + ADDEXPR ("str1 -lt aab"); + if (!recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + FREEEXPR(); + ADDEXPR ("str1 -le aaa"); + if (!recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + + FREEEXPR(); + ADDEXPR ("-c str1 -lt AAB"); + if (recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + FREEEXPR(); + ADDEXPR ("str1 -lt AAB"); + if (!recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + + + FREEEXPR(); + ADDEXPR ("uid -n"); + if (!recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + FREEEXPR(); + ADDEXPR ("uid -z"); + if (recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + + FREEEXPR(); + ADDEXPR ("nothing -z"); + if (!recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + FREEEXPR(); + ADDEXPR ("nothing -n"); + if (recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + + FREEEXPR(); + ADDEXPR ("blanks -n"); + if (!recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + FREEEXPR(); + ADDEXPR ("blanks -z"); + if (recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + + FREEEXPR(); + ADDEXPR ("letters -n"); + if (!recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + FREEEXPR(); + ADDEXPR ("letters -z"); + if (recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + + + FREEEXPR(); + ADDEXPR ("nothing -f"); + if (!recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + FREEEXPR(); + ADDEXPR ("nothing -t"); + if (recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + + FREEEXPR(); + ADDEXPR ("zero -f"); + if (!recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + FREEEXPR(); + ADDEXPR ("zero -t"); + if (recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + + FREEEXPR(); + ADDEXPR ("one -t"); + if (!recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + FREEEXPR(); + ADDEXPR ("one -f"); + if (recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + + FREEEXPR(); + ADDEXPR ("blanks -f"); + if (!recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + FREEEXPR(); + ADDEXPR ("blanks -t"); + if (recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + + FREEEXPR(); + ADDEXPR ("letter -f"); + if (!recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + FREEEXPR(); + ADDEXPR ("letters -t"); + if (recsel_select (se, test_2_getval, NULL)) + fail (0, 0); + + + FREEEXPR(); +} + + + +int +main (int argc, char **argv) +{ + int last_argc = -1; + + log_set_prefix (PGM, GPGRT_LOG_WITH_PREFIX); + init_common_subsystems (&argc, &argv); + + if (argc) + { argc--; argv++; } + while (argc && last_argc != argc ) + { + last_argc = argc; + if (!strcmp (*argv, "--")) + { + argc--; argv++; + break; + } + else if (!strcmp (*argv, "--help")) + { + fputs ("usage: " PGM " [options]\n" + "Options:\n" + " --verbose print timings etc.\n" + " --debug flyswatter\n", + stdout); + exit (0); + } + else if (!strcmp (*argv, "--verbose")) + { + verbose++; + argc--; argv++; + } + else if (!strcmp (*argv, "--debug")) + { + verbose += 2; + debug++; + argc--; argv++; + } + else if (!strncmp (*argv, "--", 2)) + { + log_error ("unknown option '%s'\n", *argv); + exit (2); + } + } + + run_test_1 (); + run_test_1b (); + run_test_2 (); + /* Fixme: We should add test for complex conditions. */ + + return 0; +} diff --git a/common/t-session-env.c b/common/t-session-env.c new file mode 100644 index 0000000..e2b942c --- /dev/null +++ b/common/t-session-env.c @@ -0,0 +1,304 @@ +/* t-session-env.c - Module test for session-env.c + * Copyright (C) 2009 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <assert.h> + +#include "util.h" +#include "session-env.h" + +#define pass() do { ; } while(0) +#define fail(e) do { fprintf (stderr, "%s:%d: function failed: %s\n", \ + __FILE__,__LINE__, gpg_strerror (e)); \ + exit (1); \ + } while(0) + +static int verbose; + +static void +listall (session_env_t se) +{ + int iterator = 0; + const char *name, *value; + int def; + + if (verbose) + printf ("environment of %p\n", se); + while ( (name = session_env_listenv (se, &iterator, &value, &def)) ) + if (verbose) + printf (" %s%s=%s\n", def? "[def] ":" ", name, value); + +} + + +static void +show_stdnames (void) +{ + const char *name, *assname; + int iterator = 0; + int count; + + printf (" > Known envvars:"); + count = 20; + while ((name = session_env_list_stdenvnames (&iterator, &assname))) + { + if (count > 60) + { + printf ("\n >"); + count = 7; + } + printf ( " %s", name); + count += strlen (name) + 1; + if (assname) + { + printf ( "(%s)", assname); + count += strlen (assname) + 2; + } + } + putchar('\n'); +} + + +static void +test_all (void) +{ + gpg_error_t err; + session_env_t se_0, se; + const char *s, *s2; + int idx; + + se_0 = session_env_new (); + if (!se_0) + fail (gpg_error_from_syserror ()); + se = session_env_new (); + if (!se) + fail (gpg_error_from_syserror ()); + + err = session_env_putenv (se, NULL); + if (gpg_err_code (err) != GPG_ERR_INV_VALUE) + fail (err); + err = session_env_putenv (se, ""); + if (gpg_err_code (err) != GPG_ERR_INV_VALUE) + fail (err); + err = session_env_putenv (se, "="); + if (gpg_err_code (err) != GPG_ERR_INV_VALUE) + fail (err); + + /* Delete some nonexistent variables. */ + err = session_env_putenv (se, "A"); + if (err) + fail (err); + err = session_env_putenv (se, "a"); + if (err) + fail (err); + err = session_env_putenv (se, "_aaaa aaaaaasssssssssssss\nddd"); + if (err) + fail (err); + + /* Create a few variables. */ + err = session_env_putenv (se, "EMPTY="); + if (err) + fail (err); + err = session_env_putenv (se, "foo=value_of_foo"); + if (err) + fail (err); + err = session_env_putenv (se, "bar=the value_of_bar"); + if (err) + fail (err); + err = session_env_putenv (se, "baz=this-is-baz"); + if (err) + fail (err); + err = session_env_putenv (se, "BAZ=this-is-big-baz"); + if (err) + fail (err); + + listall (se); + + /* Update one. */ + err = session_env_putenv (se, "baz=this-is-another-baz"); + if (err) + fail (err); + + listall (se); + + /* Delete one. */ + err = session_env_putenv (se, "bar"); + if (err) + fail (err); + + listall (se); + + /* Insert a new one. */ + err = session_env_putenv (se, "FOO=value_of_foo"); + if (err) + fail (err); + + listall (se); + + /* Retrieve a default one. */ + s = session_env_getenv_or_default (se, "HOME", NULL); + if (!s) + { + fprintf (stderr, "failed to get default of HOME\n"); + exit (1); + } + + s = session_env_getenv (se, "HOME"); + if (s) + fail(0); /* This is a default value, thus we should not see it. */ + + s = session_env_getenv_or_default (se, "HOME", NULL); + if (!s) + fail(0); /* But here we should see it. */ + + /* Add a few more. */ + err = session_env_putenv (se, "X1=A value"); + if (err) + fail (err); + err = session_env_putenv (se, "X2=Another value"); + if (err) + fail (err); + err = session_env_putenv (se, "X3=A value"); + if (err) + fail (err); + + listall (se); + + /* Check that we can overwrite a default value. */ + err = session_env_putenv (se, "HOME=/this/is/my/new/home"); + if (err) + fail (err); + /* And that we get this string back. */ + s = session_env_getenv (se, "HOME"); + if (!s) + fail (0); + if (strcmp (s, "/this/is/my/new/home")) + fail (0); + /* A new get default should return the very same string. */ + s2 = session_env_getenv_or_default (se, "HOME", NULL); + if (!s2) + fail (0); + if (s2 != s) + fail (0); + + listall (se); + + /* Check that the other object is clean. */ + { + int iterator = 0; + + if (session_env_listenv (se_0, &iterator, NULL, NULL)) + fail (0); + } + + + session_env_release (se); + + /* Use a new session for quick mass test. */ + se = session_env_new (); + if (!se) + fail (gpg_error_from_syserror ()); + + /* Create. */ + for (idx=0; idx < 500; idx++) + { + char buf[100]; + + snprintf (buf, sizeof buf, "FOO_%d=Value for %x", idx, idx); + err = session_env_putenv (se, buf); + if (err) + fail (err); + } + err = session_env_setenv (se, "TEST1", "value1"); + if (err) + fail (err); + err = session_env_setenv (se, "TEST1", "value1-updated"); + if (err) + fail (err); + + listall (se); + + /* Delete all. */ + for (idx=0; idx < 500; idx++) + { + char buf[100]; + + snprintf (buf, sizeof buf, "FOO_%d", idx); + err = session_env_putenv (se, buf); + if (err) + fail (err); + } + err = session_env_setenv (se, "TEST1", NULL); + if (err) + fail (err); + + /* Check that all are deleted. */ + { + int iterator = 0; + + if (session_env_listenv (se, &iterator, NULL, NULL)) + fail (0); + } + + /* Add a few strings again. */ + for (idx=0; idx < 500; idx++) + { + char buf[100]; + + if (!(idx % 10)) + { + if ( !(idx % 3)) + snprintf (buf, sizeof buf, "FOO_%d=", idx); + else + snprintf (buf, sizeof buf, "FOO_%d=new value for %x", idx, idx); + err = session_env_putenv (se, buf); + if (err) + fail (err); + } + } + + listall (se); + + session_env_release (se); + + session_env_release (se_0); +} + + + +int +main (int argc, char **argv) +{ + if (argc) + { argc--; argv++; } + if (argc && !strcmp (argv[0], "--verbose")) + { + verbose = 1; + argc--; argv++; + } + + + show_stdnames (); + test_all (); + + return 0; +} diff --git a/common/t-sexputil.c b/common/t-sexputil.c new file mode 100644 index 0000000..8da9760 --- /dev/null +++ b/common/t-sexputil.c @@ -0,0 +1,511 @@ +/* t-sexputil.c - Module test for sexputil.c + * Copyright (C) 2007 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> + +#include "util.h" + +#define pass() do { ; } while(0) +#define fail(a) do { fprintf (stderr, "%s:%d: test %d failed\n",\ + __FILE__,__LINE__, (a)); \ + exit (1); \ + } while(0) +#define fail2(a,e) do { fprintf (stderr, "%s:%d: test %d failed: %s\n", \ + __FILE__,__LINE__, (a), gpg_strerror ((e))); \ + exit (1); \ + } while(0) + + +static void +test_hash_algo_from_sigval (void) +{ + int algo; + /* A real world example. */ + unsigned char example1_rsa_sha1[] = + ("\x28\x37\x3A\x73\x69\x67\x2D\x76\x61\x6C\x28\x33\x3A\x72\x73\x61" + "\x28\x31\x3A\x73\x31\x32\x38\x3A\x17\xD2\xE9\x5F\xB4\x24\xD4\x1E" + "\x8C\xEE\x94\xDA\x41\x42\x1F\x26\x5E\xF4\x6D\xEC\x5B\xBD\x5B\x89" + "\x7A\x69\x11\x43\xE9\xD2\x23\x21\x25\x64\xA6\xB0\x56\xEF\xB4\xE9" + "\x06\xB2\x44\xF6\x80\x1E\xFF\x41\x23\xEB\xC9\xFA\xFD\x09\xBF\x9C" + "\x8E\xCF\x7F\xC3\x7F\x3A\x40\x48\x89\xDC\xBA\xB7\xDB\x9E\xF1\xBA" + "\x7C\x08\xEA\x74\x1D\x49\xE7\x65\xEF\x67\x79\xBC\x23\xD9\x49\xCD" + "\x05\x99\xD3\xD8\xB7\x7B\xC7\x0E\xF2\xB3\x01\x48\x0F\xC8\xEB\x05" + "\x7B\xFB\x61\xCC\x41\x04\x74\x6D\x33\x84\xB1\xE6\x6A\xD8\x0F\xBC" + "\x27\xAC\x43\x45\xFA\x04\xD1\x22\x29\x29\x28\x34\x3A\x68\x61\x73" + "\x68\x34\x3A\x73\x68\x61\x31\x29\x29"); + /* The same but without the hash algo. */ + unsigned char example1_rsa[] = + ("\x28\x37\x3A\x73\x69\x67\x2D\x76\x61\x6C\x28\x33\x3A\x72\x73\x61" + "\x28\x31\x3A\x73\x31\x32\x38\x3A\x17\xD2\xE9\x5F\xB4\x24\xD4\x1E" + "\x8C\xEE\x94\xDA\x41\x42\x1F\x26\x5E\xF4\x6D\xEC\x5B\xBD\x5B\x89" + "\x7A\x69\x11\x43\xE9\xD2\x23\x21\x25\x64\xA6\xB0\x56\xEF\xB4\xE9" + "\x06\xB2\x44\xF6\x80\x1E\xFF\x41\x23\xEB\xC9\xFA\xFD\x09\xBF\x9C" + "\x8E\xCF\x7F\xC3\x7F\x3A\x40\x48\x89\xDC\xBA\xB7\xDB\x9E\xF1\xBA" + "\x7C\x08\xEA\x74\x1D\x49\xE7\x65\xEF\x67\x79\xBC\x23\xD9\x49\xCD" + "\x05\x99\xD3\xD8\xB7\x7B\xC7\x0E\xF2\xB3\x01\x48\x0F\xC8\xEB\x05" + "\x7B\xFB\x61\xCC\x41\x04\x74\x6D\x33\x84\xB1\xE6\x6A\xD8\x0F\xBC" + "\x27\xAC\x43\x45\xFA\x04\xD1\x22\x29\x29\x29"); + + algo = hash_algo_from_sigval (example1_rsa_sha1); + if (algo != GCRY_MD_SHA1) + fail (0); + algo = hash_algo_from_sigval (example1_rsa); + if (algo) + fail (0); +} + + +static void +test_make_canon_sexp_from_rsa_pk (void) +{ + struct { + unsigned char *m; + size_t mlen; + unsigned char *e; + size_t elen; + unsigned char *result; + size_t resultlen; + gpg_err_code_t reverr; /* Expected error from the reverse function. */ + } tests[] = { + { + "\x82\xB4\x12\x48\x08\x48\xC0\x76\xAA\x8E\xF1\xF8\x7F\x5E\x9B\x89" + "\xA9\x62\x92\xA2\x16\x1B\xF5\x9F\xE1\x41\xF3\xF0\x42\xB5\x5C\x46" + "\xB8\x83\x9F\x39\x97\x73\xFF\xC5\xB2\xF4\x59\x5F\xBA\xC7\x0E\x03" + "\x9D\x27\xC0\x86\x37\x31\x46\xE0\xA1\xFE\xA1\x41\xD4\xE3\xE9\xB3" + "\x9B\xD5\x84\x65\xA5\x37\x35\x34\x07\x58\xB6\xBA\x21\xCA\x21\x72" + "\x4C\xF3\xFC\x91\x47\xD1\x3C\x1D\xA5\x9C\x38\x4D\x58\x39\x92\x16" + "\xB1\xE5\x43\xFE\xB5\x46\x4B\x43\xD1\x47\xB0\xE8\x2A\xDB\xF8\x34" + "\xB0\x5A\x22\x3D\x14\xBB\xEA\x63\x65\xA7\xF1\xF2\xF8\x97\x74\xA7", + 128, + "\x40\x00\x00\x81", + 4, + "\x28\x31\x30\x3a\x70\x75\x62\x6c\x69\x63\x2d\x6b\x65\x79\x28\x33" + "\x3a\x72\x73\x61\x28\x31\x3a\x6e\x31\x32\x39\x3a\x00\x82\xb4\x12" + "\x48\x08\x48\xc0\x76\xaa\x8e\xf1\xf8\x7f\x5e\x9b\x89\xa9\x62\x92" + "\xa2\x16\x1b\xf5\x9f\xe1\x41\xf3\xf0\x42\xb5\x5c\x46\xb8\x83\x9f" + "\x39\x97\x73\xff\xc5\xb2\xf4\x59\x5f\xba\xc7\x0e\x03\x9d\x27\xc0" + "\x86\x37\x31\x46\xe0\xa1\xfe\xa1\x41\xd4\xe3\xe9\xb3\x9b\xd5\x84" + "\x65\xa5\x37\x35\x34\x07\x58\xb6\xba\x21\xca\x21\x72\x4c\xf3\xfc" + "\x91\x47\xd1\x3c\x1d\xa5\x9c\x38\x4d\x58\x39\x92\x16\xb1\xe5\x43" + "\xfe\xb5\x46\x4b\x43\xd1\x47\xb0\xe8\x2a\xdb\xf8\x34\xb0\x5a\x22" + "\x3d\x14\xbb\xea\x63\x65\xa7\xf1\xf2\xf8\x97\x74\xa7\x29\x28\x31" + "\x3a\x65\x34\x3a\x40\x00\x00\x81\x29\x29\x29", + 171 + }, + { + "\x63\xB4\x12\x48\x08\x48\xC0\x76\xAA\x8E\xF1\xF8\x7F\x5E\x9B\x89", + 16, + "\x03", + 1, + "\x28\x31\x30\x3a\x70\x75\x62\x6c\x69\x63\x2d\x6b\x65\x79\x28\x33" + "\x3a\x72\x73\x61\x28\x31\x3a\x6e\x31\x36\x3a\x63\xb4\x12\x48\x08" + "\x48\xc0\x76\xaa\x8e\xf1\xf8\x7f\x5e\x9b\x89\x29\x28\x31\x3a\x65" + "\x31\x3a\x03\x29\x29\x29", + 54, + }, + { + "", + 0, + "", + 0, + "\x28\x31\x30\x3a\x70\x75\x62\x6c\x69\x63\x2d\x6b\x65\x79\x28\x33" + "\x3a\x72\x73\x61\x28\x31\x3a\x6e\x31\x3a\x00\x29\x28\x31\x3a\x65" + "\x31\x3a\x00\x29\x29\x29", + 38, + GPG_ERR_BAD_PUBKEY + }, + { + NULL + } + }; + int idx; + gpg_error_t err; + unsigned char *sexp; + size_t length; + const unsigned char *rsa_n, *rsa_e; + size_t rsa_n_len, rsa_e_len; + + for (idx=0; tests[idx].m; idx++) + { + sexp = make_canon_sexp_from_rsa_pk (tests[idx].m, tests[idx].mlen, + tests[idx].e, tests[idx].elen, + &length); + if (!sexp) + { + fprintf (stderr, "%s:%d: out of core\n", __FILE__, __LINE__); + exit (1); + } + + if (length != tests[idx].resultlen) + fail (idx); + if (memcmp (sexp, tests[idx].result, tests[idx].resultlen)) + fail (idx); + + /* Test the reverse function. */ + err = get_rsa_pk_from_canon_sexp (sexp, length, + &rsa_n, &rsa_n_len, + &rsa_e, &rsa_e_len); + if (gpg_err_code (err) != tests[idx].reverr) + fail (idx); + if (!err) + { + if (tests[idx].mlen != rsa_n_len) + fail (idx); + if (memcmp (tests[idx].m, rsa_n, rsa_n_len)) + fail (idx); + if (tests[idx].elen != rsa_e_len) + fail (idx); + if (memcmp (tests[idx].e, rsa_e, rsa_e_len)) + fail (idx); + } + + xfree (sexp); + } +} + + +/* Communiacation object for tcmp. */ +struct tcmp_parm_s { + int curve_seen; +}; + +/* Helper for test_cmp_canon_sexp. */ +static int +tcmp1 (void *opaque, int depth, + const unsigned char *aval, size_t alen, + const unsigned char *bval, size_t blen) +{ + struct tcmp_parm_s *parm = opaque; + + (void)depth; + + if (parm->curve_seen) + { + /* Last token was "curve", canonicalize its argument. */ + parm->curve_seen = 0; + + if (alen == 8 && !memcmp (aval, "nistp256", alen)) + { + alen = 19; + aval = "1.2.840.10045.3.1.7"; + } + + if (blen == 8 && !memcmp (bval, "nistp256", blen)) + { + blen = 19; + bval = "1.2.840.10045.3.1.7"; + } + } + else if (alen == 5 && !memcmp (aval, "curve", 5)) + parm->curve_seen = 1; + else + parm->curve_seen = 0; + + if (alen > blen) + return 1; + else if (alen < blen) + return -1; + else + return memcmp (aval, bval, alen); +} + + +static void +test_cmp_canon_sexp (void) +{ + struct { + unsigned char *a; + unsigned char *b; + int expected0; /* Expected result without compare function. */ + int expected1; /* Expected result with compare function tcmp1. */ + } + tests[] = { + { + "(10:public-key(3:ecc(5:curve8:nistp256)(1:q10:qqqqqqqqqq)))", + "(10:public-key(3:ecc(5:curve8:nistp256)(1:q10:qqqqqqqqqq)))", + 0, 0 + }, + { + "(10:public-key(3:ecc(5:curve19:1.2.840.10045.3.1.7)(1:q10:qqqqqqqqqq)))", + "(10:public-key(3:ecc(5:curve19:1.2.840.10045.3.1.7)(1:q10:qqqqqqqqqq)))", + 0, 0 + }, + { + "(10:public-key(3:ecc(5:curve8:nistp256)(1:q10:qqqqqqqqqq)))", + "(10:public-key(3:ecc(5:curve19:1.2.840.10045.3.1.7)(1:q10:qqqqqqqqqq)))", + -1, 0 + }, + { + "(10:public-key(3:ecc(5:curve19:1.2.840.10045.3.1.7)(1:q10:qqqqqqqqqq)))", + "(10:public-key(3:ecc(5:curve8:nistp256)(1:q10:qqqqqqqqqq)))", + 1, 0 + }, + { + NULL + } + }; + struct tcmp_parm_s parm = {0}; + int idx; + int res; + + for (idx=0; tests[idx].a; idx++) + { + res = cmp_canon_sexp (tests[idx].a, strlen (tests[idx].a), + tests[idx].b, strlen (tests[idx].b), + NULL, NULL); + if (res != tests[idx].expected0) + fail (idx); + res = cmp_canon_sexp (tests[idx].a, strlen (tests[idx].a), + tests[idx].b, strlen (tests[idx].b), + tcmp1, &parm); + if (res != tests[idx].expected1) + fail (idx); + } +} + + +static void +test_ecc_uncompress (void) +{ + struct { + const char *a; /* Uncompressed. */ + const char *b; /* Compressed. */ + } + tests[] = { + { + "(public-key" + " (ecc" + " (curve brainpoolP256r1)" + " (q #042ECD8679930BE2DB4AD42B8600BA3F80" + /* */"2D4D539BFF2F69B83EC9B7BBAA7F3406" + /* */"436DD11A1756AFE56CD93408410FCDA9" + /* */"BA95024EB613BD481A14FCFEC27A448A#)))", + /* The same in compressed form. */ + "(public-key" + " (ecc" + " (curve brainpoolP256r1)" + " (q #022ECD8679930BE2DB4AD42B8600BA3F80" + /* */"2D4D539BFF2F69B83EC9B7BBAA7F3406#)))" + }, + { + "(public-key" + " (ecc" + " (curve brainpoolP256r1)" + " (q #045B784CA008EE64AB3D85017EE0D2BE87" + /* */"558762C7300E0C8E06B1F9AF7C031458" + /* */"9EBBA41915313417BA54218EB0569C59" + /* */"0B156C76DBCAB6E84575E6EF68CE7B87#)))", + /* The same in compressed form. */ + "(public-key" + " (ecc" + " (curve brainpoolP256r1)" + " (q #035B784CA008EE64AB3D85017EE0D2BE87" + /* */"558762C7300E0C8E06B1F9AF7C031458#)))" + }, + { /* A key which does not require a conversion. */ + "(public-key" + " (ecdsa" + " (p #00FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF#)" + " (curve \"NIST P-256\")" + " (b #5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B#)" + " (g #046B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C2964FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5#)" + " (n #00FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551#)" + " (h #000000000000000000000000000000000000000000000000000000000000000001#)" + " (q #04C8A4CEC2E9A9BC8E173531A67B0840DF345C32E261ADD780E6D83D56EFADFD5DE872F8B854819B59543CE0B7F822330464FBC4E6324DADDCD9D059554F63B344#)))" + }, + { /* Nothing to do for an RSA private key. */ + "(private-key" + " (rsa" + " (n #00B6B509596A9ECABC939212F891E656A626BA07DA8521A9CAD4C08E640C04052FBB87F424EF1A0275A48A9299AC9DB69ABE3D0124E6C756B1F7DFB9B842D6251AEA6EE85390495CADA73D671537FCE5850A932F32BAB60AB1AC1F852C1F83C625E7A7D70CDA9EF16D5C8E47739D77DF59261ABE8454807FF441E143FBD37F8545#)" + " (e #010001#)" + " (d #077AD3DE284245F4806A1B82B79E616FBDE821C82D691A65665E57B5FAD3F34E67F401E7BD2E28699E89D9C496CF821945AE83AC7A1231176A196BA6027E77D85789055D50404A7A2A95B1512F91F190BBAEF730ED550D227D512F89C0CDB31AC06FA9A19503DDF6B66D0B42B9691BFD6140EC1720FFC48AE00C34796DC899E5#)" + " (p #00D586C78E5F1B4BF2E7CD7A04CA091911706F19788B93E44EE20AAF462E8363E98A72253ED845CCBF2481BB351E8557C85BCFFF0DABDBFF8E26A79A0938096F27#)" + " (q #00DB0CDF60F26F2A296C88D6BF9F8E5BE45C0DDD713C96CC73EBCB48B061740943F21D2A93D6E42A7211E7F02A95DCED6C390A67AD21ECF739AE8A0CA46FF2EBB3#)" + " (u #33149195F16912DB20A48D020DBC3B9E3881B39D722BF79378F6340F43148A6E9FC5F53E2853B7387BA4443BA53A52FCA8173DE6E85B42F9783D4A7817D0680B#)))" + }, + { /* Nothing to do dor a DSA key. */ + " (public-key" + " (dsa" + " (p #0084E4C626E16005770BD9509ABF7354492E85B8C0060EFAAAEC617F725B592FAA59DF5460575F41022776A9718CE62EDD542AB73C7720869EBDBC834D174ADCD7136827DF51E2613545A25CA573BC502A61B809000B6E35F5EB7FD6F18C35678C23EA1C3638FB9CFDBA2800EE1B62F41A4479DE824F2834666FBF8DC5B53C2617#)" + " (q #00B0E6F710051002A9F425D98A677B18E0E5B038AB#)" + " (g #44370CEE0FE8609994183DBFEBA7EEA97D466838BCF65EFF506E35616DA93FA4E572A2F08886B74977BC00CA8CD3DBEA7AEB7DB8CBB180E6975E0D2CA76E023E6DE9F8CCD8826EBA2F72B8516532F6001DEFFAE76AA5E59E0FA33DBA3999B4E92D1703098CDEDCC416CF008801964084CDE1980132B2B78CB4CE9C15A559528B#)" + " (y #3D5DD14AFA2BF24A791E285B90232213D0E3BA74AB1109E768AED19639A322F84BB7D959E2BA92EF73DE4C7F381AA9F4053CFA3CD4527EF9043E304E5B95ED0A3A5A9D590AA641C13DB2B6E32B9B964A6A2C730DD3EA7C8E13F7A140AFF1A91CE375E9B9B960384779DC4EA180FA1F827C52288F366C0770A220F50D6D8FD6F6#)))" + }, + { /* Nothing to do for an ECDSA key w/o curvename. */ + "(public-key" + " (ecdsa(flags param)" + " (p #00FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF#)" + " (a #00FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC#)" + " (b #5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B#)" + " (g #046B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C2964FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5#)" + " (n #00FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551#)" + " (h #000000000000000000000000000000000000000000000000000000000000000001#)" + " (q #04C8A4CEC2E9A9BC8E173531A67B0840DF345C32E261ADD780E6D83D56EFADFD5DE872F8B854819B59543CE0B7F822330464FBC4E6324DADDCD9D059554F63B344#)))" + }, + { /* Nothing to do for Ed25519 key. */ + "(public-key" + " (ecc" + " (curve Ed25519)" + " (q #04" + " 1CC662926E7EFF4982B7FB8B928E61CD74CCDD85277CC57196C3AD20B611085F" + " 47BD24842905C049257673B3F5249524E0A41FAA17B25B818D0F97E625F1A1D0#)" + " ))" + }, + { /* Nothing to do for Ed25519 with EdDSA key. */ + "(public-key" + " (ecc" + " (curve Ed25519)(flags eddsa)" + " (q #773E72848C1FD5F9652B29E2E7AF79571A04990E96F2016BF4E0EC1890C2B7DB#)" + " ))" + }, + { /* Nothing to do for Ed25519 with EdDSA key with prefix. */ + "(public-key" + " (ecc" + " (curve Ed25519)(flags eddsa)" + " (q #40" + " 773E72848C1FD5F9652B29E2E7AF79571A04990E96F2016BF4E0EC1890C2B7DB#)" + " ))" + }, + { /* Nothing to do for Ed25519 with EdDSA key with uncompress prefix. */ + "(public-key" + " (ecc" + " (curve Ed25519)(flags eddsa)" + " (q #04" + " 629ad237d1ed04dcd4abe1711dd699a1cf51b1584c4de7a4ef8b8a640180b26f" + " 5bb7c29018ece0f46b01f2960e99041a5779afe7e2292b65f9d51f8c84723e77#)" + " ))" + }, + { /* Noting to do for a Cv25519 tweaked key. */ + "(public-key" + " (ecc" + " (curve Curve25519)(flags djb-tweak)" + " (q #40" + " 918C1733127F6BF2646FAE3D081A18AE77111C903B906310B077505EFFF12740#)" + " ))" + }, + { /* Nothing to do for a shadowed key. */ + "(shadowed-private-key" + " (rsa" + " (n #00B493C79928398DA9D99AC0E949FE6EB62F683CB974FFFBFBC01066F5C9A89B" + " D3DC48EAD7C65F36EA943C2B2C865C26C4884FF9EDFDA8C99C855B737D77EEF6" + " B85DBC0CCEC0E900C1F89A6893A2A93E8B31028469B6927CEB2F08687E547C68" + " 6B0A2F7E50A194FF7AB7637E03DE0912EF7F6E5F1EC37625BD1620CCC2E7A564" + " 31E168CDAFBD1D9E61AE47A69A6FA03EF22F844528A710B2392F262B95A3078C" + " F321DC8325F92A5691EF69F34FD0DE0B22C79D29DC87723FCADE463829E8E5F7" + " D196D73D6C9C180F6A6A0DDBF7B9D8F7FA293C36163B12199EF6A1A95CAE4051" + " E3069C522CC6C4A7110F663A5DAD20F66C13A1674D050088208FAE4F33B3AB51" + " 03#)" + " (e #00010001#)" + " (shadowed t1-v1" + " (#D2760001240102000005000123350000# OPENPGP.1)" + ")))" + }, + { + NULL + }}; + gpg_error_t err; + int idx; + gcry_sexp_t sexp; + unsigned char *abuf, *bbuf, *rbuf; + size_t abuflen, bbuflen, rbuflen; + + + for (idx=0; tests[idx].a; idx++) + { + err = gcry_sexp_new (&sexp, tests[idx].a, 0, 1); + if (err) + fail2 (idx,err); + err = make_canon_sexp (sexp, &abuf, &abuflen); + if (err) + fail2 (idx,err); + gcry_sexp_release (sexp); + + if (tests[idx].b) + { + err = gcry_sexp_new (&sexp, tests[idx].b, 0, 1); + if (err) + fail2 (idx,err); + err = make_canon_sexp (sexp, &bbuf, &bbuflen); + if (err) + fail2 (idx,err); + gcry_sexp_release (sexp); + } + else + bbuf = NULL; + + err = uncompress_ecc_q_in_canon_sexp (abuf, abuflen, &rbuf, &rbuflen); + if (err) + fail2 (idx,err); + if (rbuf) + fail (idx); /* Converted without a need. */ + + if (bbuf) + { + err = uncompress_ecc_q_in_canon_sexp (bbuf, bbuflen, &rbuf, &rbuflen); + if (gpg_err_code (err) == GPG_ERR_UNKNOWN_CURVE) + { + static int shown; + fprintf (stderr, "%s:%d: test %d failed: %s - ignored\n", + __FILE__,__LINE__, idx, gpg_strerror (err)); + if (!shown) + { + shown = 1; + fprintf (stderr, "This is likely due to a patched" + " version of Libgcrypt with removed support" + " for Brainpool curves\n"); + } + } + else + { + if (err) + fail2 (idx,err); + if (!rbuf) + fail (idx); /* Not converted despite a need for it. */ + + /* log_printcanon (" orig:", abuf, abuflen); */ + /* log_printcanon (" comp:", bbuf, bbuflen); */ + /* log_printcanon ("uncomp:", rbuf, rbuflen); */ + + if (rbuflen != abuflen || memcmp (rbuf, abuf, abuflen)) + fail (idx); + } + } + + xfree (abuf); + xfree (bbuf); + xfree (rbuf); + } +} + + +int +main (int argc, char **argv) +{ + (void)argc; + (void)argv; + + test_hash_algo_from_sigval (); + test_make_canon_sexp_from_rsa_pk (); + test_cmp_canon_sexp (); + test_ecc_uncompress (); + + return 0; +} diff --git a/common/t-ssh-utils.c b/common/t-ssh-utils.c new file mode 100644 index 0000000..1c9b87b --- /dev/null +++ b/common/t-ssh-utils.c @@ -0,0 +1,391 @@ +/* t-ssh-utils.c - Module test for ssh-utils.c + * Copyright (C) 2011 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "util.h" +#include "ssh-utils.h" + + +static struct +{ + const char *key; + const char *fpr_md5; + const char *fpr_sha256; +} sample_keys[] = { + { "(protected-private-key " + "(rsa " + "(n #" + "00D88E47BCE0DA99D6180E8A9F4E6A673CC16F5BB6CF930E0E868BAABA715A8E1D3E2BEA" + "5477170E1F6CAFC0F8907B9892993C70AC476BBB301669F68EE0593532FB522DD60755A3" + "2F8B08649E856271A7F9BCB25F29554DF11707F812EA377683A99DD4698C4DBF797A0ABF" + "43C8EBB364B9FFC9EE78CBEA348C590507A4EA390312153DDD905EC4F1A63D5DA56C08FD" + "C3F6E5707BFC5DBDC09D19723B1AC6E466906F13AA2ECDBD258148F86C980D45CF233415" + "38C5857C2CF0B4C9AB2B4E6A4517FF084FDB009A33553A68907A29691B6FAE994E864F78" + "7B83F714730BEDB0AF1723D636E034D73EB7EC9BA127BB4BE80FD805813E3F45E7FAE514" + "AD2ECA9607#)" + "(e \"#\")" + "(protected openpgp-s2k3-sha1-aes-cbc " + "(" + "(sha1 #B5847F9A2DB4E0C5# \"5242880\")" + "#342D81BDE21301F18FDCE169A99A47C5#)" + "#23512602219EC7A97DBA89347CCD59D2072D80CE3F7DD6C97A058B83DAB3C829D97DF5" + "DFE9181F27DBF58258C4CDBD562A5B20BB5BC35EDCA7B1E57B8CDBF92D798F46EE5567BD" + "8A67EF3BE09908A49D41AA166A3398B64227BC75021C69A8FE8354E2903EF52DC91B1FE3" + "EF9558E5C2D34CF38BFC29E99A49AE30B0C22CE81EE14FC71E986E7C7CB5FCF807433FDA" + "EF1D00985767265BA0BE333754E44CCF622CBB98A029D78A6A9AADBC24613127B6448350" + "23DA355ED31CF089DD11A7FC6003CEEB53FB327A05604D053C99996F9E01CB355983F66E" + "7BEB9687A9277BBF440ED5FAF1A8396C9B06C9B47BA7A994E1931B08DAD34449952CD343" + "9A691477682C324EA07CCCE5DF0F0E9DAEFAE3A4717AACA6DC18ED91DD5A820C924BD36B" + "B3BA85BD63B3180C7F94EE58956940621280B9628FA5CC560BB14331AF1A7B5B499F8F03" + "0ED464ABD4E26C5FD610697EDD0FD1203983E73418F3776568A613D3CEFF17199473052A" + "18807A6F5C52A2A643185801D087EE4DC930ABEEB67C5B8A1CB2F29D0ACBD855972BEC0B" + "DE6E52387CFCC54B4C2B87EE947C97173BFCAE3E2658EB819D87F542C9A9FE6C410D08F5" + "3CD5451FB50253F4A848DFE136B3A5861D58B76A26A7E3E4E7A8F8D4BD5B80430674A6B9" + "A2C8EDD53DB37865D1ACBB07E1758DFF64A944E0126F948BF088C0FC0C3607E39522EC94" + "91483A90D9498D7F6C3C8720124C7E3F6E271E78E1CFFB4EF64F070F7424F30372A07D02" + "2355D8B17BB0DEBCBE101F621E0526551A35A56830D74E0F5BD6313DF114D1E46D4844AA" + "E4EB6268637D04B27D200D7F40AFA9AD2CFAA5415E5FC08358FFA79A9E743CCDF6668FE5" + "D79FA03D61941E57244F066A31F1C9D6A34DC62BC738C52B604F00B19EB9FD0173F3B139" + "42932066B7DC94DC4C563392F798A1CE2D5D75B8FF93E440433263CFB7016143A9923CD9" + "634E964A8056946F462B06F320F44449D85B07FA26A324505C858274F89EDBD8346950DE" + "5F#)" + "(protected-at \"20110720T135431\")" + ")" + "(comment passphrase_is_abc)" + ")", + "MD5:c7:c6:a7:ec:04:6c:87:59:54:f2:88:58:09:e0:f2:b1", + "SHA256:ksKb4DKk2SFX56GRtpt0szBnyjiYARSb2FNlUb7snnE" + }, + { + "(protected-private-key " + "(dsa " + "(p #00FC7DC086F4517079BCCFA7FD229477FE88B0231038DFC21B29CCBD74C6F6FE04FD" + "7248C0473D5028BE106D7A7C8F54B269225789E781763527D1432CD46E416C2D14DDCA70" + "27DA4B92D1E222B5BDF4B9C8C761CACCFBD108F7729412E8835653BE5073447287A6BDEB" + "4645A5411752405EE7F503E44B1DFDCA6054CD3C44630B#)" + "(q #00D498505BF0E7EE01239EB51F2B400B8EF6329B17#)" + "(g #00A127B3DD5106F0A463312E42ECB83790E6F3BEA7AC3FAF7A42FB2C00F376323676" + "C9E48984F0D4AC3FE5856F1C2765E9BC3C8A5C9C9CD3166C057E82569D187C48591AA66B" + "8966BFF2B827BE36BD0BA4B895B42136F1381D52DDA708B2A3D181F648228DFFFEB153DA" + "ACCAEBB51EF08A7807CD628024CEFF96FEE97DE95C8CBE#)" + "(y #008E2B0915A3A299D83B4333C848C5D312F25903773E8C4D50691CAF81C3B768FA41" + "7D19F0FD437B377CCF51D3AE598649656D4D74D210CDBC2B76209B16EAAFCB14D6F4D691" + "20164885852AF1CEBB4D8602AD6755DFA7163645B4DB7926CD44D2DD9F840BFEF57F3DB0" + "933C85EB6B0AAC20BC67E73F47B8DDBEC8EFAA64286EF1#)" + "(protected openpgp-s2k3-sha1-aes-cbc " + "(" + "(sha1 \"ü¿jy²üa4\" \"5242880\")" + "#FF12BEE0B03F842349717AE1AB6D7AC2#)" + "#95570487C8B5C49492D4E662259F2CF9B6D7E64F728F17A1FE1B2DA616E5976FE32861E" + "C4B1F0DA03D9006C432CF2136871266E9444377ACEF04340B36B4550B5C1E4CC69AD4380" + "A709FB0DAA5104A8B#)" + "(protected-at \"20110720T142801\")" + ")" + "(comment sample_dsa_passphrase_is_abc)" + ")", + "MD5:2d:b1:70:1a:04:9e:41:a3:ce:27:a5:c7:22:fe:3a:a3", + "SHA256:z8+8HEuD/5QpegGS4tSK02dJF+a6o2V67VM2gOPz9oQ" + }, + { /* OpenSSH 6.7p1 generated key: */ + "(protected-private-key " + "(ecdsa " + "(curve \"NIST P-256\")" + "(q #041F17ED5E3D637181DFA68157270F94A46C089B6F5D4518564600551C0A60A063B3" + "31EDE027A23CAB58A5BAD469600229DC8DED06380A92F86460ED400F963319#)" + "(protected openpgp-s2k3-sha1-aes-cbc " + "(" + "(sha1 #43F887516D94A502# \"20971520\")" + "#B135DEDA02CF36F126BA661FB22A35CF#)" + "#37E74BEC054B17723C106BA69214CFDA245512E40F4848ECF5719E3700002C940BC7EEC" + "283537CA4D8779107E07F03AAA9FAF155BA9BF6286080C35EF72DDAAF303FD9069475B03" + "C99D9FC93C58CD83A852964D2C7BFD1D803E2ECD1331937C3#)" + "(protected-at \"20150922T071259\")" + ")" + "(comment \"ecdsa w/o comment\")" + ")", /* Passphrase="abc" */ + "MD5:93:4f:08:02:7d:cb:16:9b:0c:39:21:4b:cf:28:5a:19", + "SHA256:zSj4uXfE1hlQnESD2LO723fMGXsNwzHrfqOfqep37is" + }, + { /* OpenSSH 6.7p1 generated key: */ + "(protected-private-key " + "(ecdsa " + "(curve \"NIST P-384\") " + "(q #04B6E747AC2F179F96088D1DB58EB8600BB23FAEF5F58EFE712A7478FB7BF735" + "B015EA2DFBBA965D8C6EB135A2B9B9599D65BF0167D2DB6ABF00F641F0F5FC15A4C3" + "EFE432DA331B7C8A66D6C4C2B0EBB5ED11A80301C4E57C1EBD25665CEBF123#)" + "(protected openpgp-s2k3-sha1-aes-cbc " + "(" + "(sha1 #3B13710B67D756EA# \"20971520\")" + "#720599AC095BF1BD73ED72F49FB77BFA#)" + "#F1A522F4533E3A6E40821D67CEA6C28A7FF07ACA4BEE81E0F39193B2E469E0C583D" + "A42E0E2D52ADB5ACFAB9C4CA7F1C3556FD7FD2770717FB3CE7C59474A3E2A7AF3D93" + "9EC01E067DAAA60D3D355D9BABCCD1F013E8637C555DDFA61F8FA5AFB010FF02979D" + "35BBBEED71BFD8BB508F7#)" + "(protected-at \"20150922T070806\")" + ")" + "(comment \"ecdsa w/o comment\")" + ")", /* Passphrase="abc" */ + "MD5:a3:cb:44:c8:56:15:25:62:85:fd:e8:04:7a:26:dc:76", + "SHA256:JuQh5fjduynuuTEwI9C6yAKK1NnLX9PPd7TP0qZfbGs" + }, + { /* OpenSSH 6.7p1 generated key: */ + "(protected-private-key " + "(ecdsa " + "(curve \"NIST P-521\")" + "(q #04005E460058F37DB5ADA670040203C4D7E18D9FC8A7087165904A4E25EE5EEE" + "3046406D922616DA7E71016A1CB9E57A45E3D3727D7C8DF0F11AE2BD75FAD3355CAA" + "E1019D89D33CC77424E5DA233588207444FC9F67BBE428A9528B7DC77AF8261A1D45" + "ACC1A657C99E361E93C1E5C0F214104C18807670F4CDC1E038B7C950FDBAAECB40#)" + "(protected openpgp-s2k3-sha1-aes-cbc " + "(" + "(sha1 #FB2E36984DE2E17C# \"19737600\")" + "#85DB6445B37012F9A449E5AC0D5017E9#)" + "#B4C7CCDFE9B5D32B31BA7C763B80485A62EBF34FD68D8E306DA75FD2BDDBABAA098" + "9B51972BA3B731DA5261E0ADC3FAEF9BB4C8284C53D3E88E738AEF1490941903A5B2" + "9F3747E83C4D80B6A89E0B7BDEE5C6638332F4AAEA5983F760B2887A43A1C4BE0564" + "3F72C6943987D97FDAA7D9C235C6D31973A2400DA9BAB564A16EA#)" + "(protected-at \"20150922T075611\")" + ")" + "(comment \"ecdsa w/o comment\")" + ")", /* Passphrase="abc" */ + "MD5:1e:a6:94:ab:bd:81:73:5f:22:bc:0e:c7:89:f6:68:df", + "SHA256:+pbRyYa2UBwDki1k4Wziu2CKrdJIbZM/hOWOQ/sNe/0" + }, + { /* OpenSSH 6.7p1 generated key: */ + "(protected-private-key " + "(ecc " + "(curve Ed25519)" + "(flags eddsa)" + "(q #40A3577AA7830C50EBC15B538E9505DB2F0D2FFCD57EA477DD83dcaea530f3c277#)" + "(protected openpgp-s2k3-sha1-aes-cbc " + "(" + "(sha1 #FA8123F1A37CBC1F# \"3812352\")" + "#7671C7387E2DD931CC62C35CBBE08A28#)" + "#75e928f4698172b61dffe9ef2ada1d3473f690f3879c5386e2717e5b2fa46884" + "b189ee409827aab0ff37f62996e040b5fa7e75fc4d8152c8734e2e648dff90c9" + "e8c3e39ea7485618d05c34b1b74ff59676e9a3d932245cc101b5904777a09f86#)" + "(protected-at \"20150928T050210\")" + ")" + "(comment \"eddsa w/o comment\")" + ")", /* Passphrase="abc" */ + "MD5:f1:fa:c8:a6:40:bb:b9:a1:65:d7:62:65:ac:26:78:0e", + "SHA256:yhwBfYnTOnSXcWf1EOPo+oIIpNJ6w/bG36udZ96MmsQ" + }, + { + NULL, + NULL + } +}; + + + +static char * +read_file (const char *fname, size_t *r_length) +{ + FILE *fp; + char *buf; + size_t buflen; + struct stat st; + + fp = fopen (fname, "rb"); + if (!fp) + { + fprintf (stderr, "%s:%d: can't open '%s': %s\n", + __FILE__, __LINE__, fname, strerror (errno)); + exit (1); + } + + if (fstat (fileno(fp), &st)) + { + fprintf (stderr, "%s:%d: can't stat '%s': %s\n", + __FILE__, __LINE__, fname, strerror (errno)); + exit (1); + } + + buflen = st.st_size; + buf = xmalloc (buflen+1); + if (fread (buf, buflen, 1, fp) != 1) + { + fprintf (stderr, "%s:%d: error reading '%s': %s\n", + __FILE__, __LINE__, fname, strerror (errno)); + exit (1); + } + fclose (fp); + + *r_length = buflen; + return buf; +} + + +static gcry_sexp_t +read_key (const char *fname) +{ + gpg_error_t err; + char *buf; + size_t buflen; + gcry_sexp_t key; + + buf = read_file (fname, &buflen); + + err = gcry_sexp_sscan (&key, NULL, buf, buflen); + if (err) + { + fprintf (stderr, "%s:%d: gcry_sexp_sscan failed: %s\n", + __FILE__, __LINE__, gpg_strerror (err)); + exit (1); \ + } + + xfree (buf); + return key; +} + + +int +main (int argc, char **argv) +{ + gpg_error_t err; + gcry_sexp_t key; + char *string; + int idx; + + /* --dump-keys dumps the keys as KEYGRIP.key.IDX. Useful to compute + fingerprints to enhance the test vectors. */ + if (argc == 2 && strcmp (argv[1], "--dump-keys") == 0) + for (idx=0; sample_keys[idx].key; idx++) + { + FILE *s; + char *name; + char grip[20]; + char *hexgrip; + + err = keygrip_from_canon_sexp (sample_keys[idx].key, + strlen (sample_keys[idx].key), + grip); + if (err) + { + fprintf (stderr, "%s:%d: error computing keygrip: %s\n", + __FILE__, __LINE__, gpg_strerror (err)); + exit (1); + } + hexgrip = bin2hex (grip, 20, NULL); + + name = xtryasprintf ("%s.key.%d", hexgrip, idx); + s = fopen (name, "w"); + if (s == NULL) + { + fprintf (stderr, "%s:%d: error opening file: %s\n", + __FILE__, __LINE__, gpg_strerror (gpg_error_from_syserror ())); + exit (1); + } + xfree (name); + fprintf (s, "%s", sample_keys[idx].key); + fclose (s); + } + else if (argc == 2) + { + key = read_key (argv[1]); + + err = ssh_get_fingerprint_string (key, GCRY_MD_MD5, &string); + if (err) + { + fprintf (stderr, "%s:%d: error getting fingerprint: %s\n", + __FILE__, __LINE__, gpg_strerror (err)); + exit (1); + } + puts (string); + xfree (string); + + err = ssh_get_fingerprint_string (key, GCRY_MD_SHA256, &string); + if (err) + { + fprintf (stderr, "%s:%d: error getting fingerprint: %s\n", + __FILE__, __LINE__, gpg_strerror (err)); + exit (1); + } + puts (string); + xfree (string); + + gcry_sexp_release (key); + } + else + { + for (idx=0; sample_keys[idx].key; idx++) + { + err = gcry_sexp_sscan (&key, NULL, sample_keys[idx].key, + strlen (sample_keys[idx].key)); + if (err) + { + fprintf (stderr, "%s:%d: gcry_sexp_sscan failed for " + "sample key %d: %s\n", + __FILE__, __LINE__, idx, gpg_strerror (err)); + exit (1); + } + + err = ssh_get_fingerprint_string (key, GCRY_MD_MD5, &string); + if (err) + { + fprintf (stderr, "%s:%d: error getting fingerprint for " + "sample key %d: %s\n", + __FILE__, __LINE__, idx, gpg_strerror (err)); + exit (1); + } + + if (strcmp (string, sample_keys[idx].fpr_md5)) + { + fprintf (stderr, "%s:%d: fingerprint mismatch for " + "sample key %d\n", + __FILE__, __LINE__, idx); + fprintf (stderr, "want: %s\n got: %s\n", + sample_keys[idx].fpr_md5, string); + exit (1); + } + xfree (string); + + err = ssh_get_fingerprint_string (key, GCRY_MD_SHA256, &string); + if (err) + { + fprintf (stderr, "%s:%d: error getting fingerprint for " + "sample key %d: %s\n", + __FILE__, __LINE__, idx, gpg_strerror (err)); + exit (1); + } + + if (strcmp (string, sample_keys[idx].fpr_sha256)) + { + fprintf (stderr, "%s:%d: fingerprint mismatch for " + "sample key %d\n", + __FILE__, __LINE__, idx); + fprintf (stderr, "want: %s\n got: %s\n", + sample_keys[idx].fpr_sha256, string); + exit (1); + } + xfree (string); + + gcry_sexp_release (key); + } + } + + return 0; +} diff --git a/common/t-stringhelp.c b/common/t-stringhelp.c new file mode 100644 index 0000000..d76991f --- /dev/null +++ b/common/t-stringhelp.c @@ -0,0 +1,1319 @@ +/* t-stringhelp.c - Regression tests for stringhelp.c + * Copyright (C) 2007 Free Software Foundation, Inc. + * 2015, 2021 g10 Code GmbH + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute and/or modify this + * part of GnuPG under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * GnuPG is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copies of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, see <https://www.gnu.org/licenses/>. + * SPDX-License-Identifier: (LGPL-3.0-or-later OR GPL-2.0-or-later) + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#ifdef HAVE_PWD_H +# include <pwd.h> +#endif +#include <unistd.h> +#include <sys/types.h> +#include <limits.h> +#include <assert.h> + +#include "t-support.h" +#include "sysutils.h" +#include "stringhelp.h" + + +static char *home_buffer; + + +const char * +gethome (void) +{ + if (!home_buffer) + { + char *home = getenv("HOME"); + + if(home) + home_buffer = xstrdup (home); +#if defined(HAVE_GETPWUID) && defined(HAVE_PWD_H) + else + { + struct passwd *pwd; + + pwd = getpwuid (getuid()); + if (pwd) + home_buffer = xstrdup (pwd->pw_dir); + } +#endif + } + return home_buffer; +} + + +static char * +mygetcwd (void) +{ + char *buffer; + size_t size = 100; + + for (;;) + { + buffer = xmalloc (size+1); +#ifdef HAVE_W32CE_SYSTEM + strcpy (buffer, "/"); /* Always "/". */ + return buffer; +#else + if (getcwd (buffer, size) == buffer) + return buffer; + xfree (buffer); + if (errno != ERANGE) + { + fprintf (stderr,"error getting current cwd: %s\n", + strerror (errno)); + exit (2); + } + size *= 2; +#endif + } +} + + +static void +test_percent_escape (void) +{ + char *result; + static struct { + const char *extra; + const char *value; + const char *expected; + } tests[] = + { + { NULL, "", "" }, + { NULL, "%", "%25" }, + { NULL, "%%", "%25%25" }, + { NULL, " %", " %25" }, + { NULL, ":", "%3a" }, + { NULL, " :", " %3a" }, + { NULL, ": ", "%3a " }, + { NULL, " : ", " %3a " }, + { NULL, "::", "%3a%3a" }, + { NULL, ": :", "%3a %3a" }, + { NULL, "%:", "%25%3a" }, + { NULL, ":%", "%3a%25" }, + { "\\\n:", ":%", "%3a%25" }, + { "\\\n:", "\\:%", "%5c%3a%25" }, + { "\\\n:", "\n:%", "%0a%3a%25" }, + { "\\\n:", "\xff:%", "\xff%3a%25" }, + { "\\\n:", "\xfe:%", "\xfe%3a%25" }, + { "\\\n:", "\x01:%", "\x01%3a%25" }, + { "\x01", "\x01:%", "%01%3a%25" }, + { "\xfe", "\xfe:%", "%fe%3a%25" }, + { "\xfe", "\xff:%", "\xff%3a%25" }, + + { NULL, NULL, NULL } + }; + int testno; + + result = percent_escape (NULL, NULL); + if (result) + fail (0); + for (testno=0; tests[testno].value; testno++) + { + result = percent_escape (tests[testno].value, tests[testno].extra); + if (!result) + fail (testno); + else if (strcmp (result, tests[testno].expected)) + fail (testno); + xfree (result); + } + +} + + +static void +test_compare_filenames (void) +{ + struct { + const char *a; + const char *b; + int result; + } tests[] = { + { "", "", 0 }, + { "", "a", -1 }, + { "a", "", 1 }, + { "a", "a", 0 }, + { "a", "aa", -1 }, + { "aa", "a", 1 }, + { "a", "b", -1 }, + +#ifdef HAVE_W32_SYSTEM + { "a", "A", 0 }, + { "A", "a", 0 }, + { "foo/bar", "foo\\bar", 0 }, + { "foo\\bar", "foo/bar", 0 }, + { "foo\\", "foo/", 0 }, + { "foo/", "foo\\", 0 }, +#endif /*HAVE_W32_SYSTEM*/ + { NULL, NULL, 0} + }; + int testno, result; + + for (testno=0; tests[testno].a; testno++) + { + result = compare_filenames (tests[testno].a, tests[testno].b); + result = result < 0? -1 : result > 0? 1 : 0; + if (result != tests[testno].result) + fail (testno); + } +} + + +static void +test_strconcat (void) +{ + char *out; + + out = strconcat ("1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", NULL); + if (!out) + fail (0); + else + xfree (out); + out = strconcat ("1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", NULL); + if (out) + fail (0); + else if (errno != EINVAL) + fail (0); + + out = strconcat ("1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", NULL); + if (out) + fail (0); + else if (errno != EINVAL) + fail (0); + xfree (out); + +#if __GNUC__ < 4 /* gcc 4.0 has a sentinel attribute. */ + out = strconcat (NULL); + if (!out || *out) + fail (1); +#endif + out = strconcat (NULL, NULL); + if (!out || *out) + fail (1); + xfree (out); + + out = strconcat ("", NULL); + if (!out || *out) + fail (1); + xfree (out); + + out = strconcat ("", "", NULL); + if (!out || *out) + fail (2); + xfree (out); + + out = strconcat ("a", "b", NULL); + if (!out || strcmp (out, "ab")) + fail (3); + xfree (out); + out = strconcat ("a", "b", "c", NULL); + if (!out || strcmp (out, "abc")) + fail (3); + xfree (out); + + out = strconcat ("a", "b", "cc", NULL); + if (!out || strcmp (out, "abcc")) + fail (4); + xfree (out); + out = strconcat ("a1", "b1", "c1", NULL); + if (!out || strcmp (out, "a1b1c1")) + fail (4); + xfree (out); + + out = strconcat ("", " long b ", "", "--even-longer--", NULL); + if (!out || strcmp (out, " long b --even-longer--")) + fail (5); + xfree (out); + + out = strconcat ("", " long b ", "", "--even-longer--", NULL); + if (!out || strcmp (out, " long b --even-longer--")) + fail (5); + xfree (out); +} + +static void +test_xstrconcat (void) +{ + char *out; + + out = xstrconcat ("1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", NULL); + if (!out) + fail (0); + xfree (out); + +#if __GNUC__ < 4 /* gcc 4.0 has a sentinel attribute. */ + out = xstrconcat (NULL); + if (!out) + fail (1); +#endif + out = xstrconcat (NULL, NULL); + if (!out) + fail (1); + xfree (out); + + out = xstrconcat ("", NULL); + if (!out || *out) + fail (1); + xfree (out); + + out = xstrconcat ("", "", NULL); + if (!out || *out) + fail (2); + xfree (out); + + out = xstrconcat ("a", "b", NULL); + if (!out || strcmp (out, "ab")) + fail (3); + xfree (out); + out = xstrconcat ("a", "b", "c", NULL); + if (!out || strcmp (out, "abc")) + fail (3); + xfree (out); + + out = xstrconcat ("a", "b", "cc", NULL); + if (!out || strcmp (out, "abcc")) + fail (4); + xfree (out); + out = xstrconcat ("a1", "b1", "c1", NULL); + if (!out || strcmp (out, "a1b1c1")) + fail (4); + xfree (out); + + out = xstrconcat ("", " long b ", "", "--even-longer--", NULL); + if (!out || strcmp (out, " long b --even-longer--")) + fail (5); + xfree (out); + + out = xstrconcat ("", " long b ", "", "--even-longer--", NULL); + if (!out || strcmp (out, " long b --even-longer--")) + fail (5); + xfree (out); +} + + +static void +test_make_filename_try (void) +{ + char *out; + const char *home = gethome (); + size_t homelen = home? strlen (home):0; + + out = make_filename_try ("1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", NULL); + if (out) + fail (0); + else if (errno != EINVAL) + fail (0); + xfree (out); + out = make_filename_try ("1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", NULL); + if (out) + fail (0); + else if (errno != EINVAL) + fail (0); + xfree (out); + + out = make_filename_try ("1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "1", "2", NULL); + if (!out || strcmp (out, + "1/2/3/4/5/6/7/8/9/10/" + "1/2/3/4/5/6/7/8/9/10/" + "1/2/3/4/5/6/7/8/9/10/" + "1/2")) + fail (0); + xfree (out); + + out = make_filename_try ("foo", "~/bar", "baz/cde", NULL); + if (!out || strcmp (out, "foo/~/bar/baz/cde")) + fail (1); + xfree (out); + + out = make_filename_try ("foo", "~/bar", "baz/cde/", NULL); + if (!out || strcmp (out, "foo/~/bar/baz/cde/")) + fail (1); + xfree (out); + + out = make_filename_try ("/foo", "~/bar", "baz/cde/", NULL); + if (!out || strcmp (out, "/foo/~/bar/baz/cde/")) + fail (1); + xfree (out); + + out = make_filename_try ("//foo", "~/bar", "baz/cde/", NULL); + if (!out || strcmp (out, "//foo/~/bar/baz/cde/")) + fail (1); + xfree (out); + + out = make_filename_try ("", "~/bar", "baz/cde", NULL); + if (!out || strcmp (out, "/~/bar/baz/cde")) + fail (1); + xfree (out); + + + out = make_filename_try ("~/foo", "bar", NULL); + if (!out) + fail (2); + else if (home) + { + if (strlen (out) < homelen + 7) + fail (2); + else if (strncmp (out, home, homelen)) + fail (2); + else if (strcmp (out+homelen, "/foo/bar")) + fail (2); + } + else + { + if (strcmp (out, "~/foo/bar")) + fail (2); + } + xfree (out); + + out = make_filename_try ("~", "bar", NULL); + if (!out) + fail (2); + else if (home) + { + if (strlen (out) < homelen + 3) + fail (2); + else if (strncmp (out, home, homelen)) + fail (2); + else if (strcmp (out+homelen, "/bar")) + fail (2); + } + else + { + if (strcmp (out, "~/bar")) + fail (2); + } + xfree (out); +} + + +static void +test_make_absfilename_try (void) +{ + char *out; + char *cwd = mygetcwd (); + size_t cwdlen = strlen (cwd); + + out = make_absfilename_try ("foo", "bar", NULL); + if (!out) + fail (0); + else if (strlen (out) < cwdlen + 7) + fail (0); + else if (strncmp (out, cwd, cwdlen)) + fail (0); + else if (strcmp (out+cwdlen, "/foo/bar")) + fail (0); + xfree (out); + + out = make_absfilename_try ("./foo", NULL); + if (!out) + fail (1); + else if (strlen (out) < cwdlen + 5) + fail (1); + else if (strncmp (out, cwd, cwdlen)) + fail (1); + else if (strcmp (out+cwdlen, "/./foo")) + fail (1); + xfree (out); + + out = make_absfilename_try (".", NULL); + if (!out) + fail (2); + else if (strlen (out) < cwdlen) + fail (2); + else if (strncmp (out, cwd, cwdlen)) + fail (2); + else if (strcmp (out+cwdlen, "")) + fail (2); + xfree (out); + + xfree (cwd); +} + +static void +test_strsplit (void) +{ + struct { + const char *s; + char delim; + char replacement; + const char *fields_expected[10]; + } tv[] = { + { + "a:bc:cde:fghi:jklmn::foo:", ':', '\0', + { "a", "bc", "cde", "fghi", "jklmn", "", "foo", "", NULL } + }, + { + ",a,bc,,def,", ',', '!', + { "!a!bc!!def!", "a!bc!!def!", "bc!!def!", "!def!", "def!", "", NULL } + }, + { + "", ':', ',', + { "", NULL } + } + }; + + int tidx; + + for (tidx = 0; tidx < DIM(tv); tidx++) + { + char *s2; + int field_count; + char **fields; + int field_count_expected; + int i; + + /* Count the fields. */ + for (field_count_expected = 0; + tv[tidx].fields_expected[field_count_expected]; + field_count_expected ++) + ; + + /* We need to copy s since strsplit modifies it in place. */ + s2 = xstrdup (tv[tidx].s); + fields = strsplit (s2, tv[tidx].delim, tv[tidx].replacement, + &field_count); + + if (field_count != field_count_expected) + fail (tidx * 1000); + + for (i = 0; i < field_count_expected; i ++) + if (strcmp (tv[tidx].fields_expected[i], fields[i]) != 0) + { + printf ("For field %d, expected '%s', but got '%s'\n", + i, tv[tidx].fields_expected[i], fields[i]); + fail (tidx * 1000 + i + 1); + } + + xfree (fields); + xfree (s2); + } +} + + + +static void +test_strtokenize (void) +{ + struct { + const char *s; + const char *delim; + const char *fields_expected[10]; + } tv[] = { + { + "", ":", + { "", NULL } + }, + { + "a", ":", + { "a", NULL } + }, + { + ":", ":", + { "", "", NULL } + }, + { + "::", ":", + { "", "", "", NULL } + }, + { + "a:b:c", ":", + { "a", "b", "c", NULL } + }, + { + "a:b:", ":", + { "a", "b", "", NULL } + }, + { + "a:b", ":", + { "a", "b", NULL } + }, + { + "aa:b:cd", ":", + { "aa", "b", "cd", NULL } + }, + { + "aa::b:cd", ":", + { "aa", "", "b", "cd", NULL } + }, + { + "::b:cd", ":", + { "", "", "b", "cd", NULL } + }, + { + "aa: : b:cd ", ":", + { "aa", "", "b", "cd", NULL } + }, + { + " aa: : b: cd ", ":", + { "aa", "", "b", "cd", NULL } + }, + { + " ", ":", + { "", NULL } + }, + { + " :", ":", + { "", "", NULL } + }, + { + " : ", ":", + { "", "", NULL } + }, + { + ": ", ":", + { "", "", NULL } + }, + { + ": x ", ":", + { "", "x", NULL } + }, + { + "a:bc:cde:fghi:jklmn::foo:", ":", + { "a", "bc", "cde", "fghi", "jklmn", "", "foo", "", NULL } + }, + { + ",a,bc,,def,", ",", + { "", "a", "bc", "", "def", "", NULL } + }, + { + " a ", " ", + { "", "a", "", NULL } + }, + { + " ", " ", + { "", "", NULL } + }, + { + "", " ", + { "", NULL } + } + }; + + int tidx; + + for (tidx = 0; tidx < DIM(tv); tidx++) + { + char **fields; + int field_count; + int field_count_expected; + int i; + + for (field_count_expected = 0; + tv[tidx].fields_expected[field_count_expected]; + field_count_expected ++) + ; + + fields = strtokenize (tv[tidx].s, tv[tidx].delim); + if (!fields) + fail (tidx * 1000); + else + { + for (field_count = 0; fields[field_count]; field_count++) + ; + if (field_count != field_count_expected) + fail (tidx * 1000); + else + { + for (i = 0; i < field_count_expected; i++) + if (strcmp (tv[tidx].fields_expected[i], fields[i])) + { + printf ("For field %d, expected '%s', but got '%s'\n", + i, tv[tidx].fields_expected[i], fields[i]); + fail (tidx * 1000 + i + 1); + } + } + } + + xfree (fields); + } +} + + +static void +test_strtokenize_nt (void) +{ + struct { + const char *s; + const char *delim; + const char *fields_expected[10]; + } tv[] = { + { + "", ":", + { "", NULL } + }, + { + "a", ":", + { "a", NULL } + }, + { + ":", ":", + { "", "", NULL } + }, + { + "::", ":", + { "", "", "", NULL } + }, + { + "a:b:c", ":", + { "a", "b", "c", NULL } + }, + { + "a:b:", ":", + { "a", "b", "", NULL } + }, + { + "a:b", ":", + { "a", "b", NULL } + }, + { + "aa:b:cd", ":", + { "aa", "b", "cd", NULL } + }, + { + "aa::b:cd", ":", + { "aa", "", "b", "cd", NULL } + }, + { + "::b:cd", ":", + { "", "", "b", "cd", NULL } + }, + { + "aa: : b:cd ", ":", + { "aa", " ", " b", "cd ", NULL } + }, + { + " aa: : b: cd ", ":", + { " aa", " ", " b", " cd ", NULL } + }, + { + " ", ":", + { " ", NULL } + }, + { + " :", ":", + { " ", "", NULL } + }, + { + " : ", ":", + { " ", " ", NULL } + }, + { + ": ", ":", + { "", " ", NULL } + }, + { + ": x ", ":", + { "", " x ", NULL } + }, + { + "a:bc:cde:fghi:jklmn::foo:", ":", + { "a", "bc", "cde", "fghi", "jklmn", "", "foo", "", NULL } + }, + { + ",a,bc,,def,", ",", + { "", "a", "bc", "", "def", "", NULL } + }, + { + " a ", " ", + { "", "a", "", NULL } + }, + { + " ", " ", + { "", "", NULL } + }, + { + "", " ", + { "", NULL } + } + }; + + int tidx; + + for (tidx = 0; tidx < DIM(tv); tidx++) + { + char **fields; + int field_count; + int field_count_expected; + int i; + + for (field_count_expected = 0; + tv[tidx].fields_expected[field_count_expected]; + field_count_expected ++) + ; + + fields = strtokenize_nt (tv[tidx].s, tv[tidx].delim); + if (!fields) + fail (tidx * 1000); + else + { + for (field_count = 0; fields[field_count]; field_count++) + ; + if (field_count != field_count_expected) + fail (tidx * 1000); + else + { + for (i = 0; i < field_count_expected; i++) + if (strcmp (tv[tidx].fields_expected[i], fields[i])) + { + printf ("For field %d, expected '%s', but got '%s'\n", + i, tv[tidx].fields_expected[i], fields[i]); + fail (tidx * 1000 + i + 1); + } + } + } + + xfree (fields); + } +} + + +static void +test_split_fields (void) +{ + struct { + const char *s; + int nfields; + const char *fields_expected[10]; + } tv[] = { + { + "a bc cde fghi jklmn foo ", 6, + { "a", "bc", "cde", "fghi", "jklmn", "foo", NULL } + }, + { + " a bc def ", 2, + { "a", "bc", "def", NULL } + }, + { + " a bc def ", 3, + { "a", "bc", "def", NULL } + }, + { + " a bc def ", 4, + { "a", "bc", "def", NULL } + }, + { + "", 0, + { NULL } + } + }; + + int tidx; + char *fields[10]; + int field_count_expected, nfields, field_count, i; + char *s2; + + for (tidx = 0; tidx < DIM(tv); tidx++) + { + nfields = tv[tidx].nfields; + assert (nfields <= DIM (fields)); + + /* Count the fields. */ + for (field_count_expected = 0; + tv[tidx].fields_expected[field_count_expected]; + field_count_expected ++) + ; + if (field_count_expected > nfields) + field_count_expected = nfields; + + /* We need to copy s since split_fields modifies in place. */ + s2 = xstrdup (tv[tidx].s); + field_count = split_fields (s2, fields, nfields); + + if (field_count != field_count_expected) + { + printf ("%s: tidx %d: expected %d, got %d\n", + __func__, tidx, field_count_expected, field_count); + fail (tidx * 1000); + } + else + { + for (i = 0; i < field_count_expected; i ++) + if (strcmp (tv[tidx].fields_expected[i], fields[i])) + { + printf ("%s: tidx %d, field %d: expected '%s', got '%s'\n", + __func__, + tidx, i, tv[tidx].fields_expected[i], fields[i]); + fail (tidx * 1000 + i + 1); + } + } + + xfree (s2); + } +} + + +static void +test_split_fields_colon (void) +{ + struct { + const char *s; + int nfields; + const char *fields_expected[10]; + } tv[] = { + { + "a:bc:cde:fghi:jklmn: foo ", 6, + { "a", "bc", "cde", "fghi", "jklmn", " foo ", NULL } + }, + { + " a:bc: def ", 2, + { " a", "bc", NULL } + }, + { + " a:bc :def ", 3, + { " a", "bc ", "def ", NULL } + }, + { + " a:bc: def ", 4, + { " a", "bc", " def ", NULL } + }, + { + "", 0, + { NULL } + } + }; + + int tidx; + char *fields[10]; + int field_count_expected, nfields, field_count, i; + char *s2; + + for (tidx = 0; tidx < DIM(tv); tidx++) + { + nfields = tv[tidx].nfields; + assert (nfields <= DIM (fields)); + + /* Count the fields. */ + for (field_count_expected = 0; + tv[tidx].fields_expected[field_count_expected]; + field_count_expected ++) + ; + if (field_count_expected > nfields) + field_count_expected = nfields; + + /* We need to copy s since split_fields modifies in place. */ + s2 = xstrdup (tv[tidx].s); + field_count = split_fields_colon (s2, fields, nfields); + + if (field_count != field_count_expected) + { + printf ("%s: tidx %d: expected %d, got %d\n", + __func__, tidx, field_count_expected, field_count); + fail (tidx * 1000); + } + else + { + for (i = 0; i < field_count_expected; i ++) + if (strcmp (tv[tidx].fields_expected[i], fields[i])) + { + printf ("%s: tidx %d, field %d: expected '%s', got '%s'\n", + __func__, + tidx, i, tv[tidx].fields_expected[i], fields[i]); + fail (tidx * 1000 + i + 1); + } + } + + xfree (s2); + } +} + + +static char * +stresc (char *s) +{ + char *p; + int l = strlen (s) + 1; + + for (p = s; *p; p ++) + if (*p == '\n') + l ++; + + p = xmalloc (l); + for (l = 0; *s; s ++, l ++) + { + if (*s == ' ') + p[l] = '_'; + else if (*p == '\n') + { + p[l ++] = '\\'; + p[l ++] = 'n'; + p[l] = '\n'; + } + else + p[l] = *s; + } + p[l] = *s; + + return p; +} + + +static void +test_format_text (void) +{ + struct test + { + int target_cols, max_cols; + char *input; + char *expected; + }; + + struct test tests[] = { + { + 10, 12, + "", + "", + }, + { + 10, 12, + " ", + "", + }, + { + 10, 12, + " ", + "", + }, + { + 10, 12, + " \n ", + " \n", + }, + { + 10, 12, + " \n \n ", + " \n \n", + }, + { + 10, 12, + "0123456789 0123456789 0", + "0123456789\n0123456789\n0", + }, + { + 10, 12, + " 0123456789 0123456789 0 ", + " 0123456789\n0123456789\n0", + }, + { + 10, 12, + "01 34 67 90 23 56 89 12 45 67 89 1", + "01 34 67\n90 23 56\n89 12 45\n67 89 1" + }, + { + 10, 12, + "01 34 67 90 23 56 89 12 45 67 89 1", + "01 34 67\n90 23 56\n89 12 45\n67 89 1" + }, + { + 72, 80, + "Warning: if you think you've seen more than 10 messages " + "signed by this key, then this key might be a forgery! " + "Carefully examine the email address for small variations " + "(e.g., additional white space). If the key is suspect, " + "then use 'gpg --tofu-policy bad \"FINGERPRINT\"' to mark it as being bad.\n", + "Warning: if you think you've seen more than 10 messages signed by this\n" + "key, then this key might be a forgery! Carefully examine the email\n" + "address for small variations (e.g., additional white space). If the key\n" + "is suspect, then use 'gpg --tofu-policy bad \"FINGERPRINT\"' to mark it as\n" + "being bad.\n" + + }, + { + 72, 80, + "Normally, there is only a single key associated with an email " + "address. However, people sometimes generate a new key if " + "their key is too old or they think it might be compromised. " + "Alternatively, a new key may indicate a man-in-the-middle " + "attack! Before accepting this key, you should talk to or " + "call the person to make sure this new key is legitimate.", + "Normally, there is only a single key associated with an email " + "address.\nHowever, people sometimes generate a new key if " + "their key is too old or\nthey think it might be compromised. " + "Alternatively, a new key may indicate\na man-in-the-middle " + "attack! Before accepting this key, you should talk\nto or " + "call the person to make sure this new key is legitimate.", + } + }; + + int i; + int failed = 0; + + for (i = 0; i < sizeof (tests) / sizeof (tests[0]); i ++) + { + struct test *test = &tests[i]; + char *result = + format_text (test->input, test->target_cols, test->max_cols); + if (!result) + { + fail (1); + exit (2); + } + if (strcmp (result, test->expected) != 0) + { + printf ("%s: Test #%d failed.\nExpected: '%s'\nResult: '%s'\n", + __func__, i + 1, stresc (test->expected), stresc (result)); + failed ++; + } + xfree (result); + } + + if (failed) + fail(0); +} + + +static void +test_compare_version_strings (void) +{ + struct { const char *a; const char *b; int okay; } tests[] = { + { "1.0.0", "1.0.0", 0 }, + { "1.0.0-", "1.0.0", 1 }, + { "1.0.0-1", "1.0.0", 1 }, + { "1.0.0.1", "1.0.0", 1 }, + { "1.0.0", "1.0.1", -1 }, + { "1.0.0-", "1.0.1", -1 }, + { "1.0.0-1", "1.0.1", -1 }, + { "1.0.0.1", "1.0.1", -1 }, + { "1.0.0", "1.1.0", -1 }, + { "1.0.0-", "1.1.0", -1 }, + { "1.0.0-1", "1.1.0", -1 }, + { "1.0.0.1", "1.1.0", -1 }, + + { "1.0.0", "1.0.0-", -1 }, + { "1.0.0", "1.0.0-1", -1 }, + { "1.0.0", "1.0.0.1", -1 }, + { "1.1.0", "1.0.0", 1 }, + { "1.1.1", "1.1.0", 1 }, + { "1.1.2", "1.1.2", 0 }, + { "1.1.2", "1.0.2", 1 }, + { "1.1.2", "0.0.2", 1 }, + { "1.1.2", "1.1.3", -1 }, + + { "0.99.1", "0.9.9", 1 }, + { "0.9.1", "0.91.0", -1 }, + + { "1.5.3", "1.5", 1 }, + { "1.5.0", "1.5", 0 }, + { "1.4.99", "1.5", -1 }, + { "1.5", "1.4.99", 1 }, + { "1.5", "1.5.0", 0 }, + { "1.5", "1.5.1", -1 }, + + { "1.5.3-x17", "1.5-23", 1 }, + + { "1.5.3a", "1.5.3", 1 }, + { "1.5.3a", "1.5.3b", -1 }, + + { "3.1.4-ab", "3.1.4-ab", 0 }, + { "3.1.4-ab", "3.1.4-ac", -1 }, + { "3.1.4-ac", "3.1.4-ab", 1 }, + { "3.1.4-ab", "3.1.4-abb", -1 }, + { "3.1.4-abb", "3.1.4-ab", 1 }, + + { "", "", INT_MIN }, + { NULL, "", INT_MIN }, + { "1.2.3", "", INT_MIN }, + { "1.2.3", "2", INT_MIN }, + + /* Test cases for validity of A. */ + { "", NULL, INT_MIN }, + { "1", NULL, INT_MIN }, + { "1.", NULL, 0 }, + { "1.0", NULL, 0 }, + { "1.0.", NULL, 0 }, + { "a1.2", NULL, INT_MIN }, + { NULL, NULL, INT_MIN } + }; + int idx; + int res; + + for (idx=0; idx < DIM(tests); idx++) + { + res = compare_version_strings (tests[idx].a, tests[idx].b); + /* printf ("test %d: '%s' '%s' %d -> %d\n", */ + /* idx, tests[idx].a, tests[idx].b, tests[idx].okay, res); */ + if (res != tests[idx].okay) + fail (idx); + } +} + + +static void +test_substitute_envvars (void) +{ + struct { + const char *name; + const char *value; + } envvars[] = { + { "HOME", "/home/joe" }, + { "AVAR", "avar" }, + { "AVAR1", "avarx" }, + { "AVAR2", "avarxy" }, + { "AVAR3", "avarxyz" }, + { "AVAR0", "ava" }, + { "MY_VAR", "my_vars_value" }, + { "STRANGE{X}VAR", "strange{x}vars-value" }, + { "ZERO", "" } + }; + struct { + const char *string; + const char *result; + } tests[] = { + { "foo bar", + "foo bar" + }, + { "foo $HOME", + "foo /home/joe" + }, + { "foo $HOME ", + "foo /home/joe " + }, + { "foo $HOME$$", + "foo /home/joe$" + }, + { "foo ${HOME}/.ssh", + "foo /home/joe/.ssh" + }, + { "foo $HOME/.ssh", + "foo /home/joe/.ssh" + }, + { "foo $HOME_/.ssh", + "foo /.ssh" + }, + { "foo $HOME/.ssh/$MY_VAR:1", + "foo /home/joe/.ssh/my_vars_value:1" + }, + { "foo $HOME${MY_VAR}:1", + "foo /home/joemy_vars_value:1" + }, + { "${STRANGE{X}VAR}-bla", + "strange{x}vars-value-bla" + }, + { "${STRANGE{X}{VAR}-bla", /* missing "}" */ + "${STRANGE{X}{VAR}-bla" + }, + { "zero->$ZERO<-", + "zero-><-" + }, + { "->$AVAR.$AVAR1.$AVAR2.$AVAR3.$AVAR0<-", + "->avar.avarx.avarxy.avarxyz.ava<-" + }, + { "", + "" + } + }; + int idx; + char *res; + + for (idx=0; idx < DIM(envvars); idx++) + if (gnupg_setenv (envvars[idx].name, envvars[idx].value, 1)) + { + fprintf (stderr,"error setting envvar '%s' to '%s': %s\n", + envvars[idx].name, envvars[idx].value, + strerror (errno)); + exit (2); + } + + for (idx=0; idx < DIM(tests); idx++) + { + res = substitute_envvars (tests[idx].string); + if (!res) + { + fprintf (stderr,"error substituting '%s' (test %d): %s\n", + tests[idx].string, idx, strerror (errno)); + exit (2); + } + if (strcmp (res, tests[idx].result)) + { + fprintf (stderr, "substituted '%s'\n", tests[idx].string); + fprintf (stderr, " wanted '%s'\n", tests[idx].result); + fprintf (stderr, " got '%s'\n", res); + fail (idx); + } + xfree (res); + } +} + + +int +main (int argc, char **argv) +{ + (void)argc; + (void)argv; + + test_percent_escape (); + test_compare_filenames (); + test_strconcat (); + test_xstrconcat (); + test_make_filename_try (); + test_make_absfilename_try (); + test_strsplit (); + test_strtokenize (); + test_strtokenize_nt (); + test_split_fields (); + test_split_fields_colon (); + test_compare_version_strings (); + test_format_text (); + test_substitute_envvars (); + + xfree (home_buffer); + return !!errcount; +} diff --git a/common/t-strlist.c b/common/t-strlist.c new file mode 100644 index 0000000..fdbeb9b --- /dev/null +++ b/common/t-strlist.c @@ -0,0 +1,84 @@ +/* t-strlist.c - Regression tests for strist.c + * Copyright (C) 2015 g10 Code GmbH + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute and/or modify this + * part of GnuPG under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * GnuPG is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copies of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <string.h> + +#include "strlist.h" + +#include "t-support.h" + +static void +test_strlist_rev (void) +{ + strlist_t s = NULL; + + /* Reversing an empty list should yield the empty list. */ + if (! (strlist_rev (&s) == NULL)) + fail (1); + + add_to_strlist (&s, "1"); + add_to_strlist (&s, "2"); + add_to_strlist (&s, "3"); + + if (strcmp (s->d, "3") != 0) + fail (2); + if (strcmp (s->next->d, "2") != 0) + fail (2); + if (strcmp (s->next->next->d, "1") != 0) + fail (2); + if (s->next->next->next) + fail (2); + + strlist_rev (&s); + + if (strcmp (s->d, "1") != 0) + fail (2); + if (strcmp (s->next->d, "2") != 0) + fail (2); + if (strcmp (s->next->next->d, "3") != 0) + fail (2); + if (s->next->next->next) + fail (2); + + free_strlist (s); +} + + +int +main (int argc, char **argv) +{ + (void)argc; + (void)argv; + + test_strlist_rev (); + + return 0; +} diff --git a/common/t-support.h b/common/t-support.h new file mode 100644 index 0000000..7aa46c0 --- /dev/null +++ b/common/t-support.h @@ -0,0 +1,84 @@ +/* t-support.h - Helper for the regression tests + * Copyright (C) 2007 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute and/or modify this + * part of GnuPG under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * GnuPG is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copies of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef GNUPG_COMMON_T_SUPPORT_H +#define GNUPG_COMMON_T_SUPPORT_H 1 + +#ifdef GCRYPT_VERSION +#error The regression tests should not include with gcrypt.h +#endif + +#include <stdlib.h> +#include <stdio.h> + +#include <gpg-error.h> + + +#ifndef HAVE_GETENV +# define getenv(a) (NULL) +#endif + +#ifndef DIM +# define DIM(v) (sizeof(v)/sizeof((v)[0])) +# define DIMof(type,member) DIM(((type *)0)->member) +#endif + + +/* Replacement prototypes. */ +void *gcry_xmalloc (size_t n); +void *gcry_xcalloc (size_t n, size_t m); +void *gcry_xrealloc (void *a, size_t n); +char *gcry_xstrdup (const char * a); +void gcry_free (void *a); + +/* Map the used xmalloc functions to those implemented by t-support.c */ +#define xmalloc(a) gcry_xmalloc ( (a) ) +#define xcalloc(a,b) gcry_xcalloc ( (a), (b) ) +#define xrealloc(a,n) gcry_xrealloc ( (a), (n) ) +#define xstrdup(a) gcry_xstrdup ( (a) ) +#define xfree(a) gcry_free ( (a) ) + + +/* Macros to print the result of a test. */ +#define pass() do { ; } while(0) +#define fail(a) do { fprintf (stderr, "%s:%d: test %d failed\n",\ + __FILE__,__LINE__, (a)); \ + errcount++; \ + if (!no_exit_on_fail) \ + exit (1); \ + } while(0) + +/* If this flag is set the fail macro does not call exit. */ +static int no_exit_on_fail; +/* Error counter. */ +static int errcount; + + +#endif /*GNUPG_COMMON_T_SUPPORT_H*/ diff --git a/common/t-sysutils.c b/common/t-sysutils.c new file mode 100644 index 0000000..79f8385 --- /dev/null +++ b/common/t-sysutils.c @@ -0,0 +1,89 @@ +/* t-sysutils.c - Module test for sysutils.c + * Copyright (C) 2007 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> + +#include "util.h" +#include "sysutils.h" + +#ifdef HAVE_W32CE_SYSTEM +# define rewind(f) do { fseek (f, 0, SEEK_SET); clearerr (f); } while (0) +#endif + +#define pass() do { ; } while(0) +#define fail(a) do { fprintf (stderr, "%s:%d: test %d failed\n",\ + __FILE__,__LINE__, (a)); \ + errcount++; \ + } while(0) + +static int verbose; +static int errcount; + + +static void +test_gnupg_tmpfile (void) +{ + FILE *fparr[10]; + int fparridx; + int idx; + FILE *fp; + char buffer[100]; + +#define ASTRING "fooooooooooooooo\n" /* Needs to be shorter than BUFFER. */ + + for (fparridx=0; fparridx < DIM (fparr); fparridx++) + { + fp = gnupg_tmpfile (); + fparr[fparridx] = fp; + if (!fp) + fail (fparridx); + else + { + fputs ( ASTRING, fp); + rewind (fp); + if (!fgets (buffer, sizeof (buffer), fp)) + fail (fparridx); + if (strcmp (buffer, ASTRING)) + fail (fparridx); + if (fgets (buffer, sizeof (buffer), fp)) + fail (fparridx); + } + } + for (idx=0; idx < fparridx; idx++) + { + if (fparr[idx]) + fclose (fparr[idx]); + } +} + + + +int +main (int argc, char **argv) +{ + if (argc > 1 && !strcmp (argv[1], "--verbose")) + verbose = 1; + + test_gnupg_tmpfile (); + /* Fixme: Add tests for setenv and unsetenv. */ + + return !!errcount; +} diff --git a/common/t-timestuff.c b/common/t-timestuff.c new file mode 100644 index 0000000..6a75925 --- /dev/null +++ b/common/t-timestuff.c @@ -0,0 +1,175 @@ +/* t-timestuff.c - Regression tests for time functions + * Copyright (C) 2007 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute and/or modify this + * part of GnuPG under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * GnuPG is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copies of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <time.h> + +#include "mischelp.h" + +#include "t-support.h" + + +static int +cmp_time_s (struct tm *a, struct tm *b) +{ + if (a->tm_year != b->tm_year + || a->tm_mon != b->tm_mon + || a->tm_mday != b->tm_mday + || a->tm_hour != b->tm_hour + || a->tm_min != b->tm_min + || a->tm_sec != b->tm_sec + || a->tm_wday != b->tm_wday + || a->tm_yday != b->tm_yday + || !a->tm_isdst != !b->tm_isdst) + return -1; + return 0; +} + + + +static void +test_timegm (void) +{ + static struct { + int year, mon, mday, hour, min, sec; + } tvalues[] = { + { -1 }, + { -2, 1 }, + { -2, 2 }, + { -2, 86399 }, + { -2, 86400 }, + { -2, 0x7ffffffe }, + { -2, 0x7fffffff }, + /* Note: Because we use mktime below we can only start with the + day after Epoch. */ + { 1970, 0, 2, 0, 0 , 1}, + { 1970, 0, 2, 0, 0 , 2}, + { 1970, 0, 2, 12, 0 , 0}, + { 1970, 0, 2, 23, 59 , 59}, + { 1999, 11, 31, 23, 59 , 59}, + { 2000, 0, 1, 0, 0, 0}, + { 2000, 0, 1, 0, 0, 1}, + { 2010, 11, 31, 23, 59 , 59}, + { 2010, 0, 1, 0, 0, 0}, + { 2010, 0, 1, 0, 0, 1}, + /* On GNU based 32 bit systems the end of all ticks will be on + 20380119T031408 (unless Uli takes compassion on us and changes + time_t to a u64). We check that the previous day is okay. */ + { 2038, 0, 18, 23, 59, 59} + + }; + int tidx; + time_t now, atime; + struct tm tbuf, tbuf2, *tp; + + for (tidx=0; tidx < DIM (tvalues); tidx++) + { + if (tvalues[tidx].year == -1) + { + now = time (NULL); + } + else if (tvalues[tidx].year == -2) + { + now = tvalues[tidx].mon; + } + else + { + memset (&tbuf, 0, sizeof tbuf); + tbuf.tm_year = tvalues[tidx].year - 1900; + tbuf.tm_mon = tvalues[tidx].mon; + tbuf.tm_mday = tvalues[tidx].mday; + tbuf.tm_hour = tvalues[tidx].hour; + tbuf.tm_min = tvalues[tidx].min; + tbuf.tm_sec = tvalues[tidx].sec; +#ifdef HAVE_TIMEGM + now = timegm (&tbuf); +#else + now = mktime (&tbuf); +#endif + } + if (now == (time_t)(-1)) + fail (tidx); + + tp = gmtime (&now); + if (!tp) + fail (tidx); + else + { + tbuf = *tp; + tbuf2 = tbuf; +#ifdef HAVE_TIMEGM + atime = timegm (&tbuf); +#else + atime = mktime (&tbuf); +#endif + if (atime == (time_t)(-1)) + fail (tidx); + else if (atime != now) + fail (tidx); + + tp = gmtime (&atime); + if (!tp) + fail (tidx); + else if (cmp_time_s (tp, &tbuf)) + fail (tidx); + else if (cmp_time_s (tp, &tbuf2)) + fail (tidx); + } + } +} + + + +int +main (int argc, char **argv) +{ + (void)argc; + (void)argv; + + /* If we do not have timegm, we use mktime. However, we need to use + UTC in this case so that the 20380118T235959 test does not fail + for other timezones. */ +#ifndef HAVE_TIMEGM +# ifdef HAVE_SETENV + setenv ("TZ", "UTC", 1); +#else + putenv (xstrdup ("TZ=UTC")); +#endif + tzset (); +#endif + + test_timegm (); + + return 0; +} diff --git a/common/t-w32-cmdline.c b/common/t-w32-cmdline.c new file mode 100644 index 0000000..a1039d0 --- /dev/null +++ b/common/t-w32-cmdline.c @@ -0,0 +1,250 @@ +/* t-w32-cmdline.c - Test the parser for the Windows command line + * Copyright (C) 2021 g10 Code GmbH + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <unistd.h> +#ifdef HAVE_W32_SYSTEM +# define WIN32_LEAN_AND_MEAN +# include <windows.h> +#endif + +#include "t-support.h" +#include "utf8conv.h" +#include "w32help.h" + +#define PGM "t-w32-cmdline" + +static int verbose; +static int debug; +static int errcount; + + +static void +test_all (void) +{ + static struct { + const char *cmdline; + int argc; /* Expected number of args. */ + char *argv[10]; /* Expected results. */ + int use_glob; + } tests[] = { + /* Examples from "Parsing C++ Command-Line Arguments" dated 11/18/2006. + * https://docs.microsoft.com/en-us/previous-versions/17w5ykft(v=vs.85) + */ + { "\"abc\" d e", 3, { "abc", "d", "e" }}, + { "a\\\\\\b d\"e f\"g h", 3, { "a\\\\\\b", "de fg", "h" }}, + { "a\\\\\\\"b c d", 3, { "a\\\"b", "c", "d" }}, + { "a\\\\\\\\\"b c\" d e", 3, { "a\\\\b c", "d", "e" }}, + /* Examples from "Parsing C Command-Line Arguments" dated 11/09/2020. + * https://docs.microsoft.com/en-us/cpp/c-language/\ + * parsing-c-command-line-arguments?view=msvc-160 + */ + { "\"a b c\" d e", 3, { "a b c", "d", "e" }}, + { "\"ab\\\"c\" \"\\\\\" d", 3, { "ab\"c", "\\", "d" }}, + { "a\\\\\\b d\"e f\"g h", 3, { "a\\\\\\b", "de fg", "h" }}, + { "a\\\\\\\"b c d", 3, { "a\\\"b", "c", "d" }}, + { "a\\\\\\\\\"b c\" d e", 3, { "a\\\\b c", "d", "e" }}, + { "a\"b\"\" c d", 1, { "ab\" c d" }}, + /* Some arbitrary tests created using mingw. + * But I am not sure whether their parser is fully correct. + */ + { "e:a a b\"c\" ", 3, { "e:a", "a", "bc" }}, + /* { "e:a a b\"c\"\" d\"\"e \" ", */ + /* 5, { "e:a", "a", "bc\"", "de", " " }}, */ + /* { "e:a a b\"c\"\" d\"\"e\" f\\gh ", */ + /* 4, { "e:a", "a", "bc\"", "de f\\gh "}}, */ + /* { "e:a a b\"c\"\" d\"\"e\" f\\\"gh \" ", */ + /* 4, { "e:a", "a", "bc\"", "de f\"gh " }},*/ + + { "\"foo bar\"", 1 , { "foo bar" }}, + +#ifndef HAVE_W32_SYSTEM + /* We actually don't use this code on Unix but we provide a way to + * test some of the blobing code. */ + { "foo", 1, { "foo" }, 1 }, + { "foo*", 2, { "[* follows]", "foo*" }, 1 }, + { "foo?", 2, { "[? follows]", "foo?" }, 1 }, + { "? \"*\" *", 5, { "[? follows]", "?", "*", "[* follows]", "*" }, 1 }, +#endif /*!HAVE_W32_SYSTEM*/ + { "", 1 , { "" }} + }; + int tidx; + int i, any, itemsalloced, argc; + char *cmdline; + char **argv; + + for (tidx = 0; tidx < DIM(tests); tidx++) + { + cmdline = xstrdup (tests[tidx].cmdline); + if (verbose && tidx) + putchar ('\n'); + if (verbose) + printf ("test %d: line ->%s<-\n", tidx, cmdline); + argv = w32_parse_commandline (cmdline, tests[tidx].use_glob, + &argc, &itemsalloced); + if (!argv) + { + fail (tidx); + xfree (cmdline); + continue; + } + if (tests[tidx].argc != argc) + { + fprintf (stderr, PGM": test %d: argc wrong (want %d, got %d)\n", + tidx, tests[tidx].argc, argc); + any = 1; + } + else + any = 0; + for (i=0; i < tests[tidx].argc; i++) + { + if (verbose) + printf ("test %d: argv[%d] ->%s<-\n", + tidx, i, tests[tidx].argv[i]); + if (i < argc && strcmp (tests[tidx].argv[i], argv[i])) + { + if (verbose) + printf ("test %d: got[%d] ->%s<- ERROR\n", + tidx, i, argv[i]); + any = 1; + } + } + if (any) + { + fprintf (stderr, PGM": test %d: error%s\n", + tidx, verbose? "":" (use --verbose)"); + errcount++; + } + + if (itemsalloced) + { + for (i=0; i < argc; i++) + xfree (argv[i]); + } + xfree (argv); + xfree (cmdline); + } +} + + + +int +main (int argc, char **argv) +{ + int last_argc = -1; + + no_exit_on_fail = 1; + + if (argc) + { argc--; argv++; } + while (argc && last_argc != argc ) + { + last_argc = argc; + if (!strcmp (*argv, "--")) + { + argc--; argv++; + break; + } + else if (!strcmp (*argv, "--help")) + { + fputs ("usage: " PGM " [test args]\n" + "Options:\n" + " --verbose Print timings etc.\n" + " --debug Flyswatter\n" + , stdout); + exit (0); + } + else if (!strcmp (*argv, "--verbose")) + { + verbose++; + argc--; argv++; + } + else if (!strcmp (*argv, "--debug")) + { + verbose += 2; + debug++; + argc--; argv++; + } + else if (!strncmp (*argv, "--", 2)) + { + fprintf (stderr, PGM ": unknown option '%s'\n", *argv); + exit (1); + } + } + + if (argc) + { +#ifdef HAVE_W32_SYSTEM + const wchar_t *wcmdline; + char *cmdline; + int i, myargc; + char **myargv; + + wcmdline = GetCommandLineW (); + if (!wcmdline) + { + fprintf (stderr, PGM ": GetCommandLine failed\n"); + exit (1); + } + + cmdline = wchar_to_utf8 (wcmdline); + if (!cmdline) + { + fprintf (stderr, PGM ": wchar_to_utf8 failed\n"); + exit (1); + } + + printf ("cmdline ->%s<\n", cmdline); + myargv = w32_parse_commandline (cmdline, 1, &myargc, NULL); + if (!myargv) + { + fprintf (stderr, PGM ": w32_parse_commandline failed\n"); + exit (1); + } + + for (i=0; i < myargc; i++) + printf ("argv[%d] ->%s<-\n", i, myargv[i]); + fflush (stdout); + + xfree (myargv); + xfree (cmdline); +#else + fprintf (stderr, PGM ": manual test mode not available on Unix\n"); + errcount++; +#endif + } + else + test_all (); + + return !!errcount; +} diff --git a/common/t-w32-reg.c b/common/t-w32-reg.c new file mode 100644 index 0000000..1a44e38 --- /dev/null +++ b/common/t-w32-reg.c @@ -0,0 +1,87 @@ +/* t-w32-reg.c - Regression tests for W32 registry functions + * Copyright (C) 2010 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute and/or modify this + * part of GnuPG under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * GnuPG is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copies of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <time.h> + +#include "mischelp.h" + +#include "t-support.h" +#include "w32help.h" + + +static void +test_read_registry (void) +{ + char *string1, *string2; + + string1 = read_w32_registry_string + ("HKEY_CURRENT_USER", + "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", + "User Agent"); + if (!string1) + fail (0); + fprintf (stderr, "User agent: %s\n", string1); + + string2 = read_w32_reg_string + ("HKCU\\Software\\Microsoft\\Windows\\CurrentVersion" + "\\Internet Settings:User Agent", NULL); + if (!string2) + fail (1); + fprintf (stderr, "User agent: %s\n", string2); + if (strcmp (string1, string2)) + fail (2); + + + xfree (string1); + xfree (string2); +} + + + + +int +main (int argc, char **argv) +{ + if (argc > 1) + { + char *string = read_w32_reg_string (argv[1], NULL); + printf ("%s -> %s\n", argv[1], string? string : "(null)"); + xfree (string); + } + else + test_read_registry (); + + return 0; +} diff --git a/common/t-zb32.c b/common/t-zb32.c new file mode 100644 index 0000000..956c2f5 --- /dev/null +++ b/common/t-zb32.c @@ -0,0 +1,305 @@ +/* t-zb32.c - Module tests for zb32.c + * Copyright (C) 2014 Werner Koch + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <unistd.h> +#include <errno.h> +#ifdef HAVE_DOSISH_SYSTEM +# include <fcntl.h> +#endif + +#include "zb32.h" +#include "t-support.h" + +#define PGM "t-zb32" + +static int verbose; +static int debug; +static int errcount; + + +static void +test_zb32enc (void) +{ + static struct { + size_t datalen; + char *data; + const char *expected; + } tests[] = { + /* From the DESIGN document. */ + { 1, "\x00", "y" }, + { 1, "\x80", "o" }, + { 2, "\x40", "e" }, + { 2, "\xc0", "a" }, + { 10, "\x00\x00", "yy" }, + { 10, "\x80\x80", "on" }, + { 20, "\x8b\x88\x80", "tqre" }, + { 24, "\xf0\xbf\xc7", "6n9hq" }, + { 24, "\xd4\x7a\x04", "4t7ye" }, + /* The next vector is strange: The DESIGN document from 2007 gives + "8ik66o" as result, the revision from 2009 gives "6im5sd". I + look at it for quite some time and came to the conclusion that + "6im54d" is the right encoding. */ + { 30, "\xf5\x57\xbd\x0c", "6im54d" }, + /* From ccrtp's Java code. */ + { 40, "\x01\x01\x01\x01\x01", "yryonyeb" }, + { 15, "\x01\x01", "yry" }, + { 80, "\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01", "yryonyebyryonyeb" }, + { 15, "\x81\x81", "ogy" }, + { 16, "\x81\x81", "ogyo" }, + { 20, "\x81\x81\x81", "ogya" }, + { 64, "\x81\x81\x81\x81\x81\x81\x81\x81", "ogyadycbogyan" }, + /* More tests. */ + { 160, "\x80\x61\x58\x70\xF5\xBA\xD6\x90\x33\x36" + /* */"\x86\xD0\xF2\xAD\x85\xAC\x1E\x42\xB3\x67", + /* */"oboioh8izmmjyc3so5exfmcfioxrfc58" }, + { 0, "", "" } + }; + int tidx; + char *output; + + for (tidx = 0; tidx < DIM(tests); tidx++) + { + output = zb32_encode (tests[tidx].data, tests[tidx].datalen); + if (!output) + { + fprintf (stderr, PGM": error encoding test %d: %s\n", + tidx, strerror (errno)); + exit (1); + } + /* puts (output); */ + if (strcmp (output, tests[tidx].expected)) + fail (tidx); + xfree (output); + } +} + + +/* Read the file FNAME or stdin if FNAME is NULL and return a malloced + buffer with the content. R_LENGTH received the length of the file. + Print a diagnostic and returns NULL on error. */ +static char * +read_file (const char *fname, size_t *r_length) +{ + FILE *fp; + char *buf; + size_t buflen; + + if (!fname) + { + size_t nread, bufsize = 0; + + fp = stdin; +#ifdef HAVE_DOSISH_SYSTEM + setmode (fileno(fp) , O_BINARY ); +#endif + buf = NULL; + buflen = 0; +#define NCHUNK 8192 + do + { + bufsize += NCHUNK; + if (!buf) + buf = xmalloc (bufsize); + else + buf = xrealloc (buf, bufsize); + + nread = fread (buf+buflen, 1, NCHUNK, fp); + if (nread < NCHUNK && ferror (fp)) + { + fprintf (stderr, PGM": error reading '[stdin]': %s\n", + strerror (errno)); + xfree (buf); + return NULL; + } + buflen += nread; + } + while (nread == NCHUNK); +#undef NCHUNK + + } + else + { + struct stat st; + + fp = fopen (fname, "rb"); + if (!fp) + { + fprintf (stderr, PGM": can't open '%s': %s\n", + fname, strerror (errno)); + return NULL; + } + + if (fstat (fileno(fp), &st)) + { + fprintf (stderr, PGM": can't stat '%s': %s\n", + fname, strerror (errno)); + fclose (fp); + return NULL; + } + + buflen = st.st_size; + buf = xmalloc (buflen+1); + if (fread (buf, buflen, 1, fp) != 1) + { + fprintf (stderr, PGM": error reading '%s': %s\n", + fname, strerror (errno)); + fclose (fp); + xfree (buf); + return NULL; + } + fclose (fp); + } + + *r_length = buflen; + return buf; +} + + +/* Debug helper to encode or decode to/from zb32. */ +static void +endecode_file (const char *fname, int decode) +{ + char *buffer; + size_t buflen; + char *result; + + if (decode) + { + fprintf (stderr, PGM": decode mode has not yet been implemented\n"); + errcount++; + return; + } + +#ifdef HAVE_DOSISH_SYSTEM + if (decode) + setmode (fileno (stdout), O_BINARY); +#endif + + + buffer = read_file (fname, &buflen); + if (!buffer) + { + errcount++; + return; + } + + result = zb32_encode (buffer, 8 * buflen); + if (!result) + { + fprintf (stderr, PGM": error encoding data: %s\n", strerror (errno)); + errcount++; + xfree (buffer); + return; + } + + fputs (result, stdout); + putchar ('\n'); + + xfree (result); + xfree (buffer); +} + + +int +main (int argc, char **argv) +{ + int last_argc = -1; + int opt_endecode = 0; + + no_exit_on_fail = 1; + + if (argc) + { argc--; argv++; } + while (argc && last_argc != argc ) + { + last_argc = argc; + if (!strcmp (*argv, "--")) + { + argc--; argv++; + break; + } + else if (!strcmp (*argv, "--help")) + { + fputs ("usage: " PGM " [FILE]\n" + "Options:\n" + " --verbose Print timings etc.\n" + " --debug Flyswatter\n" + " --encode Encode FILE or stdin\n" + " --decode Decode FILE or stdin\n" + , stdout); + exit (0); + } + else if (!strcmp (*argv, "--verbose")) + { + verbose++; + argc--; argv++; + } + else if (!strcmp (*argv, "--debug")) + { + verbose += 2; + debug++; + argc--; argv++; + } + else if (!strcmp (*argv, "--encode")) + { + opt_endecode = 1; + argc--; argv++; + } + else if (!strcmp (*argv, "--decode")) + { + opt_endecode = -1; + argc--; argv++; + } + else if (!strncmp (*argv, "--", 2)) + { + fprintf (stderr, PGM ": unknown option '%s'\n", *argv); + exit (1); + } + } + + if (argc > 1) + { + fprintf (stderr, PGM ": to many arguments given\n"); + exit (1); + } + + if (opt_endecode) + { + endecode_file (argc? *argv : NULL, (opt_endecode < 0)); + } + else + test_zb32enc (); + + return !!errcount; +} diff --git a/common/tlv-builder.c b/common/tlv-builder.c new file mode 100644 index 0000000..59e2691 --- /dev/null +++ b/common/tlv-builder.c @@ -0,0 +1,388 @@ +/* tlv-builder.c - Build DER encoded objects + * Copyright (C) 2020 g10 Code GmbH + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <gpg-error.h> + +#include "util.h" +#include "tlv.h" + + +struct item_s +{ + int class; + int tag; + unsigned int is_constructed:1; /* This is a constructed element. */ + unsigned int is_stop:1; /* This is a STOP item. */ + const void *value; + size_t valuelen; + char *buffer; /* Malloced space or NULL. */ +}; + + +struct tlv_builder_s +{ + gpg_error_t error; /* Last error. */ + int use_secure; /* Use secure memory for the result. */ + size_t nallocateditems; /* Number of allocated items. */ + size_t nitems; /* Number of used items. */ + struct item_s *items; /* Array of items. */ + int laststop; /* Used as return value of compute_length. */ +}; + + +/* Allocate a new TLV Builder instance. Returns NULL on error. If + * SECURE is set the final object is stored in secure memory. */ +tlv_builder_t +tlv_builder_new (int secure) +{ + tlv_builder_t tb; + + tb = xtrycalloc (1, sizeof *tb); + if (tb && secure) + tb->use_secure = 1; + return tb; +} + + +/* Make sure the array of items is large enough for one new item. + * Records any error in TB and returns true in that case. */ +static int +ensure_space (tlv_builder_t tb) +{ + struct item_s *newitems; + + if (!tb || tb->error) + return 1; + + if (tb->nitems == tb->nallocateditems) + { + tb->nallocateditems += 32; + newitems = gpgrt_reallocarray (tb->items, tb->nitems, + tb->nallocateditems, sizeof *newitems); + if (!newitems) + tb->error = gpg_error_from_syserror (); + else + tb->items = newitems; + } + return !!tb->error; +} + + + +/* Add a new primitive element to the builder instance TB. The + * element is described by CLASS, TAG, VALUE, and VALUEEN. CLASS and + * TAG must describe a primitive element and (VALUE,VALUELEN) specify + * its value. The value is a pointer and its object must not be + * changed as long as the instance TB exists. For a TAG_NULL no vlaue + * is expected. Errors are not returned but recorded for later + * retrieval. */ +void +tlv_builder_add_ptr (tlv_builder_t tb, int class, int tag, + void *value, size_t valuelen) +{ + if (ensure_space (tb)) + return; + tb->items[tb->nitems].class = class; + tb->items[tb->nitems].tag = tag; + tb->items[tb->nitems].value = value; + tb->items[tb->nitems].valuelen = valuelen; + tb->nitems++; +} + + +/* This is the same as tlv_builder_add_ptr but it takes a copy of the + * value and thus the caller does not need to care about it. */ +void +tlv_builder_add_val (tlv_builder_t tb, int class, int tag, + const void *value, size_t valuelen) +{ + void *p; + + if (ensure_space (tb)) + return; + if (!value || !valuelen) + { + tb->error = gpg_error (GPG_ERR_INV_VALUE); + return; + } + p = tb->use_secure? xtrymalloc_secure (valuelen) : xtrymalloc (valuelen); + if (!p) + { + tb->error = gpg_error_from_syserror (); + return; + } + memcpy (p, value, valuelen); + tb->items[tb->nitems].buffer = p; + tb->items[tb->nitems].class = class; + tb->items[tb->nitems].tag = tag; + tb->items[tb->nitems].value = p; + tb->items[tb->nitems].valuelen = valuelen; + tb->nitems++; +} + + +/* Add a new constructed object to the builder instance TB. The + * object is described by CLASS and TAG which must describe a + * constructed object. The elements of the constructed objects are + * added with more call to the add functions. To close a constructed + * element a call to tlv_builer_add_end is required. Errors are not + * returned but recorded for later retrieval. */ +void +tlv_builder_add_tag (tlv_builder_t tb, int class, int tag) +{ + if (ensure_space (tb)) + return; + tb->items[tb->nitems].class = class; + tb->items[tb->nitems].tag = tag; + tb->items[tb->nitems].is_constructed = 1; + tb->nitems++; +} + + +/* A call to this function closes a constructed element. This must be + * called even for an empty constructed element. */ +void +tlv_builder_add_end (tlv_builder_t tb) +{ + if (ensure_space (tb)) + return; + tb->items[tb->nitems].is_stop = 1; + tb->nitems++; +} + + +/* Compute and set the length of all constructed elements in the item + * array of TB starting at IDX up to the corresponding stop item. On + * error tb->error is set. */ +static size_t +compute_lengths (tlv_builder_t tb, int idx) +{ + size_t total = 0; + + if (tb->error) + return 0; + + for (; idx < tb->nitems; idx++) + { + if (tb->items[idx].is_stop) + { + tb->laststop = idx; + break; + } + if (tb->items[idx].is_constructed) + { + tb->items[idx].valuelen = compute_lengths (tb, idx+1); + if (tb->error) + return 0; + /* Note: The last processed IDX is stored at tb->LASTSTOP. */ + } + total += get_tlv_length (tb->items[idx].class, tb->items[idx].tag, + tb->items[idx].is_constructed, + tb->items[idx].valuelen); + if (tb->items[idx].is_constructed) + idx = tb->laststop; + } + return total; +} + + +/* Return the constructed DER encoding and release this instance. On + * success the object is stored at R_OBJ and its length at R_OBJLEN. + * The caller needs to release that memory. On error NULL is stored + * at R_OBJ and an error code is returned. Note than an error may + * stem from any of the previous call made to this object or from + * constructing the the DER object. */ +gpg_error_t +tlv_builder_finalize (tlv_builder_t tb, void **r_obj, size_t *r_objlen) +{ + gpg_error_t err; + membuf_t mb; + int mb_initialized = 0; + int idx; + + *r_obj = NULL; + *r_objlen = 0; + + if (!tb) + return gpg_error (GPG_ERR_INTERNAL); + if (tb->error) + { + err = tb->error; + goto leave; + } + if (!tb->nitems || !tb->items[tb->nitems-1].is_stop) + { + err = gpg_error (GPG_ERR_NO_OBJ); + goto leave; + } + + compute_lengths (tb, 0); + err = tb->error; + if (err) + goto leave; + + /* for (idx=0; idx < tb->nitems; idx++) */ + /* log_debug ("TLVB[%2d]: c=%d t=%2d %s p=%p l=%zu\n", */ + /* idx, */ + /* tb->items[idx].class, */ + /* tb->items[idx].tag, */ + /* tb->items[idx].is_stop? "stop": */ + /* tb->items[idx].is_constructed? "cons":"prim", */ + /* tb->items[idx].value, */ + /* tb->items[idx].valuelen); */ + + if (tb->use_secure) + init_membuf_secure (&mb, 512); + else + init_membuf (&mb, 512); + mb_initialized = 1; + + for (idx=0; idx < tb->nitems; idx++) + { + if (tb->items[idx].is_stop) + continue; + put_tlv_to_membuf (&mb, tb->items[idx].class, tb->items[idx].tag, + tb->items[idx].is_constructed, + tb->items[idx].valuelen); + if (tb->items[idx].value) + put_membuf (&mb, tb->items[idx].value, tb->items[idx].valuelen); + } + + *r_obj = get_membuf (&mb, r_objlen); + if (!*r_obj) + err = gpg_error_from_syserror (); + mb_initialized = 0; + + leave: + if (mb_initialized) + xfree (get_membuf (&mb, NULL)); + for (idx=0; idx < tb->nitems; idx++) + xfree (tb->items[idx].buffer); + xfree (tb->items); + xfree (tb); + return err; +} + + +/* Write TAG of CLASS to MEMBUF. CONSTRUCTED is a flag telling + * whether the value is constructed. LENGTH gives the length of the + * value, if it is 0 undefinite length is assumed. LENGTH is ignored + * for the NULL tag. TAG must be less that 0x1f. */ +void +put_tlv_to_membuf (membuf_t *membuf, int class, int tag, + int constructed, size_t length) +{ + unsigned char buf[20]; + int buflen = 0; + int i; + + if (tag < 0x1f) + { + *buf = (class << 6) | tag; + if (constructed) + *buf |= 0x20; + buflen++; + } + else + BUG (); + + if (!tag && !class) + buf[buflen++] = 0; /* end tag */ + else if (tag == TAG_NULL && !class) + buf[buflen++] = 0; /* NULL tag */ + else if (!length) + buf[buflen++] = 0x80; /* indefinite length */ + else if (length < 128) + buf[buflen++] = length; + else + { + /* If we know the sizeof a size_t we could support larger + * objects - however this is pretty ridiculous */ + i = (length <= 0xff ? 1: + length <= 0xffff ? 2: + length <= 0xffffff ? 3: 4); + + buf[buflen++] = (0x80 | i); + if (i > 3) + buf[buflen++] = length >> 24; + if (i > 2) + buf[buflen++] = length >> 16; + if (i > 1) + buf[buflen++] = length >> 8; + buf[buflen++] = length; + } + + put_membuf (membuf, buf, buflen); +} + + +/* Return the length of the to be constructed TLV. CONSTRUCTED is a + * flag telling whether the value is constructed. LENGTH gives the + * length of the value, if it is 0 undefinite length is assumed. + * LENGTH is ignored for the NULL tag. TAG must be less that 0x1f. */ +size_t +get_tlv_length (int class, int tag, int constructed, size_t length) +{ + size_t buflen = 0; + int i; + + (void)constructed; /* Not used, but passed for uniformity of such calls. */ + + /* coverity[identical_branches] */ + if (tag < 0x1f) + { + buflen++; + } + else + { + buflen++; /* assume one and let the actual write function bail out */ + } + + if (!tag && !class) + buflen++; /* end tag */ + else if (tag == TAG_NULL && !class) + buflen++; /* NULL tag */ + else if (!length) + buflen++; /* indefinite length */ + else if (length < 128) + buflen++; + else + { + i = (length <= 0xff ? 1: + length <= 0xffff ? 2: + length <= 0xffffff ? 3: 4); + + buflen++; + if (i > 3) + buflen++; + if (i > 2) + buflen++; + if (i > 1) + buflen++; + buflen++; + } + + return buflen + length; +} diff --git a/common/tlv.c b/common/tlv.c new file mode 100644 index 0000000..9618d04 --- /dev/null +++ b/common/tlv.c @@ -0,0 +1,309 @@ +/* tlv.c - Tag-Length-Value Utilities + * Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <gpg-error.h> + + +#include "util.h" +#include "tlv.h" + + +static const unsigned char * +do_find_tlv (const unsigned char *buffer, size_t length, + int tag, size_t *nbytes, int nestlevel) +{ + const unsigned char *s = buffer; + size_t n = length; + size_t len; + int this_tag; + int composite; + + for (;;) + { + if (n < 2) + return NULL; /* Buffer definitely too short for tag and length. */ + if (!*s || *s == 0xff) + { /* Skip optional filler between TLV objects. */ + s++; + n--; + continue; + } + composite = !!(*s & 0x20); + if ((*s & 0x1f) == 0x1f) + { /* more tag bytes to follow */ + s++; + n--; + if (n < 2) + return NULL; /* buffer definitely too short for tag and length. */ + if ((*s & 0x1f) == 0x1f) + return NULL; /* We support only up to 2 bytes. */ + this_tag = (s[-1] << 8) | (s[0] & 0x7f); + } + else + this_tag = s[0]; + len = s[1]; + s += 2; n -= 2; + if (len < 0x80) + ; + else if (len == 0x81) + { /* One byte length follows. */ + if (!n) + return NULL; /* we expected 1 more bytes with the length. */ + len = s[0]; + s++; n--; + } + else if (len == 0x82) + { /* Two byte length follows. */ + if (n < 2) + return NULL; /* We expected 2 more bytes with the length. */ + len = ((size_t)s[0] << 8) | s[1]; + s += 2; n -= 2; + } + else + return NULL; /* APDU limit is 65535, thus it does not make + sense to assume longer length fields. */ + + if (composite && nestlevel < 100) + { /* Dive into this composite DO after checking for a too deep + nesting. */ + const unsigned char *tmp_s; + size_t tmp_len; + + tmp_s = do_find_tlv (s, len, tag, &tmp_len, nestlevel+1); + if (tmp_s) + { + *nbytes = tmp_len; + return tmp_s; + } + } + + if (this_tag == tag) + { + *nbytes = len; + return s; + } + if (len > n) + return NULL; /* Buffer too short to skip to the next tag. */ + s += len; n -= len; + } +} + + +/* Locate a TLV encoded data object in BUFFER of LENGTH and + return a pointer to value as well as its length in NBYTES. Return + NULL if it was not found or if the object does not fit into the buffer. */ +const unsigned char * +find_tlv (const unsigned char *buffer, size_t length, + int tag, size_t *nbytes) +{ + const unsigned char *p; + + p = do_find_tlv (buffer, length, tag, nbytes, 0); + if (p && *nbytes > (length - (p-buffer))) + p = NULL; /* Object longer than buffer. */ + return p; +} + + + +/* Locate a TLV encoded data object in BUFFER of LENGTH and + return a pointer to value as well as its length in NBYTES. Return + NULL if it was not found. Note, that the function does not check + whether the value fits into the provided buffer. */ +const unsigned char * +find_tlv_unchecked (const unsigned char *buffer, size_t length, + int tag, size_t *nbytes) +{ + return do_find_tlv (buffer, length, tag, nbytes, 0); +} + + +/* ASN.1 BER parser: Parse BUFFER of length SIZE and return the tag + and the length part from the TLV triplet. Update BUFFER and SIZE + on success. */ +gpg_error_t +parse_ber_header (unsigned char const **buffer, size_t *size, + int *r_class, int *r_tag, + int *r_constructed, int *r_ndef, + size_t *r_length, size_t *r_nhdr) +{ + int c; + unsigned long tag; + const unsigned char *buf = *buffer; + size_t length = *size; + + *r_ndef = 0; + *r_length = 0; + *r_nhdr = 0; + + /* Get the tag. */ + if (!length) + return gpg_err_make (default_errsource, GPG_ERR_EOF); + c = *buf++; length--; ++*r_nhdr; + + *r_class = (c & 0xc0) >> 6; + *r_constructed = !!(c & 0x20); + tag = c & 0x1f; + + if (tag == 0x1f) + { + tag = 0; + do + { + tag <<= 7; + if (!length) + return gpg_err_make (default_errsource, GPG_ERR_EOF); + c = *buf++; length--; ++*r_nhdr; + tag |= c & 0x7f; + + } + while (c & 0x80); + } + *r_tag = tag; + + /* Get the length. */ + if (!length) + return gpg_err_make (default_errsource, GPG_ERR_EOF); + c = *buf++; length--; ++*r_nhdr; + + if ( !(c & 0x80) ) + *r_length = c; + else if (c == 0x80) + *r_ndef = 1; + else if (c == 0xff) + return gpg_err_make (default_errsource, GPG_ERR_BAD_BER); + else + { + unsigned long len = 0; + int count = (c & 0x7f); + + if (count > (sizeof(len)<sizeof(size_t)?sizeof(len):sizeof(size_t))) + return gpg_err_make (default_errsource, GPG_ERR_BAD_BER); + + for (; count; count--) + { + len <<= 8; + if (!length) + return gpg_err_make (default_errsource, GPG_ERR_EOF); + c = *buf++; length--; ++*r_nhdr; + len |= c & 0xff; + } + *r_length = len; + } + + if (*r_length > *r_nhdr && (*r_nhdr + *r_length) < *r_length) + { + return gpg_err_make (default_errsource, GPG_ERR_EOVERFLOW); + } + + /* Without this kludge some example certs can't be parsed. */ + if (*r_class == CLASS_UNIVERSAL && !*r_tag) + *r_length = 0; + + *buffer = buf; + *size = length; + return 0; +} + + +/* FIXME: The following function should not go into this file but for + now it is easier to keep it here. */ + +/* Return the next token of an canonical encoded S-expression. BUF + is the pointer to the S-expression and BUFLEN is a pointer to the + length of this S-expression (used to validate the syntax). Both + are updated to reflect the new position. The token itself is + returned as a pointer into the original buffer at TOK and TOKLEN. + If a parentheses is the next token, TOK will be set to NULL. + TOKLEN is checked to be within the bounds. On error an error code + is returned and pointers are not guaranteed to point to + meaningful values. DEPTH should be initialized to 0 and will + reflect on return the actual depth of the tree. To detect the end + of the S-expression it is advisable to check DEPTH after a + successful return. + + depth = 0; + while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)) + && depth) + process_token (tok, toklen); + if (err) + handle_error (); + */ +gpg_error_t +parse_sexp (unsigned char const **buf, size_t *buflen, + int *depth, unsigned char const **tok, size_t *toklen) +{ + const unsigned char *s; + size_t n, vlen; + + s = *buf; + n = *buflen; + *tok = NULL; + *toklen = 0; + if (!n) + return *depth ? gpg_err_make (default_errsource, GPG_ERR_INV_SEXP) : 0; + if (*s == '(') + { + s++; n--; + (*depth)++; + *buf = s; + *buflen = n; + return 0; + } + if (*s == ')') + { + if (!*depth) + return gpg_err_make (default_errsource, GPG_ERR_INV_SEXP); + *toklen = 1; + s++; n--; + (*depth)--; + *buf = s; + *buflen = n; + return 0; + } + for (vlen=0; n && *s && *s != ':' && (*s >= '0' && *s <= '9'); s++, n--) + vlen = vlen*10 + (*s - '0'); + if (!n || *s != ':') + return gpg_err_make (default_errsource, GPG_ERR_INV_SEXP); + s++; n--; + if (vlen > n) + return gpg_err_make (default_errsource, GPG_ERR_INV_SEXP); + *tok = s; + *toklen = vlen; + s += vlen; + n -= vlen; + *buf = s; + *buflen = n; + return 0; +} diff --git a/common/tlv.h b/common/tlv.h new file mode 100644 index 0000000..51a0ef4 --- /dev/null +++ b/common/tlv.h @@ -0,0 +1,143 @@ +/* tlv.h - Tag-Length-Value Utilities + * Copyright (C) 2004 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef SCD_TLV_H +#define SCD_TLV_H 1 + +#include "membuf.h" + + +enum tlv_tag_class { + CLASS_UNIVERSAL = 0, + CLASS_APPLICATION = 1, + CLASS_CONTEXT = 2, + CLASS_PRIVATE =3 +}; + +enum tlv_tag_type { + TAG_NONE = 0, + TAG_BOOLEAN = 1, + TAG_INTEGER = 2, + TAG_BIT_STRING = 3, + TAG_OCTET_STRING = 4, + TAG_NULL = 5, + TAG_OBJECT_ID = 6, + TAG_OBJECT_DESCRIPTOR = 7, + TAG_EXTERNAL = 8, + TAG_REAL = 9, + TAG_ENUMERATED = 10, + TAG_EMBEDDED_PDV = 11, + TAG_UTF8_STRING = 12, + TAG_REALTIVE_OID = 13, + TAG_SEQUENCE = 16, + TAG_SET = 17, + TAG_NUMERIC_STRING = 18, + TAG_PRINTABLE_STRING = 19, + TAG_TELETEX_STRING = 20, + TAG_VIDEOTEX_STRING = 21, + TAG_IA5_STRING = 22, + TAG_UTC_TIME = 23, + TAG_GENERALIZED_TIME = 24, + TAG_GRAPHIC_STRING = 25, + TAG_VISIBLE_STRING = 26, + TAG_GENERAL_STRING = 27, + TAG_UNIVERSAL_STRING = 28, + TAG_CHARACTER_STRING = 29, + TAG_BMP_STRING = 30 +}; + + +struct tlv_builder_s; +typedef struct tlv_builder_s *tlv_builder_t; + +/*-- tlv.c --*/ + +/* Locate a TLV encoded data object in BUFFER of LENGTH and return a + pointer to value as well as its length in NBYTES. Return NULL if + it was not found or if the object does not fit into the buffer. */ +const unsigned char *find_tlv (const unsigned char *buffer, size_t length, + int tag, size_t *nbytes); + + +/* Locate a TLV encoded data object in BUFFER of LENGTH and return a + pointer to value as well as its length in NBYTES. Return NULL if + it was not found. Note, that the function does not check whether + the value fits into the provided buffer.*/ +const unsigned char *find_tlv_unchecked (const unsigned char *buffer, + size_t length, + int tag, size_t *nbytes); + +/* ASN.1 BER parser: Parse BUFFER of length SIZE and return the tag + and the length part from the TLV triplet. Update BUFFER and SIZE + on success. */ +gpg_error_t parse_ber_header (unsigned char const **buffer, size_t *size, + int *r_class, int *r_tag, + int *r_constructed, + int *r_ndef, size_t *r_length, size_t *r_nhdr); + + +/* Return the next token of an canonical encoded S-expression. BUF + is the pointer to the S-expression and BUFLEN is a pointer to the + length of this S-expression (used to validate the syntax). Both + are updated to reflect the new position. The token itself is + returned as a pointer into the original buffer at TOK and TOKLEN. + If a parentheses is the next token, TOK will be set to NULL. + TOKLEN is checked to be within the bounds. On error an error code + is returned and no pointer is not guaranteed to point to + a meaningful value. DEPTH should be initialized to 0 and will + reflect on return the actual depth of the tree. To detect the end + of the S-expression it is advisable to check DEPTH after a + successful return. */ +gpg_error_t parse_sexp (unsigned char const **buf, size_t *buflen, + int *depth, unsigned char const **tok, size_t *toklen); + + +/*-- tlv-builder.c --*/ + +tlv_builder_t tlv_builder_new (int use_secure); +void tlv_builder_add_ptr (tlv_builder_t tb, int class, int tag, + void *value, size_t valuelen); +void tlv_builder_add_val (tlv_builder_t tb, int class, int tag, + const void *value, size_t valuelen); +void tlv_builder_add_tag (tlv_builder_t tb, int class, int tag); +void tlv_builder_add_end (tlv_builder_t tb); +gpg_error_t tlv_builder_finalize (tlv_builder_t tb, + void **r_obj, size_t *r_objlen); + +/* Wite a TLV header to MEMBUF. */ +void put_tlv_to_membuf (membuf_t *membuf, int class, int tag, + int constructed, size_t length); + +/* Count the length of a to be constructed TLV. */ +size_t get_tlv_length (int class, int tag, int constructed, size_t length); + + + + +#endif /* SCD_TLV_H */ diff --git a/common/ttyio.c b/common/ttyio.c new file mode 100644 index 0000000..c310817 --- /dev/null +++ b/common/ttyio.c @@ -0,0 +1,751 @@ +/* ttyio.c - tty i/O functions + * Copyright (C) 1997-2019 Werner Koch + * Copyright (C) 1998-2020 Free Software Foundation, Inc. + * Copyright (C) 2015-2020 g10 Code GmbH + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + * SPDX-License-Identifier: (LGPL-3.0-or-later OR GPL-2.0-or-later) + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include <unistd.h> + +#ifdef HAVE_TCGETATTR +# include <termios.h> +#else +# ifdef HAVE_TERMIO_H +/* simulate termios with termio */ +# include <termio.h> +# define termios termio +# define tcsetattr ioctl +# define TCSAFLUSH TCSETAF +# define tcgetattr(A,B) ioctl(A,TCGETA,B) +# define HAVE_TCGETATTR +# endif +#endif +#ifdef HAVE_W32_SYSTEM +# ifdef HAVE_WINSOCK2_H +# include <winsock2.h> +# endif +# include <windows.h> +# ifdef HAVE_TCGETATTR +# error mingw32 and termios +# endif +#endif +#include <errno.h> +#include <ctype.h> + +#include "util.h" +#include "ttyio.h" +#include "i18n.h" +#include "common-defs.h" + +#define CONTROL_D ('D' - 'A' + 1) + + +#ifdef HAVE_W32_SYSTEM +static struct { + HANDLE in, out; +} con; +#define DEF_INPMODE (ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT \ + |ENABLE_PROCESSED_INPUT ) +#define HID_INPMODE (ENABLE_LINE_INPUT|ENABLE_PROCESSED_INPUT ) +#define DEF_OUTMODE (ENABLE_WRAP_AT_EOL_OUTPUT|ENABLE_PROCESSED_OUTPUT) + +#else /* Unix */ +static FILE *ttyfp = NULL; +#endif /* Unix */ + +static int initialized; +static int last_prompt_len; +static int batchmode; +static int no_terminal; + +#ifdef HAVE_TCGETATTR + static struct termios termsave; + static int restore_termios; +#endif + +/* Hooks set by gpgrlhelp.c if required. */ +static void (*my_rl_set_completer) (rl_completion_func_t *); +static void (*my_rl_inhibit_completion) (int); +static void (*my_rl_cleanup_after_signal) (void); +static void (*my_rl_init_stream) (FILE *); +static char *(*my_rl_readline) (const char*); +static void (*my_rl_add_history) (const char*); + + +/* This is a wrapper around ttyname so that we can use it even when + the standard streams are redirected. It figures the name out the + first time and returns it in a statically allocated buffer. */ +const char * +tty_get_ttyname (void) +{ + static char *name; + + /* On a GNU system ctermid() always return /dev/tty, so this does + not make much sense - however if it is ever changed we do the + Right Thing now. */ +#ifdef HAVE_CTERMID + static int got_name; + + if (!got_name) + { + const char *s; + /* Note that despite our checks for these macros the function is + not necessarily thread save. We mainly do this for + portability reasons, in case L_ctermid is not defined. */ +# if defined(_POSIX_THREAD_SAFE_FUNCTIONS) || defined(_POSIX_TRHEADS) + char buffer[L_ctermid]; + s = ctermid (buffer); +# else + s = ctermid (NULL); +# endif + if (s) + name = strdup (s); + got_name = 1; + } +#endif /*HAVE_CTERMID*/ + /* Assume the standard tty on memory error or when there is no + ctermid. */ + return name? name : "/dev/tty"; +} + + + +#ifdef HAVE_TCGETATTR +static void +cleanup(void) +{ + if (restore_termios) + { + restore_termios = 0; /* do it prior in case it is interrupted again */ + if (tcsetattr(fileno(ttyfp), TCSAFLUSH, &termsave)) + log_error ("tcsetattr() failed: %s\n", strerror (errno)); + } +} +#endif /*HAVE_TCGETATTR*/ + + +static void +init_ttyfp(void) +{ + if (initialized) + return; + +#ifdef HAVE_W32_SYSTEM + { + SECURITY_ATTRIBUTES sa; + + memset (&sa, 0, sizeof(sa)); + sa.nLength = sizeof(sa); + sa.bInheritHandle = TRUE; + con.out = CreateFileA ("CONOUT$", GENERIC_READ|GENERIC_WRITE, + FILE_SHARE_READ|FILE_SHARE_WRITE, + &sa, OPEN_EXISTING, 0, 0 ); + if (con.out == INVALID_HANDLE_VALUE) + log_fatal ("open(CONOUT$) failed: %s\n", w32_strerror (-1)); + + memset (&sa, 0, sizeof(sa)); + sa.nLength = sizeof(sa); + sa.bInheritHandle = TRUE; + con.in = CreateFileA ("CONIN$", GENERIC_READ|GENERIC_WRITE, + FILE_SHARE_READ|FILE_SHARE_WRITE, + &sa, OPEN_EXISTING, 0, 0 ); + if (con.in == INVALID_HANDLE_VALUE) + log_fatal ("open(CONIN$) failed: %s\n", w32_strerror (-1)); + } + SetConsoleMode (con.in, DEF_INPMODE); + SetConsoleMode (con.out, DEF_OUTMODE); + +#else /* Unix */ + ttyfp = batchmode? stderr : fopen (tty_get_ttyname (), "r+"); + if (!ttyfp) + { + log_error ("cannot open '%s': %s\n", tty_get_ttyname (), strerror(errno)); + exit (2); + } + if (my_rl_init_stream) + my_rl_init_stream (ttyfp); +#endif /* Unix */ + +#ifdef HAVE_TCGETATTR + atexit (cleanup); +#endif + + initialized = 1; +} + + +int +tty_batchmode( int onoff ) +{ + int old = batchmode; + if (onoff != -1) + batchmode = onoff; + return old; +} + +int +tty_no_terminal(int onoff) +{ + int old = no_terminal; + no_terminal = onoff ? 1 : 0; + return old; +} + + +#ifdef HAVE_W32_SYSTEM +/* Write the UTF-8 encoded STRING to the console. */ +static void +w32_write_console (const char *string) +{ + wchar_t *wstring; + DWORD n, nwritten; + + wstring = utf8_to_wchar (string); + if (!wstring) + log_fatal ("w32_write_console failed: %s", strerror (errno)); + n = wcslen (wstring); + + if (!WriteConsoleW (con.out, wstring, n, &nwritten, NULL)) + { + static int shown; + if (!shown) + { + shown = 1; + log_info ("WriteConsole failed: %s", w32_strerror (-1)); + log_info ("Please configure a suitable font for the console\n"); + } + n = strlen (string); + if (!WriteConsoleA (con.out, string, n , &nwritten, NULL)) + log_fatal ("WriteConsole fallback failed: %s", w32_strerror (-1)); + } + else + { + if (n != nwritten) + log_fatal ("WriteConsole failed: %lu != %lu\n", + (unsigned long)n, (unsigned long)nwritten); + } + last_prompt_len += n; + xfree (wstring); +} +#endif /*HAVE_W32_SYSTEM*/ + + +void +tty_printf (const char *fmt, ... ) +{ + va_list arg_ptr; + + if (no_terminal) + return; + + if (!initialized) + init_ttyfp (); + + va_start (arg_ptr, fmt); + +#ifdef HAVE_W32_SYSTEM + { + char *buf = NULL; + + vasprintf(&buf, fmt, arg_ptr); + if (!buf) + log_bug ("vasprintf() failed\n"); + w32_write_console (buf); + xfree (buf); + } +#else /* Unix */ + last_prompt_len += vfprintf (ttyfp, fmt, arg_ptr) ; + fflush (ttyfp); +#endif /* Unix */ + va_end(arg_ptr); +} + + +/* Same as tty_printf but if FP is not NULL, behave like a regular + fprintf. */ +void +tty_fprintf (estream_t fp, const char *fmt, ... ) +{ + va_list arg_ptr; + + if (fp) + { + va_start (arg_ptr, fmt) ; + es_vfprintf (fp, fmt, arg_ptr ); + va_end (arg_ptr); + return; + } + + if (no_terminal) + return; + + if (!initialized) + init_ttyfp (); + + va_start (arg_ptr, fmt); + +#ifdef HAVE_W32_SYSTEM + { + char *buf = NULL; + + vasprintf (&buf, fmt, arg_ptr); + if (!buf) + log_bug ("vasprintf() failed\n"); + w32_write_console (buf); + xfree (buf); + } +#else /* Unix */ + last_prompt_len += vfprintf(ttyfp,fmt,arg_ptr) ; + fflush(ttyfp); +#endif /* Unix */ + + va_end(arg_ptr); +} + + +/* Print a string, but filter all control characters out. If FP is + * not NULL print to that stream instead to the tty. */ +static void +do_print_string (estream_t fp, const byte *p, size_t n ) +{ + if (no_terminal && !fp) + return; + + if (!initialized && !fp) + init_ttyfp(); + + if (fp) + { + print_utf8_buffer (fp, p, n); + return; + } + +#ifdef HAVE_W32_SYSTEM + /* Not so effective, change it if you want */ + for (; n; n--, p++) + { + if (iscntrl (*p)) + { + if( *p == '\n' ) + tty_printf ("\\n"); + else if( !*p ) + tty_printf ("\\0"); + else + tty_printf ("\\x%02x", *p); + } + else + tty_printf ("%c", *p); + } +#else /* Unix */ + for (; n; n--, p++) + { + if (iscntrl (*p)) + { + putc ('\\', ttyfp); + if ( *p == '\n' ) + putc ('n', ttyfp); + else if ( !*p ) + putc ('0', ttyfp); + else + fprintf (ttyfp, "x%02x", *p ); + } + else + putc (*p, ttyfp); + } +#endif /* Unix */ +} + + +void +tty_print_utf8_string2 (estream_t fp, const byte *p, size_t n, size_t max_n) +{ + size_t i; + char *buf; + + if (no_terminal && !fp) + return; + + /* We can handle plain ascii simpler, so check for it first. */ + for(i=0; i < n; i++ ) + { + if (p[i] & 0x80) + break; + } + if (i < n) + { + buf = utf8_to_native ((const char *)p, n, 0); + if (max_n && (strlen (buf) > max_n)) + buf[max_n] = 0; + /* (utf8_to_native already did the control character quoting) */ + tty_fprintf (fp, "%s", buf); + xfree (buf); + } + else + { + if (max_n && (n > max_n)) + n = max_n; + do_print_string (fp, p, n ); + } +} + + +void +tty_print_utf8_string (const byte *p, size_t n) +{ + tty_print_utf8_string2 (NULL, p, n, 0); +} + + +/* Read a string from the tty using PROMPT. If HIDDEN is set the + * input is not echoed. */ +static char * +do_get (const char *prompt, int hidden) +{ + char *buf; + int n; /* Allocated size of BUF. */ + int i; /* Number of bytes in BUF. */ + int c; +#ifdef HAVE_W32_SYSTEM + char *utf8buf; + int errcount = 0; +#else + byte cbuf[1]; +#endif + + if (batchmode) + { + log_error (_("Sorry, we are in batchmode - can't get input\n")); + exit (2); + } + + if (no_terminal) + { + log_error (_("Sorry, no terminal at all requested - can't get input\n")); + exit (2); + } + + if( !initialized ) + init_ttyfp(); + + last_prompt_len = 0; + tty_printf( "%s", prompt ); + buf = xmalloc((n=50)); + i = 0; + +#ifdef HAVE_W32_SYSTEM + if (hidden) + SetConsoleMode(con.in, HID_INPMODE ); + + utf8buf = NULL; + for (;;) + { + DWORD nread; + wchar_t wbuf[2]; + const unsigned char *s; + + if (!ReadConsoleW (con.in, wbuf, 1, &nread, NULL)) + log_fatal ("ReadConsole failed: %s", w32_strerror (-1)); + if (!nread) + continue; + + wbuf[1] = 0; + xfree (utf8buf); + utf8buf = wchar_to_utf8 (wbuf); + if (!utf8buf) + { + log_info ("wchar_to_utf8 failed: %s\n", strerror (errno)); + if (++errcount > 10) + log_fatal (_("too many errors; giving up\n")); + continue; + } + if (*utf8buf == '\n') + { + if (utf8buf[1]) + { + log_info ("ReadConsole returned more than requested" + " (0x0a,0x%02x)\n", utf8buf[1]); + if (++errcount > 10) + log_fatal (_("too many errors; giving up\n")); + } + break; + } + if (!hidden) + last_prompt_len++; + + for (s=utf8buf; *s; s++) + { + c = *s; + if (c == '\t') + c = ' '; /* Map tab to a space. */ + else if ((c >= 0 && c <= 0x1f) || c == 0x7f) + continue; /* Remove control characters. */ + if (!(i < n-1)) + { + n += 50; + buf = xrealloc (buf, n); + } + buf[i++] = c; + } + } + xfree (utf8buf); + + if (hidden) + SetConsoleMode(con.in, DEF_INPMODE ); + +#else /* Unix */ + + if (hidden) + { +#ifdef HAVE_TCGETATTR + struct termios term; + + if (tcgetattr(fileno(ttyfp), &termsave)) + log_fatal ("tcgetattr() failed: %s\n", strerror(errno)); + restore_termios = 1; + term = termsave; + term.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL); + if (tcsetattr( fileno(ttyfp), TCSAFLUSH, &term ) ) + log_fatal("tcsetattr() failed: %s\n", strerror(errno)); +#endif /*HAVE_TCGETATTR*/ + } + + /* fixme: How can we avoid that the \n is echoed w/o disabling + * canonical mode - w/o this kill_prompt can't work */ + while (read(fileno(ttyfp), cbuf, 1) == 1 && *cbuf != '\n') + { + if (!hidden) + last_prompt_len++; + c = *cbuf; + if (c == CONTROL_D) + log_info (_("Control-D detected\n")); + + if (c == '\t') /* Map tab to a space. */ + c = ' '; + else if ( (c >= 0 && c <= 0x1f) || c == 0x7f) + continue; /* Skip all other ASCII control characters. */ + if (!(i < n-1)) + { + n += 50; + buf = xrealloc (buf, n); + } + buf[i++] = c; + } + + if (*cbuf != '\n') + { + buf[0] = CONTROL_D; + i = 1; + } + + if (hidden) + { +#ifdef HAVE_TCGETATTR + if (tcsetattr (fileno(ttyfp), TCSAFLUSH, &termsave)) + log_error ("tcsetattr() failed: %s\n", strerror(errno)); + restore_termios = 0; +#endif /*HAVE_TCGETATTR*/ + } +#endif /* Unix */ + + buf[i] = 0; + return buf; +} + + +char * +tty_get( const char *prompt ) +{ + if (!batchmode && !no_terminal && my_rl_readline && my_rl_add_history) + { + char *line; + char *buf; + + if (!initialized) + init_ttyfp(); + + last_prompt_len = 0; + + line = my_rl_readline (prompt?prompt:""); + + /* We need to copy it to memory controlled by our malloc + implementations; further we need to convert an EOF to our + convention. */ + buf = xmalloc(line? strlen(line)+1:2); + if (line) + { + strcpy (buf, line); + trim_spaces (buf); + if (strlen (buf) > 2 ) + my_rl_add_history (line); /* Note that we test BUF but add LINE. */ + free (line); + } + else + { + buf[0] = CONTROL_D; + buf[1] = 0; + } + return buf; + } + else + return do_get ( prompt, 0 ); +} + + +/* Variable argument version of tty_get. The prompt is actually a + * format string with arguments. */ +char * +tty_getf (const char *promptfmt, ... ) +{ + va_list arg_ptr; + char *prompt; + char *answer; + + va_start (arg_ptr, promptfmt); + if (gpgrt_vasprintf (&prompt, promptfmt, arg_ptr) < 0) + log_fatal ("estream_vasprintf failed: %s\n", strerror (errno)); + va_end (arg_ptr); + answer = tty_get (prompt); + xfree (prompt); + return answer; +} + + +char * +tty_get_hidden( const char *prompt ) +{ + return do_get (prompt, 1); +} + + +void +tty_kill_prompt (void) +{ + if (no_terminal) + return; + + if (!initialized) + init_ttyfp (); + + if (batchmode) + last_prompt_len = 0; + if (!last_prompt_len) + return; +#ifdef HAVE_W32_SYSTEM + tty_printf ("\r%*s\r", last_prompt_len, ""); +#else /* Unix */ + { + int i; + putc ('\r', ttyfp); + for (i=0; i < last_prompt_len; i ++ ) + putc (' ', ttyfp); + putc ('\r', ttyfp); + fflush (ttyfp); + } +#endif /* Unix */ + last_prompt_len = 0; +} + + +int +tty_get_answer_is_yes( const char *prompt ) +{ + int yes; + char *p; + + p = tty_get (prompt); + tty_kill_prompt (); + yes = answer_is_yes (p); + xfree (p); + + return yes; +} + + +/* Called by gnupg_rl_initialize to setup the readline support. */ +void +tty_private_set_rl_hooks (void (*init_stream) (FILE *), + void (*set_completer) (rl_completion_func_t*), + void (*inhibit_completion) (int), + void (*cleanup_after_signal) (void), + char *(*readline_fun) (const char*), + void (*add_history_fun) (const char*)) +{ + my_rl_init_stream = init_stream; + my_rl_set_completer = set_completer; + my_rl_inhibit_completion = inhibit_completion; + my_rl_cleanup_after_signal = cleanup_after_signal; + my_rl_readline = readline_fun; + my_rl_add_history = add_history_fun; +} + + +#ifdef HAVE_LIBREADLINE +void +tty_enable_completion (rl_completion_func_t *completer) +{ + if (no_terminal || !my_rl_set_completer ) + return; + + if (!initialized) + init_ttyfp(); + + my_rl_set_completer (completer); +} + +void +tty_disable_completion (void) +{ + if (no_terminal || !my_rl_inhibit_completion) + return; + + if (!initialized) + init_ttyfp(); + + my_rl_inhibit_completion (1); +} +#endif /* HAVE_LIBREADLINE */ + +void +tty_cleanup_after_signal (void) +{ +#ifdef HAVE_TCGETATTR + cleanup (); +#endif +} + +void +tty_cleanup_rl_after_signal (void) +{ + if (my_rl_cleanup_after_signal) + my_rl_cleanup_after_signal (); +} diff --git a/common/ttyio.h b/common/ttyio.h new file mode 100644 index 0000000..5bff82f --- /dev/null +++ b/common/ttyio.h @@ -0,0 +1,73 @@ +/* ttyio.h + * Copyright (C) 1998, 1999, 2000, 2001, 2003, 2006, + * 2009 Free Software Foundation, Inc. + * + * This file is part of GNUPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ +#ifndef GNUPG_COMMON_TTYIO_H +#define GNUPG_COMMON_TTYIO_H + +#include "util.h" /* Make sure our readline typedef is available. */ + + +const char *tty_get_ttyname (void); +int tty_batchmode (int onoff); +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 5 ) +void tty_printf (const char *fmt, ... ) + __attribute__ ((format (printf,1,2))); +void tty_fprintf (estream_t fp, const char *fmt, ... ) + __attribute__ ((format (printf,2,3))); +char *tty_getf (const char *promptfmt, ... ) + __attribute__ ((format (printf,1,2))); +#else +void tty_printf (const char *fmt, ... ); +void tty_fprintf (estream_t fp, const char *fmt, ... ); +char *tty_getf (const char *promptfmt, ... ); +#endif +void tty_print_utf8_string (const unsigned char *p, size_t n); +void tty_print_utf8_string2 (estream_t fp, + const unsigned char *p, size_t n, size_t max_n); +char *tty_get (const char *prompt); +char *tty_get_hidden (const char *prompt); +void tty_kill_prompt (void); +int tty_get_answer_is_yes (const char *prompt); +int tty_no_terminal (int onoff); + +#ifdef HAVE_LIBREADLINE +void tty_enable_completion (rl_completion_func_t *completer); +void tty_disable_completion (void); +#else +/* Use a macro to stub out these functions since a macro has no need + to typedef a "rl_completion_func_t" which would be undefined + without readline. */ +#define tty_enable_completion(x) +#define tty_disable_completion() +#endif +void tty_cleanup_after_signal (void); +void tty_cleanup_rl_after_signal (void); + + +#endif /*GNUPG_COMMON_TTYIO_H*/ diff --git a/common/types.h b/common/types.h new file mode 100644 index 0000000..8e551df --- /dev/null +++ b/common/types.h @@ -0,0 +1,116 @@ +/* types.h - define some extra types + * Copyright (C) 1999, 2000, 2001, 2006 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute and/or modify this + * part of GnuPG under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * GnuPG is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copies of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef GNUPG_COMMON_TYPES_H +#define GNUPG_COMMON_TYPES_H + +#ifdef HAVE_INTTYPES_H +# include <inttypes.h> +#endif + +/* The AC_CHECK_SIZEOF() in configure fails for some machines. + * we provide some fallback values here */ +#if !SIZEOF_UNSIGNED_SHORT +# undef SIZEOF_UNSIGNED_SHORT +# define SIZEOF_UNSIGNED_SHORT 2 +#endif +#if !SIZEOF_UNSIGNED_INT +# undef SIZEOF_UNSIGNED_INT +# define SIZEOF_UNSIGNED_INT 4 +#endif +#if !SIZEOF_UNSIGNED_LONG +# undef SIZEOF_UNSIGNED_LONG +# define SIZEOF_UNSIGNED_LONG 4 +#endif + + +#include <sys/types.h> + + +/* We use byte as an abbreviation for unsigned char. On some + platforms this needs special treatment: + + - RISC OS: + Norcroft C treats char = unsigned char as legal assignment + but char* = unsigned char* as illegal assignment + and the same applies to the signed variants as well. Thus we use + char which is anyway unsigned. + + - Windows: + Windows typedefs byte in the RPC headers but we need to avoid a + warning about a double definition. + */ +#ifndef HAVE_BYTE_TYPEDEF +# undef byte /* There might be a macro with this name. */ +# ifdef __riscos__ + typedef char byte; +# elif !(defined(_WIN32) && defined(cbNDRContext)) + typedef unsigned char byte; +# endif +# define HAVE_BYTE_TYPEDEF +#endif /*!HAVE_BYTE_TYPEDEF*/ + +#ifndef HAVE_USHORT_TYPEDEF +# undef ushort /* There might be a macro with this name. */ + typedef unsigned short ushort; +# define HAVE_USHORT_TYPEDEF +#endif + +#ifndef HAVE_ULONG_TYPEDEF +# undef ulong /* There might be a macro with this name. */ + typedef unsigned long ulong; +# define HAVE_ULONG_TYPEDEF +#endif + +#ifndef HAVE_U16_TYPEDEF +# undef u16 /* There might be a macro with this name. */ +# if SIZEOF_UNSIGNED_INT == 2 + typedef unsigned int u16; +# elif SIZEOF_UNSIGNED_SHORT == 2 + typedef unsigned short u16; +# else +# error no typedef for u16 +# endif +# define HAVE_U16_TYPEDEF +#endif + +#ifndef HAVE_U32_TYPEDEF +# undef u32 /* There might be a macro with this name. */ +# if SIZEOF_UNSIGNED_INT == 4 + typedef unsigned int u32; +# elif SIZEOF_UNSIGNED_LONG == 4 + typedef unsigned long u32; +# else +# error no typedef for u32 +# endif +# define HAVE_U32_TYPEDEF +#endif + +#endif /*GNUPG_COMMON_TYPES_H*/ diff --git a/common/userids.c b/common/userids.c new file mode 100644 index 0000000..00f26b7 --- /dev/null +++ b/common/userids.c @@ -0,0 +1,435 @@ +/* userids.c - Utility functions for user ids. + * Copyright (C) 2001, 2003, 2004, 2006, + * 2009 Free Software Foundation, Inc. + * Copyright (C) 2015 g10 Code GmbH + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "util.h" +#include "userids.h" + + +/* Parse the user-id NAME and build a search description for it. + * Returns 0 on success or an error code. DESC may be NULL to merely + * check the validity of a user-id. + * + * Some used rules: + * - If the username starts with 8,9,16 or 17 hex-digits (the first one + * must be in the range 0..9), this is considered a keyid; depending + * on the length a short or complete one. + * - If the username starts with 32,33,40 or 41 hex-digits (the first one + * must be in the range 0..9), this is considered a fingerprint. + * - If the username starts with a left angle, we assume it is a complete + * email address and look only at this part. + * - If the username starts with a colon we assume it is a unified + * key specfification. + * - If the username starts with a '.', we assume it is the ending + * part of an email address + * - If the username starts with an '@', we assume it is a part of an + * email address + * - If the userid start with an '=' an exact compare is done. + * - If the userid starts with a '*' a case insensitive substring search is + * done (This is the default). + * - If the userid starts with a '+' we will compare individual words + * and a match requires that all the words are in the userid. + * Words are delimited by white space or "()<>[]{}.@-+_,;/&!" + * (note that you can't search for these characters). Compare + * is not case sensitive. + * - If the userid starts with a '&' a 40 hex digits keygrip is expected. + */ + +gpg_error_t +classify_user_id (const char *name, KEYDB_SEARCH_DESC *desc, int openpgp_hack) +{ + const char *s; + char *s2 = NULL; + int rc = 0; + int hexprefix = 0; + int hexlength; + int mode = 0; + KEYDB_SEARCH_DESC dummy_desc; + + if (!desc) + desc = &dummy_desc; + + /* Clear the structure so that the mode field is set to zero unless + we set it to the correct value right at the end of this + function. */ + memset (desc, 0, sizeof *desc); + + /* Skip leading and trailing spaces. */ + for(s = name; *s && spacep (s); s++ ) + ; + if (*s && spacep (s + strlen(s) - 1)) + { + s2 = xtrystrdup (s); + if (!s2) + { + rc = gpg_error_from_syserror (); + goto out; + } + trim_trailing_spaces (s2); + s = s2; + } + + switch (*s) + { + case 0: /* Empty string is an error. */ + rc = gpg_error (GPG_ERR_INV_USER_ID); + goto out; + + case '.': /* An email address, compare from end. Note that this + has not yet been implemented in the search code. */ + mode = KEYDB_SEARCH_MODE_MAILEND; + s++; + desc->u.name = s; + break; + + case '<': /* An email address. */ + mode = KEYDB_SEARCH_MODE_MAIL; + /* FIXME: The keyring code in g10 assumes that the mail name is + prefixed with an '<'. However the keybox code used for sm/ + assumes it has been removed. For now we use this simple hack + to overcome the problem. */ + if (!openpgp_hack) + s++; + desc->u.name = s; + break; + + case '@': /* Part of an email address. */ + mode = KEYDB_SEARCH_MODE_MAILSUB; + s++; + desc->u.name = s; + break; + + case '=': /* Exact compare. */ + mode = KEYDB_SEARCH_MODE_EXACT; + s++; + desc->u.name = s; + break; + + case '*': /* Case insensitive substring search. */ + mode = KEYDB_SEARCH_MODE_SUBSTR; + s++; + desc->u.name = s; + break; + + case '+': /* Compare individual words. Note that this has not + yet been implemented in the search code. */ + mode = KEYDB_SEARCH_MODE_WORDS; + s++; + desc->u.name = s; + break; + + case '/': /* Subject's DN. */ + s++; + if (!*s || spacep (s)) /* No DN or prefixed with a space. */ + { + rc = gpg_error (GPG_ERR_INV_USER_ID); + goto out; + } + desc->u.name = s; + mode = KEYDB_SEARCH_MODE_SUBJECT; + break; + + case '#': /* S/N with optional issuer id or just issuer id. */ + { + const char *si; + + s++; + if ( *s == '/') + { /* "#/" indicates an issuer's DN. */ + s++; + if (!*s || spacep (s)) /* No DN or prefixed with a space. */ + { + rc = gpg_error (GPG_ERR_INV_USER_ID); + goto out; + } + desc->u.name = s; + mode = KEYDB_SEARCH_MODE_ISSUER; + } + else + { /* Serialnumber + optional issuer ID. */ + for (si=s; *si && *si != '/'; si++) + { + /* Check for an invalid digit in the serial number. */ + if (!strchr("01234567890abcdefABCDEF", *si)) + { + rc = gpg_error (GPG_ERR_INV_USER_ID); + goto out; + } + } + desc->sn = (const unsigned char*)s; + desc->snlen = -1; + if (!*si) + mode = KEYDB_SEARCH_MODE_SN; + else + { + s = si+1; + if (!*s || spacep (s)) /* No DN or prefixed with a space. */ + { + rc = gpg_error (GPG_ERR_INV_USER_ID); + goto out; + } + desc->u.name = s; + mode = KEYDB_SEARCH_MODE_ISSUER_SN; + } + } + } + break; + + case ':': /* Unified fingerprint. */ + { + const char *se, *si; + int i; + + se = strchr (++s,':'); + if (!se) + { + rc = gpg_error (GPG_ERR_INV_USER_ID); + goto out; + } + for (i=0,si=s; si < se; si++, i++ ) + { + if (!strchr("01234567890abcdefABCDEF", *si)) + { + rc = gpg_error (GPG_ERR_INV_USER_ID); /* Invalid digit. */ + goto out; + } + } + if (i != 32 && i != 40) + { + rc = gpg_error (GPG_ERR_INV_USER_ID); /* Invalid length of fpr. */ + goto out; + } + for (i=0,si=s; si < se; i++, si +=2) + desc->u.fpr[i] = hextobyte(si); + for (; i < 20; i++) + desc->u.fpr[i]= 0; + mode = KEYDB_SEARCH_MODE_FPR; + } + break; + + case '&': /* Keygrip*/ + { + if (hex2bin (s+1, desc->u.grip, 20) < 0) + { + rc = gpg_error (GPG_ERR_INV_USER_ID); /* Invalid. */ + goto out; + } + mode = KEYDB_SEARCH_MODE_KEYGRIP; + } + break; + + default: + if (s[0] == '0' && s[1] == 'x') + { + hexprefix = 1; + s += 2; + } + + hexlength = strspn(s, "0123456789abcdefABCDEF"); + if (hexlength >= 8 && s[hexlength] =='!') + { + desc->exact = 1; + hexlength++; /* Just for the following check. */ + } + + /* Check if a hexadecimal number is terminated by EOS or blank. */ + if (hexlength && s[hexlength] && !spacep (s+hexlength)) + { + if (hexprefix) /* A "0x" prefix without a correct + termination is an error. */ + { + rc = gpg_error (GPG_ERR_INV_USER_ID); + goto out; + } + /* The first characters looked like a hex number, but the + entire string is not. */ + hexlength = 0; + } + + if (desc->exact) + hexlength--; /* Remove the bang. */ + + if ((hexlength == 8 + && (s[hexlength] == 0 + || (s[hexlength] == '!' && s[hexlength + 1] == 0))) + || (!hexprefix && hexlength == 9 && *s == '0')) + { + /* Short keyid. */ + if (hexlength == 9) + s++; + desc->u.kid[1] = strtoul( s, NULL, 16 ); + mode = KEYDB_SEARCH_MODE_SHORT_KID; + } + else if ((hexlength == 16 + && (s[hexlength] == 0 + || (s[hexlength] == '!' && s[hexlength + 1] == 0))) + || (!hexprefix && hexlength == 17 && *s == '0')) + { + /* Long keyid. */ + char buf[9]; + if (hexlength == 17) + s++; + mem2str (buf, s, 9); + desc->u.kid[0] = strtoul (buf, NULL, 16); + desc->u.kid[1] = strtoul (s+8, NULL, 16); + mode = KEYDB_SEARCH_MODE_LONG_KID; + } + else if ((hexlength == 32 + && (s[hexlength] == 0 + || (s[hexlength] == '!' && s[hexlength + 1] == 0))) + || (!hexprefix && hexlength == 33 && *s == '0')) + { + /* MD5 fingerprint. */ + int i; + if (hexlength == 33) + s++; + memset (desc->u.fpr+16, 0, 4); + for (i=0; i < 16; i++, s+=2) + { + int c = hextobyte(s); + if (c == -1) + { + rc = gpg_error (GPG_ERR_INV_USER_ID); + goto out; + } + desc->u.fpr[i] = c; + } + mode = KEYDB_SEARCH_MODE_FPR16; + } + else if ((hexlength == 40 + && (s[hexlength] == 0 + || (s[hexlength] == '!' && s[hexlength + 1] == 0))) + || (!hexprefix && hexlength == 41 && *s == '0')) + { + /* SHA1/RMD160 fingerprint. */ + int i; + if (hexlength == 41) + s++; + for (i=0; i < 20; i++, s+=2) + { + int c = hextobyte(s); + if (c == -1) + { + rc = gpg_error (GPG_ERR_INV_USER_ID); + goto out; + } + desc->u.fpr[i] = c; + } + mode = KEYDB_SEARCH_MODE_FPR20; + } + else if (!hexprefix) + { + /* The fingerprint of an X.509 listing is often delimited by + * colons, so we try to single this case out. Note that the + * OpenPGP bang suffix is not supported here. */ + desc->exact = 0; + mode = 0; + hexlength = strspn (s, ":0123456789abcdefABCDEF"); + if (hexlength == 59 && (!s[hexlength] || spacep (s+hexlength))) + { + int i; + + for (i=0; i < 20; i++, s += 3) + { + int c = hextobyte(s); + if (c == -1 || (i < 19 && s[2] != ':')) + break; + desc->u.fpr[i] = c; + } + if (i == 20) + mode = KEYDB_SEARCH_MODE_FPR20; + } + if (!mode) + { + /* Still not found. Now check for a space separated + OpenPGP v4 fingerprint like: + 8061 5870 F5BA D690 3336 86D0 F2AD 85AC 1E42 B367 + or + 8061 5870 F5BA D690 3336 86D0 F2AD 85AC 1E42 B367 + */ + hexlength = strspn (s, " 0123456789abcdefABCDEF"); + if (s[hexlength] && s[hexlength] != ' ') + hexlength = 0; /* Followed by non-space. */ + while (hexlength && s[hexlength-1] == ' ') + hexlength--; /* Trim trailing spaces. */ + if ((hexlength == 49 || hexlength == 50) + && (!s[hexlength] || s[hexlength] == ' ')) + { + int i, c; + + for (i=0; i < 20; i++) + { + if (i && !(i % 2)) + { + if (*s != ' ') + break; + s++; + /* Skip the double space in the middle but + don't require it to help copying + fingerprints from sources which fold + multiple space to one. */ + if (i == 10 && *s == ' ') + s++; + } + + c = hextobyte(s); + if (c == -1) + break; + desc->u.fpr[i] = c; + s += 2; + } + if (i == 20) + mode = KEYDB_SEARCH_MODE_FPR20; + } + } + if (!mode) /* Default to substring search. */ + { + desc->u.name = s; + mode = KEYDB_SEARCH_MODE_SUBSTR; + } + } + else + { + /* Hex number with a prefix but with a wrong length. */ + rc = gpg_error (GPG_ERR_INV_USER_ID); + goto out; + } + } + + desc->mode = mode; + out: + xfree (s2); + return rc; +} diff --git a/common/userids.h b/common/userids.h new file mode 100644 index 0000000..c60bc33 --- /dev/null +++ b/common/userids.h @@ -0,0 +1,39 @@ +/* userids.h - Utility functions for user ids. + * Copyright (C) 2009 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef GNUPG_COMMON_USERIDS_H +#define GNUPG_COMMON_USERIDS_H + +#include "../kbx/keybox-search-desc.h" + +gpg_error_t classify_user_id (const char *name, KEYDB_SEARCH_DESC *desc, + int openpgp_hack); + + +#endif /*GNUPG_COMMON_USERIDS_H*/ diff --git a/common/utf8conv.c b/common/utf8conv.c new file mode 100644 index 0000000..bdab225 --- /dev/null +++ b/common/utf8conv.c @@ -0,0 +1,857 @@ +/* utf8conf.c - UTF8 character set conversion + * Copyright (C) 1994, 1998, 1999, 2000, 2001, 2003, 2006, + * 2008, 2010 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute and/or modify this + * part of GnuPG under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * GnuPG is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copies of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include <ctype.h> +#ifdef HAVE_LANGINFO_CODESET +#include <langinfo.h> +#endif +#include <errno.h> + +#if HAVE_W32_SYSTEM +# /* Tell libgpg-error to provide the iconv macros. */ +# define GPGRT_ENABLE_W32_ICONV_MACROS 1 +#elif HAVE_ANDROID_SYSTEM +# /* No iconv support. */ +#else +# include <iconv.h> +#endif + + +#include "util.h" +#include "common-defs.h" +#include "i18n.h" +#include "stringhelp.h" +#include "utf8conv.h" + +#ifndef MB_LEN_MAX +#define MB_LEN_MAX 16 +#endif + +static const char *active_charset_name = "iso-8859-1"; +static int no_translation; /* Set to true if we let simply pass through. */ +static int use_iconv; /* iconv conversion functions required. */ + + +#ifdef HAVE_ANDROID_SYSTEM +/* Fake stuff to get things building. */ +typedef void *iconv_t; +#define ICONV_CONST + +static iconv_t +iconv_open (const char *tocode, const char *fromcode) +{ + (void)tocode; + (void)fromcode; + return (iconv_t)(-1); +} + +static size_t +iconv (iconv_t cd, char **inbuf, size_t *inbytesleft, + char **outbuf, size_t *outbytesleft) +{ + (void)cd; + (void)inbuf; + (void)inbytesleft; + (void)outbuf; + (void)outbytesleft; + return (size_t)(0); +} + +static int +iconv_close (iconv_t cd) +{ + (void)cd; + return 0; +} +#endif /*HAVE_ANDROID_SYSTEM*/ + + +/* Error handler for iconv failures. This is needed to not clutter the + output with repeated diagnostics about a missing conversion. */ +static void +handle_iconv_error (const char *to, const char *from, int use_fallback) +{ + if (errno == EINVAL) + { + static int shown1, shown2; + int x; + + if (to && !strcmp (to, "utf-8")) + { + x = shown1; + shown1 = 1; + } + else + { + x = shown2; + shown2 = 1; + } + + if (!x) + log_info (_("conversion from '%s' to '%s' not available\n"), + from, to); + } + else + { + static int shown; + + if (!shown) + log_info (_("iconv_open failed: %s\n"), strerror (errno)); + shown = 1; + } + + if (use_fallback) + { + /* To avoid further error messages we fallback to UTF-8 for the + native encoding. Nowadays this seems to be the best bet in + case of errors from iconv or nl_langinfo. */ + active_charset_name = "utf-8"; + no_translation = 1; + use_iconv = 0; + } +} + + + +int +set_native_charset (const char *newset) +{ + const char *full_newset; + + if (!newset) + { +#ifdef HAVE_ANDROID_SYSTEM + newset = "utf-8"; +#elif defined HAVE_W32_SYSTEM + static char codepage[30]; + unsigned int cpno; + const char *aliases; + + /* We are a console program thus we need to use the + GetConsoleOutputCP function and not the GetACP which + would give the codepage for a GUI program. Note this is not + a bulletproof detection because GetConsoleCP might return a + different one for console input. Not sure how to cope with + that. If the console Code page is not known we fall back to + the system code page. */ +#ifndef HAVE_W32CE_SYSTEM + cpno = GetConsoleOutputCP (); + if (!cpno) +#endif + cpno = GetACP (); + sprintf (codepage, "CP%u", cpno ); + /* Resolve alias. We use a long string string and not the usual + array to optimize if the code is taken to a DSO. Taken from + libiconv 1.9.2. */ + newset = codepage; + for (aliases = ("CP936" "\0" "GBK" "\0" + "CP1361" "\0" "JOHAB" "\0" + "CP20127" "\0" "ASCII" "\0" + "CP20866" "\0" "KOI8-R" "\0" + "CP21866" "\0" "KOI8-RU" "\0" + "CP28591" "\0" "ISO-8859-1" "\0" + "CP28592" "\0" "ISO-8859-2" "\0" + "CP28593" "\0" "ISO-8859-3" "\0" + "CP28594" "\0" "ISO-8859-4" "\0" + "CP28595" "\0" "ISO-8859-5" "\0" + "CP28596" "\0" "ISO-8859-6" "\0" + "CP28597" "\0" "ISO-8859-7" "\0" + "CP28598" "\0" "ISO-8859-8" "\0" + "CP28599" "\0" "ISO-8859-9" "\0" + "CP28605" "\0" "ISO-8859-15" "\0" + "CP65001" "\0" "UTF-8" "\0"); + *aliases; + aliases += strlen (aliases) + 1, aliases += strlen (aliases) + 1) + { + if (!strcmp (codepage, aliases) ||(*aliases == '*' && !aliases[1])) + { + newset = aliases + strlen (aliases) + 1; + break; + } + } + +#else /*!HAVE_W32_SYSTEM && !HAVE_ANDROID_SYSTEM*/ + +#ifdef HAVE_LANGINFO_CODESET + newset = nl_langinfo (CODESET); +#else /*!HAVE_LANGINFO_CODESET*/ + /* Try to get the used charset from environment variables. */ + static char codepage[30]; + const char *lc, *dot, *mod; + + strcpy (codepage, "iso-8859-1"); + lc = getenv ("LC_ALL"); + if (!lc || !*lc) + { + lc = getenv ("LC_CTYPE"); + if (!lc || !*lc) + lc = getenv ("LANG"); + } + if (lc && *lc) + { + dot = strchr (lc, '.'); + if (dot) + { + mod = strchr (++dot, '@'); + if (!mod) + mod = dot + strlen (dot); + if (mod - dot < sizeof codepage && dot != mod) + { + memcpy (codepage, dot, mod - dot); + codepage [mod - dot] = 0; + } + } + } + newset = codepage; +#endif /*!HAVE_LANGINFO_CODESET*/ +#endif /*!HAVE_W32_SYSTEM && !HAVE_ANDROID_SYSTEM*/ + } + + full_newset = newset; + if (strlen (newset) > 3 && !ascii_memcasecmp (newset, "iso", 3)) + { + newset += 3; + if (*newset == '-' || *newset == '_') + newset++; + } + + /* Note that we silently assume that plain ASCII is actually meant + as Latin-1. This makes sense because many Unix system don't have + their locale set up properly and thus would get annoying error + messages and we have to handle all the "bug" reports. Latin-1 has + traditionally been the character set used for 8 bit characters on + Unix systems. */ + if ( !*newset + || !ascii_strcasecmp (newset, "8859-1" ) + || !ascii_strcasecmp (newset, "646" ) + || !ascii_strcasecmp (newset, "ASCII" ) + || !ascii_strcasecmp (newset, "ANSI_X3.4-1968" ) + ) + { + active_charset_name = "iso-8859-1"; + no_translation = 0; + use_iconv = 0; + } + else if ( !ascii_strcasecmp (newset, "utf8" ) + || !ascii_strcasecmp(newset, "utf-8") ) + { + active_charset_name = "utf-8"; + no_translation = 1; + use_iconv = 0; + } + else + { + iconv_t cd; + + cd = iconv_open (full_newset, "utf-8"); + if (cd == (iconv_t)-1) + { + handle_iconv_error (full_newset, "utf-8", 0); + return -1; + } + iconv_close (cd); + cd = iconv_open ("utf-8", full_newset); + if (cd == (iconv_t)-1) + { + handle_iconv_error ("utf-8", full_newset, 0); + return -1; + } + iconv_close (cd); + active_charset_name = full_newset; + no_translation = 0; + use_iconv = 1; + } + return 0; +} + +const char * +get_native_charset () +{ + return active_charset_name; +} + +/* Return true if the native charset is utf-8. */ +int +is_native_utf8 (void) +{ + return no_translation; +} + + +/* Convert string, which is in native encoding to UTF8 and return a + new allocated UTF-8 string. This function terminates the process + on memory shortage. */ +char * +native_to_utf8 (const char *orig_string) +{ + const unsigned char *string = (const unsigned char *)orig_string; + const unsigned char *s; + char *buffer; + unsigned char *p; + size_t length = 0; + + if (no_translation) + { + /* Already utf-8 encoded. */ + buffer = xstrdup (orig_string); + } + else if (!use_iconv) + { + /* For Latin-1 we can avoid the iconv overhead. */ + for (s = string; *s; s++) + { + length++; + if (*s & 0x80) + length++; + } + buffer = xmalloc (length + 1); + for (p = (unsigned char *)buffer, s = string; *s; s++) + { + if ( (*s & 0x80 )) + { + *p++ = 0xc0 | ((*s >> 6) & 3); + *p++ = 0x80 | (*s & 0x3f); + } + else + *p++ = *s; + } + *p = 0; + } + else + { + /* Need to use iconv. */ + iconv_t cd; + const char *inptr; + char *outptr; + size_t inbytes, outbytes; + + cd = iconv_open ("utf-8", active_charset_name); + if (cd == (iconv_t)-1) + { + handle_iconv_error ("utf-8", active_charset_name, 1); + return native_to_utf8 (string); + } + + for (s=string; *s; s++ ) + { + length++; + if ((*s & 0x80)) + length += 5; /* We may need up to 6 bytes for the utf8 output. */ + } + buffer = xmalloc (length + 1); + + inptr = string; + inbytes = strlen (string); + outptr = buffer; + outbytes = length; + if ( iconv (cd, (ICONV_CONST char **)&inptr, &inbytes, + &outptr, &outbytes) == (size_t)-1) + { + static int shown; + + if (!shown) + log_info (_("conversion from '%s' to '%s' failed: %s\n"), + active_charset_name, "utf-8", strerror (errno)); + shown = 1; + /* We don't do any conversion at all but use the strings as is. */ + strcpy (buffer, string); + } + else /* Success. */ + { + *outptr = 0; + /* We could realloc the buffer now but I doubt that it makes + much sense given that it will get freed anyway soon + after. */ + } + iconv_close (cd); + } + return buffer; +} + + + +static char * +do_utf8_to_native (const char *string, size_t length, int delim, + int with_iconv) +{ + int nleft; + int i; + unsigned char encbuf[8]; + int encidx; + const unsigned char *s; + size_t n; + char *buffer = NULL; + char *p = NULL; + unsigned long val = 0; + size_t slen; + int resync = 0; + + /* First pass (p==NULL): count the extended utf-8 characters. */ + /* Second pass (p!=NULL): create string. */ + for (;;) + { + for (slen = length, nleft = encidx = 0, n = 0, + s = (const unsigned char *)string; + slen; + s++, slen--) + { + if (resync) + { + if (!(*s < 128 || (*s >= 0xc0 && *s <= 0xfd))) + { + /* Still invalid. */ + if (p) + { + sprintf (p, "\\x%02x", *s); + p += 4; + } + n += 4; + continue; + } + resync = 0; + } + if (!nleft) + { + if (!(*s & 0x80)) + { + /* Plain ascii. */ + if ( delim != -1 + && (*s < 0x20 || *s == 0x7f || *s == delim + || (delim && *s == '\\'))) + { + n++; + if (p) + *p++ = '\\'; + switch (*s) + { + case '\n': n++; if ( p ) *p++ = 'n'; break; + case '\r': n++; if ( p ) *p++ = 'r'; break; + case '\f': n++; if ( p ) *p++ = 'f'; break; + case '\v': n++; if ( p ) *p++ = 'v'; break; + case '\b': n++; if ( p ) *p++ = 'b'; break; + case 0: n++; if ( p ) *p++ = '0'; break; + default: + n += 3; + if (p) + { + sprintf (p, "x%02x", *s); + p += 3; + } + break; + } + } + else + { + if (p) + *p++ = *s; + n++; + } + } + else if ((*s & 0xe0) == 0xc0) /* 110x xxxx */ + { + val = *s & 0x1f; + nleft = 1; + encidx = 0; + encbuf[encidx++] = *s; + } + else if ((*s & 0xf0) == 0xe0) /* 1110 xxxx */ + { + val = *s & 0x0f; + nleft = 2; + encidx = 0; + encbuf[encidx++] = *s; + } + else if ((*s & 0xf8) == 0xf0) /* 1111 0xxx */ + { + val = *s & 0x07; + nleft = 3; + encidx = 0; + encbuf[encidx++] = *s; + } + else if ((*s & 0xfc) == 0xf8) /* 1111 10xx */ + { + val = *s & 0x03; + nleft = 4; + encidx = 0; + encbuf[encidx++] = *s; + } + else if ((*s & 0xfe) == 0xfc) /* 1111 110x */ + { + val = *s & 0x01; + nleft = 5; + encidx = 0; + encbuf[encidx++] = *s; + } + else /* Invalid encoding: print as \xNN. */ + { + if (p) + { + sprintf (p, "\\x%02x", *s); + p += 4; + } + n += 4; + resync = 1; + } + } + else if (*s < 0x80 || *s >= 0xc0) /* Invalid utf-8 */ + { + if (p) + { + for (i = 0; i < encidx; i++) + { + sprintf (p, "\\x%02x", encbuf[i]); + p += 4; + } + sprintf (p, "\\x%02x", *s); + p += 4; + } + n += 4 + 4 * encidx; + nleft = 0; + encidx = 0; + resync = 1; + } + else + { + encbuf[encidx++] = *s; + val <<= 6; + val |= *s & 0x3f; + if (!--nleft) /* Ready. */ + { + if (no_translation) + { + if (p) + { + for (i = 0; i < encidx; i++) + *p++ = encbuf[i]; + } + n += encidx; + encidx = 0; + } + else if (with_iconv) + { + /* Our strategy for using iconv is a bit strange + but it better keeps compatibility with + previous versions in regard to how invalid + encodings are displayed. What we do is to + keep the utf-8 as is and have the real + translation step then at the end. Yes, I + know that this is ugly. However we are short + of the 1.4 release and for this branch we + should not mess too much around with iconv + things. One reason for this is that we don't + know enough about non-GNU iconv + implementation and want to minimize the risk + of breaking the code on too many platforms. */ + if ( p ) + { + for (i=0; i < encidx; i++ ) + *p++ = encbuf[i]; + } + n += encidx; + encidx = 0; + } + else /* Latin-1 case. */ + { + if (val >= 0x80 && val < 256) + { + /* We can simply print this character */ + n++; + if (p) + *p++ = val; + } + else + { + /* We do not have a translation: print utf8. */ + if (p) + { + for (i = 0; i < encidx; i++) + { + sprintf (p, "\\x%02x", encbuf[i]); + p += 4; + } + } + n += encidx * 4; + encidx = 0; + } + } + } + + } + } + if (!buffer) + { + /* Allocate the buffer after the first pass. */ + buffer = p = xmalloc (n + 1); + } + else if (with_iconv) + { + /* Note: See above for comments. */ + iconv_t cd; + const char *inptr; + char *outbuf, *outptr; + size_t inbytes, outbytes; + + *p = 0; /* Terminate the buffer. */ + + cd = iconv_open (active_charset_name, "utf-8"); + if (cd == (iconv_t)-1) + { + handle_iconv_error (active_charset_name, "utf-8", 1); + xfree (buffer); + return utf8_to_native (string, length, delim); + } + + /* Allocate a new buffer large enough to hold all possible + encodings. */ + n = p - buffer + 1; + inbytes = n - 1;; + inptr = buffer; + outbytes = n * MB_LEN_MAX; + if (outbytes / MB_LEN_MAX != n) + BUG (); /* Actually an overflow. */ + outbuf = outptr = xmalloc (outbytes); + if ( iconv (cd, (ICONV_CONST char **)&inptr, &inbytes, + &outptr, &outbytes) == (size_t)-1) + { + static int shown; + + if (!shown) + log_info (_("conversion from '%s' to '%s' failed: %s\n"), + "utf-8", active_charset_name, strerror (errno)); + shown = 1; + /* Didn't worked out. Try again but without iconv. */ + xfree (buffer); + buffer = NULL; + xfree (outbuf); + outbuf = do_utf8_to_native (string, length, delim, 0); + } + else /* Success. */ + { + *outptr = 0; /* Make sure it is a string. */ + /* We could realloc the buffer now but I doubt that it + makes much sense given that it will get freed + anyway soon after. */ + xfree (buffer); + } + iconv_close (cd); + return outbuf; + } + else /* Not using iconv. */ + { + *p = 0; /* Make sure it is a string. */ + return buffer; + } + } +} + +/* Convert string, which is in UTF-8 to native encoding. Replace + illegal encodings by some "\xnn" and quote all control + characters. A character with value DELIM will always be quoted, it + must be a vanilla ASCII character. A DELIM value of -1 is special: + it disables all quoting of control characters. This function + terminates the process on memory shortage. */ +char * +utf8_to_native (const char *string, size_t length, int delim) +{ + return do_utf8_to_native (string, length, delim, use_iconv); +} + + + + +/* Wrapper function for iconv_open, required for W32 as we dlopen that + library on that system. */ +jnlib_iconv_t +jnlib_iconv_open (const char *tocode, const char *fromcode) +{ + return (jnlib_iconv_t)iconv_open (tocode, fromcode); +} + + +/* Wrapper function for iconv, required for W32 as we dlopen that + library on that system. */ +size_t +jnlib_iconv (jnlib_iconv_t cd, + const char **inbuf, size_t *inbytesleft, + char **outbuf, size_t *outbytesleft) +{ + return iconv ((iconv_t)cd, (ICONV_CONST char**)inbuf, inbytesleft, + outbuf, outbytesleft); +} + +/* Wrapper function for iconv_close, required for W32 as we dlopen that + library on that system. */ +int +jnlib_iconv_close (jnlib_iconv_t cd) +{ + return iconv_close ((iconv_t)cd); +} + + +#ifdef HAVE_W32_SYSTEM +/* Return a malloced string encoded for CODEPAGE from the wide char input + string STRING. Caller must free this value. Returns NULL and sets + ERRNO on failure. Calling this function with STRING set to NULL is + not defined. */ +static char * +wchar_to_cp (const wchar_t *string, unsigned int codepage) +{ + int n; + char *result; + + n = WideCharToMultiByte (codepage, 0, string, -1, NULL, 0, NULL, NULL); + if (n < 0) + { + gpg_err_set_errno (EINVAL); + return NULL; + } + + result = xtrymalloc (n+1); + if (!result) + return NULL; + + n = WideCharToMultiByte (codepage, 0, string, -1, result, n, NULL, NULL); + if (n < 0) + { + xfree (result); + gpg_err_set_errno (EINVAL); + result = NULL; + } + return result; +} + + +/* Return a malloced wide char string from a CODEPAGE encoded input + string STRING. Caller must free this value. Returns NULL and sets + ERRNO on failure. Calling this function with STRING set to NULL is + not defined. */ +static wchar_t * +cp_to_wchar (const char *string, unsigned int codepage) +{ + int n; + size_t nbytes; + wchar_t *result; + + n = MultiByteToWideChar (codepage, 0, string, -1, NULL, 0); + if (n < 0) + { + gpg_err_set_errno (EINVAL); + return NULL; + } + + nbytes = (size_t)(n+1) * sizeof(*result); + if (nbytes / sizeof(*result) != (n+1)) + { + gpg_err_set_errno (ENOMEM); + return NULL; + } + result = xtrymalloc (nbytes); + if (!result) + return NULL; + + n = MultiByteToWideChar (codepage, 0, string, -1, result, n); + if (n < 0) + { + xfree (result); + gpg_err_set_errno (EINVAL); + result = NULL; + } + return result; +} + + +/* Get the current codepage as used by wchar_to_native and + * native_to_char. Note that these functions intentionally do not use + * iconv based conversion machinery. */ +static unsigned int +get_w32_codepage (void) +{ + static unsigned int cp; + + if (!cp) + { +#ifndef HAVE_W32CE_SYSTEM + cp = GetConsoleOutputCP (); + if (!cp) +#endif + cp = GetACP (); + } + return cp; +} + +/* Return a malloced string encoded in the active code page from the + * wide char input string STRING. Caller must free this value. + * Returns NULL and sets ERRNO on failure. Calling this function with + * STRING set to NULL is not defined. */ +char * +wchar_to_native (const wchar_t *string) +{ + return wchar_to_cp (string, get_w32_codepage ()); +} + + +/* Return a malloced wide char string from native encoded input + * string STRING. Caller must free this value. Returns NULL and sets + * ERRNO on failure. Calling this function with STRING set to NULL is + * not defined. */ +wchar_t * +native_to_wchar (const char *string) +{ + return cp_to_wchar (string, get_w32_codepage ()); +} + + +/* Return a malloced string encoded in UTF-8 from the wide char input + * string STRING. Caller must free this value. Returns NULL and sets + * ERRNO on failure. Calling this function with STRING set to NULL is + * not defined. */ +char * +wchar_to_utf8 (const wchar_t *string) +{ + return wchar_to_cp (string, CP_UTF8); +} + + +/* Return a malloced wide char string from an UTF-8 encoded input + * string STRING. Caller must free this value. Returns NULL and sets + * ERRNO on failure. Calling this function with STRING set to NULL is + * not defined. */ +wchar_t * +utf8_to_wchar (const char *string) +{ + return cp_to_wchar (string, CP_UTF8); +} + +#endif /*HAVE_W32_SYSTEM*/ diff --git a/common/utf8conv.h b/common/utf8conv.h new file mode 100644 index 0000000..8b76e11 --- /dev/null +++ b/common/utf8conv.h @@ -0,0 +1,58 @@ +/* utf8conf.h + * Copyright (C) 2003, 2006 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute and/or modify this + * part of GnuPG under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * GnuPG is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copies of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef GNUPG_COMMON_UTF8CONF_H +#define GNUPG_COMMON_UTF8CONF_H + +int set_native_charset (const char *newset); +const char *get_native_charset (void); +int is_native_utf8 (void); + +char *native_to_utf8 (const char *string); +char *utf8_to_native (const char *string, size_t length, int delim); + + +/* Silly wrappers, required for W32 portability. */ +typedef void *jnlib_iconv_t; + +jnlib_iconv_t jnlib_iconv_open (const char *tocode, const char *fromcode); +size_t jnlib_iconv (jnlib_iconv_t cd, const char **inbuf, size_t *inbytesleft, + char **outbuf, size_t *outbytesleft); +int jnlib_iconv_close (jnlib_iconv_t cd); + +#ifdef HAVE_W32_SYSTEM +char *wchar_to_native (const wchar_t *string); +wchar_t *native_to_wchar (const char *string); +char *wchar_to_utf8 (const wchar_t *string); +wchar_t *utf8_to_wchar (const char *string); +#endif /*HAVE_W32_SYSTEM*/ + + +#endif /*GNUPG_COMMON_UTF8CONF_H*/ diff --git a/common/util.h b/common/util.h new file mode 100644 index 0000000..82b3a34 --- /dev/null +++ b/common/util.h @@ -0,0 +1,424 @@ +/* util.h - Utility functions for GnuPG + * Copyright (C) 2001, 2002, 2003, 2004, 2009 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute and/or modify this + * part of GnuPG under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * GnuPG is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copies of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef GNUPG_COMMON_UTIL_H +#define GNUPG_COMMON_UTIL_H + +#include <gcrypt.h> /* We need this for the memory function protos. */ +#include <errno.h> /* We need errno. */ +#include <gpg-error.h> /* We need gpg_error_t and estream. */ + +/* These error codes might be used but not defined in the required + * libgpg-error version. Define them here. + * Example: (#if GPG_ERROR_VERSION_NUMBER < 0x011500 // 1.21) + */ +#if GPG_ERROR_VERSION_NUMBER < 0x012400 /* 1.36 */ +# define GPG_ERR_NO_AUTH 314 +# define GPG_ERR_BAD_AUTH 315 +#endif + +#ifndef EXTERN_UNLESS_MAIN_MODULE +# if !defined (INCLUDED_BY_MAIN_MODULE) +# define EXTERN_UNLESS_MAIN_MODULE extern +# else +# define EXTERN_UNLESS_MAIN_MODULE +# endif +#endif + +/* Hash function used with libksba. */ +#define HASH_FNC ((void (*)(void *, const void*,size_t))gcry_md_write) + +/* The length of the keygrip. This is a SHA-1 hash of the key + * parameters as generated by gcry_pk_get_keygrip. */ +#define KEYGRIP_LEN 20 + + +/* Get all the stuff from jnlib. */ +#include "../common/logging.h" +#include "../common/argparse.h" +#include "../common/stringhelp.h" +#include "../common/mischelp.h" +#include "../common/strlist.h" +#include "../common/dotlock.h" +#include "../common/utf8conv.h" +#include "../common/dynload.h" +#include "../common/fwddecl.h" +#include "../common/utilproto.h" + +#include "gettime.h" + +/* Redefine asprintf by our estream version which uses our own memory + allocator.. */ +#define asprintf gpgrt_asprintf +#define vasprintf gpgrt_vasprintf + +/* Due to a bug in mingw32's snprintf related to the 'l' modifier and + for increased portability we use our snprintf on all systems. */ +#undef snprintf +#define snprintf gpgrt_snprintf + + +/* Replacements for macros not available with libgpg-error < 1.20. */ + +/* We need this type even if we are not using libreadline and or we + did not include libreadline in the current file. */ +#ifndef GNUPG_LIBREADLINE_H_INCLUDED +typedef char **rl_completion_func_t (const char *, int, int); +#endif /*!GNUPG_LIBREADLINE_H_INCLUDED*/ + + +/* Handy malloc macros - please use only them. */ +#define xtrymalloc(a) gcry_malloc ((a)) +#define xtrymalloc_secure(a) gcry_malloc_secure ((a)) +#define xtrycalloc(a,b) gcry_calloc ((a),(b)) +#define xtrycalloc_secure(a,b) gcry_calloc_secure ((a),(b)) +#define xtryrealloc(a,b) gcry_realloc ((a),(b)) +#define xtrystrdup(a) gcry_strdup ((a)) +#define xfree(a) gcry_free ((a)) +#define xfree_fnc gcry_free + +#define xmalloc(a) gcry_xmalloc ((a)) +#define xmalloc_secure(a) gcry_xmalloc_secure ((a)) +#define xcalloc(a,b) gcry_xcalloc ((a),(b)) +#define xcalloc_secure(a,b) gcry_xcalloc_secure ((a),(b)) +#define xrealloc(a,b) gcry_xrealloc ((a),(b)) +#define xstrdup(a) gcry_xstrdup ((a)) + +/* For compatibility with gpg 1.4 we also define these: */ +#define xmalloc_clear(a) gcry_xcalloc (1, (a)) +#define xmalloc_secure_clear(a) gcry_xcalloc_secure (1, (a)) + +/* The default error source of the application. This is different + from GPG_ERR_SOURCE_DEFAULT in that it does not depend on the + source file and thus is usable in code shared by applications. + Defined by init.c. */ +extern gpg_err_source_t default_errsource; + +/* Convenience function to return a gpg-error code for memory + allocation failures. This function makes sure that an error will + be returned even if accidentally ERRNO is not set. */ +static inline gpg_error_t +out_of_core (void) +{ + return gpg_error_from_syserror (); +} + + +/*-- yesno.c --*/ +int answer_is_yes (const char *s); +int answer_is_yes_no_default (const char *s, int def_answer); +int answer_is_yes_no_quit (const char *s); +int answer_is_okay_cancel (const char *s, int def_answer); + +/*-- xreadline.c --*/ +ssize_t read_line (FILE *fp, + char **addr_of_buffer, size_t *length_of_buffer, + size_t *max_length); + +/*-- b64enc.c and b64dec.c --*/ +struct b64state +{ + unsigned int flags; + int idx; + int quad_count; + FILE *fp; + estream_t stream; + char *title; + unsigned char radbuf[4]; + u32 crc; + int stop_seen:1; + int invalid_encoding:1; + gpg_error_t lasterr; +}; + +gpg_error_t b64enc_start (struct b64state *state, FILE *fp, const char *title); +gpg_error_t b64enc_start_es (struct b64state *state, estream_t fp, + const char *title); +gpg_error_t b64enc_write (struct b64state *state, + const void *buffer, size_t nbytes); +gpg_error_t b64enc_finish (struct b64state *state); + +gpg_error_t b64dec_start (struct b64state *state, const char *title); +gpg_error_t b64dec_proc (struct b64state *state, void *buffer, size_t length, + size_t *r_nbytes); +gpg_error_t b64dec_finish (struct b64state *state); + +/*-- sexputil.c */ +char *canon_sexp_to_string (const unsigned char *canon, size_t canonlen); +void log_printcanon (const char *text, + const unsigned char *sexp, size_t sexplen); +void log_printsexp (const char *text, gcry_sexp_t sexp); + +gpg_error_t make_canon_sexp (gcry_sexp_t sexp, + unsigned char **r_buffer, size_t *r_buflen); +gpg_error_t make_canon_sexp_pad (gcry_sexp_t sexp, int secure, + unsigned char **r_buffer, size_t *r_buflen); +gpg_error_t keygrip_from_canon_sexp (const unsigned char *key, size_t keylen, + unsigned char *grip); +int cmp_simple_canon_sexp (const unsigned char *a, const unsigned char *b); +int cmp_canon_sexp (const unsigned char *a, size_t alen, + const unsigned char *b, size_t blen, + int (*tcmp)(void *ctx, int depth, + const unsigned char *aval, size_t avallen, + const unsigned char *bval, size_t bvallen), + void *tcmpctx); +unsigned char *make_simple_sexp_from_hexstr (const char *line, + size_t *nscanned); +int hash_algo_from_sigval (const unsigned char *sigval); +unsigned char *make_canon_sexp_from_rsa_pk (const void *m, size_t mlen, + const void *e, size_t elen, + size_t *r_len); +gpg_error_t get_rsa_pk_from_canon_sexp (const unsigned char *keydata, + size_t keydatalen, + unsigned char const **r_n, + size_t *r_nlen, + unsigned char const **r_e, + size_t *r_elen); +gpg_error_t get_ecc_q_from_canon_sexp (const unsigned char *keydata, + size_t keydatalen, + unsigned char const **r_q, + size_t *r_qlen); +gpg_error_t uncompress_ecc_q_in_canon_sexp (const unsigned char *keydata, + size_t keydatalen, + unsigned char **r_newkeydata, + size_t *r_newkeydatalen); + +int get_pk_algo_from_key (gcry_sexp_t key); +int get_pk_algo_from_canon_sexp (const unsigned char *keydata, + size_t keydatalen); +char *pubkey_algo_string (gcry_sexp_t s_pkey, enum gcry_pk_algos *r_algoid); +const char *pubkey_algo_to_string (int algo); +const char *hash_algo_to_string (int algo); +const char *cipher_mode_to_string (int mode); + +/*-- convert.c --*/ +int hex2bin (const char *string, void *buffer, size_t length); +int hexcolon2bin (const char *string, void *buffer, size_t length); +char *bin2hex (const void *buffer, size_t length, char *stringbuf); +char *bin2hexcolon (const void *buffer, size_t length, char *stringbuf); +const char *hex2str (const char *hexstring, + char *buffer, size_t bufsize, size_t *buflen); +char *hex2str_alloc (const char *hexstring, size_t *r_count); + +/*-- percent.c --*/ +char *percent_plus_escape (const char *string); +char *percent_data_escape (int plus_escape, const char *prefix, + const void *data, size_t datalen); +char *percent_plus_unescape (const char *string, int nulrepl); +char *percent_unescape (const char *string, int nulrepl); + +size_t percent_plus_unescape_inplace (char *string, int nulrepl); +size_t percent_unescape_inplace (char *string, int nulrepl); + +/*-- openpgp-oid.c --*/ +gpg_error_t openpgp_oid_from_str (const char *string, gcry_mpi_t *r_mpi); +char *openpgp_oidbuf_to_str (const unsigned char *buf, size_t len); +char *openpgp_oid_to_str (gcry_mpi_t a); +int openpgp_oidbuf_is_ed25519 (const void *buf, size_t len); +int openpgp_oid_is_ed25519 (gcry_mpi_t a); +int openpgp_oidbuf_is_cv25519 (const void *buf, size_t len); +int openpgp_oid_is_cv25519 (gcry_mpi_t a); +const char *openpgp_curve_to_oid (const char *name, + unsigned int *r_nbits, int *r_algo); +const char *openpgp_oid_to_curve (const char *oid, int canon); +const char *openpgp_enum_curves (int *idxp); +const char *openpgp_is_curve_supported (const char *name, + int *r_algo, unsigned int *r_nbits); + + +/*-- homedir.c --*/ +const char *standard_homedir (void); +const char *default_homedir (void); +void gnupg_set_homedir (const char *newdir); +void gnupg_maybe_make_homedir (const char *fname, int quiet); +const char *gnupg_homedir (void); +int gnupg_default_homedir_p (void); +const char *gnupg_daemon_rootdir (void); +const char *gnupg_socketdir (void); +const char *gnupg_sysconfdir (void); +const char *gnupg_bindir (void); +const char *gnupg_libexecdir (void); +const char *gnupg_libdir (void); +const char *gnupg_datadir (void); +const char *gnupg_localedir (void); +const char *gpg_agent_socket_name (void); +const char *dirmngr_socket_name (void); + +char *_gnupg_socketdir_internal (int skip_checks, unsigned *r_info); + +/* All module names. We also include gpg and gpgsm for the sake for + gpgconf. */ +#define GNUPG_MODULE_NAME_AGENT 1 +#define GNUPG_MODULE_NAME_PINENTRY 2 +#define GNUPG_MODULE_NAME_SCDAEMON 3 +#define GNUPG_MODULE_NAME_DIRMNGR 4 +#define GNUPG_MODULE_NAME_PROTECT_TOOL 5 +#define GNUPG_MODULE_NAME_CHECK_PATTERN 6 +#define GNUPG_MODULE_NAME_GPGSM 7 +#define GNUPG_MODULE_NAME_GPG 8 +#define GNUPG_MODULE_NAME_CONNECT_AGENT 9 +#define GNUPG_MODULE_NAME_GPGCONF 10 +#define GNUPG_MODULE_NAME_DIRMNGR_LDAP 11 +#define GNUPG_MODULE_NAME_GPGV 12 +const char *gnupg_module_name (int which); +void gnupg_module_name_flush_some (void); +void gnupg_set_builddir (const char *newdir); + + + +/*-- gpgrlhelp.c --*/ +void gnupg_rl_initialize (void); + +/*-- helpfile.c --*/ +char *gnupg_get_help_string (const char *key, int only_current_locale); + +/*-- localename.c --*/ +const char *gnupg_messages_locale_name (void); + +/*-- sysutils.c --*/ +FILE *gnupg_fopen (const char *fname, const char *mode); + +/*-- miscellaneous.c --*/ + +/* This function is called at startup to tell libgcrypt to use our own + logging subsystem. */ +void setup_libgcrypt_logging (void); + +/* Print an out of core message and die. */ +void xoutofcore (void); + +/* Array allocation. */ +void *gnupg_reallocarray (void *a, size_t oldnmemb, size_t nmemb, size_t size); +void *xreallocarray (void *a, size_t oldnmemb, size_t nmemb, size_t size); + +/* Same as estream_asprintf but die on memory failure. */ +char *xasprintf (const char *fmt, ...) GPGRT_ATTR_PRINTF(1,2); +/* This is now an alias to estream_asprintf. */ +char *xtryasprintf (const char *fmt, ...) GPGRT_ATTR_PRINTF(1,2); + +void *xtryreallocarray (void *a, size_t oldnmemb, size_t nmemb, size_t size); + +/* Replacement for gcry_cipher_algo_name. */ +const char *gnupg_cipher_algo_name (int algo); + +void obsolete_option (const char *configname, unsigned int configlineno, + const char *name); + +const char *print_fname_stdout (const char *s); +const char *print_fname_stdin (const char *s); +void print_utf8_buffer3 (estream_t fp, const void *p, size_t n, + const char *delim); +void print_utf8_buffer2 (estream_t fp, const void *p, size_t n, int delim); +void print_utf8_buffer (estream_t fp, const void *p, size_t n); +void print_utf8_string (estream_t stream, const char *p); +void print_hexstring (FILE *fp, const void *buffer, size_t length, + int reserved); +char *try_make_printable_string (const void *p, size_t n, int delim); +char *make_printable_string (const void *p, size_t n, int delim); +char *decode_c_string (const char *src); + +int is_file_compressed (const char *s, int *ret_rc); + +int match_multistr (const char *multistr,const char *match); + +int gnupg_compare_version (const char *a, const char *b); + +struct debug_flags_s +{ + unsigned int flag; + const char *name; +}; +int parse_debug_flag (const char *string, unsigned int *debugvar, + const struct debug_flags_s *flags); + +struct compatibility_flags_s +{ + unsigned int flag; + const char *name; + const char *desc; +}; +int parse_compatibility_flags (const char *string, unsigned int *flagvar, + const struct compatibility_flags_s *flags); + + +/*-- Simple replacement functions. */ + +/* We use the gnupg_ttyname macro to be safe not to run into conflicts + which an extisting but broken ttyname. */ +#if !defined(HAVE_TTYNAME) || defined(HAVE_BROKEN_TTYNAME) +# define gnupg_ttyname(n) _gnupg_ttyname ((n)) +/* Systems without ttyname (W32) will merely return NULL. */ +static inline char * +_gnupg_ttyname (int fd) +{ + (void)fd; + return NULL; +} +#else /*HAVE_TTYNAME*/ +# define gnupg_ttyname(n) ttyname ((n)) +#endif /*HAVE_TTYNAME */ + +#ifdef HAVE_W32CE_SYSTEM +#define getpid() GetCurrentProcessId () +char *_gnupg_getenv (const char *name); /* See sysutils.c */ +#define getenv(a) _gnupg_getenv ((a)) +char *_gnupg_setenv (const char *name); /* See sysutils.c */ +#define setenv(a,b,c) _gnupg_setenv ((a),(b),(c)) +int _gnupg_isatty (int fd); +#define gnupg_isatty(a) _gnupg_isatty ((a)) +#else +#define gnupg_isatty(a) isatty ((a)) +#endif + + + +/*-- Macros to replace ctype ones to avoid locale problems. --*/ +#define spacep(p) (*(p) == ' ' || *(p) == '\t') +#define digitp(p) (*(p) >= '0' && *(p) <= '9') +#define alphap(p) ((*(p) >= 'A' && *(p) <= 'Z') \ + || (*(p) >= 'a' && *(p) <= 'z')) +#define alnump(p) (alphap (p) || digitp (p)) +#define hexdigitp(a) (digitp (a) \ + || (*(a) >= 'A' && *(a) <= 'F') \ + || (*(a) >= 'a' && *(a) <= 'f')) + /* Note this isn't identical to a C locale isspace() without \f and + \v, but works for the purposes used here. */ +#define ascii_isspace(a) ((a)==' ' || (a)=='\n' || (a)=='\r' || (a)=='\t') + +/* The atoi macros assume that the buffer has only valid digits. */ +#define atoi_1(p) (*(p) - '0' ) +#define atoi_2(p) ((atoi_1(p) * 10) + atoi_1((p)+1)) +#define atoi_4(p) ((atoi_2(p) * 100) + atoi_2((p)+2)) +#define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \ + *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10)) +#define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1)) +#define xtoi_4(p) ((xtoi_2(p) * 256) + xtoi_2((p)+2)) + +#endif /*GNUPG_COMMON_UTIL_H*/ diff --git a/common/utilproto.h b/common/utilproto.h new file mode 100644 index 0000000..7467f6b --- /dev/null +++ b/common/utilproto.h @@ -0,0 +1,44 @@ +/* utilproto.h - Some prototypes for inclusion by util.h + * Copyright (C) 2016 Werner Koch + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +/* This file is in general included via util.h but sometimes we do not + * want all stuff from util.h and instead use this file with its + * simple prototypes. */ + +#ifndef GNUPG_COMMON_UTILPROTO_H +#define GNUPG_COMMON_UTILPROTO_H + +/*-- signal.c --*/ +void gnupg_init_signals (int mode, void (*fast_cleanup)(void)); +void gnupg_block_all_signals (void); +void gnupg_unblock_all_signals (void); + + + +#endif /*GNUPG_COMMON_UTILPROTO_H*/ diff --git a/common/w32-cmdline.c b/common/w32-cmdline.c new file mode 100644 index 0000000..85d5752 --- /dev/null +++ b/common/w32-cmdline.c @@ -0,0 +1,450 @@ +/* w32-cmdline.c - Command line helper functions needed in Windows + * Copyright (C) 2021 g10 Code GmbH + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#ifdef HAVE_W32_SYSTEM +# define WIN32_LEAN_AND_MEAN +# include <windows.h> +#endif /*!HAVE_W32_SYSTEM*/ + +#include "util.h" +#include "w32help.h" + + +/* Helper object for add_arg. */ +struct add_arg_s +{ + char **argv; /* Calloced array. */ + int argc; /* Number of items in argc. */ + int size; /* Allocated size of argv. */ +}; + + +/* Add STRING to the argv of PARM. Returns 0 on success; on error + * sets ERRNO and returns -1. */ +static int +add_arg (struct add_arg_s *parm, const char *string) +{ + if (parm->argc == parm->size) + { + char **newargv; + int newsize; + + if (parm->size < 256) + newsize = ((parm->size + 31) / 32 + 1) * 32; + else + newsize = ((parm->size + 255) / 256 + 1) * 256; + /* We allocate one more item for the trailing NULL. */ + newargv = xtryreallocarray (parm->argv, parm->size, newsize+1, + sizeof *newargv); + if (!newargv) + return -1; + parm->argv = newargv; + parm->size = newsize; + } + parm->argv[parm->argc] = xtrystrdup (string); + if (!parm->argv[parm->argc]) + return -1; + parm->argc++; + return 0; +} + + +/* Glob PATTERN and add to the argv of PARM. Returns 0 on success; on + * error sets ERRNO and returns -1. */ +static int +glob_arg (struct add_arg_s *parm, const char *pattern) +{ + int rc; + const char *s; + +#ifdef HAVE_W32_SYSTEM + HANDLE hd; + WIN32_FIND_DATAW dir; + uintptr_t pos; /* Offset to the last slash in pattern/buffer or 0. */ + char *buffer, *p; + int any = 0; + + s = strpbrk (pattern, "*?"); + if (!s) + { + /* Called without wildcards. */ + return add_arg (parm, pattern); + } + for (; s != pattern && *s != '/' && *s != '\\'; s--) + ; + pos = s - pattern; + if (*s == '/' || *s == L'\\') + pos++; + + { + wchar_t *wpattern; + + wpattern = utf8_to_wchar (pattern); + if (!wpattern) + return -1; + + hd = FindFirstFileW (wpattern, &dir); + xfree (wpattern); + } + if (hd == INVALID_HANDLE_VALUE) + return add_arg (parm, pattern); + + /* We allocate enough space to hold all kind of UTF-8 strings. */ + buffer = xtrymalloc (strlen (pattern) + MAX_PATH*6 + 1); + if (!buffer) + { + FindClose (hd); + return -1; + } + mem2str (buffer, pattern, pos+1); + for (p=buffer; *p; p++) + if (*p == '\\') + *p = '/'; + + do + { + if (!(dir.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + { + char *name; + + name = wchar_to_utf8 (dir.cFileName); + if (!name) + rc = -1; + else + { + mem2str (buffer + pos, name, MAX_PATH*6); + xfree (name); + rc = add_arg (parm, buffer); + } + if (rc) + { + FindClose (hd); + xfree (buffer); + return rc; + } + any = 1; + } + } + while (FindNextFileW (hd, &dir)); + + FindClose (hd); + xfree (buffer); + + rc = any? 0 : add_arg (parm, pattern); + +#else /* Unix */ + + /* We use some dummy code here because this is only used in the Unix + * test suite. */ + s = strpbrk (pattern, "*?"); + if (!s) + { + /* Called without wildcards. */ + return add_arg (parm, pattern); + } + + if (strchr (pattern, '?')) + rc = add_arg (parm, "[? follows]"); + else if (strchr (pattern, '*')) + rc = add_arg (parm, "[* follows]"); + else + rc = add_arg (parm, "[no glob!]"); /* Should not happen. */ + if (!rc) + rc = add_arg (parm, pattern); + +#endif /* Unix */ + + return rc; +} + + +/* Return the number of backslashes. */ +static unsigned int +count_backslashes (const char *s) +{ + unsigned int count = 0; + + for ( ;*s == '\\'; s++) + count++; + return count; +} + + +static void +strip_one_arg (char *string, int endquote) +{ + char *s, *d; + unsigned int n, i; + + for (s=d=string; *s; s++) + if (*s == '\\') + { + n = count_backslashes (s); + if (s[n] == '"') + { + for (i=0; i < n/2; i++) + *d++ = '\\'; + if ((n&1)) /* Odd number of backslashes. */ + *d++ = '"'; /* Print the quote. */ + } + else if (!s[n] && endquote) + { + for (i=0; i < n/2; i++) + *d++ = '\\'; + s--; + } + else /* Print all backslashes. */ + { + for (i=0; i < n; i++) + *d++ = '\\'; + n--; /* Adjust for the increment in the for. */ + } + s += n; + } + else if (*s == '"' && s[1]) + *d++ = *++s; + else + *d++ = *s; + *d = 0; +} + + +/* Helper for parse_w32_commandline. If ARGV and ARGVFLAGS are not + * NULL, ARGVFLAGS is expected to be allocated at the same size of + * ARGV and zeroed; on return 1 is stored for all arguments which are + * quoted (args like (foo"bar"baz") also count as quoted. */ +static int +parse_cmdstring (char *string, char **argv, unsigned char *argvflags) +{ + int argc = 0; + int inquote = 0; + char *p0, *p; + unsigned int n; + + p0 = string; + for (p=string; *p; p++) + { + if (inquote) + { + if (*p == '\\' && p[1] == '"') + p++; + else if (*p == '\\' && p[1] == '\\') + p++; + else if (*p == '"') + { + if (p[1] == ' ' || p[1] == '\t' || !p[1]) + { + if (argv) + { + *p = 0; + strip_one_arg (p0, 1); + argv[argc] = p0; + if (argvflags) + argvflags[argc] = 1; + } + argc++; + p0 = NULL; + } + inquote = 0; + } + } + else if (*p == '\\' && (n=count_backslashes (p))) + { + if (!p0) /* First non-WS; set start. */ + p0 = p; + if (p[n] == '"') + { + if (!(n&1)) /* Even number. */ + inquote = 1; + p++; + } + p += n; + } + else if (*p == '"') + { + inquote = 1; + if (!p0 || p == string) /* First non-WS or first char; set start. */ + p0 = p + 1; + } + else if (*p == ' ' || *p == '\t') + { + if (p0) /* We are in an argument and reached WS. */ + { + if (argv) + { + *p = 0; + strip_one_arg (p0, inquote); + argv[argc] = p0; + if (argvflags && inquote) + argvflags[argc] = 1; + } + argc++; + p0 = NULL; + } + } + else if (!p0) /* First non-WS; set start. */ + p0 = p; + } + + if (inquote || p0) + { + /* Closing quote missing (we accept this as argument anyway) or + * an open argument. */ + if (argv) + { + *p = 0; + strip_one_arg (p0, inquote); + argv[argc] = p0; + if (argvflags && inquote) + argvflags[argc] = 1; + } + argc++; + } + + return argc; +} + +/* This is a Windows command line parser, returning an array with + * strings and its count. The argument CMDLINE is expected to be + * utf-8 encoded and may be modified after returning from this + * function. The returned array points into CMDLINE, so this should + * not be freed. If GLOBING is set to true globing is done for all + * items. Returns NULL on error. The number of items in the array is + * returned at R_ARGC. If R_ITEMSALLOCED is NOT NULL, it's value is + * set to true if the items at R_ALLOC are allocated and not point + * into to CMDLINE. */ +char ** +w32_parse_commandline (char *cmdline, int globing, int *r_argc, + int *r_itemsalloced) +{ + int argc, i; + char **argv; + char *argvflags; + + if (r_itemsalloced) + *r_itemsalloced = 0; + + argc = parse_cmdstring (cmdline, NULL, NULL); + if (!argc) + { + log_error ("%s failed: %s\n", __func__, "internal error"); + return NULL; /* Ooops. */ + } + argv = xtrycalloc (argc+1, sizeof *argv); + if (!argv) + { + log_error ("%s failed: %s\n", __func__, + gpg_strerror (gpg_error_from_syserror ())); + return NULL; /* Ooops. */ + } + if (globing) + { + argvflags = xtrycalloc (argc+1, sizeof *argvflags); + if (!argvflags) + { + log_error ("%s failed: %s\n", __func__, + gpg_strerror (gpg_error_from_syserror ())); + xfree (argv); + return NULL; /* Ooops. */ + } + } + else + argvflags = NULL; + + i = parse_cmdstring (cmdline, argv, argvflags); + if (argc != i) + { + log_error ("%s failed (argc=%d i=%d)\n", __func__, argc, i); + xfree (argv); + xfree (argvflags); + return NULL; /* Ooops. */ + } + + if (globing) + { + for (i=0; i < argc; i++) + if (argvflags[i] != 1 && strpbrk (argv[i], "*?")) + break; + if (i < argc) + { + /* Indeed some unquoted arguments contain wildcards. We + * need to do the globing and thus a dynamically re-allocate + * the argv array and strdup all items. */ + struct add_arg_s parm; + int rc; + + if (argc < 32) + parm.size = ((argc + 31) / 32 + 1) * 32; + else + parm.size = ((argc + 255) / 256 + 1) * 256; + parm.argc = 0; + /* We allocate one more item for the trailing NULL. */ + parm.argv = xtryreallocarray (NULL, 0, parm.size + 1, + sizeof *parm.argv); + if (!parm.argv) + { + log_error ("%s: error allocating array: %s\n", __func__, + gpg_strerror (gpg_error_from_syserror ())); + xfree (argv); + xfree (argvflags); + return NULL; /* Ooops. */ + } + rc = 0; + for (i=0; i < argc; i++) + { + if (argvflags[i] != 1) + rc = glob_arg (&parm, argv[i]); + else + rc = add_arg (&parm, argv[i]); + if (rc) + { + log_error ("%s: error adding or blobing: %s\n", __func__, + gpg_strerror (gpg_error_from_syserror ())); + for (i=0; i < parm.argc; i++) + xfree (parm.argv[i]); + xfree (parm.argv); + xfree (argv); + xfree (argvflags); + return NULL; /* Ooops. */ + } + } + xfree (argv); + argv = parm.argv; + argc = parm.argc; + if (r_itemsalloced) + *r_itemsalloced = 1; + } + } + + xfree (argvflags); + *r_argc = argc; + return argv; +} diff --git a/common/w32-reg.c b/common/w32-reg.c new file mode 100644 index 0000000..cbaba56 --- /dev/null +++ b/common/w32-reg.c @@ -0,0 +1,315 @@ +/* w32-reg.c - MS-Windows Registry access + * Copyright (C) 1999, 2002, 2007 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute and/or modify this + * part of GnuPG under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * GnuPG is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copies of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#ifdef HAVE_W32_SYSTEM + /* This module is only used in this environment */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#ifdef HAVE_WINSOCK2_H +# include <winsock2.h> +#endif +#include <windows.h> + +#include "util.h" +#include "common-defs.h" +#include "utf8conv.h" +#include "w32help.h" + + +static HKEY +get_root_key(const char *root) +{ + HKEY root_key; + + if (!root) + root_key = HKEY_CURRENT_USER; + else if (!strcmp (root, "HKEY_CLASSES_ROOT") || !strcmp (root, "HKCR")) + root_key = HKEY_CLASSES_ROOT; + else if (!strcmp (root, "HKEY_CURRENT_USER") || !strcmp (root, "HKCU")) + root_key = HKEY_CURRENT_USER; + else if (!strcmp (root, "HKEY_LOCAL_MACHINE") || !strcmp (root, "HKLM")) + root_key = HKEY_LOCAL_MACHINE; + else if (!strcmp (root, "HKEY_USERS") || !strcmp (root, "HKU")) + root_key = HKEY_USERS; + else if (!strcmp (root, "HKEY_PERFORMANCE_DATA")) + root_key = HKEY_PERFORMANCE_DATA; + else if (!strcmp (root, "HKEY_CURRENT_CONFIG") || !strcmp (root, "HKCC")) + root_key = HKEY_CURRENT_CONFIG; + else + return NULL; + + return root_key; +} + + +/* Return a string from the Win32 Registry or NULL in case of error. + Caller must release the return value. A NULL for root is an alias + for HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE in turn. */ +char * +read_w32_registry_string (const char *root, const char *dir, const char *name) +{ +#ifdef HAVE_W32CE_SYSTEM + HKEY root_key, key_handle; + DWORD n1, nbytes, type; + char *result = NULL; + wchar_t *wdir, *wname; + + if ( !(root_key = get_root_key(root) ) ) + return NULL; + + wdir = utf8_to_wchar (dir); + if (!wdir) + return NULL; + + if (RegOpenKeyEx (root_key, wdir, 0, KEY_READ, &key_handle) ) + { + if (root) + { + xfree (wdir); + return NULL; /* No need for a RegClose, so return immediately. */ + } + /* It seems to be common practise to fall back to HKLM. */ + if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, wdir, 0, KEY_READ, &key_handle) ) + { + xfree (wdir); + return NULL; /* Still no need for a RegClose. */ + } + } + xfree (wdir); + + if (name) + { + wname = utf8_to_wchar (name); + if (!wname) + goto leave; + } + else + wname = NULL; + + nbytes = 2; + if (RegQueryValueEx (key_handle, wname, 0, NULL, NULL, &nbytes)) + goto leave; + result = xtrymalloc ((n1=nbytes+2)); + if (!result) + goto leave; + if (RegQueryValueEx (key_handle, wname, 0, &type, result, &n1)) + { + xfree (result); + result = NULL; + goto leave; + } + result[nbytes] = 0; /* Make sure it is a string. */ + result[nbytes+1] = 0; + if (type == REG_SZ || type == REG_EXPAND_SZ) + { + wchar_t *tmp = (void*)result; + result = wchar_to_utf8 (tmp); + xfree (tmp); + } + + leave: + xfree (wname); + RegCloseKey (key_handle); + return result; +#else /*!HAVE_W32CE_SYSTEM*/ + HKEY root_key, key_handle; + DWORD n1, nbytes, type; + char *result = NULL; + + if ( !(root_key = get_root_key(root) ) ) + return NULL; + + if (RegOpenKeyEx (root_key, dir, 0, KEY_READ, &key_handle) ) + { + if (root) + return NULL; /* No need for a RegClose, so return immediately. */ + /* It seems to be common practise to fall back to HKLM. */ + if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, dir, 0, KEY_READ, &key_handle) ) + return NULL; /* Still no need for a RegClose. */ + } + + nbytes = 1; + if (RegQueryValueEx (key_handle, name, 0, NULL, NULL, &nbytes)) + { + if (root) + goto leave; + /* Try to fallback to HKLM also for a missing value. */ + RegCloseKey (key_handle); + if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, dir, 0, KEY_READ, &key_handle)) + return NULL; /* Nope. */ + if (RegQueryValueEx (key_handle, name, 0, NULL, NULL, &nbytes)) + goto leave; + } + + result = xtrymalloc ((n1=nbytes+1)); + if (!result) + goto leave; + if (RegQueryValueEx( key_handle, name, 0, &type, result, &n1 )) + { + xfree (result); + result = NULL; + goto leave; + } + result[nbytes] = 0; /* Make sure it is a string. */ + if (type == REG_EXPAND_SZ && strchr (result, '%')) + { + char *tmp; + + n1 += 1000; + tmp = xtrymalloc (n1+1); + if (!tmp) + goto leave; + nbytes = ExpandEnvironmentStrings (result, tmp, n1); + if (nbytes && nbytes > n1) + { + xfree (tmp); + n1 = nbytes; + tmp = xtrymalloc (n1 + 1); + if (!tmp) + goto leave; + nbytes = ExpandEnvironmentStrings (result, tmp, n1); + if (nbytes && nbytes > n1) + { + /* Oops - truncated, better don't expand at all. */ + xfree (tmp); + goto leave; + } + tmp[nbytes] = 0; + xfree (result); + result = tmp; + } + else if (nbytes) + { + /* Okay, reduce the length. */ + tmp[nbytes] = 0; + xfree (result); + result = xtrymalloc (strlen (tmp)+1); + if (!result) + result = tmp; + else + { + strcpy (result, tmp); + xfree (tmp); + } + } + else + { + /* Error - don't expand. */ + xfree (tmp); + } + } + else if (type == REG_DWORD && nbytes == sizeof (DWORD)) + { + char *tmp; + DWORD dummy; + + memcpy (&dummy, result, nbytes); + tmp = xtryasprintf ("%u", (unsigned int)dummy); + if (tmp) + { + xfree (result); + result = tmp; + } + } + + leave: + RegCloseKey (key_handle); + return result; +#endif /*!HAVE_W32CE_SYSTEM*/ +} + +/* Compact version of read_w32_registry_string. This version expects + * a single string as key described here using an example: + * + * HKCU\Software\GNU\GnuPG:HomeDir + * + * HKCU := the class, other supported classes are HKLM, HKCR, HKU, and + * HKCC. If no class is given and the string thus starts with + * a backslash HKCU with a fallback to HKLM is used. + * Software\GNU\GnuPG := The actual key. + * HomeDir := the name of the item. The name is optional to use the default + * value. + * + * Note that the first backslash and the first colon act as delimiters. + * + * Returns a malloced string or NULL if not found. If R_HKLM_FALLBACK + * is not NULL, no class was given, and the result came from HKLM, + * true is stored there. + */ +char * +read_w32_reg_string (const char *key_arg, int *r_hklm_fallback) +{ + char *key; + char *p1, *p2; + char *result, *result2; + + if (r_hklm_fallback) + *r_hklm_fallback = 0; + + if (!key_arg) + return NULL; + key = xtrystrdup (key_arg); + if (!key) + { + log_info ("warning: malloc failed while reading registry key\n"); + return NULL; + } + + p1 = strchr (key, '\\'); + if (!p1) + { + xfree (key); + return NULL; + } + *p1++ = 0; + p2 = strchr (p1, ':'); + if (p2) + *p2++ = 0; + + result = read_w32_registry_string (*key? key : NULL, p1, p2); + if (result && !*key && r_hklm_fallback) + { + /* No key given - see whether the result came from HKCU or HKLM. */ + result2 = read_w32_registry_string ("HKCU", p1, p2); + if (result2) + xfree (result2); + else + *r_hklm_fallback = 1; + } + xfree (key); + return result; +} + + +#endif /*HAVE_W32_SYSTEM*/ diff --git a/common/w32help.h b/common/w32help.h new file mode 100644 index 0000000..54e9267 --- /dev/null +++ b/common/w32help.h @@ -0,0 +1,66 @@ +/* w32help.h - W32 speicif functions + * Copyright (C) 2007 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute and/or modify this + * part of GnuPG under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * GnuPG is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copies of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef GNUPG_COMMON_W32HELP_H +#define GNUPG_COMMON_W32HELP_H + +/*-- w32-cmdline.c --*/ + +/* This module is also part of the Unix tests. */ +char **w32_parse_commandline (char *cmdline, int globing, int *r_argv, + int *r_itemsalloced); + + + +#ifdef HAVE_W32_SYSTEM + +/*-- w32-reg.c --*/ +char *read_w32_registry_string (const char *root, + const char *dir, const char *name ); +char *read_w32_reg_string (const char *key, int *r_hklm_fallback); + +/* Other stuff. */ +#ifdef HAVE_W32CE_SYSTEM +/* Setmode is missing in cegcc but available since CE 5.0. */ +int _setmode (int handle, int mode); +# define setmode(a,b) _setmode ((a),(b)) + +static inline int +umask (int a) +{ + (void)a; + return 0; +} + + +#endif /*HAVE_W32CE_SYSTEM*/ + +#endif /*HAVE_W32_SYSTEM*/ +#endif /*GNUPG_COMMON_MISCHELP_H*/ diff --git a/common/w32info-rc.h.in b/common/w32info-rc.h.in new file mode 100644 index 0000000..4c0e034 --- /dev/null +++ b/common/w32info-rc.h.in @@ -0,0 +1,32 @@ +/* w32info-rc.h.in - Common defs for VERSIONINFO resources. + * Copyright (C) 2013 g10 Code GmbH + * + * This file is free software; as a special exception the author gives + * unlimited permission to copy and/or distribute it, with or without + * modifications, as long as this notice is preserved. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY, to the extent permitted by law; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +/* This file is processed by configure to create w32info-rc.h . */ + +#define W32INFO_COMMENTS "This program is free software; \ +you can redistribute it and/or modify it under the terms of the \ +GNU General Public License as published by the Free Software Foundation; \ +either version 3 of the License, or (at your option) any later version.\0" + +#define W32INFO_COMPANYNAME "g10 Code GmbH\0" + +#define W32INFO_VI_FILEVERSION @BUILD_FILEVERSION@ +#define W32INFO_VI_PRODUCTVERSION @BUILD_FILEVERSION@ + +#define W32INFO_FILEVERSION "@VERSION@ (@BUILD_REVISION@) \ +built on @BUILD_HOSTNAME@ at @BUILD_TIMESTAMP@\0" + +#define W32INFO_PRODUCTNAME "GNU Privacy Guard (GnuPG)\0" +#define W32INFO_PRODUCTVERSION "@VERSION@\0" + +#define W32INFO_LEGALCOPYRIGHT "Copyright \xa9 \ +2022 g10 Code GmbH\0" diff --git a/common/xasprintf.c b/common/xasprintf.c new file mode 100644 index 0000000..7e37e5b --- /dev/null +++ b/common/xasprintf.c @@ -0,0 +1,123 @@ +/* xasprintf.c + * Copyright (C) 2003, 2005 Free Software Foundation, Inc. + * Copyright (C) 2020 g10 Code GmbH + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdlib.h> +#include <errno.h> + +#include "util.h" + +/* Same as asprintf but return an allocated buffer suitable to be + freed using xfree. This function simply dies on memory failure, + thus no extra check is required. + + FIXME: We should remove these functions in favor of gpgrt_bsprintf + and a xgpgrt_bsprintf or rename them to xbsprintf and + xtrybsprintf. */ +char * +xasprintf (const char *fmt, ...) +{ + va_list ap; + char *buf; + + va_start (ap, fmt); + if (gpgrt_vasprintf (&buf, fmt, ap) < 0) + log_fatal ("estream_asprintf failed: %s\n", strerror (errno)); + va_end (ap); + return buf; +} + +/* Same as above but return NULL on memory failure. */ +char * +xtryasprintf (const char *fmt, ...) +{ + int rc; + va_list ap; + char *buf; + + va_start (ap, fmt); + rc = gpgrt_vasprintf (&buf, fmt, ap); + va_end (ap); + if (rc < 0) + return NULL; + return buf; +} + + +/* This is safe version of realloc useful for reallocing a calloced + * array. There are two ways to call it: The first example + * reallocates the array A to N elements each of SIZE but does not + * clear the newly allocated elements: + * + * p = xtryreallocarray (a, n, n, nsize); + * + * Note that when NOLD is larger than N no cleaning is needed anyway. + * The second example reallocates an array of size NOLD to N elements + * each of SIZE but clear the newly allocated elements: + * + * p = xtryreallocarray (a, nold, n, nsize); + * + * Note that xtryreallocarray (NULL, 0, n, nsize) is equivalent to + * xtrycalloc (n, nsize). + * + * The same function under the name gpgrt_reallocarray exists in + * libgpg-error but only since version 1.38 and thus we use a copy + * here. + */ +void * +xtryreallocarray (void *a, size_t oldnmemb, size_t nmemb, size_t size) +{ + size_t oldbytes, bytes; + char *p; + + bytes = nmemb * size; /* size_t is unsigned so the behavior on overflow + * is defined. */ + if (size && bytes / size != nmemb) + { + gpg_err_set_errno (ENOMEM); + return NULL; + } + + p = xtryrealloc (a, bytes); + if (p && oldnmemb < nmemb) + { + /* OLDNMEMBS is lower than NMEMB thus the user asked for a + calloc. Clear all newly allocated members. */ + oldbytes = oldnmemb * size; + if (size && oldbytes / size != oldnmemb) + { + xfree (p); + gpg_err_set_errno (ENOMEM); + return NULL; + } + memset (p + oldbytes, 0, bytes - oldbytes); + } + return p; +} diff --git a/common/xreadline.c b/common/xreadline.c new file mode 100644 index 0000000..b17579f --- /dev/null +++ b/common/xreadline.c @@ -0,0 +1,127 @@ +/* xreadline.c - fgets replacement function + * Copyright (C) 1999, 2004 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> + +#include "util.h" + + +/* Same as fgets() but if the provided buffer is too short a larger + one will be allocated. This is similar to getline. A line is + considered a byte stream ending in a LF. + + If MAX_LENGTH is not NULL, it shall point to a value with the + maximum allowed allocation. + + Returns the length of the line. EOF is indicated by a line of + length zero. A truncated line is indicated by setting the value at + MAX_LENGTH to 0. If the returned value is less then 0 not enough + memory was enable and ERRNO is set accordingly. + + If a line has been truncated, the file pointer is moved forward to + the end of the line so that the next read starts with the next + line. Note that MAX_LENGTH must be re-initialzied in this case. + + Note: The returned buffer is allocated with enough extra space to + append a CR,LF,Nul + */ +ssize_t +read_line (FILE *fp, + char **addr_of_buffer, size_t *length_of_buffer, + size_t *max_length) +{ + int c; + char *buffer = *addr_of_buffer; + size_t length = *length_of_buffer; + size_t nbytes = 0; + size_t maxlen = max_length? *max_length : 0; + char *p; + + if (!buffer) + { /* No buffer given - allocate a new one. */ + length = 256; + buffer = xtrymalloc (length); + *addr_of_buffer = buffer; + if (!buffer) + { + *length_of_buffer = 0; + if (max_length) + *max_length = 0; + return -1; + } + *length_of_buffer = length; + } + + length -= 3; /* Reserve 3 bytes for CR,LF,EOL. */ + p = buffer; + while ((c = getc (fp)) != EOF) + { + if (nbytes == length) + { /* Enlarge the buffer. */ + if (maxlen && length > maxlen) /* But not beyond our limit. */ + { + /* Skip the rest of the line. */ + while (c != '\n' && (c=getc (fp)) != EOF) + ; + *p++ = '\n'; /* Always append a LF (we reserved some space). */ + nbytes++; + if (max_length) + *max_length = 0; /* Indicate truncation. */ + break; /* the while loop. */ + } + length += 3; /* Adjust for the reserved bytes. */ + length += length < 1024? 256 : 1024; + *addr_of_buffer = xtryrealloc (buffer, length); + if (!*addr_of_buffer) + { + int save_errno = errno; + xfree (buffer); + *length_of_buffer = 0; + if (max_length) + *max_length = 0; + gpg_err_set_errno (save_errno); + return -1; + } + buffer = *addr_of_buffer; + *length_of_buffer = length; + length -= 3; + p = buffer + nbytes; + } + *p++ = c; + nbytes++; + if (c == '\n') + break; + } + *p = 0; /* Make sure the line is a string. */ + + return nbytes; +} diff --git a/common/yesno.c b/common/yesno.c new file mode 100644 index 0000000..ebe8d82 --- /dev/null +++ b/common/yesno.c @@ -0,0 +1,150 @@ +/* yesno.c - Yes/No questions + * Copyright (C) 1998, 1999, 2000, 2001, 2003 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdlib.h> +#include <errno.h> + +#include "i18n.h" +#include "util.h" + + +/* Check the string S for a YES or NO answer and take care of + localization. If no valid string is given the value of DEF_ANSWER + is returned. Returns 1 for yes and 0 for no. */ +int +answer_is_yes_no_default (const char *s, int def_answer) +{ + /* TRANSLATORS: See doc/TRANSLATE about this string. */ + const char *long_yes = _("yes"); + const char *short_yes = _("yY"); + /* TRANSLATORS: See doc/TRANSLATE about this string. */ + const char *long_no = _("no"); + const char *short_no = _("nN"); + + /* Note: we have to use the local dependent compare here. */ + if ( match_multistr(long_yes,s) ) + return 1; + if ( *s && strchr( short_yes, *s ) && !s[1] ) + return 1; + /* Test for "no" strings to catch ambiguities for the next test. */ + if ( match_multistr(long_no,s) ) + return 0; + if ( *s && strchr( short_no, *s ) && !s[1] ) + return 0; + /* Test for the english version (for those who are used to type yes). */ + if ( !ascii_strcasecmp(s, "yes" ) ) + return 1; + if ( *s && strchr( "yY", *s ) && !s[1] ) + return 1; + return def_answer; +} + +int +answer_is_yes ( const char *s ) +{ + return answer_is_yes_no_default(s,0); +} + +/**************** + * Return 1 for yes, -1 for quit, or 0 for no + */ +int +answer_is_yes_no_quit ( const char *s ) +{ + /* TRANSLATORS: See doc/TRANSLATE about this string. */ + const char *long_yes = _("yes"); + /* TRANSLATORS: See doc/TRANSLATE about this string. */ + const char *long_no = _("no"); + /* TRANSLATORS: See doc/TRANSLATE about this string. */ + const char *long_quit = _("quit"); + const char *short_yes = _("yY"); + const char *short_no = _("nN"); + const char *short_quit = _("qQ"); + + /* Note: we have to use a local dependent compare here. */ + if ( match_multistr(long_no,s) ) + return 0; + if ( match_multistr(long_yes,s) ) + return 1; + if ( match_multistr(long_quit,s) ) + return -1; + if ( *s && strchr( short_no, *s ) && !s[1] ) + return 0; + if ( *s && strchr( short_yes, *s ) && !s[1] ) + return 1; + if ( *s && strchr( short_quit, *s ) && !s[1] ) + return -1; + /* but not here. */ + if ( !ascii_strcasecmp(s, "yes" ) ) + return 1; + if ( !ascii_strcasecmp(s, "quit" ) ) + return -1; + if ( *s && strchr( "yY", *s ) && !s[1] ) + return 1; + if ( *s && strchr( "qQ", *s ) && !s[1] ) + return -1; + return 0; +} + +/* + Return 1 for okay, 0 for cancel or DEF_ANSWER for default. + */ +int +answer_is_okay_cancel (const char *s, int def_answer) +{ + /* TRANSLATORS: See doc/TRANSLATE about this string. */ + const char *long_okay = _("okay|okay"); + /* TRANSLATORS: See doc/TRANSLATE about this string. */ + const char *long_cancel = _("cancel|cancel"); + const char *short_okay = _("oO"); + const char *short_cancel = _("cC"); + + /* Note: We have to use the locale dependent compare. */ + if ( match_multistr(long_okay,s) ) + return 1; + if ( match_multistr(long_cancel,s) ) + return 0; + if ( *s && strchr( short_okay, *s ) && !s[1] ) + return 1; + if ( *s && strchr( short_cancel, *s ) && !s[1] ) + return 0; + /* Always test for the English values (not locale here). */ + if ( !ascii_strcasecmp(s, "okay" ) ) + return 1; + if ( !ascii_strcasecmp(s, "ok" ) ) + return 1; + if ( !ascii_strcasecmp(s, "cancel" ) ) + return 0; + if ( *s && strchr( "oO", *s ) && !s[1] ) + return 1; + if ( *s && strchr( "cC", *s ) && !s[1] ) + return 0; + return def_answer; +} diff --git a/common/zb32.c b/common/zb32.c new file mode 100644 index 0000000..517321e --- /dev/null +++ b/common/zb32.c @@ -0,0 +1,120 @@ +/* zb32.c - z-base-32 functions + * Copyright (C) 2014 Werner Koch + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <assert.h> + +#include "util.h" +#include "zb32.h" + +/* Zooko's base32 variant. See RFC-6189 and + http://philzimmermann.com/docs/human-oriented-base-32-encoding.txt + Caller must xfree the returned string. Returns NULL and sets ERRNO + on error. To avoid integer overflow DATALEN is limited to 2^16 + bytes. Note, that DATABITS is measured in bits!. */ +char * +zb32_encode (const void *data, unsigned int databits) +{ + static char const zb32asc[32] = {'y','b','n','d','r','f','g','8', + 'e','j','k','m','c','p','q','x', + 'o','t','1','u','w','i','s','z', + 'a','3','4','5','h','7','6','9' }; + const unsigned char *s; + char *output, *d; + size_t datalen; + + datalen = (databits + 7) / 8; + if (datalen > (1 << 16)) + { + errno = EINVAL; + return NULL; + } + + d = output = xtrymalloc (8 * (datalen / 5) + + 2 * (datalen % 5) + - ((datalen%5)>2) + + 1); + if (!output) + return NULL; + + /* I use straightforward code. The compiler should be able to do a + better job on optimization than me and it is easier to read. */ + for (s = data; datalen >= 5; s += 5, datalen -= 5) + { + *d++ = zb32asc[((s[0] ) >> 3) ]; + *d++ = zb32asc[((s[0] & 7) << 2) | (s[1] >> 6) ]; + *d++ = zb32asc[((s[1] & 63) >> 1) ]; + *d++ = zb32asc[((s[1] & 1) << 4) | (s[2] >> 4) ]; + *d++ = zb32asc[((s[2] & 15) << 1) | (s[3] >> 7) ]; + *d++ = zb32asc[((s[3] & 127) >> 2) ]; + *d++ = zb32asc[((s[3] & 3) << 3) | (s[4] >> 5) ]; + *d++ = zb32asc[((s[4] & 31) ) ]; + } + + switch (datalen) + { + case 4: + *d++ = zb32asc[((s[0] ) >> 3) ]; + *d++ = zb32asc[((s[0] & 7) << 2) | (s[1] >> 6) ]; + *d++ = zb32asc[((s[1] & 63) >> 1) ]; + *d++ = zb32asc[((s[1] & 1) << 4) | (s[2] >> 4) ]; + *d++ = zb32asc[((s[2] & 15) << 1) | (s[3] >> 7) ]; + *d++ = zb32asc[((s[3] & 127) >> 2) ]; + *d++ = zb32asc[((s[3] & 3) << 3) ]; + break; + case 3: + *d++ = zb32asc[((s[0] ) >> 3) ]; + *d++ = zb32asc[((s[0] & 7) << 2) | (s[1] >> 6) ]; + *d++ = zb32asc[((s[1] & 63) >> 1) ]; + *d++ = zb32asc[((s[1] & 1) << 4) | (s[2] >> 4) ]; + *d++ = zb32asc[((s[2] & 15) << 1) ]; + break; + case 2: + *d++ = zb32asc[((s[0] ) >> 3) ]; + *d++ = zb32asc[((s[0] & 7) << 2) | (s[1] >> 6) ]; + *d++ = zb32asc[((s[1] & 63) >> 1) ]; + *d++ = zb32asc[((s[1] & 1) << 4) ]; + break; + case 1: + *d++ = zb32asc[((s[0] ) >> 3) ]; + *d++ = zb32asc[((s[0] & 7) << 2) ]; + break; + default: + break; + } + *d = 0; + + /* Need to strip some bytes if not a multiple of 40. */ + output[(databits + 5 - 1) / 5] = 0; + return output; +} diff --git a/common/zb32.h b/common/zb32.h new file mode 100644 index 0000000..47bb1f8 --- /dev/null +++ b/common/zb32.h @@ -0,0 +1,38 @@ +/* zb32.h - z-base-32 functions + * Copyright (C) 2014 Werner Koch + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * - the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * or + * + * - the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * or both in parallel, as here. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef GNUPG_COMMON_ZB32_H +#define GNUPG_COMMON_ZB32_H + +/* Encode DATA which has a length of DATABITS (bits!) using the + zbase32 encoder and return a malloced string. Returns NULL on + error and sets ERRNO. */ +char *zb32_encode (const void *data, unsigned int databits); + +#endif /*GNUPG_COMMON_ZB32_H*/ |