diff --git a/NEWS b/NEWS index f68035d2..8f9127e0 100644 --- a/NEWS +++ b/NEWS @@ -1,1447 +1,1460 @@ +Noteworthy changes in version 1.5.0 (unreleased) +------------------------------------------------ + + * On Unices the engine file names are not not anymore hardwired but + located via the envvar PATH. All configuration options to set the + name of the engines for configure run are removed. + + * If GPGME find the gpgconf binary it defaults to using gpg2 or + whatever gpgconf tells as name for the OpenPGP engine. If gpgconf + is not found, GPGME looks for an engine named "gpg". + + + Noteworthy changes in version 1.4.3 (2013-08-12) ------------------------------------------------ * The default engine names are now taken from the output of gpgconf. If gpgconf is not found the use of gpg 1 is assumed. * Under Windows the default engines names are first searched in the installation directory of the gpgme DLL. * New function gpgme_data_identify to detect the type of a message. * Interface changes relative to the 1.4.2 release: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ gpgme_signers_count NEW. gpgme_data_type_t NEW. gpgme_data_identify NEW. Noteworthy changes in version 1.4.2 (2013-05-28) ------------------------------------------------ * Allow symmetric encryption with gpgme_op_encrypt_sign. * Fixed mismatching off_t definitions on Windows. * Interface changes relative to the 1.4.1 release: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ gpgme_off_t NEW. gpgme_size_t NEW. GPGME_PROTOCOL_OPENPGP NEW alias. Noteworthy changes in version 1.4.1 (2013-05-01) ------------------------------------------------ * Fixed reading of gpg.conf files with excessive use of the group option. * Fixed building with the i686-w64-mingw32 toolchain. * Disabled FD passing by default for Apple. Noteworthy changes in version 1.4.0 (2013-02-26) ------------------------------------------------ * New function gpgme_set_global_flag to help debugging on Android. * New function gpgme_io_writen as a convenience wrapper around gpgme_io_write. * New functions to support the pinentry mode feature of GnuPG 2.1. * New macro GPGME_VERSION_NUMBER to allow supporting different API versions without the need for a configure test. * Several improvements for gpgme-tool. * Better logging of the common "invalid engine" error code. * Support for FD passing is now enabled by default. The configure option --disable-fd-passing may be used to disable this. * Interface changes relative to the 1.3.1 release: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ GPGME_VERSION_NUMBER NEW. gpgme_io_writen NEW. gpgme_set_global_flag NEW. gpgme_set_pinentry_mode NEW. gpgme_get_pinentry_mode NEW. gpgme_pinentry_mode_t NEW. GPGME_PINENTRY_MODE_DEFAULT NEW. GPGME_PINENTRY_MODE_ASK NEW. GPGME_PINENTRY_MODE_CANCEL NEW. GPGME_PINENTRY_MODE_ERROR NEW. GPGME_PINENTRY_MODE_LOOPBACK NEW. Noteworthy changes in version 1.3.2 (2012-05-02) ------------------------------------------------ * Remove support for libgpgme-pth. As far as we know, this was never used, and GnuPG is going to use our own npth in the future. * Fix signature summary information for a missing X.509 key. * Fix parsing of dates >= year 2038. Noteworthy changes in version 1.3.1 (2011-06-16) ------------------------------------------------ * Ported to Windows CE. * Detect GPG versions not supporting ---passwd. * Interface changes relative to the 1.3.0 release: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ GPGME_EXPORT_MODE_MINIMAL NEW GPGME_STATUS_SUCCESS NEW gpgme_err_code_from_syserror NEW gpgme_err_set_errno NEW gpgme_error_from_errno CHANGED: Return gpgme_error_t (compatible type). gpgme_error_from_syserror NEW ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Noteworthy changes in version 1.3.0 (2010-01-11) ------------------------------------------------ * GPGME does not come with an internal libassuan version anymore. The external libassuan 1.1.0 release or later is required. For application programmers on systems that can resolve inter-library dependencies at runtime, this is a transparent change. * New engine GPGME_PROTOCOL_G13 to support the new g13 tool. * New engine GPGME_PROTOCOL_UISERVER to support UI Servers. * New API to change the passphrase of a key. * Interface changes relative to the 1.2.0 release: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ GPGME_STATUS_INV_SGNR NEW. GPGME_STATUS_NO_SGNR NEW. GPGME_PROTOCOL_G13 NEW. gpgme_op_g13_mount NEW. gpgme_g13_result_t NEW. GPGME_PK_ECDSA NEW. GPGME_PK_ECDH NEW. gpgme_op_passwd_start NEW. gpgme_op_passwd NEW. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Noteworthy changes in version 1.2.0 (2009-06-18) ------------------------------------------------ * New encryption flag GPGME_ENCRYPT_NO_ENCRYPT_TO to disable default recipients. * gpgme_new will fail if gpgme_check_version was not called, or a selftest failed (for example, if -mms-bitfields was not used on MingW32 targets). * New functions gpgme_io_read and gpgme_io_write for use with gpgme_passphrase_cb_t and gpgme_edit_cb_t functions. * New functions gpgme_result_ref and gpgme_result_unref to detach result structures from a context. * New functions gpgme_op_export_keys_start and gpgme_op_export_keys that allow to specify exported keys through gpgme_key_t objects instead of patterns. * New mode of operation gpgme_export_mode_t that allows exporting external keys. * Interface changes relative to the 1.1.7 release: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ GPGME_KEYLIST_MODE_EPHEMERAL NEW. GPGME_PROTOCOL_ASSUAN NEW. gpgme_assuan_data_cb_t NEW. gpgme_assuan_inquire_cb_t NEW. gpgme_assuan_status_cb_t NEW. gpgme_op_assuan_transact_start NEW. gpgme_op_assuan_transact NEW. gpgme_op_assuan_result NEW. gpgme_op_import_keys NEW. gpgme_op_import_keys_start NEW. gpgme_subkey_t EXTENDED: New fields is_cardkey, card_number. GPGME_ENCRYPT_NO_ENCRYPT_TO NEW. gpgme_check_version CHANGED: Is now a macro. gpgme_new EXTENDED: More failure codes. gpgme_io_read NEW. gpgme_io_write NEW. gpgme_result_ref NEW. gpgme_result_unref NEW. gpgme_export_mode_t NEW. gpgme_export_ext_start EXTENDED: Arg RESERVED is now a MODE flag. gpgme_op_export EXTENDED: Arg RESERVED is now a MODE flag. gpgme_op_export_ext_start EXTENDED: Arg RESERVED is now a MODE flag. gpgme_op_export_ext EXTENDED: Arg RESERVED is now a MODE flag. gpgme_op_export_keys_start NEW. gpgme_op_export_keys NEW. GPGME_DATA_ENCODING_URL NEW. GPGME_DATA_ENCODING_URL0 NEW. GPGME_DATA_ENCODING_URLESC NEW. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Noteworthy changes in version 1.1.8 (2008-12-08) ------------------------------------------------ * SIGPIPE is now again ignored as described in the manual. Fixes regresion introduced with 1.1.6. Noteworthy changes in version 1.1.7 (2008-10-17) ------------------------------------------------ * Using GPGME_KEYLIST_MODE_LOCAL combined with GPGME_KEYLIST_MODE_EXTERN is now supported; it uses the --locate-keys feature of gpg (>= 2.0.10). * The encoding of gpgme_data_t objects can affect the output encoding of export, sign and encrypt operations now (the same operations that are also affected by the ASCII mode switch). We believe this change in the ABI is innocent enough not to break existing applications (it only affects the S/MIME backend on certain operations). * The reference manual now includes the specification of "The GnuPG UI Server protocol". * A new function gpgme_cancel_async can be used to asynchronously cancel any pending operation at any time, from any thread. * Interface changes relative to the 1.1.6 release: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ gpgme_op_encrypt CHANGED: Output encoding can affect result. gpgme_op_encrypt_start CHANGED: Output encoding can affect result. gpgme_op_encrypt_sign CHANGED: Output encoding can affect result. gpgme_op_encrypt_sign_start CHANGED: Output encoding can affect result. gpgme_op_sign CHANGED: Output encoding can affect result. gpgme_op_sign_start CHANGED: Output encoding can affect result. gpgme_op_export CHANGED: Output encoding can affect result. gpgme_op_export_start CHANGED: Output encoding can affect result. gpgme_op_export_ext CHANGED: Output encoding can affect result. gpgme_op_export_ext_start CHANGED: Output encoding can affect result. gpgme_cancel_async NEW ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Noteworthy changes in version 1.1.6 (2008-01-04) ------------------------------------------------ * Bug fixes for for W32. * A new, experimental (and thus undocumented and potentially unstable) interface for accessing gpg-conf through GPGME has been added. * Interface changes relative to the 1.1.1 release: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ gpgme_signature_t EXTENDED: New field chain_model. gpgme_op_getauditlog_start NEW. gpgme_op_getauditlog NEW. GPGME_AUDITLOG_HTML NEW. GPGME_AUDITLOG_WITH_HELP NEW. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Noteworthy changes in version 1.1.5 (2007-07-09) ------------------------------------------------ * Bug and portability fixes (mainly for W32). Noteworthy changes in version 1.1.4 (2007-03-05) ------------------------------------------------ * Detect and bail out on double plaintext messages. This is required so that applications can properly detect the signed parts of a message. Actual there is now a double protection as GnuPG 1.4.7 will detect this case too. Noteworthy changes in version 1.1.3 (2007-01-29) ------------------------------------------------ * Fixed a memory leak in gpgme_data_release_and_get_mem. * Fixed a bug in Windows command line quoting. Noteworthy changes in version 1.1.2 (2006-03-02) ------------------------------------------------ * Fixed a bug in the W32 glib backend. Noteworthy changes in version 1.1.1 (2006-02-23) ------------------------------------------------ * Fixed a bug in that the fingerprints of subkeys are not available. * Clarified usage of the SECRET flag in key listings. It is now reset for stub keys. * Reading signature notations and policy URLs on key signatures is supported. They can be found in the new field notations of the gpgme_key_sig_t structure. This has to be enabled with the keylist mode flag GPGME_KEYLIST_MODE_SIG_NOTATIONS. * A new gpgme_free() function solves the problem of using different allocators in a single program. This function should now be used instead calling free() to release the buffer returned by gpgme_data_release_and_get_mem. It is recommended that you always do this, but it is only necessary on certain platforms, so backwards compatibility is provided. In other words: If free() worked for you before, it will keep working. * New status codes GPGME_PKA_TRUST_GOOD and GPGME_PKA_TRUST_BAD. They are analyzed by the verify handlers and made available in the new PKA_TRUST and PKA_ADDRESS fields of the signature result structure. * Interface changes relative to the 1.1.0 release: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ gpgme_key_sig_t EXTENDED: New field notations. GPGME_KEYLIST_MODE_SIG_NOTATIONS NEW gpgme_free NEW GPGME_STATUS_PKA_TRUST_BAD NEW GPGME_STATUS_PKA_TRUST_GOOD NEW gpgme_signature_t EXTENDED: New field pka_trust. gpgme_signature_t EXTENDED: New field pka_address. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Noteworthy changes in version 1.1.0 (2005-10-01) ------------------------------------------------ * You can now configure the backend engine file name and home directory to be used, as default and per context. * Information about the recipients of an encrypted text is now available at decryption time. * New status GPGME_STATUS_PLAINTEXT. This is analyzed by the decrypt and verify handlers, the information about the plaintext filename, if available is made available in the new field file_name of the respective result structure. * The code for "automagically detecting the thread library" has been removed from libgpgme. It is deprecated since version 0.4.3. Since then, you had to link against libgpgme-pthread for applications using pthread and libgpgme-pth for applications using GNU Pth. The code was removed because it caused compilation problems on systems where the pthread.h header from GNU Pth is available in addition to the system header (FreeBSD 6 and later for example). * "./autogen.sh --build-w32" does now build gpgme.dll. * [W32] The environment variable GPGME_DEBUG now uses a semicolon as delimiter. The standard install directory is used when locating gpg or gpgsm before finally falling back to the hardwired name. * There is a new flag for keys and subkeys, is_qualified, which indicates if a key can be used for qualified signatures according to local government regulations. * You can associate a filename with a data object using the new function gpgme_data_set_file_name(). This filename will be stored in the output when encrypting or signing the data and will be returned when decrypting or verifying the output data. * You can now set notation data at signature creation with the new function gpgme_sig_notation_add(). * Interface changes relative to the 1.0.3 release: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ gpgme_set_engine_info NEW gpgme_ctx_get_engine_info NEW gpgme_ctx_set_engine_info NEW gpgme_recipient_t NEW gpgme_decrypt_result_t EXTENDED: New field recipients. gpgme_verify_result_t EXTENDED: New fields pubkey_algo, hash_algo. gpgme_decrypt_result_t EXTENDED: New field plaintext_filename. gpgme_verify_result_t EXTENDED: New field plaintext_filename. GPGME_STATUS_PLAINTEXT NEW gpgme_key_t EXTENDED: New field is_qualified. gpgme_subkey_t EXTENDED: New field is_qualified. gpgme_data_get_file_name NEW gpgme_data_set_file_name NEW gpgme_sig_notation_flags_t NEW GPGME_SIG_NOTATION_HUMAN_READABLE NEW GPGME_SIG_NOTATAION_CRITICAL NEW gpgme_sig_notation_clear NEW gpgme_sig_notation_add NEW gpgme_sig_notation_get NEW ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Noteworthy changes in version 1.0.3 (2005-06-20) ------------------------------------------------ * Previousy, GPGME would use a default "include certs" of 1. This has been changed. Now GPGME will use the crypto backend engines default unless you set the value with gpgme_set_include_certs() explicitely. A new macro GPGME_INCLUDE_CERTS_DEFAULT can be used as a value to explicitely request the new default behaviour. Because the default changes, this is a slight change of the API semantics. We consider it to be a bug fix. * A bug which made GPGME hang has been fixed. If you have experienced hanging before, please try out this version and let me know if you still experience hanging problems. * Interface changes relative to the 0.9.0 release: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ gpgme_set_include_certs CHANGED DEFAULT GPGME_INCLUDE_CERTS_DEFAULT NEW GPGME_STATUS_SIG_SUBPACKET NEW GPGME_STATUS_NEED_PASSPHRASE_PIN NEW GPGME_STATUS_SC_OP_FAILURE NEW GPGME_STATUS_SC_OP_SUCCESS NEW GPGME_STATUS_CARDCTRL NEW GPGME_STATUS_BACKUP_KEY_CREATED NEW ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Noteworthy changes in version 1.0.2 (2004-12-28) ------------------------------------------------ * Changed the license of the library to the GNU Lesser General Public License (LGPL), version 2.1 or later. Noteworthy changes in version 1.0.1 (2004-10-22) ------------------------------------------------ * Only bug fixes. Noteworthy changes in version 1.0.0 (2004-09-30) ------------------------------------------------ * Version 1.0.0! We are proud to present you with a thoroughly tested and stable version of the GPGME library. A big Thank You! to all the people who made this possible. The development will be branched into a stable 1.x.y series and the head. * The gpgme.m4 macro supports checking the API version. Just prepend it to the required version string, separated by a colon. For example, this release has the version "1:1.0.0". The last release to which this version is (mostly) ABI compatible is "1:0.4.2", which is the default required version. Noteworthy changes in version 0.9.0 (2004-06-08) ------------------------------------------------ * The type gpgme_key_t has now a new field keylist_mode that contains the keylist mode that was active at the time the key was retrieved. * The type gpgme_decrypt_result_t has a new field "wrong_key_usage" that contains a flag indicating that the key should not have been used for encryption. * Verifying a signature of a revoked key gives the correct result now (GPG_ERR_CERT_REVOKED error code). * Clarified that the error code GPG_ERR_NO_DATA from the decrypt & verify operations still allows you to look at the signature verification result. * Clarified that patterns in keylisting operations have an upper limit, and thus are not suited to list many keys at once by their fingerprint. Also improve the error message if the pattern is too long for the CMS protocol to handle. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ gpgme_key_t EXTENDED: New field keylist_mode. gpgme_decrypt_result_t EXTENDED: New field wrong_key_usage. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Noteworthy changes in version 0.4.7 (2004-04-29) ------------------------------------------------ * Correctly initialize the fields expired, revoked, invalid, and disabled in the gpgme_key_t structures. * A bug fix: The flag wrong_key_usage of gpgme_signature_t was accidently of type int instead unsigned int. * Interface changes relative to the 0.4.5 release: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ gpgme_signature_t CHANGED: wrong_key_usage is unsigned int now. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Noteworthy changes in version 0.4.6 (2004-04-06) ------------------------------------------------ * Bug fixes Noteworthy changes in version 0.4.5 (2004-03-07) ------------------------------------------------ * GPGME is now compiled with LFS (large file support) by default. This means that _all_ programs using GPGME must be compiled with LFS support enabled by default. You can do this easily with autoconf, by using the AC_SYS_LARGEFILE macro. Or you can do this without autoconf by defining the preprocessor symbol _FILE_OFFSET_BITS to 64 (by passing the -D_FILE_OFFSET_BITS=64 to the C compiler command line, or by defining this preprocessor symbol before including any system header files). For more details, read the section on LFS in the manual. Up to now, it was undocumented that GPGME was not using LFS. But the public interfaces use off_t, and file descriptors are exchanged between the application and GPGME. This was an oversight, and bound to cause troubles in the future. Writing GPGME as a dual mode library that seamlessly supports LFS while keeping backwards compatibility is possible, but does not solve the problem: Many applications already expect GPGME to have LFS (they are compiled with off_t being a 64bit value). This is true in particular for the popular Gtk+ and Qt programs. So, although this is an ABI (but not an API) break, we will not change the library version to reflect that. Because the interfaces affected are probably not used yet in any GPGME 0.4 based application, we don't expect any real failures from this change. In fact, applications already using LFS will have some subtle bugs fixed. However, if you encounter an application using GPGME 0.4.x that does _not_ use LFS by default (off_t is a 32bit value), _and_ uses at least one of the functions gpgme_data_seek, gpgme_data_new_from_filepart, or a gpgme_data_seek_cb_t with gpgme_data_new_from_cbs, then indeed this library will be ABI incompatible with the program. As said above, we don't believe such a program exists. If we are in error, then you have two options: As a quick hack, you can configure GPGME with the --disable-largefile option. This will revert the change, and GPGME will not use LFS. However, GPGME will be incompatible with programs that expect GPGME to use LFS. All applications are required to use LFS when using GPGME, so this is only good as a temporary local work-around. The other option is to change the versioning of the library and recompile all applications. We have reserved a special version of the library for that, so you can do that without expecting a version clash in the future. Furthermore, everyone who does this will agree on the version to use (this is important for distribution makers). Read the comment in configure.ac (before LIBGPGME_LT_AGE) if you want to do this. Please don't do this blindly: As stated above, we think it is unlikely this measure is needed. Still, it is there if necessary. If in doubt, contact us and we will give our advise for your specific situation. * New key listing mode GPGME_KEYLIST_MODE_VALIDATE for validation of the listed keys. * New interface gpgme_cancel() that can be used to cancel asynchronous operations. * Interface changes relative to the 0.4.4 release: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ gpgme_data_seek_cb_t CHANGED: off_t is now a largefile type. gpgme_data_seek CHANGED: off_t is now a largefile type. gpgme_data_new_from_filepart CHANGED: off_t is now a largefile type. GPGME_KEYLIST_MODE_VALIDATE NEW gpgme_cancel NEW ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Noteworthy changes in version 0.4.4 (2004-01-12) ------------------------------------------------ * The member "class" in gpgme_key_sig_t and gpgme_new_signature_t has been renamed to "sig_class", to avoid clash with C++ compilers. In the C API, the old name "class" has been preserved for backwards compatibility, but is deprecated. * Interface changes relative to the 0.4.3 release: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ gpgme_key_sig_t CHANGED: class deprecated, use new sig_class. gpgme_new_signature_t CHANGED: class deprecated, use new sig_class. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Noteworthy changes in version 0.4.3 (2003-10-06) ------------------------------------------------ * libgpgme should not be used for threaded programs anymore. This never worked reliably in all cases, because you had to be careful about the linking order and libtool wouldn't do that for you automatically. Instead, now you have to link against libgpgme-pthread for applications using pthread and libgpgme-pth for applications using GNU Pth. The old code for automagically detecting the thread library is still part of libgpgme, but it is DEPRECATED. * There are new automake macros AM_PATH_GPGME_PTH and AM_PATH_GPGME_PTHREAD, which support checking for thread-enabled versions of GPGME. They define GPGME_PTH_CFLAGS, GPGME_PTH_LIBS, GPGME_PTHREAD_CFLAGS and GPGME_PTHREAD_LIBS respectively. These variables of course also include the configuration for the thread package itself. Alternatively, use libtool. * gpgme_strerror_r as a thread safe variant of gpgme_strerror was added. * gpgme-config doesn't support setting the prefix or exec prefix anymore. I don't think it ever worked correctly, and it seems to be pointless. * gpgme_get_key fails with GPG_ERR_AMBIGUOUS_NAME if the key ID provided was not unique, instead returning the first matching key. * gpgme_key_t and gpgme_subkey_t have a new field, can_authenticate, that indicates if the key can be used for authentication. * gpgme_signature_t's status field is now correctly set to an error with error code GPG_ERR_NO_PUBKEY if public key is not found. * gpgme_new_signature_t's class field is now an unsigned int, rather than an unsigned long (the old class field is preserved for backwards compatibility). * A new function gpgme_set_locale() is provided to allow configuring the locale for the crypto backend. This is necessary for text terminals so that programs like the pinentry can be started with the right locale settings for the terminal the application is running on, in case the terminal has different settings than the system default (for example, if it is a remote terminal). You are highly recommended to call the following functions directly after gpgme_check_version: #include setlocale (LC_ALL, ""); gpgme_set_locale (NULL, LC_CTYPE, setlocale (LC_CTYPE, NULL)); gpgme_set_locale (NULL, LC_MESSAGES, setlocale (LC_MESSAGES, NULL)); GPGME can not do this for you, as setlocale is not thread safe, and there is no alternative. * The signal action for SIGPIPE is now set to SIG_IGN by gpgme_check_version, instead the first time a crypto engine is started (which is not well defined). * In the output of gpgme_hash_algo_name, change RMD160 to RIPEMD160, TIGER to TIGER192, CRC32-RFC1510 to CRC32RFC1510, and CRC24-RFC2440 to CRC24RFC2440. For now, these strings can be used as the MIC parameter for PGP/MIME (if appropriately modified). * Interface changes relative to the 0.4.2 release: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ gpgme_strerror_t NEW gpgme_get_key CHANGED: Fails correctly if key ID not unique. gpgme_key_t EXTENDED: New field can_authenticate. gpgme_subkey_t EXTENDED: New field can_authenticate. gpgme_new_signature_t CHANGED: New type for class field. gpgme_set_locale NEW gpgme_hash_algo_name CHANGED: Slight adjustment of algo names. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Noteworthy changes in version 0.4.2 (2003-07-30) ------------------------------------------------ * Allow gpg-error to be in non-standard place when linking the test suite. * Configure will fail now if gpg-error can not be found. * Fixed initialized memory backed data objects for writing, which caused the test program to crash (but only on Mac OS, surprisingly). * Eliminate use of C99 constructs. * Small improvements to the manual. Noteworthy changes in version 0.4.1 (2003-06-06) ------------------------------------------------ This is the release that 0.4.0 should have been. There are many interface changes, please see below for the details. The changes are sometimes the result of new functionality, but more often express a paradigm shift. Others are an overdue cleanup to get GPGME in line with the GNU coding standards and to make the interface more self-consistent. Here is an overview on the changes: All types have been renamed to conform to the GNU coding standards, most of the time by keeping the whole name in lowercase and inserting underscores between words. All operations consistently only accept input parameters in their invocation function, and return only an error code directly. Further information about the result of the operation has to be retrieved afterwards by calling one of the result functions. This unifies the synchronous and the asynchronous interface. The error values have been completely replaced by a more sophisticated model that allows GPGME to transparently and accurately report all errors from the other GnuPG components, irregardless of process boundaries. This is achieved by using the library libgpg-errors, which is shared by all GnuPG components. This library is now required for GPGME. The results of all operations are now provided by pointers to C structs rather than by XML structs or in other ways. Objects which used to be opaque (for example a key) are now pointers to accessible structs, so no accessor functions are necessary. Backward compatibility is provided where it was possible without too much effort and did not collide with the overall sanitization effort. However, this is only for ease of transition. NO DEPRECATED FUNCTION OR DATA TYPE IS CONSIDERED A PART OF THE API OR ABI AND WILL BE DROPPED IN THE FUTURE WITHOUT CHANGING THE SONAME OF THE LIBRARY. Recommendations how to replace deprecated or removed functionality can be found within the description of each change. What follows are all changes to the interface and behaviour of GPGME in detail. * If gpgme.h is included in sources compiled by GCC 3.1 or later, deprecated attributes will warn about use of obsolete functions and type definitions. You can suppress these warnings by passing -Wno-deprecated-declarations to the gcc command. * The following types have been renamed. The old types are still available as aliases, but they are deprecated now: Old name: New name: GpgmeCtx gpgme_ctx_t GpgmeData gpgme_data_t GpgmeError gpgme_error_t GpgmeDataEncoding gpgme_data_encoding_t GpgmeSigStat gpgme_sig_stat_t GpgmeSigMode gpgme_sig_mode_t GpgmeAttr gpgme_attr_t GpgmeValidity gpgme_validity_t GpgmeProtocol gpgme_protocol_t GpgmeKey gpgme_key_t GpgmePassphraseCb gpgme_passphrase_cb_t GpgmeProgressCb gpgme_progress_cb_t GpgmeIOCb gpgme_io_cb_t GpgmeRegisterIOCb gpgme_register_io_cb_t GpgmeRemoveIOCb gpgme_remove_io_cb_t GpgmeEventIO gpgme_event_io_t GpgmeEventIOCb gpgme_event_io_cb_t GpgmeIOCbs gpgme_io_cbs GpgmeDataReadCb gpgme_data_read_cb_t GpgmeDataWriteCb gpgme_data_write_cb_t GpgmeDataSeekCb gpgme_data_seek_cb_t GpgmeDataReleaseCb gpgme_data_release_cb_t GpgmeDataCbs gpgme_data_cbs_t GpgmeTrustItem gpgme_trust_item_t GpgmeStatusCode gpgme_status_code_t * gpgme_error_t is now identical to gpg_error_t, the error type provided by libgpg-error. More about using libgpg-error with GPGME can be found in the manual. All error symbols have been removed! * All functions and types in libgpg-error have been wrapped in GPGME. The new types are gpgme_err_code_t and gpgme_err_source_t. The new functions are gpgme_err_code, gpgme_err_source, gpgme_error, gpgme_err_make, gpgme_error_from_errno, gpgme_err_make_from_errno, gpgme_err_code_from_errno, gpgme_err_code_to_errno, gpgme_strsource. * GPGME_ATTR_IS_SECRET is not anymore representable as a string. * GnuPG 1.2.2 is required. The progress callback is now also invoked for encrypt, sign, encrypt-sign, decrypt, verify, and decrypt-verify operations. For verify operations on detached signatures, the progress callback is invoked for both the detached signature and the plaintext message, though. * gpgme_passphrase_cb_t has been changed to not provide a complete description, but the UID hint, passphrase info and a flag indicating if this is a repeated attempt individually, so the user can compose his own description from this information. The passphrase is not returned as a C string, but must be written to a file descriptor directly. This allows for secure passphrase entries. The return type has been changed to gpgme_error_t value. This allowed to remove the gpgme_cancel function; just return the error code GPG_ERR_CANCELED in the passphrase callback directly. * gpgme_edit_cb_t has been changed to take a file descriptor argument. The user is expected to write the response to the file descriptor, followed by a newline. * The recipients interface has been removed. Instead, you use NULL-terminated lists of keys for specifying the recipients of an encryption operation. Use the new encryption flag GPGME_ENCRYPT_ALWAYS_TRUST if you want to override the validity of the keys (but note that in general this is not a good idea). This change has been made to the prototypes of gpgme_op_encrypt, gpgme_op_encrypt_start, gpgme_op_encrypt_sign and gpgme_op_encrypt_sign_start. The export interface has been changed to use pattern strings like the keylist interface. Thus, new functions gpgme_op_export_ext and gpgme_op_export_ext_start have been added as well. Now the prototypes of gpgme_op_export_start and gpgme_op_export finally make sense. * gpgme_op_verify and gpgme_op_decrypt_verify don't return a status summary anymore. Use gpgme_get_sig_status to retrieve the individual stati. * gpgme_io_cb_t changed from a void function to a function returning a gpgme_error_t value. However, it will always return 0, so you can safely ignore the return value. * A new I/O callback event GPGME_EVENT_START has been added. The new requirement is that you must wait until this event until you are allowed to call the I/O callback handlers previously registered for this context operation. Calling I/O callback functions for this context operation before the start event happened is unsafe because it can lead to race conditions in a multi-threaded environment. * The idle function feature has been removed. It was not precisely defined in a multi-threaded environment and is obsoleted by the user I/O callback functions. If you still need a simple way to call something while waiting on one or multiple asynchronous operations to complete, don't set the HANG flag in gpgme_wait (note that this will return to your program more often than the idle function did). * gpgme_wait can return NULL even if hang is true, if an error occurs. In that case *status contains the error code. * gpgme_get_engine_info was radically changed. Instead an XML string, an info structure of the new type gpgme_engine_info_t is returned. This makes it easier and more robust to evaluate the information in an application. * The new function gpgme_get_protocol_name can be used to convert a gpgme_protocol_t value into a string. * The status of a context operation is not checked anymore. Starting a new operation will silently cancel the previous one. Calling a function that requires you to have started an operation before without doing so is undefined. * The FPR argument to gpgme_op_genkey was removed. Instead, use the gpgme_op_genkey_result function to retrieve a gpgme_genkey_result_t pointer to a structure which contains the fingerprint. This also works with gpgme_op_genkey_start. The structure also provides other information about the generated keys. So, instead: char *fpr; err = gpgme_op_genkey (ctx, NULL, NULL, &fpr); if (!err && fpr) printf ("%s\n", fpr); you should now do: gpgme_genkey_result_t result; err = gpgme_op_genkey (ctx, NULL, NULL); if (!err) { result = gpgme_op_genkey_result (ctx); if (result->fpr) printf ("%s\n", result->fpr); } * The new gpgme_op_import_result function provides detailed information about the result of an import operation in gpgme_import_result_t and gpgme_import_status_t objects. Thus, the gpgme_op_import_ext variant is deprecated. * The new gpgme_op_sign_result function provides detailed information about the result of a signing operation in gpgme_sign_result_t, gpgme_invalid_key_t and gpgme_new_signature_t objects. * The new gpgme_op_encrypt_result function provides detailed information about the result of an encryption operation in a GpgmeEncryptResult object. * The new gpgme_op_decrypt_result function provides detailed information about the result of a decryption operation in a GpgmeDecryptResult object. * The new gpgme_op_verify_result function provides detailed information about the result of an verify operation in a GpgmeVerifyResult object. Because of this, the GPGME_SIG_STAT_* values, gpgme_get_sig_status, gpgme_get_sig_ulong_attr, gpgme_get_sig_string_attr and gpgme_get_sig_key are now deprecated, and gpgme_get_notation is removed. * GpgmeTrustItem objects have now directly accessible data, so the gpgme_trust_item_get_string_attr and gpgme_trust_item_get_ulong_attr accessor functions are deprecated. Also, reference counting is available through gpgme_trust_item_ref and gpgme_trust_item_unref (the gpgme_trust_item_release alias for the latter is deprecated). * Keys are not cached internally anymore, so the force_update argument to gpgme_get_key has been removed. * GpgmeKey objects have now directly accessible data so the gpgme_key_get_string_attr, gpgme_key_get_ulong_attr, gpgme_key_sig_get_string_attr and gpgme_key_sig_get_ulong_attr functions are deprecated. Also, gpgme_key_release is now deprecated. The gpgme_key_get_as_xml function has been dropped. * Because all interfaces using attributes are deprecated, the GpgmeAttr data type is also deprecated. * The new gpgme_op_keylist_result function provides detailed information about the result of a key listing operation in a GpgmeKeyListResult object. * Now that each function comes with its own result retrieval interface, the generic gpgme_get_op_info interface is not useful anymore and dropped. * The type and mode of data objects is not available anymore. * Interface changes relative to the 0.4.0 release: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ GpgmeCtx DEPRECATED: Use gpgme_ctx_t. GpgmeData DEPRECATED: Use gpgme_data_t. GpgmeError DEPRECATED: Use gpgme_error_t. GpgmeDataEncoding DEPRECATED: Use gpgme_data_encoding_t. GpgmeSigStat DEPRECATED: Use gpgme_sig_stat_t. GpgmeSigMode DEPRECATED: Use gpgme_sig_mode_t. GpgmeAttr DEPRECATED: Use gpgme_attr_t. GpgmeValidity DEPRECATED: Use gpgme_validity_t. GpgmeProtocol DEPRECATED: Use gpgme_protocol_t. GpgmeKey DEPRECATED: Use gpgme_key_t. GpgmePassphraseCb DEPRECATED: Use gpgme_passphrase_cb_t. GpgmeProgressCb DEPRECATED: Use gpgme_progress_cb_t. GpgmeIOCb DEPRECATED: Use gpgme_io_cb_t. GpgmeRegisterIOCb DEPRECATED: Use gpgme_register_io_cb_t. GpgmeRemoveIOCb DEPRECATED: Use gpgme_remove_io_cb_t. GpgmeEventIO DEPRECATED: Use gpgme_event_io_t. GpgmeEventIOCb DEPRECATED: Use gpgme_event_io_cb_t. GpgmeIOCbs DEPRECATED: Use gpgme_io_cbs. GpgmeDataReadCb DEPRECATED: Use gpgme_data_read_cb_t. GpgmeDataWriteCb DEPRECATED: Use gpgme_data_write_cb_t. GpgmeDataSeekCb DEPRECATED: Use gpgme_data_seek_cb_t. GpgmeDataReleaseCb DEPRECATED: Use gpgme_data_release_cb_t. GpgmeDataCbs DEPRECATED: Use gpgme_data_cbs_t. GpgmeTrustItem DEPRECATED: Use gpgme_trust_item_t. GpgmeStatusCode DEPRECATED: Use gpgme_status_code_t. gpgme_ctx_t NEW gpgme_data_t NEW gpgme_recipients_t NEW gpgme_error_t NEW gpgme_data_encoding_t NEW gpgme_sig_stat_t NEW gpgme_sig_mode_t NEW gpgme_attr_t NEW gpgme_validity_t NEW gpgme_protocol_t NEW gpgme_key_t NEW gpgme_passphrase_cb_t NEW gpgme_progress_cb_t NEW gpgme_io_cb_t NEW gpgme_register_io_cb_t NEW gpgme_remove_io_cb_t NEW gpgme_event_io_t NEW gpgme_event_io_cb_t NEW gpgme_io_cbs NEW gpgme_data_read_cb_t NEW gpgme_data_write_cb_t NEW gpgme_data_seek_cb_t NEW gpgme_data_release_cb_t NEW gpgme_data_cbs_t NEW gpgme_trust_item_t NEW gpgme_status_code_t NEW GPGME_{some error code} REMOVED! Use GPG_ERR_* from libgpg-error. gpgme_err_code_t NEW gpgme_err_source_t NEW gpgme_err_code NEW gpgme_err_source NEW gpgme_error NEW gpgme_err_make NEW gpgme_error_from_errno NEW gpgme_err_make_from_errno NEW gpgme_err_code_from_errno NEW gpgme_err_code_to_errno NEW gpgme_strsource NEW gpgme_io_cb_t CHANGED: Return type from void to GpgmeError. gpgme_event_io_t CHANGED: New event type (all numbers changed). gpgme_passphrase_cb_t CHANGED: Desc decomposed, write directly to FD. gpgme_edit_cb_t CHANGED: Write directly to FD. gpgme_key_get_string_attr CHANGED: Don't handle GPGME_ATTR_IS_SECRET. gpgme_op_verify CHANGED: Drop R_STAT argument. gpgme_op_decrypt_verify CHANGED: Drop R_STAT argument. gpgme_wait CHANGED: Can return NULL even if hang is true. GpgmeIdleFunc REMOVED gpgme_register_idle REMOVED GpgmeRecipients REMOVED gpgme_recipients_new REMOVED gpgme_recipients_release REMOVED gpgme_recipients_add_name REMOVED gpgme_recipients_add_name_with_validity REMOVED gpgme_recipients_count REMOVED gpgme_recipients_enum_open REMOVED gpgme_recipients_enum_read REMOVED gpgme_recipients_enum_close REMOVED gpgme_encrypt_flags_t NEW GPGME_ENCRYPT_ALWAYS_TRUST NEW gpgme_op_encrypt CHANGED: Recipients passed as gpgme_key_t[]. gpgme_op_encrypt_start CHANGED: Recipients passed as gpgme_key_t[]. gpgme_op_encrypt_sign CHANGED: Recipients passed as gpgme_key_t[]. gpgme_op_encrypt_sign_start CHANGED: Recipients passed as gpgme_key_t[]. gpgme_op_export_start CHANGED: User IDs passed as patterns. gpgme_op_export CHANGED: User IDs passed as patterns. gpgme_op_export_ext_start NEW gpgme_op_export_ext NEW gpgme_keylist_mode_t NEW gpgme_sigsum_t NEW gpgme_engine_info_t NEW gpgme_get_engine_info CHANGED: Return info structure instead XML. gpgme_get_protocol_name NEW gpgme_cancel REMOVED: Return error in callback directly. gpgme_op_genkey CHANGED: FPR argument dropped. gpgme_op_genkey_result NEW gpgme_genkey_result_t NEW gpgme_op_import_ext DEPRECATED: Use gpgme_op_import_result. gpgme_op_import_result NEW gpgme_import_status_t NEW gpgme_import_result_t NEW gpgme_pubkey_algo_t NEW gpgme_hash_algo_t NEW gpgme_invalid_key_t NEW gpgme_new_signature_t NEW gpgme_sign_result_t NEW gpgme_op_sign_result NEW gpgme_pubkey_algo_name NEW gpgme_hash_algo_name NEW gpgme_encrypt_result_t NEW gpgme_op_encrypt_result NEW gpgme_decrypt_result_t NEW gpgme_op_decrypt_result NEW gpgme_verify_result_t NEW gpgme_op_verify_result NEW gpgme_get_notation REMOVED: Access verify result directly instead. gpgme_get_sig_key DEPRECATED: Use gpgme_get_key with fingerprint. gpgme_get_sig_ulong_attr DEPRECATED: Use verify result directly. gpgme_get_sig_string_attr DEPRECATED: Use verify result directly. GPGME_SIG_STAT_* DEPRECATED: Use error value in sig status. gpgme_get_sig_status DEPRECATED: Use verify result directly. gpgme_trust_item_t CHANGED: Now has user accessible data members. gpgme_trust_item_ref NEW gpgme_trust_item_unref NEW gpgme_trust_item_release DEPRECATED: Use gpgme_trust_item_unref. gpgme_trust_item_get_string_attr DEPRECATED gpgme_trust_item_get_ulong_attr DEPRECATED gpgme_get_key CHANGED: Removed force_update argument. gpgme_subkey_t NEW gpgme_key_sig_t NEW gpgme_user_id_t NEW gpgme_key_t CHANGED: Now has user accessible data members. gpgme_key_get_string_attr DEPRECATED gpgme_key_get_ulong_attr DEPRECATED gpgme_key_sig_get_string_attr DEPRECATED gpgme_key_sig_get_ulong_attr DEPRECATED gpgme_key_get_as_xml REMOVED gpgme_key_list_result_t NEW gpgme_op_keylist_result NEW gpgme_get_op_info REMOVED ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Noteworthy changes in version 0.4.0 (2002-12-23) ------------------------------------------------ * Key generation returns the fingerprint of the generated key. * New convenience function gpgme_get_key. * Supports signatures of user IDs in keys via the new GPGME_KEYLIST_MODE_SIGS keylist mode and the gpgme_key_sig_get_string_attr and gpgme_key_sig_get_ulong_attr interfaces. The XML info about a key also includes the signatures if available. * New data object interface, which is more flexible and transparent. * Interface changes relative to the 0.3.9 release: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ GpgmeDataReadCb NEW GpgmeDataWriteCb NEW GpgmeDataSeekCb NEW GpgmeDataReleaseCb NEW GpgmeDataCbs NEW gpgme_data_read CHANGED: Match read() closely. gpgme_data_write CHANGED: Match write() closely. gpgme_data_seek NEW gpgme_data_new_from_fd NEW gpgme_data_new_from_stream NEW gpgme_data_new_from_cbs NEW gpgme_data_rewind DEPRECATED: Replaced by gpgme_data_seek(). gpgme_data_new_from_read_cb DEPRECATED: Replaced by gpgme_data_from_cbs(). gpgme_data_get_type REMOVED: No replacement. gpgme_op_verify CHANGED: Take different data objects for signed text and plain text. gpgme_op_verify_start CHANGED: See gpgme_op_verify. gpgme_check_engine REMOVED: Deprecated since 0.3.0. gpgme_op_genkey CHANGED: New parameter FPR. GPGME_KEYLIST_MODE_SIGS NEW gpgme_key_sig_get_string_attr NEW gpgme_key_sig_get_ulong_attr NEW gpgme_get_key NEW GPGME_ATTR_SIG_CLASS NEW ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Noteworthy changes in version 0.3.16 (2003-11-19) ------------------------------------------------- * Compatibility fixes for GnuPG 1.9.x Noteworthy changes in version 0.3.15 (2003-02-18) ------------------------------------------------- * The progress status is sent via the progress callbacks in gpgme_op_edit. * Bug fix for signing operations with explicit signer settings for the CMS protocol. Noteworthy changes in version 0.3.14 (2002-12-04) ------------------------------------------------- * GPGME-Plug is now in its own package "cryptplug". * Workaround for a setlocale problem. Fixed a segv related to not correctly as closed marked file descriptors. Noteworthy changes in version 0.3.13 (2002-11-20) ------------------------------------------------- * Release due to changes in gpgmeplug. Noteworthy changes in version 0.3.12 (2002-10-15) ------------------------------------------------- * Fixed some bux with key listings. * The development has been branched to clean up some API issues. This 0.3 series will be kept for compatibility reasons; so do don't expect new features. Noteworthy changes in version 0.3.11 (2002-09-20) ------------------------------------------------- * Bug fixes. Noteworthy changes in version 0.3.10 (2002-09-02) ------------------------------------------------- * Setting the signing keys for the CMS protocol does now work. * The signers setting is honoured by gpgme_op_edit. Noteworthy changes in version 0.3.9 (2002-08-21) ------------------------------------------------ * A spec file for creating RPMs has been added. * An experimental interface to GnuPG's --edit-key functionality is introduced, see gpgme_op_edit. * The new gpgme_import_ext function provides a convenient access to the number of processed keys. * Interface changes relative to the 0.3.8 release: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ GpgmeStatusCode NEW GpgmeEditCb NEW gpgme_op_edit_start NEW gpgme_op_edit NEW gpgme_op_import_ext NEW ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Noteworthy changes in version 0.3.8 (2002-06-25) ------------------------------------------------ * It is possible to use an outside event loop for the I/O to the crypto engine by setting the I/O callbacks with gpgme_set_io_cbs. * Interface changes relative to the 0.3.6 release: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ GpgmeIOCb NEW GpgmeRegisterIOCb NEW GpgmeRemoveIOCb NEW GpgmeEventIO NEW GpgmeEventIOCb NEW struct GpgmeIOCbs NEW gpgme_set_io_cbs NEW gpgme_get_io_cbs NEW GPGME_ATTR_ERRTOK NEW ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Noteworthy changes in version 0.3.7 (2002-06-04) ------------------------------------------------ * GPGME_ATTR_OTRUST is implemented now. * A first step toward thread safeness has been achieved, see the documentation for details. Supported thread libraries are pthread and Pth. Noteworthy changes in version 0.3.6 (2002-05-03) ------------------------------------------------ * All error output of the gpgsm backend is send to the bit bucket. * The signature verification functions are extended. Instead of always returning GPGME_SIG_STATUS_GOOD, the functions new codes for expired signatures. 2 new functions may be used to retrieve more detailed information like the signature expiration time and a validity information of the key without an extra key looking. * The current passphrase callback and progress meter callback can be retrieved with the new functions gpgme_get_passphrase_cb and gpgme_get_progress_cb respectively. * Interface changes relative to the 0.3.5 release: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ gpgme_get_passphrase_cb NEW gpgme_get_progress_cb NEW GpgmeDataEncoding NEW gpgme_data_set_encoding NEW gpgme_data_get_encoding NEW GPGME_SIG_STAT_GOOD_EXP NEW GPGME_SIG_STAT_GOOD_EXPKEY NEW gpgme_op_verify CHANGED: Returns more status codes. GPGME_ATTR_SIG_STATUS NEW gpgme_get_sig_string_attr NEW gpgme_get_sig_ulong_attr NEW gpgme_get_protocol NEW ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Noteworthy changes in version 0.3.5 (2002-04-01) ------------------------------------------------ * gpgme_op_encrypt can be called with RECIPIENTS being 0. In this case, symmetric encryption is performed. Note that this requires a passphrase from the user. * More information is returned for X.509 certificates. * Interface changes relative to the 0.3.4 release: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ gpgme_op_encrypt EXTENDED: Symmetric encryption possible ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Noteworthy changes in version 0.3.4 (2002-03-04) ------------------------------------------------ * gpgme_op_encrypt does now fail with GPGME_Invalid_Recipients if some recipients have been invalid, whereas earlier versions succeeded in this case. The plaintext is still encrypted for all valid recipients, so the application might take this error as a hint that the ciphertext is not usable for all requested recipients. Information about invalid recipients is available with gpgme_get_op_info. * gpgme_op_verify now allows to pass an uninitialized data object as its plaintext argument to check for normal and cleartext signatures. The plaintext is then returned in the data object. * New interfaces gpgme_set_include_certs and gpgme_get_include_certs to set and get the number of certifications to include in S/MIME signed messages. * New interfaces gpgme_op_encrypt_sign and gpgme_op_encrypt_sign_start to encrypt and sign a message in a combined operation. * New interface gpgme_op_keylist_ext_start to search for multiple patterns. * gpgme_key_get_ulong_attr supports the GPGME_ATTR_EXPIRE attribute. * Interface changes relative to the 0.3.3 release: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ gpgme_op_encrypt CHANGED: Can fail with GPGME_Invalid_Recipients gpgme_op_verify EXTENDED: Accepts uninitialized text argument gpgme_key_get_ulong_attr EXTENDED: Supports GPGME_ATTR_EXPIRE gpgme_set_include_certs NEW gpgme_get_include_certs NEW gpgme_op_encrypt_sign NEW gpgme_op_encrypt_sign_start NEW gpgme_op_keylist_ext_start NEW ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Noteworthy changes in version 0.3.3 (2002-02-12) ------------------------------------------------ * Fix the Makefile in jnlib. * Fix the test suite (hopefully). It should clean up all its state with `make check' now. Noteworthy changes in version 0.3.2 (2002-02-10) ------------------------------------------------ * Remove erroneous dependency on libgcrypt in jnlib. Noteworthy changes in version 0.3.1 (2002-02-09) ------------------------------------------------ * There is a Texinfo manual documenting the API. * The gpgme_set_keylist_mode function returns an error, and changed its meaning. It is no longer usable to select between normal and fast mode (newer versions of GnuPG will always be fast), but selects between local keyring, remote keyserver, or both. For this, two new macros are defined, GPGME_KEYLIST_MODE_LOCAL and GPGME_KEYLIST_MODE_EXTERN. To make it possible to modify the current setting, a fucntion gpgme_get_keylist_mode was added to retrieve the current mode. * gpgme_wait accepts a new argument STATUS to return the error status of the operation on the context. Its definition is closer to waitpid() now than before. * The LENGTH argument to gpgme_data_new_from_filepart changed its type from off_t to the unsigned size_t. * The R_HD argument to the GpgmePassphraseCb type changed its type from void* to void**. * New interface gpgme_op_trustlist_end() to match gpgme_op_keylist_end(). * The CryptPlug modules have been renamed to gpgme-openpgp and gpgme-smime, and they are installed in pkglibdir by `make install'. * An idle function can be registered with gpgme_register_idle(). * The GpgSM backend supports key generation with gpgme_op_genkey(). * Interface changes relative to the 0.3.0 release: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ gpgme_data_new_from_filepart CHANGED: Type of LENGTH is size_t. GpgmePassphraseCb CHANGED: Type of R_HD is void **. gpgme_wait CHANGED: New argument STATUS. gpgme_set_keylist_mode CHANGED: Type of return value is GpgmeError. The function has a new meaning! gpgme_get_keylist_mode NEW GPGME_KEYLIST_MODE_LOCAL NEW GPGME_KEYLIST_MODE_EXTERN NEW gpgme_op_trustlist_next NEW GpgmeIdleFunc NEW gpgme_register_idle NEW ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Noteworthy changes in version 0.3.0 (2001-12-19) ------------------------------------------------ * New interface gpgme_set_protocol() to set the protocol and thus the crypto engine to be used by the context. Currently, the OpenPGP and the CMS protocols are supported. They are specified by the new preprocessor symbols GPGME_PROTOCOL_OpenPGP and GPGME_PROTOCOL_CMS. A new context uses the OpenPGP engine by default. * gpgme_get_engine_info() returns information for all crypto engines compiled into the library. The XML format has changed. To reliably get the version of a crypto engine, the tag after the appropriate tag has to be looked for. * New interface gpgme_engine_check_version(), obsoleting gpgme_check_engine(). Check the version of all engines you are supporting in your software. * GpgmeKey lists the user ids in the order as they are returned by GnuPG, first the primary key with index 0, then the sub-user ids. * New operation gpgme_op_decrypt_verify() to decrypt and verify signatures simultaneously. * The new interface gpgme_op_keylist_end() terminates a pending keylist operation. A keylist operation is also terminated when gpgme_op_keylist_next() returns GPGME_EOF. * GPGME can be compiled without GnuPG being installed (`--with-gpg=PATH'), cross-compiled, or even compiled without support for GnuPG (`--without-gpg'). * GPGME can be compiled with support for GpgSM (GnuPG for S/MIME, `--with-gpgsm=PATH'). It is enabled by default if the `gpgsm' is found in the path, but it can also be compiled without support for GpgSM (`--without-gpgsm'). * CryptPlug modules for GPGME are included and can be enabled at configure time (`--enable-gpgmeplug'). There is one module which uses the GnuPG engine (`gpgmeplug') and one module which uses the GpgSM engine (`gpgsmplug'). * Interface changes relative to the latest 0.2.x release: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ gpgme_key_get_as_xml CHANGED: Sub-user ids reversed in order. gpgme_key_get_string_attr CHANGED: User ids reversed in order. gpgme_key_get_ulong_attr CHANGED: User ids reversed in order. gpgme_get_engine_info CHANGED: New format, extended content. gpgme_engine_check_version NEW gpgme_decrypt_verify_start NEW gpgme_decrypt_verify NEW gpgme_op_keylist_next NEW gpgme_set_protocol NEW ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Noteworthy changes in version 0.2.3 (2001-09-17) ------------------------------------------------ * New function gpgme_get_op_info which can be used to get the micalg parameter needed for MOSS. * New functions gpgme_get_armor and gpgme_get_textmode. * The usual bug fixes and some minor functionality improvements. * Added a simple encryption component for MS-Windows; however the build procedure might have some problems. Noteworthy changes in version 0.2.2 (2001-06-12) ------------------------------------------------ * Implemented a key cache. * Fixed a race condition under W32 and some other bug fixes. Noteworthy changes in version 0.2.1 (2001-04-02) ------------------------------------------------ * Changed debug output and GPGME_DEBUG variable (gpgme/debug.c) * Handle GnuPG's new key capabilities output and support revocation et al. attributes * Made the W32 support more robust. Copyright 2001, 2002, 2003, 2004, 2005, 2007, 2008, 2009, 2010 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 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. diff --git a/README b/README index e8866479..3b39215f 100644 --- a/README +++ b/README @@ -1,120 +1,113 @@ GPGME - GnuPG Made Easy --------------------------- Copyright 2004, 2006, 2010, 2012, 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 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. Introduction -------------- GnuPG Made Easy (GPGME) is a C language library that allows to add support for cryptography to a program. It is designed to make access to public key crypto engines like GnuPG or GpgSM easier for applications. GPGME provides a high-level crypto API for encryption, decryption, signing, signature verification and key management. GPGME uses GnuPG and GpgSM as its backends to support OpenPGP and the Cryptographic Message Syntax (CMS). GPGME runs best on GNU/Linux or *BSD systems. Other Unices may require small portability fixes, please send us your patches. See the files COPYING, COPYING.LESSER, and each file for copyright and warranty information. The file AUTHORS has a list of authors and useful web and mail addresses. Installation -------------- See the file INSTALL for generic installation instructions. Check that you have unmodified sources. See below on how to do this. Don't skip it - this is an important step! -To build GPGME, you need to install libgpg-error (>= 1.8) and +To build GPGME, you need to install libgpg-error (>= 1.11) and Libassuan (>= 2.0.2). For support of the OpenPGP protocol (default), you should use the latest version of GnuPG (>= 1.4) , available at: ftp://ftp.gnupg.org/gcrypt/gnupg/ For support of the CMS (Cryptographic Message Syntax) protocol, you need a GnuPG version >= 2.0. -If configure can't find the `gpg' binary in your path, you can specify -the location with the --with-gpg=/path/to/gpg argument to configure. - -If configure can't find the `gpgsm' binary in your path, you can -specify the location with the --with-gpgsm=/path/to/gpgsm argument to -configure. - On some platforms GPGME based applications might hang at certain operations when using GPGME_PROTOCOL_CMS. A workaround for this problem is to build with the configure option --disable-fd-passing. For building the GIT version of GPGME please see the file README.GIT for more information. How to Verify the Source -------------------------- In order to check that the version of GPGME which you are going to install is an original and unmodified one, you can do it in one of the following ways: a) If you have a trusted Version of GnuPG installed, you can simply check the supplied signature: $ gpg --verify gpgme-x.y.z.tar.gz.sig This checks that the detached signature gpgme-x.y.z.tar.gz.sig is indeed a a signature of gpgme-x.y.z.tar.gz. The key used to create this signature is either of: "pub 2048R/4F25E3B6 2011-01-12 Werner Koch (dist sig)" "pub 1024D/87978569 1999-05-13 Marcus Brinkmann Marcus Brinkmann " If you do not have this key, you can get it from any keyserver. You have to make sure that this is really the key and not a faked one. You can do this by comparing the output of: $ gpg --fingerprint 0x4F25E3B6 with the fingerprint published elsewhere. b) If you don't have any of the above programs, you have to verify the SHA1 checksum: $ sha1sum gpgme-x.y.z.tar.gz This should yield an output _similar_ to this: fd9351b26b3189c1d577f0970f9dcadc3412def1 gpgme-x.y.z.tar.gz Now check that this checksum is _exactly_ the same as the one published via the announcement list and probably via Usenet. Documentation --------------- For information how to use the library you can read the info manual, which is also a reference book, in the doc/ directory. The programs in the tests/gpg/ directory may also prove useful. Please subscribe to the gnupg-devel@gnupg.org mailing list if you want to do serious work. For hacking on GPGME, please have a look at doc/HACKING. diff --git a/configure.ac b/configure.ac index 92375e6f..9575db28 100644 --- a/configure.ac +++ b/configure.ac @@ -1,1084 +1,626 @@ # configure.ac for GPGME # Copyright (C) 2000 Werner Koch (dd9jn) # Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, # 2009, 2010, 2011, 2012, 2013 g10 Code GmbH # # This file is part of GPGME. # # GPGME 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. # # GPGME 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 Lesser 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 . # (Process this file with autoconf to produce a configure script.) AC_PREREQ(2.59) min_automake_version="1.11" # To build a release you need to create a tag with the version number # (git tag -s gpgme-n.m.k) and run "./autogen.sh --force". Please # bump the version number immediately after the release and do another # commit and push so that the git magic is able to work. See below # for the LT versions. m4_define(mym4_version_major, [1]) m4_define(mym4_version_minor, [4]) m4_define(mym4_version_micro, [3]) # Below is m4 magic to extract and compute the revision number, the # decimalized short revision number, a beta version string, and a flag # indicating a development version (mym4_isgit). Note that the m4 # processing is done by autoconf and not during the configure run. m4_define(mym4_version, [mym4_version_major.mym4_version_minor.mym4_version_micro]) m4_define([mym4_revision], m4_esyscmd([git rev-parse --short HEAD | tr -d '\n\r'])) m4_define([mym4_revision_dec], m4_esyscmd_s([echo $((0x$(echo ]mym4_revision[|head -c 4)))])) m4_define([mym4_betastring], m4_esyscmd_s([git describe --match 'gpgme-[0-9].*[0-9]' --long|\ awk -F- '$3!=0{print"-beta"$3}'])) m4_define([mym4_isgit],m4_if(mym4_betastring,[],[no],[yes])) m4_define([mym4_full_version],[mym4_version[]mym4_betastring]) AC_INIT([gpgme],[mym4_full_version],[http://bugs.gnupg.org]) # LT Version numbers, remember to change them just *before* a release. # (Code changed: REVISION++) # (Interfaces added/removed/changed: CURRENT++, REVISION=0) # (Interfaces added: AGE++) # (Interfaces removed/changed: AGE=0) # LIBGPGME_LT_CURRENT=22 # Subtract 2 from this value if you want to make the LFS transition an # ABI break. [Note to self: Remove this comment with the next regular break.] LIBGPGME_LT_AGE=11 LIBGPGME_LT_REVISION=0 # If the API is changed in an incompatible way: increment the next counter. GPGME_CONFIG_API_VERSION=1 ############################################## -NEED_GPG_ERROR_VERSION=1.8 +NEED_GPG_ERROR_VERSION=1.11 NEED_LIBASSUAN_API=2 NEED_LIBASSUAN_VERSION=2.0.2 PACKAGE=$PACKAGE_NAME VERSION=$PACKAGE_VERSION AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_SRCDIR(src/gpgme.h.in) AC_CONFIG_HEADER(config.h) # Note: For automake 1.13 add the option # serial-tests AM_INIT_AUTOMAKE AM_MAINTAINER_MODE AC_CANONICAL_HOST # Enable GNU extensions on systems that have them. AC_GNU_SOURCE AH_VERBATIM([_REENTRANT], [/* To allow the use of GPGME in multithreaded programs we have to use special features from the library. IMPORTANT: gpgme is not yet fully reentrant and you should use it only from one thread. */ #ifndef _REENTRANT # define _REENTRANT 1 #endif]) AC_PROG_CC AC_PROG_CXX # Note: A suitable gitlog-to-changelog script can be found in GnuPG master. AC_CHECK_PROGS(GITLOG_TO_CHANGELOG, gitlog-to-changelog, [gitlog-to-changelog]) AC_SUBST(LIBGPGME_LT_CURRENT) AC_SUBST(LIBGPGME_LT_AGE) AC_SUBST(LIBGPGME_LT_REVISION) AC_SUBST(PACKAGE) AC_SUBST(VERSION) AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of this package]) AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version of this package]) VERSION_NUMBER=m4_esyscmd(printf "0x%02x%02x%02x" mym4_version_major \ mym4_version_minor mym4_version_micro) AC_SUBST(VERSION_NUMBER) # Don't default to build static libs. LT_PREREQ([2.2.6]) LT_INIT([win32-dll disable-static]) LT_LANG([Windows Resource]) # For now we hardcode the use of version scripts. It would be better # to write a test for this or even implement this within libtool. have_ld_version_script=no case "${host}" in *-*-linux*) have_ld_version_script=yes ;; *-*-gnu*) have_ld_version_script=yes ;; *-apple-darwin*) AC_DEFINE(_XOPEN_SOURCE, 500, Activate POSIX interface on MacOS X) ;; esac AM_CONDITIONAL(HAVE_LD_VERSION_SCRIPT, test "$have_ld_version_script" = "yes") GPG_DEFAULT=no GPGSM_DEFAULT=no GPGCONF_DEFAULT=no G13_DEFAULT=no component_system=None have_dosish_system=no have_android_system=no have_w32_system=no have_w64_system=no build_w32_glib=no build_w32_qt=no case "${host}" in x86_64-*mingw32*) have_w64_system=yes ;; *-mingw32ce*) have_w32ce_system=yes ;; *-linux-androideabi) have_android_system=yes ;; esac case "${host}" in *-mingw32ce*|*-mingw32*) have_dosish_system=yes have_w32_system=yes GPG_DEFAULT='c:\\gnupg\\gpg.exe' GPGSM_DEFAULT='c:\\gnupg\\gpgsm.exe' GPGCONF_DEFAULT='c:\\gnupg\\gpgconf.exe' G13_DEFAULT='c:\\gnupg\\g13.exe' #component_system='COM+' AM_PATH_GLIB_2_0 AC_ARG_ENABLE(w32-glib, AC_HELP_STRING([--enable-w32-glib], [build GPGME Glib for W32]), build_w32_glib=$enableval) # Check disabled, because the qt-dev packages in gpg4win do # not provide any support for cross compilation. # PKG_CHECK_MODULES(QT4_CORE, QtCore) # Use it like this: # ./configure --enable-w32-qt QT4_CORE_CFLAGS="..." QT4_CORE_LIBS="..." AC_SUBST(QT4_CORE_CFLAGS) AC_SUBST(QT4_CORE_LIBS) AC_ARG_ENABLE(w32-qt, AC_HELP_STRING([--enable-w32-qt], [build GPGME Qt for W32]), build_w32_qt=$enableval) ;; *) AC_CHECK_LIB(pthread,pthread_create,have_pthread=yes) if test "$have_pthread" = yes; then AC_DEFINE(HAVE_PTHREAD, ,[Define if we have pthread.]) fi # XXX: Probably use exec-prefix here? # GPG_DEFAULT='/usr/bin/gpg' # GPGSM_DEFAULT='/usr/bin/gpgsm' # GPGCONF_DEFAULT='/usr/bin/gpgconf' # G13_DEFAULT='/usr/bin/g13' ;; esac if test "$have_dosish_system" = yes; then AC_DEFINE(HAVE_DOSISH_SYSTEM,1, [Defined if we run on some of the PCDOS like systems (DOS, Windoze. OS/2) with special properties like no file modes]) fi AM_CONDITIONAL(HAVE_DOSISH_SYSTEM, test "$have_dosish_system" = yes) if test "$have_w32_system" = yes; then AC_DEFINE(HAVE_W32_SYSTEM,1, [Defined if we run on any kind of W32 API based system]) fi AM_CONDITIONAL(HAVE_W32_SYSTEM, test "$have_w32_system" = yes) if test "$have_w64_system" = yes; then AC_DEFINE(HAVE_W64_SYSTEM,1, [Defined if we run on a 64 bit W32 API based system]) fi AM_CONDITIONAL(HAVE_W64_SYSTEM, test "$have_w64_system" = yes) if test "$have_w32ce_system" = yes; then AC_DEFINE(HAVE_W32CE_SYSTEM,1, [Defined if we run on a W32 CE API based system]) fi AM_CONDITIONAL(HAVE_W32CE_SYSTEM, test "$have_w32ce_system" = yes) if test "$have_android_system" = yes; then AC_DEFINE(HAVE_ANDROID_SYSTEM,1, [Defined if we build for an Android system]) fi AM_CONDITIONAL(HAVE_ANDROID_SYSTEM, test "$have_android_system" = yes) AM_CONDITIONAL(BUILD_W32_GLIB, test "$build_w32_glib" = yes) AM_CONDITIONAL(BUILD_W32_QT, test "$build_w32_qt" = yes) AM_CONDITIONAL(HAVE_PTHREAD, test "$have_pthread" = "yes") # # Provide information about the build. # BUILD_REVISION="mym4_revision" AC_SUBST(BUILD_REVISION) AC_DEFINE_UNQUOTED(BUILD_REVISION, "$BUILD_REVISION", [GIT commit id revision used to build this package]) changequote(,)dnl BUILD_FILEVERSION=`echo "$PACKAGE_VERSION"|sed 's/\([0-9.]*\).*/\1./;s/\./,/g'` changequote([,])dnl BUILD_FILEVERSION="${BUILD_FILEVERSION}mym4_revision_dec" AC_SUBST(BUILD_FILEVERSION) BUILD_TIMESTAMP=`date -u +%Y-%m-%dT%H:%M+0000 2>/dev/null || date` AC_SUBST(BUILD_TIMESTAMP) AC_DEFINE_UNQUOTED(BUILD_TIMESTAMP, "$BUILD_TIMESTAMP", [The time this package was configured for a build]) +# +# Options to disable some regression tests +# +run_gpgconf_test="yes" +AC_ARG_ENABLE(gpgconf-test, + AC_HELP_STRING([--disable-gpgconf-test], [disable GPGCONF regression test]), + run_gpgconf_test=$enableval) +AM_CONDITIONAL(RUN_GPGCONF_TESTS, test "$run_gpgconf_test" = "yes") + +run_gpg_test="yes" +AC_ARG_ENABLE(gpg-test, + AC_HELP_STRING([--disable-gpg-test], [disable GPG regression test]), + run_gpg_test=$enableval) +AM_CONDITIONAL(RUN_GPG_TESTS, test "$run_gpg_test" = "yes") + +run_gpgsm_test="yes" +AC_ARG_ENABLE(gpgsm-test, + AC_HELP_STRING([--disable-gpgsm-test], [disable GPGSM regression test]), + run_gpgsm_test=$enableval) +AM_CONDITIONAL(RUN_GPGSM_TESTS, test "$run_gpgsm_test" = "yes") +run_g13_test="yes" +AC_ARG_ENABLE(g13-test, + AC_HELP_STRING([--disable-g13-test], [disable G13 regression test]), + run_g13_test=$enableval) +AM_CONDITIONAL(RUN_G13_TESTS, test "$run_g13_test" = "yes") # Checks for header files. AC_CHECK_HEADERS_ONCE([locale.h sys/select.h sys/uio.h argp.h unistd.h sys/time.h sys/types.h sys/stat.h]) # Type checks. AC_C_INLINE AC_CHECK_SIZEOF(unsigned int) AC_SYS_LARGEFILE AC_TYPE_OFF_T AC_TYPE_UINTPTR_T # A simple compile time check in gpgme.h for GNU/Linux systems that # prevents a file offset bits mismatch between gpgme and the application. NEED__FILE_OFFSET_BITS=0 if test "$have_w32_system" != yes; then case "$ac_cv_sys_file_offset_bits" in "" | no | unknown) ;; *) NEED__FILE_OFFSET_BITS=$ac_cv_sys_file_offset_bits ;; esac fi AC_SUBST(NEED__FILE_OFFSET_BITS) # Figure out platform dependent typedefs for gpgme.h if test "$have_w32_system" = yes; then INSERT__TYPEDEFS_FOR_GPGME_H=" #ifdef _WIN64 # include typedef int64_t gpgme_off_t; typedef int64_t gpgme_ssize_t; #else /* _WIN32 */ typedef long gpgme_off_t; typedef long gpgme_ssize_t; #endif /* _WIN32 */" API__OFF_T="gpgme_off_t" API__SSIZE_T="gpgme_ssize_t" else INSERT__TYPEDEFS_FOR_GPGME_H=" #include typedef off_t gpgme_off_t; typedef ssize_t gpgme_ssize_t;" API__OFF_T="off_t" API__SSIZE_T="ssize_t" fi AC_SUBST(INSERT__TYPEDEFS_FOR_GPGME_H) AM_SUBST_NOTMAKE(INSERT__TYPEDEFS_FOR_GPGME_H) AC_SUBST(API__OFF_T) AM_SUBST_NOTMAKE(API__OFF_T) AC_SUBST(API__SSIZE_T) AM_SUBST_NOTMAKE(API__SSIZE_T) # Checks for compiler features. if test "$GCC" = yes; then CFLAGS="$CFLAGS -Wall -Wcast-align -Wshadow -Wstrict-prototypes" if test "$have_w32_system" = yes; then CFLAGS="$CFLAGS -mms-bitfields" fi fi # Only used for debugging, so no serious test needed (for actual # functionality you have to test libc as well, this only tests the # compiler). AC_CACHE_CHECK([for __thread],[gpgme_cv_tls_works], AC_COMPILE_IFELSE([AC_LANG_PROGRAM([__thread int foo;])], gpgme_cv_tls_works=yes,gpgme_cv_tls_works=no)) if test "$gpgme_cv_tls_works" = yes; then AC_DEFINE(HAVE_TLS, [1], [Define if __thread is supported]) fi # Checks for library functions. AC_MSG_NOTICE([checking for libraries]) AC_FUNC_FSEEKO AC_REPLACE_FUNCS(vasprintf) if test "$ac_cv_func_vasprintf" != yes; then GNUPG_CHECK_VA_COPY fi # Try to find a thread-safe version of ttyname(). gnupg_REPLACE_TTYNAME_R if test "$ac_cv_func_ttyname_r" != yes; then AC_MSG_WARN([ *** *** ttyname() is not thread-safe and ttyname_r() does not exist ***]) fi # Try to find a thread-safe version of getenv(). have_thread_safe_getenv=no jm_GLIBC21 if test $GLIBC21 = yes; then have_thread_safe_getenv=yes fi if test $have_thread_safe_getenv = yes; then AC_DEFINE(HAVE_THREAD_SAFE_GETENV, [1], [Define if getenv() is thread-safe]) fi have_getenv_r=no AC_CHECK_FUNCS(getenv_r, have_getenv_r=yes) if test $have_getenv_r = no && test $have_thread_safe_getenv = no; then AC_MSG_WARN([ *** *** getenv() is not thread-safe and getenv_r() does not exist ***]) fi # For converting time strings to seconds since Epoch, we need the timegm # function. AC_CHECK_FUNCS(timegm) if test "$ac_cv_func_timegm" != yes; then AC_MSG_WARN([ *** *** timegm() not available - a non-thread-safe kludge will be used *** and the TZ variable might be changed at runtime. ***]) fi AC_CHECK_FUNCS(setlocale) # Checking for libgpg-error. have_gpg_error=no AM_PATH_GPG_ERROR("$NEED_GPG_ERROR_VERSION", have_gpg_error=yes, have_gpg_error=no) AC_DEFINE(GPG_ERR_SOURCE_DEFAULT, GPG_ERR_SOURCE_GPGME, [The default error source for GPGME.]) # And for libassuan. have_libassuan=no AM_PATH_LIBASSUAN("$NEED_LIBASSUAN_API:$NEED_LIBASSUAN_VERSION", have_libassuan=yes, have_libassuan=no) if test "$have_libassuan" = "yes"; then AC_DEFINE_UNQUOTED(GPGME_LIBASSUAN_VERSION, "$libassuan_version", [version of the libassuan library]) fi -AM_CONDITIONAL(HAVE_ASSUAN, test "$have_libassuan" = "yes") -if test "$have_libassuan" = "yes"; then - AC_DEFINE(ENABLE_ASSUAN,1,[Whether Assuan support is enabled]) -fi - -# Checks for system services -NEED_GPG_VERSION_DEFAULT=1.4.0 -NEED_GPGSM_VERSION_DEFAULT=1.9.6 -NEED_GPGCONF_VERSION_DEFAULT=2.0.4 -NEED_G13_VERSION_DEFAULT=2.1.0 -NEED_GPG_VERSION="$NEED_GPG_VERSION_DEFAULT" -NEED_GPGSM_VERSION="$NEED_GPGSM_VERSION_DEFAULT" -NEED_GPGCONF_VERSION="$NEED_GPGCONF_VERSION_DEFAULT" -NEED_G13_VERSION="$NEED_G13_VERSION_DEFAULT" -AC_ARG_WITH(gpg-version, - AC_HELP_STRING([--with-gpg-version=VER], [require GnuPG version VER]), - NEED_GPG_VERSION=$withval) -if test "$NEED_GPG_VERSION" = "yes"; then - NEED_GPG_VERSION="$NEED_GPG_VERSION_DEFAULT" -fi -if test "$NEED_GPG_VERSION" = "no"; then - NEED_GPG_VERSION=0.0.0 -fi -AC_ARG_WITH(gpgsm-version, - AC_HELP_STRING([--with-gpgsm-version=VER], [require GPGSM version VER]), - NEED_GPGSM_VERSION=$withval) -if test "$NEED_GPGSM_VERSION" = "yes"; then - NEED_GPGSM_VERSION="$NEED_GPGSM_VERSION_DEFAULT" -fi -if test "$NEED_GPGSM_VERSION" = "no"; then - NEED_GPGSM_VERSION=0.0.0 -fi -AC_ARG_WITH(gpgconf-version, - AC_HELP_STRING([--with-gpgconf-version=VER], [require GPGCONF version VER]), - NEED_GPGCONF_VERSION=$withval) -if test "$NEED_GPGCONF_VERSION" = "yes"; then - NEED_GPGCONF_VERSION="$NEED_GPGCONF_VERSION_DEFAULT" -fi -if test "$NEED_GPGCONF_VERSION" = "no"; then - NEED_GPGCONF_VERSION=0.0.0 -fi -AC_ARG_WITH(g13-version, - AC_HELP_STRING([--with-g13-version=VER], [require G13 version VER]), - NEED_G13_VERSION=$withval) -if test "$NEED_G13_VERSION" = "yes"; then - NEED_G13_VERSION="$NEED_G13_VERSION_DEFAULT" -fi -if test "$NEED_G13_VERSION" = "no"; then - NEED_G13_VERSION=0.0.0 -fi - -AC_DEFINE_UNQUOTED(NEED_GPGCONF_VERSION, "$NEED_GPGCONF_VERSION", - [Min. needed GPGCONF version.]) -AC_DEFINE_UNQUOTED(NEED_GPG_VERSION, "$NEED_GPG_VERSION", - [Min. needed GnuPG version.]) -AC_DEFINE_UNQUOTED(NEED_GPGSM_VERSION, "$NEED_GPGSM_VERSION", - [Min. needed GPGSM version.]) -AC_DEFINE_UNQUOTED(NEED_G13_VERSION, "$NEED_G13_VERSION", - [Min. needed G13 version.]) - -# -# Check for GPGCONF -# -gpgconf_usable_for_test=no -NO_OVERRIDE=no -AC_ARG_WITH(gpgconf, - AC_HELP_STRING([--with-gpgconf=PATH], - [use gpgconf binary at PATH]), - GPGCONF=$withval, NO_OVERRIDE=yes) -if test "$NO_OVERRIDE" = "yes" || test "$GPGCONF" = "yes"; then - GPGCONF= - NO_OVERRIDE=yes - if test "$cross_compiling" != "yes"; then - AC_PATH_PROG(GPGCONF, gpgconf) - fi - if test -z "$GPGCONF"; then - GPGCONF="$GPGCONF_DEFAULT" - fi -fi -if test "$GPGCONF" = no; then - if test "$NO_OVERRIDE" = "yes"; then - if test "$cross_compiling" != "yes"; then - AC_MSG_WARN([ -*** -*** Could not find gpgconf, install gpgconf or use --with-gpgconf=PATH to enable it -***]) - else - AC_MSG_ERROR([ -*** -*** Can not determine path to gpgconf when cross-compiling, use --with-gpgconf=PATH -***]) - fi - fi -else - AC_DEFINE_UNQUOTED(GPGCONF_PATH, "$GPGCONF", [Path to the GPGCONF binary.]) - AC_DEFINE(ENABLE_GPGCONF,1,[Whether GPGCONF support is enabled]) -fi -AM_CONDITIONAL(HAVE_GPGCONF, test "$GPGCONF" != "no") - -dnl Check for GPGCONF version requirement. -GPGCONF_VERSION=unknown -ok=maybe -if test -z "$GPGCONF" -o "x$GPGCONF" = "xno"; then - ok=no -else - if test "$cross_compiling" = "yes"; then - AC_MSG_WARN([GPGCONF version can not be checked when cross compiling]) - ok=no - else - if test ! -x "$GPGCONF"; then - AC_MSG_WARN([GPGCONF not executable, version check disabled]) - ok=no - fi - fi -fi -if test "$ok" = "maybe"; then - AC_MSG_CHECKING(for GPGCONF >= $NEED_GPGCONF_VERSION) - req_major=`echo $NEED_GPGCONF_VERSION | \ - sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\1/'` - req_minor=`echo $NEED_GPGCONF_VERSION | \ - sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\2/'` - req_micro=`echo $NEED_GPGCONF_VERSION | \ - sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\3/'` - GPGCONF_VERSION=`$GPGCONF --version | sed -n '1 s/[[^0-9]]*\(.*\)/\1/p'` - major=`echo $GPGCONF_VERSION | \ - sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*/\1/'` - minor=`echo $GPGCONF_VERSION | \ - sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*/\2/'` - micro=`echo $GPGCONF_VERSION | \ - sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*/\3/'` - - if test "$major" -gt "$req_major"; then - ok=yes - else - if test "$major" -eq "$req_major"; then - if test "$minor" -gt "$req_minor"; then - ok=yes - else - if test "$minor" -eq "$req_minor"; then - if test "$micro" -ge "$req_micro"; then - ok=yes - fi - fi - fi - fi - fi - if test "$ok" = "yes"; then - AC_MSG_RESULT(yes) - if test "$cross_compiling" != "yes"; then - gpgconf_usable_for_test=yes - fi - else - AC_MSG_RESULT(no) - AC_MSG_WARN([GPGCONF must be at least version $NEED_GPGCONF_VERSION]) - fi -fi -run_gpgconf_test="$ok" -AC_ARG_ENABLE(gpgconf-test, - AC_HELP_STRING([--disable-gpgconf-test], [disable GPGCONF run test]), - run_gpgconf_test=$enableval) -AM_CONDITIONAL(RUN_GPGCONF_TESTS, test "$run_gpgconf_test" = "yes") - - -# -# Check for GPG -# -NO_OVERRIDE=no -AC_ARG_WITH(gpg, - AC_HELP_STRING([--with-gpg=PATH], [use GnuPG binary at PATH]), - GPG=$withval, NO_OVERRIDE=yes) -if test "$NO_OVERRIDE" = "yes" || test "$GPG" = "yes"; then - GPG= - NO_OVERRIDE=yes - if test "$cross_compiling" != "yes"; then - if test "$gpgconf_usable_for_test" = "yes"; then - GPG="`$GPGCONF --list-components | grep ^gpg: | cut -d: -f 3`" - else - AC_PATH_PROG(GPG, gpg) - fi - fi - if test -z "$GPG"; then - GPG="$GPG_DEFAULT" - fi -fi -if test "$GPG" = no; then - if test "$NO_OVERRIDE" = "yes"; then - if test "$cross_compiling" != "yes"; then - AC_MSG_WARN([ -*** -*** Could not find GnuPG, install GnuPG or use --with-gpg=PATH to enable it -***]) - else - AC_MSG_ERROR([ -*** -*** Can not determine path to GnuPG when cross-compiling, use --with-gpg=PATH -***]) - fi - fi -else - AC_DEFINE_UNQUOTED(GPG_PATH, "$GPG", [Path to the GnuPG binary.]) - AC_SUBST(GPG) -fi -dnl Check for GnuPG version requirement. -GPG_VERSION=unknown -ok=maybe -if test -z "$GPG" -o "x$GPG" = "xno"; then - ok=no -else - if test "$cross_compiling" = "yes"; then - AC_MSG_WARN([GnuPG version can not be checked when cross compiling]) - ok=no - else - if test ! -x "$GPG"; then - AC_MSG_WARN([GnuPG not executable, version check disabled]) - ok=no - fi - fi -fi -if test "$ok" = "maybe"; then - AC_MSG_CHECKING(for GPG >= $NEED_GPG_VERSION) - req_major=`echo $NEED_GPG_VERSION | \ - sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\1/'` - req_minor=`echo $NEED_GPG_VERSION | \ - sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\2/'` - req_micro=`echo $NEED_GPG_VERSION | \ - sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\3/'` - GPG_VERSION=`$GPG --version | sed -n '1 s/[[^0-9]]*\(.*\)/\1/p'` - major=`echo $GPG_VERSION | \ - sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*/\1/'` - minor=`echo $GPG_VERSION | \ - sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*/\2/'` - micro=`echo $GPG_VERSION | \ - sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*/\3/'` - - if test "$major" -gt "$req_major"; then - ok=yes - else - if test "$major" -eq "$req_major"; then - if test "$minor" -gt "$req_minor"; then - ok=yes - else - if test "$minor" -eq "$req_minor"; then - if test "$micro" -ge "$req_micro"; then - ok=yes - fi - fi - fi - fi - fi - if test "$ok" = "yes"; then - AC_MSG_RESULT(yes) - else - AC_MSG_RESULT(no) - AC_MSG_WARN([GPG must be at least version $NEED_GPG_VERSION]) - fi -fi -run_gpg_test="$ok" -AC_ARG_ENABLE(gpg-test, - AC_HELP_STRING([--disable-gpg-test], [disable GPG run test]), - run_gpg_test=$enableval) -AM_CONDITIONAL(RUN_GPG_TESTS, test "$run_gpg_test" = "yes") -AC_SUBST(GPG_PATH) - - -# -# Check for GPGSM -# -NO_OVERRIDE=no -AC_ARG_WITH(gpgsm, - AC_HELP_STRING([--with-gpgsm=PATH], [use GpgSM binary at PATH]), - GPGSM=$withval, NO_OVERRIDE=yes) -if test "$NO_OVERRIDE" = "yes" || test "$GPGSM" = "yes"; then - GPGSM= - NO_OVERRIDE=yes - if test "$cross_compiling" != "yes"; then - if test "$gpgconf_usable_for_test" = "yes"; then - GPGSM="`$GPGCONF --list-components | grep ^gpgsm: | cut -d: -f 3`" - else - AC_PATH_PROG(GPGSM, gpgsm) - fi - fi - if test -z "$GPGSM"; then - GPGSM="$GPGSM_DEFAULT" - fi -fi -if test "$GPGSM" = no; then - if test "$NO_OVERRIDE" = "yes"; then - if test "$cross_compiling" != "yes"; then - AC_MSG_WARN([ -*** -*** Could not find GpgSM, install GpgSM or use --with-gpgsm=PATH to enable it -***]) - else - AC_MSG_ERROR([ -*** -*** Can not determine path to GpgSM when cross-compiling, use --with-gpgsm=PATH -***]) - fi - fi -else - AC_DEFINE_UNQUOTED(GPGSM_PATH, "$GPGSM", [Path to the GPGSM binary.]) - AC_DEFINE(ENABLE_GPGSM,1,[Whether GPGSM support is enabled]) -fi -AM_CONDITIONAL(HAVE_GPGSM, test "$GPGSM" != "no") - - -dnl Check for GPGSM version requirement. -GPGSM_VERSION=unknown -ok=maybe -if test -z "$GPGSM" -o "x$GPGSM" = "xno"; then - ok=no -else - if test "$cross_compiling" = "yes"; then - AC_MSG_WARN([GPGSM version can not be checked when cross compiling]) - ok=no - else - if test ! -x "$GPGSM"; then - AC_MSG_WARN([GPGSM not executable, version check disabled]) - ok=no - fi - fi -fi -if test "$ok" = "maybe"; then - AC_MSG_CHECKING(for GPGSM >= $NEED_GPGSM_VERSION) - req_major=`echo $NEED_GPGSM_VERSION | \ - sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\1/'` - req_minor=`echo $NEED_GPGSM_VERSION | \ - sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\2/'` - req_micro=`echo $NEED_GPGSM_VERSION | \ - sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\3/'` - GPGSM_VERSION=`$GPGSM --version | sed -n '1 s/[[^0-9]]*\(.*\)/\1/p'` - major=`echo $GPGSM_VERSION | \ - sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*/\1/'` - minor=`echo $GPGSM_VERSION | \ - sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*/\2/'` - micro=`echo $GPGSM_VERSION | \ - sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*/\3/'` - - if test "$major" -gt "$req_major"; then - ok=yes - else - if test "$major" -eq "$req_major"; then - if test "$minor" -gt "$req_minor"; then - ok=yes - else - if test "$minor" -eq "$req_minor"; then - if test "$micro" -ge "$req_micro"; then - ok=yes - fi - fi - fi - fi - fi - if test "$ok" = "yes"; then - AC_MSG_RESULT(yes) - else - AC_MSG_RESULT(no) - AC_MSG_WARN([GPGSM must be at least version $NEED_GPGSM_VERSION]) - fi -fi -run_gpgsm_test="$ok" -AC_ARG_ENABLE(gpgsm-test, - AC_HELP_STRING([--disable-gpgsm-test], [disable GPGSM run test]), - run_gpgsm_test=$enableval) -AM_CONDITIONAL(RUN_GPGSM_TESTS, test "$run_gpgsm_test" = "yes") - - -# -# Check for G13 -# -NO_OVERRIDE=no -AC_ARG_WITH(g13, - AC_HELP_STRING([--with-g13=PATH], - [use g13 binary at PATH]), - G13=$withval, NO_OVERRIDE=yes) -if test "$NO_OVERRIDE" = "yes" || test "$G13" = "yes"; then - G13= - NO_OVERRIDE=yes - if test "$cross_compiling" != "yes"; then - if test "$gpgconf_usable_for_test" = "yes"; then - G13="`$GPGCONF --list-components | grep ^g13: | cut -d: -f 3`" - if test -z "$G13"; then - # Use a hack if gpgconf has no support for g13. - G13="`$GPGCONF --list-dirs | grep ^bindir: | cut -d: -f 2`/g13" - fi - else - AC_PATH_PROG(G13, g13) - fi - fi - if test -z "$G13"; then - G13="$G13_DEFAULT" - fi -fi -if test "$G13" = no; then - if test "$NO_OVERRIDE" = "yes"; then - if test "$cross_compiling" != "yes"; then - AC_MSG_WARN([ -*** -*** Could not find g13, install g13 or use --with-g13=PATH to enable it -***]) - else - AC_MSG_ERROR([ -*** -*** Can not determine path to g13 when cross-compiling, use --with-g13=PATH -***]) - fi - fi -else - AC_DEFINE_UNQUOTED(G13_PATH, "$G13", [Path to the G13 binary.]) - AC_DEFINE(ENABLE_G13,1,[Whether G13 support is enabled]) -fi -AM_CONDITIONAL(HAVE_G13, test "$G13" != "no") - -dnl Check for G13 version requirement. -G13_VERSION=unknown -ok=maybe -if test -z "$G13" -o "x$G13" = "xno"; then - ok=no -else - if test "$cross_compiling" = "yes"; then - AC_MSG_WARN([G13 version can not be checked when cross compiling]) - ok=no - else - if test ! -x "$G13"; then - AC_MSG_WARN([G13 not executable, version check disabled]) - ok=no - fi - fi -fi -if test "$ok" = "maybe"; then - AC_MSG_CHECKING(for G13 >= $NEED_G13_VERSION) - req_major=`echo $NEED_G13_VERSION | \ - sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\1/'` - req_minor=`echo $NEED_G13_VERSION | \ - sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\2/'` - req_micro=`echo $NEED_G13_VERSION | \ - sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\3/'` - G13_VERSION=`$G13 --version | sed -n '1 s/.*\ \([[0-9]].*\)/\1/p'` - major=`echo $G13_VERSION | \ - sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*/\1/'` - minor=`echo $G13_VERSION | \ - sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*/\2/'` - micro=`echo $G13_VERSION | \ - sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*/\3/'` - - if test "$major" -gt "$req_major"; then - ok=yes - else - if test "$major" -eq "$req_major"; then - if test "$minor" -gt "$req_minor"; then - ok=yes - else - if test "$minor" -eq "$req_minor"; then - if test "$micro" -ge "$req_micro"; then - ok=yes - fi - fi - fi - fi - fi - if test "$ok" = "yes"; then - AC_MSG_RESULT(yes) - else - AC_MSG_RESULT(no) - AC_MSG_WARN([G13 must be at least version $NEED_G13_VERSION]) - fi -fi -run_g13_test="$ok" -AC_ARG_ENABLE(g13-test, - AC_HELP_STRING([--disable-g13-test], [disable G13 run test]), - run_g13_test=$enableval) -AM_CONDITIONAL(RUN_G13_TESTS, test "$run_g13_test" = "yes") # # Other checks # # Check for funopen AC_CHECK_FUNCS(funopen) if test $ac_cv_func_funopen != yes; then # No funopen but we can implement that in terms of fopencookie. AC_CHECK_FUNCS(fopencookie) if test $ac_cv_func_fopencookie = yes; then AC_REPLACE_FUNCS(funopen) else AC_MSG_WARN([ *** *** No implementation of fopencookie or funopen available ***]) fi fi # Check for getgid etc AC_CHECK_FUNCS(getgid getegid) # Replacement functions. AC_REPLACE_FUNCS(stpcpy) AC_REPLACE_FUNCS(setenv) # Assuan check for descriptor passing. AC_CHECK_MEMBER(struct cmsghdr.cmsg_len, [supports_descriptor_passing=yes], [supports_descriptor_passing=no AC_MSG_WARN([ *** *** Data structure for sending ancillary data missing. *** Descriptor passing won't work. ***])],[ #include #include #include #include #include #include #if HAVE_SYS_UIO_H #include #endif #include ]) dnl There seems to be a problem with Apple and decriptor passing. dnl Until we found a solution we change the default to no. dnl See bug 1483. case "${host}" in *-apple-darwin*) use_descriptor_passing=no ;; *) use_descriptor_passing=yes ;; esac AC_ARG_ENABLE(fd-passing, AC_HELP_STRING([--disable-fd-passing], [do not use FD passing]), use_descriptor_passing=$enableval) if test "$supports_descriptor_passing" != "yes"; then use_descriptor_passing=no fi if test "$use_descriptor_passing" = "yes"; then AC_DEFINE(USE_DESCRIPTOR_PASSING,1, [Defined if descriptor passing is enabled and supported]) fi AM_CONDITIONAL(USE_DESCRIPTOR_PASSING, test "$use_descriptor_passing" = "yes") uiserver=no if test "$use_descriptor_passing" = "yes" && test "$have_libassuan" = "yes"; then uiserver=yes fi if test "$uiserver" != "no"; then AC_DEFINE(ENABLE_UISERVER, 1, [Defined if we are building with uiserver support.]) fi AM_CONDITIONAL(HAVE_UISERVER, test "$uiserver" != "no") # Add a few constants to help porting to W32 AH_VERBATIM([SEPCONSTANTS], [ /* Separators as used in $PATH and file name. */ #ifdef HAVE_DOSISH_SYSTEM #define PATHSEP_C ';' #define DIRSEP_C '\\' #define DIRSEP_S "\\" #else #define PATHSEP_C ':' #define DIRSEP_C '/' #define DIRSEP_S "/" #endif ]) AH_BOTTOM([ /* Definition of GCC specific attributes. */ #if __GNUC__ > 2 # define GPGME_GCC_A_PURE __attribute__ ((__pure__)) #else # define GPGME_GCC_A_PURE #endif /* Under WindowsCE we need gpg-error's strerror macro. */ #define GPG_ERR_ENABLE_ERRNO_MACROS 1 #define CRIGHTBLURB "Copyright (C) 2000 Werner Koch\n" \ "Copyright (C) 2001--2013 g10 Code GmbH\n" ]) # Substitution used for gpgme-config GPGME_CONFIG_LIBS="-lgpgme" GPGME_CONFIG_CFLAGS="" GPGME_CONFIG_HOST="$host" AC_SUBST(GPGME_CONFIG_API_VERSION) AC_SUBST(GPGME_CONFIG_LIBS) AC_SUBST(GPGME_CONFIG_CFLAGS) AC_SUBST(GPGME_CONFIG_HOST) # Frob'da Variables LTLIBOBJS=`echo "$LIB@&t@OBJS" | sed 's,\.[[^.]]* ,.lo ,g;s,\.[[^.]]*$,.lo,'` AC_SUBST(LTLIBOBJS) # Some checks for gpgme-tool # Done at top: AC_CHECK_HEADER([argp.h]) AC_CHECK_TYPES([error_t], [], [AC_DEFINE([error_t], [int], [Define to a type to use for `error_t' if it is not otherwise available.])], [#include ]) # A substitution to set generated files in a Emacs buffer to read-only. AC_SUBST(emacs_local_vars_begin, [['Local][ ][Variables:']]) AC_SUBST(emacs_local_vars_read_only, ['buffer-read-only: t']) AC_SUBST(emacs_local_vars_end, ['End:']) # Last check. die=no if test "$have_gpg_error" = "no"; then die=yes AC_MSG_NOTICE([[ *** *** You need libgpg-error to build this program. ** This library is for example available at *** ftp://ftp.gnupg.org/gcrypt/libgpg-error *** (at least version $NEED_GPG_ERROR_VERSION is required.) ***]]) fi if test "$have_libassuan" = "no"; then die=yes AC_MSG_NOTICE([[ *** -*** You need libassuan to build this program with GPGSM support. +*** You need libassuan to build this program. *** This library is for example available at *** ftp://ftp.gnupg.org/gcrypt/libassuan/ *** (at least version $NEED_LIBASSUAN_VERSION (API $NEED_LIBASSUAN_API) is required). ***]]) fi if test "$die" = "yes"; then AC_MSG_ERROR([[ *** *** Required libraries not found. Please consult the above messages *** and install them before running configure again. ***]]) fi # # Create config files AC_CONFIG_FILES(Makefile src/Makefile tests/Makefile tests/gpg/Makefile tests/gpgsm/Makefile tests/opassuan/Makefile doc/Makefile src/versioninfo.rc src/gpgme.h) AC_CONFIG_FILES(src/gpgme-config, chmod +x src/gpgme-config) AC_CONFIG_FILES([lang/Makefile lang/cl/Makefile lang/cl/gpgme.asd]) AC_OUTPUT echo " GPGME v${VERSION} has been configured as follows: Revision: mym4_revision (mym4_revision_dec) Platform: $host - Gpgconf at: $GPGCONF - Gpgconf version: $GPGCONF_VERSION, min. $NEED_GPGCONF_VERSION - GPG at: $GPG - GPG version: $GPG_VERSION, min. $NEED_GPG_VERSION - Gpgsm at: $GPGSM - Gpgsm version: $GPGSM_VERSION, min. $NEED_GPGSM_VERSION - G13 at: $G13 - G13 version: $G13_VERSION, min. $NEED_G13_VERSION - - Assuan version: $libassuan_config_version, min. $NEED_LIBASSUAN_VERSION - UI Server: $uiserver FD Passing: $use_descriptor_passing GPGME Pthread: $have_pthread " diff --git a/src/Makefile.am b/src/Makefile.am index 1f951039..abc014c7 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,232 +1,208 @@ # Copyright (C) 2000 Werner Koch (dd9jn) # Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007 g10 Code GmbH # # This file is part of GPGME. # # GPGME 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. # # GPGME 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 Lesser 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 . ## Process this file with automake to produce Makefile.in # Note: moc_kdpipeiodevice should actually be a dependcy below. EXTRA_DIST = gpgme-config.in gpgme.m4 libgpgme.vers ChangeLog-2011 \ gpgme.h.in versioninfo.rc.in gpgme.def moc_kdpipeiodevice.cpp bin_SCRIPTS = gpgme-config m4datadir = $(datadir)/aclocal m4data_DATA = gpgme.m4 nodist_include_HEADERS = gpgme.h if HAVE_PTHREAD ltlib_gpgme_pthread = libgpgme-pthread.la else ltlib_gpgme_pthread = endif if BUILD_W32_GLIB ltlib_gpgme_glib = libgpgme-glib.la else ltlib_gpgme_glib = endif if BUILD_W32_QT ltlib_gpgme_qt = libgpgme-qt.la else ltlib_gpgme_qt = endif lib_LTLIBRARIES = libgpgme.la $(ltlib_gpgme_glib) $(ltlib_gpgme_qt) \ $(ltlib_gpgme_pthread) if HAVE_LD_VERSION_SCRIPT libgpgme_version_script_cmd = -Wl,--version-script=$(srcdir)/libgpgme.vers else libgpgme_version_script_cmd = endif if HAVE_DOSISH_SYSTEM system_components = w32-util.c w32-sema.c system_components_not_extra = w32-io.c else system_components = ath.h posix-util.c posix-sema.c posix-io.c system_components_not_extra = endif if HAVE_W32CE_SYSTEM system_components += w32-ce.h w32-ce.c endif -if HAVE_GPGSM -gpgsm_components = engine-gpgsm.c -else -gpgsm_components = -endif - -if HAVE_ASSUAN -assuan_components = assuan-support.c engine-assuan.c -else -assuan_components = -endif - -if HAVE_GPGCONF -gpgconf_components = engine-gpgconf.c -else -gpgconf_components = -endif - -if HAVE_G13 -g13_components = engine-g13.c -else -g13_components = -endif - if HAVE_UISERVER uiserver_components = engine-uiserver.c else uiserver_components = endif # These are the source files common to all library versions. We used # to build a non-installed library for that, but that does not work # correctly on all platforms (in particular, one can not specify the # right linking order with libtool, as the non-installed version has # unresolved symbols to the thread module. main_sources = \ util.h conversion.c get-env.c context.h ops.h \ parsetlv.c parsetlv.h \ data.h data.c data-fd.c data-stream.c data-mem.c data-user.c \ data-compat.c data-identify.c \ signers.c sig-notation.c \ wait.c wait-global.c wait-private.c wait-user.c wait.h \ op-support.c \ encrypt.c encrypt-sign.c decrypt.c decrypt-verify.c verify.c \ sign.c passphrase.c progress.c \ key.c keylist.c trust-item.c trustlist.c \ import.c export.c genkey.c delete.c edit.c getauditlog.c \ - opassuan.c passwd.c \ + opassuan.c passwd.c assuan-support.c \ engine.h engine-backend.h engine.c engine-gpg.c status-table.c \ - $(gpgsm_components) $(assuan_components) $(gpgconf_components) \ + engine-gpgsm.c engine-assuan.c engine-gpgconf.c \ $(uiserver_components) \ - $(g13_components) vfs-mount.c vfs-create.c \ + engine-g13.c vfs-mount.c vfs-create.c \ gpgconf.c \ sema.h priv-io.h $(system_components) sys-util.h dirinfo.c \ debug.c debug.h gpgme.c version.c error.c libgpgme_la_SOURCES = $(main_sources) \ ath.h ath.c $(system_components_not_extra) libgpgme_pthread_la_SOURCES = $(main_sources) \ ath.h ath-pthread.c $(system_components_not_extra) if BUILD_W32_GLIB libgpgme_glib_la_SOURCES = $(main_sources) ath.h ath.c w32-glib-io.c endif if BUILD_W32_QT libgpgme_qt_la_SOURCES = $(main_sources) ath.h ath.c w32-qt-io.cpp \ kdpipeiodevice.h kdpipeiodevice.cpp kdpipeiodevice.moc # FIXME: Add extra depedency: moc_kdpipeiodevice.cpp # These are built sources (normally). # moc_kdpipeiodevice.cpp: kdpipeiodevice.h # $(MOC4) -o $@ $< # # kdpipeiodevice.moc: kdpipeiodevice.cpp # $(MOC4) -o $@ $< endif # We use a global CFLAGS and CPPFLAGS setting for all library # versions, because then every object file is only compiled once. AM_CPPFLAGS = @GPG_ERROR_CFLAGS@ @QT4_CORE_CFLAGS@ AM_CFLAGS = @LIBASSUAN_CFLAGS@ @GLIB_CFLAGS@ @QT4_CORE_CFLAGS@ if HAVE_W32_SYSTEM # Windows provides us with an endless stream of Tough Love. To spawn # processes with a controlled set of inherited handles, we need a # wrapper process. # Except on Windows CE. There nothing is inheritable anyway. if HAVE_W32CE_SYSTEM libexec_PROGRAMS = else libexec_PROGRAMS = gpgme-w32spawn endif RCCOMPILE = $(RC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) LTRCCOMPILE = $(LIBTOOL) --mode=compile --tag=RC $(RCCOMPILE) SUFFIXES = .rc .lo .rc.lo: $(LTRCCOMPILE) -i "$<" -o "$@" gpgme_res = versioninfo.lo no_undefined = -no-undefined export_symbols = -export-symbols $(srcdir)/gpgme.def install-def-file: -$(INSTALL) -d $(DESTDIR)$(libdir) $(INSTALL) $(srcdir)/gpgme.def $(DESTDIR)$(libdir)/gpgme.def uninstall-def-file: -rm $(DESTDIR)$(libdir)/gpgme.def gpgme_deps = $(gpgme_res) gpgme.def else gpgme_res = no_undefined = export_symbols = install-def-file: uninstall-def-file: gpgme_deps = endif libgpgme_la_LDFLAGS = $(no_undefined) $(export_symbols) \ $(libgpgme_version_script_cmd) -version-info \ @LIBGPGME_LT_CURRENT@:@LIBGPGME_LT_REVISION@:@LIBGPGME_LT_AGE@ libgpgme_la_DEPENDENCIES = @LTLIBOBJS@ $(srcdir)/libgpgme.vers $(gpgme_deps) libgpgme_la_LIBADD = $(gpgme_res) @LIBASSUAN_LIBS@ @LTLIBOBJS@ \ @GPG_ERROR_LIBS@ libgpgme_pthread_la_LDFLAGS = $(no_undefined) $(export_symbols) \ $(libgpgme_version_script_cmd) -version-info \ @LIBGPGME_LT_CURRENT@:@LIBGPGME_LT_REVISION@:@LIBGPGME_LT_AGE@ libgpgme_pthread_la_DEPENDENCIES = @LTLIBOBJS@ $(srcdir)/libgpgme.vers libgpgme_pthread_la_LIBADD = $(gpgme_res) @LIBASSUAN_LIBS@ @LTLIBOBJS@ \ -lpthread @GPG_ERROR_LIBS@ if BUILD_W32_GLIB libgpgme_glib_la_LDFLAGS = $(no_undefined) \ $(export_symbols) $(libgpgme_version_script_cmd) -version-info \ @LIBGPGME_LT_CURRENT@:@LIBGPGME_LT_REVISION@:@LIBGPGME_LT_AGE@ libgpgme_glib_la_DEPENDENCIES = @LTLIBOBJS@ \ $(srcdir)/libgpgme.vers $(gpgme_deps) libgpgme_glib_la_LIBADD = $(gpgme_res) @LIBASSUAN_LIBS@ @LTLIBOBJS@ \ @GPG_ERROR_LIBS@ @GLIB_LIBS@ endif if BUILD_W32_QT libgpgme_qt_la_LDFLAGS = $(no_undefined) \ $(export_symbols) $(libgpgme_version_script_cmd) -version-info \ @LIBGPGME_LT_CURRENT@:@LIBGPGME_LT_REVISION@:@LIBGPGME_LT_AGE@ libgpgme_qt_la_DEPENDENCIES = @LTLIBOBJS@ $(srcdir)/libgpgme.vers $(gpgme_deps) libgpgme_qt_la_LIBADD = $(gpgme_res) @LIBASSUAN_LIBS@ @LTLIBOBJS@ \ @GPG_ERROR_LIBS@ @QT4_CORE_LIBS@ endif noinst_PROGRAMS = gpgme-tool gpgme_tool_LDADD = libgpgme.la @LIBASSUAN_LIBS@ install-data-local: install-def-file uninstall-local: uninstall-def-file diff --git a/src/dirinfo.c b/src/dirinfo.c index 27c0dd76..2e387fa1 100644 --- a/src/dirinfo.c +++ b/src/dirinfo.c @@ -1,313 +1,315 @@ /* dirinfo.c - Get directory information * Copyright (C) 2009, 2013 g10 Code GmbH * * This file is part of GPGME. * * GPGME 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. * * GPGME 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 * Lesser 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 . */ #if HAVE_CONFIG_H #include #endif #include #include #include "gpgme.h" #include "util.h" #include "priv-io.h" #include "debug.h" #include "sema.h" #include "sys-util.h" DEFINE_STATIC_LOCK (dirinfo_lock); /* Constants used internally to select the data. */ enum { WANT_HOMEDIR, WANT_AGENT_SOCKET, + WANT_GPGCONF_NAME, WANT_GPG_NAME, WANT_GPGSM_NAME, WANT_G13_NAME, WANT_UISRV_SOCKET }; /* Values retrieved via gpgconf and cached here. */ static struct { int valid; /* Cached information is valid. */ char *homedir; char *agent_socket; + char *gpgconf_name; char *gpg_name; char *gpgsm_name; char *g13_name; char *uisrv_socket; } dirinfo; /* Parse the output of "gpgconf --list-dirs". This function expects that DIRINFO_LOCK is held by the caller. If COMPONENTS is set, the output of --list-components is expected. */ static void parse_output (char *line, int components) { char *value, *p; value = strchr (line, ':'); if (!value) return; *value++ = 0; if (components) { /* Skip the second field. */ value = strchr (value, ':'); if (!value) return; *value++ = 0; } p = strchr (value, ':'); if (p) *p = 0; if (_gpgme_decode_percent_string (value, &value, strlen (value)+1, 0)) return; if (!*value) return; if (components) { if (!strcmp (line, "gpg") && !dirinfo.gpg_name) dirinfo.gpg_name = strdup (value); else if (!strcmp (line, "gpgsm") && !dirinfo.gpgsm_name) dirinfo.gpgsm_name = strdup (value); else if (!strcmp (line, "g13") && !dirinfo.g13_name) dirinfo.g13_name = strdup (value); } else { if (!strcmp (line, "homedir") && !dirinfo.homedir) { const char name[] = "S.uiserver"; dirinfo.homedir = strdup (value); if (dirinfo.homedir) { dirinfo.uisrv_socket = malloc (strlen (dirinfo .homedir) + 1 + strlen (name) + 1); if (dirinfo.uisrv_socket) strcpy (stpcpy (stpcpy (dirinfo.uisrv_socket, dirinfo.homedir), DIRSEP_S), name); } } else if (!strcmp (line, "agent-socket") && !dirinfo.agent_socket) dirinfo.agent_socket = strdup (value); } } /* Read the directory information from gpgconf. This function expects that DIRINFO_LOCK is held by the caller. PGNAME is the name of the gpgconf binary. If COMPONENTS is set, not the directories bit the name of the componeNts are read. */ static void read_gpgconf_dirs (const char *pgmname, int components) { char linebuf[1024] = {0}; int linelen = 0; char * argv[3]; int rp[2]; struct spawn_fd_item_s cfd[] = { {-1, 1 /* STDOUT_FILENO */, -1, 0}, {-1, -1} }; int status; int nread; char *mark = NULL; argv[0] = (char *)pgmname; argv[1] = components? "--list-components" : "--list-dirs"; argv[2] = NULL; if (_gpgme_io_pipe (rp, 1) < 0) return; cfd[0].fd = rp[1]; status = _gpgme_io_spawn (pgmname, argv, 0, cfd, NULL, NULL, NULL); if (status < 0) { _gpgme_io_close (rp[0]); _gpgme_io_close (rp[1]); return; } do { nread = _gpgme_io_read (rp[0], linebuf + linelen, sizeof linebuf - linelen - 1); if (nread > 0) { char *line; const char *lastmark = NULL; size_t nused; linelen += nread; linebuf[linelen] = '\0'; for (line=linebuf; (mark = strchr (line, '\n')); line = mark+1 ) { lastmark = mark; if (mark > line && mark[-1] == '\r') mark[-1] = '\0'; else mark[0] = '\0'; parse_output (line, components); } nused = lastmark? (lastmark + 1 - linebuf) : 0; memmove (linebuf, linebuf + nused, linelen - nused); linelen -= nused; } } while (nread > 0 && linelen < sizeof linebuf - 1); _gpgme_io_close (rp[0]); } static const char * get_gpgconf_item (int what) { const char *result = NULL; LOCK (dirinfo_lock); if (!dirinfo.valid) { - const char *pgmname; + char *pgmname; pgmname = _gpgme_get_gpgconf_path (); if (pgmname && access (pgmname, F_OK)) { _gpgme_debug (DEBUG_INIT, "gpgme_dinfo: gpgconf='%s' [not installed]\n", pgmname); + free (pgmname); pgmname = NULL; /* Not available. */ } else _gpgme_debug (DEBUG_INIT, "gpgme_dinfo: gpgconf='%s'\n", pgmname? pgmname : "[null]"); if (!pgmname) { /* Probably gpgconf is not installed. Assume we are using GnuPG-1. */ pgmname = _gpgme_get_gpg_path (); if (pgmname) - dirinfo.gpg_name = strdup (pgmname); + dirinfo.gpg_name = pgmname; } else { read_gpgconf_dirs (pgmname, 0); read_gpgconf_dirs (pgmname, 1); + dirinfo.gpgconf_name = pgmname; } /* Even if the reading of the directories failed (e.g. due to an too old version gpgconf or no gpgconf at all), we need to mark the entries as valid so that we won't try over and over to read them. Note further that we are not able to change the read values later because they are practically statically allocated. */ dirinfo.valid = 1; if (dirinfo.gpg_name) _gpgme_debug (DEBUG_INIT, "gpgme_dinfo: gpg='%s'\n", dirinfo.gpg_name); if (dirinfo.g13_name) _gpgme_debug (DEBUG_INIT, "gpgme_dinfo: g13='%s'\n", dirinfo.g13_name); if (dirinfo.gpgsm_name) _gpgme_debug (DEBUG_INIT, "gpgme_dinfo: gpgsm='%s'\n", dirinfo.gpgsm_name); if (dirinfo.homedir) _gpgme_debug (DEBUG_INIT, "gpgme_dinfo: homedir='%s'\n", dirinfo.homedir); if (dirinfo.agent_socket) _gpgme_debug (DEBUG_INIT, "gpgme_dinfo: agent='%s'\n", dirinfo.agent_socket); if (dirinfo.uisrv_socket) _gpgme_debug (DEBUG_INIT, "gpgme_dinfo: uisrv='%s'\n", dirinfo.uisrv_socket); } switch (what) { case WANT_HOMEDIR: result = dirinfo.homedir; break; case WANT_AGENT_SOCKET: result = dirinfo.agent_socket; break; + case WANT_GPGCONF_NAME: result = dirinfo.gpgconf_name; break; case WANT_GPG_NAME: result = dirinfo.gpg_name; break; case WANT_GPGSM_NAME: result = dirinfo.gpgsm_name; break; case WANT_G13_NAME: result = dirinfo.g13_name; break; case WANT_UISRV_SOCKET: result = dirinfo.uisrv_socket; break; } UNLOCK (dirinfo_lock); return result; } /* Return the default home directory. Returns NULL if not known. */ const char * _gpgme_get_default_homedir (void) { return get_gpgconf_item (WANT_HOMEDIR); } /* Return the default gpg-agent socket name. Returns NULL if not known. */ const char * _gpgme_get_default_agent_socket (void) { return get_gpgconf_item (WANT_AGENT_SOCKET); } /* Return the default gpg file name. Returns NULL if not known. */ const char * _gpgme_get_default_gpg_name (void) { return get_gpgconf_item (WANT_GPG_NAME); } /* Return the default gpgsm file name. Returns NULL if not known. */ const char * _gpgme_get_default_gpgsm_name (void) { return get_gpgconf_item (WANT_GPGSM_NAME); } /* Return the default g13 file name. Returns NULL if not known. */ const char * _gpgme_get_default_g13_name (void) { return get_gpgconf_item (WANT_G13_NAME); } -/* Return the default gpgconf file name. Returns NULL if not known. - Because gpgconf is the binary used to retrieved all these default - names, this function is merely a simple wrapper around the function - used to locate this binary. */ +/* Return the default gpgconf file name. Returns NULL if not known. */ const char * _gpgme_get_default_gpgconf_name (void) { - return _gpgme_get_gpgconf_path (); + return get_gpgconf_item (WANT_GPGCONF_NAME); } /* Return the default UI-server socket name. Returns NULL if not known. */ const char * _gpgme_get_default_uisrv_socket (void) { return get_gpgconf_item (WANT_UISRV_SOCKET); } diff --git a/src/engine-backend.h b/src/engine-backend.h index a4c0eb27..a768652c 100644 --- a/src/engine-backend.h +++ b/src/engine-backend.h @@ -1,147 +1,139 @@ /* engine-backend.h - A crypto backend for the engine interface. Copyright (C) 2002, 2003, 2004, 2009 g10 Code GmbH This file is part of GPGME. GPGME 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. GPGME 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 Lesser 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 . */ #ifndef ENGINE_BACKEND_H #define ENGINE_BACKEND_H #include "engine.h" struct engine_ops { /* Static functions. */ /* Return the default file name for the binary of this engine. */ const char *(*get_file_name) (void); /* Return the default home dir for the binary of this engine. If this function pointer is not set, the standard default home dir of the engine is used. */ const char *(*get_home_dir) (void); /* Returns a malloced string containing the version of the engine with the given binary file name (or the default if FILE_NAME is NULL. */ char *(*get_version) (const char *file_name); /* Returns a statically allocated string containing the required version. */ const char *(*get_req_version) (void); gpgme_error_t (*new) (void **r_engine, const char *file_name, const char *home_dir); /* Member functions. */ void (*release) (void *engine); gpgme_error_t (*reset) (void *engine); void (*set_status_handler) (void *engine, engine_status_handler_t fnc, void *fnc_value); gpgme_error_t (*set_command_handler) (void *engine, engine_command_handler_t fnc, void *fnc_value, gpgme_data_t data); gpgme_error_t (*set_colon_line_handler) (void *engine, engine_colon_line_handler_t fnc, void *fnc_value); gpgme_error_t (*set_locale) (void *engine, int category, const char *value); gpgme_error_t (*set_protocol) (void *engine, gpgme_protocol_t protocol); gpgme_error_t (*decrypt) (void *engine, gpgme_data_t ciph, gpgme_data_t plain); gpgme_error_t (*decrypt_verify) (void *engine, gpgme_data_t ciph, gpgme_data_t plain); gpgme_error_t (*delete) (void *engine, gpgme_key_t key, int allow_secret); gpgme_error_t (*edit) (void *engine, int type, gpgme_key_t key, gpgme_data_t out, gpgme_ctx_t ctx /* FIXME */); gpgme_error_t (*encrypt) (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t ciph, int use_armor); gpgme_error_t (*encrypt_sign) (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t ciph, int use_armor, gpgme_ctx_t ctx /* FIXME */); gpgme_error_t (*export) (void *engine, const char *pattern, gpgme_export_mode_t mode, gpgme_data_t keydata, int use_armor); gpgme_error_t (*export_ext) (void *engine, const char *pattern[], gpgme_export_mode_t mode, gpgme_data_t keydata, int use_armor); gpgme_error_t (*genkey) (void *engine, gpgme_data_t help_data, int use_armor, gpgme_data_t pubkey, gpgme_data_t seckey); gpgme_error_t (*import) (void *engine, gpgme_data_t keydata, gpgme_key_t *keyarray); gpgme_error_t (*keylist) (void *engine, const char *pattern, int secret_only, gpgme_keylist_mode_t mode); gpgme_error_t (*keylist_ext) (void *engine, const char *pattern[], int secret_only, int reserved, gpgme_keylist_mode_t mode); gpgme_error_t (*sign) (void *engine, gpgme_data_t in, gpgme_data_t out, gpgme_sig_mode_t mode, int use_armor, int use_textmode, int include_certs, gpgme_ctx_t ctx /* FIXME */); gpgme_error_t (*trustlist) (void *engine, const char *pattern); gpgme_error_t (*verify) (void *engine, gpgme_data_t sig, gpgme_data_t signed_text, gpgme_data_t plaintext); gpgme_error_t (*getauditlog) (void *engine, gpgme_data_t output, unsigned int flags); gpgme_error_t (*opassuan_transact) (void *engine, const char *command, gpgme_assuan_data_cb_t data_cb, void *data_cb_value, gpgme_assuan_inquire_cb_t inq_cb, void *inq_cb_value, gpgme_assuan_status_cb_t status_cb, void *status_cb_value); gpgme_error_t (*conf_load) (void *engine, gpgme_conf_comp_t *conf_p); gpgme_error_t (*conf_save) (void *engine, gpgme_conf_comp_t conf); void (*set_io_cbs) (void *engine, gpgme_io_cbs_t io_cbs); void (*io_event) (void *engine, gpgme_event_io_t type, void *type_data); /* Cancel the whole engine session. */ gpgme_error_t (*cancel) (void *engine); /* Cancel only the current operation, not the whole session. */ gpgme_error_t (*cancel_op) (void *engine); /* Change the passphrase for KEY. */ gpgme_error_t (*passwd) (void *engine, gpgme_key_t key, unsigned int flags); /* Set the pinentry mode. */ gpgme_error_t (*set_pinentry_mode) (void *engine, gpgme_pinentry_mode_t mode); }; extern struct engine_ops _gpgme_engine_ops_gpg; /* OpenPGP. */ -#ifdef ENABLE_GPGSM extern struct engine_ops _gpgme_engine_ops_gpgsm; /* CMS. */ -#endif -#ifdef ENABLE_GPGCONF extern struct engine_ops _gpgme_engine_ops_gpgconf; /* gpg-conf. */ -#endif -#ifdef ENABLE_ASSUAN extern struct engine_ops _gpgme_engine_ops_assuan; /* Low-level Assuan. */ -#endif -#ifdef ENABLE_G13 extern struct engine_ops _gpgme_engine_ops_g13; /* Crypto VFS. */ -#endif #ifdef ENABLE_UISERVER extern struct engine_ops _gpgme_engine_ops_uiserver; #endif #endif /* ENGINE_BACKEND_H */ diff --git a/src/engine-g13.c b/src/engine-g13.c index b97e0b44..f0910159 100644 --- a/src/engine-g13.c +++ b/src/engine-g13.c @@ -1,803 +1,803 @@ /* engine-g13.c - G13 engine. Copyright (C) 2000 Werner Koch (dd9jn) Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2009 g10 Code GmbH This file is part of GPGME. GPGME 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. GPGME 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 Lesser 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, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #if HAVE_CONFIG_H #include #endif #include #include #ifdef HAVE_SYS_TYPES_H # include #endif #include #ifdef HAVE_UNISTD_H # include #endif #ifdef HAVE_LOCALE_H #include #endif #include /* FIXME */ #include #include "gpgme.h" #include "util.h" #include "ops.h" #include "wait.h" #include "priv-io.h" #include "sema.h" #include "assuan.h" #include "debug.h" #include "engine-backend.h" typedef struct { int fd; /* FD we talk about. */ int server_fd;/* Server FD for this connection. */ int dir; /* Inbound/Outbound, maybe given implicit? */ void *data; /* Handler-specific data. */ void *tag; /* ID from the user for gpgme_remove_io_callback. */ char server_fd_str[15]; /* Same as SERVER_FD but as a string. We need this because _gpgme_io_fd2str can't be used on a closed descriptor. */ } iocb_data_t; struct engine_g13 { assuan_context_t assuan_ctx; int lc_ctype_set; int lc_messages_set; iocb_data_t status_cb; struct gpgme_io_cbs io_cbs; /* User provided callbacks. */ struct { gpgme_assuan_data_cb_t data_cb; void *data_cb_value; gpgme_assuan_inquire_cb_t inq_cb; void *inq_cb_value; gpgme_assuan_status_cb_t status_cb; void *status_cb_value; } user; }; typedef struct engine_g13 *engine_g13_t; static void g13_io_event (void *engine, gpgme_event_io_t type, void *type_data); static char * g13_get_version (const char *file_name) { return _gpgme_get_program_version (file_name ? file_name : _gpgme_get_default_g13_name ()); } static const char * g13_get_req_version (void) { - return NEED_G13_VERSION; + return "2.1.0"; } static void close_notify_handler (int fd, void *opaque) { engine_g13_t g13 = opaque; assert (fd != -1); if (g13->status_cb.fd == fd) { if (g13->status_cb.tag) (*g13->io_cbs.remove) (g13->status_cb.tag); g13->status_cb.fd = -1; g13->status_cb.tag = NULL; } } /* This is the default inquiry callback. We use it to handle the Pinentry notifications. */ static gpgme_error_t default_inq_cb (engine_g13_t g13, const char *keyword, const char *args) { gpg_error_t err; if (!strcmp (keyword, "PINENTRY_LAUNCHED")) { _gpgme_allow_set_foreground_window ((pid_t)strtoul (args, NULL, 10)); } if (g13->user.inq_cb) { gpgme_data_t data = NULL; err = g13->user.inq_cb (g13->user.inq_cb_value, keyword, args, &data); if (!err && data) { /* FIXME: Returning data is not yet implemented. However we need to allow the caller to cleanup his data object. Thus we run the callback in finish mode immediately. */ err = g13->user.inq_cb (g13->user.inq_cb_value, NULL, NULL, &data); } } else err = 0; return err; } static gpgme_error_t g13_cancel (void *engine) { engine_g13_t g13 = engine; if (!g13) return gpg_error (GPG_ERR_INV_VALUE); if (g13->status_cb.fd != -1) _gpgme_io_close (g13->status_cb.fd); if (g13->assuan_ctx) { assuan_release (g13->assuan_ctx); g13->assuan_ctx = NULL; } return 0; } static gpgme_error_t g13_cancel_op (void *engine) { engine_g13_t g13 = engine; if (!g13) return gpg_error (GPG_ERR_INV_VALUE); if (g13->status_cb.fd != -1) _gpgme_io_close (g13->status_cb.fd); return 0; } static void g13_release (void *engine) { engine_g13_t g13 = engine; if (!g13) return; g13_cancel (engine); free (g13); } static gpgme_error_t g13_new (void **engine, const char *file_name, const char *home_dir) { gpgme_error_t err = 0; engine_g13_t g13; int argc; const char *argv[5]; char *dft_display = NULL; char dft_ttyname[64]; char *dft_ttytype = NULL; char *optstr; g13 = calloc (1, sizeof *g13); if (!g13) return gpg_error_from_syserror (); g13->status_cb.fd = -1; g13->status_cb.dir = 1; g13->status_cb.tag = 0; g13->status_cb.data = g13; argc = 0; argv[argc++] = "g13"; if (home_dir) { argv[argc++] = "--homedir"; argv[argc++] = home_dir; } argv[argc++] = "--server"; argv[argc++] = NULL; err = assuan_new_ext (&g13->assuan_ctx, GPG_ERR_SOURCE_GPGME, &_gpgme_assuan_malloc_hooks, _gpgme_assuan_log_cb, NULL); if (err) goto leave; assuan_ctx_set_system_hooks (g13->assuan_ctx, &_gpgme_assuan_system_hooks); #if USE_DESCRIPTOR_PASSING err = assuan_pipe_connect (g13->assuan_ctx, file_name ? file_name : _gpgme_get_default_g13_name (), argv, NULL, NULL, NULL, ASSUAN_PIPE_CONNECT_FDPASSING); #else err = assuan_pipe_connect (g13->assuan_ctx, file_name ? file_name : _gpgme_get_default_g13_name (), argv, NULL, NULL, NULL, 0); #endif if (err) goto leave; err = _gpgme_getenv ("DISPLAY", &dft_display); if (err) goto leave; if (dft_display) { if (asprintf (&optstr, "OPTION display=%s", dft_display) < 0) { free (dft_display); err = gpg_error_from_syserror (); goto leave; } free (dft_display); err = assuan_transact (g13->assuan_ctx, optstr, NULL, NULL, NULL, NULL, NULL, NULL); free (optstr); if (err) goto leave; } if (isatty (1)) { int rc; rc = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname)); if (rc) { err = gpg_error_from_errno (rc); goto leave; } else { if (asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0) { err = gpg_error_from_syserror (); goto leave; } err = assuan_transact (g13->assuan_ctx, optstr, NULL, NULL, NULL, NULL, NULL, NULL); free (optstr); if (err) goto leave; err = _gpgme_getenv ("TERM", &dft_ttytype); if (err) goto leave; if (dft_ttytype) { if (asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype) < 0) { free (dft_ttytype); err = gpg_error_from_syserror (); goto leave; } free (dft_ttytype); err = assuan_transact (g13->assuan_ctx, optstr, NULL, NULL, NULL, NULL, NULL, NULL); free (optstr); if (err) goto leave; } } } #ifdef HAVE_W32_SYSTEM /* Under Windows we need to use AllowSetForegroundWindow. Tell g13 to tell us when it needs it. */ if (!err) { err = assuan_transact (g13->assuan_ctx, "OPTION allow-pinentry-notify", NULL, NULL, NULL, NULL, NULL, NULL); if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION) err = 0; /* This is a new feature of g13. */ } #endif /*HAVE_W32_SYSTEM*/ leave: if (err) g13_release (g13); else *engine = g13; return err; } static gpgme_error_t g13_set_locale (void *engine, int category, const char *value) { engine_g13_t g13 = engine; gpgme_error_t err; char *optstr; char *catstr; /* FIXME: If value is NULL, we need to reset the option to default. But we can't do this. So we error out here. G13 needs support for this. */ if (0) ; #ifdef LC_CTYPE else if (category == LC_CTYPE) { catstr = "lc-ctype"; if (!value && g13->lc_ctype_set) return gpg_error (GPG_ERR_INV_VALUE); if (value) g13->lc_ctype_set = 1; } #endif #ifdef LC_MESSAGES else if (category == LC_MESSAGES) { catstr = "lc-messages"; if (!value && g13->lc_messages_set) return gpg_error (GPG_ERR_INV_VALUE); if (value) g13->lc_messages_set = 1; } #endif /* LC_MESSAGES */ else return gpg_error (GPG_ERR_INV_VALUE); /* FIXME: Reset value to default. */ if (!value) return 0; if (asprintf (&optstr, "OPTION %s=%s", catstr, value) < 0) err = gpg_error_from_syserror (); else { err = assuan_transact (g13->assuan_ctx, optstr, NULL, NULL, NULL, NULL, NULL, NULL); free (optstr); } return err; } #if USE_DESCRIPTOR_PASSING static gpgme_error_t g13_assuan_simple_command (assuan_context_t ctx, char *cmd, engine_status_handler_t status_fnc, void *status_fnc_value) { gpg_error_t err; char *line; size_t linelen; err = assuan_write_line (ctx, cmd); if (err) return err; do { err = assuan_read_line (ctx, &line, &linelen); if (err) return err; if (*line == '#' || !linelen) continue; if (linelen >= 2 && line[0] == 'O' && line[1] == 'K' && (line[2] == '\0' || line[2] == ' ')) return 0; else if (linelen >= 4 && line[0] == 'E' && line[1] == 'R' && line[2] == 'R' && line[3] == ' ') err = atoi (&line[4]); else if (linelen >= 2 && line[0] == 'S' && line[1] == ' ') { char *rest; rest = strchr (line + 2, ' '); if (!rest) rest = line + linelen; /* set to an empty string */ else *(rest++) = 0; /* Nothing to do with status lines. */ } else err = gpg_error (GPG_ERR_GENERAL); } while (!err); return err; } #endif static gpgme_error_t status_handler (void *opaque, int fd) { struct io_cb_data *data = (struct io_cb_data *) opaque; engine_g13_t g13 = (engine_g13_t) data->handler_value; gpgme_error_t err = 0; char *line; size_t linelen; do { err = assuan_read_line (g13->assuan_ctx, &line, &linelen); if (err) { /* Try our best to terminate the connection friendly. */ /* assuan_write_line (g13->assuan_ctx, "BYE"); */ TRACE2 (DEBUG_CTX, "gpgme:status_handler", g13, "fd 0x%x: error reading assuan line: %s", fd, gpg_strerror (err)); } else if (linelen >= 3 && line[0] == 'E' && line[1] == 'R' && line[2] == 'R' && (line[3] == '\0' || line[3] == ' ')) { if (line[3] == ' ') err = atoi (&line[4]); if (! err) err = gpg_error (GPG_ERR_GENERAL); TRACE2 (DEBUG_CTX, "gpgme:status_handler", g13, "fd 0x%x: ERR line: %s", fd, err ? gpg_strerror (err) : "ok"); /* Command execution errors are not fatal, as we use a session based protocol. */ data->op_err = err; /* The caller will do the rest (namely, call cancel_op, which closes status_fd). */ return 0; } else if (linelen >= 2 && line[0] == 'O' && line[1] == 'K' && (line[2] == '\0' || line[2] == ' ')) { TRACE1 (DEBUG_CTX, "gpgme:status_handler", g13, "fd 0x%x: OK line", fd); _gpgme_io_close (g13->status_cb.fd); return 0; } else if (linelen > 2 && line[0] == 'D' && line[1] == ' ') { /* We are using the colon handler even for plain inline data - strange name for that function but for historic reasons we keep it. */ /* FIXME We can't use this for binary data because we assume this is a string. For the current usage of colon output it is correct. */ char *src = line + 2; char *end = line + linelen; char *dst = src; linelen = 0; while (src < end) { if (*src == '%' && src + 2 < end) { /* Handle escaped characters. */ ++src; *dst++ = _gpgme_hextobyte (src); src += 2; } else *dst++ = *src++; linelen++; } src = line + 2; if (linelen && g13->user.data_cb) err = g13->user.data_cb (g13->user.data_cb_value, src, linelen); else err = 0; TRACE2 (DEBUG_CTX, "gpgme:g13_status_handler", g13, "fd 0x%x: D inlinedata; status from cb: %s", fd, (g13->user.data_cb ? (err? gpg_strerror (err):"ok"):"no callback")); } else if (linelen > 2 && line[0] == 'S' && line[1] == ' ') { char *src; char *args; src = line + 2; while (*src == ' ') src++; args = strchr (line + 2, ' '); if (!args) args = line + linelen; /* set to an empty string */ else *(args++) = 0; while (*args == ' ') args++; if (g13->user.status_cb) err = g13->user.status_cb (g13->user.status_cb_value, src, args); else err = 0; TRACE3 (DEBUG_CTX, "gpgme:g13_status_handler", g13, "fd 0x%x: S line (%s) - status from cb: %s", fd, line+2, (g13->user.status_cb ? (err? gpg_strerror (err):"ok"):"no callback")); } else if (linelen >= 7 && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q' && line[3] == 'U' && line[4] == 'I' && line[5] == 'R' && line[6] == 'E' && (line[7] == '\0' || line[7] == ' ')) { char *src; char *args; for (src=line+7; *src == ' '; src++) ; args = strchr (src, ' '); if (!args) args = line + linelen; /* Let it point to an empty string. */ else *(args++) = 0; while (*args == ' ') args++; err = default_inq_cb (g13, src, args); if (!err) { /* Flush and send END. */ err = assuan_send_data (g13->assuan_ctx, NULL, 0); } else if (gpg_err_code (err) == GPG_ERR_ASS_CANCELED) { /* Flush and send CANcel. */ err = assuan_send_data (g13->assuan_ctx, NULL, 1); } assuan_write_line (g13->assuan_ctx, "END"); } } while (!err && assuan_pending_line (g13->assuan_ctx)); return err; } static gpgme_error_t add_io_cb (engine_g13_t g13, iocb_data_t *iocbd, gpgme_io_cb_t handler) { gpgme_error_t err; TRACE_BEG2 (DEBUG_ENGINE, "engine-g13:add_io_cb", g13, "fd %d, dir %d", iocbd->fd, iocbd->dir); err = (*g13->io_cbs.add) (g13->io_cbs.add_priv, iocbd->fd, iocbd->dir, handler, iocbd->data, &iocbd->tag); if (err) return TRACE_ERR (err); if (!iocbd->dir) /* FIXME Kludge around poll() problem. */ err = _gpgme_io_set_nonblocking (iocbd->fd); return TRACE_ERR (err); } static gpgme_error_t start (engine_g13_t g13, const char *command) { gpgme_error_t err; assuan_fd_t afdlist[5]; int fdlist[5]; int nfds; int i; /* We need to know the fd used by assuan for reads. We do this by using the assumption that the first returned fd from assuan_get_active_fds() is always this one. */ nfds = assuan_get_active_fds (g13->assuan_ctx, 0 /* read fds */, afdlist, DIM (afdlist)); if (nfds < 1) return gpg_error (GPG_ERR_GENERAL); /* FIXME */ /* For now... */ for (i = 0; i < nfds; i++) fdlist[i] = (int) afdlist[i]; /* We "duplicate" the file descriptor, so we can close it here (we can't close fdlist[0], as that is closed by libassuan, and closing it here might cause libassuan to close some unrelated FD later). Alternatively, we could special case status_fd and register/unregister it manually as needed, but this increases code duplication and is more complicated as we can not use the close notifications etc. A third alternative would be to let Assuan know that we closed the FD, but that complicates the Assuan interface. */ g13->status_cb.fd = _gpgme_io_dup (fdlist[0]); if (g13->status_cb.fd < 0) return gpg_error_from_syserror (); if (_gpgme_io_set_close_notify (g13->status_cb.fd, close_notify_handler, g13)) { _gpgme_io_close (g13->status_cb.fd); g13->status_cb.fd = -1; return gpg_error (GPG_ERR_GENERAL); } err = add_io_cb (g13, &g13->status_cb, status_handler); if (!err) err = assuan_write_line (g13->assuan_ctx, command); if (!err) g13_io_event (g13, GPGME_EVENT_START, NULL); return err; } #if USE_DESCRIPTOR_PASSING static gpgme_error_t g13_reset (void *engine) { engine_g13_t g13 = engine; /* We must send a reset because we need to reset the list of signers. Note that RESET does not reset OPTION commands. */ return g13_assuan_simple_command (g13->assuan_ctx, "RESET", NULL, NULL); } #endif static gpgme_error_t g13_transact (void *engine, const char *command, gpgme_assuan_data_cb_t data_cb, void *data_cb_value, gpgme_assuan_inquire_cb_t inq_cb, void *inq_cb_value, gpgme_assuan_status_cb_t status_cb, void *status_cb_value) { engine_g13_t g13 = engine; gpgme_error_t err; if (!g13 || !command || !*command) return gpg_error (GPG_ERR_INV_VALUE); g13->user.data_cb = data_cb; g13->user.data_cb_value = data_cb_value; g13->user.inq_cb = inq_cb; g13->user.inq_cb_value = inq_cb_value; g13->user.status_cb = status_cb; g13->user.status_cb_value = status_cb_value; err = start (g13, command); return err; } static void g13_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs) { engine_g13_t g13 = engine; g13->io_cbs = *io_cbs; } static void g13_io_event (void *engine, gpgme_event_io_t type, void *type_data) { engine_g13_t g13 = engine; TRACE3 (DEBUG_ENGINE, "gpgme:g13_io_event", g13, "event %p, type %d, type_data %p", g13->io_cbs.event, type, type_data); if (g13->io_cbs.event) (*g13->io_cbs.event) (g13->io_cbs.event_priv, type, type_data); } struct engine_ops _gpgme_engine_ops_g13 = { /* Static functions. */ _gpgme_get_default_g13_name, NULL, g13_get_version, g13_get_req_version, g13_new, /* Member functions. */ g13_release, #if USE_DESCRIPTOR_PASSING g13_reset, #else NULL, /* reset */ #endif NULL, /* set_status_handler */ NULL, /* set_command_handler */ NULL, /* set_colon_line_handler */ g13_set_locale, NULL, /* set_protocol */ NULL, /* decrypt */ NULL, /* decrypt_verify */ NULL, /* delete */ NULL, /* edit */ NULL, /* encrypt */ NULL, /* encrypt_sign */ NULL, /* export */ NULL, /* export_ext */ NULL, /* genkey */ NULL, /* import */ NULL, /* keylist */ NULL, /* keylist_ext */ NULL, /* sign */ NULL, /* trustlist */ NULL, /* verify */ NULL, /* getauditlog */ g13_transact, NULL, /* conf_load */ NULL, /* conf_save */ g13_set_io_cbs, g13_io_event, g13_cancel, g13_cancel_op, NULL, /* passwd */ NULL /* set_pinentry_mode */ }; diff --git a/src/engine-gpg.c b/src/engine-gpg.c index 3bc9f660..2f59bb9a 100644 --- a/src/engine-gpg.c +++ b/src/engine-gpg.c @@ -1,2443 +1,2443 @@ /* engine-gpg.c - Gpg Engine. Copyright (C) 2000 Werner Koch (dd9jn) Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, - 2009, 2010, 2012 g10 Code GmbH + 2009, 2010, 2012, 2013 g10 Code GmbH This file is part of GPGME. GPGME 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. GPGME 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 Lesser 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 . */ #if HAVE_CONFIG_H #include #endif #include #include #include #include #include #ifdef HAVE_UNISTD_H # include #endif #ifdef HAVE_LOCALE_H #include #endif #include "gpgme.h" #include "util.h" #include "ops.h" #include "wait.h" #include "context.h" /*temp hack until we have GpmeData methods to do I/O */ #include "priv-io.h" #include "sema.h" #include "debug.h" #include "engine-backend.h" /* This type is used to build a list of gpg arguments and data sources/sinks. */ struct arg_and_data_s { struct arg_and_data_s *next; gpgme_data_t data; /* If this is not NULL, use arg below. */ int inbound; /* True if this is used for reading from gpg. */ int dup_to; int print_fd; /* Print the fd number and not the special form of it. */ int *arg_locp; /* Write back the argv idx of this argument when building command line to this location. */ char arg[1]; /* Used if data above is not used. */ }; struct fd_data_map_s { gpgme_data_t data; int inbound; /* true if this is used for reading from gpg */ int dup_to; int fd; /* the fd to use */ int peer_fd; /* the other side of the pipe */ int arg_loc; /* The index into the argv for translation purposes. */ void *tag; }; typedef gpgme_error_t (*colon_preprocessor_t) (char *line, char **rline); struct engine_gpg { char *file_name; char *lc_messages; char *lc_ctype; struct arg_and_data_s *arglist; struct arg_and_data_s **argtail; struct { int fd[2]; int arg_loc; size_t bufsize; char *buffer; size_t readpos; int eof; engine_status_handler_t fnc; void *fnc_value; void *tag; } status; /* This is a kludge - see the comment at colon_line_handler. */ struct { int fd[2]; int arg_loc; size_t bufsize; char *buffer; size_t readpos; int eof; engine_colon_line_handler_t fnc; /* this indicate use of this structrue */ void *fnc_value; void *tag; colon_preprocessor_t preprocess_fnc; } colon; char **argv; struct fd_data_map_s *fd_data_map; /* stuff needed for interactive (command) mode */ struct { int used; int fd; void *cb_data; int idx; /* Index in fd_data_map */ gpgme_status_code_t code; /* last code */ char *keyword; /* what has been requested (malloced) */ engine_command_handler_t fnc; void *fnc_value; /* The kludges never end. This is used to couple command handlers with output data in edit key mode. */ gpgme_data_t linked_data; int linked_idx; } cmd; struct gpgme_io_cbs io_cbs; gpgme_pinentry_mode_t pinentry_mode; }; typedef struct engine_gpg *engine_gpg_t; static void gpg_io_event (void *engine, gpgme_event_io_t type, void *type_data) { engine_gpg_t gpg = engine; TRACE3 (DEBUG_ENGINE, "gpgme:gpg_io_event", gpg, "event %p, type %d, type_data %p", gpg->io_cbs.event, type, type_data); if (gpg->io_cbs.event) (*gpg->io_cbs.event) (gpg->io_cbs.event_priv, type, type_data); } static void close_notify_handler (int fd, void *opaque) { engine_gpg_t gpg = opaque; assert (fd != -1); if (gpg->status.fd[0] == fd) { if (gpg->status.tag) (*gpg->io_cbs.remove) (gpg->status.tag); gpg->status.fd[0] = -1; } else if (gpg->status.fd[1] == fd) gpg->status.fd[1] = -1; else if (gpg->colon.fd[0] == fd) { if (gpg->colon.tag) (*gpg->io_cbs.remove) (gpg->colon.tag); gpg->colon.fd[0] = -1; } else if (gpg->colon.fd[1] == fd) gpg->colon.fd[1] = -1; else if (gpg->cmd.fd == fd) gpg->cmd.fd = -1; else if (gpg->fd_data_map) { int i; for (i = 0; gpg->fd_data_map[i].data; i++) { if (gpg->fd_data_map[i].fd == fd) { if (gpg->fd_data_map[i].tag) (*gpg->io_cbs.remove) (gpg->fd_data_map[i].tag); gpg->fd_data_map[i].fd = -1; break; } if (gpg->fd_data_map[i].peer_fd == fd) { gpg->fd_data_map[i].peer_fd = -1; break; } } } } /* If FRONT is true, push at the front of the list. Use this for options added late in the process. */ static gpgme_error_t _add_arg (engine_gpg_t gpg, const char *arg, int front, int *arg_locp) { struct arg_and_data_s *a; assert (gpg); assert (arg); a = malloc (sizeof *a + strlen (arg)); if (!a) return gpg_error_from_syserror (); a->data = NULL; a->dup_to = -1; a->arg_locp = arg_locp; strcpy (a->arg, arg); if (front) { a->next = gpg->arglist; if (!gpg->arglist) { /* If this is the first argument, we need to update the tail pointer. */ gpg->argtail = &a->next; } gpg->arglist = a; } else { a->next = NULL; *gpg->argtail = a; gpg->argtail = &a->next; } return 0; } static gpgme_error_t add_arg_ext (engine_gpg_t gpg, const char *arg, int front) { return _add_arg (gpg, arg, front, NULL); } static gpgme_error_t add_arg_with_locp (engine_gpg_t gpg, const char *arg, int *locp) { return _add_arg (gpg, arg, 0, locp); } static gpgme_error_t add_arg (engine_gpg_t gpg, const char *arg) { return add_arg_ext (gpg, arg, 0); } static gpgme_error_t add_data (engine_gpg_t gpg, gpgme_data_t data, int dup_to, int inbound) { struct arg_and_data_s *a; assert (gpg); assert (data); a = malloc (sizeof *a - 1); if (!a) return gpg_error_from_syserror (); a->next = NULL; a->data = data; a->inbound = inbound; a->arg_locp = NULL; if (dup_to == -2) { a->print_fd = 1; a->dup_to = -1; } else { a->print_fd = 0; a->dup_to = dup_to; } *gpg->argtail = a; gpg->argtail = &a->next; return 0; } static char * gpg_get_version (const char *file_name) { return _gpgme_get_program_version (file_name ? file_name : _gpgme_get_default_gpg_name ()); } static const char * gpg_get_req_version (void) { - return NEED_GPG_VERSION; + return "1.4.0"; } static void free_argv (char **argv) { int i; for (i = 0; argv[i]; i++) free (argv[i]); free (argv); } static void free_fd_data_map (struct fd_data_map_s *fd_data_map) { int i; if (!fd_data_map) return; for (i = 0; fd_data_map[i].data; i++) { if (fd_data_map[i].fd != -1) _gpgme_io_close (fd_data_map[i].fd); if (fd_data_map[i].peer_fd != -1) _gpgme_io_close (fd_data_map[i].peer_fd); /* Don't release data because this is only a reference. */ } free (fd_data_map); } static gpgme_error_t gpg_cancel (void *engine) { engine_gpg_t gpg = engine; if (!gpg) return gpg_error (GPG_ERR_INV_VALUE); /* If gpg may be waiting for a cmd, close the cmd fd first. On Windows, close operations block on the reader/writer thread. */ if (gpg->cmd.used) { if (gpg->cmd.fd != -1) _gpgme_io_close (gpg->cmd.fd); else if (gpg->fd_data_map && gpg->fd_data_map[gpg->cmd.idx].fd != -1) _gpgme_io_close (gpg->fd_data_map[gpg->cmd.idx].fd); } if (gpg->status.fd[0] != -1) _gpgme_io_close (gpg->status.fd[0]); if (gpg->status.fd[1] != -1) _gpgme_io_close (gpg->status.fd[1]); if (gpg->colon.fd[0] != -1) _gpgme_io_close (gpg->colon.fd[0]); if (gpg->colon.fd[1] != -1) _gpgme_io_close (gpg->colon.fd[1]); if (gpg->fd_data_map) { free_fd_data_map (gpg->fd_data_map); gpg->fd_data_map = NULL; } return 0; } static void gpg_release (void *engine) { engine_gpg_t gpg = engine; if (!gpg) return; gpg_cancel (engine); if (gpg->file_name) free (gpg->file_name); if (gpg->lc_messages) free (gpg->lc_messages); if (gpg->lc_ctype) free (gpg->lc_ctype); while (gpg->arglist) { struct arg_and_data_s *next = gpg->arglist->next; if (gpg->arglist) free (gpg->arglist); gpg->arglist = next; } if (gpg->status.buffer) free (gpg->status.buffer); if (gpg->colon.buffer) free (gpg->colon.buffer); if (gpg->argv) free_argv (gpg->argv); if (gpg->cmd.keyword) free (gpg->cmd.keyword); free (gpg); } static gpgme_error_t gpg_new (void **engine, const char *file_name, const char *home_dir) { engine_gpg_t gpg; gpgme_error_t rc = 0; char *dft_display = NULL; char dft_ttyname[64]; char *dft_ttytype = NULL; gpg = calloc (1, sizeof *gpg); if (!gpg) return gpg_error_from_syserror (); if (file_name) { gpg->file_name = strdup (file_name); if (!gpg->file_name) { rc = gpg_error_from_syserror (); goto leave; } } gpg->argtail = &gpg->arglist; gpg->status.fd[0] = -1; gpg->status.fd[1] = -1; gpg->colon.fd[0] = -1; gpg->colon.fd[1] = -1; gpg->cmd.fd = -1; gpg->cmd.idx = -1; gpg->cmd.linked_data = NULL; gpg->cmd.linked_idx = -1; /* Allocate the read buffer for the status pipe. */ gpg->status.bufsize = 1024; gpg->status.readpos = 0; gpg->status.buffer = malloc (gpg->status.bufsize); if (!gpg->status.buffer) { rc = gpg_error_from_syserror (); goto leave; } /* In any case we need a status pipe - create it right here and don't handle it with our generic gpgme_data_t mechanism. */ if (_gpgme_io_pipe (gpg->status.fd, 1) == -1) { rc = gpg_error_from_syserror (); goto leave; } if (_gpgme_io_set_close_notify (gpg->status.fd[0], close_notify_handler, gpg) || _gpgme_io_set_close_notify (gpg->status.fd[1], close_notify_handler, gpg)) { rc = gpg_error (GPG_ERR_GENERAL); goto leave; } gpg->status.eof = 0; if (home_dir) { rc = add_arg (gpg, "--homedir"); if (!rc) rc = add_arg (gpg, home_dir); if (rc) goto leave; } rc = add_arg (gpg, "--status-fd"); if (rc) goto leave; { char buf[25]; _gpgme_io_fd2str (buf, sizeof (buf), gpg->status.fd[1]); rc = add_arg_with_locp (gpg, buf, &gpg->status.arg_loc); if (rc) goto leave; } rc = add_arg (gpg, "--no-tty"); if (!rc) rc = add_arg (gpg, "--charset"); if (!rc) rc = add_arg (gpg, "utf8"); if (!rc) rc = add_arg (gpg, "--enable-progress-filter"); if (rc) goto leave; rc = _gpgme_getenv ("DISPLAY", &dft_display); if (rc) goto leave; if (dft_display) { rc = add_arg (gpg, "--display"); if (!rc) rc = add_arg (gpg, dft_display); free (dft_display); } if (isatty (1)) { int err; err = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname)); if (err) rc = gpg_error_from_errno (err); else { if (*dft_ttyname) { rc = add_arg (gpg, "--ttyname"); if (!rc) rc = add_arg (gpg, dft_ttyname); } else rc = 0; if (!rc) { rc = _gpgme_getenv ("TERM", &dft_ttytype); if (rc) goto leave; if (dft_ttytype) { rc = add_arg (gpg, "--ttytype"); if (!rc) rc = add_arg (gpg, dft_ttytype); } free (dft_ttytype); } } if (rc) goto leave; } leave: if (rc) gpg_release (gpg); else *engine = gpg; return rc; } static gpgme_error_t gpg_set_locale (void *engine, int category, const char *value) { engine_gpg_t gpg = engine; if (0) ; #ifdef LC_CTYPE else if (category == LC_CTYPE) { if (gpg->lc_ctype) { free (gpg->lc_ctype); gpg->lc_ctype = NULL; } if (value) { gpg->lc_ctype = strdup (value); if (!gpg->lc_ctype) return gpg_error_from_syserror (); } } #endif #ifdef LC_MESSAGES else if (category == LC_MESSAGES) { if (gpg->lc_messages) { free (gpg->lc_messages); gpg->lc_messages = NULL; } if (value) { gpg->lc_messages = strdup (value); if (!gpg->lc_messages) return gpg_error_from_syserror (); } } #endif /* LC_MESSAGES */ else return gpg_error (GPG_ERR_INV_VALUE); return 0; } /* Note, that the status_handler is allowed to modifiy the args value. */ static void gpg_set_status_handler (void *engine, engine_status_handler_t fnc, void *fnc_value) { engine_gpg_t gpg = engine; gpg->status.fnc = fnc; gpg->status.fnc_value = fnc_value; } /* Kludge to process --with-colon output. */ static gpgme_error_t gpg_set_colon_line_handler (void *engine, engine_colon_line_handler_t fnc, void *fnc_value) { engine_gpg_t gpg = engine; gpg->colon.bufsize = 1024; gpg->colon.readpos = 0; gpg->colon.buffer = malloc (gpg->colon.bufsize); if (!gpg->colon.buffer) return gpg_error_from_syserror (); if (_gpgme_io_pipe (gpg->colon.fd, 1) == -1) { int saved_err = gpg_error_from_syserror (); free (gpg->colon.buffer); gpg->colon.buffer = NULL; return saved_err; } if (_gpgme_io_set_close_notify (gpg->colon.fd[0], close_notify_handler, gpg) || _gpgme_io_set_close_notify (gpg->colon.fd[1], close_notify_handler, gpg)) return gpg_error (GPG_ERR_GENERAL); gpg->colon.eof = 0; gpg->colon.fnc = fnc; gpg->colon.fnc_value = fnc_value; return 0; } static gpgme_error_t command_handler (void *opaque, int fd) { struct io_cb_data *data = (struct io_cb_data *) opaque; engine_gpg_t gpg = (engine_gpg_t) data->handler_value; gpgme_error_t err; int processed = 0; assert (gpg->cmd.used); assert (gpg->cmd.code); assert (gpg->cmd.fnc); err = gpg->cmd.fnc (gpg->cmd.fnc_value, gpg->cmd.code, gpg->cmd.keyword, fd, &processed); gpg->cmd.code = 0; /* And sleep again until read_status will wake us up again. */ /* XXX We must check if there are any more fds active after removing this one. */ (*gpg->io_cbs.remove) (gpg->fd_data_map[gpg->cmd.idx].tag); gpg->cmd.fd = gpg->fd_data_map[gpg->cmd.idx].fd; gpg->fd_data_map[gpg->cmd.idx].fd = -1; if (err) return err; /* We always need to send at least a newline character. */ if (!processed) _gpgme_io_write (fd, "\n", 1); return 0; } /* The Fnc will be called to get a value for one of the commands with a key KEY. If the Code passed to FNC is 0, the function may release resources associated with the returned value from another call. To match such a second call to a first call, the returned value from the first call is passed as keyword. */ static gpgme_error_t gpg_set_command_handler (void *engine, engine_command_handler_t fnc, void *fnc_value, gpgme_data_t linked_data) { engine_gpg_t gpg = engine; gpgme_error_t rc; rc = add_arg (gpg, "--command-fd"); if (rc) return rc; /* This is a hack. We don't have a real data object. The only thing that matters is that we use something unique, so we use the address of the cmd structure in the gpg object. */ rc = add_data (gpg, (void *) &gpg->cmd, -2, 0); if (rc) return rc; gpg->cmd.fnc = fnc; gpg->cmd.cb_data = (void *) &gpg->cmd; gpg->cmd.fnc_value = fnc_value; gpg->cmd.linked_data = linked_data; gpg->cmd.used = 1; return 0; } static gpgme_error_t build_argv (engine_gpg_t gpg) { gpgme_error_t err; struct arg_and_data_s *a; struct fd_data_map_s *fd_data_map; size_t datac=0, argc=0; char **argv; int need_special = 0; int use_agent = 0; char *p; /* We don't want to use the agent with a malformed environment variable. This is only a very basic test but sufficient to make our life in the regression tests easier. */ err = _gpgme_getenv ("GPG_AGENT_INFO", &p); if (err) return err; use_agent = (p && strchr (p, ':')); if (p) free (p); if (gpg->argv) { free_argv (gpg->argv); gpg->argv = NULL; } if (gpg->fd_data_map) { free_fd_data_map (gpg->fd_data_map); gpg->fd_data_map = NULL; } argc++; /* For argv[0]. */ for (a = gpg->arglist; a; a = a->next) { argc++; if (a->data) { /*fprintf (stderr, "build_argv: data\n" );*/ datac++; if (a->dup_to == -1 && !a->print_fd) need_special = 1; } else { /* fprintf (stderr, "build_argv: arg=`%s'\n", a->arg );*/ } } if (need_special) argc++; if (use_agent) argc++; if (gpg->pinentry_mode) argc++; if (!gpg->cmd.used) argc++; /* --batch */ argc += 1; /* --no-sk-comment */ argv = calloc (argc + 1, sizeof *argv); if (!argv) return gpg_error_from_syserror (); fd_data_map = calloc (datac + 1, sizeof *fd_data_map); if (!fd_data_map) { int saved_err = gpg_error_from_syserror (); free_argv (argv); return saved_err; } argc = datac = 0; argv[argc] = strdup ("gpg"); /* argv[0] */ if (!argv[argc]) { int saved_err = gpg_error_from_syserror (); free (fd_data_map); free_argv (argv); return saved_err; } argc++; if (need_special) { argv[argc] = strdup ("--enable-special-filenames"); if (!argv[argc]) { int saved_err = gpg_error_from_syserror (); free (fd_data_map); free_argv (argv); return saved_err; } argc++; } if (use_agent) { argv[argc] = strdup ("--use-agent"); if (!argv[argc]) { int saved_err = gpg_error_from_syserror (); free (fd_data_map); free_argv (argv); return saved_err; } argc++; } if (gpg->pinentry_mode) { const char *s = NULL; switch (gpg->pinentry_mode) { case GPGME_PINENTRY_MODE_DEFAULT: break; case GPGME_PINENTRY_MODE_ASK: s = "--pinentry-mode=ask"; break; case GPGME_PINENTRY_MODE_CANCEL: s = "--pinentry-mode=cancel"; break; case GPGME_PINENTRY_MODE_ERROR: s = "--pinentry-mode=error"; break; case GPGME_PINENTRY_MODE_LOOPBACK:s = "--pinentry-mode=loopback"; break; } if (s) { argv[argc] = strdup (s); if (!argv[argc]) { int saved_err = gpg_error_from_syserror (); free (fd_data_map); free_argv (argv); return saved_err; } argc++; } } if (!gpg->cmd.used) { argv[argc] = strdup ("--batch"); if (!argv[argc]) { int saved_err = gpg_error_from_syserror (); free (fd_data_map); free_argv (argv); return saved_err; } argc++; } argv[argc] = strdup ("--no-sk-comment"); if (!argv[argc]) { int saved_err = gpg_error_from_syserror (); free (fd_data_map); free_argv (argv); return saved_err; } argc++; for (a = gpg->arglist; a; a = a->next) { if (a->arg_locp) *(a->arg_locp) = argc; if (a->data) { /* Create a pipe to pass it down to gpg. */ fd_data_map[datac].inbound = a->inbound; /* Create a pipe. */ { int fds[2]; if (_gpgme_io_pipe (fds, fd_data_map[datac].inbound ? 1 : 0) == -1) { int saved_errno = errno; free (fd_data_map); free_argv (argv); return gpg_error (saved_errno); } if (_gpgme_io_set_close_notify (fds[0], close_notify_handler, gpg) || _gpgme_io_set_close_notify (fds[1], close_notify_handler, gpg)) { return gpg_error (GPG_ERR_GENERAL); } /* If the data_type is FD, we have to do a dup2 here. */ if (fd_data_map[datac].inbound) { fd_data_map[datac].fd = fds[0]; fd_data_map[datac].peer_fd = fds[1]; } else { fd_data_map[datac].fd = fds[1]; fd_data_map[datac].peer_fd = fds[0]; } } /* Hack to get hands on the fd later. */ if (gpg->cmd.used) { if (gpg->cmd.cb_data == a->data) { assert (gpg->cmd.idx == -1); gpg->cmd.idx = datac; } else if (gpg->cmd.linked_data == a->data) { assert (gpg->cmd.linked_idx == -1); gpg->cmd.linked_idx = datac; } } fd_data_map[datac].data = a->data; fd_data_map[datac].dup_to = a->dup_to; if (a->dup_to == -1) { char *ptr; int buflen = 25; argv[argc] = malloc (buflen); if (!argv[argc]) { int saved_err = gpg_error_from_syserror (); free (fd_data_map); free_argv (argv); return saved_err; } ptr = argv[argc]; if (!a->print_fd) { *(ptr++) = '-'; *(ptr++) = '&'; buflen -= 2; } _gpgme_io_fd2str (ptr, buflen, fd_data_map[datac].peer_fd); fd_data_map[datac].arg_loc = argc; argc++; } datac++; } else { argv[argc] = strdup (a->arg); if (!argv[argc]) { int saved_err = gpg_error_from_syserror (); free (fd_data_map); free_argv (argv); return saved_err; } argc++; } } gpg->argv = argv; gpg->fd_data_map = fd_data_map; return 0; } static gpgme_error_t add_io_cb (engine_gpg_t gpg, int fd, int dir, gpgme_io_cb_t handler, void *data, void **tag) { gpgme_error_t err; err = (*gpg->io_cbs.add) (gpg->io_cbs.add_priv, fd, dir, handler, data, tag); if (err) return err; if (!dir) /* FIXME Kludge around poll() problem. */ err = _gpgme_io_set_nonblocking (fd); return err; } /* Handle the status output of GnuPG. This function does read entire lines and passes them as C strings to the callback function (we can use C Strings because the status output is always UTF-8 encoded). Of course we have to buffer the lines to cope with long lines e.g. with a large user ID. Note: We can optimize this to only cope with status line code we know about and skip all other stuff without buffering (i.e. without extending the buffer). */ static gpgme_error_t read_status (engine_gpg_t gpg) { char *p; int nread; size_t bufsize = gpg->status.bufsize; char *buffer = gpg->status.buffer; size_t readpos = gpg->status.readpos; assert (buffer); if (bufsize - readpos < 256) { /* Need more room for the read. */ bufsize += 1024; buffer = realloc (buffer, bufsize); if (!buffer) return gpg_error_from_syserror (); } nread = _gpgme_io_read (gpg->status.fd[0], buffer + readpos, bufsize-readpos); if (nread == -1) return gpg_error_from_syserror (); if (!nread) { gpg->status.eof = 1; if (gpg->status.fnc) { gpgme_error_t err; err = gpg->status.fnc (gpg->status.fnc_value, GPGME_STATUS_EOF, ""); if (err) return err; } return 0; } while (nread > 0) { for (p = buffer + readpos; nread; nread--, p++) { if (*p == '\n') { /* (we require that the last line is terminated by a LF) */ if (p > buffer && p[-1] == '\r') p[-1] = 0; *p = 0; if (!strncmp (buffer, "[GNUPG:] ", 9) && buffer[9] >= 'A' && buffer[9] <= 'Z') { char *rest; gpgme_status_code_t r; rest = strchr (buffer + 9, ' '); if (!rest) rest = p; /* Set to an empty string. */ else *rest++ = 0; r = _gpgme_parse_status (buffer + 9); if (r >= 0) { if (gpg->cmd.used && (r == GPGME_STATUS_GET_BOOL || r == GPGME_STATUS_GET_LINE || r == GPGME_STATUS_GET_HIDDEN)) { gpg->cmd.code = r; if (gpg->cmd.keyword) free (gpg->cmd.keyword); gpg->cmd.keyword = strdup (rest); if (!gpg->cmd.keyword) return gpg_error_from_syserror (); /* This should be the last thing we have received and the next thing will be that the command handler does its action. */ if (nread > 1) TRACE0 (DEBUG_CTX, "gpgme:read_status", 0, "error: unexpected data"); add_io_cb (gpg, gpg->cmd.fd, 0, command_handler, gpg, &gpg->fd_data_map[gpg->cmd.idx].tag); gpg->fd_data_map[gpg->cmd.idx].fd = gpg->cmd.fd; gpg->cmd.fd = -1; } else if (gpg->status.fnc) { gpgme_error_t err; err = gpg->status.fnc (gpg->status.fnc_value, r, rest); if (err) return err; } if (r == GPGME_STATUS_END_STREAM) { if (gpg->cmd.used) { /* Before we can actually add the command fd, we might have to flush the linked output data pipe. */ if (gpg->cmd.linked_idx != -1 && gpg->fd_data_map[gpg->cmd.linked_idx].fd != -1) { struct io_select_fd_s fds; fds.fd = gpg->fd_data_map[gpg->cmd.linked_idx].fd; fds.for_read = 1; fds.for_write = 0; fds.opaque = NULL; do { fds.signaled = 0; _gpgme_io_select (&fds, 1, 1); if (fds.signaled) _gpgme_data_inbound_handler (gpg->cmd.linked_data, fds.fd); } while (fds.signaled); } /* XXX We must check if there are any more fds active after removing this one. */ (*gpg->io_cbs.remove) (gpg->fd_data_map[gpg->cmd.idx].tag); gpg->cmd.fd = gpg->fd_data_map[gpg->cmd.idx].fd; gpg->fd_data_map[gpg->cmd.idx].fd = -1; } } } } /* To reuse the buffer for the next line we have to shift the remaining data to the buffer start and restart the loop Hmmm: We can optimize this function by looking forward in the buffer to see whether a second complete line is available and in this case avoid the memmove for this line. */ nread--; p++; if (nread) memmove (buffer, p, nread); readpos = 0; break; /* the for loop */ } else readpos++; } } /* Update the gpg object. */ gpg->status.bufsize = bufsize; gpg->status.buffer = buffer; gpg->status.readpos = readpos; return 0; } static gpgme_error_t status_handler (void *opaque, int fd) { struct io_cb_data *data = (struct io_cb_data *) opaque; engine_gpg_t gpg = (engine_gpg_t) data->handler_value; int err; assert (fd == gpg->status.fd[0]); err = read_status (gpg); if (err) return err; if (gpg->status.eof) _gpgme_io_close (fd); return 0; } static gpgme_error_t read_colon_line (engine_gpg_t gpg) { char *p; int nread; size_t bufsize = gpg->colon.bufsize; char *buffer = gpg->colon.buffer; size_t readpos = gpg->colon.readpos; assert (buffer); if (bufsize - readpos < 256) { /* Need more room for the read. */ bufsize += 1024; buffer = realloc (buffer, bufsize); if (!buffer) return gpg_error_from_syserror (); } nread = _gpgme_io_read (gpg->colon.fd[0], buffer+readpos, bufsize-readpos); if (nread == -1) return gpg_error_from_syserror (); if (!nread) { gpg->colon.eof = 1; assert (gpg->colon.fnc); gpg->colon.fnc (gpg->colon.fnc_value, NULL); return 0; } while (nread > 0) { for (p = buffer + readpos; nread; nread--, p++) { if ( *p == '\n' ) { /* (we require that the last line is terminated by a LF) and we skip empty lines. Note: we use UTF8 encoding and escaping of special characters. We require at least one colon to cope with some other printed information. */ *p = 0; if (*buffer && strchr (buffer, ':')) { char *line = NULL; if (gpg->colon.preprocess_fnc) { gpgme_error_t err; err = gpg->colon.preprocess_fnc (buffer, &line); if (err) return err; } assert (gpg->colon.fnc); gpg->colon.fnc (gpg->colon.fnc_value, line ? line : buffer); if (line) free (line); } /* To reuse the buffer for the next line we have to shift the remaining data to the buffer start and restart the loop Hmmm: We can optimize this function by looking forward in the buffer to see whether a second complete line is available and in this case avoid the memmove for this line. */ nread--; p++; if (nread) memmove (buffer, p, nread); readpos = 0; break; /* The for loop. */ } else readpos++; } } /* Update the gpg object. */ gpg->colon.bufsize = bufsize; gpg->colon.buffer = buffer; gpg->colon.readpos = readpos; return 0; } /* This colonline handler thing is not the clean way to do it. It might be better to enhance the gpgme_data_t object to act as a wrapper for a callback. Same goes for the status thing. For now we use this thing here because it is easier to implement. */ static gpgme_error_t colon_line_handler (void *opaque, int fd) { struct io_cb_data *data = (struct io_cb_data *) opaque; engine_gpg_t gpg = (engine_gpg_t) data->handler_value; gpgme_error_t rc = 0; assert (fd == gpg->colon.fd[0]); rc = read_colon_line (gpg); if (rc) return rc; if (gpg->colon.eof) _gpgme_io_close (fd); return 0; } static gpgme_error_t start (engine_gpg_t gpg) { gpgme_error_t rc; int i, n; int status; struct spawn_fd_item_s *fd_list; pid_t pid; if (!gpg) return gpg_error (GPG_ERR_INV_VALUE); if (!gpg->file_name && !_gpgme_get_default_gpg_name ()) return trace_gpg_error (GPG_ERR_INV_ENGINE); if (gpg->lc_ctype) { rc = add_arg_ext (gpg, gpg->lc_ctype, 1); if (!rc) rc = add_arg_ext (gpg, "--lc-ctype", 1); if (rc) return rc; } if (gpg->lc_messages) { rc = add_arg_ext (gpg, gpg->lc_messages, 1); if (!rc) rc = add_arg_ext (gpg, "--lc-messages", 1); if (rc) return rc; } rc = build_argv (gpg); if (rc) return rc; /* status_fd, colon_fd and end of list. */ n = 3; for (i = 0; gpg->fd_data_map[i].data; i++) n++; fd_list = calloc (n, sizeof *fd_list); if (! fd_list) return gpg_error_from_syserror (); /* Build the fd list for the child. */ n = 0; fd_list[n].fd = gpg->status.fd[1]; fd_list[n].dup_to = -1; fd_list[n].arg_loc = gpg->status.arg_loc; n++; if (gpg->colon.fnc) { fd_list[n].fd = gpg->colon.fd[1]; fd_list[n].dup_to = 1; n++; } for (i = 0; gpg->fd_data_map[i].data; i++) { fd_list[n].fd = gpg->fd_data_map[i].peer_fd; fd_list[n].dup_to = gpg->fd_data_map[i].dup_to; fd_list[n].arg_loc = gpg->fd_data_map[i].arg_loc; n++; } fd_list[n].fd = -1; fd_list[n].dup_to = -1; status = _gpgme_io_spawn (gpg->file_name ? gpg->file_name : _gpgme_get_default_gpg_name (), gpg->argv, IOSPAWN_FLAG_ALLOW_SET_FG, fd_list, NULL, NULL, &pid); { int saved_err = gpg_error_from_syserror (); free (fd_list); if (status == -1) return saved_err; } /*_gpgme_register_term_handler ( closure, closure_value, pid );*/ rc = add_io_cb (gpg, gpg->status.fd[0], 1, status_handler, gpg, &gpg->status.tag); if (rc) /* FIXME: kill the child */ return rc; if (gpg->colon.fnc) { assert (gpg->colon.fd[0] != -1); rc = add_io_cb (gpg, gpg->colon.fd[0], 1, colon_line_handler, gpg, &gpg->colon.tag); if (rc) /* FIXME: kill the child */ return rc; } for (i = 0; gpg->fd_data_map[i].data; i++) { if (gpg->cmd.used && i == gpg->cmd.idx) { /* Park the cmd fd. */ gpg->cmd.fd = gpg->fd_data_map[i].fd; gpg->fd_data_map[i].fd = -1; } else { rc = add_io_cb (gpg, gpg->fd_data_map[i].fd, gpg->fd_data_map[i].inbound, gpg->fd_data_map[i].inbound ? _gpgme_data_inbound_handler : _gpgme_data_outbound_handler, gpg->fd_data_map[i].data, &gpg->fd_data_map[i].tag); if (rc) /* FIXME: kill the child */ return rc; } } gpg_io_event (gpg, GPGME_EVENT_START, NULL); /* fixme: check what data we can release here */ return 0; } static gpgme_error_t gpg_decrypt (void *engine, gpgme_data_t ciph, gpgme_data_t plain) { engine_gpg_t gpg = engine; gpgme_error_t err; err = add_arg (gpg, "--decrypt"); /* Tell the gpg object about the data. */ if (!err) err = add_arg (gpg, "--output"); if (!err) err = add_arg (gpg, "-"); if (!err) err = add_data (gpg, plain, 1, 1); if (!err) err = add_arg (gpg, "--"); if (!err) err = add_data (gpg, ciph, -1, 0); if (!err) start (gpg); return err; } static gpgme_error_t gpg_delete (void *engine, gpgme_key_t key, int allow_secret) { engine_gpg_t gpg = engine; gpgme_error_t err; err = add_arg (gpg, allow_secret ? "--delete-secret-and-public-key" : "--delete-key"); if (!err) err = add_arg (gpg, "--"); if (!err) { if (!key->subkeys || !key->subkeys->fpr) return gpg_error (GPG_ERR_INV_VALUE); else err = add_arg (gpg, key->subkeys->fpr); } if (!err) start (gpg); return err; } static gpgme_error_t gpg_passwd (void *engine, gpgme_key_t key, unsigned int flags) { engine_gpg_t gpg = engine; gpgme_error_t err; if (!key || !key->subkeys || !key->subkeys->fpr) return gpg_error (GPG_ERR_INV_CERT_OBJ); err = add_arg (gpg, "--passwd"); if (!err) err = add_arg (gpg, key->subkeys->fpr); if (!err) start (gpg); return err; } static gpgme_error_t append_args_from_signers (engine_gpg_t gpg, gpgme_ctx_t ctx /* FIXME */) { gpgme_error_t err = 0; int i; gpgme_key_t key; for (i = 0; (key = gpgme_signers_enum (ctx, i)); i++) { const char *s = key->subkeys ? key->subkeys->keyid : NULL; if (s) { if (!err) err = add_arg (gpg, "-u"); if (!err) err = add_arg (gpg, s); } gpgme_key_unref (key); if (err) break; } return err; } static gpgme_error_t append_args_from_sig_notations (engine_gpg_t gpg, gpgme_ctx_t ctx /* FIXME */) { gpgme_error_t err = 0; gpgme_sig_notation_t notation; notation = gpgme_sig_notation_get (ctx); while (!err && notation) { if (notation->name && !(notation->flags & GPGME_SIG_NOTATION_HUMAN_READABLE)) err = gpg_error (GPG_ERR_INV_VALUE); else if (notation->name) { char *arg; /* Maximum space needed is one byte for the "critical" flag, the name, one byte for '=', the value, and a terminating '\0'. */ arg = malloc (1 + notation->name_len + 1 + notation->value_len + 1); if (!arg) err = gpg_error_from_syserror (); if (!err) { char *argp = arg; if (notation->critical) *(argp++) = '!'; memcpy (argp, notation->name, notation->name_len); argp += notation->name_len; *(argp++) = '='; /* We know that notation->name is '\0' terminated. */ strcpy (argp, notation->value); } if (!err) err = add_arg (gpg, "--sig-notation"); if (!err) err = add_arg (gpg, arg); if (arg) free (arg); } else { /* This is a policy URL. */ char *value; if (notation->critical) { value = malloc (1 + notation->value_len + 1); if (!value) err = gpg_error_from_syserror (); else { value[0] = '!'; /* We know that notation->value is '\0' terminated. */ strcpy (&value[1], notation->value); } } else value = notation->value; if (!err) err = add_arg (gpg, "--sig-policy-url"); if (!err) err = add_arg (gpg, value); if (value != notation->value) free (value); } notation = notation->next; } return err; } static gpgme_error_t gpg_edit (void *engine, int type, gpgme_key_t key, gpgme_data_t out, gpgme_ctx_t ctx /* FIXME */) { engine_gpg_t gpg = engine; gpgme_error_t err; err = add_arg (gpg, "--with-colons"); if (!err) err = append_args_from_signers (gpg, ctx); if (!err) err = add_arg (gpg, type == 0 ? "--edit-key" : "--card-edit"); if (!err) err = add_data (gpg, out, 1, 1); if (!err) err = add_arg (gpg, "--"); if (!err && type == 0) { const char *s = key->subkeys ? key->subkeys->fpr : NULL; if (!s) err = gpg_error (GPG_ERR_INV_VALUE); else err = add_arg (gpg, s); } if (!err) err = start (gpg); return err; } static gpgme_error_t append_args_from_recipients (engine_gpg_t gpg, gpgme_key_t recp[]) { gpgme_error_t err = 0; int i = 0; while (recp[i]) { if (!recp[i]->subkeys || !recp[i]->subkeys->fpr) err = gpg_error (GPG_ERR_INV_VALUE); if (!err) err = add_arg (gpg, "-r"); if (!err) err = add_arg (gpg, recp[i]->subkeys->fpr); if (err) break; i++; } return err; } static gpgme_error_t gpg_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t ciph, int use_armor) { engine_gpg_t gpg = engine; gpgme_error_t err; int symmetric = !recp; err = add_arg (gpg, symmetric ? "--symmetric" : "--encrypt"); if (!err && use_armor) err = add_arg (gpg, "--armor"); if (!symmetric) { /* If we know that all recipients are valid (full or ultimate trust) we can suppress further checks. */ if (!err && (flags & GPGME_ENCRYPT_ALWAYS_TRUST)) err = add_arg (gpg, "--always-trust"); if (!err && (flags & GPGME_ENCRYPT_NO_ENCRYPT_TO)) err = add_arg (gpg, "--no-encrypt-to"); if (!err) err = append_args_from_recipients (gpg, recp); } /* Tell the gpg object about the data. */ if (!err) err = add_arg (gpg, "--output"); if (!err) err = add_arg (gpg, "-"); if (!err) err = add_data (gpg, ciph, 1, 1); if (gpgme_data_get_file_name (plain)) { if (!err) err = add_arg (gpg, "--set-filename"); if (!err) err = add_arg (gpg, gpgme_data_get_file_name (plain)); } if (!err) err = add_arg (gpg, "--"); if (!err) err = add_data (gpg, plain, -1, 0); if (!err) err = start (gpg); return err; } static gpgme_error_t gpg_encrypt_sign (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t ciph, int use_armor, gpgme_ctx_t ctx /* FIXME */) { engine_gpg_t gpg = engine; gpgme_error_t err; int symmetric = !recp; err = add_arg (gpg, symmetric ? "--symmetric" : "--encrypt"); if (!err) err = add_arg (gpg, "--sign"); if (!err && use_armor) err = add_arg (gpg, "--armor"); if (!symmetric) { /* If we know that all recipients are valid (full or ultimate trust) we can suppress further checks. */ if (!err && (flags & GPGME_ENCRYPT_ALWAYS_TRUST)) err = add_arg (gpg, "--always-trust"); if (!err && (flags & GPGME_ENCRYPT_NO_ENCRYPT_TO)) err = add_arg (gpg, "--no-encrypt-to"); if (!err) err = append_args_from_recipients (gpg, recp); } if (!err) err = append_args_from_signers (gpg, ctx); if (!err) err = append_args_from_sig_notations (gpg, ctx); /* Tell the gpg object about the data. */ if (!err) err = add_arg (gpg, "--output"); if (!err) err = add_arg (gpg, "-"); if (!err) err = add_data (gpg, ciph, 1, 1); if (gpgme_data_get_file_name (plain)) { if (!err) err = add_arg (gpg, "--set-filename"); if (!err) err = add_arg (gpg, gpgme_data_get_file_name (plain)); } if (!err) err = add_arg (gpg, "--"); if (!err) err = add_data (gpg, plain, -1, 0); if (!err) err = start (gpg); return err; } static gpgme_error_t export_common (engine_gpg_t gpg, gpgme_export_mode_t mode, gpgme_data_t keydata, int use_armor) { gpgme_error_t err = 0; if ((mode & ~(GPGME_EXPORT_MODE_EXTERN |GPGME_EXPORT_MODE_MINIMAL))) return gpg_error (GPG_ERR_NOT_SUPPORTED); if ((mode & GPGME_EXPORT_MODE_MINIMAL)) err = add_arg (gpg, "--export-options=export-minimal"); if (err) ; else if ((mode & GPGME_EXPORT_MODE_EXTERN)) { err = add_arg (gpg, "--send-keys"); } else { err = add_arg (gpg, "--export"); if (!err && use_armor) err = add_arg (gpg, "--armor"); if (!err) err = add_data (gpg, keydata, 1, 1); } if (!err) err = add_arg (gpg, "--"); return err; } static gpgme_error_t gpg_export (void *engine, const char *pattern, gpgme_export_mode_t mode, gpgme_data_t keydata, int use_armor) { engine_gpg_t gpg = engine; gpgme_error_t err; err = export_common (gpg, mode, keydata, use_armor); if (!err && pattern && *pattern) err = add_arg (gpg, pattern); if (!err) err = start (gpg); return err; } static gpgme_error_t gpg_export_ext (void *engine, const char *pattern[], gpgme_export_mode_t mode, gpgme_data_t keydata, int use_armor) { engine_gpg_t gpg = engine; gpgme_error_t err; err = export_common (gpg, mode, keydata, use_armor); if (pattern) { while (!err && *pattern && **pattern) err = add_arg (gpg, *(pattern++)); } if (!err) err = start (gpg); return err; } static gpgme_error_t gpg_genkey (void *engine, gpgme_data_t help_data, int use_armor, gpgme_data_t pubkey, gpgme_data_t seckey) { engine_gpg_t gpg = engine; gpgme_error_t err; if (!gpg) return gpg_error (GPG_ERR_INV_VALUE); /* We need a special mechanism to get the fd of a pipe here, so that we can use this for the %pubring and %secring parameters. We don't have this yet, so we implement only the adding to the standard keyrings. */ if (pubkey || seckey) return gpg_error (GPG_ERR_NOT_IMPLEMENTED); err = add_arg (gpg, "--gen-key"); if (!err && use_armor) err = add_arg (gpg, "--armor"); if (!err) err = add_arg (gpg, "--"); if (!err) err = add_data (gpg, help_data, -1, 0); if (!err) err = start (gpg); return err; } /* Return the next DELIM delimited string from DATA as a C-string. The caller needs to provide the address of a pointer variable which he has to set to NULL before the first call. After the last call to this function, this function needs to be called once more with DATA set to NULL so that the function can release its internal state. After that the pointer variable is free for use again. Note that we use a delimiter and thus a trailing delimiter is not required. DELIM may not be changed after the first call. */ static const char * string_from_data (gpgme_data_t data, int delim, void **helpptr, gpgme_error_t *r_err) { #define MYBUFLEN 2000 /* Fixme: We don't support URLs longer than that. */ struct { int eof_seen; int nbytes; /* Length of the last returned string including the delimiter. */ int buflen; /* Valid length of BUF. */ char buf[MYBUFLEN+1]; /* Buffer with one byte extra space. */ } *self; char *p; int nread; *r_err = 0; if (!data) { if (*helpptr) { free (*helpptr); *helpptr = NULL; } return NULL; } if (*helpptr) self = *helpptr; else { self = malloc (sizeof *self); if (!self) { *r_err = gpg_error_from_syserror (); return NULL; } *helpptr = self; self->eof_seen = 0; self->nbytes = 0; self->buflen = 0; } if (self->eof_seen) return NULL; assert (self->nbytes <= self->buflen); memmove (self->buf, self->buf + self->nbytes, self->buflen - self->nbytes); self->buflen -= self->nbytes; self->nbytes = 0; do { /* Fixme: This is fairly infective scanning because we may scan the buffer several times. */ p = memchr (self->buf, delim, self->buflen); if (p) { *p = 0; self->nbytes = p - self->buf + 1; return self->buf; } if ( !(MYBUFLEN - self->buflen) ) { /* Not enough space - URL too long. */ *r_err = gpg_error (GPG_ERR_TOO_LARGE); return NULL; } nread = gpgme_data_read (data, self->buf + self->buflen, MYBUFLEN - self->buflen); if (nread < 0) { *r_err = gpg_error_from_syserror (); return NULL; } self->buflen += nread; } while (nread); /* EOF reached. If we have anything in the buffer, append a Nul and return it. */ self->eof_seen = 1; if (self->buflen) { self->buf[self->buflen] = 0; /* (we allocated one extra byte) */ return self->buf; } return NULL; #undef MYBUFLEN } static gpgme_error_t gpg_import (void *engine, gpgme_data_t keydata, gpgme_key_t *keyarray) { engine_gpg_t gpg = engine; gpgme_error_t err; int idx; gpgme_data_encoding_t dataenc; if (keydata && keyarray) return gpg_error (GPG_ERR_INV_VALUE); /* Only one is allowed. */ dataenc = gpgme_data_get_encoding (keydata); if (keyarray) { err = add_arg (gpg, "--recv-keys"); if (!err) err = add_arg (gpg, "--"); for (idx=0; !err && keyarray[idx]; idx++) { if (keyarray[idx]->protocol != GPGME_PROTOCOL_OpenPGP) ; else if (!keyarray[idx]->subkeys) ; else if (keyarray[idx]->subkeys->fpr && *keyarray[idx]->subkeys->fpr) err = add_arg (gpg, keyarray[idx]->subkeys->fpr); else if (*keyarray[idx]->subkeys->keyid) err = add_arg (gpg, keyarray[idx]->subkeys->keyid); } } else if (dataenc == GPGME_DATA_ENCODING_URL || dataenc == GPGME_DATA_ENCODING_URL0) { void *helpptr; const char *string; gpgme_error_t xerr; int delim = (dataenc == GPGME_DATA_ENCODING_URL)? '\n': 0; /* FIXME: --fetch-keys is probably not correct because it can't grok all kinds of URLs. On Unix it should just work but on Windows we will build the command line and that may fail for some embedded control characters. It is anyway limited to the maximum size of the command line. We need another command which can take its input from a file. Maybe we should use an option to gpg to modify such commands (ala --multifile). */ err = add_arg (gpg, "--fetch-keys"); if (!err) err = add_arg (gpg, "--"); helpptr = NULL; while (!err && (string = string_from_data (keydata, delim, &helpptr, &xerr))) err = add_arg (gpg, string); if (!err) err = xerr; string_from_data (NULL, delim, &helpptr, &xerr); } else if (dataenc == GPGME_DATA_ENCODING_URLESC) { /* Already escaped URLs are not yet supported. */ err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); } else { err = add_arg (gpg, "--import"); if (!err) err = add_arg (gpg, "--"); if (!err) err = add_data (gpg, keydata, -1, 0); } if (!err) err = start (gpg); return err; } /* The output for external keylistings in GnuPG is different from all the other key listings. We catch this here with a special preprocessor that reformats the colon handler lines. */ static gpgme_error_t gpg_keylist_preprocess (char *line, char **r_line) { enum { RT_NONE, RT_INFO, RT_PUB, RT_UID } rectype = RT_NONE; #define NR_FIELDS 16 char *field[NR_FIELDS]; int fields = 0; *r_line = NULL; while (line && fields < NR_FIELDS) { field[fields++] = line; line = strchr (line, ':'); if (line) *(line++) = '\0'; } if (!strcmp (field[0], "info")) rectype = RT_INFO; else if (!strcmp (field[0], "pub")) rectype = RT_PUB; else if (!strcmp (field[0], "uid")) rectype = RT_UID; else rectype = RT_NONE; switch (rectype) { case RT_INFO: /* FIXME: Eventually, check the version number at least. */ return 0; case RT_PUB: if (fields < 7) return 0; /* The format is: pub:::::: as defined in 5.2. Machine Readable Indexes of the OpenPGP HTTP Keyserver Protocol (draft). We want: pub:o::::::::::::: */ if (asprintf (r_line, "pub:o%s:%s:%s:%s:%s:%s::::::::", field[6], field[3], field[2], field[1], field[4], field[5]) < 0) return gpg_error_from_syserror (); return 0; case RT_UID: /* The format is: uid:::: as defined in 5.2. Machine Readable Indexes of the OpenPGP HTTP Keyserver Protocol (draft). We want: uid:o::::::::: */ { /* The user ID is percent escaped, but we want c-coded. Because we have to replace each '%HL' by '\xHL', we need at most 4/3 th the number of bytes. But because we also need to escape the backslashes we allocate twice as much. */ char *uid = malloc (2 * strlen (field[1]) + 1); char *src; char *dst; if (! uid) return gpg_error_from_syserror (); src = field[1]; dst = uid; while (*src) { if (*src == '%') { *(dst++) = '\\'; *(dst++) = 'x'; src++; /* Copy the next two bytes unconditionally. */ if (*src) *(dst++) = *(src++); if (*src) *(dst++) = *(src++); } else if (*src == '\\') { *dst++ = '\\'; *dst++ = '\\'; } else *(dst++) = *(src++); } *dst = '\0'; if (asprintf (r_line, "uid:o%s::::%s:%s:::%s:", field[4], field[2], field[3], uid) < 0) return gpg_error_from_syserror (); } return 0; case RT_NONE: /* Unknown record. */ break; } return 0; } static gpg_error_t gpg_keylist_build_options (engine_gpg_t gpg, int secret_only, gpgme_keylist_mode_t mode) { gpg_error_t err; err = add_arg (gpg, "--with-colons"); if (!err) err = add_arg (gpg, "--fixed-list-mode"); if (!err) err = add_arg (gpg, "--with-fingerprint"); if (!err) err = add_arg (gpg, "--with-fingerprint"); if (!err && (mode & GPGME_KEYLIST_MODE_SIGS) && (mode & GPGME_KEYLIST_MODE_SIG_NOTATIONS)) { err = add_arg (gpg, "--list-options"); if (!err) err = add_arg (gpg, "show-sig-subpackets=\"20,26\""); } if (!err) { if ( (mode & GPGME_KEYLIST_MODE_EXTERN) ) { if (secret_only) err = gpg_error (GPG_ERR_NOT_SUPPORTED); else if ( (mode & GPGME_KEYLIST_MODE_LOCAL)) { /* The local+extern mode is special. It works only with gpg >= 2.0.10. FIXME: We should check that we have such a version to that we can return a proper error code. The problem is that we don't know the context here and thus can't access the cached version number for the engine info structure. */ err = add_arg (gpg, "--locate-keys"); if ((mode & GPGME_KEYLIST_MODE_SIGS)) err = add_arg (gpg, "--with-sig-check"); } else { err = add_arg (gpg, "--search-keys"); gpg->colon.preprocess_fnc = gpg_keylist_preprocess; } } else { err = add_arg (gpg, secret_only ? "--list-secret-keys" : ((mode & GPGME_KEYLIST_MODE_SIGS) ? "--check-sigs" : "--list-keys")); } } if (!err) err = add_arg (gpg, "--"); return err; } static gpgme_error_t gpg_keylist (void *engine, const char *pattern, int secret_only, gpgme_keylist_mode_t mode) { engine_gpg_t gpg = engine; gpgme_error_t err; err = gpg_keylist_build_options (gpg, secret_only, mode); if (!err && pattern && *pattern) err = add_arg (gpg, pattern); if (!err) err = start (gpg); return err; } static gpgme_error_t gpg_keylist_ext (void *engine, const char *pattern[], int secret_only, int reserved, gpgme_keylist_mode_t mode) { engine_gpg_t gpg = engine; gpgme_error_t err; if (reserved) return gpg_error (GPG_ERR_INV_VALUE); err = gpg_keylist_build_options (gpg, secret_only, mode); if (pattern) { while (!err && *pattern && **pattern) err = add_arg (gpg, *(pattern++)); } if (!err) err = start (gpg); return err; } static gpgme_error_t gpg_sign (void *engine, gpgme_data_t in, gpgme_data_t out, gpgme_sig_mode_t mode, int use_armor, int use_textmode, int include_certs, gpgme_ctx_t ctx /* FIXME */) { engine_gpg_t gpg = engine; gpgme_error_t err; if (mode == GPGME_SIG_MODE_CLEAR) err = add_arg (gpg, "--clearsign"); else { err = add_arg (gpg, "--sign"); if (!err && mode == GPGME_SIG_MODE_DETACH) err = add_arg (gpg, "--detach"); if (!err && use_armor) err = add_arg (gpg, "--armor"); if (!err && use_textmode) err = add_arg (gpg, "--textmode"); } if (!err) err = append_args_from_signers (gpg, ctx); if (!err) err = append_args_from_sig_notations (gpg, ctx); if (gpgme_data_get_file_name (in)) { if (!err) err = add_arg (gpg, "--set-filename"); if (!err) err = add_arg (gpg, gpgme_data_get_file_name (in)); } /* Tell the gpg object about the data. */ if (!err) err = add_arg (gpg, "--"); if (!err) err = add_data (gpg, in, -1, 0); if (!err) err = add_data (gpg, out, 1, 1); if (!err) start (gpg); return err; } static gpgme_error_t gpg_trustlist (void *engine, const char *pattern) { engine_gpg_t gpg = engine; gpgme_error_t err; err = add_arg (gpg, "--with-colons"); if (!err) err = add_arg (gpg, "--list-trust-path"); /* Tell the gpg object about the data. */ if (!err) err = add_arg (gpg, "--"); if (!err) err = add_arg (gpg, pattern); if (!err) err = start (gpg); return err; } static gpgme_error_t gpg_verify (void *engine, gpgme_data_t sig, gpgme_data_t signed_text, gpgme_data_t plaintext) { engine_gpg_t gpg = engine; gpgme_error_t err = 0; if (plaintext) { /* Normal or cleartext signature. */ err = add_arg (gpg, "--output"); if (!err) err = add_arg (gpg, "-"); if (!err) err = add_arg (gpg, "--"); if (!err) err = add_data (gpg, sig, -1, 0); if (!err) err = add_data (gpg, plaintext, 1, 1); } else { err = add_arg (gpg, "--verify"); if (!err) err = add_arg (gpg, "--"); if (!err) err = add_data (gpg, sig, -1, 0); if (!err && signed_text) err = add_data (gpg, signed_text, -1, 0); } if (!err) err = start (gpg); return err; } static void gpg_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs) { engine_gpg_t gpg = engine; gpg->io_cbs = *io_cbs; } static gpgme_error_t gpg_set_pinentry_mode (void *engine, gpgme_pinentry_mode_t mode) { engine_gpg_t gpg = engine; gpg->pinentry_mode = mode; return 0; } struct engine_ops _gpgme_engine_ops_gpg = { /* Static functions. */ _gpgme_get_default_gpg_name, NULL, gpg_get_version, gpg_get_req_version, gpg_new, /* Member functions. */ gpg_release, NULL, /* reset */ gpg_set_status_handler, gpg_set_command_handler, gpg_set_colon_line_handler, gpg_set_locale, NULL, /* set_protocol */ gpg_decrypt, gpg_decrypt, /* decrypt_verify */ gpg_delete, gpg_edit, gpg_encrypt, gpg_encrypt_sign, gpg_export, gpg_export_ext, gpg_genkey, gpg_import, gpg_keylist, gpg_keylist_ext, gpg_sign, gpg_trustlist, gpg_verify, NULL, /* getauditlog */ NULL, /* opassuan_transact */ NULL, /* conf_load */ NULL, /* conf_save */ gpg_set_io_cbs, gpg_io_event, gpg_cancel, NULL, /* cancel_op */ gpg_passwd, gpg_set_pinentry_mode }; diff --git a/src/engine-gpgconf.c b/src/engine-gpgconf.c index 1d457bb9..47cde669 100644 --- a/src/engine-gpgconf.c +++ b/src/engine-gpgconf.c @@ -1,965 +1,965 @@ /* engine-gpgconf.c - gpg-conf engine. Copyright (C) 2000 Werner Koch (dd9jn) Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2008, 2013 g10 Code GmbH This file is part of GPGME. GPGME 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. GPGME 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 Lesser 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 . */ #if HAVE_CONFIG_H #include #endif #include #include #ifdef HAVE_SYS_TYPES_H # include #endif #include #ifdef HAVE_UNISTD_H # include #endif #include /* FIXME */ #include #include "gpgme.h" #include "util.h" #include "ops.h" #include "wait.h" #include "priv-io.h" #include "sema.h" #include "assuan.h" #include "debug.h" #include "engine-backend.h" struct engine_gpgconf { char *file_name; char *home_dir; }; typedef struct engine_gpgconf *engine_gpgconf_t; static char * gpgconf_get_version (const char *file_name) { return _gpgme_get_program_version (file_name ? file_name : _gpgme_get_default_gpgconf_name ()); } static const char * gpgconf_get_req_version (void) { - return NEED_GPGCONF_VERSION; + return "2.0.4"; } static void gpgconf_release (void *engine) { engine_gpgconf_t gpgconf = engine; if (!gpgconf) return; if (gpgconf->file_name) free (gpgconf->file_name); if (gpgconf->home_dir) free (gpgconf->home_dir); free (gpgconf); } static gpgme_error_t gpgconf_new (void **engine, const char *file_name, const char *home_dir) { gpgme_error_t err = 0; engine_gpgconf_t gpgconf; gpgconf = calloc (1, sizeof *gpgconf); if (!gpgconf) return gpg_error_from_syserror (); gpgconf->file_name = strdup (file_name ? file_name : _gpgme_get_default_gpgconf_name ()); if (!gpgconf->file_name) err = gpg_error_from_syserror (); if (!err && home_dir) { gpgconf->home_dir = strdup (home_dir); if (!gpgconf->home_dir) err = gpg_error_from_syserror (); } if (err) gpgconf_release (gpgconf); else *engine = gpgconf; return err; } static void release_arg (gpgme_conf_arg_t arg, gpgme_conf_type_t alt_type) { while (arg) { gpgme_conf_arg_t next = arg->next; if (alt_type == GPGME_CONF_STRING) free (arg->value.string); free (arg); arg = next; } } static void release_opt (gpgme_conf_opt_t opt) { if (opt->name) free (opt->name); if (opt->description) free (opt->description); if (opt->argname) free (opt->argname); release_arg (opt->default_value, opt->alt_type); if (opt->default_description) free (opt->default_description); release_arg (opt->no_arg_value, opt->alt_type); release_arg (opt->value, opt->alt_type); release_arg (opt->new_value, opt->alt_type); free (opt); } static void release_comp (gpgme_conf_comp_t comp) { gpgme_conf_opt_t opt; if (comp->name) free (comp->name); if (comp->description) free (comp->description); if (comp->program_name) free (comp->program_name); opt = comp->options; while (opt) { gpgme_conf_opt_t next = opt->next; release_opt (opt); opt = next; } free (comp); } static void gpgconf_config_release (gpgme_conf_comp_t conf) { while (conf) { gpgme_conf_comp_t next = conf->next; release_comp (conf); conf = next; } } /* Read from gpgconf and pass line after line to the hook function. We put a limit of 64 k on the maximum size for a line. This should allow for quite a long "group" line, which is usually the longest line (mine is currently ~3k). */ static gpgme_error_t gpgconf_read (void *engine, char *arg1, char *arg2, gpgme_error_t (*cb) (void *hook, char *line), void *hook) { struct engine_gpgconf *gpgconf = engine; gpgme_error_t err = 0; char *linebuf; size_t linebufsize; int linelen; char *argv[4] = { NULL /* file_name */, NULL, NULL, NULL }; int rp[2]; struct spawn_fd_item_s cfd[] = { {-1, 1 /* STDOUT_FILENO */, -1, 0}, {-1, -1} }; int status; int nread; char *mark = NULL; argv[1] = arg1; argv[2] = arg2; /* FIXME: Deal with engine->home_dir. */ /* _gpgme_engine_new guarantees that this is not NULL. */ argv[0] = gpgconf->file_name; if (_gpgme_io_pipe (rp, 1) < 0) return gpg_error_from_syserror (); cfd[0].fd = rp[1]; status = _gpgme_io_spawn (gpgconf->file_name, argv, 0, cfd, NULL, NULL, NULL); if (status < 0) { _gpgme_io_close (rp[0]); _gpgme_io_close (rp[1]); return gpg_error_from_syserror (); } linebufsize = 1024; /* Usually enough for conf lines. */ linebuf = malloc (linebufsize); if (!linebuf) { err = gpg_error_from_syserror (); goto leave; } linelen = 0; while ((nread = _gpgme_io_read (rp[0], linebuf + linelen, linebufsize - linelen - 1))) { char *line; const char *lastmark = NULL; size_t nused; if (nread < 0) { err = gpg_error_from_syserror (); goto leave; } linelen += nread; linebuf[linelen] = '\0'; for (line=linebuf; (mark = strchr (line, '\n')); line = mark+1 ) { lastmark = mark; if (mark > line && mark[-1] == '\r') mark[-1] = '\0'; else mark[0] = '\0'; /* Got a full line. Due to the CR removal code (which occurs only on Windows) we might be one-off and thus would see empty lines. Don't pass them to the callback. */ err = *line? (*cb) (hook, line) : 0; if (err) goto leave; } nused = lastmark? (lastmark + 1 - linebuf) : 0; memmove (linebuf, linebuf + nused, linelen - nused); linelen -= nused; if (!(linelen < linebufsize - 1)) { char *newlinebuf; if (linelen < 8 * 1024 - 1) linebufsize = 8 * 1024; else if (linelen < 64 * 1024 - 1) linebufsize = 64 * 1024; else { /* We reached our limit - give up. */ err = gpg_error (GPG_ERR_LINE_TOO_LONG); goto leave; } newlinebuf = realloc (linebuf, linebufsize); if (!newlinebuf) { err = gpg_error_from_syserror (); goto leave; } linebuf = newlinebuf; } } leave: free (linebuf); _gpgme_io_close (rp[0]); return err; } static gpgme_error_t gpgconf_config_load_cb (void *hook, char *line) { gpgme_conf_comp_t *comp_p = hook; gpgme_conf_comp_t comp = *comp_p; #define NR_FIELDS 16 char *field[NR_FIELDS]; int fields = 0; while (line && fields < NR_FIELDS) { field[fields++] = line; line = strchr (line, ':'); if (line) *(line++) = '\0'; } /* We require at least the first 3 fields. */ if (fields < 2) return trace_gpg_error (GPG_ERR_INV_ENGINE); /* Find the pointer to the new component in the list. */ while (comp && comp->next) comp = comp->next; if (comp) comp_p = &comp->next; comp = calloc (1, sizeof (*comp)); if (!comp) return gpg_error_from_syserror (); /* Prepare return value. */ comp->_last_opt_p = &comp->options; *comp_p = comp; comp->name = strdup (field[0]); if (!comp->name) return gpg_error_from_syserror (); comp->description = strdup (field[1]); if (!comp->description) return gpg_error_from_syserror (); if (fields >= 3) { comp->program_name = strdup (field[2]); if (!comp->program_name) return gpg_error_from_syserror (); } return 0; } static gpgme_error_t gpgconf_parse_option (gpgme_conf_opt_t opt, gpgme_conf_arg_t *arg_p, char *line) { gpgme_error_t err; char *mark; if (!line[0]) return 0; while (line) { gpgme_conf_arg_t arg; mark = strchr (line, ','); if (mark) *mark = '\0'; arg = calloc (1, sizeof (*arg)); if (!arg) return gpg_error_from_syserror (); *arg_p = arg; arg_p = &arg->next; if (*line == '\0') arg->no_arg = 1; else { switch (opt->alt_type) { /* arg->value.count is an alias for arg->value.uint32. */ case GPGME_CONF_NONE: case GPGME_CONF_UINT32: arg->value.uint32 = strtoul (line, NULL, 0); break; case GPGME_CONF_INT32: arg->value.uint32 = strtol (line, NULL, 0); break; case GPGME_CONF_STRING: /* The complex types below are only here to silent the compiler warning. */ case GPGME_CONF_FILENAME: case GPGME_CONF_LDAP_SERVER: case GPGME_CONF_KEY_FPR: case GPGME_CONF_PUB_KEY: case GPGME_CONF_SEC_KEY: case GPGME_CONF_ALIAS_LIST: /* Skip quote character. */ line++; err = _gpgme_decode_percent_string (line, &arg->value.string, 0, 0); if (err) return err; break; } } /* Find beginning of next value. */ if (mark++ && *mark) line = mark; else line = NULL; } return 0; } static gpgme_error_t gpgconf_config_load_cb2 (void *hook, char *line) { gpgme_error_t err; gpgme_conf_comp_t comp = hook; gpgme_conf_opt_t *opt_p = comp->_last_opt_p; gpgme_conf_opt_t opt; #define NR_FIELDS 16 char *field[NR_FIELDS]; int fields = 0; while (line && fields < NR_FIELDS) { field[fields++] = line; line = strchr (line, ':'); if (line) *(line++) = '\0'; } /* We require at least the first 10 fields. */ if (fields < 10) return trace_gpg_error (GPG_ERR_INV_ENGINE); opt = calloc (1, sizeof (*opt)); if (!opt) return gpg_error_from_syserror (); comp->_last_opt_p = &opt->next; *opt_p = opt; if (field[0][0]) { opt->name = strdup (field[0]); if (!opt->name) return gpg_error_from_syserror (); } opt->flags = strtoul (field[1], NULL, 0); opt->level = strtoul (field[2], NULL, 0); if (field[3][0]) { opt->description = strdup (field[3]); if (!opt->description) return gpg_error_from_syserror (); } opt->type = strtoul (field[4], NULL, 0); opt->alt_type = strtoul (field[5], NULL, 0); if (field[6][0]) { opt->argname = strdup (field[6]); if (!opt->argname) return gpg_error_from_syserror (); } if (opt->flags & GPGME_CONF_DEFAULT) { err = gpgconf_parse_option (opt, &opt->default_value, field[7]); if (err) return err; } else if ((opt->flags & GPGME_CONF_DEFAULT_DESC) && field[7][0]) { opt->default_description = strdup (field[7]); if (!opt->default_description) return gpg_error_from_syserror (); } if (opt->flags & GPGME_CONF_NO_ARG_DESC) { opt->no_arg_description = strdup (field[8]); if (!opt->no_arg_description) return gpg_error_from_syserror (); } else { err = gpgconf_parse_option (opt, &opt->no_arg_value, field[8]); if (err) return err; } err = gpgconf_parse_option (opt, &opt->value, field[9]); if (err) return err; return 0; } static gpgme_error_t gpgconf_conf_load (void *engine, gpgme_conf_comp_t *comp_p) { gpgme_error_t err; gpgme_conf_comp_t comp = NULL; gpgme_conf_comp_t cur_comp; *comp_p = NULL; err = gpgconf_read (engine, "--list-components", NULL, gpgconf_config_load_cb, &comp); if (err) { gpgconf_release (comp); return err; } cur_comp = comp; while (!err && cur_comp) { err = gpgconf_read (engine, "--list-options", cur_comp->name, gpgconf_config_load_cb2, cur_comp); cur_comp = cur_comp->next; } if (err) { gpgconf_release (comp); return err; } *comp_p = comp; return 0; } gpgme_error_t _gpgme_conf_arg_new (gpgme_conf_arg_t *arg_p, gpgme_conf_type_t type, const void *value) { gpgme_conf_arg_t arg; arg = calloc (1, sizeof (*arg)); if (!arg) return gpg_error_from_syserror (); if (!value) arg->no_arg = 1; else { /* We need to switch on type here because the alt-type is not yet known. */ switch (type) { case GPGME_CONF_NONE: case GPGME_CONF_UINT32: arg->value.uint32 = *((unsigned int *) value); break; case GPGME_CONF_INT32: arg->value.int32 = *((int *) value); break; case GPGME_CONF_STRING: case GPGME_CONF_FILENAME: case GPGME_CONF_LDAP_SERVER: case GPGME_CONF_KEY_FPR: case GPGME_CONF_PUB_KEY: case GPGME_CONF_SEC_KEY: case GPGME_CONF_ALIAS_LIST: arg->value.string = strdup (value); if (!arg->value.string) { free (arg); return gpg_error_from_syserror (); } break; default: free (arg); return gpg_error (GPG_ERR_INV_VALUE); } } *arg_p = arg; return 0; } void _gpgme_conf_arg_release (gpgme_conf_arg_t arg, gpgme_conf_type_t type) { /* Lacking the alt_type we need to switch on type here. */ switch (type) { case GPGME_CONF_NONE: case GPGME_CONF_UINT32: case GPGME_CONF_INT32: case GPGME_CONF_STRING: default: break; case GPGME_CONF_FILENAME: case GPGME_CONF_LDAP_SERVER: case GPGME_CONF_KEY_FPR: case GPGME_CONF_PUB_KEY: case GPGME_CONF_SEC_KEY: case GPGME_CONF_ALIAS_LIST: type = GPGME_CONF_STRING; break; } release_arg (arg, type); } gpgme_error_t _gpgme_conf_opt_change (gpgme_conf_opt_t opt, int reset, gpgme_conf_arg_t arg) { if (reset) { if (opt->new_value) release_arg (opt->new_value, opt->alt_type); opt->new_value = NULL; opt->change_value = 0; } else { /* Support self-assignment, for example for adding an item to an existing list. */ if (opt->new_value && arg != opt->new_value) release_arg (opt->new_value, opt->alt_type); opt->new_value = arg; opt->change_value = 1; } return 0; } /* FIXME: Major problem: We don't get errors from gpgconf. */ static gpgme_error_t gpgconf_write (void *engine, char *arg1, char *arg2, gpgme_data_t conf) { struct engine_gpgconf *gpgconf = engine; gpgme_error_t err = 0; #define BUFLEN 1024 char buf[BUFLEN]; int buflen = 0; char *argv[] = { NULL /* file_name */, arg1, arg2, 0 }; int rp[2]; struct spawn_fd_item_s cfd[] = { {-1, 0 /* STDIN_FILENO */}, {-1, -1} }; int status; int nwrite; /* FIXME: Deal with engine->home_dir. */ /* _gpgme_engine_new guarantees that this is not NULL. */ argv[0] = gpgconf->file_name; if (_gpgme_io_pipe (rp, 0) < 0) return gpg_error_from_syserror (); cfd[0].fd = rp[0]; status = _gpgme_io_spawn (gpgconf->file_name, argv, 0, cfd, NULL, NULL, NULL); if (status < 0) { _gpgme_io_close (rp[0]); _gpgme_io_close (rp[1]); return gpg_error_from_syserror (); } for (;;) { if (buflen == 0) { do { buflen = gpgme_data_read (conf, buf, BUFLEN); } while (buflen < 0 && errno == EAGAIN); if (buflen < 0) { err = gpg_error_from_syserror (); _gpgme_io_close (rp[1]); return err; } else if (buflen == 0) { /* All is written. */ _gpgme_io_close (rp[1]); return 0; } } do { nwrite = _gpgme_io_write (rp[1], buf, buflen); } while (nwrite < 0 && errno == EAGAIN); if (nwrite > 0) { buflen -= nwrite; if (buflen > 0) memmove (&buf[0], &buf[nwrite], buflen); } else if (nwrite < 0) { _gpgme_io_close (rp[1]); return gpg_error_from_syserror (); } } return 0; } static gpgme_error_t arg_to_data (gpgme_data_t conf, gpgme_conf_opt_t option, gpgme_conf_arg_t arg) { gpgme_error_t err = 0; int amt = 0; char buf[16]; while (amt >= 0 && arg) { switch (option->alt_type) { case GPGME_CONF_NONE: case GPGME_CONF_UINT32: default: snprintf (buf, sizeof (buf), "%u", arg->value.uint32); buf[sizeof (buf) - 1] = '\0'; amt = gpgme_data_write (conf, buf, strlen (buf)); break; case GPGME_CONF_INT32: snprintf (buf, sizeof (buf), "%i", arg->value.uint32); buf[sizeof (buf) - 1] = '\0'; amt = gpgme_data_write (conf, buf, strlen (buf)); break; case GPGME_CONF_STRING: /* The complex types below are only here to silent the compiler warning. */ case GPGME_CONF_FILENAME: case GPGME_CONF_LDAP_SERVER: case GPGME_CONF_KEY_FPR: case GPGME_CONF_PUB_KEY: case GPGME_CONF_SEC_KEY: case GPGME_CONF_ALIAS_LIST: if (arg->value.string) { /* One quote character, and three times to allow for percent escaping. */ char *ptr = arg->value.string; amt = gpgme_data_write (conf, "\"", 1); if (amt < 0) break; while (!err && *ptr) { switch (*ptr) { case '%': amt = gpgme_data_write (conf, "%25", 3); break; case ':': amt = gpgme_data_write (conf, "%3a", 3); break; case ',': amt = gpgme_data_write (conf, "%2c", 3); break; default: amt = gpgme_data_write (conf, ptr, 1); } ptr++; } } break; } if (amt < 0) break; arg = arg->next; /* Comma separator. */ if (arg) amt = gpgme_data_write (conf, ",", 1); } if (amt < 0) return gpg_error_from_syserror (); return 0; } static gpgme_error_t gpgconf_conf_save (void *engine, gpgme_conf_comp_t comp) { gpgme_error_t err; int amt = 0; /* We use a data object to store the new configuration. */ gpgme_data_t conf; gpgme_conf_opt_t option; int something_changed = 0; err = gpgme_data_new (&conf); if (err) return err; option = comp->options; while (!err && amt >= 0 && option) { if (option->change_value) { unsigned int flags = 0; char buf[16]; something_changed = 1; amt = gpgme_data_write (conf, option->name, strlen (option->name)); if (amt >= 0) amt = gpgme_data_write (conf, ":", 1); if (amt < 0) break; if (!option->new_value) flags |= GPGME_CONF_DEFAULT; snprintf (buf, sizeof (buf), "%u", flags); buf[sizeof (buf) - 1] = '\0'; amt = gpgme_data_write (conf, buf, strlen (buf)); if (amt >= 0) amt = gpgme_data_write (conf, ":", 1); if (amt < 0) break; if (option->new_value) { err = arg_to_data (conf, option, option->new_value); if (err) break; } amt = gpgme_data_write (conf, "\n", 1); } option = option->next; } if (!err && amt < 0) err = gpg_error_from_syserror (); if (err || !something_changed) goto bail; err = gpgme_data_seek (conf, 0, SEEK_SET); if (err) goto bail; err = gpgconf_write (engine, "--change-options", comp->name, conf); bail: gpgme_data_release (conf); return err; } static void gpgconf_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs) { /* Nothing to do. */ } /* Currently, we do not use the engine interface for the various operations. */ void _gpgme_conf_release (gpgme_conf_comp_t conf) { gpgconf_config_release (conf); } struct engine_ops _gpgme_engine_ops_gpgconf = { /* Static functions. */ _gpgme_get_default_gpgconf_name, NULL, gpgconf_get_version, gpgconf_get_req_version, gpgconf_new, /* Member functions. */ gpgconf_release, NULL, /* reset */ NULL, /* set_status_handler */ NULL, /* set_command_handler */ NULL, /* set_colon_line_handler */ NULL, /* set_locale */ NULL, /* set_protocol */ NULL, /* decrypt */ NULL, /* decrypt_verify */ NULL, /* delete */ NULL, /* edit */ NULL, /* encrypt */ NULL, /* encrypt_sign */ NULL, /* export */ NULL, /* export_ext */ NULL, /* genkey */ NULL, /* import */ NULL, /* keylist */ NULL, /* keylist_ext */ NULL, /* sign */ NULL, /* trustlist */ NULL, /* verify */ NULL, /* getauditlog */ NULL, /* opassuan_transact */ gpgconf_conf_load, gpgconf_conf_save, gpgconf_set_io_cbs, NULL, /* io_event */ NULL, /* cancel */ NULL, /* cancel_op */ NULL, /* passwd */ NULL /* set_pinentry_mode */ }; diff --git a/src/engine-gpgsm.c b/src/engine-gpgsm.c index cee20e31..24cd34d9 100644 --- a/src/engine-gpgsm.c +++ b/src/engine-gpgsm.c @@ -1,1993 +1,1993 @@ /* engine-gpgsm.c - GpgSM engine. Copyright (C) 2000 Werner Koch (dd9jn) Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2009, 2010 g10 Code GmbH This file is part of GPGME. GPGME 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. GPGME 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 Lesser 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, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #if HAVE_CONFIG_H #include #endif #include #include #ifdef HAVE_SYS_TYPES_H # include #endif #include #ifdef HAVE_UNISTD_H # include #endif #ifdef HAVE_LOCALE_H #include #endif #include /* FIXME */ #include #include "gpgme.h" #include "util.h" #include "ops.h" #include "wait.h" #include "priv-io.h" #include "sema.h" #include "data.h" #include "assuan.h" #include "debug.h" #include "engine-backend.h" typedef struct { int fd; /* FD we talk about. */ int server_fd;/* Server FD for this connection. */ int dir; /* Inbound/Outbound, maybe given implicit? */ void *data; /* Handler-specific data. */ void *tag; /* ID from the user for gpgme_remove_io_callback. */ char server_fd_str[15]; /* Same as SERVER_FD but as a string. We need this because _gpgme_io_fd2str can't be used on a closed descriptor. */ } iocb_data_t; struct engine_gpgsm { assuan_context_t assuan_ctx; int lc_ctype_set; int lc_messages_set; iocb_data_t status_cb; /* Input, output etc are from the servers perspective. */ iocb_data_t input_cb; gpgme_data_t input_helper_data; /* Input helper data object. */ void *input_helper_memory; /* Input helper memory block. */ iocb_data_t output_cb; iocb_data_t message_cb; struct { engine_status_handler_t fnc; void *fnc_value; } status; struct { engine_colon_line_handler_t fnc; void *fnc_value; struct { char *line; int linesize; int linelen; } attic; int any; /* any data line seen */ } colon; gpgme_data_t inline_data; /* Used to collect D lines. */ struct gpgme_io_cbs io_cbs; }; typedef struct engine_gpgsm *engine_gpgsm_t; static void gpgsm_io_event (void *engine, gpgme_event_io_t type, void *type_data); static char * gpgsm_get_version (const char *file_name) { return _gpgme_get_program_version (file_name ? file_name : _gpgme_get_default_gpgsm_name ()); } static const char * gpgsm_get_req_version (void) { - return NEED_GPGSM_VERSION; + return "2.0.4"; } static void close_notify_handler (int fd, void *opaque) { engine_gpgsm_t gpgsm = opaque; assert (fd != -1); if (gpgsm->status_cb.fd == fd) { if (gpgsm->status_cb.tag) (*gpgsm->io_cbs.remove) (gpgsm->status_cb.tag); gpgsm->status_cb.fd = -1; gpgsm->status_cb.tag = NULL; } else if (gpgsm->input_cb.fd == fd) { if (gpgsm->input_cb.tag) (*gpgsm->io_cbs.remove) (gpgsm->input_cb.tag); gpgsm->input_cb.fd = -1; gpgsm->input_cb.tag = NULL; if (gpgsm->input_helper_data) { gpgme_data_release (gpgsm->input_helper_data); gpgsm->input_helper_data = NULL; } if (gpgsm->input_helper_memory) { free (gpgsm->input_helper_memory); gpgsm->input_helper_memory = NULL; } } else if (gpgsm->output_cb.fd == fd) { if (gpgsm->output_cb.tag) (*gpgsm->io_cbs.remove) (gpgsm->output_cb.tag); gpgsm->output_cb.fd = -1; gpgsm->output_cb.tag = NULL; } else if (gpgsm->message_cb.fd == fd) { if (gpgsm->message_cb.tag) (*gpgsm->io_cbs.remove) (gpgsm->message_cb.tag); gpgsm->message_cb.fd = -1; gpgsm->message_cb.tag = NULL; } } /* This is the default inquiry callback. We use it to handle the Pinentry notifications. */ static gpgme_error_t default_inq_cb (engine_gpgsm_t gpgsm, const char *line) { if (!strncmp (line, "PINENTRY_LAUNCHED", 17) && (line[17]==' '||!line[17])) { _gpgme_allow_set_foreground_window ((pid_t)strtoul (line+17, NULL, 10)); } return 0; } static gpgme_error_t gpgsm_cancel (void *engine) { engine_gpgsm_t gpgsm = engine; if (!gpgsm) return gpg_error (GPG_ERR_INV_VALUE); if (gpgsm->status_cb.fd != -1) _gpgme_io_close (gpgsm->status_cb.fd); if (gpgsm->input_cb.fd != -1) _gpgme_io_close (gpgsm->input_cb.fd); if (gpgsm->output_cb.fd != -1) _gpgme_io_close (gpgsm->output_cb.fd); if (gpgsm->message_cb.fd != -1) _gpgme_io_close (gpgsm->message_cb.fd); if (gpgsm->assuan_ctx) { assuan_release (gpgsm->assuan_ctx); gpgsm->assuan_ctx = NULL; } return 0; } static void gpgsm_release (void *engine) { engine_gpgsm_t gpgsm = engine; if (!gpgsm) return; gpgsm_cancel (engine); free (gpgsm->colon.attic.line); free (gpgsm); } static gpgme_error_t gpgsm_new (void **engine, const char *file_name, const char *home_dir) { gpgme_error_t err = 0; engine_gpgsm_t gpgsm; const char *argv[5]; int argc; #if !USE_DESCRIPTOR_PASSING int fds[2]; int child_fds[4]; #endif char *dft_display = NULL; char dft_ttyname[64]; char *dft_ttytype = NULL; char *optstr; gpgsm = calloc (1, sizeof *gpgsm); if (!gpgsm) return gpg_error_from_syserror (); gpgsm->status_cb.fd = -1; gpgsm->status_cb.dir = 1; gpgsm->status_cb.tag = 0; gpgsm->status_cb.data = gpgsm; gpgsm->input_cb.fd = -1; gpgsm->input_cb.dir = 0; gpgsm->input_cb.tag = 0; gpgsm->input_cb.server_fd = -1; *gpgsm->input_cb.server_fd_str = 0; gpgsm->output_cb.fd = -1; gpgsm->output_cb.dir = 1; gpgsm->output_cb.tag = 0; gpgsm->output_cb.server_fd = -1; *gpgsm->output_cb.server_fd_str = 0; gpgsm->message_cb.fd = -1; gpgsm->message_cb.dir = 0; gpgsm->message_cb.tag = 0; gpgsm->message_cb.server_fd = -1; *gpgsm->message_cb.server_fd_str = 0; gpgsm->status.fnc = 0; gpgsm->colon.fnc = 0; gpgsm->colon.attic.line = 0; gpgsm->colon.attic.linesize = 0; gpgsm->colon.attic.linelen = 0; gpgsm->colon.any = 0; gpgsm->inline_data = NULL; gpgsm->io_cbs.add = NULL; gpgsm->io_cbs.add_priv = NULL; gpgsm->io_cbs.remove = NULL; gpgsm->io_cbs.event = NULL; gpgsm->io_cbs.event_priv = NULL; #if !USE_DESCRIPTOR_PASSING if (_gpgme_io_pipe (fds, 0) < 0) { err = gpg_error_from_syserror (); goto leave; } gpgsm->input_cb.fd = fds[1]; gpgsm->input_cb.server_fd = fds[0]; if (_gpgme_io_pipe (fds, 1) < 0) { err = gpg_error_from_syserror (); goto leave; } gpgsm->output_cb.fd = fds[0]; gpgsm->output_cb.server_fd = fds[1]; if (_gpgme_io_pipe (fds, 0) < 0) { err = gpg_error_from_syserror (); goto leave; } gpgsm->message_cb.fd = fds[1]; gpgsm->message_cb.server_fd = fds[0]; child_fds[0] = gpgsm->input_cb.server_fd; child_fds[1] = gpgsm->output_cb.server_fd; child_fds[2] = gpgsm->message_cb.server_fd; child_fds[3] = -1; #endif argc = 0; argv[argc++] = "gpgsm"; if (home_dir) { argv[argc++] = "--homedir"; argv[argc++] = home_dir; } argv[argc++] = "--server"; argv[argc++] = NULL; err = assuan_new_ext (&gpgsm->assuan_ctx, GPG_ERR_SOURCE_GPGME, &_gpgme_assuan_malloc_hooks, _gpgme_assuan_log_cb, NULL); if (err) goto leave; assuan_ctx_set_system_hooks (gpgsm->assuan_ctx, &_gpgme_assuan_system_hooks); #if USE_DESCRIPTOR_PASSING err = assuan_pipe_connect (gpgsm->assuan_ctx, file_name ? file_name : _gpgme_get_default_gpgsm_name (), argv, NULL, NULL, NULL, ASSUAN_PIPE_CONNECT_FDPASSING); #else { assuan_fd_t achild_fds[4]; int i; /* For now... */ for (i = 0; i < 4; i++) achild_fds[i] = (assuan_fd_t) child_fds[i]; err = assuan_pipe_connect (gpgsm->assuan_ctx, file_name ? file_name : _gpgme_get_default_gpgsm_name (), argv, achild_fds, NULL, NULL, 0); /* For now... */ for (i = 0; i < 4; i++) child_fds[i] = (int) achild_fds[i]; } /* On Windows, handles are inserted in the spawned process with DuplicateHandle, and child_fds contains the server-local names for the inserted handles when assuan_pipe_connect returns. */ if (!err) { /* Note: We don't use _gpgme_io_fd2str here. On W32 the returned handles are real W32 system handles, not whatever GPGME uses internally (which may be a system handle, a C library handle or a GLib/Qt channel. Confusing, yes, but remember these are server-local names, so they are not part of GPGME at all. */ snprintf (gpgsm->input_cb.server_fd_str, sizeof gpgsm->input_cb.server_fd_str, "%d", child_fds[0]); snprintf (gpgsm->output_cb.server_fd_str, sizeof gpgsm->output_cb.server_fd_str, "%d", child_fds[1]); snprintf (gpgsm->message_cb.server_fd_str, sizeof gpgsm->message_cb.server_fd_str, "%d", child_fds[2]); } #endif if (err) goto leave; err = _gpgme_getenv ("DISPLAY", &dft_display); if (err) goto leave; if (dft_display) { if (asprintf (&optstr, "OPTION display=%s", dft_display) < 0) { free (dft_display); err = gpg_error_from_syserror (); goto leave; } free (dft_display); err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL, NULL, NULL, NULL); free (optstr); if (err) goto leave; } if (isatty (1)) { int rc; rc = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname)); if (rc) { err = gpg_error_from_errno (rc); goto leave; } else { if (asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0) { err = gpg_error_from_syserror (); goto leave; } err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL, NULL, NULL, NULL); free (optstr); if (err) goto leave; err = _gpgme_getenv ("TERM", &dft_ttytype); if (err) goto leave; if (dft_ttytype) { if (asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype) < 0) { free (dft_ttytype); err = gpg_error_from_syserror (); goto leave; } free (dft_ttytype); err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL, NULL, NULL, NULL); free (optstr); if (err) goto leave; } } } /* Ask gpgsm to enable the audit log support. */ if (!err) { err = assuan_transact (gpgsm->assuan_ctx, "OPTION enable-audit-log=1", NULL, NULL, NULL, NULL, NULL, NULL); if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION) err = 0; /* This is an optional feature of gpgsm. */ } #ifdef HAVE_W32_SYSTEM /* Under Windows we need to use AllowSetForegroundWindow. Tell gpgsm to tell us when it needs it. */ if (!err) { err = assuan_transact (gpgsm->assuan_ctx, "OPTION allow-pinentry-notify", NULL, NULL, NULL, NULL, NULL, NULL); if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION) err = 0; /* This is a new feature of gpgsm. */ } #endif /*HAVE_W32_SYSTEM*/ #if !USE_DESCRIPTOR_PASSING if (!err && (_gpgme_io_set_close_notify (gpgsm->input_cb.fd, close_notify_handler, gpgsm) || _gpgme_io_set_close_notify (gpgsm->output_cb.fd, close_notify_handler, gpgsm) || _gpgme_io_set_close_notify (gpgsm->message_cb.fd, close_notify_handler, gpgsm))) { err = gpg_error (GPG_ERR_GENERAL); goto leave; } #endif leave: /* Close the server ends of the pipes (because of this, we must use the stored server_fd_str in the function start). Our ends are closed in gpgsm_release(). */ #if !USE_DESCRIPTOR_PASSING if (gpgsm->input_cb.server_fd != -1) _gpgme_io_close (gpgsm->input_cb.server_fd); if (gpgsm->output_cb.server_fd != -1) _gpgme_io_close (gpgsm->output_cb.server_fd); if (gpgsm->message_cb.server_fd != -1) _gpgme_io_close (gpgsm->message_cb.server_fd); #endif if (err) gpgsm_release (gpgsm); else *engine = gpgsm; return err; } static gpgme_error_t gpgsm_set_locale (void *engine, int category, const char *value) { engine_gpgsm_t gpgsm = engine; gpgme_error_t err; char *optstr; char *catstr; /* FIXME: If value is NULL, we need to reset the option to default. But we can't do this. So we error out here. GPGSM needs support for this. */ if (0) ; #ifdef LC_CTYPE else if (category == LC_CTYPE) { catstr = "lc-ctype"; if (!value && gpgsm->lc_ctype_set) return gpg_error (GPG_ERR_INV_VALUE); if (value) gpgsm->lc_ctype_set = 1; } #endif #ifdef LC_MESSAGES else if (category == LC_MESSAGES) { catstr = "lc-messages"; if (!value && gpgsm->lc_messages_set) return gpg_error (GPG_ERR_INV_VALUE); if (value) gpgsm->lc_messages_set = 1; } #endif /* LC_MESSAGES */ else return gpg_error (GPG_ERR_INV_VALUE); /* FIXME: Reset value to default. */ if (!value) return 0; if (asprintf (&optstr, "OPTION %s=%s", catstr, value) < 0) err = gpg_error_from_syserror (); else { err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL, NULL, NULL, NULL); free (optstr); } return err; } static gpgme_error_t gpgsm_assuan_simple_command (assuan_context_t ctx, char *cmd, engine_status_handler_t status_fnc, void *status_fnc_value) { gpg_error_t err; char *line; size_t linelen; err = assuan_write_line (ctx, cmd); if (err) return err; do { err = assuan_read_line (ctx, &line, &linelen); if (err) return err; if (*line == '#' || !linelen) continue; if (linelen >= 2 && line[0] == 'O' && line[1] == 'K' && (line[2] == '\0' || line[2] == ' ')) return 0; else if (linelen >= 4 && line[0] == 'E' && line[1] == 'R' && line[2] == 'R' && line[3] == ' ') err = atoi (&line[4]); else if (linelen >= 2 && line[0] == 'S' && line[1] == ' ') { char *rest; gpgme_status_code_t r; rest = strchr (line + 2, ' '); if (!rest) rest = line + linelen; /* set to an empty string */ else *(rest++) = 0; r = _gpgme_parse_status (line + 2); if (r >= 0 && status_fnc) err = status_fnc (status_fnc_value, r, rest); else err = gpg_error (GPG_ERR_GENERAL); } else err = gpg_error (GPG_ERR_GENERAL); } while (!err); return err; } typedef enum { INPUT_FD, OUTPUT_FD, MESSAGE_FD } fd_type_t; static void gpgsm_clear_fd (engine_gpgsm_t gpgsm, fd_type_t fd_type) { #if !USE_DESCRIPTOR_PASSING switch (fd_type) { case INPUT_FD: _gpgme_io_close (gpgsm->input_cb.fd); break; case OUTPUT_FD: _gpgme_io_close (gpgsm->output_cb.fd); break; case MESSAGE_FD: _gpgme_io_close (gpgsm->message_cb.fd); break; } #endif } #define COMMANDLINELEN 40 static gpgme_error_t gpgsm_set_fd (engine_gpgsm_t gpgsm, fd_type_t fd_type, const char *opt) { gpg_error_t err = 0; char line[COMMANDLINELEN]; char *which; iocb_data_t *iocb_data; #if USE_DESCRIPTOR_PASSING int dir; #endif switch (fd_type) { case INPUT_FD: which = "INPUT"; iocb_data = &gpgsm->input_cb; break; case OUTPUT_FD: which = "OUTPUT"; iocb_data = &gpgsm->output_cb; break; case MESSAGE_FD: which = "MESSAGE"; iocb_data = &gpgsm->message_cb; break; default: return gpg_error (GPG_ERR_INV_VALUE); } #if USE_DESCRIPTOR_PASSING dir = iocb_data->dir; /* We try to short-cut the communication by giving GPGSM direct access to the file descriptor, rather than using a pipe. */ iocb_data->server_fd = _gpgme_data_get_fd (iocb_data->data); if (iocb_data->server_fd < 0) { int fds[2]; if (_gpgme_io_pipe (fds, dir) < 0) return gpg_error_from_syserror (); iocb_data->fd = dir ? fds[0] : fds[1]; iocb_data->server_fd = dir ? fds[1] : fds[0]; if (_gpgme_io_set_close_notify (iocb_data->fd, close_notify_handler, gpgsm)) { err = gpg_error (GPG_ERR_GENERAL); goto leave_set_fd; } } err = assuan_sendfd (gpgsm->assuan_ctx, iocb_data->server_fd); if (err) goto leave_set_fd; _gpgme_io_close (iocb_data->server_fd); iocb_data->server_fd = -1; if (opt) snprintf (line, COMMANDLINELEN, "%s FD %s", which, opt); else snprintf (line, COMMANDLINELEN, "%s FD", which); #else if (opt) snprintf (line, COMMANDLINELEN, "%s FD=%s %s", which, iocb_data->server_fd_str, opt); else snprintf (line, COMMANDLINELEN, "%s FD=%s", which, iocb_data->server_fd_str); #endif err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, line, NULL, NULL); #if USE_DESCRIPTOR_PASSING leave_set_fd: if (err) { _gpgme_io_close (iocb_data->fd); iocb_data->fd = -1; if (iocb_data->server_fd != -1) { _gpgme_io_close (iocb_data->server_fd); iocb_data->server_fd = -1; } } #endif return err; } static const char * map_data_enc (gpgme_data_t d) { switch (gpgme_data_get_encoding (d)) { case GPGME_DATA_ENCODING_NONE: break; case GPGME_DATA_ENCODING_BINARY: return "--binary"; case GPGME_DATA_ENCODING_BASE64: return "--base64"; case GPGME_DATA_ENCODING_ARMOR: return "--armor"; default: break; } return NULL; } static gpgme_error_t status_handler (void *opaque, int fd) { struct io_cb_data *data = (struct io_cb_data *) opaque; engine_gpgsm_t gpgsm = (engine_gpgsm_t) data->handler_value; gpgme_error_t err = 0; char *line; size_t linelen; do { err = assuan_read_line (gpgsm->assuan_ctx, &line, &linelen); if (err) { /* Try our best to terminate the connection friendly. */ /* assuan_write_line (gpgsm->assuan_ctx, "BYE"); */ TRACE3 (DEBUG_CTX, "gpgme:status_handler", gpgsm, "fd 0x%x: error from assuan (%d) getting status line : %s", fd, err, gpg_strerror (err)); } else if (linelen >= 3 && line[0] == 'E' && line[1] == 'R' && line[2] == 'R' && (line[3] == '\0' || line[3] == ' ')) { if (line[3] == ' ') err = atoi (&line[4]); if (! err) err = gpg_error (GPG_ERR_GENERAL); TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm, "fd 0x%x: ERR line - mapped to: %s", fd, err ? gpg_strerror (err) : "ok"); /* Try our best to terminate the connection friendly. */ /* assuan_write_line (gpgsm->assuan_ctx, "BYE"); */ } else if (linelen >= 2 && line[0] == 'O' && line[1] == 'K' && (line[2] == '\0' || line[2] == ' ')) { if (gpgsm->status.fnc) err = gpgsm->status.fnc (gpgsm->status.fnc_value, GPGME_STATUS_EOF, ""); if (!err && gpgsm->colon.fnc && gpgsm->colon.any) { /* We must tell a colon function about the EOF. We do this only when we have seen any data lines. Note that this inlined use of colon data lines will eventually be changed into using a regular data channel. */ gpgsm->colon.any = 0; err = gpgsm->colon.fnc (gpgsm->colon.fnc_value, NULL); } TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm, "fd 0x%x: OK line - final status: %s", fd, err ? gpg_strerror (err) : "ok"); _gpgme_io_close (gpgsm->status_cb.fd); return err; } else if (linelen > 2 && line[0] == 'D' && line[1] == ' ' && gpgsm->colon.fnc) { /* We are using the colon handler even for plain inline data - strange name for that function but for historic reasons we keep it. */ /* FIXME We can't use this for binary data because we assume this is a string. For the current usage of colon output it is correct. */ char *src = line + 2; char *end = line + linelen; char *dst; char **aline = &gpgsm->colon.attic.line; int *alinelen = &gpgsm->colon.attic.linelen; if (gpgsm->colon.attic.linesize < *alinelen + linelen + 1) { char *newline = realloc (*aline, *alinelen + linelen + 1); if (!newline) err = gpg_error_from_syserror (); else { *aline = newline; gpgsm->colon.attic.linesize += linelen + 1; } } if (!err) { dst = *aline + *alinelen; while (!err && src < end) { if (*src == '%' && src + 2 < end) { /* Handle escaped characters. */ ++src; *dst = _gpgme_hextobyte (src); (*alinelen)++; src += 2; } else { *dst = *src++; (*alinelen)++; } if (*dst == '\n') { /* Terminate the pending line, pass it to the colon handler and reset it. */ gpgsm->colon.any = 1; if (*alinelen > 1 && *(dst - 1) == '\r') dst--; *dst = '\0'; /* FIXME How should we handle the return code? */ err = gpgsm->colon.fnc (gpgsm->colon.fnc_value, *aline); if (!err) { dst = *aline; *alinelen = 0; } } else dst++; } } TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm, "fd 0x%x: D line; final status: %s", fd, err? gpg_strerror (err):"ok"); } else if (linelen > 2 && line[0] == 'D' && line[1] == ' ' && gpgsm->inline_data) { char *src = line + 2; char *end = line + linelen; char *dst = src; gpgme_ssize_t nwritten; linelen = 0; while (src < end) { if (*src == '%' && src + 2 < end) { /* Handle escaped characters. */ ++src; *dst++ = _gpgme_hextobyte (src); src += 2; } else *dst++ = *src++; linelen++; } src = line + 2; while (linelen > 0) { nwritten = gpgme_data_write (gpgsm->inline_data, src, linelen); if (!nwritten || (nwritten < 0 && errno != EINTR) || nwritten > linelen) { err = gpg_error_from_syserror (); break; } src += nwritten; linelen -= nwritten; } TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm, "fd 0x%x: D inlinedata; final status: %s", fd, err? gpg_strerror (err):"ok"); } else if (linelen > 2 && line[0] == 'S' && line[1] == ' ') { char *rest; gpgme_status_code_t r; rest = strchr (line + 2, ' '); if (!rest) rest = line + linelen; /* set to an empty string */ else *(rest++) = 0; r = _gpgme_parse_status (line + 2); if (r >= 0) { if (gpgsm->status.fnc) err = gpgsm->status.fnc (gpgsm->status.fnc_value, r, rest); } else fprintf (stderr, "[UNKNOWN STATUS]%s %s", line + 2, rest); TRACE3 (DEBUG_CTX, "gpgme:status_handler", gpgsm, "fd 0x%x: S line (%s) - final status: %s", fd, line+2, err? gpg_strerror (err):"ok"); } else if (linelen >= 7 && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q' && line[3] == 'U' && line[4] == 'I' && line[5] == 'R' && line[6] == 'E' && (line[7] == '\0' || line[7] == ' ')) { char *keyword = line+7; while (*keyword == ' ') keyword++;; default_inq_cb (gpgsm, keyword); assuan_write_line (gpgsm->assuan_ctx, "END"); } } while (!err && assuan_pending_line (gpgsm->assuan_ctx)); return err; } static gpgme_error_t add_io_cb (engine_gpgsm_t gpgsm, iocb_data_t *iocbd, gpgme_io_cb_t handler) { gpgme_error_t err; TRACE_BEG2 (DEBUG_ENGINE, "engine-gpgsm:add_io_cb", gpgsm, "fd %d, dir %d", iocbd->fd, iocbd->dir); err = (*gpgsm->io_cbs.add) (gpgsm->io_cbs.add_priv, iocbd->fd, iocbd->dir, handler, iocbd->data, &iocbd->tag); if (err) return TRACE_ERR (err); if (!iocbd->dir) /* FIXME Kludge around poll() problem. */ err = _gpgme_io_set_nonblocking (iocbd->fd); return TRACE_ERR (err); } static gpgme_error_t start (engine_gpgsm_t gpgsm, const char *command) { gpgme_error_t err; assuan_fd_t afdlist[5]; int fdlist[5]; int nfds; int i; /* We need to know the fd used by assuan for reads. We do this by using the assumption that the first returned fd from assuan_get_active_fds() is always this one. */ nfds = assuan_get_active_fds (gpgsm->assuan_ctx, 0 /* read fds */, afdlist, DIM (afdlist)); if (nfds < 1) return gpg_error (GPG_ERR_GENERAL); /* FIXME */ /* For now... */ for (i = 0; i < nfds; i++) fdlist[i] = (int) afdlist[i]; /* We "duplicate" the file descriptor, so we can close it here (we can't close fdlist[0], as that is closed by libassuan, and closing it here might cause libassuan to close some unrelated FD later). Alternatively, we could special case status_fd and register/unregister it manually as needed, but this increases code duplication and is more complicated as we can not use the close notifications etc. A third alternative would be to let Assuan know that we closed the FD, but that complicates the Assuan interface. */ gpgsm->status_cb.fd = _gpgme_io_dup (fdlist[0]); if (gpgsm->status_cb.fd < 0) return gpg_error_from_syserror (); if (_gpgme_io_set_close_notify (gpgsm->status_cb.fd, close_notify_handler, gpgsm)) { _gpgme_io_close (gpgsm->status_cb.fd); gpgsm->status_cb.fd = -1; return gpg_error (GPG_ERR_GENERAL); } err = add_io_cb (gpgsm, &gpgsm->status_cb, status_handler); if (!err && gpgsm->input_cb.fd != -1) err = add_io_cb (gpgsm, &gpgsm->input_cb, _gpgme_data_outbound_handler); if (!err && gpgsm->output_cb.fd != -1) err = add_io_cb (gpgsm, &gpgsm->output_cb, _gpgme_data_inbound_handler); if (!err && gpgsm->message_cb.fd != -1) err = add_io_cb (gpgsm, &gpgsm->message_cb, _gpgme_data_outbound_handler); if (!err) err = assuan_write_line (gpgsm->assuan_ctx, command); if (!err) gpgsm_io_event (gpgsm, GPGME_EVENT_START, NULL); return err; } #if USE_DESCRIPTOR_PASSING static gpgme_error_t gpgsm_reset (void *engine) { engine_gpgsm_t gpgsm = engine; /* IF we have an active connection we must send a reset because we need to reset the list of signers. Note that RESET does not reset OPTION commands. */ return (gpgsm->assuan_ctx ? gpgsm_assuan_simple_command (gpgsm->assuan_ctx, "RESET", NULL, NULL) : 0); } #endif static gpgme_error_t gpgsm_decrypt (void *engine, gpgme_data_t ciph, gpgme_data_t plain) { engine_gpgsm_t gpgsm = engine; gpgme_error_t err; if (!gpgsm) return gpg_error (GPG_ERR_INV_VALUE); gpgsm->input_cb.data = ciph; err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data)); if (err) return gpg_error (GPG_ERR_GENERAL); /* FIXME */ gpgsm->output_cb.data = plain; err = gpgsm_set_fd (gpgsm, OUTPUT_FD, 0); if (err) return gpg_error (GPG_ERR_GENERAL); /* FIXME */ gpgsm_clear_fd (gpgsm, MESSAGE_FD); gpgsm->inline_data = NULL; err = start (engine, "DECRYPT"); return err; } static gpgme_error_t gpgsm_delete (void *engine, gpgme_key_t key, int allow_secret) { engine_gpgsm_t gpgsm = engine; gpgme_error_t err; char *fpr = key->subkeys ? key->subkeys->fpr : NULL; char *linep = fpr; char *line; int length = 8; /* "DELKEYS " */ if (!fpr) return gpg_error (GPG_ERR_INV_VALUE); while (*linep) { length++; if (*linep == '%' || *linep == ' ' || *linep == '+') length += 2; linep++; } length++; line = malloc (length); if (!line) return gpg_error_from_syserror (); strcpy (line, "DELKEYS "); linep = &line[8]; while (*fpr) { switch (*fpr) { case '%': *(linep++) = '%'; *(linep++) = '2'; *(linep++) = '5'; break; case ' ': *(linep++) = '%'; *(linep++) = '2'; *(linep++) = '0'; break; case '+': *(linep++) = '%'; *(linep++) = '2'; *(linep++) = 'B'; break; default: *(linep++) = *fpr; break; } fpr++; } *linep = '\0'; gpgsm_clear_fd (gpgsm, OUTPUT_FD); gpgsm_clear_fd (gpgsm, INPUT_FD); gpgsm_clear_fd (gpgsm, MESSAGE_FD); gpgsm->inline_data = NULL; err = start (gpgsm, line); free (line); return err; } static gpgme_error_t set_recipients (engine_gpgsm_t gpgsm, gpgme_key_t recp[]) { gpgme_error_t err = 0; assuan_context_t ctx = gpgsm->assuan_ctx; char *line; int linelen; int invalid_recipients = 0; int i; linelen = 10 + 40 + 1; /* "RECIPIENT " + guess + '\0'. */ line = malloc (10 + 40 + 1); if (!line) return gpg_error_from_syserror (); strcpy (line, "RECIPIENT "); for (i =0; !err && recp[i]; i++) { char *fpr; int newlen; if (!recp[i]->subkeys || !recp[i]->subkeys->fpr) { invalid_recipients++; continue; } fpr = recp[i]->subkeys->fpr; newlen = 11 + strlen (fpr); if (linelen < newlen) { char *newline = realloc (line, newlen); if (! newline) { int saved_err = gpg_error_from_syserror (); free (line); return saved_err; } line = newline; linelen = newlen; } strcpy (&line[10], fpr); err = gpgsm_assuan_simple_command (ctx, line, gpgsm->status.fnc, gpgsm->status.fnc_value); /* FIXME: This requires more work. */ if (gpg_err_code (err) == GPG_ERR_NO_PUBKEY) invalid_recipients++; else if (err) { free (line); return err; } } free (line); return gpg_error (invalid_recipients ? GPG_ERR_UNUSABLE_PUBKEY : GPG_ERR_NO_ERROR); } static gpgme_error_t gpgsm_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t ciph, int use_armor) { engine_gpgsm_t gpgsm = engine; gpgme_error_t err; if (!gpgsm) return gpg_error (GPG_ERR_INV_VALUE); if (!recp) return gpg_error (GPG_ERR_NOT_IMPLEMENTED); if (flags & GPGME_ENCRYPT_NO_ENCRYPT_TO) { err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, "OPTION no-encrypt-to", NULL, NULL); if (err) return err; } gpgsm->input_cb.data = plain; err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data)); if (err) return err; gpgsm->output_cb.data = ciph; err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor" : map_data_enc (gpgsm->output_cb.data)); if (err) return err; gpgsm_clear_fd (gpgsm, MESSAGE_FD); gpgsm->inline_data = NULL; err = set_recipients (gpgsm, recp); if (!err) err = start (gpgsm, "ENCRYPT"); return err; } static gpgme_error_t gpgsm_export (void *engine, const char *pattern, gpgme_export_mode_t mode, gpgme_data_t keydata, int use_armor) { engine_gpgsm_t gpgsm = engine; gpgme_error_t err = 0; char *cmd; if (!gpgsm) return gpg_error (GPG_ERR_INV_VALUE); if (mode) return gpg_error (GPG_ERR_NOT_SUPPORTED); if (!pattern) pattern = ""; cmd = malloc (7 + strlen (pattern) + 1); if (!cmd) return gpg_error_from_syserror (); strcpy (cmd, "EXPORT "); strcpy (&cmd[7], pattern); gpgsm->output_cb.data = keydata; err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor" : map_data_enc (gpgsm->output_cb.data)); if (err) return err; gpgsm_clear_fd (gpgsm, INPUT_FD); gpgsm_clear_fd (gpgsm, MESSAGE_FD); gpgsm->inline_data = NULL; err = start (gpgsm, cmd); free (cmd); return err; } static gpgme_error_t gpgsm_export_ext (void *engine, const char *pattern[], gpgme_export_mode_t mode, gpgme_data_t keydata, int use_armor) { engine_gpgsm_t gpgsm = engine; gpgme_error_t err = 0; char *line; /* Length is "EXPORT " + p + '\0'. */ int length = 7 + 1; char *linep; if (!gpgsm) return gpg_error (GPG_ERR_INV_VALUE); if (mode) return gpg_error (GPG_ERR_NOT_SUPPORTED); if (pattern && *pattern) { const char **pat = pattern; while (*pat) { const char *patlet = *pat; while (*patlet) { length++; if (*patlet == '%' || *patlet == ' ' || *patlet == '+') length += 2; patlet++; } pat++; length++; } } line = malloc (length); if (!line) return gpg_error_from_syserror (); strcpy (line, "EXPORT "); linep = &line[7]; if (pattern && *pattern) { while (*pattern) { const char *patlet = *pattern; while (*patlet) { switch (*patlet) { case '%': *(linep++) = '%'; *(linep++) = '2'; *(linep++) = '5'; break; case ' ': *(linep++) = '%'; *(linep++) = '2'; *(linep++) = '0'; break; case '+': *(linep++) = '%'; *(linep++) = '2'; *(linep++) = 'B'; break; default: *(linep++) = *patlet; break; } patlet++; } pattern++; if (*pattern) *linep++ = ' '; } } *linep = '\0'; gpgsm->output_cb.data = keydata; err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor" : map_data_enc (gpgsm->output_cb.data)); if (err) return err; gpgsm_clear_fd (gpgsm, INPUT_FD); gpgsm_clear_fd (gpgsm, MESSAGE_FD); gpgsm->inline_data = NULL; err = start (gpgsm, line); free (line); return err; } static gpgme_error_t gpgsm_genkey (void *engine, gpgme_data_t help_data, int use_armor, gpgme_data_t pubkey, gpgme_data_t seckey) { engine_gpgsm_t gpgsm = engine; gpgme_error_t err; if (!gpgsm || !pubkey || seckey) return gpg_error (GPG_ERR_INV_VALUE); gpgsm->input_cb.data = help_data; err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data)); if (err) return err; gpgsm->output_cb.data = pubkey; err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor" : map_data_enc (gpgsm->output_cb.data)); if (err) return err; gpgsm_clear_fd (gpgsm, MESSAGE_FD); gpgsm->inline_data = NULL; err = start (gpgsm, "GENKEY"); return err; } static gpgme_error_t gpgsm_import (void *engine, gpgme_data_t keydata, gpgme_key_t *keyarray) { engine_gpgsm_t gpgsm = engine; gpgme_error_t err; gpgme_data_encoding_t dataenc; int idx; if (!gpgsm) return gpg_error (GPG_ERR_INV_VALUE); if (keydata && keyarray) return gpg_error (GPG_ERR_INV_VALUE); /* Only one is allowed. */ dataenc = gpgme_data_get_encoding (keydata); if (keyarray) { size_t buflen; char *buffer, *p; /* Fist check whether the engine already features the --re-import option. */ err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, "GETINFO cmd_has_option IMPORT re-import", NULL, NULL); if (err) return gpg_error (GPG_ERR_NOT_SUPPORTED); /* Create an internal data object with a list of all fingerprints. The data object and its memory (to avoid an extra copy by gpgme_data_new_from_mem) are stored in two variables which are released by the close_notify_handler. */ for (idx=0, buflen=0; keyarray[idx]; idx++) { if (keyarray[idx]->protocol == GPGME_PROTOCOL_CMS && keyarray[idx]->subkeys && keyarray[idx]->subkeys->fpr && *keyarray[idx]->subkeys->fpr) buflen += strlen (keyarray[idx]->subkeys->fpr) + 1; } /* Allocate a bufer with extra space for the trailing Nul introduced by the use of stpcpy. */ buffer = malloc (buflen+1); if (!buffer) return gpg_error_from_syserror (); for (idx=0, p = buffer; keyarray[idx]; idx++) { if (keyarray[idx]->protocol == GPGME_PROTOCOL_CMS && keyarray[idx]->subkeys && keyarray[idx]->subkeys->fpr && *keyarray[idx]->subkeys->fpr) p = stpcpy (stpcpy (p, keyarray[idx]->subkeys->fpr), "\n"); } err = gpgme_data_new_from_mem (&gpgsm->input_helper_data, buffer, buflen, 0); if (err) { free (buffer); return err; } gpgsm->input_helper_memory = buffer; gpgsm->input_cb.data = gpgsm->input_helper_data; err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data)); if (err) { gpgme_data_release (gpgsm->input_helper_data); gpgsm->input_helper_data = NULL; free (gpgsm->input_helper_memory); gpgsm->input_helper_memory = NULL; return err; } gpgsm_clear_fd (gpgsm, OUTPUT_FD); gpgsm_clear_fd (gpgsm, MESSAGE_FD); gpgsm->inline_data = NULL; return start (gpgsm, "IMPORT --re-import"); } else if (dataenc == GPGME_DATA_ENCODING_URL || dataenc == GPGME_DATA_ENCODING_URL0 || dataenc == GPGME_DATA_ENCODING_URLESC) { return gpg_error (GPG_ERR_NOT_IMPLEMENTED); } else { gpgsm->input_cb.data = keydata; err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data)); if (err) return err; gpgsm_clear_fd (gpgsm, OUTPUT_FD); gpgsm_clear_fd (gpgsm, MESSAGE_FD); gpgsm->inline_data = NULL; return start (gpgsm, "IMPORT"); } } static gpgme_error_t gpgsm_keylist (void *engine, const char *pattern, int secret_only, gpgme_keylist_mode_t mode) { engine_gpgsm_t gpgsm = engine; char *line; gpgme_error_t err; int list_mode = 0; if (mode & GPGME_KEYLIST_MODE_LOCAL) list_mode |= 1; if (mode & GPGME_KEYLIST_MODE_EXTERN) list_mode |= 2; if (!pattern) pattern = ""; /* Hack to make sure that the agent is started. Only if the agent has been started an application may connect to the agent via GPGME_PROTOCOL_ASSUAN - for example to look for smartcards. We do this only if a secret key listing has been requested. In general this is not needed because a secret key listing starts the agent. However on a fresh installation no public keys are available and thus there is no need for gpgsm to ask the agent whether a secret key exists for the public key. */ if (secret_only) gpgsm_assuan_simple_command (gpgsm->assuan_ctx, "GETINFO agent-check", NULL, NULL); /* Always send list-mode option because RESET does not reset it. */ if (asprintf (&line, "OPTION list-mode=%d", (list_mode & 3)) < 0) return gpg_error_from_syserror (); err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, line, NULL, NULL); free (line); if (err) return err; /* Always send key validation because RESET does not reset it. */ /* Use the validation mode if requested. We don't check for an error yet because this is a pretty fresh gpgsm features. */ gpgsm_assuan_simple_command (gpgsm->assuan_ctx, (mode & GPGME_KEYLIST_MODE_VALIDATE)? "OPTION with-validation=1": "OPTION with-validation=0" , NULL, NULL); /* Include the ephemeral keys if requested. We don't check for an error yet because this is a pretty fresh gpgsm features. */ gpgsm_assuan_simple_command (gpgsm->assuan_ctx, (mode & GPGME_KEYLIST_MODE_EPHEMERAL)? "OPTION with-ephemeral-keys=1": "OPTION with-ephemeral-keys=0" , NULL, NULL); /* Length is "LISTSECRETKEYS " + p + '\0'. */ line = malloc (15 + strlen (pattern) + 1); if (!line) return gpg_error_from_syserror (); if (secret_only) { strcpy (line, "LISTSECRETKEYS "); strcpy (&line[15], pattern); } else { strcpy (line, "LISTKEYS "); strcpy (&line[9], pattern); } gpgsm_clear_fd (gpgsm, INPUT_FD); gpgsm_clear_fd (gpgsm, OUTPUT_FD); gpgsm_clear_fd (gpgsm, MESSAGE_FD); gpgsm->inline_data = NULL; err = start (gpgsm, line); free (line); return err; } static gpgme_error_t gpgsm_keylist_ext (void *engine, const char *pattern[], int secret_only, int reserved, gpgme_keylist_mode_t mode) { engine_gpgsm_t gpgsm = engine; char *line; gpgme_error_t err; /* Length is "LISTSECRETKEYS " + p + '\0'. */ int length = 15 + 1; char *linep; int any_pattern = 0; int list_mode = 0; if (reserved) return gpg_error (GPG_ERR_INV_VALUE); if (mode & GPGME_KEYLIST_MODE_LOCAL) list_mode |= 1; if (mode & GPGME_KEYLIST_MODE_EXTERN) list_mode |= 2; /* Always send list-mode option because RESET does not reset it. */ if (asprintf (&line, "OPTION list-mode=%d", (list_mode & 3)) < 0) return gpg_error_from_syserror (); err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, line, NULL, NULL); free (line); if (err) return err; /* Always send key validation because RESET does not reset it. */ /* Use the validation mode if required. We don't check for an error yet because this is a pretty fresh gpgsm features. */ gpgsm_assuan_simple_command (gpgsm->assuan_ctx, (mode & GPGME_KEYLIST_MODE_VALIDATE)? "OPTION with-validation=1": "OPTION with-validation=0" , NULL, NULL); if (pattern && *pattern) { const char **pat = pattern; while (*pat) { const char *patlet = *pat; while (*patlet) { length++; if (*patlet == '%' || *patlet == ' ' || *patlet == '+') length += 2; patlet++; } pat++; length++; } } line = malloc (length); if (!line) return gpg_error_from_syserror (); if (secret_only) { strcpy (line, "LISTSECRETKEYS "); linep = &line[15]; } else { strcpy (line, "LISTKEYS "); linep = &line[9]; } if (pattern && *pattern) { while (*pattern) { const char *patlet = *pattern; while (*patlet) { switch (*patlet) { case '%': *(linep++) = '%'; *(linep++) = '2'; *(linep++) = '5'; break; case ' ': *(linep++) = '%'; *(linep++) = '2'; *(linep++) = '0'; break; case '+': *(linep++) = '%'; *(linep++) = '2'; *(linep++) = 'B'; break; default: *(linep++) = *patlet; break; } patlet++; } any_pattern = 1; *linep++ = ' '; pattern++; } } if (any_pattern) linep--; *linep = '\0'; gpgsm_clear_fd (gpgsm, INPUT_FD); gpgsm_clear_fd (gpgsm, OUTPUT_FD); gpgsm_clear_fd (gpgsm, MESSAGE_FD); gpgsm->inline_data = NULL; err = start (gpgsm, line); free (line); return err; } static gpgme_error_t gpgsm_sign (void *engine, gpgme_data_t in, gpgme_data_t out, gpgme_sig_mode_t mode, int use_armor, int use_textmode, int include_certs, gpgme_ctx_t ctx /* FIXME */) { engine_gpgsm_t gpgsm = engine; gpgme_error_t err; char *assuan_cmd; int i; gpgme_key_t key; if (!gpgsm) return gpg_error (GPG_ERR_INV_VALUE); /* FIXME: This does not work as RESET does not reset it so we can't revert back to default. */ if (include_certs != GPGME_INCLUDE_CERTS_DEFAULT) { /* FIXME: Make sure that if we run multiple operations, that we can reset any previously set value in case the default is requested. */ if (asprintf (&assuan_cmd, "OPTION include-certs %i", include_certs) < 0) return gpg_error_from_syserror (); err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, assuan_cmd, NULL, NULL); free (assuan_cmd); if (err) return err; } for (i = 0; (key = gpgme_signers_enum (ctx, i)); i++) { const char *s = key->subkeys ? key->subkeys->fpr : NULL; if (s && strlen (s) < 80) { char buf[100]; strcpy (stpcpy (buf, "SIGNER "), s); err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, buf, gpgsm->status.fnc, gpgsm->status.fnc_value); } else err = gpg_error (GPG_ERR_INV_VALUE); gpgme_key_unref (key); if (err) return err; } gpgsm->input_cb.data = in; err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data)); if (err) return err; gpgsm->output_cb.data = out; err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor" : map_data_enc (gpgsm->output_cb.data)); if (err) return err; gpgsm_clear_fd (gpgsm, MESSAGE_FD); gpgsm->inline_data = NULL; err = start (gpgsm, mode == GPGME_SIG_MODE_DETACH ? "SIGN --detached" : "SIGN"); return err; } static gpgme_error_t gpgsm_verify (void *engine, gpgme_data_t sig, gpgme_data_t signed_text, gpgme_data_t plaintext) { engine_gpgsm_t gpgsm = engine; gpgme_error_t err; if (!gpgsm) return gpg_error (GPG_ERR_INV_VALUE); gpgsm->input_cb.data = sig; err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data)); if (err) return err; if (plaintext) { /* Normal or cleartext signature. */ gpgsm->output_cb.data = plaintext; err = gpgsm_set_fd (gpgsm, OUTPUT_FD, 0); gpgsm_clear_fd (gpgsm, MESSAGE_FD); } else { /* Detached signature. */ gpgsm->message_cb.data = signed_text; err = gpgsm_set_fd (gpgsm, MESSAGE_FD, 0); gpgsm_clear_fd (gpgsm, OUTPUT_FD); } gpgsm->inline_data = NULL; if (!err) err = start (gpgsm, "VERIFY"); return err; } /* Send the GETAUDITLOG command. The result is saved to a gpgme data object. */ static gpgme_error_t gpgsm_getauditlog (void *engine, gpgme_data_t output, unsigned int flags) { engine_gpgsm_t gpgsm = engine; gpgme_error_t err = 0; if (!gpgsm || !output) return gpg_error (GPG_ERR_INV_VALUE); #if USE_DESCRIPTOR_PASSING gpgsm->output_cb.data = output; err = gpgsm_set_fd (gpgsm, OUTPUT_FD, 0); if (err) return err; gpgsm_clear_fd (gpgsm, INPUT_FD); gpgsm_clear_fd (gpgsm, MESSAGE_FD); gpgsm->inline_data = NULL; # define CMD "GETAUDITLOG" #else gpgsm_clear_fd (gpgsm, OUTPUT_FD); gpgsm_clear_fd (gpgsm, INPUT_FD); gpgsm_clear_fd (gpgsm, MESSAGE_FD); gpgsm->inline_data = output; # define CMD "GETAUDITLOG --data" #endif err = start (gpgsm, (flags & GPGME_AUDITLOG_HTML)? CMD " --html" : CMD); return err; } static void gpgsm_set_status_handler (void *engine, engine_status_handler_t fnc, void *fnc_value) { engine_gpgsm_t gpgsm = engine; gpgsm->status.fnc = fnc; gpgsm->status.fnc_value = fnc_value; } static gpgme_error_t gpgsm_set_colon_line_handler (void *engine, engine_colon_line_handler_t fnc, void *fnc_value) { engine_gpgsm_t gpgsm = engine; gpgsm->colon.fnc = fnc; gpgsm->colon.fnc_value = fnc_value; gpgsm->colon.any = 0; return 0; } static void gpgsm_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs) { engine_gpgsm_t gpgsm = engine; gpgsm->io_cbs = *io_cbs; } static void gpgsm_io_event (void *engine, gpgme_event_io_t type, void *type_data) { engine_gpgsm_t gpgsm = engine; TRACE3 (DEBUG_ENGINE, "gpgme:gpgsm_io_event", gpgsm, "event %p, type %d, type_data %p", gpgsm->io_cbs.event, type, type_data); if (gpgsm->io_cbs.event) (*gpgsm->io_cbs.event) (gpgsm->io_cbs.event_priv, type, type_data); } static gpgme_error_t gpgsm_passwd (void *engine, gpgme_key_t key, unsigned int flags) { engine_gpgsm_t gpgsm = engine; gpgme_error_t err; char *line; if (!key || !key->subkeys || !key->subkeys->fpr) return gpg_error (GPG_ERR_INV_CERT_OBJ); if (asprintf (&line, "PASSWD -- %s", key->subkeys->fpr) < 0) return gpg_error_from_syserror (); gpgsm_clear_fd (gpgsm, OUTPUT_FD); gpgsm_clear_fd (gpgsm, INPUT_FD); gpgsm_clear_fd (gpgsm, MESSAGE_FD); gpgsm->inline_data = NULL; err = start (gpgsm, line); free (line); return err; } struct engine_ops _gpgme_engine_ops_gpgsm = { /* Static functions. */ _gpgme_get_default_gpgsm_name, NULL, gpgsm_get_version, gpgsm_get_req_version, gpgsm_new, /* Member functions. */ gpgsm_release, #if USE_DESCRIPTOR_PASSING gpgsm_reset, #else NULL, /* reset */ #endif gpgsm_set_status_handler, NULL, /* set_command_handler */ gpgsm_set_colon_line_handler, gpgsm_set_locale, NULL, /* set_protocol */ gpgsm_decrypt, gpgsm_decrypt, gpgsm_delete, /* decrypt_verify */ NULL, /* edit */ gpgsm_encrypt, NULL, /* encrypt_sign */ gpgsm_export, gpgsm_export_ext, gpgsm_genkey, gpgsm_import, gpgsm_keylist, gpgsm_keylist_ext, gpgsm_sign, NULL, /* trustlist */ gpgsm_verify, gpgsm_getauditlog, NULL, /* opassuan_transact */ NULL, /* conf_load */ NULL, /* conf_save */ gpgsm_set_io_cbs, gpgsm_io_event, gpgsm_cancel, NULL, /* cancel_op */ gpgsm_passwd, NULL /* set_pinentry_mode */ }; diff --git a/src/engine.c b/src/engine.c index 09f379c7..80febea5 100644 --- a/src/engine.c +++ b/src/engine.c @@ -1,938 +1,922 @@ /* engine.c - GPGME engine support. Copyright (C) 2000 Werner Koch (dd9jn) Copyright (C) 2001, 2002, 2003, 2004, 2006, 2009, 2010 g10 Code GmbH This file is part of GPGME. GPGME 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. GPGME 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 Lesser 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 . */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include "gpgme.h" #include "util.h" #include "sema.h" #include "ops.h" #include "debug.h" #include "engine.h" #include "engine-backend.h" struct engine { struct engine_ops *ops; void *engine; }; static struct engine_ops *engine_ops[] = { &_gpgme_engine_ops_gpg, /* OpenPGP. */ -#ifdef ENABLE_GPGSM &_gpgme_engine_ops_gpgsm, /* CMS. */ -#else - NULL, -#endif -#ifdef ENABLE_GPGCONF &_gpgme_engine_ops_gpgconf, /* gpg-conf. */ -#else - NULL, -#endif -#ifdef ENABLE_ASSUAN &_gpgme_engine_ops_assuan, /* Low-Level Assuan. */ -#else - NULL, -#endif -#ifdef ENABLE_G13 &_gpgme_engine_ops_g13, /* Crypto VFS. */ -#else - NULL, -#endif #ifdef ENABLE_UISERVER &_gpgme_engine_ops_uiserver /* UI-Server. */ #else NULL #endif }; /* The engine info. */ static gpgme_engine_info_t engine_info; DEFINE_STATIC_LOCK (engine_info_lock); /* Get the file name of the engine for PROTOCOL. */ static const char * engine_get_file_name (gpgme_protocol_t proto) { if (proto > DIM (engine_ops)) return NULL; if (engine_ops[proto] && engine_ops[proto]->get_file_name) return (*engine_ops[proto]->get_file_name) (); else return NULL; } /* Get the standard home dir of the engine for PROTOCOL. */ static const char * engine_get_home_dir (gpgme_protocol_t proto) { if (proto > DIM (engine_ops)) return NULL; if (engine_ops[proto] && engine_ops[proto]->get_home_dir) return (*engine_ops[proto]->get_home_dir) (); else return NULL; } /* Get a malloced string containing the version number of the engine for PROTOCOL. */ static char * engine_get_version (gpgme_protocol_t proto, const char *file_name) { if (proto > DIM (engine_ops)) return NULL; if (engine_ops[proto] && engine_ops[proto]->get_version) return (*engine_ops[proto]->get_version) (file_name); else return NULL; } /* Get the required version number of the engine for PROTOCOL. */ static const char * engine_get_req_version (gpgme_protocol_t proto) { if (proto > DIM (engine_ops)) return NULL; if (engine_ops[proto] && engine_ops[proto]->get_req_version) return (*engine_ops[proto]->get_req_version) (); else return NULL; } /* Verify the version requirement for the engine for PROTOCOL. */ gpgme_error_t gpgme_engine_check_version (gpgme_protocol_t proto) { gpgme_error_t err; gpgme_engine_info_t info; int result; LOCK (engine_info_lock); info = engine_info; if (!info) { /* Make sure it is initialized. */ UNLOCK (engine_info_lock); err = gpgme_get_engine_info (&info); if (err) return err; LOCK (engine_info_lock); } while (info && info->protocol != proto) info = info->next; if (!info) result = 0; else result = _gpgme_compare_versions (info->version, info->req_version); UNLOCK (engine_info_lock); return result ? 0 : trace_gpg_error (GPG_ERR_INV_ENGINE); } /* Release the engine info INFO. */ void _gpgme_engine_info_release (gpgme_engine_info_t info) { while (info) { gpgme_engine_info_t next_info = info->next; assert (info->file_name); free (info->file_name); if (info->home_dir) free (info->home_dir); if (info->version) free (info->version); free (info); info = next_info; } } /* Get the information about the configured and installed engines. A pointer to the first engine in the statically allocated linked list is returned in *INFO. If an error occurs, it is returned. The returned data is valid until the next gpgme_set_engine_info. */ gpgme_error_t gpgme_get_engine_info (gpgme_engine_info_t *info) { LOCK (engine_info_lock); if (!engine_info) { gpgme_engine_info_t *lastp = &engine_info; gpgme_protocol_t proto_list[] = { GPGME_PROTOCOL_OpenPGP, GPGME_PROTOCOL_CMS, GPGME_PROTOCOL_GPGCONF, GPGME_PROTOCOL_ASSUAN, GPGME_PROTOCOL_G13, GPGME_PROTOCOL_UISERVER }; unsigned int proto; for (proto = 0; proto < DIM (proto_list); proto++) { const char *ofile_name = engine_get_file_name (proto_list[proto]); const char *ohome_dir = engine_get_home_dir (proto_list[proto]); char *file_name; char *home_dir; if (!ofile_name) continue; file_name = strdup (ofile_name); home_dir = ohome_dir? strdup (ohome_dir): NULL; *lastp = malloc (sizeof (*engine_info)); if (!*lastp || !file_name) { int saved_err = gpg_error_from_syserror (); _gpgme_engine_info_release (engine_info); engine_info = NULL; if (file_name) free (file_name); if (home_dir) free (home_dir); UNLOCK (engine_info_lock); return saved_err; } (*lastp)->protocol = proto_list[proto]; (*lastp)->file_name = file_name; (*lastp)->home_dir = home_dir; (*lastp)->version = engine_get_version (proto_list[proto], NULL); (*lastp)->req_version = engine_get_req_version (proto_list[proto]); (*lastp)->next = NULL; lastp = &(*lastp)->next; } } *info = engine_info; UNLOCK (engine_info_lock); return 0; } /* Get a deep copy of the engine info and return it in INFO. */ gpgme_error_t _gpgme_engine_info_copy (gpgme_engine_info_t *r_info) { gpgme_error_t err = 0; gpgme_engine_info_t info; gpgme_engine_info_t new_info; gpgme_engine_info_t *lastp; LOCK (engine_info_lock); info = engine_info; if (!info) { /* Make sure it is initialized. */ UNLOCK (engine_info_lock); err = gpgme_get_engine_info (&info); if (err) return err; LOCK (engine_info_lock); } new_info = NULL; lastp = &new_info; while (info) { char *file_name; char *home_dir; char *version; assert (info->file_name); file_name = strdup (info->file_name); if (info->home_dir) { home_dir = strdup (info->home_dir); if (!home_dir) err = gpg_error_from_syserror (); } else home_dir = NULL; if (info->version) { version = strdup (info->version); if (!version) err = gpg_error_from_syserror (); } else version = NULL; *lastp = malloc (sizeof (*engine_info)); if (!*lastp || !file_name || err) { int saved_err = gpg_error_from_syserror (); _gpgme_engine_info_release (new_info); if (file_name) free (file_name); if (home_dir) free (home_dir); if (version) free (version); UNLOCK (engine_info_lock); return saved_err; } (*lastp)->protocol = info->protocol; (*lastp)->file_name = file_name; (*lastp)->home_dir = home_dir; (*lastp)->version = version; (*lastp)->req_version = info->req_version; (*lastp)->next = NULL; lastp = &(*lastp)->next; info = info->next; } *r_info = new_info; UNLOCK (engine_info_lock); return 0; } /* Set the engine info for the info list INFO, protocol PROTO, to the file name FILE_NAME and the home directory HOME_DIR. */ gpgme_error_t _gpgme_set_engine_info (gpgme_engine_info_t info, gpgme_protocol_t proto, const char *file_name, const char *home_dir) { char *new_file_name; char *new_home_dir; /* FIXME: Use some PROTO_MAX definition. */ if (proto > DIM (engine_ops)) return gpg_error (GPG_ERR_INV_VALUE); while (info && info->protocol != proto) info = info->next; if (!info) return trace_gpg_error (GPG_ERR_INV_ENGINE); /* Prepare new members. */ if (file_name) new_file_name = strdup (file_name); else { const char *ofile_name = engine_get_file_name (proto); assert (ofile_name); new_file_name = strdup (ofile_name); } if (!new_file_name) return gpg_error_from_syserror (); if (home_dir) { new_home_dir = strdup (home_dir); if (!new_home_dir) { free (new_file_name); return gpg_error_from_syserror (); } } else { const char *ohome_dir = engine_get_home_dir (proto); if (ohome_dir) { new_home_dir = strdup (ohome_dir); if (!new_home_dir) { free (new_file_name); return gpg_error_from_syserror (); } } else new_home_dir = NULL; } /* Remove the old members. */ assert (info->file_name); free (info->file_name); if (info->home_dir) free (info->home_dir); if (info->version) free (info->version); /* Install the new members. */ info->file_name = new_file_name; info->home_dir = new_home_dir; info->version = engine_get_version (proto, new_file_name); return 0; } /* Set the default engine info for the protocol PROTO to the file name FILE_NAME and the home directory HOME_DIR. */ gpgme_error_t gpgme_set_engine_info (gpgme_protocol_t proto, const char *file_name, const char *home_dir) { gpgme_error_t err; gpgme_engine_info_t info; LOCK (engine_info_lock); info = engine_info; if (!info) { /* Make sure it is initialized. */ UNLOCK (engine_info_lock); err = gpgme_get_engine_info (&info); if (err) return err; LOCK (engine_info_lock); } err = _gpgme_set_engine_info (info, proto, file_name, home_dir); UNLOCK (engine_info_lock); return err; } gpgme_error_t _gpgme_engine_new (gpgme_engine_info_t info, engine_t *r_engine) { engine_t engine; if (!info->file_name || !info->version) return trace_gpg_error (GPG_ERR_INV_ENGINE); engine = calloc (1, sizeof *engine); if (!engine) return gpg_error_from_syserror (); engine->ops = engine_ops[info->protocol]; if (engine->ops->new) { gpgme_error_t err; err = (*engine->ops->new) (&engine->engine, info->file_name, info->home_dir); if (err) { free (engine); return err; } } else engine->engine = NULL; *r_engine = engine; return 0; } gpgme_error_t _gpgme_engine_reset (engine_t engine) { if (!engine) return gpg_error (GPG_ERR_INV_VALUE); if (!engine->ops->reset) return gpg_error (GPG_ERR_NOT_IMPLEMENTED); return (*engine->ops->reset) (engine->engine); } void _gpgme_engine_release (engine_t engine) { if (!engine) return; if (engine->ops->release) (*engine->ops->release) (engine->engine); free (engine); } void _gpgme_engine_set_status_handler (engine_t engine, engine_status_handler_t fnc, void *fnc_value) { if (!engine) return; if (engine->ops->set_status_handler) (*engine->ops->set_status_handler) (engine->engine, fnc, fnc_value); } gpgme_error_t _gpgme_engine_set_command_handler (engine_t engine, engine_command_handler_t fnc, void *fnc_value, gpgme_data_t linked_data) { if (!engine) return gpg_error (GPG_ERR_INV_VALUE); if (!engine->ops->set_command_handler) return gpg_error (GPG_ERR_NOT_IMPLEMENTED); return (*engine->ops->set_command_handler) (engine->engine, fnc, fnc_value, linked_data); } gpgme_error_t _gpgme_engine_set_colon_line_handler (engine_t engine, engine_colon_line_handler_t fnc, void *fnc_value) { if (!engine) return gpg_error (GPG_ERR_INV_VALUE); if (!engine->ops->set_colon_line_handler) return gpg_error (GPG_ERR_NOT_IMPLEMENTED); return (*engine->ops->set_colon_line_handler) (engine->engine, fnc, fnc_value); } gpgme_error_t _gpgme_engine_set_locale (engine_t engine, int category, const char *value) { if (!engine) return gpg_error (GPG_ERR_INV_VALUE); if (!engine->ops->set_locale) return gpg_error (GPG_ERR_NOT_IMPLEMENTED); return (*engine->ops->set_locale) (engine->engine, category, value); } gpgme_error_t _gpgme_engine_set_protocol (engine_t engine, gpgme_protocol_t protocol) { if (!engine) return gpg_error (GPG_ERR_INV_VALUE); if (!engine->ops->set_protocol) return gpg_error (GPG_ERR_NOT_IMPLEMENTED); return (*engine->ops->set_protocol) (engine->engine, protocol); } gpgme_error_t _gpgme_engine_op_decrypt (engine_t engine, gpgme_data_t ciph, gpgme_data_t plain) { if (!engine) return gpg_error (GPG_ERR_INV_VALUE); if (!engine->ops->decrypt) return gpg_error (GPG_ERR_NOT_IMPLEMENTED); return (*engine->ops->decrypt) (engine->engine, ciph, plain); } gpgme_error_t _gpgme_engine_op_decrypt_verify (engine_t engine, gpgme_data_t ciph, gpgme_data_t plain) { if (!engine) return gpg_error (GPG_ERR_INV_VALUE); if (!engine->ops->decrypt_verify) return gpg_error (GPG_ERR_NOT_IMPLEMENTED); return (*engine->ops->decrypt_verify) (engine->engine, ciph, plain); } gpgme_error_t _gpgme_engine_op_delete (engine_t engine, gpgme_key_t key, int allow_secret) { if (!engine) return gpg_error (GPG_ERR_INV_VALUE); if (!engine->ops->delete) return gpg_error (GPG_ERR_NOT_IMPLEMENTED); return (*engine->ops->delete) (engine->engine, key, allow_secret); } gpgme_error_t _gpgme_engine_op_edit (engine_t engine, int type, gpgme_key_t key, gpgme_data_t out, gpgme_ctx_t ctx /* FIXME */) { if (!engine) return gpg_error (GPG_ERR_INV_VALUE); if (!engine->ops->edit) return gpg_error (GPG_ERR_NOT_IMPLEMENTED); return (*engine->ops->edit) (engine->engine, type, key, out, ctx); } gpgme_error_t _gpgme_engine_op_encrypt (engine_t engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t ciph, int use_armor) { if (!engine) return gpg_error (GPG_ERR_INV_VALUE); if (!engine->ops->encrypt) return gpg_error (GPG_ERR_NOT_IMPLEMENTED); return (*engine->ops->encrypt) (engine->engine, recp, flags, plain, ciph, use_armor); } gpgme_error_t _gpgme_engine_op_encrypt_sign (engine_t engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t ciph, int use_armor, gpgme_ctx_t ctx /* FIXME */) { if (!engine) return gpg_error (GPG_ERR_INV_VALUE); if (!engine->ops->encrypt_sign) return gpg_error (GPG_ERR_NOT_IMPLEMENTED); return (*engine->ops->encrypt_sign) (engine->engine, recp, flags, plain, ciph, use_armor, ctx); } gpgme_error_t _gpgme_engine_op_export (engine_t engine, const char *pattern, gpgme_export_mode_t mode, gpgme_data_t keydata, int use_armor) { if (!engine) return gpg_error (GPG_ERR_INV_VALUE); if (!engine->ops->export) return gpg_error (GPG_ERR_NOT_IMPLEMENTED); return (*engine->ops->export) (engine->engine, pattern, mode, keydata, use_armor); } gpgme_error_t _gpgme_engine_op_export_ext (engine_t engine, const char *pattern[], unsigned int reserved, gpgme_data_t keydata, int use_armor) { if (!engine) return gpg_error (GPG_ERR_INV_VALUE); if (!engine->ops->export_ext) return gpg_error (GPG_ERR_NOT_IMPLEMENTED); return (*engine->ops->export_ext) (engine->engine, pattern, reserved, keydata, use_armor); } gpgme_error_t _gpgme_engine_op_genkey (engine_t engine, gpgme_data_t help_data, int use_armor, gpgme_data_t pubkey, gpgme_data_t seckey) { if (!engine) return gpg_error (GPG_ERR_INV_VALUE); if (!engine->ops->genkey) return gpg_error (GPG_ERR_NOT_IMPLEMENTED); return (*engine->ops->genkey) (engine->engine, help_data, use_armor, pubkey, seckey); } gpgme_error_t _gpgme_engine_op_import (engine_t engine, gpgme_data_t keydata, gpgme_key_t *keyarray) { if (!engine) return gpg_error (GPG_ERR_INV_VALUE); if (!engine->ops->import) return gpg_error (GPG_ERR_NOT_IMPLEMENTED); return (*engine->ops->import) (engine->engine, keydata, keyarray); } gpgme_error_t _gpgme_engine_op_keylist (engine_t engine, const char *pattern, int secret_only, gpgme_keylist_mode_t mode) { if (!engine) return gpg_error (GPG_ERR_INV_VALUE); if (!engine->ops->keylist) return gpg_error (GPG_ERR_NOT_IMPLEMENTED); return (*engine->ops->keylist) (engine->engine, pattern, secret_only, mode); } gpgme_error_t _gpgme_engine_op_keylist_ext (engine_t engine, const char *pattern[], int secret_only, int reserved, gpgme_keylist_mode_t mode) { if (!engine) return gpg_error (GPG_ERR_INV_VALUE); if (!engine->ops->keylist_ext) return gpg_error (GPG_ERR_NOT_IMPLEMENTED); return (*engine->ops->keylist_ext) (engine->engine, pattern, secret_only, reserved, mode); } gpgme_error_t _gpgme_engine_op_sign (engine_t engine, gpgme_data_t in, gpgme_data_t out, gpgme_sig_mode_t mode, int use_armor, int use_textmode, int include_certs, gpgme_ctx_t ctx /* FIXME */) { if (!engine) return gpg_error (GPG_ERR_INV_VALUE); if (!engine->ops->sign) return gpg_error (GPG_ERR_NOT_IMPLEMENTED); return (*engine->ops->sign) (engine->engine, in, out, mode, use_armor, use_textmode, include_certs, ctx); } gpgme_error_t _gpgme_engine_op_trustlist (engine_t engine, const char *pattern) { if (!engine) return gpg_error (GPG_ERR_INV_VALUE); if (!engine->ops->trustlist) return gpg_error (GPG_ERR_NOT_IMPLEMENTED); return (*engine->ops->trustlist) (engine->engine, pattern); } gpgme_error_t _gpgme_engine_op_verify (engine_t engine, gpgme_data_t sig, gpgme_data_t signed_text, gpgme_data_t plaintext) { if (!engine) return gpg_error (GPG_ERR_INV_VALUE); if (!engine->ops->verify) return gpg_error (GPG_ERR_NOT_IMPLEMENTED); return (*engine->ops->verify) (engine->engine, sig, signed_text, plaintext); } gpgme_error_t _gpgme_engine_op_getauditlog (engine_t engine, gpgme_data_t output, unsigned int flags) { if (!engine) return gpg_error (GPG_ERR_INV_VALUE); if (!engine->ops->getauditlog) return gpg_error (GPG_ERR_NOT_IMPLEMENTED); return (*engine->ops->getauditlog) (engine->engine, output, flags); } gpgme_error_t _gpgme_engine_op_assuan_transact (engine_t engine, const char *command, gpgme_assuan_data_cb_t data_cb, void *data_cb_value, gpgme_assuan_inquire_cb_t inq_cb, void *inq_cb_value, gpgme_assuan_status_cb_t status_cb, void *status_cb_value) { if (!engine) return gpg_error (GPG_ERR_INV_VALUE); if (!engine->ops->opassuan_transact) return gpg_error (GPG_ERR_NOT_IMPLEMENTED); return (*engine->ops->opassuan_transact) (engine->engine, command, data_cb, data_cb_value, inq_cb, inq_cb_value, status_cb, status_cb_value); } gpgme_error_t _gpgme_engine_op_conf_load (engine_t engine, gpgme_conf_comp_t *conf_p) { if (!engine) return gpg_error (GPG_ERR_INV_VALUE); if (!engine->ops->conf_load) return gpg_error (GPG_ERR_NOT_IMPLEMENTED); return (*engine->ops->conf_load) (engine->engine, conf_p); } gpgme_error_t _gpgme_engine_op_conf_save (engine_t engine, gpgme_conf_comp_t conf) { if (!engine) return gpg_error (GPG_ERR_INV_VALUE); if (!engine->ops->conf_save) return gpg_error (GPG_ERR_NOT_IMPLEMENTED); return (*engine->ops->conf_save) (engine->engine, conf); } void _gpgme_engine_set_io_cbs (engine_t engine, gpgme_io_cbs_t io_cbs) { if (!engine) return; (*engine->ops->set_io_cbs) (engine->engine, io_cbs); } void _gpgme_engine_io_event (engine_t engine, gpgme_event_io_t type, void *type_data) { if (!engine) return; (*engine->ops->io_event) (engine->engine, type, type_data); } /* Cancel the session and the pending operation if any. */ gpgme_error_t _gpgme_engine_cancel (engine_t engine) { if (!engine) return gpg_error (GPG_ERR_INV_VALUE); if (!engine->ops->cancel) return gpg_error (GPG_ERR_NOT_IMPLEMENTED); return (*engine->ops->cancel) (engine->engine); } /* Cancel the pending operation, but not the complete session. */ gpgme_error_t _gpgme_engine_cancel_op (engine_t engine) { if (!engine) return gpg_error (GPG_ERR_INV_VALUE); if (!engine->ops->cancel_op) return 0; return (*engine->ops->cancel_op) (engine->engine); } /* Change the passphrase for KEY. */ gpgme_error_t _gpgme_engine_op_passwd (engine_t engine, gpgme_key_t key, unsigned int flags) { if (!engine) return gpg_error (GPG_ERR_INV_VALUE); if (!engine->ops->passwd) return gpg_error (GPG_ERR_NOT_IMPLEMENTED); return (*engine->ops->passwd) (engine->engine, key, flags); } /* Set the pinentry mode for ENGINE to MODE. */ gpgme_error_t _gpgme_engine_set_pinentry_mode (engine_t engine, gpgme_pinentry_mode_t mode) { if (!engine) return gpg_error (GPG_ERR_INV_VALUE); if (!engine->ops->set_pinentry_mode) return gpg_error (GPG_ERR_NOT_IMPLEMENTED); return (*engine->ops->set_pinentry_mode) (engine->engine, mode); } diff --git a/src/posix-util.c b/src/posix-util.c index fd445070..d4e4e3f4 100644 --- a/src/posix-util.c +++ b/src/posix-util.c @@ -1,86 +1,99 @@ /* posix-util.c - Utility functions for Posix Copyright (C) 2001 Werner Koch (dd9jn) Copyright (C) 2001, 2002, 2004 g10 Code GmbH This file is part of GPGME. GPGME 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. GPGME 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 Lesser 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, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include "util.h" #include "sys-util.h" -const char * -_gpgme_get_gpg_path (void) -{ -#ifdef GPG_PATH - return GPG_PATH; -#else - return NULL; -#endif -} -const char * -_gpgme_get_gpgsm_path (void) +/* Find an executable program PGM along the envvar PATH. */ +static char * +walk_path (const char *pgm) { -#ifdef GPGSM_PATH - return GPGSM_PATH; -#else + const char *path, *s; + char *fname, *p; + + path = getenv ("PATH"); + if (!path) + path = "/bin:/usr/bin:."; + + fname = malloc (strlen (path) + 1 + strlen (pgm) + 1); + if (!fname) + return NULL; + + for (;;) + { + for (s=path, p=fname; *s && *s != ':'; s++, p++) + *p = *s; + if (*p != '/') + *p++ = '/'; + strcpy (p, pgm); + if (!access (fname, X_OK)) + return fname; + if (!*s) + break; + path = s + 1; + } + + free (fname); return NULL; -#endif } -const char * -_gpgme_get_gpgconf_path (void) + +/* Return the full file name of the GPG binary. This function is used + if gpgconf was not found and thus it can be assumed that gpg2 is + not installed. This function is only called by get_gpgconf_item + and may not be called concurrently. */ +char * +_gpgme_get_gpg_path (void) { -#ifdef GPGCONF_PATH - return GPGCONF_PATH; -#else - return NULL; -#endif + return walk_path ("gpg"); } -const char * -_gpgme_get_g13_path (void) + +/* This function is only called by get_gpgconf_item and may not be + called concurrently. */ +char * +_gpgme_get_gpgconf_path (void) { -#ifdef G13_PATH - return G13_PATH; -#else - return NULL; -#endif + return walk_path ("gpgconf"); } - /* See w32-util.c */ int _gpgme_get_conf_int (const char *key, int *value) { return 0; } void _gpgme_allow_set_foreground_window (pid_t pid) { (void)pid; /* Not needed. */ } diff --git a/src/sys-util.h b/src/sys-util.h index f6506d33..3686f897 100644 --- a/src/sys-util.h +++ b/src/sys-util.h @@ -1,29 +1,27 @@ /* sys-util.h - System utilities not generally used. * Copyright (C) 2013 g10 Code GmbH * * This file is part of GPGME. * * GPGME 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. * * GPGME 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 * Lesser 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 . */ #ifndef SYS_UTIL_H #define SYS_UTIL_H /*-- {posix,w32}-util.c --*/ -const char *_gpgme_get_gpg_path (void); -const char *_gpgme_get_gpgsm_path (void); -const char *_gpgme_get_gpgconf_path (void); -const char *_gpgme_get_g13_path (void); +char *_gpgme_get_gpg_path (void); +char *_gpgme_get_gpgconf_path (void); #endif /* SYS_UTIL_H */ diff --git a/src/util.h b/src/util.h index c4329801..57c8b138 100644 --- a/src/util.h +++ b/src/util.h @@ -1,175 +1,163 @@ /* util.h Copyright (C) 2000 Werner Koch (dd9jn) Copyright (C) 2001, 2002, 2003, 2004, 2005 g10 Code GmbH This file is part of GPGME. GPGME 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. GPGME 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 Lesser 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, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef UTIL_H #define UTIL_H #ifdef HAVE_W32_SYSTEM # ifdef HAVE_W32CE_SYSTEM # include "w32-ce.h" # else # include "winsock2.h" # include "windows.h" # endif #endif /* For pid_t. */ #ifdef HAVE_SYS_TYPES_H # include #endif /* We must see the symbol ttyname_r before a redefinition. */ #ifdef HAVE_UNISTD_H # include #endif #include "gpgme.h" #define DIM(v) (sizeof(v)/sizeof((v)[0])) /*-- {posix,w32}-util.c --*/ int _gpgme_get_conf_int (const char *key, int *value); void _gpgme_allow_set_foreground_window (pid_t pid); /*-- dirinfo.c --*/ const char *_gpgme_get_default_homedir (void); const char *_gpgme_get_default_agent_socket (void); const char *_gpgme_get_default_gpg_name (void); const char *_gpgme_get_default_gpgsm_name (void); const char *_gpgme_get_default_g13_name (void); const char *_gpgme_get_default_gpgconf_name (void); const char *_gpgme_get_default_uisrv_socket (void); /*-- replacement functions in .c --*/ #ifdef HAVE_CONFIG_H #ifndef HAVE_STPCPY static _GPGME_INLINE char * _gpgme_stpcpy (char *a, const char *b) { while (*b) *a++ = *b++; *a = 0; return a; } #define stpcpy(a,b) _gpgme_stpcpy ((a), (b)) #endif /*!HAVE_STPCPY*/ #if !HAVE_VASPRINTF #include int vasprintf (char **result, const char *format, va_list args); int asprintf (char **result, const char *format, ...); #endif #if REPLACE_TTYNAME_R int _gpgme_ttyname_r (int fd, char *buf, size_t buflen); #undef ttyname_r #define ttyname_r(a,b,c) _gpgme_ttyname_r ((a), (b), (c)) #endif #endif /*HAVE_CONFIG_H*/ /*-- conversion.c --*/ /* Convert two hexadecimal digits from STR to the value they represent. Returns -1 if one of the characters is not a hexadecimal digit. */ int _gpgme_hextobyte (const char *str); /* Decode the C formatted string SRC and store the result in the buffer *DESTP which is LEN bytes long. If LEN is zero, then a large enough buffer is allocated with malloc and *DESTP is set to the result. Currently, LEN is only used to specify if allocation is desired or not, the caller is expected to make sure that *DESTP is large enough if LEN is not zero. */ gpgme_error_t _gpgme_decode_c_string (const char *src, char **destp, size_t len); /* Decode the percent escaped string SRC and store the result in the buffer *DESTP which is LEN bytes long. If LEN is zero, then a large enough buffer is allocated with malloc and *DESTP is set to the result. Currently, LEN is only used to specify if allocation is desired or not, the caller is expected to make sure that *DESTP is large enough if LEN is not zero. If BINARY is 1, then '\0' characters are allowed in the output. */ gpgme_error_t _gpgme_decode_percent_string (const char *src, char **destp, size_t len, int binary); gpgme_error_t _gpgme_encode_percent_string (const char *src, char **destp, size_t len); /* 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. */ time_t _gpgme_parse_timestamp (const char *timestamp, char **endp); gpgme_error_t _gpgme_map_gnupg_error (char *err); /* Retrieve the environment variable NAME and return a copy of it in a malloc()'ed buffer in *VALUE. If the environment variable is not set, return NULL in *VALUE. */ gpgme_error_t _gpgme_getenv (const char *name, char **value); /*-- status-table.c --*/ /* Convert a status string to a status code. */ void _gpgme_status_init (void); gpgme_status_code_t _gpgme_parse_status (const char *name); #ifdef HAVE_W32_SYSTEM int _gpgme_mkstemp (int *fd, char **name); const char *_gpgme_get_w32spawn_path (void); #endif /*HAVE_W32_SYSTEM*/ #ifdef HAVE_W32CE_SYSTEM char *_gpgme_w32ce_get_debug_envvar (void); #endif /*HAVE_W32CE_SYSTEM*/ -/*-- Error codes not yet available in current gpg-error.h. --*/ -#ifndef GPG_ERR_UNFINISHED -#define GPG_ERR_UNFINISHED 199 -#endif -#ifndef GPG_ERR_NOT_OPERATIONAL -#define GPG_ERR_NOT_OPERATIONAL 176 -#endif -#ifndef GPG_ERR_MISSING_ISSUER_CERT -#define GPG_ERR_MISSING_ISSUER_CERT 185 -#endif -#ifdef ENABLE_ASSUAN #include /* System hooks for assuan integration. */ extern struct assuan_system_hooks _gpgme_assuan_system_hooks; extern struct assuan_malloc_hooks _gpgme_assuan_malloc_hooks; int _gpgme_assuan_log_cb (assuan_context_t ctx, void *hook, unsigned int cat, const char *msg); -#endif #endif /* UTIL_H */ diff --git a/src/w32-util.c b/src/w32-util.c index 4cee1cb6..31a58bbd 100644 --- a/src/w32-util.c +++ b/src/w32-util.c @@ -1,704 +1,668 @@ /* w32-util.c - Utility functions for the W32 API * Copyright (C) 1999 Free Software Foundation, Inc * Copyright (C) 2001 Werner Koch (dd9jn) * Copyright (C) 2001, 2002, 2003, 2004, 2007, 2013 g10 Code GmbH * * This file is part of GPGME. * * GPGME 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. * * GPGME 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 * Lesser 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 . **/ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #ifdef HAVE_SYS_TIME_H # include #endif #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif #include #include #if __MINGW64_VERSION_MAJOR >= 2 # define _WIN32_IE 0x0501 /* Required by mingw64 toolkit. */ #else # define _WIN32_IE 0x0400 /* Required for SHGetSpecialFolderPathA. */ #endif /* We need to include the windows stuff here prior to shlobj.h so that we get the right winsock version. This is usually done in util.h but that header also redefines some Windows functions which we need to avoid unless having included shlobj.h. */ #include #include #include #include #include "util.h" #include "ath.h" #include "sema.h" #include "debug.h" #include "sys-util.h" #ifndef HAVE_W32CE_SYSTEM #define HAVE_ALLOW_SET_FOREGROUND_WINDOW 1 #endif #ifndef F_OK # define F_OK 0 #endif DEFINE_STATIC_LOCK (get_path_lock); /* The module handle of this DLL. If we are linked statically, dllmain does not exists and thus the value of my_hmodule will be NULL. The effect is that a GetModuleFileName always returns the file name of the DLL or executable which contains the gpgme code. */ static HMODULE my_hmodule; #ifdef HAVE_ALLOW_SET_FOREGROUND_WINDOW #define RTLD_LAZY 0 static GPG_ERR_INLINE void * dlopen (const char * name, int flag) { void * hd = LoadLibrary (name); return hd; } static GPG_ERR_INLINE void * dlsym (void * hd, const char * sym) { if (hd && sym) { void * fnc = GetProcAddress (hd, sym); if (!fnc) return NULL; return fnc; } return NULL; } static GPG_ERR_INLINE int dlclose (void * hd) { if (hd) { FreeLibrary (hd); return 0; } return -1; } #endif /* HAVE_ALLOW_SET_FOREGROUND_WINDOW */ /* 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. */ static char * wchar_to_utf8 (const wchar_t *string) { int n; char *result; n = WideCharToMultiByte (CP_UTF8, 0, string, -1, NULL, 0, NULL, NULL); if (n < 0) { gpg_err_set_errno (EINVAL); return NULL; } result = malloc (n+1); if (!result) return NULL; n = WideCharToMultiByte (CP_UTF8, 0, string, -1, result, n, NULL, NULL); if (n < 0) { free (result); gpg_err_set_errno (EINVAL); result = NULL; } return result; } void _gpgme_allow_set_foreground_window (pid_t pid) { #ifdef HAVE_ALLOW_SET_FOREGROUND_WINDOW static int initialized; static BOOL (WINAPI * func)(DWORD); void *handle; if (!initialized) { /* Available since W2000; thus we dynload it. */ initialized = 1; handle = dlopen ("user32.dll", RTLD_LAZY); if (handle) { func = dlsym (handle, "AllowSetForegroundWindow"); if (!func) { dlclose (handle); handle = NULL; } } } if (!pid || pid == (pid_t)(-1)) { TRACE1 (DEBUG_ENGINE, "gpgme:AllowSetForegroundWindow", 0, "no action for pid %d", (int)pid); } else if (func) { int rc = func (pid); TRACE2 (DEBUG_ENGINE, "gpgme:AllowSetForegroundWindow", 0, "called for pid %d; result=%d", (int)pid, rc); } else { TRACE0 (DEBUG_ENGINE, "gpgme:AllowSetForegroundWindow", 0, "function not available"); } #endif /* HAVE_ALLOW_SET_FOREGROUND_WINDOW */ } /* Return a string from the W32 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. */ static char * read_w32_registry_string (const char *root, const char *dir, const char *name) { HKEY root_key, key_handle; DWORD n1, nbytes, type; char *result = NULL; if (!root) root_key = HKEY_CURRENT_USER; else if (!strcmp( root, "HKEY_CLASSES_ROOT")) root_key = HKEY_CLASSES_ROOT; else if (!strcmp( root, "HKEY_CURRENT_USER")) root_key = HKEY_CURRENT_USER; else if (!strcmp( root, "HKEY_LOCAL_MACHINE")) root_key = HKEY_LOCAL_MACHINE; else if (!strcmp( root, "HKEY_USERS")) root_key = HKEY_USERS; else if (!strcmp( root, "HKEY_PERFORMANCE_DATA")) root_key = HKEY_PERFORMANCE_DATA; else if (!strcmp( root, "HKEY_CURRENT_CONFIG")) root_key = HKEY_CURRENT_CONFIG; else return NULL; if (RegOpenKeyExA (root_key, dir, 0, KEY_READ, &key_handle)) { if (root) return NULL; /* no need for a RegClose, so return direct */ /* It seems to be common practise to fall back to HKLM. */ if (RegOpenKeyExA (HKEY_LOCAL_MACHINE, dir, 0, KEY_READ, &key_handle)) return NULL; /* still no need for a RegClose, so return direct */ } nbytes = 1; if (RegQueryValueExA (key_handle, name, 0, NULL, NULL, &nbytes)) { if (root) goto leave; /* Try to fallback to HKLM also vor a missing value. */ RegCloseKey (key_handle); if (RegOpenKeyExA (HKEY_LOCAL_MACHINE, dir, 0, KEY_READ, &key_handle)) return NULL; /* Nope. */ if (RegQueryValueExA (key_handle, name, 0, NULL, NULL, &nbytes)) goto leave; } n1 = nbytes + 1; result = malloc (n1); if (!result) goto leave; if (RegQueryValueExA (key_handle, name, 0, &type, (LPBYTE) result, &n1)) { free (result); result = NULL; goto leave; } result[nbytes] = 0; /* Make sure it is really a string. */ #ifndef HAVE_W32CE_SYSTEM /* Windows CE does not have an environment. */ if (type == REG_EXPAND_SZ && strchr (result, '%')) { char *tmp; n1 += 1000; tmp = malloc (n1 + 1); if (!tmp) goto leave; nbytes = ExpandEnvironmentStrings (result, tmp, n1); if (nbytes && nbytes > n1) { free (tmp); n1 = nbytes; tmp = malloc (n1 + 1); if (!tmp) goto leave; nbytes = ExpandEnvironmentStrings (result, tmp, n1); if (nbytes && nbytes > n1) { free (tmp); /* Oops - truncated, better don't expand at all. */ goto leave; } tmp[nbytes] = 0; free (result); result = tmp; } else if (nbytes) /* Okay, reduce the length. */ { tmp[nbytes] = 0; free (result); result = malloc (strlen (tmp)+1); if (!result) result = tmp; else { strcpy (result, tmp); free (tmp); } } else /* Error - don't expand. */ { free (tmp); } } #endif leave: RegCloseKey (key_handle); return result; } /* Return the name of the directory with the gpgme DLL or the EXE (if statically linked). May return NULL on severe errors. */ const char * _gpgme_get_inst_dir (void) { static char *inst_dir; LOCK (get_path_lock); if (!inst_dir) { wchar_t *moddir; moddir = malloc ((MAX_PATH+5) * sizeof *moddir); if (moddir) { if (!GetModuleFileNameW (my_hmodule, moddir, MAX_PATH)) *moddir = 0; if (!*moddir) gpg_err_set_errno (ENOENT); else { inst_dir = wchar_to_utf8 (moddir); if (inst_dir) { char *p = strrchr (inst_dir, '\\'); if (p) *p = 0; } } free (moddir); } } UNLOCK (get_path_lock); return inst_dir; } static char * find_program_in_dir (const char *dir, const char *name) { char *result; result = malloc (strlen (dir) + 1 + strlen (name) + 1); if (!result) return NULL; strcpy (stpcpy (stpcpy (result, dir), "\\"), name); if (access (result, F_OK)) { free (result); return NULL; } return result; } static char * find_program_in_inst_dir (const char *inst_dir, const char *name) { char *result; char *dir; /* If an installation directory has been passed, this overrides a location given bu the registry. The idea here is that we prefer - a a program installed alongside with gpgme. We don't want the + a program installed alongside with gpgme. We don't want the registry to override this to have a better isolation of an gpgme aware applications for other effects. Note that the "Install Directory" registry item has been used for ages in Gpg4win and earlier GnuPG windows installers. It is technically not anymore required. */ if (inst_dir) { result = find_program_in_dir (inst_dir, name); if (result) return result; } dir = read_w32_registry_string ("HKEY_LOCAL_MACHINE", "Software\\GNU\\GnuPG", "Install Directory"); if (dir) { result = find_program_in_dir (dir, name); free (dir); return result; } return NULL; } static char * find_program_at_standard_place (const char *name) { char path[MAX_PATH]; char *result = NULL; /* See http://wiki.tcl.tk/17492 for details on compatibility. */ if (SHGetSpecialFolderPathA (NULL, path, CSIDL_PROGRAM_FILES, 0)) { result = malloc (strlen (path) + 1 + strlen (name) + 1); if (result) { strcpy (stpcpy (stpcpy (result, path), "\\"), name); if (access (result, F_OK)) { free (result); result = NULL; } } } return result; } -const char * +/* Return the full file name of the GPG binary. This function is used + if gpgconf was not found and thus it can be assumed that gpg2 is + not installed. This function is only called by get_gpgconf_item + and may not be called concurrently. */ +char * _gpgme_get_gpg_path (void) { - static char *gpg_program; + char *gpg; const char *inst_dir; inst_dir = _gpgme_get_inst_dir (); - LOCK (get_path_lock); - if (!gpg_program) - gpg_program = find_program_in_inst_dir (inst_dir, "gpg.exe"); - if (!gpg_program) - gpg_program = find_program_at_standard_place ("GNU\\GnuPG\\gpg.exe"); - UNLOCK (get_path_lock); - return gpg_program; + gpg = find_program_in_inst_dir (inst_dir, "gpg.exe"); + if (!gpg) + gpg = find_program_at_standard_place ("GNU\\GnuPG\\gpg.exe"); + return gpg; } - -const char * -_gpgme_get_gpgsm_path (void) -{ - static char *gpgsm_program; - const char *inst_dir; - - inst_dir = _gpgme_get_inst_dir (); - LOCK (get_path_lock); - if (!gpgsm_program) - gpgsm_program = find_program_in_inst_dir (inst_dir, "gpgsm.exe"); - if (!gpgsm_program) - gpgsm_program = find_program_at_standard_place ("GNU\\GnuPG\\gpgsm.exe"); - UNLOCK (get_path_lock); - return gpgsm_program; -} - - -const char * +/* This function is only called by get_gpgconf_item and may not be + called concurrently. */ +char * _gpgme_get_gpgconf_path (void) { - static char *gpgconf_program; - const char *inst_dir; - - inst_dir = _gpgme_get_inst_dir (); - LOCK (get_path_lock); - if (!gpgconf_program) - gpgconf_program = find_program_in_inst_dir (inst_dir, "gpgconf.exe"); - if (!gpgconf_program) - gpgconf_program - = find_program_at_standard_place ("GNU\\GnuPG\\gpgconf.exe"); - UNLOCK (get_path_lock); - return gpgconf_program; -} - - -const char * -_gpgme_get_g13_path (void) -{ - static char *g13_program; + char *gpgconf; const char *inst_dir; inst_dir = _gpgme_get_inst_dir (); - LOCK (get_path_lock); - if (!g13_program) - g13_program = find_program_in_inst_dir (inst_dir, "g13.exe"); - if (!g13_program) - g13_program = find_program_at_standard_place ("GNU\\GnuPG\\g13.exe"); - UNLOCK (get_path_lock); - return g13_program; + gpgconf = find_program_in_inst_dir (inst_dir, "gpgconf.exe"); + if (!gpgconf) + gpgconf = find_program_at_standard_place ("GNU\\GnuPG\\gpgconf.exe"); + return gpgconf; } const char * _gpgme_get_w32spawn_path (void) { static char *w32spawn_program; const char *inst_dir; inst_dir = _gpgme_get_inst_dir (); LOCK (get_path_lock); if (!w32spawn_program) w32spawn_program = find_program_in_inst_dir (inst_dir,"gpgme-w32spawn.exe"); if (!w32spawn_program) w32spawn_program = find_program_at_standard_place ("GNU\\GnuPG\\gpgme-w32spawn.exe"); UNLOCK (get_path_lock); return w32spawn_program; } /* Return an integer value from gpgme specific configuration entries. VALUE receives that value; function returns true if a value has been configured and false if not. */ int _gpgme_get_conf_int (const char *key, int *value) { char *tmp = read_w32_registry_string (NULL, "Software\\GNU\\gpgme", key); if (!tmp) return 0; *value = atoi (tmp); free (tmp); return 1; } #ifdef HAVE_W32CE_SYSTEM int _gpgme_mkstemp (int *fd, char **name) { return -1; } #else /* mkstemp extracted from libc/sysdeps/posix/tempname.c. Copyright (C) 1991-1999, 2000, 2001, 2006 Free Software Foundation, Inc. The GNU C Library 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. */ static const char letters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; /* Generate a temporary file name based on TMPL. TMPL must match the rules for mk[s]temp (i.e. end in "XXXXXX"). The name constructed does not exist at the time of the call to mkstemp. TMPL is overwritten with the result. */ static int mkstemp (char *tmpl) { int len; char *XXXXXX; static uint64_t value; uint64_t random_time_bits; unsigned int count; int fd = -1; int save_errno = errno; /* 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. 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) fail to give the system administrator the chance to remove the problems. */ #define ATTEMPTS_MIN (62 * 62 * 62) /* 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 -1; } /* This is where the Xs start. */ XXXXXX = &tmpl[len - 6]; /* Get some more or less random data. */ { FILETIME ft; GetSystemTimeAsFileTime (&ft); random_time_bits = (((uint64_t)ft.dwHighDateTime << 32) | (uint64_t)ft.dwLowDateTime); } value += random_time_bits ^ ath_self (); for (count = 0; count < attempts; value += 7777, ++count) { uint64_t v = value; /* Fill in the random bits. */ XXXXXX[0] = letters[v % 62]; v /= 62; XXXXXX[1] = letters[v % 62]; v /= 62; XXXXXX[2] = letters[v % 62]; v /= 62; XXXXXX[3] = letters[v % 62]; v /= 62; XXXXXX[4] = letters[v % 62]; v /= 62; XXXXXX[5] = letters[v % 62]; fd = open (tmpl, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); if (fd >= 0) { gpg_err_set_errno (save_errno); return fd; } else if (errno != EEXIST) return -1; } /* We got out of the loop because we ran out of combinations to try. */ gpg_err_set_errno (EEXIST); return -1; } int _gpgme_mkstemp (int *fd, char **name) { char tmp[MAX_PATH + 2]; char *tmpname; int err; *fd = -1; *name = NULL; err = GetTempPathA (MAX_PATH + 1, tmp); if (err == 0 || err > MAX_PATH + 1) strcpy (tmp,"c:\\windows\\temp"); else { int len = strlen(tmp); /* GetTempPath may return with \ on the end */ while(len > 0 && tmp[len - 1] == '\\') { tmp[len-1] = '\0'; len--; } } tmpname = malloc (strlen (tmp) + 13 + 1); if (!tmpname) return -1; strcpy (stpcpy (tmpname, tmp), "\\gpgme-XXXXXX"); *fd = mkstemp (tmpname); if (fd < 0) return -1; *name = tmpname; return 0; } #endif #ifdef HAVE_W32CE_SYSTEM /* Return a malloced string with the replacement value for the GPGME_DEBUG envvar. Caller must release. Returns NULL if not set. */ char * _gpgme_w32ce_get_debug_envvar (void) { char *tmp; tmp = read_w32_registry_string (NULL, "\\Software\\GNU\\gpgme", "debug"); if (tmp && !*tmp) { free (tmp); tmp = NULL; } return tmp; } #endif /*HAVE_W32CE_SYSTEM*/ /* Entry point called by the DLL loader. */ #ifdef DLL_EXPORT int WINAPI DllMain (HINSTANCE hinst, DWORD reason, LPVOID reserved) { (void)reserved; if (reason == DLL_PROCESS_ATTACH) my_hmodule = hinst; return TRUE; } #endif /*DLL_EXPORT*/ diff --git a/tests/gpg/Makefile.am b/tests/gpg/Makefile.am index c9000c93..e72bd492 100644 --- a/tests/gpg/Makefile.am +++ b/tests/gpg/Makefile.am @@ -1,99 +1,99 @@ # Copyright (C) 2000 Werner Koch (dd9jn) # Copyright (C) 2001, 2004, 2005, 2009 g10 Code GmbH # # This file is part of GPGME. # # GPGME 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. # # GPGME 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 Lesser 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, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA ## Process this file with automake to produce Makefile.in -GPG = @GPG@ -GPG_AGENT = @GPG_AGENT@ +GPG = gpg +GPG_AGENT = gpg-agent TESTS_ENVIRONMENT = GNUPGHOME=$(abs_builddir) LC_ALL=C GPG_AGENT_INFO= # The keylist tests must come after the import and the edit test. noinst_HEADERS = t-support.h if HAVE_W32_SYSTEM tests_unix = else tests_unix = t-eventloop t-thread1 endif c_tests = \ t-encrypt t-encrypt-sym t-encrypt-sign t-sign t-signers \ t-decrypt t-verify t-decrypt-verify t-sig-notation t-export \ t-import t-trustlist t-edit t-keylist t-keylist-sig t-wait \ t-encrypt-large t-file-name t-gpgconf $(tests_unix) TESTS = initial.test $(c_tests) final.test CLEANFILES = secring.gpg pubring.gpg pubring.kbx trustdb.gpg dirmngr.conf \ gpg-agent.conf pubring.kbx~ S.gpg-agent gpg.conf pubring.gpg~ \ random_seed S.gpg-agent private_keys = \ 13CD0F3BDF24BE53FE192D62F18737256FF6E4FD \ 76F7E2B35832976B50A27A282D9B87E44577EB66 \ A0747D5F9425E6664F4FFBEED20FBCA79FDED2BD \ 13CBE3758AFE42B5E5E2AE4CED27AFA455E3F87F \ 7A030357C0F253A5BBCD282FFC4E521B37558F5C EXTRA_DIST = start-stop-agent initial.test final.test \ pubdemo.asc secdemo.asc cipher-1.asc cipher-2.asc \ geheim.txt pubkey-1.asc seckey-1.asc pinentry $(private_keys) INCLUDES = -I$(top_builddir)/src AM_CPPFLAGS = @GPG_ERROR_CFLAGS@ LDADD = ../../src/libgpgme.la t_thread1_LDADD = ../../src/libgpgme-pthread.la -lpthread # We don't run t-genkey in the test suite, because it takes too long noinst_PROGRAMS = $(c_tests) t-genkey clean-local: -$(srcdir)/start-stop-agent --stop -rm -fR private-keys-v1.d all-local: ./gpg.conf ./gpg-agent.conf ./pubring.gpg \ ./private-keys-v1.d/gpg-sample.stamp export GNUPGHOME := $(abs_builddir) export GPG_AGENT_INFO := ./private-keys-v1.d/gpg-sample.stamp: $(srcdir)/$(private_keys) test -d ./private-keys-v1.d || mkdir ./private-keys-v1.d for k in $(private_keys); do \ cp $(srcdir)/$$k private-keys-v1.d/$$k.key; \ done echo x > ./private-keys-v1.d/gpg-sample.stamp ./pubring.gpg: $(srcdir)/pubdemo.asc -$(GPG) --no-permission-warning \ --import $(srcdir)/pubdemo.asc -$(GPG) --no-permission-warning \ --import $(srcdir)/secdemo.asc ./gpg.conf: # This is required for t-sig-notations. echo no-force-v3-sigs > ./gpg.conf ./gpg-agent.conf: # This is required for gpg2, which does not support command fd. echo pinentry-program $(abs_srcdir)/pinentry > ./gpg-agent.conf diff --git a/tests/gpgsm/Makefile.am b/tests/gpgsm/Makefile.am index 9086134b..45b3b50c 100644 --- a/tests/gpgsm/Makefile.am +++ b/tests/gpgsm/Makefile.am @@ -1,72 +1,72 @@ # Copyright (C) 2000 Werner Koch (dd9jn) # Copyright (C) 2001 g10 Code GmbH -# +# # This file is part of GPGME. -# +# # GPGME 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. -# +# # GPGME 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 Lesser 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, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA ## Process this file with automake to produce Makefile.in -GPGSM = @GPGSM@ +GPGSM = gpgsm -TESTS_ENVIRONMENT = GNUPGHOME=$(abs_builddir) GPG_AGENT_INFO= +TESTS_ENVIRONMENT = GNUPGHOME=$(abs_builddir) GPG_AGENT_INFO= noinst_HEADERS = t-support.h TESTS = t-import t-keylist t-encrypt t-verify t-decrypt t-sign t-export EXTRA_DIST = cert_dfn_pca01.der cert_dfn_pca15.der cert_g10code_test1.der \ $(key_id) INCLUDES = -I$(top_builddir)/src AM_CPPFLAGS = @GPG_ERROR_CFLAGS@ LDADD = ../../src/libgpgme.la # We don't run t-genkey in the test suite, because it takes too long # and needs a working pinentry. noinst_PROGRAMS = $(TESTS) t-genkey cms-keylist cms-decrypt key_id = 32100C27173EF6E9C4E9A25D3D69F86D37A4F939 CLEANFILES = pubring.kbx pubring.kbx~ gpgsm.conf trustlist.txt \ - random_seed S.gpg-agent + random_seed S.gpg-agent clean-local: -gpg-connect-agent KILLAGENT /bye -rm -fR private-keys-v1.d all-local: ./pubring.kbx ./gpgsm.conf ./private-keys-v1.d/$(key_id).key ./trustlist.txt export GNUPGHOME := $(abs_builddir) -export GPG_AGENT_INFO := +export GPG_AGENT_INFO := ./pubring.kbx: $(srcdir)/cert_g10code_test1.der $(GPGSM) --import $(srcdir)/cert_g10code_test1.der ./gpgsm.conf: echo disable-crl-checks > ./gpgsm.conf echo faked-system-time 1008241200 >> ./gpgsm.conf ./private-keys-v1.d/$(key_id).key: $(srcdir)/$(key_id) test -d ./private-keys-v1.d || mkdir ./private-keys-v1.d cp $(srcdir)/$(key_id) private-keys-v1.d/$(key_id).key ./trustlist.txt: echo $(key_id) > ./trustlist.txt echo >> ./trustlist.txt echo "# CN=test cert 1,OU=Aegypten Project,O=g10 Code GmbH,L=Düsseldorf,C=DE" >> ./trustlist.txt echo "3CF405464F66ED4A7DF45BBDD1E4282E33BDB76E S" >> ./trustlist.txt diff --git a/tests/t-engine-info.c b/tests/t-engine-info.c index ec2e1e84..43acd3d2 100644 --- a/tests/t-engine-info.c +++ b/tests/t-engine-info.c @@ -1,94 +1,61 @@ /* t-engine-info.c - Regression test for gpgme_get_engine_info. Copyright (C) 2003, 2004, 2007 g10 Code GmbH This file is part of GPGME. GPGME 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. GPGME 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 Lesser 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, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #define fail_if_err(err) \ do \ { \ if (err) \ { \ fprintf (stderr, "%s:%d: gpgme_error_t %s\n", \ __FILE__, __LINE__, gpgme_strerror (err)); \ exit (1); \ } \ } \ while (0) - -void -check_engine_info (gpgme_engine_info_t info, gpgme_protocol_t protocol, - const char *file_name, const char *req_version) -{ - if (info->protocol != protocol) - { - fprintf (stderr, "Unexpected protocol %i (expected %i instead)\n", - info->protocol, protocol); - exit (1); - } - if (strcmp (info->file_name, file_name)) - { - fprintf (stderr, "Unexpected file name to executable %s (expected %s instead)\n", - info->file_name, file_name); - exit (1); - } - if (strcmp (info->req_version, req_version)) - { - fprintf (stderr, "Unexpected required version %s (expected %s instead)\n", - info->req_version, req_version); - exit (1); - } -} int main (int argc, char **argv ) { gpgme_engine_info_t info; gpgme_error_t err; gpgme_check_version (NULL); err = gpgme_get_engine_info (&info); fail_if_err (err); - check_engine_info (info, GPGME_PROTOCOL_OpenPGP, GPG_PATH, NEED_GPG_VERSION); - - info = info->next; -#ifdef GPGSM_PATH - check_engine_info (info, GPGME_PROTOCOL_CMS, GPGSM_PATH, NEED_GPGSM_VERSION); -#else - if (info) - { - fprintf (stderr, "Unexpected engine info.\n"); - exit (1); - } -#endif + for (; info; info = info->next) + fprintf (stdout, "protocol=%d engine='%s' v='%s' (min='%s')\n", + info->protocol, info->file_name, info->version, info->req_version); return 0; }