diff options
94 files changed, 8424 insertions, 919 deletions
@@ -1,5 +1,5 @@ Package: gpgme -Homepage: https://gnupg.org/related_software/gpgme/ +Homepage: https://gnupg.org/software/gpgme/ Download: https://gnupg.org/ftp/gcrypt/gpgme/ Repository: git://git.gnupg.org/gpgme.git Maintainer: Werner Koch <wk@gnupg.org> @@ -19,7 +19,7 @@ List of Copyright holders Copyright (C) 1991-2013 Free Software Foundation, Inc. Copyright (C) 2000-2001 Werner Koch - Copyright (C) 2001-2017 g10 Code GmbH + Copyright (C) 2001-2018 g10 Code GmbH Copyright (C) 2002 Klarälvdalens Datakonsult AB Copyright (C) 2004-2008 Igor Belyi Copyright (C) 2002 John Goerzen @@ -1,3 +1,1262 @@ +2018-04-18 Werner Koch <wk@gnupg.org> + + Release 1.11.0. + + commit 3f55c52b9adc3680c7a9fb0e598598e6ed3a2a5d + * configure.ac: Bump LT version to C31/A20/R0. For cpp to C12/A6/R0. + For qt to: C10/A3/R1. + + json: Add stpcpy replacement. + + commit 8e34a14fe694a8c765973aaa3a8b2a2d2c3ba8b9 + * src/gpgme-json.c [!HAVE_STPCPY](_my_stpcpy): New. + + core: New convenience constant GPGME_KEYLIST_MODE_LOCATE. + + commit 55e9a94680370e584fbe5d21161a2cee3fe95744 + * src/gpgme.h.in (GPGME_KEYLIST_MODE_LOCATE): New. + + json: Add command "decrypt" to gpgme-json. + + commit e5273fc4431dfb597a2d9cf4af5172572476a2de + * src/gpgme-json.c (make_data_object): Enable auto-detection of + base-64. + (op_encrypt): Support a 'mime' flag. + (op_decrypt): New. + (process_request): Add command "encrypt". + + core: Add 'is_mime' flags to the verify and decrypt results. + + commit 65479fe7b871ad6237d5a8959b73afcc7db784da + * src/op-support.c (_gpgme_parse_plaintext): Add arg r_mime. + * src/decrypt.c (_gpgme_decrypt_status_handler): Ser mime flag. + * src/verify.c (_gpgme_verify_status_handler): Ditto. + * src/gpgme.h.in (gpgme_op_verify_result_t): Append fields 'is_mime' + and '_unused'. + (gpgme_op_decrypt_result_t): New field 'is_mime'. Shrink '_unused'. + + * tests/run-decrypt.c (print_result): Print MIME flag. + * tests/run-verify.c (print_result): Ditto. + + core: Fix possible compliance mode detection error. + + commit 23177e4410d05d590c0f2e1675dc645bbb4ad62c + * src/verify.c (_gpgme_verify_status_handler): Insert missing break. + + json: Improve help meta command in gpgme-json. + + commit 49a617f8bbff116884ca5c7238c2e0ea4e26ce59 + * src/gpgme-json.c (process_meta_commands): Add ",help CMD". + + json: Add command "getmore" to gpgme-json. + + commit ed1052842df633bc149b14c4cb17859e3c66afe4 + * src/gpgme-json.c (MIN_REPLY_CHUNK_SIZE): New const. + (DEF_REPLY_CHUNK_SIZE): New const. + (MAX_REPLY_CHUNK_SIZE): New const. + (pending_data): New var. + (add_base64_to_object): Chnage to take a plain data pointer. + (get_chunksize): New. + (make_data_object): New. + (op_encrypt): Get chunksize and use make_data_object. + (op_getmore): New. + (process_request): Release pending data for all commands but "getmore" + and "help". + + json: Add meta command ,read to gpgme-json. + + commit e69b175e8ed5430b56e2e8f3d68c16a45f0fed17 + * src/gpgme-json.c: Include stat.h. + (get_file): New. + (process_meta_commands): Implement ",read". + +2018-04-17 Werner Koch <wk@gnupg.org> + + core: Extend decryption result with symkey_algo. + + commit 01435da498af9f7538d7ee810392d7eaa407957e + * src/gpgme.h.in (gpgme_op_decrypt_result_t): Add field 'symkey_algo'. + * src/decrypt.c (release_op_data): Free SYMKEY_ALGO. + (gpgme_op_decrypt_result): Make sure SYMKEY_ALGO is not NULL. + (parse_decryption_info): New. + (_gpgme_decrypt_status_handler): Parse DECRYPTION_INFO status. + * src/conversion.c (_gpgme_cipher_algo_name): New. + (_gpgme_cipher_mode_name): New. + + * tests/run-decrypt.c (print_result): Print SYMKEY_ALGO + + * src/util.h (_gpgme_map_gnupg_error): Remove obsolete prototype. + + core: New keyword --file for OpenPGP recpstring. + + commit 3589da0500f1c80717e658d103a0cb2751d27b49 + * src/engine-gpg.c (append_args_from_recipients_string): Add new + flags. + + core: For OpenPGP let offline mode disable dirmngr. + + commit c143ab692c7fc7cf2ec0aebe40b9479ee15eaba9 + * src/engine-gpg.c (struct engine_gpg): New flag.offline. + (gpg_set_engine_flags): Set it. Also fix setting of no_symkey_cache. + (build_argv): Pass --disable-dirmngr in offline mode. + +2018-04-17 Tobias Mueller <muelli@cryptobitch.de> + + python: Fix crash by leaving struct members intact. + + commit bbfa7c42337bb619e6af20bf051fe0755ed5a9fd + * lang/python/setup.py.in: Copy gpgme.h instead of parsing it. + +2018-04-17 Werner Koch <wk@gnupg.org> + + core: Allow for --hidden keyword in OpenPGP recpstrings. + + commit 4bba3b8e2c350b8ff0d562ec63cc03a096448d84 + * src/engine-gpg.c (append_args_from_recipients_string): Add special + keywords. + + core: New encryption flag GPGME_ENCRYPT_WANT_ADDRESS. + + commit 86efba2be270d2cdd0bc66c9d3fe190495b7af2f + * src/gpgme.h.in (GPGME_ENCRYPT_WANT_ADDRESS): New flag. + * src/engine-gpg.c (add_arg_recipient): New. + (add_arg_recipient_string): New. + (append_args_from_recipients): Call new helper function. + (append_args_from_recipients_string): Ditto. + * src/gpgme-json.c (op_encrypt): Add flag "want-address". + + core: Add extended versions of the encrypt functions. + + commit a1f76b3b54b75a150fe272b804d85ffd40a507a6 + * src/gpgme.h.in (gpgme_op_encrypt_ext_start) New. + (gpgme_op_encrypt_ext): New. + (gpgme_op_encrypt_sign_ext_start): New. + (gpgme_op_encrypt_sign_ext): New. + * src/libgpgme.vers, tests/run-encrypt.c: Add them. + + * src/encrypt.c (encrypt_start): Add arg recpstring. + (gpgme_op_encrypt): Factor code out to ... + (gpgme_op_encrypt_ext): new function with new arg recpstring. + (gpgme_op_encrypt_start): Factor code out to ... + (gpgme_op_encrypt_ext_start): new function with new arg recpstring. + * src/encrypt-sign.c (encrypt_sign_start): Add arg recpstring. + (gpgme_op_encrypt_sign): Factor code out to ... + (gpgme_op_encrypt_sign_ext): new function with new arg recpstring. + (gpgme_op_encrypt_sign_start): Factor code out to ... + (gpgme_op_encrypt_sign_ext_start): new function with new arg + recpstring. + + * src/engine-backend.h (struct engine_ops): Change fields encrypt and + encrypt_sign. + * src/engine.c (_gpgme_engine_op_encrypt): Add arg recpstring and pass + to engine. + (_gpgme_engine_op_encrypt_sign): Ditto. + * src/engine-gpg.c (append_args_from_recipients_string): New. + (gpg_encrypt): Add arg recpstring and call new function as needed. + (gpg_encrypt_sign): Ditto. + * src/engine-gpgsm.c (set_recipients_from_string): New. + (gpgsm_encrypt): Add arg recpstring and call new function as needed. + * src/engine-uiserver.c (set_recipients_from_string): New. + (uiserver_encrypt): Add arg recpstring and call new function as + needed. + + * tests/run-encrypt.c (xstrdup): New. + (main): Add option --keystring. + + * src/gpgme-json.c (get_keys): Simplify. + (op_encrypt): Modify to make use of the extended encrypt function. + +2018-04-12 Werner Koch <wk@gnupg.org> + + core: Tweak STATUS_FAILURE handling. + + commit b99502274ae5efdf6df0d967900ec3d1e64373d7 + * src/op-support.c (_gpgme_parse_failure): Ignore failures with + location "gpg-exit". + * tests/gpg/t-verify.c (main): Adjust for the now working checking of + the second key. + + core: For a failed verification return the sig's fingerprint. + + commit 478d1650bbef84958ccce439fac982ef57b16cd0 + * src/verify.c (parse_new_sig): Parse the new ERRSIG fpr. + + tests: Avoid segv in run-verify due to Policy URLs. + + commit ee8fad3ea0cbc82f31c86b3483abd8549df62b69 + * tests/run-verify.c (print_result): Take care of Policy URLs. + + tests: Add another check to gpg/t-verify. + + commit bdf7cd2e28432cf0fa7e0758acdfee03d7bfd45f + * tests/gpg/t-verify.c (PGM): New. Use it instead of __FILE__. + (test_sig1_plus_unknown_key): New test signature. + (check_result): Allow checking of several signatures. + (main): Check a signature with a know and an unknown key. + + core: Add new context flag "no-symkey-cache". + + commit f7700a016926f0d8e9cb3c0337837deb7fe01079 + * src/gpgme.c (gpgme_set_ctx_flag): Set flag. + (gpgme_get_ctx_flag): Get flag. + * src/context.h (struct gpgme_context): Add field no_symkey_cache. + * src/engine-gpg.c (struct engine_gpg): Ditto. + (gpg_set_engine_flags): Set flag. + (build_argv): Pass option --no-symkey-cache to gpg. + + * tests/run-decrypt.c (print_result): Fix segv for symmetric messages. + (main): New option --no-symkey-cache. + * tests/run-encrypt.c (main): New option --no-symkey-cache. + +2018-04-04 Andre Heinecke <aheinecke@intevation.de> + + qt: Add test for resetting config value. + + commit 5eb261d6028ab2c0ddd9af8e3e1f82e479c6109c + * lang/qt/tests/t-config.cpp (CryptoConfigTest::testDefault): New. + +2018-03-29 Werner Koch <wk@gnupg.org> + + json: Build only a dummy if libgpg-error is < 1.28. + + commit 60d7a1e8f625ea0db455bff989534dd52f0650c7 + + +2018-03-29 Ben McGinnes <ben@adversary.org> + + example: revoke UID. + + commit 3b91f6af378ccc37dcf8924cbc157894c35b5192 + * Script to revoke a UID on an existing key. + + docs: python bindings howto. + + commit d65864989c0560b5f51cb8d05d9ea9f1957b453e + * Added section on revoking UIDs. + + example: key signing. + + commit 56bbfd39acea90eb87a28b11a515b0314cdda54c + * Added script for signing or certifying keys. + + doc: python bindings howto. + + commit 5a553f5a317e5ad5ab0274d58854df1ecf390e0d + * Fixed a typo. + +2018-03-28 Ben McGinnes <ben@adversary.org> + + docs python bindings howto. + + commit 2f507b045909d32bf29d23da04db02b078e5fb9d + * PEP8 compliance: a collection of minor edits across multiple example + code snippets. + + example: add user ID. + + commit 5cd419341807d8ae23fec7bd9bb7025a8a2dcb3c + * Added script to add a UID to an existing key. + +2018-03-28 Werner Koch <wk@gnupg.org> + + json: Remove the "message" object thingy again. + + commit 3345a17dda2222e3c1592235e8a1cd9493192777 + * src/gpgme-json.c (process_request): Remove 'nm_mode'. + + json: Make native messaging work. + + commit 4b2fa657d195382d14ac99be40b66327e0fc855c + * src/gpgme-json.c (opt_debug): New. + (process_request): Add optional arg nm_mode. In this mode take the + request from a "message" object. + (native_messaging_repl): Add debug output and call process_request + in NM_MODE. + (main): Add option --debug. Parse envvar GPGME_JSON_DEBUG as an + alternative way to enable this. Use a default log file. + +2018-03-27 Andre Heinecke <aheinecke@intevation.de> + + core: Initialize key return value in gpgme_get_key. + + commit 7c220e387d511b0cf66f99370759c36b729cc444 + * src/keylist.c (gpgme_get_key): Set r_key to NULL. + +2018-03-27 Ben McGinnes <ben@adversary.org> + + doc: python bindings howto. + + commit a2eedef630891397f8eccb5bb426a0728588bf41 + * Fixed some minor PEP8 compliance issues in the key creation examples. + + example: key creation. + + commit f9159b1d75d3209b1c22bbb0ed4472800b60a522 + * Script to generate a new key with encryption subkey taking input + from interactive prompts. + * Will also take a passphrase via pinentry and uses passphrase caching + of five minutes when used in conjunction with the temp homedir + script. + + script: temp homedir config. + + commit 1b5da37a47ceef41545e0b2474738613f36be949 + * added passphrase caching of 5 minutes. + +2018-03-25 Ben McGinnes <ben@adversary.org> + + doc: python bindings howto. + + commit 5b32efbaf37920b2e99d4bb87cb383b2809b1688 + * Testing the addition of a HTML header set in org-mode in order to + had RSS update links for files. + * This should work with any [X]HTML export from current versions of + Org-Mode, but if it also works on website generated pages then it'll + tick off one of the wishlist itmes. + + script: temp homedir. + + commit 40a9dea5d56506400b67b0c11f6e55a1629dc6fe + * Fixed whitespace. + + doc: python bindings howto. + + commit 3b724aae423f2de01812165d54df2a7b524c82f6 + * Added a reference to new script which will setup a temporary homedir + for a user. + + script: temporary homedir creation. + + commit dde1aae312958776fab475d6c0cdfa19cc255863 + * Script to create a temporary gnupg homedir in the user's directory + for testing or scripting purposes. + * Creates a hidden directory on POSIX systems with the correct + permissions (700). + * Creates a gpg.conf in that directory containing the same + configuration options as used in the "Danger Mouse" example in the + HOWTO with the correct permissions (600). + + doc: python bindings howto. + + commit 22247f658ce2f8e527c26746358cfc2643c4832f + * Fixed the plaintext, result and verify_result references in the + decryption section. + +2018-03-23 Ben McGinnes <ben@adversary.org> + + doc: python bindings howto. + + commit e6180f2b36cc8a6c6154e5f3d702324af573132a + * Fixed a minor spelling error and a minor grammatical error. + +2018-03-23 Werner Koch <wk@gnupg.org> + + core: Need to increase an array in the gpg engine. + + commit 4763974ef6932c503e35c3d14fe47a66a5323f48 + * src/engine-gpg.c (build_argv): Allcate one slot more for ARGV. + + json: Use a request origin of "browser". + + commit e5abf4827aead50437bbdff8cfdd5e9fdc6ed72d + * src/gpgme-json.c (_create_new_context): Set flag. + + core: New gpgme_set_ctx_flag "request-origin". + + commit b9000bc293164ff62efa7e91e5cf6d5fc19d482f + * src/context.h (gpgme_context): Add 'request_origin'. + * src/gpgme.c (gpgme_release): Free that field. + (gpgme_set_ctx_flag, gpgme_get_ctx_flag): Add "request-origin". + * src/engine-backend.h (engine_ops): Add 'set_engine_ops' func ptr and + adjust all users. + * src/engine.c (_gpgme_engine_set_engine_flags): New. + * src/op-support.c (_gpgme_op_reset): Call that func. + * src/engine-gpg.c (struct engine_gpg): Add 'request_origin'. + (gpg_set_engine_flags): New. + (_gpgme_engine_ops_gpg): Hook it. + (build_argv): Use command line option --request-origin. + * src/engine-gpgsm.c (struct engine_gpgsm): Add 'request_origin'. + (gpgsm_set_engine_flags): New. + (_gpgme_engine_ops_gpgsm): Hook it. + (start): Send OPTION "request-origin". + * src/engine-assuan.c (struct engine_llass): Add 'request_origin'. + (gpgsm_set_engine_flags): New. + (_gpgme_engine_ops_assuan): Hook it. + (start): Send OPTION "pretend-request-origin". + + build: Allow building with released libgpg-error. + + commit eee68c1b13fbe21c123f469712817e0c81f16383 + * src/gpgme-json.c (add_base64_to_object): Return an error if building + with an older libgpg-error. + + json: Finish op:encrypt. + + commit d83482a1d768fc5afd3aa4836f2fefe5c549d02e + * src/gpgme-json.c (add_base64_to_object): New. + (data_from_base64_string): New. + (op_encrypt): Employ them. + (process_request): Print unformated json. + + json: Add a new function to cJSON. + + commit 6525d78d0a1d303c449762082942e71d3002b9ca + * src/cJSON.c (cJSON_CreateStringConvey): New. + +2018-03-22 Ben McGinnes <ben@adversary.org> + + doc: python bindings howto. + + commit 65ed4ac82598734551b87fc89deab3cee010bd37 + * Fixed table. + +2018-03-21 Ben McGinnes <ben@adversary.org> + + doc and examples: python bindings HOWTO. + + commit 5722148bacab5862b40a027861d64683a0f214ea + * Added GPGME Python bindings HOWTO in Australian/British English. + ** en-US "translation" still to be done. + * Added several example scripts comprised of the "Basic Functions" + section of the HOWTO (plus the work-around at the end). + ** As these scripts are very basic examples they are released under + both the GPLv2+ and the LGPLv2.1+ (just like GPGME itself). + + examples: multi-key selection operations. + + commit 05e59933056ee8ef8ba7579351a58ed25dd7f754 + * Temporarily removing multi-key selection based examples. + * There are a few issues with getting the key selections to play + nicely with gpg.Context().keylist object types. + * Will troubleshoot them separately and restore them when that's + worked out, but I don't want these more complicated examples to + delay merging the HOWTO with master. + + example: group encryption. + + commit 61a988036bd3f0d43f7d55bfa43f5f05bec978c4 + * Troubleshooting. + + examples: encryption. + + commit 7ddff71908a85111c8e0da41312197b3b1a77da6 + * Fixed two incorrect Context() objects. + + example: key selection. + + commit c6a0395f0a3a57071f0c943f7815f58a02f9d2f3 + * Removed extraneous blank line. + + example: key selection. + + commit 0a0d57fd41380cd797d29e11cec8a77c7404e960 + * Similar to group-key-selection.py, but does not use an existing + group from gpg.conf; instead takes multiple key IDs, fingerprints or + patterns on the command line and adds them to a keylist object. + + example: sign and encrypt to group. + + commit 0ccc57c9512246d82d46e7732bfb0f95c18ca9d3 + * Begins to string together some of the simpler examples to do more + useful things. + * Signs and encrypts a file while encrypting to every key in a group + specified in the gpg.conf file. + +2018-03-21 Werner Koch <wk@gnupg.org> + + json: Use gpgrt_argparse instead of argsparse.c. + + commit e14f1f687ff618716ed17e309a0475df95e1c0aa + * src/gpgme-json.c: Remove header argparse.h. Define + GPGRT_ENABLE_ARGPARSE_MACROS. + (interactive_repl): Replace strusage by gpgrt_strusage. + (my_strusage): Add SPDX level. + (main): Switch to gpgrt_argparse stuff but keep very limited + functionality when building with an older libgpg-error. + +2018-03-21 Ben McGinnes <ben@adversary.org> + + example: group key selection. + + commit 8b401bfc76eac762553f76faab53c2f4cd117a8d + * Example of preparing a keylist object using an existing group line + from the gpg.conf file. + + example groups work around. + + commit 6c6af9a7b0ae4e7182d669bec282c6edaaa7eaa1 + * Updated usage so it only references importing the final list of + lists produced. Trying to use some of the mid-points can have + unpredictable results (this is part of the problem with work + arounds). + + example: groups. + + commit a4e3f827652c59d850b4e5506a92c1ecd190c1bb + * Added a docstring. + + example: verify signatures. + + commit ad6cb4f9b8b97a2bc501c17fc542a84b725dedea + * Added example for verifying detached signatures against the files + they're the signatures for. + + example: verify signed file. + + commit ae2767eb27b6a76284ee4403e575869afe2e80a8 + * Added example to verify normal and clearsigned files. + + doc: python bindings howto. + + commit e57388a69f61d14e3df3c842d227fb450c96c807 + * Fixed minor error in one of the verification examples. + + example: detach sign file. + + commit ac6a552c37147a000de74f49d1bff34dad52252e + * Added example to make detached signatures of a file with key selection. + + example: encrypt-sign-file.py. + + commit af6cbba18ba5e2bbecce5f8268c146282cd12367 + * Adjusted the doc string. + + examples: doc strings. + + commit 6fa2a344282e369e6aca8155bc77dd2c12a29414 + * Fixed minor errors in two doc strings. + + example: clear signing. + + commit 1fdd1f306d45f6aeee91c7f016f7c37286ee3b3b + * Added example to clear sign a file with signing key selection. + + doc: python bindings howto. + + commit 1d2746433c9632fc0c7bc10b59280fca15895545 + * deconstructed and fixed all three signing methods. + + example: sign file. + + commit 0390ede18696520be9cc1a42f628e23159b7c2eb + * Similar to encrypt file except for signing a file in normal mode. + * Noticed additional changes to be made to the howto to match this, + but they will have to wait due to a power outage (currently running + on battery and a mobile connection, but that won't last). + +2018-03-20 Werner Koch <wk@gnupg.org> + + json: Implement op:encrypt. + + commit 6073789a6d3514263404c93fa795a398bfd93d91 + + + core: Do not clobber R_KEY in gpgme_get_key on error. + + commit 343d3e2232a22d0999e1693f0f95e5e290005829 + * src/keylist.c (gpgme_get_key): Assign a value to the return + parameter only on success. + + core: Fix ABI regression in recent commit. + + commit 9e1e6554834d0e803dd0889deaef4f11047c7e47 + * src/gpgme.h.in (_gpgme_op_import_result): Move new field + 'skipped_v3_keys' to the end. + +2018-03-20 Ben McGinnes <ben@adversary.org> + + doc: python bindings howto. + + commit 52e262991f1fdf7da93882c3b22c05537376cf49 + * Fixed typos in examples. + + example: keycount. + + commit 96d0395bccbbff91f73c06cb7bd6c131f04b8a9a + * Fixed missing parenthesis. + + example: decrypt file. + + commit 51258975d763c9471859d635e6080c2ec02e8647 + * Decrypts a file taking file names as command line parameters. + + example: groups work-around. + + commit 29e918171f352c71a90a16c04d4a3dcafa5db682 + * Added groups selection work around code. + * Intended for use as a module to be imported by other scripts, + usually with "from groups import group_lists" or "from groups import + group_lines" or similar. + +2018-03-19 Ben McGinnes <ben@adversary.org> + + example: encrypt file. + + commit 7221bb67642eb01a07957d66d0cbcd4ef8aadbf8 + * Nested encryption in try/except statement in case recipient key is + untrusted or invalid. + + example: sign and encrypt file. + + commit f3fe47e8fd2e7bc748016befcae494421223368c + * Example to sign and encrypt a file. + * Similar to encrypt-file.py except all keys are considered trusted + and signs with the default key. + * Also encrypts to the default key. + + example: encrypt file. + + commit f0790f224d7af9521efe96e69a8f719fb89a5af2 + * Fixed typo in second encryption call. + + example: encrypt file. + + commit 7ab42e79ade89f28507ea42d51148a40b4bfc736 + * Example to encrypt a file to a single key. + * Takes key ID and/or fpr as a CLI parameter. + * Takes path and filename as a CLI parameter. + * Encrypts to specified key only, no signing and writes the output in + both ASCII armoured and GPG binary formats with output filenames + based on input filename. + + example: python bindings key count. + + commit cfbdcb7fb3fa438cafba82e4fb8f327df596f98e + * Added script wo count the number of keys in both the public and + secret key stores. + + doc: python bindings examples. + + commit b30ebf89725641018b3b08f77876530f9b983fa2 + * Explicitly stated that all this code is released under the GPLv2+ + and the LGPLv2.1+. + + doc: python bindings example README. + + commit 8f7672ad1b267f122f647bb5f984734d0ff66a5c + * Added the same license as used with the HOWTO. + * Since these examples are so basic, they'll be dual licensed the same + as GPGME itself (otherwise it would slip too dangerously against the + need for permissive licensing of crypto libraries). + + docs: python bindings examples. + + commit 6950a63e63d60685ddb6f4cbff7b826b8acb5b13 + * Added reference to location where all the examples included in the + HOWTO will be available as executable scripts. + * Included a short README file in that location. + + example: python bindings encryption. + + commit 3e0f68fdff1998dae9cb6f8510a3e945a268d1f6 + * Since we don't want to encourage accessing the low level functions + (e.g. op_encrypt), but since this example can still be useful to + understand, renaming it and will add new encryption examples to + match the instructions in the HOWTO. + + doc: python bindings howto. + + commit d5f6dec048d3d4d94f1fcdb3f4249cf6e71c4b92 + * Slight python-gnupg clarification. See also this thread: + https://lists.gnupg.org/pipermail/gnupg-devel/2018-March/033528.html + + doc: python bindings howto. + + commit 0fb8a5d45c1c77a5928d6e356271da055aa55994 + * Adjusted the python-gnupg so the comments regarding insecure + invocation of commands via subprocess (shell=True) were a major + historical issue and not a a current issue. + * Not including Vinay Sajip's requested change to say it is now secure + since no audit of the current code base has been performed and my + last major inspection of that code was around the time I first + ported PyME to Python 3 in 2015. + + doc: python bindings todo list. + + commit bf67cf433fe82924ed40e79785e95403c07cc068 + * Checked off several points of howto coverage as completed. + * Reorganised to move S/MIME coverage to its own separate group of tasks. + * Noted only revocation remains for howto completion. + + doc: python bindings howto. + + commit 1779d7b9d6769b2e47f1e90260290e25c8c3aa02 + * deconstructing multi-recipient encryption. + + doc: python bindings howto. + + commit 64c5886132aceefc9d9600a3a6dbbbf404b95b81 + * Replaced the single encryption methods with one main way (i.e. cut + the low level stuff involving SEEK_SET instructions). + +2018-03-18 Ben McGinnes <ben@adversary.org> + + doc: python bindings howto. + + commit 4811ff7b6c8ef97c7d4858ce235e9bf8227f4917 + * moved single encrytion examples up to the first ones, pending merge + and major cut. + * This is basically just to make future checks of revisions a little easier. + + doc: python bindings howto. + + commit 82c5af225f2bdf3acc6fc652a96ee61c9b057395 + * Stripped decryption example to the bare bones as suggested by Justus. + +2018-03-16 Ben McGinnes <ben@adversary.org> + + doc: python bindings howto. + + commit b549f69d0520bb74957b95cec9ea918dba2374f6 + * Made the changes suggested by Jakub Wilk on gnupg-devel. + * Still need to make the far more comprehensive changes suggested by Justus. + +2018-03-16 Werner Koch <wk@gnupg.org> + + json: Add framework for the gpgme-json tool. + + commit d2b31d8c106423bd0eaa5fffaa39b0983c9ae525 + * src/gpgme-json.c: New. + * src/Makefile.am (bin_PROGRAMS): Add gpgme-json. + (gpgme_json_SOURCES, gpgme_json_LDADD): New. + + core: Adjust cJSON code for use in GPGME. + + commit 81c90d0cd0f959fd5e01baed9b4af0ec35ecb85c + * src/cJSON.c: Remove util.h. Use gpgrt alloc functions. + (cJSON_Delete): Do not clobber ERRNO. + (cJSON_AddItemToObject): Return OBJECT or NULL. + (cJSON_AddNullToObject): New. + (cJSON_AddTrueToObject): New. + (cJSON_AddFalseToObject): New. + (cJSON_AddBoolToObject): New. + (cJSON_AddNumberToObject): New. + (cJSON_AddStringToObject): New. + * src/cJSON.h (cJSON__h): Replace macro by cJSON_h for C compliance. + (cJSON_AddNullToObject): Remove macro. + (cJSON_AddTrueToObject): Remove macro. + (cJSON_AddFalseToObject): Remove macro. + (cJSON_AddBoolToObject): Remove macro. + (cJSON_AddNumberToObject): Remove macro. + (cJSON_AddStringToObject): Remove macro. + +2018-03-15 Ben McGinnes <ben@adversary.org> + + doc: python bindings howto. + + commit 431897a4c48fe1bc9d37f655097aabaf5b685d11 + * Added clarification on why it's not on PyPI. + +2018-03-15 Andre Heinecke <aheinecke@intevation.de> + + cpp: Expose skipped_v3_keys. + + commit ad95288d3b3efc38998841add4fe658c84701f98 + * lang/cpp/src/importresult.cpp, + lang/cpp/src/importresult.h (ImportResult::numV3KeysSkipped): New. + + core: Parse skipped_v3_keys. + + commit a630a1e3e74c926163864b013cb164b4cd1866fc + * src/gpgme.h.in (gpgme_import_result_t): Extend with skipped_v3_keys. + * src/import.c (gpgme_op_import_result): Extend debug with new field. + (parse_import_res): Parse skipped_v3_keys. + * tests/gpg/t-support.h, tests/run-support.h (print_import_result): + Print skipped_v3_keys. + +2018-03-15 Ben McGinnes <ben@adversary.org> + + doc: python bindings howto. + + commit 22e2445beee46ed1e527a98e635153c7cf03786f + * fixed custom_id for decryption so the XHTML validates. + + doc: python bindings howto. + + commit 94a95ac12364989db7f4be333107f3c023551857 + * Promoted final encryption example so that it will appear as heading + 6.1.3 when exported to HTML or PDF. + + doc: python bindings howto. + + commit 3d0c7a2202c8e9bd4f284fd00069d34b8d3d3d4c + * Fixed a minor typographic error. + * Bumped version number in preparation for merge with master. + * While there are probably a few more things worthy of being added + (mainly how to revoke things), this document is essentially ready + for publication now. + + doc: python bindings howto. + + commit 961aea212ef48914ecbfa169addf951b0854b0b4 + * Added key signing. + + doc: python bindings howto. + + commit 7ac65b10837740caf68cdade791b8c5ce4eb1b03 + * Added a new user ID for Danger Mouse. + * Removed the empty entry for key preferences since that is handled + through gpg.conf and/or editing the key directly. + + doc: python bindings howto. + + commit 9e3e4a835c64f5d06de821b1fd648af37827ff26 + * Spell checking and fixing the few errors. + + doc: python bindings howto. + + commit b02d9d0a7b96b186eb3063d94bde369339181461 + * Added an encryption subkey to Danger Mouse's primary key. + + doc: python bindings howto. + + commit 5432e5f9d1dfc02812d0b181f8d88cdf4a2bfbfb + * generated a new primary key for Danger Mouse in an alternative homedir. + + doc: python bindings howto. + + commit 5d1dd2abe5cf787875d12afe46c78c75385d7b31 + * Added sections for key generation and key editing. + + doc: python bindings howto. + + commit 1d05e6aa4ea467c8c5926b827cfcfba357d03312 + * Added c.get_key instructions and examples. + + doc: python bindings howto. + + commit b35aaef7a3b793b8f6f5b42596c0a6a51e87f78c + * Added text for verifying signatures. + +2018-03-14 Ben McGinnes <ben@adversary.org> + + doc: python bindings howto. + + commit 6bc12a0eeb20409770cb8b923d08c18c2b730cb8 + * Added 4 signature verification methods and partial text for them. + + doc: python bindings howto. + + commit e5c85fba25de1187949697e2dae0e89345b71e89 + * Added description for detached signatures. + + doc: python bindings howto. + + commit ada059b07178147821b1598c935aa70ae45e3e6c + * Fixed multiple sample code examples of writing output to a file. + * Added the description of detached signatures. + +2018-03-14 Werner Koch <wk@gnupg.org> + + core: Import cJSON code from the payproc project. + + commit 8eb08b318913644d918002f3195f7ec0e75ae239 + * src/cJSON.c: New. + * src/cJSON.h: New. + * src/cJSON.readme: New. + +2018-03-14 Ben McGinnes <ben@adversary.org> + + doc: python bindings todo. + + commit ef27f3781a37e264d0eb7d1745eb2c804ec062c4 + * minor phrasing fix. + + doc: python bindings howto. + + commit 423fdcd4653cb01f07f2b0e72cfcf49554930f70 + * Added recommended method of single encryption with description. + +2018-03-13 Ben McGinnes <ben@adversary.org> + + doc: python binding howto. + + commit a71205dc3b58970adf591b4e4553824a33f353db + * Clarified which English dialects this is written in. + * Translating to American can happen *after* it's done. + ** The Yank version would probably want to change some of the examples + anyway. + * Began the description for normal/default signing. + + doc: python bindings howto. + + commit a10dcb4f138eb5a21881cdbc4806c25129d4ae4e + * Added a section on key selection. + * Included recommendation for using fingerprint when selecting one + specific key. + * Also included the most ironically amusing example of multiple key + selection in a GPG guide. Hey, it's public data ... (heh). + + doc: python bindings howto. + + commit 952b6042f78017c476452088261af8d352cfa729 + * Added explanation of the ascendance of Python 3 over Python 2 in the + guide to the intro. + * Expanded key selection description so people know what not to + include regarding key IDs with this key selection method. + + doc: python bindings howto. + + commit c92da2c7eb148ce9fb06495a8470dd9caf662f9a + * Added key selection for specifying signing key or keys. + + doc: python bindings howto. + + commit e489ddd08af29fdad8db8aa0aec0c314daa3678c + * During the course of working out the updated signature methods, + determined that key selection (including counting) will beed to be + presented before the basic functions. + * Moved "working with keys" up. + + doc: python bindings howto. + + commit f29bda8d7146b4bc0bf73d6e613131545ff86b73 + * Signatures have changed as a result of the recent update from Justus. + * Sample code updated. + * Text to follow later. + + doc: python bindings howto. + + commit c27a7a3f994dad0eccee890185582f4350fbf233 + * Added text description for the decryption example. + +2018-03-12 Ben McGinnes <ben@adversary.org> + + doc: python bindings howto. + + commit f81adeba992a9fd3b5a199e9a2e242a0f53cf639 + * Added a miscellaneous work-arounds section at the end. + * Included code in said miscellaneous section for accessing the groups + specified in a gpg.conf file. + * It's a bit ugly since it does require subprocess (but not call, + Popen or shell access and only accesses one command). + + doc: python bindings howto. + + commit 36dfbdffea60c529a6d1e1ff3e507be016b6a0f6 + * Fixed a spelling error in the key counting text. + + doc: python bindings howto. + + commit 484e9a6229ac9c80c6be4df638bce711f08a74c6 + * updated multi-encryption final example to be complete. + * second example shows most likely method of reading plaintext. + * updated example filenames to stick with running gag + (i.e. secret_plans.txt). + + doc: python bindings howto. + + commit a8f48b6f577d562c25fd0191c0cc2cc8e96078c1 + * error corrections. + * multiple typesetting fixes only required due to certain archaic + eccentricities of LaTeX. + * a couple of minor python PEP8 compliance corrections. + + doc: python bindings howto. + + commit 83b1336ceebb86e13a55bbf220df2d750f6b3ec6 + * Fixed an error in the encryption try/except statement. + + doc: python bindings howto. + + commit 0e1300ce777dd0c87f31ac8bc49846b9df242df9 + * Added a more complicated encryption example with a few variations on + the encryption method to account for untrusted recipient keys, + signing or not signing, including or excluding default keys and so + on. + + doc: python bindings howto. + + commit 7ebc5a357057d01b7ef965521ab68b7cb7e20a8f + * Switched from links to some external docs to using footnotes where + necessary. + * Ideally the howto should be as stand alone as possible. + * Also it makes it difficult to convert to another format for + proof-reading if there are links that the conversion can't find. + +2018-03-09 Ben McGinnes <ben@adversary.org> + + doc: python bindings HOWTO. + + commit 172baaf4d3e4ed03a4d3437be9efa3dfe6a847bc + * Added instructions and code to count the number of public and secret + keys available since it was quick and easy. + + doc: python TODO list. + + commit f2c1e8d8d54068a7f072efa178fc30460821eff3 + * Slightly tweaked one heading to make it clear it wasn't a duplicate. + + doc: python bindings howto. + + commit 01686463948ac6096dd8579a110c478d3a1f9a83 + * Wrote the text description explaining each step in the most basic + encryption operation. + * Will need to include additional examples for encrypting to multiple + recipients using Context().encrypt instead of Context().op_encrypt. + + doc: python bindings TODO list. + + commit 93252df9dc4c9932467814745655350a8cab900e + * Updated to reflect the most recent work on the HOWTO for the Python + bindings. + + doc: python bindings howto. + + commit ab81c2d868bba79fdb8f8d7f576b6bd88c6bdf3c + * Added example for verifying both detached and "in-line" signatures. + +2018-03-08 Ben McGinnes <ben@adversary.org> + + docs: TODO. + + commit e846c3daeeb4e7092169cdb7bf4f55e0b105aac3 + * Removed WS. + + doc: TODO list update. + + commit 6849924ffbd48ba3f9d32b4a59a02e1d2083fc19 + * Closed off a few ancient items and added recommendation to others to + use the dev.gnupg.org site for real bugs/features updates. + * See also this statement on the gnupg-devel list regarding this file: + https://lists.gnupg.org/pipermail/gnupg-devel/2018-March/033499.html + + docs: python bindings howto update. + + commit fa4927146b68dd045903285f1c45fb64deb2e361 + * Added all four signing code examples that are most likely to be + used: armoured, clearsigned, detached armoured and detached binary. + * May remove some examples and just discuss the differences, but it + depends on the way the text is filled out. + + doc: python bindings howto update. + + commit c767a4a3590bd8a224d0268746df443942cb28c2 + * Added example of decryption. + * included some quick notes for myself regarding aspects to explain + when I flesh out the explanatory text. + + doc: Basic operation of the python bindings. + + commit 75463d589522cba427f9e5a3a408192ffad8bb21 + * Added sample code for encrypting some text to a single key. + * Basically I'm just lifting existing production code and changing the + key IDs from mine to "0x12345678DEADBEEF" for these first few + examples. + * I'll fill in the text description after. + * Note: due to my regional location, I might split some tasks into + more commits in order to be sure no work gets lost in case of + emergency (or to put it another way: I know Telstra too well to + trust them). + + doc-howto: fundamental aspects of GPGME vs Python. + + commit a98f2c556fe6e33a9cd38279e64e4b09f05cc675 + * Added a section for those pythonistas who are too used to web + programming. Stressed that it's not simply not RESTful, it's not + even REST-like. + * Letting me move on to drawing a very loose parallel between a + session and a context. The differences should become obvious in the + subsequent sections. + + doc: Added multiple TODOs for inclusion in the HOWTO. + + commit e8adab68f8c0cd865ff220f06dfaff7fe183e8a1 + * Some instructions to include are fairly obvious; as with encryption, + decryption and signature verification. + * Some are a little less obvious. + * This includes the requests received to specifically include subkey + management (adding and revoking subkeys on a primary key that's + being retained. + * Added the UID equivalents to the list, as well as key selection + matters (and may or may not include something for handling group + lines since that involves wrapping a CLI binary). + * Key control documentation and examples requested by Mike Ingle of + confidantmail.org. + +2018-03-07 Ben McGinnes <ben@adversary.org> + + GPL compatible license for documentation. + + commit 47d401d159852ea08e90af21d91bb4b93be9000d + * Added the same, slightly modified GPL based license that is used in + other parts of GnuPG. + + HOWTO update. + + commit 8a76deb11efd7dadfde6e8e7e69fbcd92577982f + * removed one bit of whitespace. + * Marked up references to gpgme.h. + * Fixed one spelling error. + * Removed py2.6 from python search order since even if it is + supported, it shouldn't be encouraged. + + GPGME Python bindings HOWTO. + + commit 5215d58ae2521d81c3db0b45dfbdce01a679acab + * Started work on the GPGME Python bindings HOWTO. + * 1,050 words to begin with at approx. 7.5KB. + * Got as far as installation. + * Includes instruction not to use PyPI for this. + + TODO - HOWTO. + + commit 8f2c0f4534ea2a07f071f360a63e877f60dc52f2 + * Added suv-entry for the new HOWTO being started and, since it has + been started, checked it off. + + TODO. + + commit d4778bb23d0817ee6fbcbe4f0ff0ff0429bf3669 + * Slightly expanded the list. + + copyright fix. + + commit 3a746d5d46ffd7d332dc24fd6a4d24efc5fc1230 + * Made the copyright line a new top level org heading in order to + prevent it getting folded into other tasks which will eventually get + closed (so it doesn't go missing if those items are subsequently + archived). + + Nuxed doubles. + + commit 13d2164cd9f313b409b2210d9e63465681cccc99 + * Just because there's a lot of documentation which needs to be added, + doesn't mean it needs to be listed twice. Merged the two lists. + + Removed double. + + commit 1516c56ee4da28eb720bbacb026892393d10b24a + * default.profraw didn't need to be listed twice. + +2018-03-04 Ben McGinnes <ben@adversary.org> + + IDENTIFY. + + commit f61d4f585f27c13fabf7a23ad295bdc8bea7c838 + * Fixed sp error in docstring. + +2018-03-03 Ben McGinnes <ben@adversary.org> + + Missed a couple. + + commit 75f5e6e6672a1bbd16b7680313c0f96796c219bd + * WS indicated 2 custom-ids were missed, now they're set. + + TODO DONE. + + commit b438e5e44c2eaf22549db94141a3ec8731556674 + * Marked off a TODO for this clean-up. + + TODO. + + commit 1d910672539686e2e17fd8fa1a894cee92863417 + * WS removal + + TODO updates. + + commit 12a87af1df906744a14079ff7ff88e7d60679695 + * Updated TODO with tags to make everything appear properly when + exported to HTML or PDF. + * Added a couple more items, mainly to do with docs or future bindings. + * Marked some, but not all as actual TODO items. + * Some items should probably be removed, but haven't been yet. + * Some have probably been completed already. + +2018-02-27 Ben McGinnes <ben@adversary.org> + + New stuff. + + commit 85bdca3b2b095afb672f19abbffccd2bcb8bbd0a + * Added the idea for alternative Emacs bindings (to extend the + existing ones, not replace them outright). + * Added a reference to the API Squared part of the project. + + TODO the TODO. + + commit 8047e1374fe6e69e8c4502e58e6522ea86e4bef4 + * Added an actual TODO to fix the TODOs. + * Pretty sure I know who will be completing this one ... ;) + + TODO ... the TODO. + + commit 73c51bc9858de2aab25844e7e283a6334038ccc8 + * If we're going to use Org, let's actually use Org. + * Added the properties tags for each point. + * Some of thems still need custom ID tags assigned properly. + * Probably ought to make them actual TODOs at some point, but that can + wait until they can be marked as DONE anyway. + * Will add a TODO for some of this stuff ... oh, the irony. + +2018-02-27 NIIBE Yutaka <gniibe@fsij.org> + + core: Support non-thread-safe getenv. + + commit 59fe3f26c1ca0fba16f76738cd05aaf80fb735ef + * src/get-env.c (_gpgme_getenv): Use gpgrt_lock_lock + and gpgrt_lock_unlock to protect call of getnev. + +2018-02-26 Ben McGinnes <ben@adversary.org> + + WS removal. + + commit c58f61e92226c4a03f216f5d07bdc5479ce8a2d9 + * Whitespace removal. + + LaTeX headers. + + commit 6f2e2e0f150d5c6d53de5bc48af137f7864d5fd9 + * Set LaTeX headers to enable ligatures and a 12pt font by default. + * Paper size left for regional defaults. + * Using XeLaTeX for easier font control. + * Using default LaTeX font of Latin Main, but that's easy enough to change. + +2018-02-19 Ben McGinnes <ben@adversary.org> + + Renaming ad infinitum ... + + commit 272a8e778a959cb24298f488e77fce9bffd23b7a + * Dropped the .txt from the end of the file ... + + Text conversion. + + commit c82b17c6ce490738efb07186532e752e6f70ea07 + * Exported from Org Mode to UTF-8 text. + * Removed my name from just under the title. + + Title fix. + + commit ea481d4bb9e6197fbc195187d1362b844e2cba2e + * Fixed title. + + Conflict with Phabricator files. + + commit 1ae3ead2cd854258954e784bf51f03822321aefe + * The developers of Phabricator, the web front-end on dev.gnupg.org + have not implemented renderers for Markdown, Org-Mode or any other + common markdown like language. + * They also refuse to do so. + * Instead they re-invented the wheel and implemented their own version + of Markdown-like thing which is incompatible with everything else. + It is called Remarkup. + * The developers of Phabricator and Remarkup have refused to provide + conversion tools to move files to/from any format to/from Remarkup. + * They expect everyone to learn their new favourite pet project. + * Remarkup may or may not display Org Mode files, but if so then it is + likely to only want to do so as plain text. + * There is an unaffiliated and unofficial project to convert Github + Markdown to Remarkup via Pandoc. This might be adapted for our use, + but requires testing. + * Until then exporting from Org Mode to UTF-8 text is likely the least + worst plan. + * Which means renaming this file to README.org first. + + Schizophrenic file types. + + commit fe4f3edd70949329cb992dd963de2de3c86dcd81 + * Removed Markdown style heading underlining. + * Removed in-line file type declaration (which is not correctly parsed + by the web interface on dev.gnupg.org). + +2018-02-16 Andre Heinecke <aheinecke@intevation.de> + + cpp: Add shorthand for key locate. + + commit 7f9d5c6cd204bfd84ea477b284df795b1dadb1af + * lang/cpp/src/key.cpp (Key::locate): New static helper. + * lang/cpp/src/key.h: Update accordingly. + +2018-02-16 Ben McGinnes <ben@adversary.org> + + History path. + + commit fb16eaa685fe488f12f4df9b59d1f3689c813034 + * Fixed a typo in a filepath reference. + * Moved conjecture regarding the first version of Python used to a + footnote. + +2018-02-15 Ben McGinnes <ben@adversary.org> + + LaTeX margins. + + commit 6f15d821404742ac2683f54ca4102ee4aaedacf2 + * Added LaTeX header for 1 inch margins in the quite likely event that + all PDF output ultimately uses LaTeX. + + TODO Documentation. + + commit 235d899a5fc24cdf9c856adbc021a69c43985c99 + * Checked off the decision to stick with Org Mode. + + TODO. + + commit 40da5022922172ed898172956a8ccf5622e5638d + * Beginning to turn the first part of this into something kind of like + an actual TODO list as Org Mode uses it (maybe). + +2018-02-15 NIIBE Yutaka <gniibe@fsij.org> + + tests: Fix previous commit. + + commit 3224d7f0ea83a3c2baaa9f97846c4a5b392d2c59 + + + tests: More Makefile portability. + + commit ba6e610baa138ba9b43be303df2c5981dd04de5a + * tests/gpg/Makefile.am: Invoke GPG with TESTS_ENVIRONMENT. + + tests: Makefile portability. + + commit b5ec21b9baf017b4cee88c9ef3cc1a638547cd20 + * tests/gpg/Makefile.am: Don't use "export" directive. + * tests/gpgsm/Makefile.am: Ditto. + * lang/qt/tests/Makefile.am: Ditto. + * lang/python/tests/Makefile.am: Ditto. + + build: More Makefile fix. + + commit c9a351f5af289c8f6919854c40f235c781b76ec7 + * lang/python/tests/Makefile.am: Avoid target with '/'. + +2018-02-14 Ben McGinnes <ben@adversary.org> + + Short History. + + commit 3c3b149996036e7ff4cc4c77ef2d97062d880409 + * Fixed or updated the most fundamental errors. + * Also included some details on which modules are available on PyPI, + as well as what happened to the PyME commit log. + + Subsectioned history. + + commit 7c662d22a8f7d2e6ad6532014f2ecc1769de16fb + * Split the main parts down into subsections. + * Still need to cull the incorrect stuff towards the end and add more + recent changes. + + History. + + commit a1bc710c5fb7a7d2253434c1443e33e019020a55 + * Reshaping the history file to fit Org Mode's structuring for docs. + * Also said history needs to be a bit more clear (it was kind of + unfinished). + + TODO. + + commit fccd2ea3871f5d63fb038db0733a34f9c5d550c3 + * Updated TODO. + * The entirety of the old TODO has been replaced with either more + relevant tasks or goals for the examples and a more measured + approach to the docs and why, in this project, Org Mode trumps reST, + even though it's Python through and through. + + TODO. + + commit 487ed9337e8e07d1c63e53b785cc39e3bd35ff6e + * Removed reST version of file. + + TODO. + + commit c4fa4216199b16c1f169725c0a1e0a40764b1ebb + * Converted document from reST to org-mode. + + Short History. + + commit 1d48b04cfb3d1de654f3995578e9434a6b7b2fed + * Removed reST version. + + Short History. + + commit d86fd7c54c462ef23c70370624e07fcc1bc15121 + * Converted document from reST to org-mode. + + House keeping. + + commit 1b5719cd57d80dcd9577141491a0860912e74cd4 + * Added a bunch of things to .gitignore that might otherwise creep in + during Python development. + * This really should be merged ASAP. You'll thank me later ... + +2018-02-14 NIIBE Yutaka <gniibe@fsij.org> + + build: Fix Makefiles for portability. + + commit f1d8a7975b0a166f55aef06eb25d50230781b96f + * tests/gpg/Makefile.am: Don't allow target with '/'. + * tests/gpgsm/Makefile.am: Ditto. + +2018-02-13 Andre Heinecke <aheinecke@intevation.de> + + configure: Fix mingw check for getenv. + + commit 5ec890b9c76db0f23cc396108ff6f1fe658855a8 + * configure.ac: have_thread_safe_getenv=yes with have_w32_system. + +2018-02-13 NIIBE Yutaka <gniibe@fsij.org> + + configure: MinGW has thread-safe getenv. + + commit 77ca9cc2db2c6da303b9224a931679a325ebda1e + * configure.ac: have_thread_safe_getenv=yes with have_w64_system. + +2018-02-09 Andre Heinecke <aheinecke@intevation.de> + + cpp: Add SpawnShowWindow flag. + + commit 7e27a0ff64626026521dc5877b278794cea72e61 + * lang/cpp/src/context.h (SpawnShowWindow): New. + + core, w32: Enable spawning GUI applications. + + commit 201db83a7f1b7759173b6e9f0a844caef4da6cce + * src/engine-spawn.c (engspawn_start): Translate spawn flag + to IOSPAWN flag. + * src/gpgme-w32spawn.c (my_spawn): Handle the new flag. + * src/gpgme.h.in (GPGME_SPAWN_SHOW_WINDOW): New. + * src/priv-io.h (IOSPAWN_FLAG_SHOW_WINDOW): New. + + core, w32: Fix flags passing to gpgme-w32-spawn. + + commit f10605ffb5cc9d457c3e432918fdfbfaf3d04185 + * src/w32-io.c (_gpgme_io_spawn): Don't hardcode flags value. + + cpp: Add conveniance Data::toString. + + commit 5a5b0d4996c17bfbc69b90f89fec23732f92813a + * lang/cpp/src/data.h, lang/cpp/src/data.cpp: Add Data::toString. + +2018-01-30 NIIBE Yutaka <gniibe@fsij.org> + + Fix for BSD Make. + + commit 59fcabbdf537b2745ef0c3cae908b21970a5b39b + * tests/gpg/Makefile.am, tests/gpgsm/Makefile.am: Remove ./. + +2018-01-29 NIIBE Yutaka <gniibe@fsij.org> + + Fix compile error message. + + commit bbb5e70e7e8598978b6c61b13ba77705ff86e469 + * src/get-env.c (_gpgme_getenv): Fix error message. + + tests: Fix for NetBSD. + + commit 58130b97f6582455fd355ac58ae3182a28812cfc + * tests/gpg/pinentry: Use /bin/sh instead of Bash. + + core: Implement _gpgme_getenv for NetBSD. + + commit 37d62e9d0f685c257fdb5f303e60ff01f8a36a2f + * src/get-env.c [HAVE_GETENV_R] (_gpgme_getenv): New. + 2017-12-12 Werner Koch <wk@gnupg.org> Release 1.10.0. @@ -1,3 +1,65 @@ +Noteworthy changes in version 1.11.0 (2018-04-18) +------------------------------------------------- + + * New encryption API to support direct key specification including + hidden recipients option and taking keys from a file. This also + allows to enforce the use of a subkey. + + * New encryption flag for the new API to enforce the use of plain + mail addresses (addr-spec). + + * The import API can now tell whether v3 keys are skipped. These old + and basically broken keys are not anymore supported by GnuPG 2.1. + + * The decrypt and verify API will now return the MIME flag as + specified by RFC-4880bis. + + * The offline mode now has an effect on gpg by disabling all network + access. [#3831] + + * A failed OpenPGP verification how returns the fingerprint of the + intended key if a recent gpg version was used for signature + creation. + + * New tool gpgme-json as native messaging server for web browsers. + As of now public key encryption and decryption is supported. + Requires Libgpg-error 1.29. + + * New context flag "request-origin" which has an effect when used + with GnuPG 2.2.6 or later. + + * New context flag "no-symkey-cache" which has an effect when used + with GnuPG 2.2.7 or later. + + * New convenience constant GPGME_KEYLIST_MODE_LOCATE. + + * Improved the Python documentation. + + * Fixed a potential regression with GnuPG 2.2.6 or later. + + * Fixed a crash in the Python bindings on 32 bit platforms. [#3892] + + * Various minor fixes. + + * Interface changes relative to the 1.10.0 release: + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + gpgme_op_encrypt_ext NEW. + gpgme_op_encrypt_ext_start NEW. + gpgme_op_encrypt_sign_ext NEW. + gpgme_op_encrypt_sign_ext_start NEW. + GPGME_ENCRYPT_WANT_ADDRESS NEW. + GPGME_KEYLIST_MODE_LOCATE NEW. + gpgme_import_result_t EXTENDED: New field 'skipped_v3_keys'. + gpgme_decrypt_result_t EXTENDED: New field 'symkey_algo'. + gpgme_decrypt_result_t EXTENDED: New field 'is_mime'. + gpgme_verify_result_t EXTENDED: New field 'is_mime'. + cpp: Key::locate NEW. + cpp: Data::toString NEW. + cpp: ImportResult::numV3KeysSkipped NEW. + + [c=C31/A20/R0 cpp=C12/A6/R0 qt=C10/A3/R1] + + Noteworthy changes in version 1.10.0 (2017-12-12) ------------------------------------------------- @@ -1,7 +1,7 @@ GPGME - GnuPG Made Easy --------------------------- -Copyright 2001-2017 g10 Code GmbH +Copyright 2001-2018 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 @@ -1,58 +1,254 @@ +#+TITLE: TODO List Hey Emacs, this is -*- org -*- mode! -* Document all the new stuff. +* IMPORTANT! + :PROPERTIES: + :CUSTOM_ID: dev-gnupg-org + :END: + + There was a nine year gap (2009 to 2018) between edits of this file, + so it is likely that much of the old information in it is wrong or + no longer applicable. + + Bugs, feature requests and other development related work will be + tracked through the [[https://dev.gnupg.org/][dev.gnupg.org]] site. + + +* Documentation + :PROPERTIES: + :CUSTOM_ID: documentation + :END: + +** Document all the new stuff. + :PROPERTIES: + :CUSTOM_ID: more-docs-is-better + :END: + +*** TODO Fix this TODO list. + :PROPERTIES: + :CUSTOM_ID: fix-todo + :END: + + Clean up the current TODO list. Include properties as relevant (so + if someone does make a PDF or HTML version the TOC will work). + + Also check ans see if some of these ancient things can be removed + (e.g. do we really need to fix things that were broken in GPG + 1.3.x? I'm thinking not so much). + +**** DONE fix TODO items + CLOSED: [2018-03-04 Sun 08:55] + :PROPERTIES: + :CUSTOM_ID: fix-todo-items + :END: + + Adjust todo items so each can now be referenced by custom-id and + checked off as necessary. + +** TODO Document validity and trust issues. + :PROPERTIES: + :CUSTOM_ID: valid-trust-issues + :END: + +** In gpgme.texi: Register callbacks under the right letter in the index. + :PROPERTIES: + :CUSTOM_ID: gpgme-texi + :END: + + * Fix the remaining UI Server problems: + :PROPERTIES: + :CUSTOM_ID: ui-server-fix + :END: ** VERIFY --silent support. + :PROPERTIES: + :CUSTOM_ID: verify-silent + :END: ** ENCRYPT/DECRYPT/VERIFY/SIGN reset the engine, shouldn't be done with UISERVER? + :PROPERTIES: + :CUSTOM_ID: reset-engine-not-ui + :END: + * IMPORTANT + :PROPERTIES: + :CUSTOM_ID: important-stuff-really + :END: ** When using descriptor passing, we need to set the fd to blocking before + :PROPERTIES: + :CUSTOM_ID: set-fd-blocking + :END: issueing simple commands, because we are mixing synchronous commands into potentially asynchronous operations. -** Might want to implement nonblock for w32 native backend! Right now, - we block reading the next line with assuan. +** Might want to implement nonblock for w32 native backend! + :PROPERTIES: + :CUSTOM_ID: nonblock-win32 + :END: + Right now we block reading the next line with assuan. + * Before release: -** Some gpg tests fail with gpg 1.3.4-cvs (gpg/t-keylist-sig) + :PROPERTIES: + :CUSTOM_ID: pre-release + :END: + +** CANCELLED Some gpg tests fail with gpg 1.3.4-cvs (gpg/t-keylist-sig) + CLOSED: [2018-03-09 Fri 08:16] + :PROPERTIES: + :CUSTOM_ID: gpg-1-3-4-really + :END: + - State "CANCELLED" from "TODO" [2018-03-09 Fri 08:16] \\ + WON'T FIX — too old or no longer applies. The test is currently disabled there and in gpg/t-import. + ** When gpg supports it, write binary subpackets directly, + :PROPERTIES: + :CUSTOM_ID: binary-subpackets + :END: and parse SUBPACKET status lines. + * ABI's to break: + :PROPERTIES: + :CUSTOM_ID: abi-breakage-apparently-on-purpose + :END: + ** Old opassuan interface. + :PROPERTIES: + :CUSTOM_ID: old-opassuan + :END: + ** Implementation: Remove support for old style error codes in + :PROPERTIES: + :CUSTOM_ID: remove-old-error-codes + :END: conversion.c::_gpgme_map_gnupg_error. + ** gpgme_edit_cb_t: Add "processed" return argument + :PROPERTIES: + :CUSTOM_ID: add-processed-return + :END: (see edit.c::command_handler). + ** I/O and User Data could be made extensible. But this can be done + :PROPERTIES: + :CUSTOM_ID: add-io-user-data + :END: without breaking the ABI hopefully. + ** All enums should be replaced by ints and simple macros for + :PROPERTIES: + :CUSTOM_ID: enums-should-be-ints + :END: maximum compatibility. + ** Compatibility interfaces that can be removed in future versions: + :PROPERTIES: + :CUSTOM_ID: compat-interfaces-to-go + :END: + *** gpgme_data_new_from_filepart + :PROPERTIES: + :CUSTOM_ID: gpgme-data-new-from-filepart + :END: + *** gpgme_data_new_from_file + :PROPERTIES: + :CUSTOM_ID: gpgme-data-new-from-file + :END: + *** gpgme_data_new_with_read_cb + :PROPERTIES: + :CUSTOM_ID: gpgme-data-new-with-read-cb + :END: + *** gpgme_data_rewind + :PROPERTIES: + :CUSTOM_ID: gpgme-data-rewind + :END: + *** gpgme_op_import_ext + :PROPERTIES: + :CUSTOM_ID: gpgme-op-import-ext + :END: + *** gpgme_get_sig_key + :PROPERTIES: + :CUSTOM_ID: gpgme-get-sig-key + :END: + *** gpgme_get_sig_ulong_attr + :PROPERTIES: + :CUSTOM_ID: gpgme-get-sig-ulong-attr + :END: + *** gpgme_get_sig_string_attr + :PROPERTIES: + :CUSTOM_ID: gpgme-get-sig-string-attr + :END: + *** GPGME_SIG_STAT_* + :PROPERTIES: + :CUSTOM_ID: gpgme-sig-stat + :END: + *** gpgme_get_sig_status + :PROPERTIES: + :CUSTOM_ID: gpgme-get-sig-status + :END: + *** gpgme_trust_item_release + :PROPERTIES: + :CUSTOM_ID: gpgme-trust-item-release + :END: + *** gpgme_trust_item_get_string_attr + :PROPERTIES: + :CUSTOM_ID: gpgme-trust-item-get-string-attr + :END: + *** gpgme_trust_item_get_ulong_attr + :PROPERTIES: + :CUSTOM_ID: gpgme-trust-item-get-ulong-attr + :END: + *** gpgme_attr_t + :PROPERTIES: + :CUSTOM_ID: gpgme-attr-t + :END: + *** All Gpgme* typedefs. + :PROPERTIES: + :CUSTOM_ID: all-gpgme-typedefs + :END: * Thread support: + :PROPERTIES: + :CUSTOM_ID: threads + :END: + ** When GNU Pth supports sendmsg/recvmsg, wrap them properly. + :PROPERTIES: + :CUSTOM_ID: wrap-oth + :END: + ** Without timegm (3) support our ISO time parser is not thread safe. + :PROPERTIES: + :CUSTOM_ID: time-threads + :END: There is a configure time warning, though. + * New features: + :PROPERTIES: + :CUSTOM_ID: new-features + :END: + ** Flow control for data objects. + :PROPERTIES: + :CUSTOM_ID: flow-control-is-not-a-euphemism-for-an-s-bend + :END: Currently, gpgme_data_t objects are assumed to be blocking. To break this assumption, we need either (A) a way for an user I/O callback to store the current operation in a continuation that can @@ -61,9 +257,17 @@ Hey Emacs, this is -*- org -*- mode! respective event loop. or (B) a way for gpgme data objects to be associated with a waitable object, that can be registered with the user event loop. Neither is particularly simple. + ** Extended notation support. When gpg supports arbitrary binary + :PROPERTIES: + :CUSTOM_ID: extended-notation + :END: notation data, provide a user interface for that. + ** notification system + :PROPERTIES: + :CUSTOM_ID: notification-system + :END: We need a simple notification system, probably a simple callback with a string and some optional arguments. This is for example required to notify an application of a changed smartcard, The @@ -75,35 +279,77 @@ Hey Emacs, this is -*- org -*- mode! sufficient for this. ** --learn-code support + :PROPERTIES: + :CUSTOM_ID: learn-code + :END: This might be integrated with import. we still need to work out how to learn a card when gpg and gpgsm have support for smartcards. In GPA we currently invoke gpg directly. ** Might need a stat() for data objects and use it for length param to gpg. + :PROPERTIES: + :CUSTOM_ID: stat-data + :END: + ** Implement support for photo ids. + :PROPERTIES: + :CUSTOM_ID: photo-id + :END: + ** Allow selection of subkeys + :PROPERTIES: + :CUSTOM_ID: subkey-selection + :END: + ** Allow to return time stamps in ISO format - This allows us to handle years later than 2037 properly. With the - time_t interface they are all mapped to 2037-12-31 + :PROPERTIES: + :CUSTOM_ID: iso-format-datetime + :END: + This allows us to handle years later than 2037 properly. With the + time_t interface they are all mapped to 2037-12-31 + ** New features requested by our dear users, but rejected or left for + :PROPERTIES: + :CUSTOM_ID: feature-requests + :END: later consideration: + *** Allow to export secret keys. + :PROPERTIES: + :CUSTOM_ID: export-secret-keys + :END: Rejected because this is conceptually flawed. Secret keys on a smart card can not be exported, for example. May eventually e supproted with a keywrapping system. + *** Selecting the key ring, setting the version or comment in output. + :PROPERTIES: + :CUSTOM_ID: select-keyring-version + :END: Rejected because the naive implementation is engine specific, the configuration is part of the engine's configuration or readily worked around in a different way + *** Selecting the symmetric cipher. + :PROPERTIES: + :CUSTOM_ID: symmetric-cipher-selection + :END: + *** Exchanging keys with key servers. + :PROPERTIES: + :CUSTOM_ID: key-server-exchange + :END: -* Documentation -** Document validity and trust issues. -** In gpgme.texi: Register callbacks under the right letter in the index. * Engines + :PROPERTIES: + :CUSTOM_ID: engines + :END: + ** Do not create/destroy engines, but create engine and then reset it. + :PROPERTIES: + :CUSTOM_ID: reset-engine-is-not-quite-just-ignition + :END: Internally the reset operation still spawns a new engine process, but this can be replaced with a reset later. Also, be very sure to release everything properly at a reset and at an error. Think hard @@ -112,85 +358,255 @@ Hey Emacs, this is -*- org -*- mode! Note that we need support in gpgsm to set include-certs to default as RESET does not reset it, also for no_encrypt_to and probably other options. + ** Optimize the case where a data object has an underlying fd we can pass + :PROPERTIES: + :CUSTOM_ID: optimus-data-cousin-of-optimus-prime + :END: directly to the engine. This will be automatic with socket I/O and descriptor passing. + ** Move code common to all engines up from gpg to engine. + :PROPERTIES: + :CUSTOM_ID: move-code-common-to-engines-out-of-gpg + :END: + ** engine operations can return General Error on unknown protocol + :PROPERTIES: + :CUSTOM_ID: general-error-looking-to-be-court-martialled + :END: (it's an internal error, as select_protocol checks already). + ** When server mode is implemented properly, more care has to be taken to + :PROPERTIES: + :CUSTOM_ID: server-mode + :END: release all resources on error (for example to free assuan_cmd). -** op_import_keys and op_export_keys have a limit ion the number of keys. + +** op_import_keys and op_export_keys have a limit in the number of keys. + :PROPERTIES: + :CUSTOM_ID: import-export-problems + :END: This is because we pass them in gpg via the command line and gpgsm via an assuan control line. We should pipe them instead and maybe change gpg/gpgsm to not put them in memory. + * GPG breakage: -** gpg 1.4.2 lacks error reporting if sign/encrypt with revoked key. -** gpg 1.4.2 does crappy error reporting (namely none at all) when + :PROPERTIES: + :CUSTOM_ID: gpg-breakage + :END: + +** CANCELLED gpg 1.4.2 lacks error reporting if sign/encrypt with revoked key. + CLOSED: [2018-03-09 Fri 08:19] + :PROPERTIES: + :CUSTOM_ID: gpg-classic-lacks-stuff + :END: + - State "CANCELLED" from "TODO" [2018-03-09 Fri 08:19] \\ + WON'T FIX. + +** CANCELLED gpg 1.4.2 does crappy error reporting (namely none at all) when + CLOSED: [2018-03-09 Fri 08:20] + :PROPERTIES: + :CUSTOM_ID: gpg-classic-problems-but-do-we-care + :END: + - State "CANCELLED" from "TODO" [2018-03-09 Fri 08:20] \\ + WON'T FIX. smart card is missing for sign operation: [GNUPG:] CARDCTRL 4 gpg: selecting openpgp failed: ec=6.110 gpg: signing failed: general error [GNUPG:] BEGIN_ENCRYPTION 2 10 gpg: test: sign+encrypt failed: general error -** Without agent and with wrong passphrase, gpg 1.4.2 enters into an + +** DONE Without agent and with wrong passphrase, gpg 1.4.2 enters into an + CLOSED: [2018-03-09 Fri 08:20] + :PROPERTIES: + :CUSTOM_ID: recursive-gpg-classic + :END: + - State "DONE" from "TODO" [2018-03-09 Fri 08:20] \\ + Must have been fixed in a subsequent release. infinite loop. -** Use correct argv[0] + +** CANCELLED Use correct argv[0] + CLOSED: [2018-03-09 Fri 08:24] + :PROPERTIES: + :CUSTOM_ID: correct-argv + :END: + - State "CANCELLED" from "TODO" [2018-03-09 Fri 08:24] \\ + WON'T FIX. + + Also, there is no rungpg.c file in GPGME (or in GPG or most, if not + all of the rest of the libs and packages; I suspect there hasn't been + for a very long time). In rungpg.c:build_argv we use argv[argc] = strdup ("gpg"); /* argv[0] */ This should be changed to take the real file name used in account. * Operations + :PROPERTIES: + :CUSTOM_ID: operations-are-not-surgical + :END: + ** Include cert values -2, -1, 0 and 1 should be defined as macros. + :PROPERTIES: + :CUSTOM_ID: certified-macros + :END: + ** If an operation failed, make sure that the result functions don't return + :PROPERTIES: + :CUSTOM_ID: operation-failure + :END: corrupt partial information. !!! NOTE: The EOF status handler is not called in this case !!! + ** Verify must not fail on NODATA premature if auto-key-retrieval failed. + :PROPERTIES: + :CUSTOM_ID: autobot-key-retrieval + :END: It should not fail silently if it knows there is an error. !!! + ** All operations: Better error reporting. !! + :PROPERTIES: + :CUSTOM_ID: better-reporting-not-like-fox-news + :END: + ** Export status handler need much more work. !!! + :PROPERTIES: + :CUSTOM_ID: export-status-handler + :END: + ** Import should return a useful error when one happened. + :PROPERTIES: + :CUSTOM_ID: import-useful-stuff-even-wrong-stuff + :END: + *** Import does not take notice of NODATA status report. + :PROPERTIES: + :CUSTOM_ID: import-no-data + :END: + *** When GPGSM does issue IMPORT_OK status reports, make sure to check for + :PROPERTIES: + :CUSTOM_ID: gpgsm-import-ok + :END: them in tests/gpgs m/t-import.c. + ** Verify can include info about version/algo/class, but currently + :PROPERTIES: + :CUSTOM_ID: verify-class + :END: this is only available for gpg, not gpgsm. + ** Return ENC_TO output in verify result. Again, this is not available + :PROPERTIES: + :CUSTOM_ID: return-to-enc + :END: for gpgsm. + ** Genkey should return something more useful than General_Error. + :PROPERTIES: + :CUSTOM_ID: general-key-assumed-command-from-general-error + :END: + ** If possible, use --file-setsize to set the file size for proper progress + :PROPERTIES: + :CUSTOM_ID: file-setsize + :END: callback handling. Write data interface for file size. + ** Optimize the file descriptor list, so the number of open fds is + :PROPERTIES: + :CUSTOM_ID: optimus-descriptus-younger-brother-of-optimus-prime + :END: always known easily. + ** Encryption: It should be verified that the behaviour for partially untrusted + :PROPERTIES: + :CUSTOM_ID: only-mostly-dead-means-partially-alive + :END: recipients is correct. + ** When GPG issues INV_something for invalid signers, catch them. + :PROPERTIES: + :CUSTOM_ID: invalid-sig + :END: + * Error Values + :PROPERTIES: + :CUSTOM_ID: error-value + :END: + ** Map ASSUAN/GpgSM ERR error values in a better way than is done now. !! + :PROPERTIES: + :CUSTOM_ID: map-ass-error + :END: + ** Some error values should identify the source more correctly (mostly error + :PROPERTIES: + :CUSTOM_ID: source-errors + :END: values derived from status messages). + ** In rungpg.c we need to check the version of the engine + :PROPERTIES: + :CUSTOM_ID: rungpg-c-engine-ver + :END: This requires a way to get the cached version number from the engine layer. * Tests -** Write a fake gpg-agent so that we can supply known passphrases to + :PROPERTIES: + :CUSTOM_ID: tests + :END: + +** TODO Write a fake gpg-agent so that we can supply known passphrases to + :PROPERTIES: + :CUSTOM_ID: test-fake-gpg-agent + :END: gpgsm and setup the configuration files to use the agent. Without this we are testing a currently running gpg-agent which is not a clever idea. ! + ** t-data + :PROPERTIES: + :CUSTOM_ID: test-data + :END: + *** Test gpgme_data_release_and_get_mem. + :PROPERTIES: + :CUSTOM_ID: test-gpgme-data-release-mem + :END: + *** Test gpgme_data_seek for invalid types. + :PROPERTIES: + :CUSTOM_ID: test-gpgme-data-seek + :END: + ** t-keylist + :PROPERTIES: + :CUSTOM_ID: test-keylist + :END: Write a test for ext_keylist. + ** Test reading key signatures. + :PROPERTIES: + :CUSTOM_ID: test-key-sig + :END: + * Debug + :PROPERTIES: + :CUSTOM_ID: debug + :END: + ** Tracepoints should be added at: Every public interface enter/leave, + :PROPERTIES: + :CUSTOM_ID: tracepoint-pub-int + :END: before and in every callback, at major decision points, at every internal data point which might easily be observed by the outside (system handles). We also trace handles and I/O support threads in @@ -202,21 +618,83 @@ Hey Emacs, this is -*- org -*- mode! decrypt-verify.c delete.c edit.c encrypt.c encrypt-sign.c export.c genkey.c import.c key.c keylist.c passphrase.c progress.c signers.c sig-notation.c trust-item.c trustlist.c verify.c -** Handle malloc and vasprintf errors. But decide first if they should be + +** TODO Handle malloc and vasprintf errors. But decide first if they should be + :PROPERTIES: + :CUSTOM_ID: malloc-vasprintf + :END: + ignored (and logged with 255?!), or really be assertions. ! + * Build suite -** Make sure everything is cleaned correctly (esp. test area). -** Enable AC_CONFIG_MACRO_DIR and bump up autoconf version requirement. + :PROPERTIES: + :CUSTOM_ID: build-suite + :END: + +** TODO Make sure everything is cleaned correctly (esp. test area). + :PROPERTIES: + :CUSTOM_ID: clean-tests + :END: + +** TODO Enable AC_CONFIG_MACRO_DIR and bump up autoconf version requirement. + :PROPERTIES: + :CUSTOM_ID: autoconf-macros + :END: (To fix "./autogen.sh; ./configure --enable-maintainer-mode; touch configure.ac; make"). Currently worked around with ACLOCAL_AMFLAGS??? + * Error checking -** engine-gpgsm, with-validation + :PROPERTIES: + :CUSTOM_ID: error-checking + :END: + +** TODO engine-gpgsm, with-validation + :PROPERTIES: + :CUSTOM_ID: gpgsm-validation + :END: Add error checking some time after releasing a new gpgsm. -Copyright 2004, 2005 g10 Code GmbH +* Language bindings and related components + :PROPERTIES: + :CUSTOM_ID: language-bindings-and-related-stuff + :END: + +** TODO Emacs and elisp binding + :PROPERTIES: + :CUSTOM_ID: emacs-and-elisp + :END: + + Currently GNU Emacs uses EPA and EPG to provide GnuPG support. EPG + does this by calling the GPG executable and wrapping the commands + with elisp functions. A more preferable solution would be to + implement an epgme.el which integrated with GPGME, then if it could + not to attempt calling the gpgme-tool and only if those failed to + fall back to the current epg.el and calling the command line + binaries. + +** TODO API of an API + :PROPERTIES: + :CUSTOM_ID: api-squared + :END: + + See the more detailed notes on this in the [[lang/python/docs/TODO.org][python TODO]]. + +** TODO GPGME installation and package management guide + :PROPERTIES: + :CUSTOM_ID: package-management + :END: + + Write a guide/best practices for maintainers of GPGME packages with + third party package management systems. + + +* Copyright 2004, 2005, 2018 g10 Code GmbH + :PROPERTIES: + :CUSTOM_ID: copyright-and-license + :END: This file is free software; as a special exception the author gives unlimited permission to copy and/or distribute it, with or without @@ -1 +1 @@ -1.10.0 +1.11.0 diff --git a/config.h.in b/config.h.in index 276dbef..d388ae5 100644 --- a/config.h.in +++ b/config.h.in @@ -281,5 +281,5 @@ #define GPG_ERR_ENABLE_ERRNO_MACROS 1 #define CRIGHTBLURB "Copyright (C) 2000 Werner Koch\n" \ - "Copyright (C) 2001--2017 g10 Code GmbH\n" + "Copyright (C) 2001--2018 g10 Code GmbH\n" @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for gpgme 1.10.0. +# Generated by GNU Autoconf 2.69 for gpgme 1.11.0. # # Report bugs to <http://bugs.gnupg.org>. # @@ -590,8 +590,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='gpgme' PACKAGE_TARNAME='gpgme' -PACKAGE_VERSION='1.10.0' -PACKAGE_STRING='gpgme 1.10.0' +PACKAGE_VERSION='1.11.0' +PACKAGE_STRING='gpgme 1.11.0' PACKAGE_BUGREPORT='http://bugs.gnupg.org' PACKAGE_URL='' @@ -1457,7 +1457,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures gpgme 1.10.0 to adapt to many kinds of systems. +\`configure' configures gpgme 1.11.0 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1527,7 +1527,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of gpgme 1.10.0:";; + short | recursive ) echo "Configuration of gpgme 1.11.0:";; esac cat <<\_ACEOF @@ -1677,7 +1677,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -gpgme configure 1.10.0 +gpgme configure 1.11.0 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -2507,7 +2507,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by gpgme $as_me 1.10.0, which was +It was created by gpgme $as_me 1.11.0, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2870,20 +2870,20 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu # (Interfaces added: AGE++) # (Interfaces removed/changed: AGE=0) # -LIBGPGME_LT_CURRENT=30 -LIBGPGME_LT_AGE=19 +LIBGPGME_LT_CURRENT=31 +LIBGPGME_LT_AGE=20 LIBGPGME_LT_REVISION=0 # If there is an ABI break in gpgmepp or qgpgme also bump the # version in IMPORTED_LOCATION in the GpgmeppConfig-w32.cmake.in.in -LIBGPGMEPP_LT_CURRENT=11 -LIBGPGMEPP_LT_AGE=5 +LIBGPGMEPP_LT_CURRENT=12 +LIBGPGMEPP_LT_AGE=6 LIBGPGMEPP_LT_REVISION=0 LIBQGPGME_LT_CURRENT=10 LIBQGPGME_LT_AGE=3 -LIBQGPGME_LT_REVISION=0 +LIBQGPGME_LT_REVISION=1 # If the API is changed in an incompatible way: increment the next counter. GPGME_CONFIG_API_VERSION=1 @@ -2898,7 +2898,7 @@ PACKAGE=$PACKAGE_NAME VERSION=$PACKAGE_VERSION VERSION_MAJOR=1 -VERSION_MINOR=10 +VERSION_MINOR=11 VERSION_MICRO=0 ac_aux_dir= @@ -3420,7 +3420,7 @@ fi # Define the identity of the package. PACKAGE='gpgme' - VERSION='1.10.0' + VERSION='1.11.0' cat >>confdefs.h <<_ACEOF @@ -6470,7 +6470,7 @@ cat >>confdefs.h <<_ACEOF #define VERSION "$VERSION" _ACEOF -VERSION_NUMBER=0x010a00 +VERSION_NUMBER=0x010b00 # We need to compile and run a program on the build machine. A @@ -20754,7 +20754,7 @@ ENABLED_LANGUAGES=$enabled_languages # # Provide information about the build. # -BUILD_REVISION="6a42eb5" +BUILD_REVISION="3f55c52" cat >>confdefs.h <<_ACEOF @@ -20763,7 +20763,7 @@ _ACEOF BUILD_FILEVERSION=`echo "$PACKAGE_VERSION"|sed 's/\([0-9.]*\).*/\1./;s/\./,/g'` -BUILD_FILEVERSION="${BUILD_FILEVERSION}27202" +BUILD_FILEVERSION="${BUILD_FILEVERSION}16213" # Check whether --enable-build-timestamp was given. @@ -21662,7 +21662,7 @@ $as_echo "$ac_cv_gnu_library_2_1" >&6; } GLIBC21="$ac_cv_gnu_library_2_1" -if test $GLIBC21 = yes; then +if test $GLIBC21 = yes -o $have_w32_system = yes; then have_thread_safe_getenv=yes fi if test $have_thread_safe_getenv = yes; then @@ -23006,7 +23006,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by gpgme $as_me 1.10.0, which was +This file was extended by gpgme $as_me 1.11.0, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -23072,7 +23072,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -gpgme config.status 1.10.0 +gpgme config.status 1.11.0 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" @@ -25384,7 +25384,7 @@ fi echo " GPGME v${VERSION} has been configured as follows: - Revision: 6a42eb5 (27202) + Revision: 3f55c52 (16213) Platform: $host UI Server: $uiserver diff --git a/configure.ac b/configure.ac index 6ea4bcd..a04b059 100644 --- a/configure.ac +++ b/configure.ac @@ -28,7 +28,7 @@ min_automake_version="1.14" # 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, [10]) +m4_define(mym4_version_minor, [11]) m4_define(mym4_version_micro, [0]) # Below is m4 magic to extract and compute the revision number, the @@ -55,20 +55,20 @@ AC_INIT([gpgme],[mym4_full_version],[http://bugs.gnupg.org]) # (Interfaces added: AGE++) # (Interfaces removed/changed: AGE=0) # -LIBGPGME_LT_CURRENT=30 -LIBGPGME_LT_AGE=19 +LIBGPGME_LT_CURRENT=31 +LIBGPGME_LT_AGE=20 LIBGPGME_LT_REVISION=0 # If there is an ABI break in gpgmepp or qgpgme also bump the # version in IMPORTED_LOCATION in the GpgmeppConfig-w32.cmake.in.in -LIBGPGMEPP_LT_CURRENT=11 -LIBGPGMEPP_LT_AGE=5 +LIBGPGMEPP_LT_CURRENT=12 +LIBGPGMEPP_LT_AGE=6 LIBGPGMEPP_LT_REVISION=0 LIBQGPGME_LT_CURRENT=10 LIBQGPGME_LT_AGE=3 -LIBQGPGME_LT_REVISION=0 +LIBQGPGME_LT_REVISION=1 # If the API is changed in an incompatible way: increment the next counter. GPGME_CONFIG_API_VERSION=1 @@ -656,7 +656,7 @@ fi # Try to find a thread-safe version of getenv(). have_thread_safe_getenv=no jm_GLIBC21 -if test $GLIBC21 = yes; then +if test $GLIBC21 = yes -o $have_w32_system = yes; then have_thread_safe_getenv=yes fi if test $have_thread_safe_getenv = yes; then @@ -810,7 +810,7 @@ AH_BOTTOM([ #define GPG_ERR_ENABLE_ERRNO_MACROS 1 #define CRIGHTBLURB "Copyright (C) 2000 Werner Koch\n" \ - "Copyright (C) 2001--2017 g10 Code GmbH\n" + "Copyright (C) 2001--2018 g10 Code GmbH\n" ]) diff --git a/doc/gpgme.info b/doc/gpgme.info index 82aeb5b..dfdad87 100644 --- a/doc/gpgme.info +++ b/doc/gpgme.info @@ -19,8 +19,8 @@ END-INFO-DIR-ENTRY This file documents the GPGME library. - This is Edition 1.10.0, last updated 8 December 2017, of â€The â€GnuPG -Made Easy’ Reference Manual’, for Version 1.10.0. + This is Edition 1.10.1-beta188, last updated 8 December 2017, of â€The +â€GnuPG Made Easy’ Reference Manual’, for Version 1.10.1-beta188. Copyright © 2002–2008, 2010, 2012–2017 g10 Code GmbH. @@ -37,130 +37,130 @@ Public License for more details. Indirect: -gpgme.info-1: 1673 -gpgme.info-2: 302282 +gpgme.info-1: 1689 +gpgme.info-2: 302743 Tag Table: (Indirect) -Node: Top1673 -Node: Introduction9267 -Node: Getting Started10057 -Node: Features11518 -Node: Overview12830 -Node: Preparation13939 -Node: Header14936 -Node: Building the Source15683 -Node: Largefile Support (LFS)17827 -Node: Using Automake23243 -Node: Using Libtool25796 -Node: Library Version Check26158 -Node: Signal Handling32218 -Node: Multi-Threading33488 -Ref: Multi-Threading-Footnote-134904 -Node: Protocols and Engines35327 -Node: Engine Version Check38078 -Node: Engine Information40601 -Node: Engine Configuration44461 -Node: OpenPGP45765 -Node: Cryptographic Message Syntax46105 -Node: Assuan46418 -Node: Algorithms46792 -Ref: Algorithms-Footnote-147271 -Node: Public Key Algorithms47399 -Node: Hash Algorithms50001 -Node: Error Handling51215 -Node: Error Values53089 -Node: Error Sources58292 -Node: Error Codes60732 -Node: Error Strings65525 -Node: Exchanging Data67332 -Node: Creating Data Buffers69217 -Node: Memory Based Data Buffers69733 -Node: File Based Data Buffers73166 -Node: Callback Based Data Buffers75368 -Node: Destroying Data Buffers79559 -Node: Manipulating Data Buffers81066 -Node: Data Buffer I/O Operations81558 -Node: Data Buffer Meta-Data83931 -Node: Data Buffer Convenience88450 -Node: Contexts90670 -Node: Creating Contexts91856 -Node: Destroying Contexts92703 -Node: Result Management93042 -Node: Context Attributes94623 -Node: Protocol Selection95660 -Node: Crypto Engine96692 -Node: Setting the Sender98581 -Node: ASCII Armor100094 -Node: Text Mode100723 -Node: Offline Mode101657 -Node: Pinentry Mode102767 -Node: Included Certificates104661 -Node: Key Listing Mode106107 -Node: Passphrase Callback110319 -Node: Progress Meter Callback113881 -Node: Status Message Callback115866 -Node: Locale121218 -Node: Key Management122796 -Node: Key objects124024 -Node: Listing Keys138248 -Node: Information About Keys146901 -Node: Manipulating Keys148209 -Node: Generating Keys148779 -Node: Signing Keys167101 -Node: Exporting Keys170734 -Node: Importing Keys177541 -Ref: Importing Keys-Footnote-1184685 -Node: Deleting Keys184813 -Node: Changing Passphrases187093 -Node: Changing TOFU Data188420 -Node: Advanced Key Editing190528 -Node: Trust Item Management193261 -Node: Listing Trust Items194297 -Node: Manipulating Trust Items196658 -Node: Crypto Operations197301 -Node: Decrypt198565 -Node: Verify205072 -Node: Decrypt and Verify217536 -Node: Sign220391 -Node: Selecting Signers220955 -Node: Creating a Signature222361 -Node: Signature Notation Data227131 -Node: Encrypt229416 -Node: Encrypting a Plaintext229772 -Node: Miscellaneous237495 -Node: Running other Programs237907 -Node: Using the Assuan protocol240070 -Node: Checking for updates242868 -Node: Run Control247685 -Node: Waiting For Completion248429 -Node: Using External Event Loops250547 -Node: I/O Callback Interface252519 -Node: Registering I/O Callbacks257759 -Node: I/O Callback Example259798 -Node: I/O Callback Example GTK+266423 -Node: I/O Callback Example GDK268212 -Node: I/O Callback Example Qt269854 -Node: Cancellation272142 -Node: UI Server Protocol274450 -Ref: UI Server Protocol-Footnote-1275885 -Node: UI Server Encrypt276004 -Node: UI Server Sign281362 -Node: UI Server Decrypt283715 -Node: UI Server Verify285370 -Node: UI Server Set Input Files288942 -Node: UI Server Sign/Encrypt Files290012 -Node: UI Server Verify/Decrypt Files291820 -Node: UI Server Import/Export Keys293696 -Node: UI Server Checksum Files294758 -Node: Miscellaneous UI Server Commands296976 -Ref: command SENDER298907 -Node: Debugging302282 -Node: Deprecated Functions304031 -Node: Library Copying329248 -Node: Copying357468 -Node: Concept Index395218 -Node: Function and Data Index409997 +Node: Top1689 +Node: Introduction9299 +Node: Getting Started10089 +Node: Features11550 +Node: Overview12862 +Node: Preparation13971 +Node: Header14968 +Node: Building the Source15715 +Node: Largefile Support (LFS)17859 +Node: Using Automake23275 +Node: Using Libtool25828 +Node: Library Version Check26190 +Node: Signal Handling32250 +Node: Multi-Threading33520 +Ref: Multi-Threading-Footnote-134936 +Node: Protocols and Engines35359 +Node: Engine Version Check38110 +Node: Engine Information40633 +Node: Engine Configuration44493 +Node: OpenPGP45797 +Node: Cryptographic Message Syntax46137 +Node: Assuan46450 +Node: Algorithms46824 +Ref: Algorithms-Footnote-147303 +Node: Public Key Algorithms47431 +Node: Hash Algorithms50033 +Node: Error Handling51247 +Node: Error Values53121 +Node: Error Sources58324 +Node: Error Codes60764 +Node: Error Strings65557 +Node: Exchanging Data67364 +Node: Creating Data Buffers69249 +Node: Memory Based Data Buffers69765 +Node: File Based Data Buffers73198 +Node: Callback Based Data Buffers75400 +Node: Destroying Data Buffers79591 +Node: Manipulating Data Buffers81098 +Node: Data Buffer I/O Operations81590 +Node: Data Buffer Meta-Data83963 +Node: Data Buffer Convenience88482 +Node: Contexts90702 +Node: Creating Contexts91888 +Node: Destroying Contexts92735 +Node: Result Management93074 +Node: Context Attributes94655 +Node: Protocol Selection95692 +Node: Crypto Engine96724 +Node: Setting the Sender98613 +Node: ASCII Armor100126 +Node: Text Mode100755 +Node: Offline Mode101689 +Node: Pinentry Mode103187 +Node: Included Certificates105081 +Node: Key Listing Mode106527 +Node: Passphrase Callback110985 +Node: Progress Meter Callback114547 +Node: Status Message Callback116532 +Node: Locale122500 +Node: Key Management124078 +Node: Key objects125306 +Node: Listing Keys139530 +Node: Information About Keys148183 +Node: Manipulating Keys149491 +Node: Generating Keys150061 +Node: Signing Keys168383 +Node: Exporting Keys172016 +Node: Importing Keys178823 +Ref: Importing Keys-Footnote-1186226 +Node: Deleting Keys186354 +Node: Changing Passphrases188634 +Node: Changing TOFU Data189961 +Node: Advanced Key Editing192069 +Node: Trust Item Management194802 +Node: Listing Trust Items195838 +Node: Manipulating Trust Items198199 +Node: Crypto Operations198842 +Node: Decrypt200106 +Node: Verify206858 +Node: Decrypt and Verify219322 +Node: Sign222177 +Node: Selecting Signers222741 +Node: Creating a Signature224147 +Node: Signature Notation Data228917 +Node: Encrypt231202 +Node: Encrypting a Plaintext231558 +Node: Miscellaneous243791 +Node: Running other Programs244203 +Node: Using the Assuan protocol246366 +Node: Checking for updates249164 +Node: Run Control253981 +Node: Waiting For Completion254725 +Node: Using External Event Loops256843 +Node: I/O Callback Interface258815 +Node: Registering I/O Callbacks264055 +Node: I/O Callback Example266094 +Node: I/O Callback Example GTK+272719 +Node: I/O Callback Example GDK274508 +Node: I/O Callback Example Qt276150 +Node: Cancellation278438 +Node: UI Server Protocol280746 +Ref: UI Server Protocol-Footnote-1282181 +Node: UI Server Encrypt282300 +Node: UI Server Sign287658 +Node: UI Server Decrypt290011 +Node: UI Server Verify291666 +Node: UI Server Set Input Files295238 +Node: UI Server Sign/Encrypt Files296308 +Node: UI Server Verify/Decrypt Files298116 +Node: UI Server Import/Export Keys299992 +Node: UI Server Checksum Files302743 +Node: Miscellaneous UI Server Commands304961 +Ref: command SENDER306892 +Node: Debugging308594 +Node: Deprecated Functions310343 +Node: Library Copying335560 +Node: Copying363780 +Node: Concept Index401530 +Node: Function and Data Index416309 End Tag Table diff --git a/doc/gpgme.info-1 b/doc/gpgme.info-1 index 4dc3349..888e180 100644 --- a/doc/gpgme.info-1 +++ b/doc/gpgme.info-1 @@ -19,8 +19,8 @@ END-INFO-DIR-ENTRY This file documents the GPGME library. - This is Edition 1.10.0, last updated 8 December 2017, of â€The â€GnuPG -Made Easy’ Reference Manual’, for Version 1.10.0. + This is Edition 1.10.1-beta188, last updated 8 December 2017, of â€The +â€GnuPG Made Easy’ Reference Manual’, for Version 1.10.1-beta188. Copyright © 2002–2008, 2010, 2012–2017 g10 Code GmbH. @@ -41,8 +41,9 @@ File: gpgme.info, Node: Top, Next: Introduction, Up: (dir) Main Menu ********* -This is Edition 1.10.0, last updated 8 December 2017, of â€The â€GnuPG -Made Easy’ Reference Manual’, for Version 1.10.0 of the GPGME library. +This is Edition 1.10.1-beta188, last updated 8 December 2017, of â€The +â€GnuPG Made Easy’ Reference Manual’, for Version 1.10.1-beta188 of the +GPGME library. * Menu: @@ -2421,20 +2422,26 @@ File: gpgme.info, Node: Offline Mode, Next: Pinentry Mode, Prev: Text Mode, SINCE: 1.6.0 The function â€gpgme_set_offline’ specifies if offline mode should - be used. By default, offline mode is not used. + be used. Offline mode is disabled if YES is zero, and enabled + otherwise. By default, offline mode is disabled. - The offline mode specifies if dirmngr should be used to do - additional validation that might require connections to external - services. (e.g. CRL / OCSP checks). + The details of the offline mode depend on the used protocol and its + backend engine. It may eventually be extended to be more stricter + and for example completely disable the use of Dirmngr for any + engine. - Offline mode only affects the keylist mode - â€GPGME_KEYLIST_MODE_VALIDATE’ and is only relevant to the CMS - crypto engine. Offline mode is ignored otherwise. + For the CMS protocol the offline mode specifies whether Dirmngr + shall be used to do additional validation that might require + connecting external services (e.g. CRL / OCSP checks). Here the + offline mode only affects the keylist mode + â€GPGME_KEYLIST_MODE_VALIDATE’. - This option may be extended in the future to completely disable the - use of dirmngr for any engine. + For the OpenPGP protocol offline mode entirely disables the use of + the Dirmngr and will thus guarantee that no network connections are + done as part of an operation on this context. It has only an + effect with GnuPG versions 2.1.23 or later. - Offline mode is disabled if YES is zero, and enabled otherwise. + For all other protocols the offline mode is currently ignored. -- Function: int gpgme_get_offline (gpgme_ctx_t CTX) SINCE: 1.6.0 @@ -2570,6 +2577,11 @@ File: gpgme.info, Node: Key Listing Mode, Next: Passphrase Callback, Prev: In â€GPGME_KEYLIST_MODE_LOCAL’. For example, it can be a remote keyserver or LDAP certificate server. + â€GPGME_KEYLIST_MODE_LOCATE’ + This is a shortcut for the combination of + â€GPGME_KEYLIST_MODE_LOCAL’ and â€GPGME_KEYLIST_MODE_EXTERN’ and + convenient when the –locate-key feature of OpenPGP is desired. + â€GPGME_KEYLIST_MODE_SIGS’ The â€GPGME_KEYLIST_MODE_SIGS’ symbol specifies that the key signatures should be included in the listed keys. @@ -2860,6 +2872,19 @@ File: gpgme.info, Node: Status Message Callback, Next: Locale, Prev: Progress the operator can tell both your IP address and the time when you verified the signature. + â€"request-origin"’ + The string given in VALUE is passed to the GnuPG engines to + request restrictions based on the origin of the request. + Valid values are documented in the GnuPG manual and the gpg + man page under the option “–request-origin”. Requires at + least GnuPG 2.2.6 to have an effect. + + â€"no-symkey-cache"’ + For OpenPGP disable the passphrase cache used for symmetrical + en- and decryption. This cache is based on the message + specific salt value. Requires at least GnuPG 2.2.7 to have an + effect. + This function returns â€0’ on success. -- Function: const char * gpgme_get_ctx_flag (gpgme_ctx_t CTX, @@ -4423,6 +4448,12 @@ Importing keys means the same as running â€gpg’ with the command A list of gpgme_import_status_t objects which contain more information about the keys for which an import was attempted. + â€int skipped_v3_keys’ + For security reasons modern versions of GnuPG do not anymore + support v3 keys (created with PGP 2.x) and ignores them on + import. This counter provides the number of such skipped v3 + keys. + -- Function: gpgme_import_result_t gpgme_op_import_result (gpgme_ctx_t CTX) The function â€gpgme_op_import_result’ returns a @@ -4827,8 +4858,8 @@ File: gpgme.info, Node: Decrypt, Next: Verify, Up: Crypto Operations SINCE: 1.8.0 The function â€gpgme_op_decrypt_ext’ is the same as - â€gpgme_op_decrypt_ext’ but has an additional argument FLAGS. If - FLAGS is 0 both function behave identically. + â€gpgme_op_decrypt’ but has an additional argument FLAGS. If FLAGS + is 0 both function behave identically. The value in FLAGS is a bitwise-or combination of one or multiple of the following bit values: @@ -4933,6 +4964,13 @@ File: gpgme.info, Node: Decrypt, Next: Verify, Up: Crypto Operations success or â€gpgme_get_ctx_flag (ctx, "export-session-key")’ returns true (non-empty string). + â€char *symkey_algo’ + SINCE: 1.11.0 + + A string with the symmetric encryption algorithm and mode + using the format "<algo>.<mode>". Note that old non-MDC + encryption mode of OpenPGP is given as "PGPCFB". + -- Function: gpgme_decrypt_result_t gpgme_op_decrypt_result (gpgme_ctx_t CTX) The function â€gpgme_op_decrypt_result’ returns a @@ -5592,7 +5630,7 @@ File: gpgme.info, Node: Encrypting a Plaintext, Up: Encrypt The â€GPGME_ENCRYPT_SYMMETRIC’ symbol specifies that the output should be additionally encrypted symmetrically even if recipients are provided. This feature is only supported for - for the OpenPGP crypto engine. + the OpenPGP crypto engine. â€GPGME_ENCRYPT_THROW_KEYIDS’ SINCE: 1.8.0 @@ -5611,6 +5649,19 @@ File: gpgme.info, Node: Encrypting a Plaintext, Up: Encrypt OpenPGP message and not a plain data. This is the counterpart to â€GPGME_DECRYPT_UNWRAP’. + â€GPGME_ENCRYPT_WANT_ADDRESS’ + SINCE: 1.11.0 + + The â€GPGME_ENCRYPT_WANT_ADDRESS’ symbol requests that all + supplied keys or key specifications include a syntactically + valid mail address. If this is not the case the operation is + not even tried and the error code â€GPG_ERR_INV_USER_ID’ is + returned. Only the address part of the key specification is + conveyed to the backend. As of now the key must be specified + using the RECPSTRING argument of the extended encrypt + functions. This feature is currently only supported for the + OpenPGP crypto engine. + If â€GPG_ERR_UNUSABLE_PUBKEY’ is returned, some recipients in RECP are invalid, but not all. In this case the plaintext might be encrypted for all valid recipients and returned in CIPHER (if this @@ -5648,6 +5699,60 @@ File: gpgme.info, Node: Encrypting a Plaintext, Up: Encrypt â€GPG_ERR_UNUSABLE_PUBKEY’ if RSET does not contain any valid recipients. + -- Function: gpgme_error_t gpgme_op_encrypt_ext (gpgme_ctx_t CTX, + gpgme_key_t RECP[], const char *RECPSTRING, + gpgme_encrypt_flags_t FLAGS, gpgme_data_t PLAIN, + gpgme_data_t CIPHER) + + SINCE: 1.11.0 + + This is an extended version of â€gpgme_op_encrypt’ with RECPSTRING + as additional parameter. If RECP is NULL and RECPSTRING is not + NULL, the latter is expected to be a linefeed delimited string with + the set of key specifications. In contrast to RECP the keys are + given directly as strings and there is no need to first create key + objects. Leading and trailing white space is remove from each line + in RECPSTRING. The keys are then passed verbatim to the backend + engine. + + For the OpenPGP backend several special keywords are supported to + modify the operation. These keywords are given instead of a key + specification. The currently supported keywords are: + + â€--hidden’ + â€--no-hidden’ + These keywords toggle between normal and hidden recipients for + all following key specifications. When a hidden recipient is + requested the gpg option â€-R’ (or â€-F’ in file mode) is used + instead of â€-r’ (â€-f’ in file mode). + + â€--file’ + â€--no-file’ + These keywords toggle between regular and file mode for all + following key specification. In file mode the option â€-f’ or + â€-F’ is passed to gpg. At least GnuPG version 2.1.14 is + required to handle these options. The + â€GPGME_ENCRYPT_WANT_ADDRESS’ flag is ignored in file mode. + + â€--’ + This keyword disables all keyword detection up to the end of + the string. All keywords are treated as verbatim arguments. + + -- Function: gpgme_error_t gpgme_op_encrypt_ext_start (gpgme_ctx_t CTX, + gpgme_key_t RECP[], const char *RECPSTRING, + gpgme_encrypt_flags_t FLAGS, gpgme_data_t PLAIN, + gpgme_data_t CIPHER) + + SINCE: 1.11.0 + + This is an extended version of â€gpgme_op_encrypt_start’ with + RECPSTRING as additional parameter. If RECP is NULL and RECPSTRING + is not NULL, the latter is expected to be a linefeed delimited + string with the set of key specifications. In contrast to RECP the + keys are given directly as strings and there is no need to first + create key objects. The keys are passed verbatim to the backend + engine. + -- Data type: gpgme_encrypt_result_t This is a pointer to a structure used to store the result of a â€gpgme_op_encrypt’ operation. After successfully encrypting data, @@ -5692,6 +5797,36 @@ File: gpgme.info, Node: Encrypting a Plaintext, Up: Encrypt operation could be started successfully, and â€GPG_ERR_INV_VALUE’ if CTX, RSET, PLAIN or CIPHER is not a valid pointer. + -- Function: gpgme_error_t gpgme_op_encrypt_sign_ext (gpgme_ctx_t CTX, + gpgme_key_t RECP[], const char *RECPSTRING, + gpgme_encrypt_flags_t FLAGS, gpgme_data_t PLAIN, + gpgme_data_t CIPHER) + + SINCE: 1.11.0 + + This is an extended version of â€gpgme_op_encrypt_sign’ with + RECPSTRING as additional parameter. If RECP is NULL and RECPSTRING + is not NULL, the latter is expected to be a linefeed delimited + string with the set of key specifications. In contrast to RECP the + keys are given directly as strings and there is no need to first + create the key objects. The keys are passed verbatim to the + backend engine. + + -- Function: gpgme_error_t gpgme_op_encrypt_sign_ext_start + (gpgme_ctx_t CTX, gpgme_key_t RECP[], const char *RECPSTRING, + gpgme_encrypt_flags_t FLAGS, gpgme_data_t PLAIN, + gpgme_data_t CIPHER) + + SINCE: 1.11.0 + + This is an extended version of â€gpgme_op_encrypt_sign_start’ with + RECPSTRING as additional parameter. If RECP is NULL and RECPSTRING + is not NULL, the latter is expected to be a linefeed delimited + string with the set of key specifications. In contrast to RECP the + keys are given directly as strings and there is no need to first + create the key objects. The keys are passed verbatim to the + backend engine. + File: gpgme.info, Node: Miscellaneous, Next: Run Control, Prev: Crypto Operations, Up: Contexts @@ -7132,128 +7267,3 @@ commands. Afterwards, the actual operation is requested: FIXME: It may be nice to support an â€EXPORT’ command as well, which is enabled by the context menu of the background of a directory. - -File: gpgme.info, Node: UI Server Checksum Files, Next: Miscellaneous UI Server Commands, Prev: UI Server Import/Export Keys, Up: UI Server Protocol - -A.9 UI Server: Create and verify checksums for files. -===================================================== - -First, the input files need to be specified by one or more â€FILE’ -commands. Afterwards, the actual operation is requested: - - -- Command: CHECKSUM_CREATE_FILES --nohup - Request that checksums are created for the files specified by - â€FILE’. The choice of checksum algorithm and the destination - storage and format for the created checksums depend on the - preferences of the user and the functionality provided by the UI - server. For directories, the server may offer multiple options to - the user (for example ignore or process recursively). - - The option â€--nohup’ is mandatory. It is currently unspecified - what should happen if â€--nohup’ is not present. Because â€--nohup’ - is present, the server always returns â€OK’ promptly, and completes - the operation asynchronously. - - -- Command: CHECKSUM_VERIFY_FILES --nohup - Request that checksums are created for the files specified by - â€FILE’ and verified against previously created and stored - checksums. The choice of checksum algorithm and the source storage - and format for previously created checksums depend on the - preferences of the user and the functionality provided by the UI - server. For directories, the server may offer multiple options to - the user (for example ignore or process recursively). - - If the source storage of previously created checksums is available - to the user through the Windows shell, this command may also accept - such checksum files as â€FILE’ arguments. In this case, the UI - server should instead verify the checksum of the referenced files - as if they were given as INPUT files. - - The option â€--nohup’ is mandatory. It is currently unspecified - what should happen if â€--nohup’ is not present. Because â€--nohup’ - is present, the server always returns â€OK’ promptly, and completes - the operation asynchronously. - - -File: gpgme.info, Node: Miscellaneous UI Server Commands, Prev: UI Server Checksum Files, Up: UI Server Protocol - -A.10 Miscellaneous UI Server Commands -===================================== - -The server needs to implement the following commands which are not -related to a specific command: - - -- Command: GETINFO WHAT - This is a multi purpose command, commonly used to return a variety - of information. The required subcommands as described by the WHAT - parameter are: - - â€pid’ - Return the process id of the server in decimal notation using - an Assuan data line. - -To allow the server to pop up the windows in the correct relation to the -client, the client is advised to tell the server by sending the option: - - -- Command option: window-id NUMBER - The NUMBER represents the native window ID of the clients current - window. On Windows systems this is a windows handle (â€HWND’) and - on X11 systems it is the â€X Window ID’. The number needs to be - given as a hexadecimal value so that it is easier to convey pointer - values (e.g. â€HWND’). - -A client may want to fire up the certificate manager of the server. To -do this it uses the Assuan command: - - -- Command: START_KEYMANAGER - The server shall pop up the main window of the key manager (aka - certificate manager). The client expects that the key manager is - brought into the foregound and that this command immediatley - returns (does not wait until the key manager has been fully brought - up). - -A client may want to fire up the configuration dialog of the server. To -do this it uses the Assuan command: - - -- Command: START_CONFDIALOG - The server shall pop up its configuration dialog. The client - expects that this dialog is brought into the foregound and that - this command immediatley returns (i.e. it does not wait until the - dialog has been fully brought up). - -When doing an operation on a mail, it is useful to let the server know -the address of the sender: - - -- Command: SENDER [--info] [--protocol=NAME] EMAIL - EMAIL is the plain ASCII encoded address ("addr-spec" as per - RFC-2822) enclosed in angle brackets. The address set with this - command is valid until a successful completion of the operation or - until a â€RESET’ command. A second command overrides the effect of - the first one; if EMAIL is not given and â€--info’ is not used, the - server shall use the default signing key. - - If option â€--info’ is not given, the server shall also suggest a - protocol to use for signing. The client may use this suggested - protocol on its own discretion. The same status line as with - PREP_ENCRYPT is used for this. - - The option â€--protocol’ may be used to give the server a hint on - which signing protocol should be preferred. - -To allow the UI-server to visually identify a running operation or to -associate operations the server MAY support the command: - - -- Command: SESSION NUMBER [STRING] - The NUMBER is an arbitrary value, a server may use to associate - simultaneous running sessions. It is a 32 bit unsigned integer - with â€0’ as a special value indicating that no session association - shall be done. - - If STRING is given, the server may use this as the title of a - window or, in the case of an email operation, to extract the - sender’s address. The string may contain spaces; thus no - plus-escaping is used. - - This command may be used at any time and overrides the effect of - the last command. A â€RESET’ undoes the effect of this command. - diff --git a/doc/gpgme.info-2 b/doc/gpgme.info-2 index 2a805d3..4888966 100644 --- a/doc/gpgme.info-2 +++ b/doc/gpgme.info-2 @@ -19,8 +19,8 @@ END-INFO-DIR-ENTRY This file documents the GPGME library. - This is Edition 1.10.0, last updated 8 December 2017, of â€The â€GnuPG -Made Easy’ Reference Manual’, for Version 1.10.0. + This is Edition 1.10.1-beta188, last updated 8 December 2017, of â€The +â€GnuPG Made Easy’ Reference Manual’, for Version 1.10.1-beta188. Copyright © 2002–2008, 2010, 2012–2017 g10 Code GmbH. @@ -36,6 +36,131 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +File: gpgme.info, Node: UI Server Checksum Files, Next: Miscellaneous UI Server Commands, Prev: UI Server Import/Export Keys, Up: UI Server Protocol + +A.9 UI Server: Create and verify checksums for files. +===================================================== + +First, the input files need to be specified by one or more â€FILE’ +commands. Afterwards, the actual operation is requested: + + -- Command: CHECKSUM_CREATE_FILES --nohup + Request that checksums are created for the files specified by + â€FILE’. The choice of checksum algorithm and the destination + storage and format for the created checksums depend on the + preferences of the user and the functionality provided by the UI + server. For directories, the server may offer multiple options to + the user (for example ignore or process recursively). + + The option â€--nohup’ is mandatory. It is currently unspecified + what should happen if â€--nohup’ is not present. Because â€--nohup’ + is present, the server always returns â€OK’ promptly, and completes + the operation asynchronously. + + -- Command: CHECKSUM_VERIFY_FILES --nohup + Request that checksums are created for the files specified by + â€FILE’ and verified against previously created and stored + checksums. The choice of checksum algorithm and the source storage + and format for previously created checksums depend on the + preferences of the user and the functionality provided by the UI + server. For directories, the server may offer multiple options to + the user (for example ignore or process recursively). + + If the source storage of previously created checksums is available + to the user through the Windows shell, this command may also accept + such checksum files as â€FILE’ arguments. In this case, the UI + server should instead verify the checksum of the referenced files + as if they were given as INPUT files. + + The option â€--nohup’ is mandatory. It is currently unspecified + what should happen if â€--nohup’ is not present. Because â€--nohup’ + is present, the server always returns â€OK’ promptly, and completes + the operation asynchronously. + + +File: gpgme.info, Node: Miscellaneous UI Server Commands, Prev: UI Server Checksum Files, Up: UI Server Protocol + +A.10 Miscellaneous UI Server Commands +===================================== + +The server needs to implement the following commands which are not +related to a specific command: + + -- Command: GETINFO WHAT + This is a multi purpose command, commonly used to return a variety + of information. The required subcommands as described by the WHAT + parameter are: + + â€pid’ + Return the process id of the server in decimal notation using + an Assuan data line. + +To allow the server to pop up the windows in the correct relation to the +client, the client is advised to tell the server by sending the option: + + -- Command option: window-id NUMBER + The NUMBER represents the native window ID of the clients current + window. On Windows systems this is a windows handle (â€HWND’) and + on X11 systems it is the â€X Window ID’. The number needs to be + given as a hexadecimal value so that it is easier to convey pointer + values (e.g. â€HWND’). + +A client may want to fire up the certificate manager of the server. To +do this it uses the Assuan command: + + -- Command: START_KEYMANAGER + The server shall pop up the main window of the key manager (aka + certificate manager). The client expects that the key manager is + brought into the foregound and that this command immediatley + returns (does not wait until the key manager has been fully brought + up). + +A client may want to fire up the configuration dialog of the server. To +do this it uses the Assuan command: + + -- Command: START_CONFDIALOG + The server shall pop up its configuration dialog. The client + expects that this dialog is brought into the foregound and that + this command immediatley returns (i.e. it does not wait until the + dialog has been fully brought up). + +When doing an operation on a mail, it is useful to let the server know +the address of the sender: + + -- Command: SENDER [--info] [--protocol=NAME] EMAIL + EMAIL is the plain ASCII encoded address ("addr-spec" as per + RFC-2822) enclosed in angle brackets. The address set with this + command is valid until a successful completion of the operation or + until a â€RESET’ command. A second command overrides the effect of + the first one; if EMAIL is not given and â€--info’ is not used, the + server shall use the default signing key. + + If option â€--info’ is not given, the server shall also suggest a + protocol to use for signing. The client may use this suggested + protocol on its own discretion. The same status line as with + PREP_ENCRYPT is used for this. + + The option â€--protocol’ may be used to give the server a hint on + which signing protocol should be preferred. + +To allow the UI-server to visually identify a running operation or to +associate operations the server MAY support the command: + + -- Command: SESSION NUMBER [STRING] + The NUMBER is an arbitrary value, a server may use to associate + simultaneous running sessions. It is a 32 bit unsigned integer + with â€0’ as a special value indicating that no session association + shall be done. + + If STRING is given, the server may use this as the title of a + window or, in the case of an email operation, to extract the + sender’s address. The string may contain spaces; thus no + plus-escaping is used. + + This command may be used at any time and overrides the effect of + the last command. A â€RESET’ undoes the effect of this command. + + File: gpgme.info, Node: Debugging, Next: Deprecated Functions, Prev: UI Server Protocol, Up: Top Appendix B How to solve problems @@ -2290,7 +2415,7 @@ Function and Data Index * gpgme_edit_cb_t: Deprecated Functions. (line 37) * gpgme_encrypt_result_t: Encrypting a Plaintext. - (line 118) + (line 188) * gpgme_engine_check_version: Engine Version Check. (line 67) * gpgme_engine_info_t: Engine Information. (line 6) @@ -2332,7 +2457,7 @@ Function and Data Index * gpgme_genkey_result_t: Generating Keys. (line 381) * gpgme_get_armor: ASCII Armor. (line 13) * gpgme_get_ctx_flag: Status Message Callback. - (line 109) + (line 122) * gpgme_get_dirinfo: Engine Version Check. (line 6) * gpgme_get_engine_info: Engine Information. (line 46) @@ -2341,8 +2466,8 @@ Function and Data Index * gpgme_get_io_cbs: Registering I/O Callbacks. (line 44) * gpgme_get_key: Listing Keys. (line 178) -* gpgme_get_keylist_mode: Key Listing Mode. (line 83) -* gpgme_get_offline: Offline Mode. (line 25) +* gpgme_get_keylist_mode: Key Listing Mode. (line 88) +* gpgme_get_offline: Offline Mode. (line 31) * gpgme_get_passphrase_cb: Passphrase Callback. (line 63) * gpgme_get_pinentry_mode: Pinentry Mode. (line 18) * gpgme_get_progress_cb: Progress Meter Callback. @@ -2407,7 +2532,7 @@ Function and Data Index * gpgme_op_decrypt: Decrypt. (line 6) * gpgme_op_decrypt_ext: Decrypt. (line 30) * gpgme_op_decrypt_ext_start: Decrypt. (line 60) -* gpgme_op_decrypt_result: Decrypt. (line 144) +* gpgme_op_decrypt_result: Decrypt. (line 151) * gpgme_op_decrypt_start: Decrypt. (line 20) * gpgme_op_decrypt_verify: Decrypt and Verify. (line 6) * gpgme_op_decrypt_verify_start: Decrypt and Verify. (line 30) @@ -2421,14 +2546,22 @@ Function and Data Index (line 72) * gpgme_op_encrypt: Encrypting a Plaintext. (line 6) +* gpgme_op_encrypt_ext: Encrypting a Plaintext. + (line 131) +* gpgme_op_encrypt_ext_start: Encrypting a Plaintext. + (line 171) * gpgme_op_encrypt_result: Encrypting a Plaintext. - (line 129) + (line 199) * gpgme_op_encrypt_sign: Encrypting a Plaintext. - (line 140) + (line 210) +* gpgme_op_encrypt_sign_ext: Encrypting a Plaintext. + (line 235) +* gpgme_op_encrypt_sign_ext_start: Encrypting a Plaintext. + (line 251) * gpgme_op_encrypt_sign_start: Encrypting a Plaintext. - (line 151) + (line 221) * gpgme_op_encrypt_start: Encrypting a Plaintext. - (line 100) + (line 113) * gpgme_op_export: Exporting Keys. (line 46) * gpgme_op_export_ext: Exporting Keys. (line 79) * gpgme_op_export_ext_start: Exporting Keys. (line 101) @@ -2443,7 +2576,7 @@ Function and Data Index (line 22) * gpgme_op_import_keys: Importing Keys. (line 35) * gpgme_op_import_keys_start: Importing Keys. (line 65) -* gpgme_op_import_result: Importing Keys. (line 168) +* gpgme_op_import_result: Importing Keys. (line 174) * gpgme_op_import_start: Importing Keys. (line 24) * gpgme_op_interact: Advanced Key Editing. (line 23) diff --git a/doc/gpgme.texi b/doc/gpgme.texi index cd7bb4b..20bfa23 100644 --- a/doc/gpgme.texi +++ b/doc/gpgme.texi @@ -2606,22 +2606,26 @@ valid pointer. @deftypefun void gpgme_set_offline (@w{gpgme_ctx_t @var{ctx}}, @w{int @var{yes}}) @since{1.6.0} -The function @code{gpgme_set_offline} specifies if offline mode -should be used. By default, offline mode is not used. +The function @code{gpgme_set_offline} specifies if offline mode should +be used. Offline mode is disabled if @var{yes} is zero, and enabled +otherwise. By default, offline mode is disabled. -The offline mode specifies if dirmngr should be used to do additional -validation that might require connections to external services. -(e.g. CRL / OCSP checks). +The details of the offline mode depend on the used protocol and its +backend engine. It may eventually be extended to be more stricter and +for example completely disable the use of Dirmngr for any engine. -Offline mode only affects the keylist mode @code{GPGME_KEYLIST_MODE_VALIDATE} -and is only relevant to the CMS crypto engine. Offline mode -is ignored otherwise. +For the CMS protocol the offline mode specifies whether Dirmngr shall +be used to do additional validation that might require connecting +external services (e.g. CRL / OCSP checks). Here the offline mode +only affects the keylist mode @code{GPGME_KEYLIST_MODE_VALIDATE}. -This option may be extended in the future to completely disable -the use of dirmngr for any engine. +For the OpenPGP protocol offline mode entirely disables the use of the +Dirmngr and will thus guarantee that no network connections are done +as part of an operation on this context. It has only an effect with +GnuPG versions 2.1.23 or later. + +For all other protocols the offline mode is currently ignored. -Offline mode is disabled if @var{yes} is zero, and enabled -otherwise. @end deftypefun @deftypefun int gpgme_get_offline (@w{gpgme_ctx_t @var{ctx}}) @@ -2765,6 +2769,11 @@ type of external source is dependent on the crypto engine used and whether it is combined with @code{GPGME_KEYLIST_MODE_LOCAL}. For example, it can be a remote keyserver or LDAP certificate server. +@item GPGME_KEYLIST_MODE_LOCATE +This is a shortcut for the combination of +@code{GPGME_KEYLIST_MODE_LOCAL} and @code{GPGME_KEYLIST_MODE_EXTERN} +and convenient when the --locate-key feature of OpenPGP is desired. + @item GPGME_KEYLIST_MODE_SIGS The @code{GPGME_KEYLIST_MODE_SIGS} symbol specifies that the key signatures should be included in the listed keys. @@ -3065,6 +3074,17 @@ a message signed by a brand new key (which you naturally will not have on your local keyring), the operator can tell both your IP address and the time when you verified the signature. +@item "request-origin" +The string given in @var{value} is passed to the GnuPG engines to +request restrictions based on the origin of the request. Valid values +are documented in the GnuPG manual and the gpg man page under the +option ``--request-origin''. Requires at least GnuPG 2.2.6 to have an +effect. + +@item "no-symkey-cache" +For OpenPGP disable the passphrase cache used for symmetrical en- and +decryption. This cache is based on the message specific salt value. +Requires at least GnuPG 2.2.7 to have an effect. @end table @@ -4811,6 +4831,12 @@ The number of keys not imported. @item gpgme_import_status_t imports A list of gpgme_import_status_t objects which contain more information about the keys for which an import was attempted. + +@item int skipped_v3_keys +For security reasons modern versions of GnuPG do not anymore support +v3 keys (created with PGP 2.x) and ignores them on import. This +counter provides the number of such skipped v3 keys. + @end table @end deftp @@ -5263,7 +5289,7 @@ if @var{cipher} or @var{plain} is not a valid pointer. @since{1.8.0} The function @code{gpgme_op_decrypt_ext} is the same as -@code{gpgme_op_decrypt_ext} but has an additional argument +@code{gpgme_op_decrypt} but has an additional argument @var{flags}. If @var{flags} is 0 both function behave identically. The value in @var{flags} is a bitwise-or combination of one or @@ -5378,6 +5404,13 @@ You must not try to access this member of the struct unless or @code{gpgme_get_ctx_flag (ctx, "export-session-key")} returns true (non-empty string). +@item char *symkey_algo +@since{1.11.0} + +A string with the symmetric encryption algorithm and mode using the +format "<algo>.<mode>". Note that old non-MDC encryption mode of +OpenPGP is given as "PGPCFB". + @end table @end deftp @@ -6077,7 +6110,7 @@ also expect a sign command. The @code{GPGME_ENCRYPT_SYMMETRIC} symbol specifies that the output should be additionally encrypted symmetrically even -if recipients are provided. This feature is only supported for +if recipients are provided. This feature is only supported for the OpenPGP crypto engine. @item GPGME_ENCRYPT_THROW_KEYIDS @@ -6096,6 +6129,18 @@ The @code{GPGME_ENCRYPT_WRAP} symbol specifies that the input is an OpenPGP message and not a plain data. This is the counterpart to @code{GPGME_DECRYPT_UNWRAP}. +@item GPGME_ENCRYPT_WANT_ADDRESS +@since{1.11.0} + +The @code{GPGME_ENCRYPT_WANT_ADDRESS} symbol requests that all +supplied keys or key specifications include a syntactically valid mail +address. If this is not the case the operation is not even tried and +the error code @code{GPG_ERR_INV_USER_ID} is returned. Only the +address part of the key specification is conveyed to the backend. As +of now the key must be specified using the @var{recpstring} argument +of the extended encrypt functions. This feature is currently only +supported for the OpenPGP crypto engine. + @end table If @code{GPG_ERR_UNUSABLE_PUBKEY} is returned, some recipients in @@ -6137,6 +6182,73 @@ pointer, and @code{GPG_ERR_UNUSABLE_PUBKEY} if @var{rset} does not contain any valid recipients. @end deftypefun +@deftypefun gpgme_error_t gpgme_op_encrypt_ext @ + (@w{gpgme_ctx_t @var{ctx}}, @ + @w{gpgme_key_t @var{recp}[]}, @ + @w{const char *@var{recpstring}}, @ + @w{gpgme_encrypt_flags_t @var{flags}}, @ + @w{gpgme_data_t @var{plain}}, @w{gpgme_data_t @var{cipher}}) + +@since{1.11.0} + +This is an extended version of @code{gpgme_op_encrypt} with +@var{recpstring} as additional parameter. If @var{recp} is NULL and +@var{recpstring} is not NULL, the latter is expected to be a linefeed +delimited string with the set of key specifications. In contrast to +@var{recp} the keys are given directly as strings and there is no need +to first create key objects. Leading and trailing white space is +remove from each line in @var{recpstring}. The keys are then passed +verbatim to the backend engine. + +For the OpenPGP backend several special keywords are supported to +modify the operation. These keywords are given instead of a key +specification. The currently supported keywords are: + +@table @code +@item --hidden +@itemx --no-hidden +These keywords toggle between normal and hidden recipients for all +following key specifications. When a hidden recipient is requested +the gpg option @option{-R} (or @option{-F} in file mode) is used +instead of @option{-r} (@option{-f} in file mode). + +@item --file +@itemx --no-file +These keywords toggle between regular and file mode for all following +key specification. In file mode the option @option{-f} or @option{-F} +is passed to gpg. At least GnuPG version 2.1.14 is required to handle +these options. The @code{GPGME_ENCRYPT_WANT_ADDRESS} flag is ignored +in file mode. + +@item -- +This keyword disables all keyword detection up to the end of the +string. All keywords are treated as verbatim arguments. + +@end table + + +@end deftypefun + + +@deftypefun gpgme_error_t gpgme_op_encrypt_ext_start @ + (@w{gpgme_ctx_t @var{ctx}}, @ + @w{gpgme_key_t @var{recp}[]}, @ + @w{const char *@var{recpstring}}, @ + @w{gpgme_encrypt_flags_t @var{flags}}, @ + @w{gpgme_data_t @var{plain}}, @w{gpgme_data_t @var{cipher}}) + +@since{1.11.0} + +This is an extended version of @code{gpgme_op_encrypt_start} with +@var{recpstring} as additional parameter. If @var{recp} is NULL and +@var{recpstring} is not NULL, the latter is expected to be a linefeed +delimited string with the set of key specifications. In contrast to +@var{recp} the keys are given directly as strings and there is no need +to first create key objects. The keys are passed verbatim to the +backend engine. + +@end deftypefun + @deftp {Data type} {gpgme_encrypt_result_t} This is a pointer to a structure used to store the result of a @code{gpgme_op_encrypt} operation. After successfully encrypting @@ -6186,6 +6298,44 @@ if @var{ctx}, @var{rset}, @var{plain} or @var{cipher} is not a valid pointer. @end deftypefun +@deftypefun gpgme_error_t gpgme_op_encrypt_sign_ext @ + (@w{gpgme_ctx_t @var{ctx}}, @ + @w{gpgme_key_t @var{recp}[]}, @ + @w{const char *@var{recpstring}}, @ + @w{gpgme_encrypt_flags_t @var{flags}}, @ + @w{gpgme_data_t @var{plain}}, @w{gpgme_data_t @var{cipher}}) + +@since{1.11.0} + +This is an extended version of @code{gpgme_op_encrypt_sign} with +@var{recpstring} as additional parameter. If @var{recp} is NULL and +@var{recpstring} is not NULL, the latter is expected to be a linefeed +delimited string with the set of key specifications. In contrast to +@var{recp} the keys are given directly as strings and there is no need +to first create the key objects. The keys are passed verbatim to the +backend engine. + +@end deftypefun + +@deftypefun gpgme_error_t gpgme_op_encrypt_sign_ext_start @ + (@w{gpgme_ctx_t @var{ctx}}, @ + @w{gpgme_key_t @var{recp}[]}, @ + @w{const char *@var{recpstring}}, @ + @w{gpgme_encrypt_flags_t @var{flags}}, @ + @w{gpgme_data_t @var{plain}}, @w{gpgme_data_t @var{cipher}}) + +@since{1.11.0} + +This is an extended version of @code{gpgme_op_encrypt_sign_start} with +@var{recpstring} as additional parameter. If @var{recp} is NULL and +@var{recpstring} is not NULL, the latter is expected to be a linefeed +delimited string with the set of key specifications. In contrast to +@var{recp} the keys are given directly as strings and there is no need +to first create the key objects. The keys are passed verbatim to the +backend engine. + +@end deftypefun + @node Miscellaneous @section Miscellaneous operations @@ -1,7 +1,7 @@ # This is a template. The dist target uses it to create the real file. Summary: GPGME - GnuPG Made Easy Name: gpgme -Version: 1.10.0 +Version: 1.11.0 Release: 1 URL: https://gnupg.org/gpgme.html Source: ftp://ftp.gnupg.org/gcrypt/alpha/gpgme/%{name}-%{version}.tar.gz diff --git a/lang/README b/lang/README index 0c5bbd9..ee99f0f 100644 --- a/lang/README +++ b/lang/README @@ -13,3 +13,4 @@ cl Common Lisp cpp C++ qt Qt-Framework API python Python 2 and 3 (module name: gpg) +javascript Native messaging client for the gpgme-json server. diff --git a/lang/cl/gpgme.asd b/lang/cl/gpgme.asd index 4db4d63..a39b61a 100644 --- a/lang/cl/gpgme.asd +++ b/lang/cl/gpgme.asd @@ -27,7 +27,7 @@ (defsystem gpgme :description "GnuPG Made Easy." :author "g10 Code GmbH" - :version "1.10.0" + :version "1.11.0" :licence "GPL" :depends-on ("cffi" "gpg-error") :components ((:file "gpgme-package") diff --git a/lang/cpp/src/context.h b/lang/cpp/src/context.h index 4cd5b30..aff8e49 100644 --- a/lang/cpp/src/context.h +++ b/lang/cpp/src/context.h @@ -408,7 +408,8 @@ public: enum SpawnFlags { SpawnNone = 0, SpawnDetached = 1, - SpawnAllowSetFg = 2 + SpawnAllowSetFg = 2, + SpawnShowWindow = 4 }; /** Spwan the process \a file with arguments \a argv. * diff --git a/lang/cpp/src/data.cpp b/lang/cpp/src/data.cpp index 32ca561..52b8da2 100644 --- a/lang/cpp/src/data.cpp +++ b/lang/cpp/src/data.cpp @@ -254,3 +254,17 @@ std::vector<GpgME::Key> GpgME::Data::toKeys(Protocol proto) const delete ctx; return ret; } + +std::string GpgME::Data::toString() +{ + std::string ret; + char buf[4096]; + size_t nread; + seek (0, SEEK_SET); + while ((nread = read (buf, 4096)) > 0) + { + ret += std::string (buf, nread); + } + seek (0, SEEK_SET); + return ret; +} diff --git a/lang/cpp/src/data.h b/lang/cpp/src/data.h index cc7906f..446f6fa 100644 --- a/lang/cpp/src/data.h +++ b/lang/cpp/src/data.h @@ -114,6 +114,9 @@ public: * Protocol proto. Returns an empty list on error.*/ std::vector<Key> toKeys(const Protocol proto = Protocol::OpenPGP) const; + /** Return a copy of the data as std::string. Sets seek pos to 0 */ + std::string toString(); + class Private; Private *impl() { diff --git a/lang/cpp/src/importresult.cpp b/lang/cpp/src/importresult.cpp index 8c35f9c..dbb31d0 100644 --- a/lang/cpp/src/importresult.cpp +++ b/lang/cpp/src/importresult.cpp @@ -154,6 +154,11 @@ int GpgME::ImportResult::notImported() const return d ? d->res.not_imported : 0 ; } +int GpgME::ImportResult::numV3KeysSkipped() const +{ + return d ? d->res.skipped_v3_keys : 0 ; +} + GpgME::Import GpgME::ImportResult::import(unsigned int idx) const { return Import(d, idx); diff --git a/lang/cpp/src/importresult.h b/lang/cpp/src/importresult.h index 2f0e7f2..0547679 100644 --- a/lang/cpp/src/importresult.h +++ b/lang/cpp/src/importresult.h @@ -78,6 +78,7 @@ public: int numSecretKeysUnchanged() const; int notImported() const; + int numV3KeysSkipped() const; Import import(unsigned int idx) const; std::vector<Import> imports() const; diff --git a/lang/cpp/src/key.cpp b/lang/cpp/src/key.cpp index 66fdea9..0e86a19 100644 --- a/lang/cpp/src/key.cpp +++ b/lang/cpp/src/key.cpp @@ -371,6 +371,27 @@ void Key::update() return; } +// static +Key Key::locate(const char *mbox) +{ + if (!mbox) { + return Key(); + } + + auto ctx = Context::createForProtocol(OpenPGP); + if (!ctx) { + return Key(); + } + + ctx->setKeyListMode (Extern | Local); + + Error e = ctx->startKeyListing (mbox); + auto ret = ctx->nextKey (e); + delete ctx; + + return ret; +} + // // // class Subkey diff --git a/lang/cpp/src/key.h b/lang/cpp/src/key.h index 829bd26..c3c711c 100644 --- a/lang/cpp/src/key.h +++ b/lang/cpp/src/key.h @@ -164,6 +164,20 @@ public: * @returns a possible error. **/ Error addUid(const char *uid); + + /** + * @brief try to locate the best pgp key for a given mailbox. + * + * Boils down to gpg --locate-key <mbox> + * This may take some time if remote sources are also + * used. + * + * @param mbox should be a mail address does not need to be normalized. + * + * @returns The best key for a mailbox or a null key. + */ + static Key locate(const char *mbox); + private: gpgme_key_t impl() const { diff --git a/lang/python/README b/lang/python/README index 6a2e8b8..99da4dd 100644 --- a/lang/python/README +++ b/lang/python/README @@ -1,61 +1,77 @@ -gpg - GPGME bindings for Python -*- org -*- -======================= + â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â” + GPG - GPGME BINDINGS FOR PYTHON + â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â” + + +Table of Contents +───────────────── + +1 Mailing List +2 Bugs +3 Authors +4 History + The "gpg" module is a python interface to the GPGME library: -https://www.gnupg.org/related_software/gpgme/ +[https://www.gnupg.org/software/gpgme/] -"gpg" offers two interfaces, one is a high-level, curated, and -idiomatic interface that is implemented as a shim on top of the -low-level interface automatically created using SWIG. +"gpg" offers two interfaces, one is a high-level, curated, and idiomatic +interface that is implemented as a shim on top of the low-level +interface automatically created using SWIG. This way we make simple things easy, while still providing the entire functionality of the underlying library. -* Mailing List -For general discussion and help see the gnupg-users mailing list: -https://lists.gnupg.org/mailman/listinfo/gnupg-users +1 Mailing List +â•â•â•â•â•â•â•â•â•â•â•â•â•â• + + For general discussion and help see the gnupg-users mailing list: + [https://lists.gnupg.org/mailman/listinfo/gnupg-users] + + For development see the gnupg-devel mailing list: + [https://lists.gnupg.org/mailman/listinfo/gnupg-devel] + + +2 Bugs +â•â•â•â•â•â• -For development see the gnupg-devel mailing list: -https://lists.gnupg.org/mailman/listinfo/gnupg-devel + Please report bugs using our bug tracker [https://bugs.gnupg.org] with + tag (aka project) 'gpgme'. -* Bugs -Please report bugs using our bug tracker using the category 'gpgme', -and topic 'python': -https://bugs.gnupg.org/gnupg/ +3 Authors +â•â•â•â•â•â•â•â•â• -* Authors + PyME was created by John Goerzen, and maintained, developed, and + cherished by Igor Belyi, Martin Albrecht, Ben McGinnes, and everyone + who contributed to it in any way. -PyME was created by John Goerzen, and maintained, developed, and -cherished by Igor Belyi, Martin Albrecht, Ben McGinnes, and everyone -who contributed to it in any way. + In 2016 we merged a port of PyME to into the GPGME repository, and + development will continue there. Please see the VCS history for the + list of contributors, and if you do find bugs, or want to contribute, + please get in touch and help maintain the python gpg bindings. -In 2016 we merged a port of PyME to into the GPGME repository, and -development will continue there. Please see the VCS history for the -list of contributors, and if you do find bugs, or want to contribute, -please get in touch and help maintain the python gpg bindings. + Please see the section 'History' further down this document for + references to previous versions. -Please see the section 'History' further down this document for -references to previous versions. -* History +4 History +â•â•â•â•â•â•â•â•â• - - The python bindings were renamed from PyME to "gpg" in 2016. + • The python bindings were renamed from PyME to "gpg" in 2016. - - The bindings have been merged into the GPGME repository in 2016. + • The bindings have been merged into the GPGME repository in 2016. - - The latest version of PyME for Python 3.2 and above (as of - May, 2015) is v0.9.1. - https://git.gnupg.org/gpgme.git/lang/py3-pyme + • The latest version of PyME for Python 3.2 and above (as of May, + 2015) is v0.9.1. [https://git.gnupg.org/gpgme.git/lang/py3-pyme] - - The latest version of PyME for Python 2.6 and 2.7 (as of this - writing) is v0.9.0. https://bitbucket.org/malb/pyme + • The latest version of PyME for Python 2.6 and 2.7 (as of this + writing) is v0.9.0. [https://bitbucket.org/malb/pyme] - - A previous version of PyME v0.8.0 can be found on sourceforge: - http://pyme.sourceforge.net/ + • A previous version of PyME v0.8.0 can be found on sourceforge: + [http://pyme.sourceforge.net/] - - A previous version of PyME v0.5.1 which works with GPGME v0.3.15 - can be found on John Goerzen's PyME page: - http://quux.org/devel/pyme/ - http://www.complete.org/JohnGoerzen + • A previous version of PyME v0.5.1 which works with GPGME v0.3.15 can + be found on John Goerzen's PyME page: [http://quux.org/devel/pyme/] + [http://www.complete.org/JohnGoerzen] diff --git a/lang/python/examples/howto/README.org b/lang/python/examples/howto/README.org new file mode 100644 index 0000000..b74ae7e --- /dev/null +++ b/lang/python/examples/howto/README.org @@ -0,0 +1,58 @@ +#+TITLE: GPGME Python Bindings HOWTO Examples +#+LATEX_COMPILER: xelatex +#+LATEX_CLASS: article +#+LATEX_CLASS_OPTIONS: [12pt] +#+LATEX_HEADER: \usepackage{xltxtra} +#+LATEX_HEADER: \usepackage[margin=1in]{geometry} +#+LATEX_HEADER: \setmainfont[Ligatures={Common}]{Times New Roman} +#+LATEX_HEADER: \author{Ben McGinnes <ben@gnupg.org>} + + +* Examples + :PROPERTIES: + :CUSTOM_ID: gpgme-python3-examples + :END: + + The contents of this directory are the examples included in the /GNU + Privacy Guard (GnuPG) Made Easy Python Bindings HOWTO/ file. Each + script is explicitly for Python 3 and specifically for Python 3.4 or + later. + + Some of these scripts may work with Python 2.7, but there are no + guarantees. They will include the relevant imports from the + =__future__= module to facilitate that if possible. + + +* Copyright and Licensing + :PROPERTIES: + :CUSTOM_ID: copyright-and-license + :END: + + Unless otherwise stated, all the examples in this directory are + released under the same terms as GPGME itself; that is they are dual + licensed under the terms of both the GNU General Public License + version 2.0 (or any later version) *and* the GNU Lesser General + Public License version 2.1 (or any later version). + + +** Copyright (C) The GnuPG Project, 2018 + :PROPERTIES: + :CUSTOM_ID: copyright + :END: + + Copyright © The GnuPG Project, 2018. + + +** License GPL compatible + :PROPERTIES: + :CUSTOM_ID: license + :END: + + 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/lang/python/examples/howto/add-userid.py b/lang/python/examples/howto/add-userid.py new file mode 100755 index 0000000..b868979 --- /dev/null +++ b/lang/python/examples/howto/add-userid.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +from __future__ import absolute_import, division, unicode_literals + +# Copyright (C) 2018 Ben McGinnes <ben@gnupg.org> +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License and the GNU +# Lesser General Public Licensefor more details. +# +# You should have received a copy of the GNU General Public License and the GNU +# Lesser General Public along with this program; if not, see +# <http://www.gnu.org/licenses/>. + +import gpg +import os.path + +print(""" +This script adds a new user ID to an existing key. + +The gpg-agent and pinentry are invoked to enter the passphrase. +""") + +c = gpg.Context() + +homedir = input("Enter the GPG configuration directory path (optional): ") +fpr0 = input("Enter the fingerprint of the key to modify: ") +uid_name = input("Enter the name of the user ID: ") +uid_email = input("Enter the email address of the user ID: ") +uid_cmnt = input("Enter a comment to include (optional): ") + +if homedir.startswith("~"): + if os.path.exists(os.path.expanduser(homedir)) is True: + c.home_dir = os.path.expanduser(homedir) + else: + pass +elif os.path.exists(homedir) is True: + c.home_dir = homedir +else: + pass + +fpr = "".join(fpr0.split()) + +if len(uid_cmnt) > 0: + userid = "{0} ({1}) <{2}>".format(uid_name, uid_cmnt, uid_email) +else: + userid = "{0} <{2}>".format(uid_name, uid_email) + +key = c.get_key(fpr, secret=True) +c.key_add_uid(key, userid) diff --git a/lang/python/examples/howto/clear-sign-file.py b/lang/python/examples/howto/clear-sign-file.py new file mode 100755 index 0000000..597bbc5 --- /dev/null +++ b/lang/python/examples/howto/clear-sign-file.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +from __future__ import absolute_import, division, unicode_literals + +# Copyright (C) 2018 Ben McGinnes <ben@gnupg.org> +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License and the GNU +# Lesser General Public Licensefor more details. +# +# You should have received a copy of the GNU General Public License and the GNU +# Lesser General Public along with this program; if not, see +# <http://www.gnu.org/licenses/>. + +import gpg +import sys + +""" +Clear-signs a file with a specified key. If entering both the key and the +filename on the command line, the key must be entered first. +""" + +if len(sys.argv) > 3: + logrus = sys.argv[1] + filename = " ".join(sys.argv[2:]) +elif len(sys.argv) == 3: + logrus = sys.argv[1] + filename = sys.argv[2] +elif len(sys.argv) == 2: + logrus = sys.argv[1] + filename = input("Enter the path and filename to sign: ") +else: + logrus = input("Enter the fingerprint or key ID to sign with: ") + filename = input("Enter the path and filename to sign: ") + +with open(filename, "rb") as f: + text = f.read() + +key = list(gpg.Context().keylist(pattern=logrus)) + +with gpg.Context(armor=True, signers=key) as c: + signed_data, result = c.sign(text, mode=gpg.constants.sig.mode.CLEAR) + with open("{0}.asc".format(filename), "wb") as f: + f.write(signed_data) diff --git a/lang/python/examples/howto/create-key.py b/lang/python/examples/howto/create-key.py new file mode 100755 index 0000000..429ab1f --- /dev/null +++ b/lang/python/examples/howto/create-key.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +from __future__ import absolute_import, division, unicode_literals + +# Copyright (C) 2018 Ben McGinnes <ben@gnupg.org> +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License and the GNU +# Lesser General Public Licensefor more details. +# +# You should have received a copy of the GNU General Public License and the GNU +# Lesser General Public along with this program; if not, see +# <http://www.gnu.org/licenses/>. + +import gpg +import os.path + +print(""" +This script generates a new key which does not expire. + +The gpg-agent and pinentry are invoked to set the passphrase. +""") + +c = gpg.Context() + +homedir = input("Enter the GPG configuration directory path (optional): ") +uid_name = input("Enter the name of the user ID: ") +uid_email = input("Enter the email address of the user ID: ") +uid_cmnt = input("Enter a comment to include (optional): ") +key_algo = input("Enter the key algorithm, RSA or DSA (default is RSA): ") +key_size = input("Enter the key size (2048-4096, default is 2048): ") + +if homedir.startswith("~"): + if os.path.exists(os.path.expanduser(homedir)) is True: + c.home_dir = os.path.expanduser(homedir) + else: + pass +elif os.path.exists(homedir) is True: + c.home_dir = homedir +else: + pass + +if len(uid_cmnt) > 0: + userid = "{0} ({1}) <{2}>".format(uid_name, uid_cmnt, uid_email) +else: + userid = "{0} <{2}>".format(uid_name, uid_email) + +if key_algo.lower() == "dsa": + ka = "dsa" +else: + ka = "rsa" + +if len(key_size) == 4: + try: + ks0 = int(key_size) + except ValueError: + ks0 = None + if ks0 is None: + ks = "2048" + else: + if ks0 < 2048: + ks = "2048" + elif ka == "dsa" and ks0 > 3072: + ks = "3072" + elif ka == "rsa" and ks0 > 4096: + ks = "4096" + else: + ks = key_size +else: + ks = "2048" + +keyalgo = "{0}{1}".format(ka, ks) + +newkey = c.create_key(userid, algorithm=keyalgo, expires=False, + passphrase=True, certify=True) +key = c.get_key(newkey.fpr, secret=True) + +if ka == "rsa": + newsub = c.create_subkey(key, algorithm=keyalgo, expires=False, + passphrase=True, encrypt=True) +else: + newsub = c.create_subkey(key, expires=False, passphrase=True, + encrypt=True) diff --git a/lang/python/examples/howto/decrypt-file.py b/lang/python/examples/howto/decrypt-file.py new file mode 100755 index 0000000..60a050b --- /dev/null +++ b/lang/python/examples/howto/decrypt-file.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +from __future__ import absolute_import, division, unicode_literals + +# Copyright (C) 2018 Ben McGinnes <ben@gnupg.org> +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License and the GNU +# Lesser General Public Licensefor more details. +# +# You should have received a copy of the GNU General Public License and the GNU +# Lesser General Public along with this program; if not, see +# <http://www.gnu.org/licenses/>. + +import gpg +import sys + +if len(sys.argv) == 3: + ciphertext = sys.argv[1] + newfile = sys.argv[2] +elif len(sys.argv) == 2: + ciphertext = sys.argv[1] + newfile = input("Enter path and filename of file to save decrypted data to: ") +else: + ciphertext = input("Enter path and filename of encrypted file: ") + newfile = input("Enter path and filename of file to save decrypted data to: ") + +with open(ciphertext, "rb") as cfile: + plaintext, result, verify_result = gpg.Context().decrypt(cfile) + +with open(newfile, "wb") as nfile: + nfile.write(plaintext) diff --git a/lang/python/examples/howto/detach-sign-file.py b/lang/python/examples/howto/detach-sign-file.py new file mode 100755 index 0000000..99fbe65 --- /dev/null +++ b/lang/python/examples/howto/detach-sign-file.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +from __future__ import absolute_import, division, unicode_literals + +# Copyright (C) 2018 Ben McGinnes <ben@gnupg.org> +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License and the GNU +# Lesser General Public Licensefor more details. +# +# You should have received a copy of the GNU General Public License and the GNU +# Lesser General Public along with this program; if not, see +# <http://www.gnu.org/licenses/>. + +import gpg +import sys + +""" +Signs a file with a specified key. If entering both the key and the filename +on the command line, the key must be entered first. + +Will produce both an ASCII armoured and GPG binary format copy of the detached +signature file. +""" + +if len(sys.argv) > 3: + logrus = sys.argv[1] + filename = " ".join(sys.argv[2:]) +elif len(sys.argv) == 3: + logrus = sys.argv[1] + filename = sys.argv[2] +elif len(sys.argv) == 2: + logrus = sys.argv[1] + filename = input("Enter the path and filename to sign: ") +else: + logrus = input("Enter the fingerprint or key ID to sign with: ") + filename = input("Enter the path and filename to sign: ") + +with open(filename, "rb") as f: + text = f.read() + +key = list(gpg.Context().keylist(pattern=logrus)) + +with gpg.Context(armor=True, signers=key) as ca: + signed_data, result = ca.sign(text, mode=gpg.constants.sig.mode.DETACH) + with open("{0}.asc".format(filename), "wb") as fa: + fa.write(signed_data) + +with gpg.Context(signers=key) as cb: + signed_data, result = cb.sign(text, mode=gpg.constants.sig.mode.DETACH) + with open("{0}.sig".format(filename), "wb") as fb: + fb.write(signed_data) diff --git a/lang/python/examples/howto/encrypt-file.py b/lang/python/examples/howto/encrypt-file.py new file mode 100755 index 0000000..ad4e1ce --- /dev/null +++ b/lang/python/examples/howto/encrypt-file.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +from __future__ import absolute_import, division, unicode_literals + +# Copyright (C) 2018 Ben McGinnes <ben@gnupg.org> +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License and the GNU +# Lesser General Public Licensefor more details. +# +# You should have received a copy of the GNU General Public License and the GNU +# Lesser General Public along with this program; if not, see +# <http://www.gnu.org/licenses/>. + +import gpg +import sys + +""" +Encrypts a file to a specified key. If entering both the key and the filename +on the command line, the key must be entered first. + +Will produce both an ASCII armoured and GPG binary format copy of the encrypted +file. +""" + +if len(sys.argv) > 3: + a_key = sys.argv[1] + filename = " ".join(sys.argv[2:]) +elif len(sys.argv) == 3: + a_key = sys.argv[1] + filename = sys.argv[2] +elif len(sys.argv) == 2: + a_key = sys.argv[1] + filename = input("Enter the path and filename to encrypt: ") +else: + a_key = input("Enter the fingerprint or key ID to encrypt to: ") + filename = input("Enter the path and filename to encrypt: ") + +rkey = list(gpg.Context().keylist(pattern=a_key, secret=False)) +with open(filename, "rb") as f: + text = f.read() + +with gpg.Context(armor=True) as ca: + try: + ciphertext, result, sign_result = ca.encrypt(text, recipients=rkey, + sign=False) + with open("{0}.asc".format(filename), "wb") as fa: + fa.write(ciphertext) + except gpg.errors.InvalidRecipients as e: + print(e) + +with gpg.Context() as cg: + try: + ciphertext, result, sign_result = cg.encrypt(text, recipients=rkey, + sign=False) + with open("{0}.gpg".format(filename), "wb") as fg: + fg.write(ciphertext) + except gpg.errors.InvalidRecipients as e: + print(e) diff --git a/lang/python/examples/howto/encrypt-sign-file.py b/lang/python/examples/howto/encrypt-sign-file.py new file mode 100755 index 0000000..41aaac8 --- /dev/null +++ b/lang/python/examples/howto/encrypt-sign-file.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +from __future__ import absolute_import, division, unicode_literals + +# Copyright (C) 2018 Ben McGinnes <ben@gnupg.org> +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License and the GNU +# Lesser General Public Licensefor more details. +# +# You should have received a copy of the GNU General Public License and the GNU +# Lesser General Public along with this program; if not, see +# <http://www.gnu.org/licenses/>. + +import gpg +import sys + +""" +Signs and encrypts a file to a specified key. If entering both the key and the +filename on the command line, the key must be entered first. + +Signs with and also encrypts to the default key of the user invoking the +script. Will treat all recipients as trusted to permit encryption. + +Will produce both an ASCII armoured and GPG binary format copy of the signed +and encrypted file. +""" + +if len(sys.argv) > 3: + a_key = sys.argv[1] + filename = " ".join(sys.argv[2:]) +elif len(sys.argv) == 3: + a_key = sys.argv[1] + filename = sys.argv[2] +elif len(sys.argv) == 2: + a_key = sys.argv[1] + filename = input("Enter the path and filename to encrypt: ") +else: + a_key = input("Enter the fingerprint or key ID to encrypt to: ") + filename = input("Enter the path and filename to encrypt: ") + +rkey = list(gpg.Context().keylist(pattern=a_key, secret=False)) +with open(filename, "rb") as f: + text = f.read() + +with gpg.Context(armor=True) as ca: + ciphertext, result, sign_result = ca.encrypt(text, recipients=rkey, + always_trust=True, + add_encrypt_to=True) + with open("{0}.asc".format(filename), "wb") as fa: + fa.write(ciphertext) + +with gpg.Context() as cg: + ciphertext, result, sign_result = cg.encrypt(text, recipients=rkey, + always_trust=True, + add_encrypt_to=True) + with open("{0}.gpg".format(filename), "wb") as fg: + fg.write(ciphertext) diff --git a/lang/python/examples/howto/groups.py b/lang/python/examples/howto/groups.py new file mode 100644 index 0000000..5e7fdf6 --- /dev/null +++ b/lang/python/examples/howto/groups.py @@ -0,0 +1,50 @@ +# -*- coding: utf-8 -*- + +from __future__ import absolute_import, division, unicode_literals + +# Copyright (C) 2018 Ben McGinnes <ben@gnupg.org> +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License and the GNU +# Lesser General Public Licensefor more details. +# +# You should have received a copy of the GNU General Public License and the GNU +# Lesser General Public along with this program; if not, see +# <http://www.gnu.org/licenses/>. + +import subprocess + +""" +Intended for use with other scripts. + +Usage: from groups import group_lists +""" + +lines = subprocess.getoutput("gpgconf --list-options gpg").splitlines() + +for i in range(len(lines)): + if lines[i].startswith("group") is True: + line = lines[i] + else: + pass + +groups = line.split(":")[-1].replace('"', '').split(',') + +group_lines = groups +for i in range(len(group_lines)): + group_lines[i] = group_lines[i].split("=") + +group_lists = group_lines +for i in range(len(group_lists)): + group_lists[i][1] = group_lists[i][1].split() diff --git a/lang/python/examples/howto/keycount.py b/lang/python/examples/howto/keycount.py new file mode 100755 index 0000000..8e25454 --- /dev/null +++ b/lang/python/examples/howto/keycount.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +from __future__ import absolute_import, division, unicode_literals + +# Copyright (C) 2018 Ben McGinnes <ben@gnupg.org> +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License and the GNU +# Lesser General Public Licensefor more details. +# +# You should have received a copy of the GNU General Public License and the GNU +# Lesser General Public along with this program; if not, see +# <http://www.gnu.org/licenses/>. + +import gpg + +c = gpg.Context() +seckeys = c.keylist(pattern=None, secret=True) +pubkeys = c.keylist(pattern=None, secret=False) + +seclist = list(seckeys) +secnum = len(seclist) + +publist = list(pubkeys) +pubnum = len(publist) + +print(""" +Number of secret keys: {0} +Number of public keys: {1} +""".format(secnum, pubnum)) diff --git a/lang/python/examples/howto/revoke-userid.py b/lang/python/examples/howto/revoke-userid.py new file mode 100755 index 0000000..7a3d190 --- /dev/null +++ b/lang/python/examples/howto/revoke-userid.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +from __future__ import absolute_import, division, unicode_literals + +# Copyright (C) 2018 Ben McGinnes <ben@gnupg.org> +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License and the GNU +# Lesser General Public Licensefor more details. +# +# You should have received a copy of the GNU General Public License and the GNU +# Lesser General Public along with this program; if not, see +# <http://www.gnu.org/licenses/>. + +import gpg +import os.path + +print(""" +This script revokes a user ID on an existing key. + +The gpg-agent and pinentry are invoked to enter the passphrase. +""") + +c = gpg.Context() + +homedir = input("Enter the GPG configuration directory path (optional): ") +fpr0 = input("Enter the fingerprint of the key to modify: ") +uid_name = input("Enter the name of the user ID: ") +uid_email = input("Enter the email address of the user ID: ") +uid_cmnt = input("Enter a comment to include (optional): ") + +if homedir.startswith("~"): + if os.path.exists(os.path.expanduser(homedir)) is True: + c.home_dir = os.path.expanduser(homedir) + else: + pass +elif os.path.exists(homedir) is True: + c.home_dir = homedir +else: + pass + +fpr = "".join(fpr0.split()) + +if len(uid_cmnt) > 0: + userid = "{0} ({1}) <{2}>".format(uid_name, uid_cmnt, uid_email) +else: + userid = "{0} <{2}>".format(uid_name, uid_email) + +key = c.get_key(fpr, secret=True) +c.key_revoke_uid(key, userid) diff --git a/lang/python/examples/howto/sign-file.py b/lang/python/examples/howto/sign-file.py new file mode 100755 index 0000000..01006df --- /dev/null +++ b/lang/python/examples/howto/sign-file.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +from __future__ import absolute_import, division, unicode_literals + +# Copyright (C) 2018 Ben McGinnes <ben@gnupg.org> +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License and the GNU +# Lesser General Public Licensefor more details. +# +# You should have received a copy of the GNU General Public License and the GNU +# Lesser General Public along with this program; if not, see +# <http://www.gnu.org/licenses/>. + +import gpg +import sys + +""" +Signs a file with a specified key. If entering both the key and the filename +on the command line, the key must be entered first. + +Will produce both an ASCII armoured and GPG binary format copy of the signed +file. +""" + +if len(sys.argv) > 3: + logrus = sys.argv[1] + filename = " ".join(sys.argv[2:]) +elif len(sys.argv) == 3: + logrus = sys.argv[1] + filename = sys.argv[2] +elif len(sys.argv) == 2: + logrus = sys.argv[1] + filename = input("Enter the path and filename to sign: ") +else: + logrus = input("Enter the fingerprint or key ID to sign with: ") + filename = input("Enter the path and filename to sign: ") + +with open(filename, "rb") as f: + text = f.read() + +key = list(gpg.Context().keylist(pattern=logrus)) + +with gpg.Context(armor=True, signers=key) as ca: + signed_data, result = ca.sign(text, mode=gpg.constants.sig.mode.NORMAL) + with open("{0}.asc".format(filename), "wb") as fa: + fa.write(signed_data) + +with gpg.Context(signers=key) as cg: + signed_data, result = cg.sign(text, mode=gpg.constants.sig.mode.NORMAL) + with open("{0}.gpg".format(filename), "wb") as fg: + fg.write(signed_data) diff --git a/lang/python/examples/howto/sign-key.py b/lang/python/examples/howto/sign-key.py new file mode 100755 index 0000000..b1afe13 --- /dev/null +++ b/lang/python/examples/howto/sign-key.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +from __future__ import absolute_import, division, unicode_literals + +# Copyright (C) 2018 Ben McGinnes <ben@gnupg.org> +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License and the GNU +# Lesser General Public Licensefor more details. +# +# You should have received a copy of the GNU General Public License and the GNU +# Lesser General Public along with this program; if not, see +# <http://www.gnu.org/licenses/>. + +import gpg +import os.path + +print(""" +This script signs or certifies a key. + +The gpg-agent and pinentry are invoked to enter the passphrase. +""") + +c = gpg.Context() + +homedir = input("Enter the GPG configuration directory path (optional): ") +fpr0 = input("Enter the fingerprint of the key to sign: ") +userid = input("Enter the UID to sign (case sensitive, optional): ") +sig_type = input("Enter the certification type (local or normal): ") + +if homedir.startswith("~"): + if os.path.exists(os.path.expanduser(homedir)) is True: + c.home_dir = os.path.expanduser(homedir) + else: + pass +elif os.path.exists(homedir) is True: + c.home_dir = homedir +else: + pass + +fpr = "".join(fpr0.split()) +key = c.get_key(fpr, secret=False) + +if len(userid) > 0 and sig_type.lower() == "local": + c.key_sign(key, uids=userid, local=True) +elif len(userid) > 0 and sig_type.lower() != "local": + c.key_sign(key, uids=userid) +elif len(userid) == 0 and sig_type.lower() == "local": + c.key_sign(key, local=True) +else: + c.key_sign(key) diff --git a/lang/python/examples/howto/temp-homedir-config.py b/lang/python/examples/howto/temp-homedir-config.py new file mode 100755 index 0000000..ddd7932 --- /dev/null +++ b/lang/python/examples/howto/temp-homedir-config.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +from __future__ import absolute_import, division, unicode_literals + +# Copyright (C) 2018 Ben McGinnes <ben@gnupg.org> +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License and the GNU +# Lesser General Public Licensefor more details. +# +# You should have received a copy of the GNU General Public License and the GNU +# Lesser General Public along with this program; if not, see +# <http://www.gnu.org/licenses/>. + +import os +import os.path +import sys + +intro = """ +This script creates a temporary directory to use as a homedir for +testing key generation tasks with the correct permissions, along +with a gpg.conf file containing the same configuration options +listed in the HOWTO. + +You may wish to change the order of the cipher preferences or +remove those not relevant to your installation. These +configuration parameters assume that all ciphers and digests are +installed and available rather than limiting to the default +ciphers and digests. + +The script prompts for a directory name to be installed as a hidden +directory in the user's home directory on POSIX systems. So if you +enter "gnupg-temp" on a Linux, BSD or OS X system, it will create +"~/.gnupg-temp" (you do not need to enter the leading dot). + +This script has not been tested on Windows systems and may have +unpredictable results. That said, it will not delete or copy over +existing data. + +If the directory already exists, the script will terminate with a +message telling you to specify a new directory name. There is no +default directory name. +""" + +gpgconf = """# gpg.conf settings for key generation: +expert +allow-freeform-uid +allow-secret-key-import +trust-model tofu+pgp +tofu-default-policy unknown +enable-large-rsa +enable-dsa2 +cert-digest-algo SHA512 +default-preference-list TWOFISH CAMELLIA256 AES256 CAMELLIA192 AES192 CAMELLIA128 AES BLOWFISH IDEA CAST5 3DES SHA512 SHA384 SHA256 SHA224 RIPEMD160 SHA1 ZLIB BZIP2 ZIP Uncompressed +personal-cipher-preferences TWOFISH CAMELLIA256 AES256 CAMELLIA192 AES192 CAMELLIA128 AES BLOWFISH IDEA CAST5 3DES +personal-digest-preferences SHA512 SHA384 SHA256 SHA224 RIPEMD160 SHA1 +personal-compress-preferences ZLIB BZIP2 ZIP Uncompressed +""" + +agentconf = """# gpg-agent.conf settings for key generation: +default-cache-ttl 300 +""" + +if len(sys.argv) == 1: + print(intro) + new_homedir = input("Enter the temporary gnupg homedir name: ") +elif len(sys.argv) == 2: + new_homedir = sys.argv[1] +else: + new_homedir = " ".join(sys.argv[1:]) + +userdir = os.path.expanduser("~") + +if new_homedir.startswith("~"): + new_homdir.replace("~", "") +else: + pass + +if new_homedir.startswith("/"): + new_homdir.replace("/", "") +else: + pass + +if new_homedir.startswith("."): + new_homdir.replace(".", "_") +else: + pass + +if new_homedir.count(" ") > 0: + new_homedir.replace(" ", "_") +else: + pass + +nh = "{0}/.{1}".format(userdir, new_homedir) + +if os.path.exists(nh) is True: + print("The {0} directory already exists.".format(nh)) +else: + print("Creating the {0} directory.".format(nh)) + os.mkdir(nh) + os.chmod(nh, 0o700) + with open("{0}/{1}".format(nh, "gpg.conf"), "w") as f1: + f1.write(gpgconf) + os.chmod("{0}/{1}".format(nh, "gpg.conf"), 0o600) + with open("{0}/{1}".format(nh, "gpg-agent.conf"), "w") as f2: + f2.write(gpgconf) + os.chmod("{0}/{1}".format(nh, "gpg-agent.conf"), 0o600) + print("""You may now use the {0} directory as an alternative GPG homedir: + +gpg --homedir {0} +gpg --homedir --full-gen-key + +Or with GPGME scripts, including the GPGME Python bindings. +""") diff --git a/lang/python/examples/howto/verify-signatures.py b/lang/python/examples/howto/verify-signatures.py new file mode 100755 index 0000000..8aafc3b --- /dev/null +++ b/lang/python/examples/howto/verify-signatures.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +from __future__ import absolute_import, division, unicode_literals + +# Copyright (C) 2018 Ben McGinnes <ben@gnupg.org> +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License and the GNU +# Lesser General Public Licensefor more details. +# +# You should have received a copy of the GNU General Public License and the GNU +# Lesser General Public along with this program; if not, see +# <http://www.gnu.org/licenses/>. + +import gpg +import sys +import time + +""" +Verifies a signed file which has been signed with a detached signature. +""" + +if len(sys.argv) > 2: + filename = sys.argv[1] + sig_file = sys.argv[2] +elif len(sys.argv) == 2: + filename = sys.argv[1] + sig_file = input("Enter the path and filename of the detached signature: ") +else: + filename = input("Enter the path and filename to verify: ") + sig_file = input("Enter the path and filename of the detached signature: ") + +c = gpg.Context() + +try: + data, result = c.verify(open(filename), open(sig_file)) + verified = True +except gpg.errors.BadSignatures as e: + verified = False + print(e) + +if verified is True: + for i in range(len(result.signatures)): + sign = result.signatures[i] + print("""Good signature from: +{0} +with key {1} +made at {2} +""".format(c.get_key(sign.fpr).uids[0].uid, sign.fpr, + time.ctime(sign.timestamp))) +else: + pass diff --git a/lang/python/examples/howto/verify-signed-file.py b/lang/python/examples/howto/verify-signed-file.py new file mode 100755 index 0000000..9f8702f --- /dev/null +++ b/lang/python/examples/howto/verify-signed-file.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +from __future__ import absolute_import, division, unicode_literals + +# Copyright (C) 2018 Ben McGinnes <ben@gnupg.org> +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License and the GNU +# Lesser General Public Licensefor more details. +# +# You should have received a copy of the GNU General Public License and the GNU +# Lesser General Public along with this program; if not, see +# <http://www.gnu.org/licenses/>. + +import gpg +import sys +import time + +""" +Verifies a signed file which has been signed with either NORMAL or CLEAR modes. +""" + +if len(sys.argv) > 2: + filename = " ".join(sys.argv[1:]) +elif len(sys.argv) == 2: + filename = sys.argv[1] +else: + filename = input("Enter the path and filename to sign: ") + +c = gpg.Context() + +try: + data, result = c.verify(open(filename)) + verified = True +except gpg.errors.BadSignatures as e: + verified = False + print(e) + +if verified is True: + for i in range(len(result.signatures)): + sign = result.signatures[i] + print("""Good signature from: +{0} +with key {1} +made at {2} +""".format(c.get_key(sign.fpr).uids[0].uid, sign.fpr, + time.ctime(sign.timestamp))) +else: + pass diff --git a/lang/python/examples/encrypt-to-all.py b/lang/python/examples/low_level-encrypt_to_all.py index bad4220..bad4220 100755 --- a/lang/python/examples/encrypt-to-all.py +++ b/lang/python/examples/low_level-encrypt_to_all.py diff --git a/lang/python/setup.py.in b/lang/python/setup.py.in index f9dda20..2595073 100755 --- a/lang/python/setup.py.in +++ b/lang/python/setup.py.in @@ -152,25 +152,8 @@ class BuildExtFirstHack(build): sink.write(content) def _generate_gpgme_h(self, source_name, sink_name): - if up_to_date(source_name, sink_name): - return - print("Using gpgme.h from {}".format(source_name)) - - deprec_func = re.compile(r'^(.*typedef.*|.*\(.*\)|[^#]+\s+.+)' - + r'\s*_GPGME_DEPRECATED(_OUTSIDE_GPGME)?\(.*\);\s*', - re.S) - line_break = re.compile(';|\\$|\\x0c|^\s*#|{') - - with open(sink_name, "w") as sink, open(source_name) as source: - text = '' - for line in source: - text += re.sub(' class ', ' _py_obsolete_class ', line) - if line_break.search(line): - if not deprec_func.search(text): - sink.write(text) - text = '' - sink.write(text) + shutil.copy2(source_name, sink_name) def _generate_errors_i(self): diff --git a/lang/python/tests/Makefile.am b/lang/python/tests/Makefile.am index 25b15f2..3864f8b 100644 --- a/lang/python/tests/Makefile.am +++ b/lang/python/tests/Makefile.am @@ -18,8 +18,6 @@ GPG = gpg GPG_AGENT = gpg-agent -export GNUPGHOME := $(abs_builddir) -export GPG_AGENT_INFO := test_srcdir = $(top_srcdir)/tests/gpg @@ -79,7 +77,7 @@ xcheck: all CLEANFILES = secring.gpg pubring.gpg pubring.kbx trustdb.gpg dirmngr.conf \ gpg-agent.conf pubring.kbx~ gpg.conf pubring.gpg~ \ random_seed .gpg-v21-migrated tofu.db \ - pubring-stamp private-keys-v1.d/gpg-sample.stamp + pubring-stamp gpg-sample.stamp private_keys = \ $(test_srcdir)/13CD0F3BDF24BE53FE192D62F18737256FF6E4FD \ @@ -89,26 +87,25 @@ private_keys = \ $(test_srcdir)/7A030357C0F253A5BBCD282FFC4E521B37558F5C clean-local: - -$(top_srcdir)/tests/start-stop-agent --stop + -$(TESTS_ENVIRONMENT) $(top_srcdir)/tests/start-stop-agent --stop -rm -fR -- private-keys-v1.d openpgp-revocs.d S.gpg-agent sshcontrol BUILT_SOURCES = gpg.conf gpg-agent.conf pubring-stamp \ - private-keys-v1.d/gpg-sample.stamp + gpg-sample.stamp -private-keys-v1.d/gpg-sample.stamp: $(private_keys) - -gpgconf --kill all +gpg-sample.stamp: $(private_keys) + -$(TESTS_ENVIRONMENT) gpgconf --kill all $(MKDIR_P) ./private-keys-v1.d for k in $(private_keys); do \ cp $$k private-keys-v1.d/$${k#$(test_srcdir)/}.key; \ done - echo x > ./private-keys-v1.d/gpg-sample.stamp + echo x > ./gpg-sample.stamp -pubring-stamp: $(test_srcdir)/pubdemo.asc \ - ./private-keys-v1.d/gpg-sample.stamp - $(GPG) --batch --no-permission-warning \ +pubring-stamp: $(test_srcdir)/pubdemo.asc gpg-sample.stamp + $(TESTS_ENVIRONMENT) $(GPG) --batch --no-permission-warning \ --import $(test_srcdir)/pubdemo.asc - -$(GPG) --batch --no-permission-warning \ + -$(TESTS_ENVIRONMENT) $(GPG) --batch --no-permission-warning \ --import $(test_srcdir)/secdemo.asc echo x > ./pubring-stamp diff --git a/lang/python/tests/Makefile.in b/lang/python/tests/Makefile.in index 89fcee2..c7512dd 100644 --- a/lang/python/tests/Makefile.in +++ b/lang/python/tests/Makefile.in @@ -375,7 +375,7 @@ EXTRA_DIST = support.py $(XTESTS) encrypt-only.asc sign-only.asc \ CLEANFILES = secring.gpg pubring.gpg pubring.kbx trustdb.gpg dirmngr.conf \ gpg-agent.conf pubring.kbx~ gpg.conf pubring.gpg~ \ random_seed .gpg-v21-migrated tofu.db \ - pubring-stamp private-keys-v1.d/gpg-sample.stamp + pubring-stamp gpg-sample.stamp private_keys = \ $(test_srcdir)/13CD0F3BDF24BE53FE192D62F18737256FF6E4FD \ @@ -385,7 +385,7 @@ private_keys = \ $(test_srcdir)/7A030357C0F253A5BBCD282FFC4E521B37558F5C BUILT_SOURCES = gpg.conf gpg-agent.conf pubring-stamp \ - private-keys-v1.d/gpg-sample.stamp + gpg-sample.stamp all: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) all-am @@ -582,8 +582,6 @@ uninstall-am: mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags-am uninstall uninstall-am -export GNUPGHOME := $(abs_builddir) -export GPG_AGENT_INFO := # XXX: Currently, one cannot override automake's 'check' target. As a # workaround, we avoid defining 'TESTS', thus automake will not emit @@ -601,22 +599,21 @@ xcheck: all $(XTESTS) clean-local: - -$(top_srcdir)/tests/start-stop-agent --stop + -$(TESTS_ENVIRONMENT) $(top_srcdir)/tests/start-stop-agent --stop -rm -fR -- private-keys-v1.d openpgp-revocs.d S.gpg-agent sshcontrol -private-keys-v1.d/gpg-sample.stamp: $(private_keys) - -gpgconf --kill all +gpg-sample.stamp: $(private_keys) + -$(TESTS_ENVIRONMENT) gpgconf --kill all $(MKDIR_P) ./private-keys-v1.d for k in $(private_keys); do \ cp $$k private-keys-v1.d/$${k#$(test_srcdir)/}.key; \ done - echo x > ./private-keys-v1.d/gpg-sample.stamp + echo x > ./gpg-sample.stamp -pubring-stamp: $(test_srcdir)/pubdemo.asc \ - ./private-keys-v1.d/gpg-sample.stamp - $(GPG) --batch --no-permission-warning \ +pubring-stamp: $(test_srcdir)/pubdemo.asc gpg-sample.stamp + $(TESTS_ENVIRONMENT) $(GPG) --batch --no-permission-warning \ --import $(test_srcdir)/pubdemo.asc - -$(GPG) --batch --no-permission-warning \ + -$(TESTS_ENVIRONMENT) $(GPG) --batch --no-permission-warning \ --import $(test_srcdir)/secdemo.asc echo x > ./pubring-stamp diff --git a/lang/qt/tests/Makefile.am b/lang/qt/tests/Makefile.am index a662b4c..104672e 100644 --- a/lang/qt/tests/Makefile.am +++ b/lang/qt/tests/Makefile.am @@ -70,21 +70,19 @@ CLEANFILES = secring.gpg pubring.gpg pubring.kbx trustdb.gpg dirmngr.conf \ gpg.conf tofu.db clean-local: - -$(top_srcdir)/tests/start-stop-agent --stop + -$(TESTS_ENVIRONMENT) $(top_srcdir)/tests/start-stop-agent --stop -rm -fR private-keys-v1.d crls.d -export GNUPGHOME := $(abs_builddir) - pubring-stamp: $(top_srcdir)/tests/gpg/pubdemo.asc \ $(top_srcdir)/tests/gpg/secdemo.asc - -gpgconf --kill all + -$(TESTS_ENVIRONMENT) gpgconf --kill all echo "ignore-invalid-option allow-loopback-pinentry" > $(abs_builddir)/gpg-agent.conf echo "allow-loopback-pinentry" >> gpg-agent.conf echo "ignore-invalid-option pinentry-mode" > gpg.conf echo "pinentry-mode loopback" >> gpg.conf - $(GPG) --no-permission-warning \ + $(TESTS_ENVIRONMENT) $(GPG) --no-permission-warning \ --import $(top_srcdir)/tests/gpg/pubdemo.asc - $(GPG) --no-permission-warning \ + $(TESTS_ENVIRONMENT) $(GPG) --no-permission-warning \ --passphrase "abc" \ --import $(top_srcdir)/tests/gpg/secdemo.asc touch pubring-stamp diff --git a/lang/qt/tests/Makefile.in b/lang/qt/tests/Makefile.in index adf9bb1..075747f 100644 --- a/lang/qt/tests/Makefile.in +++ b/lang/qt/tests/Makefile.in @@ -958,21 +958,19 @@ uninstall-am: clean-local: - -$(top_srcdir)/tests/start-stop-agent --stop + -$(TESTS_ENVIRONMENT) $(top_srcdir)/tests/start-stop-agent --stop -rm -fR private-keys-v1.d crls.d -export GNUPGHOME := $(abs_builddir) - pubring-stamp: $(top_srcdir)/tests/gpg/pubdemo.asc \ $(top_srcdir)/tests/gpg/secdemo.asc - -gpgconf --kill all + -$(TESTS_ENVIRONMENT) gpgconf --kill all echo "ignore-invalid-option allow-loopback-pinentry" > $(abs_builddir)/gpg-agent.conf echo "allow-loopback-pinentry" >> gpg-agent.conf echo "ignore-invalid-option pinentry-mode" > gpg.conf echo "pinentry-mode loopback" >> gpg.conf - $(GPG) --no-permission-warning \ + $(TESTS_ENVIRONMENT) $(GPG) --no-permission-warning \ --import $(top_srcdir)/tests/gpg/pubdemo.asc - $(GPG) --no-permission-warning \ + $(TESTS_ENVIRONMENT) $(GPG) --no-permission-warning \ --passphrase "abc" \ --import $(top_srcdir)/tests/gpg/secdemo.asc touch pubring-stamp diff --git a/lang/qt/tests/t-config.cpp b/lang/qt/tests/t-config.cpp index e04a6bb..afbb4d1 100644 --- a/lang/qt/tests/t-config.cpp +++ b/lang/qt/tests/t-config.cpp @@ -39,6 +39,8 @@ #include "t-support.h" #include "protocol.h" #include "cryptoconfig.h" +#include "engineinfo.h" + #include <unistd.h> using namespace QGpgME; @@ -51,7 +53,7 @@ private Q_SLOTS: void testKeyserver() { // Repeatedly set a config value and clear it - // this war broken at some point so it gets a + // this was broken at some point so it gets a // unit test. for (int i = 0; i < 10; i++) { auto conf = cryptoConfig(); @@ -78,6 +80,36 @@ private Q_SLOTS: } } + void testDefault() + { + if (GpgME::engineInfo(GpgME::GpgEngine).engineVersion() < "2.2.0") { + // We are using compliance here and other options might also + // be unsupported in older versions. + return; + } + // First set compliance to de-vs + auto conf = cryptoConfig(); + QVERIFY(conf); + auto entry = conf->entry(QStringLiteral("gpg"), + QStringLiteral("Configuration"), + QStringLiteral("compliance")); + QVERIFY(entry); + entry->setStringValue("de-vs"); + conf->sync(true); + conf->clear(); + entry = conf->entry(QStringLiteral("gpg"), + QStringLiteral("Configuration"), + QStringLiteral("compliance")); + QCOMPARE(entry->stringValue(), QStringLiteral("de-vs")); + entry->resetToDefault(); + conf->sync(true); + conf->clear(); + entry = conf->entry(QStringLiteral("gpg"), + QStringLiteral("Configuration"), + QStringLiteral("compliance")); + QCOMPARE(entry->stringValue(), QStringLiteral("gnupg")); + } + void initTestCase() { QGpgMETest::initTestCase(); diff --git a/src/Makefile.am b/src/Makefile.am index ce6f1d4..c2d4a84 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -26,7 +26,7 @@ m4datadir = $(datadir)/aclocal m4data_DATA = gpgme.m4 nodist_include_HEADERS = gpgme.h -bin_PROGRAMS = gpgme-tool +bin_PROGRAMS = gpgme-tool gpgme-json if BUILD_W32_GLIB ltlib_gpgme_glib = libgpgme-glib.la @@ -95,13 +95,18 @@ if BUILD_W32_GLIB libgpgme_glib_la_SOURCES = $(main_sources) w32-glib-io.c endif -# We use a global CFLAGS setting for all library +# We use a global CFLAGS setting for all libraries # versions, because then every object file is only compiled once. AM_CFLAGS = @LIBASSUAN_CFLAGS@ @GLIB_CFLAGS@ gpgme_tool_SOURCES = gpgme-tool.c argparse.c argparse.h gpgme_tool_LDADD = libgpgme.la @LIBASSUAN_LIBS@ +gpgme_json_SOURCES = gpgme-json.c cJSON.c cJSON.h +gpgme_json_LDADD = -lm libgpgme.la $(GPG_ERROR_LIBS) +# We use -no-install temporary during development. +gpgme_json_LDFLAGS = -no-install + if HAVE_W32_SYSTEM # Windows provides us with an endless stream of Tough Love. To spawn diff --git a/src/Makefile.in b/src/Makefile.in index dfdf550..5eac9e1 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -100,14 +100,14 @@ PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ -bin_PROGRAMS = gpgme-tool$(EXEEXT) +bin_PROGRAMS = gpgme-tool$(EXEEXT) gpgme-json$(EXEEXT) @HAVE_W32CE_SYSTEM_TRUE@am__append_1 = w32-ce.h w32-ce.c @HAVE_W32CE_SYSTEM_FALSE@@HAVE_W32_SYSTEM_TRUE@libexec_PROGRAMS = gpgme-w32spawn$(EXEEXT) subdir = src DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ $(top_srcdir)/build-aux/mkinstalldirs \ $(srcdir)/versioninfo.rc.in $(srcdir)/gpgme.h.in \ - $(srcdir)/gpgme-config.in ttyname_r.c setenv.c stpcpy.c \ + $(srcdir)/gpgme-config.in stpcpy.c setenv.c ttyname_r.c \ $(top_srcdir)/build-aux/depcomp ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \ @@ -231,6 +231,13 @@ libgpgme_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(libgpgme_la_LDFLAGS) $(LDFLAGS) -o $@ PROGRAMS = $(bin_PROGRAMS) $(libexec_PROGRAMS) +am_gpgme_json_OBJECTS = gpgme-json.$(OBJEXT) cJSON.$(OBJEXT) +gpgme_json_OBJECTS = $(am_gpgme_json_OBJECTS) +am__DEPENDENCIES_2 = +gpgme_json_DEPENDENCIES = libgpgme.la $(am__DEPENDENCIES_2) +gpgme_json_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(gpgme_json_LDFLAGS) $(LDFLAGS) -o $@ am_gpgme_tool_OBJECTS = gpgme-tool.$(OBJEXT) argparse.$(OBJEXT) gpgme_tool_OBJECTS = $(am_gpgme_tool_OBJECTS) gpgme_tool_DEPENDENCIES = libgpgme.la @@ -273,10 +280,10 @@ am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libgpgme_glib_la_SOURCES) $(libgpgme_la_SOURCES) \ - $(gpgme_tool_SOURCES) gpgme-w32spawn.c + $(gpgme_json_SOURCES) $(gpgme_tool_SOURCES) gpgme-w32spawn.c DIST_SOURCES = $(am__libgpgme_glib_la_SOURCES_DIST) \ - $(am__libgpgme_la_SOURCES_DIST) $(gpgme_tool_SOURCES) \ - gpgme-w32spawn.c + $(am__libgpgme_la_SOURCES_DIST) $(gpgme_json_SOURCES) \ + $(gpgme_tool_SOURCES) gpgme-w32spawn.c am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ @@ -551,11 +558,15 @@ main_sources = \ libgpgme_la_SOURCES = $(main_sources) $(system_components_not_extra) @BUILD_W32_GLIB_TRUE@libgpgme_glib_la_SOURCES = $(main_sources) w32-glib-io.c -# We use a global CFLAGS setting for all library +# We use a global CFLAGS setting for all libraries # versions, because then every object file is only compiled once. AM_CFLAGS = @LIBASSUAN_CFLAGS@ @GLIB_CFLAGS@ gpgme_tool_SOURCES = gpgme-tool.c argparse.c argparse.h gpgme_tool_LDADD = libgpgme.la @LIBASSUAN_LIBS@ +gpgme_json_SOURCES = gpgme-json.c cJSON.c cJSON.h +gpgme_json_LDADD = -lm libgpgme.la $(GPG_ERROR_LIBS) +# We use -no-install temporary during development. +gpgme_json_LDFLAGS = -no-install @HAVE_W32_SYSTEM_TRUE@RCCOMPILE = $(RC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) @HAVE_W32_SYSTEM_TRUE@LTRCCOMPILE = $(LIBTOOL) --mode=compile --tag=RC $(RCCOMPILE) @HAVE_W32_SYSTEM_TRUE@SUFFIXES = .rc .lo @@ -768,6 +779,10 @@ clean-libexecPROGRAMS: echo " rm -f" $$list; \ rm -f $$list +gpgme-json$(EXEEXT): $(gpgme_json_OBJECTS) $(gpgme_json_DEPENDENCIES) $(EXTRA_gpgme_json_DEPENDENCIES) + @rm -f gpgme-json$(EXEEXT) + $(AM_V_CCLD)$(gpgme_json_LINK) $(gpgme_json_OBJECTS) $(gpgme_json_LDADD) $(LIBS) + gpgme-tool$(EXEEXT): $(gpgme_tool_OBJECTS) $(gpgme_tool_DEPENDENCIES) $(EXTRA_gpgme_tool_DEPENDENCIES) @rm -f gpgme-tool$(EXEEXT) $(AM_V_CCLD)$(LINK) $(gpgme_tool_OBJECTS) $(gpgme_tool_LDADD) $(LIBS) @@ -824,6 +839,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/assuan-support.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ath.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/b64dec.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cJSON.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/conversion.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/data-compat.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/data-fd.Plo@am__quote@ @@ -854,6 +870,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/get-env.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getauditlog.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gpgconf.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gpgme-json.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gpgme-tool.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gpgme-w32spawn.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gpgme.Plo@am__quote@ diff --git a/src/cJSON.c b/src/cJSON.c new file mode 100644 index 0000000..cf0cb13 --- /dev/null +++ b/src/cJSON.c @@ -0,0 +1,1404 @@ +/* cJSON.c - JSON parser in C. + * Copyright (c) 2009 Dave Gamble + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + * + * Note that this code has been modified from the original code taken + * from cjson-code-58.zip. + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <string.h> +#include <stdio.h> +#include <math.h> +#include <stdlib.h> +#include <float.h> +#include <limits.h> +#include <ctype.h> +#include <errno.h> + +#include "cJSON.h" + +/* We use malloc function wrappers from gpgrt (aka libgpg-error). */ +#if 1 +# include <gpgrt.h> +# define xtrymalloc(a) gpgrt_malloc ((a)) +# define xtrycalloc(a,b) gpgrt_calloc ((a), (b)) +# define xtrystrdup(a) gpgrt_strdup ((a)) +# define xfree(a) gpgrt_free ((a)) +#else +# define xtrymalloc(a) malloc ((a)) +# define xtrycalloc(a,b) calloc ((a), (b)) +# define xtrystrdup(a) strdup ((a)) +# define xfree(a) free ((a)) +#endif + + +static int +cJSON_strcasecmp (const char *s1, const char *s2) +{ + if (!s1) + return (s1 == s2) ? 0 : 1; + if (!s2) + return 1; + for (; tolower (*(const unsigned char *)s1) + == tolower (*(const unsigned char *) s2); ++s1, ++s2) + if (*s1 == 0) + return 0; + return tolower (*(const unsigned char *) s1) - + tolower (*(const unsigned char *) s2); +} + +/* Internal constructor. */ +static cJSON * +cJSON_New_Item (void) +{ + return xtrycalloc (1, sizeof (cJSON)); +} + +/* Delete a cJSON structure. (Does not clobber ERRNO). */ +void +cJSON_Delete (cJSON * c) +{ + cJSON *next; + int save_errno; + + if (!c) + return; + + save_errno = errno; + while (c) + { + next = c->next; + if (!(c->type & cJSON_IsReference) && c->child) + cJSON_Delete (c->child); + if (!(c->type & cJSON_IsReference) && c->valuestring) + xfree (c->valuestring); + if (c->string) + xfree (c->string); + xfree (c); + c = next; + } + errno = save_errno; +} + +/* Parse the input text to generate a number, and populate the result + * into item. */ +static const char * +parse_number (cJSON * item, const char *num) +{ + double n = 0, sign = 1, scale = 0; + int subscale = 0, signsubscale = 1; + + if (*num == '-') + sign = -1, num++; /* Has sign? */ + if (*num == '0') + num++; /* is zero */ + if (*num >= '1' && *num <= '9') + do + n = (n * 10.0) + (*num++ - '0'); + while (*num >= '0' && *num <= '9'); /* Number? */ + if (*num == '.' && num[1] >= '0' && num[1] <= '9') + { + num++; + do + n = (n * 10.0) + (*num++ - '0'), scale--; + while (*num >= '0' && *num <= '9'); + } /* Fractional part? */ + if (*num == 'e' || *num == 'E') /* Exponent? */ + { + num++; + if (*num == '+') + num++; + else if (*num == '-') + signsubscale = -1, num++; /* With sign? */ + while (*num >= '0' && *num <= '9') + subscale = (subscale * 10) + (*num++ - '0'); /* Number? */ + } + + /* number = +/- number.fraction * 10^+/- exponent */ + n = sign * n * pow (10.0, (scale + subscale * signsubscale)); + + item->valuedouble = n; + item->valueint = (int) n; + item->type = cJSON_Number; + return num; +} + +/* Render the number nicely from the given item into a string. */ +static char * +print_number (cJSON * item) +{ + char *str; + double d = item->valuedouble; + if (fabs (((double) item->valueint) - d) <= DBL_EPSILON && d <= INT_MAX + && d >= INT_MIN) + { + /* 2^64+1 can be represented in 21 chars. */ + str = xtrymalloc (21); + if (str) + sprintf (str, "%d", item->valueint); + } + else + { + str = xtrymalloc (64); /* This is a nice tradeoff. */ + if (str) + { + if (fabs (floor (d) - d) <= DBL_EPSILON && fabs (d) < 1.0e60) + sprintf (str, "%.0f", d); + else if (fabs (d) < 1.0e-6 || fabs (d) > 1.0e9) + sprintf (str, "%e", d); + else + sprintf (str, "%f", d); + } + } + return str; +} + +static unsigned +parse_hex4 (const char *str) +{ + unsigned h = 0; + if (*str >= '0' && *str <= '9') + h += (*str) - '0'; + else if (*str >= 'A' && *str <= 'F') + h += 10 + (*str) - 'A'; + else if (*str >= 'a' && *str <= 'f') + h += 10 + (*str) - 'a'; + else + return 0; + h = h << 4; + str++; + if (*str >= '0' && *str <= '9') + h += (*str) - '0'; + else if (*str >= 'A' && *str <= 'F') + h += 10 + (*str) - 'A'; + else if (*str >= 'a' && *str <= 'f') + h += 10 + (*str) - 'a'; + else + return 0; + h = h << 4; + str++; + if (*str >= '0' && *str <= '9') + h += (*str) - '0'; + else if (*str >= 'A' && *str <= 'F') + h += 10 + (*str) - 'A'; + else if (*str >= 'a' && *str <= 'f') + h += 10 + (*str) - 'a'; + else + return 0; + h = h << 4; + str++; + if (*str >= '0' && *str <= '9') + h += (*str) - '0'; + else if (*str >= 'A' && *str <= 'F') + h += 10 + (*str) - 'A'; + else if (*str >= 'a' && *str <= 'f') + h += 10 + (*str) - 'a'; + else + return 0; + return h; +} + +/* Parse the input text into an unescaped cstring, and populate item. */ +static const unsigned char firstByteMark[7] = + { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; +static const char * +parse_string (cJSON * item, const char *str, const char **ep) +{ + const char *ptr = str + 1; + char *ptr2; + char *out; + int len = 0; + unsigned uc, uc2; + if (*str != '\"') + { + *ep = str; + return 0; + } /* not a string! */ + + while (*ptr != '\"' && *ptr && ++len) + if (*ptr++ == '\\') + ptr++; /* Skip escaped quotes. */ + + out = xtrymalloc (len + 1); /* This is how long we need for the + string, roughly. */ + if (!out) + return 0; + + ptr = str + 1; + ptr2 = out; + while (*ptr != '\"' && *ptr) + { + if (*ptr != '\\') + *ptr2++ = *ptr++; + else + { + ptr++; + switch (*ptr) + { + case 'b': + *ptr2++ = '\b'; + break; + case 'f': + *ptr2++ = '\f'; + break; + case 'n': + *ptr2++ = '\n'; + break; + case 'r': + *ptr2++ = '\r'; + break; + case 't': + *ptr2++ = '\t'; + break; + case 'u': /* transcode utf16 to utf8. */ + uc = parse_hex4 (ptr + 1); + ptr += 4; /* get the unicode char. */ + + if ((uc >= 0xDC00 && uc <= 0xDFFF) || uc == 0) + break; /* check for invalid. */ + + if (uc >= 0xD800 && uc <= 0xDBFF) /* UTF16 surrogate pairs. */ + { + if (ptr[1] != '\\' || ptr[2] != 'u') + break; /* missing second-half of surrogate. */ + uc2 = parse_hex4 (ptr + 3); + ptr += 6; + if (uc2 < 0xDC00 || uc2 > 0xDFFF) + break; /* invalid second-half of surrogate. */ + uc = 0x10000 + (((uc & 0x3FF) << 10) | (uc2 & 0x3FF)); + } + + len = 4; + if (uc < 0x80) + len = 1; + else if (uc < 0x800) + len = 2; + else if (uc < 0x10000) + len = 3; + ptr2 += len; + + switch (len) + { + case 4: + *--ptr2 = ((uc | 0x80) & 0xBF); + uc >>= 6; + case 3: + *--ptr2 = ((uc | 0x80) & 0xBF); + uc >>= 6; + case 2: + *--ptr2 = ((uc | 0x80) & 0xBF); + uc >>= 6; + case 1: + *--ptr2 = (uc | firstByteMark[len]); + } + ptr2 += len; + break; + default: + *ptr2++ = *ptr; + break; + } + ptr++; + } + } + *ptr2 = 0; + if (*ptr == '\"') + ptr++; + item->valuestring = out; + item->type = cJSON_String; + return ptr; +} + +/* Render the cstring provided to an escaped version that can be printed. */ +static char * +print_string_ptr (const char *str) +{ + const char *ptr; + char *ptr2, *out; + int len = 0; + unsigned char token; + + if (!str) + return xtrystrdup (""); + ptr = str; + while ((token = *ptr) && ++len) + { + if (strchr ("\"\\\b\f\n\r\t", token)) + len++; + else if (token < 32) + len += 5; + ptr++; + } + + out = xtrymalloc (len + 3); + if (!out) + return 0; + + ptr2 = out; + ptr = str; + *ptr2++ = '\"'; + while (*ptr) + { + if ((unsigned char) *ptr > 31 && *ptr != '\"' && *ptr != '\\') + *ptr2++ = *ptr++; + else + { + *ptr2++ = '\\'; + switch (token = *ptr++) + { + case '\\': + *ptr2++ = '\\'; + break; + case '\"': + *ptr2++ = '\"'; + break; + case '\b': + *ptr2++ = 'b'; + break; + case '\f': + *ptr2++ = 'f'; + break; + case '\n': + *ptr2++ = 'n'; + break; + case '\r': + *ptr2++ = 'r'; + break; + case '\t': + *ptr2++ = 't'; + break; + default: + sprintf (ptr2, "u%04x", token); + ptr2 += 5; + break; /* escape and print */ + } + } + } + *ptr2++ = '\"'; + *ptr2++ = 0; + return out; +} + +/* Invote print_string_ptr (which is useful) on an item. */ +static char * +print_string (cJSON * item) +{ + return print_string_ptr (item->valuestring); +} + +/* Predeclare these prototypes. */ +static const char *parse_value (cJSON * item, const char *value, + const char **ep); +static char *print_value (cJSON * item, int depth, int fmt); +static const char *parse_array (cJSON * item, const char *value, + const char **ep); +static char *print_array (cJSON * item, int depth, int fmt); +static const char *parse_object (cJSON * item, const char *value, + const char **ep); +static char *print_object (cJSON * item, int depth, int fmt); + +/* Utility to jump whitespace and cr/lf */ +static const char * +skip (const char *in) +{ + while (in && *in && (unsigned char) *in <= 32) + in++; + return in; +} + +/* Parse an object - create a new root, and populate. */ +cJSON * +cJSON_ParseWithOpts (const char *value, const char **return_parse_end, + int require_null_terminated, size_t *r_erroff) +{ + const char *end = 0; + const char *ep = 0; + cJSON *c; + + if (r_erroff) + *r_erroff = 0; + + c = cJSON_New_Item (); + if (!c) + return NULL; /* memory fail */ + + end = parse_value (c, skip (value), &ep); + if (!end) + { + cJSON_Delete (c); + errno = EINVAL; + if (r_erroff) + *r_erroff = ep - value; + return 0; + } /* parse failure. ep is set. */ + + /* if we require null-terminated JSON without appended garbage, skip + and then check for a null terminator */ + if (require_null_terminated) + { + end = skip (end); + if (*end) + { + cJSON_Delete (c); + ep = end; + errno = EINVAL; + if (r_erroff) + *r_erroff = ep - value; + return 0; + } + } + if (return_parse_end) + *return_parse_end = end; + return c; +} + +/* Default options for cJSON_Parse */ +cJSON * +cJSON_Parse (const char *value, size_t *r_erroff) +{ + return cJSON_ParseWithOpts (value, 0, 0, r_erroff); +} + +/* Render a cJSON item/entity/structure to text. */ +char * +cJSON_Print (cJSON * item) +{ + return print_value (item, 0, 1); +} + +char * +cJSON_PrintUnformatted (cJSON * item) +{ + return print_value (item, 0, 0); +} + +/* Parser core - when encountering text, process appropriately. */ +static const char * +parse_value (cJSON * item, const char *value, const char **ep) +{ + if (!value) + return 0; /* Fail on null. */ + if (!strncmp (value, "null", 4)) + { + item->type = cJSON_NULL; + return value + 4; + } + if (!strncmp (value, "false", 5)) + { + item->type = cJSON_False; + return value + 5; + } + if (!strncmp (value, "true", 4)) + { + item->type = cJSON_True; + item->valueint = 1; + return value + 4; + } + if (*value == '\"') + { + return parse_string (item, value, ep); + } + if (*value == '-' || (*value >= '0' && *value <= '9')) + { + return parse_number (item, value); + } + if (*value == '[') + { + return parse_array (item, value, ep); + } + if (*value == '{') + { + return parse_object (item, value, ep); + } + + *ep = value; + return 0; /* failure. */ +} + +/* Render a value to text. */ +static char * +print_value (cJSON * item, int depth, int fmt) +{ + char *out = 0; + if (!item) + return 0; + switch ((item->type) & 255) + { + case cJSON_NULL: + out = xtrystrdup ("null"); + break; + case cJSON_False: + out = xtrystrdup ("false"); + break; + case cJSON_True: + out = xtrystrdup ("true"); + break; + case cJSON_Number: + out = print_number (item); + break; + case cJSON_String: + out = print_string (item); + break; + case cJSON_Array: + out = print_array (item, depth, fmt); + break; + case cJSON_Object: + out = print_object (item, depth, fmt); + break; + } + return out; +} + +/* Build an array from input text. */ +static const char * +parse_array (cJSON * item, const char *value, const char **ep) +{ + cJSON *child; + if (*value != '[') + { + *ep = value; + return 0; + } /* not an array! */ + + item->type = cJSON_Array; + value = skip (value + 1); + if (*value == ']') + return value + 1; /* empty array. */ + + item->child = child = cJSON_New_Item (); + if (!item->child) + return 0; /* memory fail */ + /* skip any spacing, get the value. */ + value = skip (parse_value (child, skip (value), ep)); + if (!value) + return 0; + + while (*value == ',') + { + cJSON *new_item; + if (!(new_item = cJSON_New_Item ())) + return 0; /* memory fail */ + child->next = new_item; + new_item->prev = child; + child = new_item; + value = skip (parse_value (child, skip (value + 1), ep)); + if (!value) + return 0; /* memory fail */ + } + + if (*value == ']') + return value + 1; /* end of array */ + *ep = value; + return 0; /* malformed. */ +} + +/* Render an array to text */ +static char * +print_array (cJSON * item, int depth, int fmt) +{ + char **entries; + char *out = 0, *ptr, *ret; + int len = 5; + cJSON *child = item->child; + int numentries = 0, i = 0, fail = 0; + + /* How many entries in the array? */ + while (child) + numentries++, child = child->next; + /* Explicitly handle numentries==0 */ + if (!numentries) + { + out = xtrymalloc (3); + if (out) + strcpy (out, "[]"); + return out; + } + /* Allocate an array to hold the values for each */ + entries = xtrymalloc (numentries * sizeof (char *)); + if (!entries) + return 0; + memset (entries, 0, numentries * sizeof (char *)); + /* Retrieve all the results: */ + child = item->child; + while (child && !fail) + { + ret = print_value (child, depth + 1, fmt); + entries[i++] = ret; + if (ret) + len += strlen (ret) + 2 + (fmt ? 1 : 0); + else + fail = 1; + child = child->next; + } + + /* If we didn't fail, try to xtrymalloc the output string */ + if (!fail) + out = xtrymalloc (len); + /* If that fails, we fail. */ + if (!out) + fail = 1; + + /* Handle failure. */ + if (fail) + { + for (i = 0; i < numentries; i++) + if (entries[i]) + xfree (entries[i]); + xfree (entries); + return 0; + } + + /* Compose the output array. */ + *out = '['; + ptr = out + 1; + *ptr = 0; + for (i = 0; i < numentries; i++) + { + strcpy (ptr, entries[i]); + ptr += strlen (entries[i]); + if (i != numentries - 1) + { + *ptr++ = ','; + if (fmt) + *ptr++ = ' '; + *ptr = 0; + } + xfree (entries[i]); + } + xfree (entries); + *ptr++ = ']'; + *ptr++ = 0; + return out; +} + +/* Build an object from the text. */ +static const char * +parse_object (cJSON * item, const char *value, const char **ep) +{ + cJSON *child; + if (*value != '{') + { + *ep = value; + return 0; + } /* not an object! */ + + item->type = cJSON_Object; + value = skip (value + 1); + if (*value == '}') + return value + 1; /* empty array. */ + + item->child = child = cJSON_New_Item (); + if (!item->child) + return 0; + value = skip (parse_string (child, skip (value), ep)); + if (!value) + return 0; + child->string = child->valuestring; + child->valuestring = 0; + if (*value != ':') + { + *ep = value; + return 0; + } /* fail! */ + /* skip any spacing, get the value. */ + value = skip (parse_value (child, skip (value + 1), ep)); + if (!value) + return 0; + + while (*value == ',') + { + cJSON *new_item; + if (!(new_item = cJSON_New_Item ())) + return 0; /* memory fail */ + child->next = new_item; + new_item->prev = child; + child = new_item; + value = skip (parse_string (child, skip (value + 1), ep)); + if (!value) + return 0; + child->string = child->valuestring; + child->valuestring = 0; + if (*value != ':') + { + *ep = value; + return 0; + } /* fail! */ + /* skip any spacing, get the value. */ + value = skip (parse_value (child, skip (value + 1), ep)); + if (!value) + return 0; + } + + if (*value == '}') + return value + 1; /* end of array */ + *ep = value; + return 0; /* malformed. */ +} + +/* Render an object to text. */ +static char * +print_object (cJSON * item, int depth, int fmt) +{ + char **entries = 0, **names = 0; + char *out = 0, *ptr, *ret, *str; + int len = 7, i = 0, j; + cJSON *child = item->child; + int numentries = 0, fail = 0; + /* Count the number of entries. */ + while (child) + numentries++, child = child->next; + /* Explicitly handle empty object case */ + if (!numentries) + { + out = xtrymalloc (fmt ? depth + 4 : 3); + if (!out) + return 0; + ptr = out; + *ptr++ = '{'; + if (fmt) + { + *ptr++ = '\n'; + for (i = 0; i < depth - 1; i++) + *ptr++ = '\t'; + } + *ptr++ = '}'; + *ptr++ = 0; + return out; + } + /* Allocate space for the names and the objects */ + entries = xtrymalloc (numentries * sizeof (char *)); + if (!entries) + return 0; + names = xtrymalloc (numentries * sizeof (char *)); + if (!names) + { + xfree (entries); + return 0; + } + memset (entries, 0, sizeof (char *) * numentries); + memset (names, 0, sizeof (char *) * numentries); + + /* Collect all the results into our arrays: */ + child = item->child; + depth++; + if (fmt) + len += depth; + while (child) + { + names[i] = str = print_string_ptr (child->string); + entries[i++] = ret = print_value (child, depth, fmt); + if (str && ret) + len += strlen (ret) + strlen (str) + 2 + (fmt ? 2 + depth : 0); + else + fail = 1; + child = child->next; + } + + /* Try to allocate the output string */ + if (!fail) + out = xtrymalloc (len); + if (!out) + fail = 1; + + /* Handle failure */ + if (fail) + { + for (i = 0; i < numentries; i++) + { + if (names[i]) + xfree (names[i]); + if (entries[i]) + xfree (entries[i]); + } + xfree (names); + xfree (entries); + return 0; + } + + /* Compose the output: */ + *out = '{'; + ptr = out + 1; + if (fmt) + *ptr++ = '\n'; + *ptr = 0; + for (i = 0; i < numentries; i++) + { + if (fmt) + for (j = 0; j < depth; j++) + *ptr++ = '\t'; + strcpy (ptr, names[i]); + ptr += strlen (names[i]); + *ptr++ = ':'; + if (fmt) + *ptr++ = '\t'; + strcpy (ptr, entries[i]); + ptr += strlen (entries[i]); + if (i != numentries - 1) + *ptr++ = ','; + if (fmt) + *ptr++ = '\n'; + *ptr = 0; + xfree (names[i]); + xfree (entries[i]); + } + + xfree (names); + xfree (entries); + if (fmt) + for (i = 0; i < depth - 1; i++) + *ptr++ = '\t'; + *ptr++ = '}'; + *ptr++ = 0; + return out; +} + +/* Get Array size/item / object item. */ +int +cJSON_GetArraySize (cJSON * array) +{ + cJSON *c = array->child; + int i = 0; + while (c) + i++, c = c->next; + return i; +} + +cJSON * +cJSON_GetArrayItem (cJSON * array, int item) +{ + cJSON *c = array->child; + while (c && item > 0) + item--, c = c->next; + return c; +} + +cJSON * +cJSON_GetObjectItem (cJSON * object, const char *string) +{ + cJSON *c = object->child; + while (c && cJSON_strcasecmp (c->string, string)) + c = c->next; + return c; +} + +/* Utility for array list handling. */ +static void +suffix_object (cJSON * prev, cJSON * item) +{ + prev->next = item; + item->prev = prev; +} + +/* Utility for handling references. */ +static cJSON * +create_reference (cJSON * item) +{ + cJSON *ref = cJSON_New_Item (); + if (!ref) + return 0; + memcpy (ref, item, sizeof (cJSON)); + ref->string = 0; + ref->type |= cJSON_IsReference; + ref->next = ref->prev = 0; + return ref; +} + +/* Add item to array/object. */ +void +cJSON_AddItemToArray (cJSON * array, cJSON * item) +{ + cJSON *c = array->child; + if (!item) + return; + if (!c) + { + array->child = item; + } + else + { + while (c && c->next) + c = c->next; + suffix_object (c, item); + } +} + +cJSON * +cJSON_AddItemToObject (cJSON * object, const char *string, cJSON * item) +{ + char *tmp; + + if (!item) + return 0; + tmp = xtrystrdup (string); + if (!tmp) + return NULL; + + if (item->string) + xfree (item->string); + item->string = tmp; + cJSON_AddItemToArray (object, item); + return object; +} + +cJSON * +cJSON_AddNullToObject (cJSON *object, const char *name) +{ + cJSON *obj, *tmp; + + tmp = cJSON_CreateNull (); + if (!tmp) + return NULL; + obj = cJSON_AddItemToObject(object, name, tmp); + if (!obj) + cJSON_Delete (tmp); + return obj; +} + +cJSON * +cJSON_AddTrueToObject (cJSON *object, const char *name) +{ + cJSON *obj, *tmp; + + tmp = cJSON_CreateTrue (); + if (!tmp) + return NULL; + obj = cJSON_AddItemToObject(object, name, tmp); + if (!obj) + cJSON_Delete (tmp); + return obj; +} + +cJSON * +cJSON_AddFalseToObject (cJSON *object, const char *name) +{ + cJSON *obj, *tmp; + + tmp = cJSON_CreateFalse (); + if (!tmp) + return NULL; + obj = cJSON_AddItemToObject(object, name, tmp); + if (!obj) + cJSON_Delete (tmp); + return obj; +} + +cJSON * +cJSON_AddBoolToObject (cJSON *object, const char *name, int b) +{ + cJSON *obj, *tmp; + + tmp = cJSON_CreateBool (b); + if (!tmp) + return NULL; + obj = cJSON_AddItemToObject(object, name, tmp); + if (!obj) + cJSON_Delete (tmp); + return obj; +} + +cJSON * +cJSON_AddNumberToObject (cJSON *object, const char *name, double num) +{ + cJSON *obj, *tmp; + + tmp = cJSON_CreateNumber (num); + if (!tmp) + return NULL; + obj = cJSON_AddItemToObject(object, name, tmp); + if (!obj) + cJSON_Delete (tmp); + return obj; +} + +cJSON * +cJSON_AddStringToObject (cJSON *object, const char *name, const char *string) +{ + cJSON *obj, *tmp; + + tmp = cJSON_CreateString (string); + if (!tmp) + return NULL; + obj = cJSON_AddItemToObject(object, name, tmp); + if (!obj) + cJSON_Delete (tmp); + return obj; +} + +void +cJSON_AddItemReferenceToArray (cJSON * array, cJSON * item) +{ + cJSON_AddItemToArray (array, create_reference (item)); +} + +void +cJSON_AddItemReferenceToObject (cJSON * object, const char *string, + cJSON * item) +{ + cJSON_AddItemToObject (object, string, create_reference (item)); +} + +cJSON * +cJSON_DetachItemFromArray (cJSON * array, int which) +{ + cJSON *c = array->child; + while (c && which > 0) + c = c->next, which--; + if (!c) + return 0; + if (c->prev) + c->prev->next = c->next; + if (c->next) + c->next->prev = c->prev; + if (c == array->child) + array->child = c->next; + c->prev = c->next = 0; + return c; +} + +void +cJSON_DeleteItemFromArray (cJSON * array, int which) +{ + cJSON_Delete (cJSON_DetachItemFromArray (array, which)); +} + +cJSON * +cJSON_DetachItemFromObject (cJSON * object, const char *string) +{ + int i = 0; + cJSON *c = object->child; + while (c && cJSON_strcasecmp (c->string, string)) + i++, c = c->next; + if (c) + return cJSON_DetachItemFromArray (object, i); + return 0; +} + +void +cJSON_DeleteItemFromObject (cJSON * object, const char *string) +{ + cJSON_Delete (cJSON_DetachItemFromObject (object, string)); +} + +/* Replace array/object items with new ones. */ +void +cJSON_ReplaceItemInArray (cJSON * array, int which, cJSON * newitem) +{ + cJSON *c = array->child; + while (c && which > 0) + c = c->next, which--; + if (!c) + return; + newitem->next = c->next; + newitem->prev = c->prev; + if (newitem->next) + newitem->next->prev = newitem; + if (c == array->child) + array->child = newitem; + else + newitem->prev->next = newitem; + c->next = c->prev = 0; + cJSON_Delete (c); +} + +void +cJSON_ReplaceItemInObject (cJSON * object, const char *string, + cJSON * newitem) +{ + int i = 0; + cJSON *c = object->child; + while (c && cJSON_strcasecmp (c->string, string)) + i++, c = c->next; + if (c) + { + newitem->string = xtrystrdup (string); + cJSON_ReplaceItemInArray (object, i, newitem); + } +} + +/* Create basic types: */ +cJSON * +cJSON_CreateNull (void) +{ + cJSON *item = cJSON_New_Item (); + if (item) + item->type = cJSON_NULL; + return item; +} + +cJSON * +cJSON_CreateTrue (void) +{ + cJSON *item = cJSON_New_Item (); + if (item) + item->type = cJSON_True; + return item; +} + +cJSON * +cJSON_CreateFalse (void) +{ + cJSON *item = cJSON_New_Item (); + if (item) + item->type = cJSON_False; + return item; +} + +cJSON * +cJSON_CreateBool (int b) +{ + cJSON *item = cJSON_New_Item (); + if (item) + item->type = b ? cJSON_True : cJSON_False; + return item; +} + +cJSON * +cJSON_CreateNumber (double num) +{ + cJSON *item = cJSON_New_Item (); + if (item) + { + item->type = cJSON_Number; + item->valuedouble = num; + item->valueint = (int) num; + } + return item; +} + +cJSON * +cJSON_CreateString (const char *string) +{ + cJSON *item = cJSON_New_Item (); + if (item) + { + item->type = cJSON_String; + item->valuestring = xtrystrdup (string); + } + return item; +} + +cJSON * +cJSON_CreateStringConvey (char *string) +{ + cJSON *item = cJSON_New_Item (); + if (item) + { + item->type = cJSON_String; + item->valuestring = string; + } + return item; +} + +cJSON * +cJSON_CreateArray (void) +{ + cJSON *item = cJSON_New_Item (); + if (item) + item->type = cJSON_Array; + return item; +} + +cJSON * +cJSON_CreateObject (void) +{ + cJSON *item = cJSON_New_Item (); + if (item) + item->type = cJSON_Object; + return item; +} + +/* Create Arrays: */ +cJSON * +cJSON_CreateIntArray (const int *numbers, int count) +{ + int i; + cJSON *n = 0, *p = 0, *a = cJSON_CreateArray (); + for (i = 0; a && i < count; i++) + { + n = cJSON_CreateNumber (numbers[i]); + if (!i) + a->child = n; + else + suffix_object (p, n); + p = n; + } + return a; +} + +cJSON * +cJSON_CreateFloatArray (const float *numbers, int count) +{ + int i; + cJSON *n = 0, *p = 0, *a = cJSON_CreateArray (); + for (i = 0; a && i < count; i++) + { + n = cJSON_CreateNumber (numbers[i]); + if (!i) + a->child = n; + else + suffix_object (p, n); + p = n; + } + return a; +} + +cJSON * +cJSON_CreateDoubleArray (const double *numbers, int count) +{ + int i; + cJSON *n = 0, *p = 0, *a = cJSON_CreateArray (); + for (i = 0; a && i < count; i++) + { + n = cJSON_CreateNumber (numbers[i]); + if (!i) + a->child = n; + else + suffix_object (p, n); + p = n; + } + return a; +} + +cJSON * +cJSON_CreateStringArray (const char **strings, int count) +{ + int i; + cJSON *n = 0, *p = 0, *a = cJSON_CreateArray (); + for (i = 0; a && i < count; i++) + { + n = cJSON_CreateString (strings[i]); + if (!i) + a->child = n; + else + suffix_object (p, n); + p = n; + } + return a; +} + +/* Duplication */ +cJSON * +cJSON_Duplicate (cJSON * item, int recurse) +{ + cJSON *newitem, *cptr, *nptr = 0, *newchild; + /* Bail on bad ptr */ + if (!item) + return 0; + /* Create new item */ + newitem = cJSON_New_Item (); + if (!newitem) + return 0; + /* Copy over all vars */ + newitem->type = item->type & (~cJSON_IsReference), newitem->valueint = + item->valueint, newitem->valuedouble = item->valuedouble; + if (item->valuestring) + { + newitem->valuestring = xtrystrdup (item->valuestring); + if (!newitem->valuestring) + { + cJSON_Delete (newitem); + return 0; + } + } + if (item->string) + { + newitem->string = xtrystrdup (item->string); + if (!newitem->string) + { + cJSON_Delete (newitem); + return 0; + } + } + /* If non-recursive, then we're done! */ + if (!recurse) + return newitem; + /* Walk the ->next chain for the child. */ + cptr = item->child; + while (cptr) + { + /* Duplicate (with recurse) each item in the ->next chain */ + newchild = cJSON_Duplicate (cptr, 1); + if (!newchild) + { + cJSON_Delete (newitem); + return 0; + } + if (nptr) + { + /* If newitem->child already set, + * then crosswire ->prev and ->next and move on. */ + nptr->next = newchild, newchild->prev = nptr; + nptr = newchild; + } + else + { + /* Set newitem->child and move to it. */ + newitem->child = newchild; + nptr = newchild; + } + cptr = cptr->next; + } + return newitem; +} + +void +cJSON_Minify (char *json) +{ + char *into = json; + while (*json) + { + if (*json == ' ') + json++; + else if (*json == '\t') + json++; /* Whitespace characters. */ + else if (*json == '\r') + json++; + else if (*json == '\n') + json++; + else if (*json == '/' && json[1] == '/') + while (*json && *json != '\n') + json++; /* Double-slash comments, to end of line. */ + else if (*json == '/' && json[1] == '*') + { + while (*json && !(*json == '*' && json[1] == '/')) + json++; + json += 2; + } /* Multiline comments. */ + else if (*json == '\"') + { + *into++ = *json++; + while (*json && *json != '\"') + { + if (*json == '\\') + *into++ = *json++; + *into++ = *json++; + } + *into++ = *json++; + } /* String literals, which are \" sensitive. */ + else + *into++ = *json++; /* All other characters. */ + } + *into = 0; /* and null-terminate. */ +} diff --git a/src/cJSON.h b/src/cJSON.h new file mode 100644 index 0000000..a200c31 --- /dev/null +++ b/src/cJSON.h @@ -0,0 +1,187 @@ +/* cJSON.h + * Copyright (c) 2009 Dave Gamble + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + * + * Note that this code has been modified from the original code taken + * from cjson-code-58.zip. + */ + +#ifndef cJSON_h +#define cJSON_h + +#ifdef __cplusplus +extern "C" +{ +#if 0 /*(to make Emacs auto-indent happy)*/ +} +#endif +#endif + +/* cJSON Types: */ +#define cJSON_False 0 +#define cJSON_True 1 +#define cJSON_NULL 2 +#define cJSON_Number 3 +#define cJSON_String 4 +#define cJSON_Array 5 +#define cJSON_Object 6 + +#define cJSON_IsReference 256 + +/* The cJSON structure: */ +typedef struct cJSON +{ + /* next/prev allow you to walk array/object chains. Alternatively, + use GetArraySize/GetArrayItem/GetObjectItem */ + struct cJSON *next, *prev; + + /* An array or object item will have a child pointer pointing to a + chain of the items in the array/object. */ + struct cJSON *child; + + int type; /* The type of the item, as above. */ + + char *valuestring; /* The item's string, if type==cJSON_String */ + int valueint; /* The item's number, if type==cJSON_Number */ + double valuedouble; /* The item's number, if type==cJSON_Number */ + + /* The item's name string, if this item is the child of, or is in + the list of subitems of an object. */ + char *string; +} cJSON; + +typedef struct cJSON *cjson_t; + +/* Macros to test the type of an object. */ +#define cjson_is_boolean(a) (!((a)->type & ~1)) +#define cjson_is_false(a) ((a)->type == cJSON_False) +#define cjson_is_true(a) ((a)->type == cJSON_True) +#define cjson_is_null(a) ((a)->type == cJSON_NULL) +#define cjson_is_number(a) ((a)->type == cJSON_Number) +#define cjson_is_string(a) ((a)->type == cJSON_String) +#define cjson_is_array(a) ((a)->type == cJSON_Array) +#define cjson_is_object(a) ((a)->type == cJSON_Object) + +/* Supply a block of JSON, and this returns a cJSON object you can + interrogate. Call cJSON_Delete when finished. */ +extern cJSON *cJSON_Parse(const char *value, size_t *r_erroff); + +/* Render a cJSON entity to text for transfer/storage. Free the char* + when finished. */ +extern char *cJSON_Print(cJSON *item); + +/* Render a cJSON entity to text for transfer/storage without any + formatting. Free the char* when finished. */ +extern char *cJSON_PrintUnformatted(cJSON *item); + +/* Delete a cJSON entity and all subentities. */ +extern void cJSON_Delete(cJSON *c); + +/* Returns the number of items in an array (or object). */ +extern int cJSON_GetArraySize(cJSON *array); + +/* Retrieve item number "item" from array "array". Returns NULL if + unsuccessful. */ +extern cJSON *cJSON_GetArrayItem(cJSON *array,int item); + +/* Get item "string" from object. Case insensitive. */ +extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string); + +/* These calls create a cJSON item of the appropriate type. */ +extern cJSON *cJSON_CreateNull(void); +extern cJSON *cJSON_CreateTrue(void); +extern cJSON *cJSON_CreateFalse(void); +extern cJSON *cJSON_CreateBool(int b); +extern cJSON *cJSON_CreateNumber(double num); +extern cJSON *cJSON_CreateString(const char *string); +extern cJSON *cJSON_CreateStringConvey (char *string); +extern cJSON *cJSON_CreateArray(void); +extern cJSON *cJSON_CreateObject(void); + +/* These utilities create an Array of count items. */ +extern cJSON *cJSON_CreateIntArray(const int *numbers,int count); +extern cJSON *cJSON_CreateFloatArray(const float *numbers,int count); +extern cJSON *cJSON_CreateDoubleArray(const double *numbers,int count); +extern cJSON *cJSON_CreateStringArray(const char **strings,int count); + +/* Append item to the specified array. */ +extern void cJSON_AddItemToArray(cJSON *array, cJSON *item); + +/* Append item to the specified object. */ +extern cJSON *cJSON_AddItemToObject(cJSON *object, const char *name, + cJSON *item); +extern cJSON *cJSON_AddNullToObject (cJSON *object, const char *name); +extern cJSON *cJSON_AddTrueToObject (cJSON *object, const char *name); +extern cJSON *cJSON_AddFalseToObject (cJSON *object, const char *name); +extern cJSON *cJSON_AddBoolToObject (cJSON *object, const char *name, int b); +extern cJSON *cJSON_AddNumberToObject (cJSON *object, const char *name, + double num); +extern cJSON *cJSON_AddStringToObject (cJSON *object, const char *name, + const char *string); + +/* Append reference to item to the specified array/object. Use this + when you want to add an existing cJSON to a new cJSON, but don't + want to corrupt your existing cJSON. */ +extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); +extern void cJSON_AddItemReferenceToObject(cJSON *object, + const char *string,cJSON *item); + +/* Remove/Detatch items from Arrays/Objects. */ +extern cJSON *cJSON_DetachItemFromArray(cJSON *array,int which); +extern void cJSON_DeleteItemFromArray(cJSON *array,int which); +extern cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string); +extern void cJSON_DeleteItemFromObject(cJSON *object,const char *string); + +/* Update array items. */ +extern void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem); +extern void cJSON_ReplaceItemInObject(cJSON *object, + const char *string, cJSON *newitem); + +/* Duplicate a cJSON item */ +extern cJSON *cJSON_Duplicate(cJSON *item,int recurse); + +/* Duplicate will create a new, identical cJSON item to the one you + pass, in new memory that will need to be released. With recurse!=0, + it will duplicate any children connected to the item. The + item->next and ->prev pointers are always zero on return from + Duplicate. */ + +/* ParseWithOpts allows you to require (and check) that the JSON is + null terminated, and to retrieve the pointer to the final byte + parsed. */ +extern cJSON *cJSON_ParseWithOpts(const char *value, + const char **return_parse_end, + int require_null_terminated, + size_t *r_erroff); + +extern void cJSON_Minify(char *json); + +/* When assigning an integer value, it needs to be propagated to + valuedouble too. */ +#define cJSON_SetIntValue(object,val) \ + ((object)?(object)->valueint=(object)->valuedouble=(val):(val)) + +#ifdef __cplusplus +} +#endif + +#endif /* cJSON_h */ diff --git a/src/context.h b/src/context.h index 1e763d2..c8e75ba 100644 --- a/src/context.h +++ b/src/context.h @@ -121,6 +121,9 @@ struct gpgme_context /* True if the option --auto-key-retrieve shall be passed to gpg. */ unsigned int auto_key_retrieve : 1; + /* Do not use the symmtric encryption passphrase cache. */ + unsigned int no_symkey_cache : 1; + /* Flags for keylist mode. */ gpgme_keylist_mode_t keylist_mode; @@ -145,6 +148,9 @@ struct gpgme_context /* The gpg specific override session key or NULL. */ char *override_session_key; + /* The optional request origin. */ + char *request_origin; + /* The locale for the pinentry. */ char *lc_ctype; char *lc_messages; diff --git a/src/conversion.c b/src/conversion.c index 5b84f67..4bfd3d3 100644 --- a/src/conversion.c +++ b/src/conversion.c @@ -575,3 +575,49 @@ _gpgme_map_pk_algo (int algo, gpgme_protocol_t protocol) return algo; } + + +/* Return a string with a cipher algorithm. */ +const char * +_gpgme_cipher_algo_name (int algo, gpgme_protocol_t protocol) +{ + if (protocol == GPGME_PROTOCOL_OPENPGP) + { + /* The algo is given according to OpenPGP specs. */ + switch (algo) + { + case 1: return "IDEA"; + case 2: return "3DES"; + case 3: return "CAST5"; + case 4: return "BLOWFISH"; + case 7: return "AES"; + case 8: return "AES192"; + case 9: return "AES256"; + case 10: return "TWOFISH"; + case 11: return "CAMELLIA128"; + case 12: return "CAMELLIA192"; + case 13: return "CAMELLIA256"; + } + } + + return "Unknown"; +} + + +/* Return a string with the cipher mode. */ +const char * +_gpgme_cipher_mode_name (int algo, gpgme_protocol_t protocol) +{ + if (protocol == GPGME_PROTOCOL_OPENPGP) + { + /* The algo is given according to OpenPGP specs. */ + switch (algo) + { + case 0: return "CFB"; + case 1: return "EAX"; + case 2: return "OCB"; + } + } + + return "Unknown"; +} diff --git a/src/decrypt.c b/src/decrypt.c index 8c2cd4d..155e18e 100644 --- a/src/decrypt.c +++ b/src/decrypt.c @@ -69,14 +69,10 @@ release_op_data (void *hook) op_data_t opd = (op_data_t) hook; gpgme_recipient_t recipient = opd->result.recipients; - if (opd->result.unsupported_algorithm) - free (opd->result.unsupported_algorithm); - - if (opd->result.file_name) - free (opd->result.file_name); - - if (opd->result.session_key) - free (opd->result.session_key); + free (opd->result.unsupported_algorithm); + free (opd->result.file_name); + free (opd->result.session_key); + free (opd->result.symkey_algo); while (recipient) { @@ -104,6 +100,17 @@ gpgme_op_decrypt_result (gpgme_ctx_t ctx) return NULL; } + /* Make sure that SYMKEY_ALGO has a value. */ + if (!opd->result.symkey_algo) + { + opd->result.symkey_algo = strdup ("?.?"); + if (!opd->result.symkey_algo) + { + TRACE_SUC0 ("result=(null)"); + return NULL; + } + } + if (_gpgme_debug_trace ()) { gpgme_recipient_t rcp; @@ -263,6 +270,49 @@ parse_enc_to (char *args, gpgme_recipient_t *recp, gpgme_protocol_t protocol) } +/* Parse the ARGS of a + * DECRYPTION_INFO <mdc_method> <sym_algo> [<aead_algo>] + * status. Returns 0 on success and updates the OPD. + */ +static gpgme_error_t +parse_decryption_info (char *args, op_data_t opd, gpgme_protocol_t protocol) +{ + char *field[3]; + int nfields; + char *args2; + int mdc, mode; + const char *algostr, *modestr; + + if (!args) + return trace_gpg_error (GPG_ERR_INV_ENGINE); + + args2 = strdup (args); /* Split modifies the input string. */ + nfields = _gpgme_split_fields (args2, field, DIM (field)); + if (nfields < 2) + { + free (args2); + return trace_gpg_error (GPG_ERR_INV_ENGINE); /* Required arg missing. */ + } + + mdc = atoi (field[0]); + algostr = _gpgme_cipher_algo_name (atoi (field[1]), protocol); + mode = nfields < 3? 0 : atoi (field[2]); + modestr = _gpgme_cipher_mode_name (mode, protocol); + + free (args2); + + free (opd->result.symkey_algo); + if (!mode && mdc != 2) + opd->result.symkey_algo = _gpgme_strconcat (algostr, ".PGPCFB", NULL); + else + opd->result.symkey_algo = _gpgme_strconcat (algostr, ".", modestr, NULL); + if (!opd->result.symkey_algo) + return gpg_error_from_syserror (); + + return 0; +} + + gpgme_error_t _gpgme_decrypt_status_handler (void *priv, gpgme_status_code_t code, char *args) @@ -303,7 +353,9 @@ _gpgme_decrypt_status_handler (void *priv, gpgme_status_code_t code, break; case GPGME_STATUS_DECRYPTION_INFO: - /* Fixme: Provide a way to return the used symmetric algorithm. */ + err = parse_decryption_info (args, opd, ctx->protocol); + if (err) + return err; break; case GPGME_STATUS_DECRYPTION_OKAY: @@ -357,9 +409,14 @@ _gpgme_decrypt_status_handler (void *priv, gpgme_status_code_t code, break; case GPGME_STATUS_PLAINTEXT: - err = _gpgme_parse_plaintext (args, &opd->result.file_name); - if (err) - return err; + { + int mime = 0; + err = _gpgme_parse_plaintext (args, &opd->result.file_name, &mime); + if (err) + return err; + gpgrt_log_debug ("decrypt.c setting mime to %d\n", mime); + opd->result.is_mime = !!mime; + } break; case GPGME_STATUS_INQUIRE_MAXLEN: diff --git a/src/encrypt-sign.c b/src/encrypt-sign.c index af6de63..4db46e2 100644 --- a/src/encrypt-sign.c +++ b/src/encrypt-sign.c @@ -62,6 +62,7 @@ encrypt_sym_status_handler (void *priv, gpgme_status_code_t code, char *args) static gpgme_error_t encrypt_sign_start (gpgme_ctx_t ctx, int synchronous, gpgme_key_t recp[], + const char *recpstring, gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t cipher) { @@ -72,7 +73,7 @@ encrypt_sign_start (gpgme_ctx_t ctx, int synchronous, gpgme_key_t recp[], if (err) return err; - symmetric = !recp || (flags & GPGME_ENCRYPT_SYMMETRIC); + symmetric = (!recp && !recpstring) || (flags & GPGME_ENCRYPT_SYMMETRIC); if (!plain) return gpg_error (GPG_ERR_NO_DATA); @@ -103,43 +104,75 @@ encrypt_sign_start (gpgme_ctx_t ctx, int synchronous, gpgme_key_t recp[], : encrypt_sign_status_handler, ctx); - return _gpgme_engine_op_encrypt_sign (ctx->engine, recp, flags, plain, + return _gpgme_engine_op_encrypt_sign (ctx->engine, recp, recpstring, + flags, plain, cipher, ctx->use_armor, ctx /* FIXME */); } -/* Encrypt plaintext PLAIN within CTX for the recipients RECP and - store the resulting ciphertext in CIPHER. Also sign the ciphertext - with the signers in CTX. */ +/* Old version of gpgme_op_encrypt_sign_ext_start w/o RECPSTRING. */ gpgme_error_t gpgme_op_encrypt_sign_start (gpgme_ctx_t ctx, gpgme_key_t recp[], gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t cipher) { + return gpgme_op_encrypt_sign_ext_start (ctx, recp, NULL, + flags, plain, cipher); +} + + +/* Old version of gpgme_op_encrypt_sign_ext w/o RECPSTRING. */ +gpgme_error_t +gpgme_op_encrypt_sign (gpgme_ctx_t ctx, gpgme_key_t recp[], + gpgme_encrypt_flags_t flags, + gpgme_data_t plain, gpgme_data_t cipher) +{ + return gpgme_op_encrypt_sign_ext (ctx, recp, NULL, flags, plain, cipher); +} + + +/* Encrypt plaintext PLAIN within CTX for the recipients RECP and + * store the resulting ciphertext in CIPHER. Also sign the ciphertext + * with the signers in CTX. */ +gpgme_error_t +gpgme_op_encrypt_sign_ext (gpgme_ctx_t ctx, gpgme_key_t recp[], + const char *recpstring, + gpgme_encrypt_flags_t flags, + gpgme_data_t plain, gpgme_data_t cipher) +{ gpgme_error_t err; - TRACE_BEG3 (DEBUG_CTX, "gpgme_op_encrypt_sign_start", ctx, + TRACE_BEG3 (DEBUG_CTX, "gpgme_op_encrypt_sign", ctx, "flags=0x%x, plain=%p, cipher=%p", flags, plain, cipher); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); - if (_gpgme_debug_trace () && recp) + if (_gpgme_debug_trace () && (recp || recpstring)) { - int i = 0; - - while (recp[i]) - { - TRACE_LOG3 ("recipient[%i] = %p (%s)", i, recp[i], - (recp[i]->subkeys && recp[i]->subkeys->fpr) ? - recp[i]->subkeys->fpr : "invalid"); - i++; - } + if (recp) + { + int i = 0; + + while (recp[i]) + { + TRACE_LOG3 ("recipient[%i] = %p (%s)", i, recp[i], + (recp[i]->subkeys && recp[i]->subkeys->fpr) ? + recp[i]->subkeys->fpr : "invalid"); + i++; + } + } + else + { + TRACE_LOG1 ("recipients = '%s'", recpstring); + } } - err = encrypt_sign_start (ctx, 0, recp, flags, plain, cipher); - return err; + err = encrypt_sign_start (ctx, 1, recp, recpstring, flags, plain, cipher); + if (!err) + err = _gpgme_wait_one (ctx); + return TRACE_ERR (err); } @@ -147,33 +180,39 @@ gpgme_op_encrypt_sign_start (gpgme_ctx_t ctx, gpgme_key_t recp[], store the resulting ciphertext in CIPHER. Also sign the ciphertext with the signers in CTX. */ gpgme_error_t -gpgme_op_encrypt_sign (gpgme_ctx_t ctx, gpgme_key_t recp[], - gpgme_encrypt_flags_t flags, - gpgme_data_t plain, gpgme_data_t cipher) +gpgme_op_encrypt_sign_ext_start (gpgme_ctx_t ctx, gpgme_key_t recp[], + const char *recpstring, + gpgme_encrypt_flags_t flags, + gpgme_data_t plain, gpgme_data_t cipher) { gpgme_error_t err; - TRACE_BEG3 (DEBUG_CTX, "gpgme_op_encrypt_sign", ctx, + TRACE_BEG3 (DEBUG_CTX, "gpgme_op_encrypt_sign_start", ctx, "flags=0x%x, plain=%p, cipher=%p", flags, plain, cipher); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); - if (_gpgme_debug_trace () && recp) + if (_gpgme_debug_trace () && (recp || recpstring)) { - int i = 0; - - while (recp[i]) - { - TRACE_LOG3 ("recipient[%i] = %p (%s)", i, recp[i], - (recp[i]->subkeys && recp[i]->subkeys->fpr) ? - recp[i]->subkeys->fpr : "invalid"); - i++; - } + if (recp) + { + int i = 0; + + while (recp[i]) + { + TRACE_LOG3 ("recipient[%i] = %p (%s)", i, recp[i], + (recp[i]->subkeys && recp[i]->subkeys->fpr) ? + recp[i]->subkeys->fpr : "invalid"); + i++; + } + } + else + { + TRACE_LOG1 ("recipients = '%s'", recpstring); + } } - err = encrypt_sign_start (ctx, 1, recp, flags, plain, cipher); - if (!err) - err = _gpgme_wait_one (ctx); - return TRACE_ERR (err); + err = encrypt_sign_start (ctx, 0, recp, recpstring, flags, plain, cipher); + return err; } diff --git a/src/encrypt.c b/src/encrypt.c index 4023654..2318497 100644 --- a/src/encrypt.c +++ b/src/encrypt.c @@ -214,6 +214,7 @@ _gpgme_op_encrypt_init_result (gpgme_ctx_t ctx) static gpgme_error_t encrypt_start (gpgme_ctx_t ctx, int synchronous, gpgme_key_t recp[], + const char *recpstring, gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t cipher) { @@ -228,13 +229,13 @@ encrypt_start (gpgme_ctx_t ctx, int synchronous, gpgme_key_t recp[], if (err) return err; - symmetric = !recp || (flags & GPGME_ENCRYPT_SYMMETRIC); + symmetric = (!recp && !recpstring) || (flags & GPGME_ENCRYPT_SYMMETRIC); if (!plain) return gpg_error (GPG_ERR_NO_DATA); if (!cipher) return gpg_error (GPG_ERR_INV_VALUE); - if (recp && ! *recp) + if (recp && !*recp) return gpg_error (GPG_ERR_INV_VALUE); if (symmetric && ctx->passphrase_cb) @@ -252,72 +253,111 @@ encrypt_start (gpgme_ctx_t ctx, int synchronous, gpgme_key_t recp[], : encrypt_status_handler, ctx); - return _gpgme_engine_op_encrypt (ctx->engine, recp, flags, plain, cipher, - ctx->use_armor); + return _gpgme_engine_op_encrypt (ctx->engine, recp, recpstring, + flags, plain, cipher, ctx->use_armor); } +/* Old version of gpgme_op_encrypt_ext without RECPSTRING. */ +gpgme_error_t +gpgme_op_encrypt (gpgme_ctx_t ctx, gpgme_key_t recp[], + gpgme_encrypt_flags_t flags, + gpgme_data_t plain, gpgme_data_t cipher) +{ + return gpgme_op_encrypt_ext (ctx, recp, NULL, flags, plain, cipher); +} + + +/* Old version of gpgme_op_encrypt_ext_start without RECPSTRING. */ gpgme_error_t gpgme_op_encrypt_start (gpgme_ctx_t ctx, gpgme_key_t recp[], gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t cipher) { + return gpgme_op_encrypt_ext_start (ctx, recp, NULL, flags, plain, cipher); +} + + +/* Encrypt plaintext PLAIN within CTX for the recipients RECP and + * store the resulting ciphertext in CIPHER. RECPSTRING can be used + * instead of the RECP array to directly specify recipients as LF + * delimited strings; these may be any kind of recipient specification + * patterns as supported by the backend. */ +gpgme_error_t +gpgme_op_encrypt_ext (gpgme_ctx_t ctx, gpgme_key_t recp[], + const char *recpstring, + gpgme_encrypt_flags_t flags, + gpgme_data_t plain, gpgme_data_t cipher) +{ gpgme_error_t err; - TRACE_BEG3 (DEBUG_CTX, "gpgme_op_encrypt_start", ctx, + TRACE_BEG3 (DEBUG_CTX, "gpgme_op_encrypt", ctx, "flags=0x%x, plain=%p, cipher=%p", flags, plain, cipher); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); - if (_gpgme_debug_trace () && recp) + if (_gpgme_debug_trace () && (recp || recpstring)) { - int i = 0; + if (recp) + { + int i = 0; - while (recp[i]) - { - TRACE_LOG3 ("recipient[%i] = %p (%s)", i, recp[i], + while (recp[i]) + { + TRACE_LOG3 ("recipient[%i] = %p (%s)", i, recp[i], (recp[i]->subkeys && recp[i]->subkeys->fpr) ? - recp[i]->subkeys->fpr : "invalid"); - i++; - } + recp[i]->subkeys->fpr : "invalid"); + i++; + } + } + else + { + TRACE_LOG1 ("recipients = '%s'", recpstring); + } } - err = encrypt_start (ctx, 0, recp, flags, plain, cipher); + err = encrypt_start (ctx, 1, recp, recpstring, flags, plain, cipher); + if (!err) + err = _gpgme_wait_one (ctx); return TRACE_ERR (err); } -/* Encrypt plaintext PLAIN within CTX for the recipients RECP and - store the resulting ciphertext in CIPHER. */ gpgme_error_t -gpgme_op_encrypt (gpgme_ctx_t ctx, gpgme_key_t recp[], - gpgme_encrypt_flags_t flags, - gpgme_data_t plain, gpgme_data_t cipher) +gpgme_op_encrypt_ext_start (gpgme_ctx_t ctx, gpgme_key_t recp[], + const char *recpstring, + gpgme_encrypt_flags_t flags, + gpgme_data_t plain, gpgme_data_t cipher) { gpgme_error_t err; - TRACE_BEG3 (DEBUG_CTX, "gpgme_op_encrypt", ctx, + TRACE_BEG3 (DEBUG_CTX, "gpgme_op_encrypt_start", ctx, "flags=0x%x, plain=%p, cipher=%p", flags, plain, cipher); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); - if (_gpgme_debug_trace () && recp) + if (_gpgme_debug_trace () && (recp || recpstring)) { - int i = 0; - - while (recp[i]) - { - TRACE_LOG3 ("recipient[%i] = %p (%s)", i, recp[i], - (recp[i]->subkeys && recp[i]->subkeys->fpr) ? - recp[i]->subkeys->fpr : "invalid"); - i++; - } + if (recp) + { + int i = 0; + + while (recp[i]) + { + TRACE_LOG3 ("recipient[%i] = %p (%s)", i, recp[i], + (recp[i]->subkeys && recp[i]->subkeys->fpr) ? + recp[i]->subkeys->fpr : "invalid"); + i++; + } + } + else + { + TRACE_LOG1 ("recipients = '%s'", recpstring); + } } - err = encrypt_start (ctx, 1, recp, flags, plain, cipher); - if (!err) - err = _gpgme_wait_one (ctx); + err = encrypt_start (ctx, 0, recp, recpstring, flags, plain, cipher); return TRACE_ERR (err); } diff --git a/src/engine-assuan.c b/src/engine-assuan.c index bb2290a..6e603d9 100644 --- a/src/engine-assuan.c +++ b/src/engine-assuan.c @@ -96,6 +96,7 @@ struct engine_llass int gpg_agent:1; /* Assume this is a gpg-agent connection. */ } opt; + char request_origin[10]; /* Copy from the CTX. */ }; typedef struct engine_llass *engine_llass_t; @@ -365,6 +366,24 @@ llass_new (void **engine, const char *file_name, const char *home_dir, } +/* Copy flags from CTX into the engine object. */ +static void +llass_set_engine_flags (void *engine, const gpgme_ctx_t ctx) +{ + engine_llass_t llass = engine; + + if (ctx->request_origin) + { + if (strlen (ctx->request_origin) + 1 > sizeof llass->request_origin) + strcpy (llass->request_origin, "xxx"); /* Too long - force error */ + else + strcpy (llass->request_origin, ctx->request_origin); + } + else + *llass->request_origin = 0; +} + + static gpgme_error_t llass_set_locale (void *engine, int category, const char *value) { @@ -660,6 +679,21 @@ start (engine_llass_t llass, const char *command) int nfds; int i; + if (*llass->request_origin && llass->opt.gpg_agent) + { + char *cmd; + + cmd = _gpgme_strconcat ("OPTION pretend-request-origin=", + llass->request_origin, NULL); + if (!cmd) + return gpg_error_from_syserror (); + err = assuan_transact (llass->assuan_ctx, cmd, NULL, NULL, NULL, + NULL, NULL, NULL); + free (cmd); + if (err && gpg_err_code (err) != GPG_ERR_UNKNOWN_OPTION) + return err; + } + /* 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. */ @@ -775,6 +809,7 @@ struct engine_ops _gpgme_engine_ops_assuan = NULL, /* set_colon_line_handler */ llass_set_locale, NULL, /* set_protocol */ + llass_set_engine_flags, NULL, /* decrypt */ NULL, /* delete */ NULL, /* edit */ diff --git a/src/engine-backend.h b/src/engine-backend.h index 421eb16..f692666 100644 --- a/src/engine-backend.h +++ b/src/engine-backend.h @@ -61,6 +61,7 @@ struct engine_ops 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); + void (*set_engine_flags) (void *engine, gpgme_ctx_t ctx); gpgme_error_t (*decrypt) (void *engine, gpgme_decrypt_flags_t flags, gpgme_data_t ciph, @@ -71,10 +72,12 @@ struct engine_ops 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[], + const char *recpstring, 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[], + const char *recpstring, gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t ciph, int use_armor, gpgme_ctx_t ctx /* FIXME */); diff --git a/src/engine-g13.c b/src/engine-g13.c index f8f3178..ec2f7af 100644 --- a/src/engine-g13.c +++ b/src/engine-g13.c @@ -790,6 +790,7 @@ struct engine_ops _gpgme_engine_ops_g13 = NULL, /* set_colon_line_handler */ g13_set_locale, NULL, /* set_protocol */ + NULL, /* set_engine_flags */ NULL, /* decrypt */ NULL, /* delete */ NULL, /* edit */ diff --git a/src/engine-gpg.c b/src/engine-gpg.c index bfe7d13..173e940 100644 --- a/src/engine-gpg.c +++ b/src/engine-gpg.c @@ -43,6 +43,7 @@ #include "sema.h" #include "debug.h" #include "data.h" +#include "mbox-util.h" #include "engine-backend.h" @@ -143,6 +144,12 @@ struct engine_gpg struct gpgme_io_cbs io_cbs; gpgme_pinentry_mode_t pinentry_mode; + char request_origin[10]; + + struct { + unsigned int no_symkey_cache : 1; + unsigned int offline : 1; + } flags; /* NULL or the data object fed to --override_session_key-fd. */ gpgme_data_t override_session_key; @@ -628,6 +635,30 @@ gpg_new (void **engine, const char *file_name, const char *home_dir, } +/* Copy flags from CTX into the engine object. */ +static void +gpg_set_engine_flags (void *engine, const gpgme_ctx_t ctx) +{ + engine_gpg_t gpg = engine; + + if (ctx->request_origin && have_gpg_version (gpg, "2.2.6")) + { + if (strlen (ctx->request_origin) + 1 > sizeof gpg->request_origin) + strcpy (gpg->request_origin, "xxx"); /* Too long - force error */ + else + strcpy (gpg->request_origin, ctx->request_origin); + } + else + *gpg->request_origin = 0; + + gpg->flags.no_symkey_cache = (ctx->no_symkey_cache + && have_gpg_version (gpg, "2.2.7")); + + gpg->flags.offline = (ctx->offline && have_gpg_version (gpg, "2.1.23")); + +} + + static gpgme_error_t gpg_set_locale (void *engine, int category, const char *value) { @@ -856,7 +887,8 @@ build_argv (engine_gpg_t gpg, const char *pgmname) argc++; if (!gpg->cmd.used) argc++; /* --batch */ - argc += 1; /* --no-sk-comments */ + argc += 4; /* --no-sk-comments, --request-origin, --no-symkey-cache */ + /* --disable-dirmngr */ argv = calloc (argc + 1, sizeof *argv); if (!argv) @@ -904,6 +936,46 @@ build_argv (engine_gpg_t gpg, const char *pgmname) argc++; } + if (*gpg->request_origin) + { + argv[argc] = _gpgme_strconcat ("--request-origin=", + gpg->request_origin, NULL); + if (!argv[argc]) + { + int saved_err = gpg_error_from_syserror (); + free (fd_data_map); + free_argv (argv); + return saved_err; + } + argc++; + } + + if (gpg->flags.no_symkey_cache) + { + argv[argc] = strdup ("--no-symkey-cache"); + if (!argv[argc]) + { + int saved_err = gpg_error_from_syserror (); + free (fd_data_map); + free_argv (argv); + return saved_err; + } + argc++; + } + + if (gpg->flags.offline) + { + argv[argc] = strdup ("--disable-dirmngr"); + 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 && have_gpg_version (gpg, "2.1.0")) { const char *s = NULL; @@ -1838,8 +1910,70 @@ gpg_edit (void *engine, int type, gpgme_key_t key, gpgme_data_t out, } +/* Add a single argument from a key to an -r option. */ +static gpg_error_t +add_arg_recipient (engine_gpg_t gpg, gpgme_encrypt_flags_t flags, + gpgme_key_t key) +{ + gpg_error_t err; + + if ((flags & GPGME_ENCRYPT_WANT_ADDRESS)) + { + /* We have no way to figure out which mail address was + * requested. FIXME: It would be possible to figure this out by + * consulting the SENDER property of the context. */ + err = gpg_error (GPG_ERR_INV_USER_ID); + } + else + err = add_arg (gpg, key->subkeys->fpr); + + return err; +} + + +/* Add a single argument from a USERID string to an -r option. */ +static gpg_error_t +add_arg_recipient_string (engine_gpg_t gpg, gpgme_encrypt_flags_t flags, + const char *userid, int useridlen) +{ + gpg_error_t err; + + if ((flags & GPGME_ENCRYPT_WANT_ADDRESS)) + { + char *tmpstr, *mbox; + + tmpstr = malloc (useridlen + 1); + if (!tmpstr) + err = gpg_error_from_syserror (); + else + { + memcpy (tmpstr, userid, useridlen); + tmpstr[useridlen] = 0; + + mbox = _gpgme_mailbox_from_userid (tmpstr); + if (!mbox) + { + err = gpg_error_from_syserror (); + if (gpg_err_code (err) == GPG_ERR_EINVAL) + err = gpg_error (GPG_ERR_INV_USER_ID); + } + else + err = add_arg (gpg, mbox); + + free (mbox); + free (tmpstr); + } + } + else + err = add_arg_len (gpg, NULL, userid, useridlen); + + return err; +} + + static gpgme_error_t -append_args_from_recipients (engine_gpg_t gpg, gpgme_key_t recp[]) +append_args_from_recipients (engine_gpg_t gpg, gpgme_encrypt_flags_t flags, + gpgme_key_t recp[]) { gpgme_error_t err = 0; int i = 0; @@ -1851,7 +1985,7 @@ append_args_from_recipients (engine_gpg_t gpg, gpgme_key_t recp[]) if (!err) err = add_arg (gpg, "-r"); if (!err) - err = add_arg (gpg, recp[i]->subkeys->fpr); + err = add_arg_recipient (gpg, flags, recp[i]); if (err) break; i++; @@ -1860,17 +1994,86 @@ append_args_from_recipients (engine_gpg_t gpg, gpgme_key_t recp[]) } +/* Take recipients from the LF delimited STRING and add -r args. */ +static gpg_error_t +append_args_from_recipients_string (engine_gpg_t gpg, + gpgme_encrypt_flags_t flags, + const char *string) +{ + gpg_error_t err = 0; + gpgme_encrypt_flags_t orig_flags = flags; + int any = 0; + int ignore = 0; + int hidden = 0; + int file = 0; + const char *s; + int n; + + do + { + /* Skip leading white space */ + while (*string == ' ' || *string == '\t') + string++; + if (!*string) + break; + + /* Look for the LF. */ + s = strchr (string, '\n'); + if (s) + n = s - string; + else + n = strlen (string); + while (n && (string[n-1] == ' ' || string[n-1] == '\t')) + n--; + + if (!ignore && n == 2 && !memcmp (string, "--", 2)) + ignore = 1; + else if (!ignore && n == 8 && !memcmp (string, "--hidden", 8)) + hidden = 1; + else if (!ignore && n == 11 && !memcmp (string, "--no-hidden", 11)) + hidden = 0; + else if (!ignore && n == 6 && !memcmp (string, "--file", 6)) + { + file = 1; + /* Because the key is used as is we need to ignore this flag: */ + flags &= ~GPGME_ENCRYPT_WANT_ADDRESS; + } + else if (!ignore && n == 9 && !memcmp (string, "--no-file", 9)) + { + file = 0; + flags = orig_flags; + } + else if (n) /* Not empty - use it. */ + { + err = add_arg (gpg, file? (hidden? "-F":"-f") : (hidden? "-R":"-r")); + if (!err) + err = add_arg_recipient_string (gpg, flags, string, n); + if (!err) + any = 1; + } + + string += n + !!s; + } + while (!err); + + if (!err && !any) + err = gpg_error (GPG_ERR_MISSING_KEY); + return err; +} + + static gpgme_error_t -gpg_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags, +gpg_encrypt (void *engine, gpgme_key_t recp[], const char *recpstring, + 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 = 0; - if (recp) + if (recp || recpstring) err = add_arg (gpg, "--encrypt"); - if (!err && ((flags & GPGME_ENCRYPT_SYMMETRIC) || !recp)) + if (!err && ((flags & GPGME_ENCRYPT_SYMMETRIC) || (!recp && !recpstring))) err = add_arg (gpg, "--symmetric"); if (!err && use_armor) @@ -1897,7 +2100,7 @@ gpg_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags, && have_gpg_version (gpg, "2.1.14")) err = add_arg (gpg, "--mimemode"); - if (recp) + if (recp || recpstring) { /* If we know that all recipients are valid (full or ultimate trust) we can suppress further checks. */ @@ -1907,8 +2110,10 @@ gpg_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags, 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 && !recp && recpstring) + err = append_args_from_recipients_string (gpg, flags, recpstring); + else if (!err) + err = append_args_from_recipients (gpg, flags, recp); } /* Tell the gpg object about the data. */ @@ -1941,6 +2146,7 @@ gpg_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags, static gpgme_error_t gpg_encrypt_sign (void *engine, gpgme_key_t recp[], + const char *recpstring, gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t ciph, int use_armor, gpgme_ctx_t ctx /* FIXME */) @@ -1948,10 +2154,10 @@ gpg_encrypt_sign (void *engine, gpgme_key_t recp[], engine_gpg_t gpg = engine; gpgme_error_t err = 0; - if (recp) + if (recp || recpstring) err = add_arg (gpg, "--encrypt"); - if (!err && ((flags & GPGME_ENCRYPT_SYMMETRIC) || !recp)) + if (!err && ((flags & GPGME_ENCRYPT_SYMMETRIC) || (!recp && !recpstring))) err = add_arg (gpg, "--symmetric"); if (!err) @@ -1969,7 +2175,7 @@ gpg_encrypt_sign (void *engine, gpgme_key_t recp[], && have_gpg_version (gpg, "2.1.14")) err = add_arg (gpg, "--mimemode"); - if (recp) + if (recp || recpstring) { /* If we know that all recipients are valid (full or ultimate trust) we can suppress further checks. */ @@ -1979,8 +2185,10 @@ gpg_encrypt_sign (void *engine, gpgme_key_t recp[], 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 && !recp && recpstring) + err = append_args_from_recipients_string (gpg, flags, recpstring); + else if (!err) + err = append_args_from_recipients (gpg, flags, recp); } if (!err) @@ -3090,6 +3298,7 @@ struct engine_ops _gpgme_engine_ops_gpg = gpg_set_colon_line_handler, gpg_set_locale, NULL, /* set_protocol */ + gpg_set_engine_flags, /* set_engine_flags */ gpg_decrypt, gpg_delete, gpg_edit, diff --git a/src/engine-gpgconf.c b/src/engine-gpgconf.c index 94ae67f..24867c7 100644 --- a/src/engine-gpgconf.c +++ b/src/engine-gpgconf.c @@ -1287,6 +1287,7 @@ struct engine_ops _gpgme_engine_ops_gpgconf = NULL, /* set_colon_line_handler */ NULL, /* set_locale */ NULL, /* set_protocol */ + NULL, /* set_engine_flags */ NULL, /* decrypt */ NULL, /* delete */ NULL, /* edit */ diff --git a/src/engine-gpgsm.c b/src/engine-gpgsm.c index e337fed..da7e524 100644 --- a/src/engine-gpgsm.c +++ b/src/engine-gpgsm.c @@ -107,6 +107,8 @@ struct engine_gpgsm gpgme_data_t inline_data; /* Used to collect D lines. */ + char request_origin[10]; + struct gpgme_io_cbs io_cbs; }; @@ -521,6 +523,24 @@ gpgsm_new (void **engine, const char *file_name, const char *home_dir, } +/* Copy flags from CTX into the engine object. */ +static void +gpgsm_set_engine_flags (void *engine, const gpgme_ctx_t ctx) +{ + engine_gpgsm_t gpgsm = engine; + + if (ctx->request_origin) + { + if (strlen (ctx->request_origin) + 1 > sizeof gpgsm->request_origin) + strcpy (gpgsm->request_origin, "xxx"); /* Too long - force error */ + else + strcpy (gpgsm->request_origin, ctx->request_origin); + } + else + *gpgsm->request_origin = 0; +} + + static gpgme_error_t gpgsm_set_locale (void *engine, int category, const char *value) { @@ -1058,6 +1078,20 @@ start (engine_gpgsm_t gpgsm, const char *command) int nfds; int i; + if (*gpgsm->request_origin) + { + char *cmd; + + cmd = _gpgme_strconcat ("OPTION request-origin=", + gpgsm->request_origin, NULL); + if (!cmd) + return gpg_error_from_syserror (); + err = gpgsm_assuan_simple_command (gpgsm, cmd, NULL, NULL); + free (cmd); + if (err && gpg_err_code (err) != GPG_ERR_UNKNOWN_OPTION) + return err; + } + /* 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. */ @@ -1293,8 +1327,57 @@ set_recipients (engine_gpgsm_t gpgsm, gpgme_key_t recp[]) } +/* Take recipients from the LF delimited STRING and send RECIPIENT + * commands to gpgsm. */ static gpgme_error_t -gpgsm_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags, +set_recipients_from_string (engine_gpgsm_t gpgsm, const char *string) +{ + gpg_error_t err = 0; + char *line = NULL; + int no_pubkey = 0; + const char *s; + int n; + + for (;;) + { + while (*string == ' ' || *string == '\t') + string++; + if (!*string) + break; + + s = strchr (string, '\n'); + if (s) + n = s - string; + else + n = strlen (string); + while (n && (string[n-1] == ' ' || string[n-1] == '\t')) + n--; + + gpgrt_free (line); + if (gpgrt_asprintf (&line, "RECIPIENT %.*s", n, string) < 0) + { + err = gpg_error_from_syserror (); + break; + } + string += n + !!s; + + err = gpgsm_assuan_simple_command (gpgsm, line, gpgsm->status.fnc, + gpgsm->status.fnc_value); + + /* Fixme: Improve error reporting. */ + if (gpg_err_code (err) == GPG_ERR_NO_PUBKEY) + no_pubkey++; + else if (err) + break; + } + gpgrt_free (line); + return err? err : no_pubkey? gpg_error (GPG_ERR_NO_PUBKEY) : 0; +} + + +static gpgme_error_t +gpgsm_encrypt (void *engine, gpgme_key_t recp[], const char *recpstring, + gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t ciph, int use_armor) { engine_gpgsm_t gpgsm = engine; @@ -1305,7 +1388,7 @@ gpgsm_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags, if (!recp) return gpg_error (GPG_ERR_NOT_IMPLEMENTED); - if (flags & GPGME_ENCRYPT_NO_ENCRYPT_TO) + if ((flags & GPGME_ENCRYPT_NO_ENCRYPT_TO)) { err = gpgsm_assuan_simple_command (gpgsm, "OPTION no-encrypt-to", NULL, NULL); @@ -1325,7 +1408,10 @@ gpgsm_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags, gpgsm_clear_fd (gpgsm, MESSAGE_FD); gpgsm->inline_data = NULL; - err = set_recipients (gpgsm, recp); + if (!recp && recpstring) + err = set_recipients_from_string (gpgsm, recpstring); + else + err = set_recipients (gpgsm, recp); if (!err) err = start (gpgsm, "ENCRYPT"); @@ -2102,6 +2188,7 @@ struct engine_ops _gpgme_engine_ops_gpgsm = gpgsm_set_colon_line_handler, gpgsm_set_locale, NULL, /* set_protocol */ + gpgsm_set_engine_flags, gpgsm_decrypt, gpgsm_delete, /* decrypt_verify */ NULL, /* edit */ diff --git a/src/engine-spawn.c b/src/engine-spawn.c index 7044781..7b7a9cd 100644 --- a/src/engine-spawn.c +++ b/src/engine-spawn.c @@ -241,7 +241,8 @@ engspawn_start (engine_spawn_t esp, const char *file, const char *argv[], spflags |= IOSPAWN_FLAG_DETACHED; if ((flags & GPGME_SPAWN_ALLOW_SET_FG)) spflags |= IOSPAWN_FLAG_ALLOW_SET_FG; - + if ((flags & GPGME_SPAWN_SHOW_WINDOW)) + spflags |= IOSPAWN_FLAG_SHOW_WINDOW; err = build_fd_data_map (esp); if (err) @@ -448,6 +449,7 @@ struct engine_ops _gpgme_engine_ops_spawn = NULL, /* set_colon_line_handler */ NULL, /* set_locale */ NULL, /* set_protocol */ + NULL, /* set_engine_flags */ NULL, /* decrypt */ NULL, /* delete */ NULL, /* edit */ diff --git a/src/engine-uiserver.c b/src/engine-uiserver.c index bc3f3fb..d8f4fce 100644 --- a/src/engine-uiserver.c +++ b/src/engine-uiserver.c @@ -1075,8 +1075,58 @@ set_recipients (engine_uiserver_t uiserver, gpgme_key_t recp[]) } +/* Take recipients from the LF delimited STRING and send RECIPIENT + * commands to gpgsm. */ static gpgme_error_t -uiserver_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags, +set_recipients_from_string (engine_uiserver_t uiserver, const char *string) +{ + gpg_error_t err = 0; + char *line = NULL; + int no_pubkey = 0; + const char *s; + int n; + + for (;;) + { + while (*string == ' ' || *string == '\t') + string++; + if (!*string) + break; + + s = strchr (string, '\n'); + if (s) + n = s - string; + else + n = strlen (string); + while (n && (string[n-1] == ' ' || string[n-1] == '\t')) + n--; + + gpgrt_free (line); + if (gpgrt_asprintf (&line, "RECIPIENT %.*s", n, string) < 0) + { + err = gpg_error_from_syserror (); + break; + } + string += n + !!s; + + err = uiserver_assuan_simple_command (uiserver, line, + uiserver->status.fnc, + uiserver->status.fnc_value); + + /* Fixme: Improve error reporting. */ + if (gpg_err_code (err) == GPG_ERR_NO_PUBKEY) + no_pubkey++; + else if (err) + break; + } + gpgrt_free (line); + return err? err : no_pubkey? gpg_error (GPG_ERR_NO_PUBKEY) : 0; +} + + +static gpgme_error_t +uiserver_encrypt (void *engine, gpgme_key_t recp[], const char *recpstring, + gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t ciph, int use_armor) { engine_uiserver_t uiserver = engine; @@ -1140,9 +1190,12 @@ uiserver_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags, uiserver->inline_data = NULL; - if (recp) + if (recp || recpstring) { - err = set_recipients (uiserver, recp); + if (recp) + err = set_recipients (uiserver, recp); + else + err = set_recipients_from_string (uiserver, recpstring); if (err) { gpgrt_free (cmd); @@ -1368,6 +1421,7 @@ struct engine_ops _gpgme_engine_ops_uiserver = uiserver_set_colon_line_handler, uiserver_set_locale, uiserver_set_protocol, + NULL, /* set_engine_flags */ uiserver_decrypt, NULL, /* delete */ NULL, /* edit */ diff --git a/src/engine.c b/src/engine.c index 28ba9fd..b716ca2 100644 --- a/src/engine.c +++ b/src/engine.c @@ -651,6 +651,26 @@ _gpgme_engine_set_protocol (engine_t engine, gpgme_protocol_t protocol) } +/* Pass information about the current context to the engine. The + * engine may use this context to retrieve context specific flags. + * Important: The engine is required to immediately copy the required + * flags to its own context! + * + * This function will eventually be used to reduce the number of + * explicit passed flags. */ +void +_gpgme_engine_set_engine_flags (engine_t engine, gpgme_ctx_t ctx) +{ + if (!engine) + return; + + if (!engine->ops->set_engine_flags) + return; + + (*engine->ops->set_engine_flags) (engine->engine, ctx); +} + + gpgme_error_t _gpgme_engine_op_decrypt (engine_t engine, gpgme_decrypt_flags_t flags, @@ -701,6 +721,7 @@ _gpgme_engine_op_edit (engine_t engine, int type, gpgme_key_t key, gpgme_error_t _gpgme_engine_op_encrypt (engine_t engine, gpgme_key_t recp[], + const char *recpstring, gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t ciph, int use_armor) { @@ -710,13 +731,14 @@ _gpgme_engine_op_encrypt (engine_t engine, gpgme_key_t recp[], if (!engine->ops->encrypt) return gpg_error (GPG_ERR_NOT_IMPLEMENTED); - return (*engine->ops->encrypt) (engine->engine, recp, flags, plain, ciph, - use_armor); + return (*engine->ops->encrypt) (engine->engine, recp, recpstring, + flags, plain, ciph, use_armor); } gpgme_error_t _gpgme_engine_op_encrypt_sign (engine_t engine, gpgme_key_t recp[], + const char *recpstring, gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t ciph, int use_armor, gpgme_ctx_t ctx /* FIXME */) @@ -727,8 +749,8 @@ _gpgme_engine_op_encrypt_sign (engine_t engine, gpgme_key_t recp[], 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); + return (*engine->ops->encrypt_sign) (engine->engine, recp, recpstring, + flags, plain, ciph, use_armor, ctx); } diff --git a/src/engine.h b/src/engine.h index 0bf1bb2..8b692f2 100644 --- a/src/engine.h +++ b/src/engine.h @@ -69,6 +69,7 @@ gpgme_error_t _gpgme_engine_set_locale (engine_t engine, int category, const char *value); gpgme_error_t _gpgme_engine_set_protocol (engine_t engine, gpgme_protocol_t protocol); +void _gpgme_engine_set_engine_flags (engine_t engine, gpgme_ctx_t ctx); void _gpgme_engine_release (engine_t engine); void _gpgme_engine_set_status_cb (engine_t engine, gpgme_status_cb_t cb, void *cb_value); @@ -97,11 +98,13 @@ gpgme_error_t _gpgme_engine_op_edit (engine_t engine, int type, gpgme_ctx_t ctx /* FIXME */); gpgme_error_t _gpgme_engine_op_encrypt (engine_t engine, gpgme_key_t recp[], + const char *recpstring, gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t ciph, int use_armor); gpgme_error_t _gpgme_engine_op_encrypt_sign (engine_t engine, gpgme_key_t recp[], + const char *recpstring, gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t ciph, diff --git a/src/get-env.c b/src/get-env.c index 57fd419..b13706f 100644 --- a/src/get-env.c +++ b/src/get-env.c @@ -28,17 +28,71 @@ #include "util.h" -#if defined(HAVE_THREAD_SAFE_GETENV) || !defined (HAVE_GETENV_R) -/* We prefer using getenv() if it is thread-safe. */ - /* 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. */ + +#ifdef HAVE_GETENV_R +#define INITIAL_GETENV_SIZE 32 + +gpgme_error_t +_gpgme_getenv (const char *name, char **value) +{ + size_t len = INITIAL_GETENV_SIZE; + char *env_value; + + env_value = malloc (len); + + while (1) + { + *value = env_value; + if (!env_value) + return gpg_error_from_syserror (); + + if (getenv_r (name, env_value, len) == 0) + break; + + if (errno == ERANGE) + { + len *= 2; + env_value = realloc (env_value, len); + } + else + { + int saved = errno; + + free (env_value); + *value = NULL; + if (errno == ENOENT) + return 0; + else + return gpg_error_from_errno (saved); + } + } + + return 0; +} +#else +#ifndef HAVE_THREAD_SAFE_GETENV +GPGRT_LOCK_DEFINE (environ_lock); +#endif + gpgme_error_t _gpgme_getenv (const char *name, char **value) { char *env_value; + gpgme_error_t err = 0; + +#ifndef HAVE_THREAD_SAFE_GETENV + gpg_err_code_t rc; + rc= gpgrt_lock_lock (&environ_lock); + if (rc) + { + err = gpg_error (rc); + goto leave; + } +#endif env_value = getenv (name); if (!env_value) *value = NULL; @@ -46,14 +100,14 @@ _gpgme_getenv (const char *name, char **value) { *value = strdup (env_value); if (!*value) - return gpg_error_from_syserror (); + err = gpg_error_from_syserror (); } - return 0; +#ifndef HAVE_THREAD_SAFE_GETENV + rc = gpgrt_lock_unlock (&environ_lock); + if (rc) + err = gpg_error (rc); + leave: +#endif + return err; } - -#else - -/* FIXME: Implement this when we have the specification for it. */ -#error Use of getenv_r not implemented. - #endif diff --git a/src/gpgme-json.c b/src/gpgme-json.c new file mode 100644 index 0000000..f1e9f25 --- /dev/null +++ b/src/gpgme-json.c @@ -0,0 +1,1772 @@ +/* gpgme-json.c - JSON based interface to gpgme (server) + * Copyright (C) 2018 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 <http://www.gnu.org/licenses/>. + * SPDX-License-Identifier: LGPL-2.1+ + */ + +/* This is tool implements the Native Messaging protocol of web + * browsers and provides the server part of it. A Javascript based + * client can be found in lang/javascript. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#ifdef HAVE_LOCALE_H +#include <locale.h> +#endif +#include <stdint.h> +#include <sys/stat.h> + +#define GPGRT_ENABLE_ES_MACROS 1 +#define GPGRT_ENABLE_LOG_MACROS 1 +#define GPGRT_ENABLE_ARGPARSE_MACROS 1 +#include "gpgme.h" +#include "cJSON.h" + + +#if GPGRT_VERSION_NUMBER < 0x011c00 /* 1.28 */ +int main (void){fputs ("Build with Libgpg-error >= 1.28!\n", stderr);return 1;} +#else /* libgpg-error >= 1.28 */ + +/* We don't allow a request with more than 64 MiB. */ +#define MAX_REQUEST_SIZE (64 * 1024 * 1024) + +/* Minimal, default and maximum chunk size for returned data. The + * first chunk is returned directly. If the "more" flag is also + * returned, a "getmore" command needs to be used to get the next + * chunk. Right now this value covers just the value of the "data" + * element; so to cover for the other returned objects this values + * needs to be lower than the maximum allowed size of the browser. */ +#define MIN_REPLY_CHUNK_SIZE 512 +#define DEF_REPLY_CHUNK_SIZE (512 * 1024) +#define MAX_REPLY_CHUNK_SIZE (10 * 1024 * 1024) + + +static void xoutofcore (const char *type) GPGRT_ATTR_NORETURN; +static cjson_t error_object_v (cjson_t json, const char *message, + va_list arg_ptr) GPGRT_ATTR_PRINTF(2,0); +static cjson_t error_object (cjson_t json, const char *message, + ...) GPGRT_ATTR_PRINTF(2,3); +static char *error_object_string (const char *message, + ...) GPGRT_ATTR_PRINTF(1,2); + + +/* True if interactive mode is active. */ +static int opt_interactive; +/* True is debug mode is active. */ +static int opt_debug; + +/* Pending data to be returned by a getmore command. */ +static struct +{ + char *buffer; /* Malloced data or NULL if not used. */ + size_t length; /* Length of that data. */ + size_t written; /* # of already written bytes from BUFFER. */ + const char *type;/* The "type" of the data. */ + int base64; /* The "base64" flag of the data. */ +} pending_data; + + +/* + * Helper functions and macros + */ + +#define xtrymalloc(a) gpgrt_malloc ((a)) +#define xtrystrdup(a) gpgrt_strdup ((a)) +#define xmalloc(a) ({ \ + void *_r = gpgrt_malloc ((a)); \ + if (!_r) \ + xoutofcore ("malloc"); \ + _r; }) +#define xcalloc(a,b) ({ \ + void *_r = gpgrt_calloc ((a), (b)); \ + if (!_r) \ + xoutofcore ("calloc"); \ + _r; }) +#define xstrdup(a) ({ \ + char *_r = gpgrt_strdup ((a)); \ + if (!_r) \ + xoutofcore ("strdup"); \ + _r; }) +#define xstrconcat(a, ...) ({ \ + char *_r = gpgrt_strconcat ((a), __VA_ARGS__); \ + if (!_r) \ + xoutofcore ("strconcat"); \ + _r; }) +#define xfree(a) gpgrt_free ((a)) + +#define spacep(p) (*(p) == ' ' || *(p) == '\t') + +#ifndef HAVE_STPCPY +static GPGRT_INLINE char * +_my_stpcpy (char *a, const char *b) +{ + while (*b) + *a++ = *b++; + *a = 0; + return a; +} +#define stpcpy(a,b) _my_stpcpy ((a), (b)) +#endif /*!HAVE_STPCPY*/ + + + +static void +xoutofcore (const char *type) +{ + gpg_error_t err = gpg_error_from_syserror (); + log_error ("%s failed: %s\n", type, gpg_strerror (err)); + exit (2); +} + + +/* Call cJSON_CreateObject but terminate in case of an error. */ +static cjson_t +xjson_CreateObject (void) +{ + cjson_t json = cJSON_CreateObject (); + if (!json) + xoutofcore ("cJSON_CreateObject"); + return json; +} + + +/* Wrapper around cJSON_AddStringToObject which returns an gpg-error + * code instead of the NULL or the new object. */ +static gpg_error_t +cjson_AddStringToObject (cjson_t object, const char *name, const char *string) +{ + if (!cJSON_AddStringToObject (object, name, string)) + return gpg_error_from_syserror (); + return 0; +} + + +/* Same as cjson_AddStringToObject but prints an error message and + * terminates the process. */ +static void +xjson_AddStringToObject (cjson_t object, const char *name, const char *string) +{ + if (!cJSON_AddStringToObject (object, name, string)) + xoutofcore ("cJSON_AddStringToObject"); +} + + +/* Wrapper around cJSON_AddBoolToObject which terminates the process + * in case of an error. */ +static void +xjson_AddBoolToObject (cjson_t object, const char *name, int abool) +{ + if (!cJSON_AddBoolToObject (object, name, abool)) + xoutofcore ("cJSON_AddStringToObject"); + return ; +} + +/* This is similar to cJSON_AddStringToObject but takes (DATA, + * DATALEN) and adds it under NAME as a base 64 encoded string to + * OBJECT. */ +static gpg_error_t +add_base64_to_object (cjson_t object, const char *name, + const void *data, size_t datalen) +{ +#if GPGRT_VERSION_NUMBER < 0x011d00 /* 1.29 */ + return gpg_error (GPG_ERR_NOT_SUPPORTED); +#else + gpg_err_code_t err; + estream_t fp = NULL; + gpgrt_b64state_t state = NULL; + cjson_t j_str = NULL; + void *buffer = NULL; + + fp = es_fopenmem (0, "rwb"); + if (!fp) + { + err = gpg_err_code_from_syserror (); + goto leave; + } + state = gpgrt_b64enc_start (fp, ""); + if (!state) + { + err = gpg_err_code_from_syserror (); + goto leave; + } + + err = gpgrt_b64enc_write (state, data, datalen); + if (err) + goto leave; + + err = gpgrt_b64enc_finish (state); + state = NULL; + if (err) + return err; + + es_fputc (0, fp); + if (es_fclose_snatch (fp, &buffer, NULL)) + { + fp = NULL; + err = gpg_error_from_syserror (); + goto leave; + } + fp = NULL; + + j_str = cJSON_CreateStringConvey (buffer); + if (!j_str) + { + err = gpg_error_from_syserror (); + goto leave; + } + buffer = NULL; + + if (!cJSON_AddItemToObject (object, name, j_str)) + { + err = gpg_error_from_syserror (); + cJSON_Delete (j_str); + j_str = NULL; + goto leave; + } + j_str = NULL; + + leave: + xfree (buffer); + cJSON_Delete (j_str); + gpgrt_b64enc_finish (state); + es_fclose (fp); + return err; +#endif +} + + +/* Create a JSON error object. If JSON is not NULL the error message + * is appended to that object. An existing "type" item will be replaced. */ +static cjson_t +error_object_v (cjson_t json, const char *message, va_list arg_ptr) +{ + cjson_t response, j_tmp; + char *msg; + + msg = gpgrt_vbsprintf (message, arg_ptr); + if (!msg) + xoutofcore ("error_object"); + + response = json? json : xjson_CreateObject (); + + if (!(j_tmp = cJSON_GetObjectItem (response, "type"))) + xjson_AddStringToObject (response, "type", "error"); + else /* Replace existing "type". */ + { + j_tmp = cJSON_CreateString ("error"); + if (!j_tmp) + xoutofcore ("cJSON_CreateString"); + cJSON_ReplaceItemInObject (response, "type", j_tmp); + } + xjson_AddStringToObject (response, "msg", msg); + + xfree (msg); + return response; +} + + +/* Call cJSON_Print but terminate in case of an error. */ +static char * +xjson_Print (cjson_t object) +{ + char *buf; + buf = cJSON_Print (object); + if (!buf) + xoutofcore ("cJSON_Print"); + return buf; +} + + +static cjson_t +error_object (cjson_t json, const char *message, ...) +{ + cjson_t response; + va_list arg_ptr; + + va_start (arg_ptr, message); + response = error_object_v (json, message, arg_ptr); + va_end (arg_ptr); + return response; +} + + +static char * +error_object_string (const char *message, ...) +{ + cjson_t response; + va_list arg_ptr; + char *msg; + + va_start (arg_ptr, message); + response = error_object_v (NULL, message, arg_ptr); + va_end (arg_ptr); + + msg = xjson_Print (response); + cJSON_Delete (response); + return msg; +} + + +/* Get the boolean property NAME from the JSON object and store true + * or valse at R_VALUE. If the name is unknown the value of DEF_VALUE + * is returned. If the type of the value is not boolean, + * GPG_ERR_INV_VALUE is returned and R_VALUE set to DEF_VALUE. */ +static gpg_error_t +get_boolean_flag (cjson_t json, const char *name, int def_value, int *r_value) +{ + cjson_t j_item; + + j_item = cJSON_GetObjectItem (json, name); + if (!j_item) + *r_value = def_value; + else if (cjson_is_true (j_item)) + *r_value = 1; + else if (cjson_is_false (j_item)) + *r_value = 0; + else + { + *r_value = def_value; + return gpg_error (GPG_ERR_INV_VALUE); + } + + return 0; +} + + +/* Get the boolean property PROTOCOL from the JSON object and store + * its value at R_PROTOCOL. The default is OpenPGP. */ +static gpg_error_t +get_protocol (cjson_t json, gpgme_protocol_t *r_protocol) +{ + cjson_t j_item; + + *r_protocol = GPGME_PROTOCOL_OpenPGP; + j_item = cJSON_GetObjectItem (json, "protocol"); + if (!j_item) + ; + else if (!cjson_is_string (j_item)) + return gpg_error (GPG_ERR_INV_VALUE); + else if (!strcmp(j_item->valuestring, "openpgp")) + ; + else if (!strcmp(j_item->valuestring, "cms")) + *r_protocol = GPGME_PROTOCOL_CMS; + else + return gpg_error (GPG_ERR_UNSUPPORTED_PROTOCOL); + + return 0; +} + + +/* Get the chunksize from JSON and store it at R_CHUNKSIZE. */ +static gpg_error_t +get_chunksize (cjson_t json, size_t *r_chunksize) +{ + cjson_t j_item; + + *r_chunksize = DEF_REPLY_CHUNK_SIZE; + j_item = cJSON_GetObjectItem (json, "chunksize"); + if (!j_item) + ; + else if (!cjson_is_number (j_item)) + return gpg_error (GPG_ERR_INV_VALUE); + else if ((size_t)j_item->valueint < MIN_REPLY_CHUNK_SIZE) + *r_chunksize = MIN_REPLY_CHUNK_SIZE; + else if ((size_t)j_item->valueint > MAX_REPLY_CHUNK_SIZE) + *r_chunksize = MAX_REPLY_CHUNK_SIZE; + else + *r_chunksize = (size_t)j_item->valueint; + + return 0; +} + + +/* Extract the keys from the "keys" array in the JSON object. On + * success a string with the keys identifiers is stored at R_KEYS. + * The keys in that string are LF delimited. On failure an error code + * is returned. */ +static gpg_error_t +get_keys (cjson_t json, char **r_keystring) +{ + cjson_t j_keys, j_item; + int i, nkeys; + char *p; + size_t length; + + *r_keystring = NULL; + + j_keys = cJSON_GetObjectItem (json, "keys"); + if (!j_keys) + return gpg_error (GPG_ERR_NO_KEY); + if (!cjson_is_array (j_keys) && !cjson_is_string (j_keys)) + return gpg_error (GPG_ERR_INV_VALUE); + + /* Fixme: We should better use a membuf like thing. */ + length = 1; /* For the EOS. */ + if (cjson_is_string (j_keys)) + { + nkeys = 1; + length += strlen (j_keys->valuestring); + if (strchr (j_keys->valuestring, '\n')) + return gpg_error (GPG_ERR_INV_USER_ID); + } + else + { + nkeys = cJSON_GetArraySize (j_keys); + if (!nkeys) + return gpg_error (GPG_ERR_NO_KEY); + for (i=0; i < nkeys; i++) + { + j_item = cJSON_GetArrayItem (j_keys, i); + if (!j_item || !cjson_is_string (j_item)) + return gpg_error (GPG_ERR_INV_VALUE); + if (i) + length++; /* Space for delimiter. */ + length += strlen (j_item->valuestring); + if (strchr (j_item->valuestring, '\n')) + return gpg_error (GPG_ERR_INV_USER_ID); + } + } + + p = *r_keystring = xtrymalloc (length); + if (!p) + return gpg_error_from_syserror (); + + if (cjson_is_string (j_keys)) + { + strcpy (p, j_keys->valuestring); + } + else + { + for (i=0; i < nkeys; i++) + { + j_item = cJSON_GetArrayItem (j_keys, i); + if (i) + *p++ = '\n'; /* Add delimiter. */ + p = stpcpy (p, j_item->valuestring); + } + } + return 0; +} + + + + +/* + * GPGME support functions. + */ + +/* Helper for get_context. */ +static gpgme_ctx_t +_create_new_context (gpgme_protocol_t proto) +{ + gpg_error_t err; + gpgme_ctx_t ctx; + + err = gpgme_new (&ctx); + if (err) + log_fatal ("error creating GPGME context: %s\n", gpg_strerror (err)); + gpgme_set_protocol (ctx, proto); + gpgme_set_ctx_flag (ctx, "request-origin", "browser"); + return ctx; +} + + +/* Return a context object for protocol PROTO. This is currently a + * statuically allocated context initialized for PROTO. Termnates + * process on failure. */ +static gpgme_ctx_t +get_context (gpgme_protocol_t proto) +{ + static gpgme_ctx_t ctx_openpgp, ctx_cms; + + if (proto == GPGME_PROTOCOL_OpenPGP) + { + if (!ctx_openpgp) + ctx_openpgp = _create_new_context (proto); + return ctx_openpgp; + } + else if (proto == GPGME_PROTOCOL_CMS) + { + if (!ctx_cms) + ctx_cms = _create_new_context (proto); + return ctx_cms; + } + else + log_bug ("invalid protocol %d requested\n", proto); +} + + + +/* Free context object retrieved by get_context. */ +static void +release_context (gpgme_ctx_t ctx) +{ + /* Nothing to do right now. */ + (void)ctx; +} + + + +/* Given a Base-64 encoded string object in JSON return a gpgme data + * object at R_DATA. */ +static gpg_error_t +data_from_base64_string (gpgme_data_t *r_data, cjson_t json) +{ +#if GPGRT_VERSION_NUMBER < 0x011d00 /* 1.29 */ + *r_data = NULL; + return gpg_error (GPG_ERR_NOT_SUPPORTED); +#else + gpg_error_t err; + size_t len; + char *buf = NULL; + gpgrt_b64state_t state = NULL; + gpgme_data_t data = NULL; + + *r_data = NULL; + + /* A quick check on the JSON. */ + if (!cjson_is_string (json)) + { + err = gpg_error (GPG_ERR_INV_VALUE); + goto leave; + } + + state = gpgrt_b64dec_start (NULL); + if (!state) + { + err = gpg_err_code_from_syserror (); + goto leave; + } + + /* Fixme: Data duplication - we should see how to snatch the memory + * from the json object. */ + len = strlen (json->valuestring); + buf = xtrystrdup (json->valuestring); + if (!buf) + { + err = gpg_error_from_syserror (); + goto leave; + } + + err = gpgrt_b64dec_proc (state, buf, len, &len); + if (err) + goto leave; + + err = gpgrt_b64dec_finish (state); + state = NULL; + if (err) + goto leave; + + err = gpgme_data_new_from_mem (&data, buf, len, 1); + if (err) + goto leave; + *r_data = data; + data = NULL; + + leave: + xfree (data); + xfree (buf); + gpgrt_b64dec_finish (state); + return err; +#endif +} + + + +/* + * Implementation of the commands. + */ + + +/* Create a "data" object and the "type", "base64" and "more" flags + * from DATA and append them to RESULT. Ownership if DATA is + * transferred to this function. TYPE must be a fixed string. + * CHUNKSIZE is the chunksize requested from the caller. If BASE64 is + * -1 the need for base64 encoding is determined by the content of + * DATA, all other values are take as rtue or false. Note that + * op_getmore has similar code but works on PENDING_DATA which is set + * here. */ +static gpg_error_t +make_data_object (cjson_t result, gpgme_data_t data, size_t chunksize, + const char *type, int base64) +{ + gpg_error_t err; + char *buffer; + size_t buflen; + int c; + + if (!base64 || base64 == -1) /* Make sure that we really have a string. */ + gpgme_data_write (data, "", 1); + + buffer = gpgme_data_release_and_get_mem (data, &buflen); + data = NULL; + if (!buffer) + { + err = gpg_error_from_syserror (); + goto leave; + } + + if (base64 == -1) + { + base64 = 0; + if (!buflen) + log_fatal ("Appended Nul byte got lost\n"); + if (memchr (buffer, 0, buflen-1)) + { + buflen--; /* Adjust for the extra nul byte. */ + base64 = 1; + } + /* Fixme: We might want to do more advanced heuristics than to + * only look for a Nul. */ + } + + /* Adjust the chunksize if we need to do base64 conversion. */ + if (base64) + chunksize = (chunksize / 4) * 3; + + xjson_AddStringToObject (result, "type", type); + xjson_AddBoolToObject (result, "base64", base64); + + if (buflen > chunksize) + { + xjson_AddBoolToObject (result, "more", 1); + + c = buffer[chunksize]; + buffer[chunksize] = 0; + if (base64) + err = add_base64_to_object (result, "data", buffer, chunksize); + else + err = cjson_AddStringToObject (result, "data", buffer); + buffer[chunksize] = c; + if (err) + goto leave; + + pending_data.buffer = buffer; + buffer = NULL; + pending_data.length = buflen; + pending_data.written = chunksize; + pending_data.type = type; + pending_data.base64 = base64; + } + else + { + if (base64) + err = add_base64_to_object (result, "data", buffer, buflen); + else + err = cjson_AddStringToObject (result, "data", buffer); + } + + leave: + gpgme_free (buffer); + return err; +} + + + +static const char hlp_encrypt[] = + "op: \"encrypt\"\n" + "keys: Array of strings with the fingerprints or user-ids\n" + " of the keys to encrypt the data. For a single key\n" + " a String may be used instead of an array.\n" + "data: Input data. \n" + "\n" + "Optional parameters:\n" + "protocol: Either \"openpgp\" (default) or \"cms\".\n" + "chunksize: Max number of bytes in the resulting \"data\".\n" + "\n" + "Optional boolean flags (default is false):\n" + "base64: Input data is base64 encoded.\n" + "mime: Indicate that data is a MIME object.\n" + "armor: Request output in armored format.\n" + "always-trust: Request --always-trust option.\n" + "no-encrypt-to: Do not use a default recipient.\n" + "no-compress: Do not compress the plaintext first.\n" + "throw-keyids: Request the --throw-keyids option.\n" + "want-address: Require that the keys include a mail address.\n" + "wrap: Assume the input is an OpenPGP message.\n" + "\n" + "Response on success:\n" + "type: \"ciphertext\"\n" + "data: Unless armor mode is used a Base64 encoded binary\n" + " ciphertext. In armor mode a string with an armored\n" + " OpenPGP or a PEM message.\n" + "base64: Boolean indicating whether data is base64 encoded.\n" + "more: Optional boolean indicating that \"getmore\" is required."; +static gpg_error_t +op_encrypt (cjson_t request, cjson_t result) +{ + gpg_error_t err; + gpgme_ctx_t ctx = NULL; + gpgme_protocol_t protocol; + size_t chunksize; + int opt_base64; + int opt_mime; + char *keystring = NULL; + cjson_t j_input; + gpgme_data_t input = NULL; + gpgme_data_t output = NULL; + int abool; + gpgme_encrypt_flags_t encrypt_flags = 0; + + if ((err = get_protocol (request, &protocol))) + goto leave; + ctx = get_context (protocol); + if ((err = get_chunksize (request, &chunksize))) + goto leave; + + if ((err = get_boolean_flag (request, "base64", 0, &opt_base64))) + goto leave; + if ((err = get_boolean_flag (request, "mime", 0, &opt_mime))) + goto leave; + + if ((err = get_boolean_flag (request, "armor", 0, &abool))) + goto leave; + gpgme_set_armor (ctx, abool); + if ((err = get_boolean_flag (request, "always-trust", 0, &abool))) + goto leave; + if (abool) + encrypt_flags |= GPGME_ENCRYPT_ALWAYS_TRUST; + if ((err = get_boolean_flag (request, "no-encrypt-to", 0,&abool))) + goto leave; + if (abool) + encrypt_flags |= GPGME_ENCRYPT_NO_ENCRYPT_TO; + if ((err = get_boolean_flag (request, "no-compress", 0, &abool))) + goto leave; + if (abool) + encrypt_flags |= GPGME_ENCRYPT_NO_COMPRESS; + if ((err = get_boolean_flag (request, "throw-keyids", 0, &abool))) + goto leave; + if (abool) + encrypt_flags |= GPGME_ENCRYPT_THROW_KEYIDS; + if ((err = get_boolean_flag (request, "wrap", 0, &abool))) + goto leave; + if (abool) + encrypt_flags |= GPGME_ENCRYPT_WRAP; + if ((err = get_boolean_flag (request, "want-address", 0, &abool))) + goto leave; + if (abool) + encrypt_flags |= GPGME_ENCRYPT_WANT_ADDRESS; + + + /* Get the keys. */ + err = get_keys (request, &keystring); + if (err) + { + /* Provide a custom error response. */ + error_object (result, "Error getting keys: %s", gpg_strerror (err)); + goto leave; + } + + /* Get the data. Note that INPUT is a shallow data object with the + * storage hold in REQUEST. */ + j_input = cJSON_GetObjectItem (request, "data"); + if (!j_input) + { + err = gpg_error (GPG_ERR_NO_DATA); + goto leave; + } + if (!cjson_is_string (j_input)) + { + err = gpg_error (GPG_ERR_INV_VALUE); + goto leave; + } + if (opt_base64) + { + err = data_from_base64_string (&input, j_input); + if (err) + { + error_object (result, "Error decoding Base-64 encoded 'data': %s", + gpg_strerror (err)); + goto leave; + } + } + else + { + err = gpgme_data_new_from_mem (&input, j_input->valuestring, + strlen (j_input->valuestring), 0); + if (err) + { + error_object (result, "Error getting 'data': %s", gpg_strerror (err)); + goto leave; + } + } + if (opt_mime) + gpgme_data_set_encoding (input, GPGME_DATA_ENCODING_MIME); + + + /* Create an output data object. */ + err = gpgme_data_new (&output); + if (err) + { + error_object (result, "Error creating output data object: %s", + gpg_strerror (err)); + goto leave; + } + + /* Encrypt. */ + err = gpgme_op_encrypt_ext (ctx, NULL, keystring, encrypt_flags, + input, output); + /* encrypt_result = gpgme_op_encrypt_result (ctx); */ + if (err) + { + error_object (result, "Encryption failed: %s", gpg_strerror (err)); + goto leave; + } + gpgme_data_release (input); + input = NULL; + + /* We need to base64 if armoring has not been requested. */ + err = make_data_object (result, output, chunksize, + "ciphertext", !gpgme_get_armor (ctx)); + output = NULL; + + leave: + xfree (keystring); + release_context (ctx); + gpgme_data_release (input); + gpgme_data_release (output); + return err; +} + + + +static const char hlp_decrypt[] = + "op: \"decrypt\"\n" + "data: The encrypted data.\n" + "\n" + "Optional parameters:\n" + "protocol: Either \"openpgp\" (default) or \"cms\".\n" + "chunksize: Max number of bytes in the resulting \"data\".\n" + "\n" + "Optional boolean flags (default is false):\n" + "base64: Input data is base64 encoded.\n" + "\n" + "Response on success:\n" + "type: \"plaintext\"\n" + "data: The decrypted data. This may be base64 encoded.\n" + "base64: Boolean indicating whether data is base64 encoded.\n" + "mime: A Boolean indicating whether the data is a MIME object.\n" + "info: An optional object with extra information.\n" + "more: Optional boolean indicating that \"getmore\" is required."; +static gpg_error_t +op_decrypt (cjson_t request, cjson_t result) +{ + gpg_error_t err; + gpgme_ctx_t ctx = NULL; + gpgme_protocol_t protocol; + size_t chunksize; + int opt_base64; + cjson_t j_input; + gpgme_data_t input = NULL; + gpgme_data_t output = NULL; + gpgme_decrypt_result_t decrypt_result; + + if ((err = get_protocol (request, &protocol))) + goto leave; + ctx = get_context (protocol); + if ((err = get_chunksize (request, &chunksize))) + goto leave; + + if ((err = get_boolean_flag (request, "base64", 0, &opt_base64))) + goto leave; + + /* Get the data. Note that INPUT is a shallow data object with the + * storage hold in REQUEST. */ + j_input = cJSON_GetObjectItem (request, "data"); + if (!j_input) + { + err = gpg_error (GPG_ERR_NO_DATA); + goto leave; + } + if (!cjson_is_string (j_input)) + { + err = gpg_error (GPG_ERR_INV_VALUE); + goto leave; + } + if (opt_base64) + { + err = data_from_base64_string (&input, j_input); + if (err) + { + error_object (result, "Error decoding Base-64 encoded 'data': %s", + gpg_strerror (err)); + goto leave; + } + } + else + { + err = gpgme_data_new_from_mem (&input, j_input->valuestring, + strlen (j_input->valuestring), 0); + if (err) + { + error_object (result, "Error getting 'data': %s", gpg_strerror (err)); + goto leave; + } + } + + /* Create an output data object. */ + err = gpgme_data_new (&output); + if (err) + { + error_object (result, "Error creating output data object: %s", + gpg_strerror (err)); + goto leave; + } + + /* Decrypt. */ + err = gpgme_op_decrypt_ext (ctx, GPGME_DECRYPT_VERIFY, + input, output); + decrypt_result = gpgme_op_decrypt_result (ctx); + if (err) + { + error_object (result, "Decryption failed: %s", gpg_strerror (err)); + goto leave; + } + gpgme_data_release (input); + input = NULL; + + if (decrypt_result->is_mime) + xjson_AddBoolToObject (result, "mime", 1); + + err = make_data_object (result, output, chunksize, "plaintext", -1); + output = NULL; + + leave: + release_context (ctx); + gpgme_data_release (input); + gpgme_data_release (output); + return err; +} + + + +static const char hlp_getmore[] = + "op: \"getmore\"\n" + "\n" + "Optional parameters:\n" + "chunksize: Max number of bytes in the \"data\" object.\n" + "\n" + "Response on success:\n" + "type: Type of the pending data\n" + "data: The next chunk of data\n" + "base64: Boolean indicating whether data is base64 encoded\n" + "more: Optional boolean requesting another \"getmore\"."; +static gpg_error_t +op_getmore (cjson_t request, cjson_t result) +{ + gpg_error_t err; + int c; + size_t n; + size_t chunksize; + + if ((err = get_chunksize (request, &chunksize))) + goto leave; + + /* Adjust the chunksize if we need to do base64 conversion. */ + if (pending_data.base64) + chunksize = (chunksize / 4) * 3; + + /* Do we have anything pending? */ + if (!pending_data.buffer) + { + err = gpg_error (GPG_ERR_NO_DATA); + error_object (result, "Operation not possible: %s", gpg_strerror (err)); + goto leave; + } + + xjson_AddStringToObject (result, "type", pending_data.type); + xjson_AddBoolToObject (result, "base64", pending_data.base64); + + if (pending_data.written >= pending_data.length) + { + /* EOF reached. This should not happen but we return an empty + * string once in case of client errors. */ + gpgme_free (pending_data.buffer); + pending_data.buffer = NULL; + xjson_AddBoolToObject (result, "more", 0); + err = cjson_AddStringToObject (result, "data", ""); + } + else + { + n = pending_data.length - pending_data.written; + if (n > chunksize) + { + n = chunksize; + xjson_AddBoolToObject (result, "more", 1); + } + else + xjson_AddBoolToObject (result, "more", 0); + + c = pending_data.buffer[pending_data.written + n]; + pending_data.buffer[pending_data.written + n] = 0; + if (pending_data.base64) + err = add_base64_to_object (result, "data", + (pending_data.buffer + + pending_data.written), n); + else + err = cjson_AddStringToObject (result, "data", + (pending_data.buffer + + pending_data.written)); + pending_data.buffer[pending_data.written + n] = c; + if (!err) + { + pending_data.written += n; + if (pending_data.written >= pending_data.length) + { + gpgme_free (pending_data.buffer); + pending_data.buffer = NULL; + } + } + } + + leave: + return err; +} + + + +static const char hlp_help[] = + "The tool expects a JSON object with the request and responds with\n" + "another JSON object. Even on error a JSON object is returned. The\n" + "property \"op\" is mandatory and its string value selects the\n" + "operation; if the property \"help\" with the value \"true\" exists, the\n" + "operation is not performned but a string with the documentation\n" + "returned. To list all operations it is allowed to leave out \"op\" in\n" + "help mode. Supported values for \"op\" are:\n\n" + " encrypt Encrypt data.\n" + " getmore Retrieve remaining data.\n" + " help Help overview."; +static gpg_error_t +op_help (cjson_t request, cjson_t result) +{ + cjson_t j_tmp; + char *buffer = NULL; + const char *msg; + + j_tmp = cJSON_GetObjectItem (request, "interactive_help"); + if (opt_interactive && j_tmp && cjson_is_string (j_tmp)) + msg = buffer = xstrconcat (hlp_help, "\n", j_tmp->valuestring, NULL); + else + msg = hlp_help; + + xjson_AddStringToObject (result, "type", "help"); + xjson_AddStringToObject (result, "msg", msg); + + xfree (buffer); + return 0; +} + + + +/* + * Dispatcher + */ + +/* Process a request and return the response. The response is a newly + * allocated string or NULL in case of an error. */ +static char * +process_request (const char *request) +{ + static struct { + const char *op; + gpg_error_t (*handler)(cjson_t request, cjson_t result); + const char * const helpstr; + } optbl[] = { + { "encrypt", op_encrypt, hlp_encrypt }, + { "decrypt", op_decrypt, hlp_decrypt }, + { "getmore", op_getmore, hlp_getmore }, + { "help", op_help, hlp_help }, + { NULL } + }; + size_t erroff; + cjson_t json; + cjson_t j_tmp, j_op; + cjson_t response; + int helpmode; + const char *op; + char *res; + int idx; + + response = xjson_CreateObject (); + + json = cJSON_Parse (request, &erroff); + if (!json) + { + log_string (GPGRT_LOGLVL_INFO, request); + log_info ("invalid JSON object at offset %zu\n", erroff); + error_object (response, "invalid JSON object at offset %zu\n", erroff); + goto leave; + } + + j_tmp = cJSON_GetObjectItem (json, "help"); + helpmode = (j_tmp && cjson_is_true (j_tmp)); + + j_op = cJSON_GetObjectItem (json, "op"); + if (!j_op || !cjson_is_string (j_op)) + { + if (!helpmode) + { + error_object (response, "Property \"op\" missing"); + goto leave; + } + op = "help"; /* Help summary. */ + } + else + op = j_op->valuestring; + + for (idx=0; optbl[idx].op; idx++) + if (!strcmp (op, optbl[idx].op)) + break; + if (optbl[idx].op) + { + if (helpmode && strcmp (op, "help")) + { + xjson_AddStringToObject (response, "type", "help"); + xjson_AddStringToObject (response, "op", op); + xjson_AddStringToObject (response, "msg", optbl[idx].helpstr); + } + else + { + gpg_error_t err; + + /* If this is not the "getmore" command and we have any + * pending data release that data. */ + if (pending_data.buffer && optbl[idx].handler != op_getmore) + { + gpgme_free (pending_data.buffer); + pending_data.buffer = NULL; + } + + err = optbl[idx].handler (json, response); + if (err) + { + if (!(j_tmp = cJSON_GetObjectItem (response, "type")) + || !cjson_is_string (j_tmp) + || strcmp (j_tmp->valuestring, "error")) + { + /* No error type response - provide a generic one. */ + error_object (response, "Operation failed: %s", + gpg_strerror (err)); + } + + xjson_AddStringToObject (response, "op", op); + } + } + } + else /* Operation not supported. */ + { + error_object (response, "Unknown operation '%s'", op); + xjson_AddStringToObject (response, "op", op); + } + + leave: + cJSON_Delete (json); + if (opt_interactive) + res = cJSON_Print (response); + else + res = cJSON_PrintUnformatted (response); + if (!res) + log_error ("Printing JSON data failed\n"); + cJSON_Delete (response); + return res; +} + + + +/* + * Driver code + */ + +static char * +get_file (const char *fname) +{ + gpg_error_t err; + estream_t fp; + struct stat st; + char *buf; + size_t buflen; + + fp = es_fopen (fname, "r"); + if (!fp) + { + err = gpg_error_from_syserror (); + log_error ("can't open '%s': %s\n", fname, gpg_strerror (err)); + return NULL; + } + + if (fstat (es_fileno(fp), &st)) + { + err = gpg_error_from_syserror (); + log_error ("can't stat '%s': %s\n", fname, gpg_strerror (err)); + es_fclose (fp); + return NULL; + } + + buflen = st.st_size; + buf = xmalloc (buflen+1); + if (es_fread (buf, buflen, 1, fp) != 1) + { + err = gpg_error_from_syserror (); + log_error ("error reading '%s': %s\n", fname, gpg_strerror (err)); + es_fclose (fp); + xfree (buf); + return NULL; + } + buf[buflen] = 0; + es_fclose (fp); + + return buf; +} + + +/* Return a malloced line or NULL on EOF. Terminate on read + * error. */ +static char * +get_line (void) +{ + char *line = NULL; + size_t linesize = 0; + gpg_error_t err; + size_t maxlength = 2048; + int n; + const char *s; + char *p; + + again: + n = es_read_line (es_stdin, &line, &linesize, &maxlength); + if (n < 0) + { + err = gpg_error_from_syserror (); + log_error ("error reading line: %s\n", gpg_strerror (err)); + exit (1); + } + if (!n) + { + xfree (line); + line = NULL; + return NULL; /* EOF */ + } + if (!maxlength) + { + log_info ("line too long - skipped\n"); + goto again; + } + if (memchr (line, 0, n)) + log_info ("warning: line shortened due to embedded Nul character\n"); + + if (line[n-1] == '\n') + line[n-1] = 0; + + /* Trim leading spaces. */ + for (s=line; spacep (s); s++) + ; + if (s != line) + { + for (p=line; *s;) + *p++ = *s++; + *p = 0; + n = p - line; + } + + return line; +} + + +/* Process meta commands used with the standard REPL. */ +static char * +process_meta_commands (const char *request) +{ + char *result = NULL; + + while (spacep (request)) + request++; + + if (!strncmp (request, "help", 4) && (spacep (request+4) || !request[4])) + { + if (request[4]) + { + char *buf = xstrconcat ("{ \"help\":true, \"op\":\"", request+5, + "\" }", NULL); + result = process_request (buf); + xfree (buf); + } + else + result = process_request ("{ \"op\": \"help\"," + " \"interactive_help\": " + "\"\\nMeta commands:\\n" + " ,read FNAME Process data from FILE\\n" + " ,help CMD Print help for a command\\n" + " ,quit Terminate process\"" + "}"); + } + else if (!strncmp (request, "quit", 4) && (spacep (request+4) || !request[4])) + exit (0); + else if (!strncmp (request, "read", 4) && (spacep (request+4) || !request[4])) + { + if (!request[4]) + log_info ("usage: ,read FILENAME\n"); + else + { + char *buffer = get_file (request + 5); + if (buffer) + { + result = process_request (buffer); + xfree (buffer); + } + } + } + else + log_info ("invalid meta command\n"); + + return result; +} + + +/* If STRING has a help response, return the MSG property in a human + * readable format. */ +static char * +get_help_msg (const char *string) +{ + cjson_t json, j_type, j_msg; + const char *msg; + char *buffer = NULL; + char *p; + + json = cJSON_Parse (string, NULL); + if (json) + { + j_type = cJSON_GetObjectItem (json, "type"); + if (j_type && cjson_is_string (j_type) + && !strcmp (j_type->valuestring, "help")) + { + j_msg = cJSON_GetObjectItem (json, "msg"); + if (j_msg || cjson_is_string (j_msg)) + { + msg = j_msg->valuestring; + buffer = malloc (strlen (msg)+1); + if (buffer) + { + for (p=buffer; *msg; msg++) + { + if (*msg == '\\' && msg[1] == '\n') + *p++ = '\n'; + else + *p++ = *msg; + } + *p = 0; + } + } + } + cJSON_Delete (json); + } + return buffer; +} + + +/* An interactive standard REPL. */ +static void +interactive_repl (void) +{ + char *line = NULL; + char *request = NULL; + char *response = NULL; + char *p; + int first; + + es_setvbuf (es_stdin, NULL, _IONBF, 0); +#if GPGRT_VERSION_NUMBER >= 0x011d00 /* 1.29 */ + es_fprintf (es_stderr, "%s %s ready (enter \",help\" for help)\n", + gpgrt_strusage (11), gpgrt_strusage (13)); +#endif + do + { + es_fputs ("> ", es_stderr); + es_fflush (es_stderr); + es_fflush (es_stdout); + xfree (line); + line = get_line (); + es_fflush (es_stderr); + es_fflush (es_stdout); + + first = !request; + if (line && *line) + { + if (!request) + request = xstrdup (line); + else + request = xstrconcat (request, "\n", line, NULL); + } + + if (!line) + es_fputs ("\n", es_stderr); + + if (!line || !*line || (first && *request == ',')) + { + /* Process the input. */ + xfree (response); + response = NULL; + if (request && *request == ',') + { + response = process_meta_commands (request+1); + } + else if (request) + { + response = process_request (request); + } + xfree (request); + request = NULL; + + if (response) + { + if (opt_interactive) + { + char *msg = get_help_msg (response); + if (msg) + { + xfree (response); + response = msg; + } + } + + es_fputs ("===> ", es_stderr); + es_fflush (es_stderr); + for (p=response; *p; p++) + { + if (*p == '\n') + { + es_fflush (es_stdout); + es_fputs ("\n===> ", es_stderr); + es_fflush (es_stderr); + } + else + es_putc (*p, es_stdout); + } + es_fflush (es_stdout); + es_fputs ("\n", es_stderr); + } + } + } + while (line); + + xfree (request); + xfree (response); + xfree (line); +} + + +/* Read and process a single request. */ +static void +read_and_process_single_request (void) +{ + char *line = NULL; + char *request = NULL; + char *response = NULL; + size_t n; + + for (;;) + { + xfree (line); + line = get_line (); + if (line && *line) + request = (request? xstrconcat (request, "\n", line, NULL) + /**/ : xstrdup (line)); + if (!line) + { + if (request) + { + xfree (response); + response = process_request (request); + if (response) + { + es_fputs (response, es_stdout); + if ((n = strlen (response)) && response[n-1] != '\n') + es_fputc ('\n', es_stdout); + } + es_fflush (es_stdout); + } + break; + } + } + + xfree (response); + xfree (request); + xfree (line); +} + + +/* The Native Messaging processing loop. */ +static void +native_messaging_repl (void) +{ + gpg_error_t err; + uint32_t nrequest, nresponse; + char *request = NULL; + char *response = NULL; + size_t n; + + /* Due to the length octets we need to switch the I/O stream into + * binary mode. */ + es_set_binary (es_stdin); + es_set_binary (es_stdout); + es_setbuf (es_stdin, NULL); /* stdin needs to be unbuffered! */ + + for (;;) + { + /* Read length. Note that the protocol uses native endianess. + * Is it allowed to call such a thing a well thought out + * protocol? */ + if (es_read (es_stdin, &nrequest, sizeof nrequest, &n)) + { + err = gpg_error_from_syserror (); + log_error ("error reading request header: %s\n", gpg_strerror (err)); + break; + } + if (!n) + break; /* EOF */ + if (n != sizeof nrequest) + { + log_error ("error reading request header: short read\n"); + break; + } + if (nrequest > MAX_REQUEST_SIZE) + { + log_error ("error reading request: request too long (%zu MiB)\n", + (size_t)nrequest / (1024*1024)); + /* Fixme: Shall we read the request to the bit bucket and + * return an error reponse or just return an error reponse + * and terminate? Needs some testing. */ + break; + } + + /* Read request. */ + request = xtrymalloc (nrequest); + if (!request) + { + err = gpg_error_from_syserror (); + log_error ("error reading request: Not enough memory for %zu MiB)\n", + (size_t)nrequest / (1024*1024)); + /* FIXME: See comment above. */ + break; + } + if (es_read (es_stdin, request, nrequest, &n)) + { + err = gpg_error_from_syserror (); + log_error ("error reading request: %s\n", gpg_strerror (err)); + break; + } + if (n != nrequest) + { + /* That is a protocol violation. */ + xfree (response); + response = error_object_string ("Invalid request:" + " short read (%zu of %zu bytes)\n", + n, (size_t)nrequest); + } + else /* Process request */ + { + if (opt_debug) + log_debug ("request='%s'\n", request); + xfree (response); + response = process_request (request); + if (opt_debug) + log_debug ("response='%s'\n", response); + } + nresponse = strlen (response); + + /* Write response */ + if (es_write (es_stdout, &nresponse, sizeof nresponse, &n)) + { + err = gpg_error_from_syserror (); + log_error ("error writing request header: %s\n", gpg_strerror (err)); + break; + } + if (n != sizeof nrequest) + { + log_error ("error writing request header: short write\n"); + break; + } + if (es_write (es_stdout, response, nresponse, &n)) + { + err = gpg_error_from_syserror (); + log_error ("error writing request: %s\n", gpg_strerror (err)); + break; + } + if (n != nresponse) + { + log_error ("error writing request: short write\n"); + break; + } + if (es_fflush (es_stdout) || es_ferror (es_stdout)) + { + err = gpg_error_from_syserror (); + log_error ("error writing request: %s\n", gpg_strerror (err)); + break; + } + } + + xfree (response); + xfree (request); +} + + + +static const char * +my_strusage( int level ) +{ + const char *p; + + switch (level) + { + case 9: p = "LGPL-2.1-or-later"; break; + case 11: p = "gpgme-json"; break; + case 13: p = PACKAGE_VERSION; break; + case 14: p = "Copyright (C) 2018 g10 Code GmbH"; break; + case 19: p = "Please report bugs to <" PACKAGE_BUGREPORT ">.\n"; break; + case 1: + case 40: + p = "Usage: gpgme-json [OPTIONS]"; + break; + case 41: + p = "Native messaging based GPGME operations.\n"; + break; + case 42: + p = "1"; /* Flag print 40 as part of 41. */ + break; + default: p = NULL; break; + } + return p; +} + +int +main (int argc, char *argv[]) +{ +#if GPGRT_VERSION_NUMBER < 0x011d00 /* 1.29 */ + + fprintf (stderr, "WARNING: Old libgpg-error - using limited mode\n"); + native_messaging_repl (); + +#else /* This is a modern libgp-error. */ + + enum { CMD_DEFAULT = 0, + CMD_INTERACTIVE = 'i', + CMD_SINGLE = 's', + CMD_LIBVERSION = 501, + } cmd = CMD_DEFAULT; + enum { + OPT_DEBUG = 600 + }; + + static gpgrt_opt_t opts[] = { + ARGPARSE_c (CMD_INTERACTIVE, "interactive", "Interactive REPL"), + ARGPARSE_c (CMD_SINGLE, "single", "Single request mode"), + ARGPARSE_c (CMD_LIBVERSION, "lib-version", "Show library version"), + ARGPARSE_s_n(OPT_DEBUG, "debug", "Flyswatter"), + + ARGPARSE_end() + }; + gpgrt_argparse_t pargs = { &argc, &argv}; + + gpgrt_set_strusage (my_strusage); + +#ifdef HAVE_SETLOCALE + setlocale (LC_ALL, ""); +#endif + gpgme_check_version (NULL); +#ifdef LC_CTYPE + gpgme_set_locale (NULL, LC_CTYPE, setlocale (LC_CTYPE, NULL)); +#endif +#ifdef LC_MESSAGES + gpgme_set_locale (NULL, LC_MESSAGES, setlocale (LC_MESSAGES, NULL)); +#endif + + while (gpgrt_argparse (NULL, &pargs, opts)) + { + switch (pargs.r_opt) + { + case CMD_INTERACTIVE: + opt_interactive = 1; + /* Fall trough. */ + case CMD_SINGLE: + case CMD_LIBVERSION: + cmd = pargs.r_opt; + break; + + case OPT_DEBUG: opt_debug = 1; break; + + default: + pargs.err = ARGPARSE_PRINT_WARNING; + break; + } + } + gpgrt_argparse (NULL, &pargs, NULL); + + if (!opt_debug) + { + const char *s = getenv ("GPGME_JSON_DEBUG"); + if (s && atoi (s) > 0) + opt_debug = 1; + } + + if (opt_debug) + { + const char *home = getenv ("HOME"); + char *file = xstrconcat ("socket://", + home? home:"/tmp", + "/.gnupg/S.gpgme-json.log", NULL); + log_set_file (file); + xfree (file); + } + + if (opt_debug) + { int i; + for (i=0; argv[i]; i++) + log_debug ("argv[%d]='%s'\n", i, argv[i]); + } + + switch (cmd) + { + case CMD_DEFAULT: + native_messaging_repl (); + break; + + case CMD_SINGLE: + read_and_process_single_request (); + break; + + case CMD_INTERACTIVE: + interactive_repl (); + break; + + case CMD_LIBVERSION: + printf ("Version from header: %s (0x%06x)\n", + GPGME_VERSION, GPGME_VERSION_NUMBER); + printf ("Version from binary: %s\n", gpgme_check_version (NULL)); + printf ("Copyright blurb ...:%s\n", gpgme_check_version ("\x01\x01")); + break; + } + + if (opt_debug) + log_debug ("ready"); + +#endif /* This is a modern libgp-error. */ + return 0; +} +#endif /* libgpg-error >= 1.28 */ diff --git a/src/gpgme-tool.c b/src/gpgme-tool.c index 3e2dc78..e7a7a6f 100644 --- a/src/gpgme-tool.c +++ b/src/gpgme-tool.c @@ -3101,7 +3101,7 @@ cmd_hash_algo_name (assuan_context_t ctx, char *line) static const char hlp_identify[] = - "IDENTIY\n" + "IDENTIFY\n" "\n" "Identify the type of data set with the INPUT command."; static gpg_error_t diff --git a/src/gpgme-w32spawn.c b/src/gpgme-w32spawn.c index d86c850..868dbd5 100644 --- a/src/gpgme-w32spawn.c +++ b/src/gpgme-w32spawn.c @@ -121,8 +121,6 @@ my_spawn (char **argv, struct spawn_fd_item_s *fd_list, unsigned int flags) int duped_stdout = 0; int duped_stderr = 0; HANDLE hnul = INVALID_HANDLE_VALUE; - /* FIXME. */ - int debug_me = 0; i = 0; while (argv[i]) @@ -142,7 +140,7 @@ my_spawn (char **argv, struct spawn_fd_item_s *fd_list, unsigned int flags) memset (&si, 0, sizeof si); si.cb = sizeof (si); si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; - si.wShowWindow = debug_me ? SW_SHOW : SW_HIDE; + si.wShowWindow = (flags & IOSPAWN_FLAG_SHOW_WINDOW) ? SW_SHOW : SW_HIDE; si.hStdInput = GetStdHandle (STD_INPUT_HANDLE); si.hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE); si.hStdError = GetStdHandle (STD_ERROR_HANDLE); diff --git a/src/gpgme.c b/src/gpgme.c index d0a5afe..82d6747 100644 --- a/src/gpgme.c +++ b/src/gpgme.c @@ -248,6 +248,7 @@ gpgme_release (gpgme_ctx_t ctx) free (ctx->lc_ctype); free (ctx->lc_messages); free (ctx->override_session_key); + free (ctx->request_origin); _gpgme_engine_info_release (ctx->engine_info); ctx->engine_info = NULL; DESTROY_LOCK (ctx->lock); @@ -486,13 +487,8 @@ gpgme_get_armor (gpgme_ctx_t ctx) } -/* Set the flag NAME for CTX to VALUE. The supported flags are: - * - * - full-status :: With a value of "1" the status callback set by - * gpgme_set_status_cb returns all status lines - * except for PROGRESS lines. With the default of - * "0" the status callback is only called in certain - * situations. +/* Set the flag NAME for CTX to VALUE. Please consult the manual for + * a description of the flags. */ gpgme_error_t gpgme_set_ctx_flag (gpgme_ctx_t ctx, const char *name, const char *value) @@ -535,6 +531,17 @@ gpgme_set_ctx_flag (gpgme_ctx_t ctx, const char *name, const char *value) { ctx->auto_key_retrieve = abool; } + else if (!strcmp (name, "request-origin")) + { + free (ctx->request_origin); + ctx->request_origin = strdup (value); + if (!ctx->request_origin) + err = gpg_error_from_syserror (); + } + else if (!strcmp (name, "no-symkey-cache")) + { + ctx->no_symkey_cache = abool; + } else err = gpg_error (GPG_ERR_UNKNOWN_NAME); @@ -576,6 +583,14 @@ gpgme_get_ctx_flag (gpgme_ctx_t ctx, const char *name) { return ctx->auto_key_retrieve? "1":""; } + else if (!strcmp (name, "request-origin")) + { + return ctx->request_origin? ctx->request_origin : ""; + } + else if (!strcmp (name, "no-symkey-cache")) + { + return ctx->no_symkey_cache? "1":""; + } else return NULL; } diff --git a/src/gpgme.def b/src/gpgme.def index cad30f6..a01d89a 100644 --- a/src/gpgme.def +++ b/src/gpgme.def @@ -267,5 +267,10 @@ EXPORTS gpgme_op_conf_dir @199 + gpgme_op_encrypt_ext @200 + gpgme_op_encrypt_ext_start @201 + gpgme_op_encrypt_sign_ext @202 + gpgme_op_encrypt_sign_ext_start @203 + ; END diff --git a/src/gpgme.h.in b/src/gpgme.h.in index 31a9060..49fafb9 100644 --- a/src/gpgme.h.in +++ b/src/gpgme.h.in @@ -1,6 +1,6 @@ /* gpgme.h - Public interface to GnuPG Made Easy. -*- c -*- * Copyright (C) 2000 Werner Koch (dd9jn) - * Copyright (C) 2001-2017 g10 Code GmbH + * Copyright (C) 2001-2018 g10 Code GmbH * * This file is part of GPGME. * @@ -16,6 +16,7 @@ * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see <http://www.gnu.org/licenses/>. + * SPDX-License-Identifier: LGPL-2.1+ * * Generated from gpgme.h.in for @GPGME_CONFIG_HOST@. */ @@ -37,14 +38,14 @@ extern "C" { /* The version of this header should match the one of the library. Do - not use this symbol in your application, use gpgme_check_version - instead. The purpose of this macro is to let autoconf (using the - AM_PATH_GPGME macro) check that this header matches the installed - library. */ + * not use this symbol in your application, use gpgme_check_version + * instead. The purpose of this macro is to let autoconf (using the + * AM_PATH_GPGME macro) check that this header matches the installed + * library. */ #define GPGME_VERSION "@PACKAGE_VERSION@" /* The version number of this header. It may be used to handle minor - API incompatibilities. */ + * API incompatibilities. */ #define GPGME_VERSION_NUMBER @VERSION_NUMBER@ @@ -87,7 +88,7 @@ extern "C" { /* The macro _GPGME_DEPRECATED_OUTSIDE_GPGME suppresses warnings for - fields we must access in GPGME for ABI compatibility. */ + * fields we must access in GPGME for ABI compatibility. */ #ifdef _GPGME_IN_GPGME #define _GPGME_DEPRECATED_OUTSIDE_GPGME(a,b) #else @@ -113,7 +114,7 @@ extern "C" { */ /* The context holds some global state and configuration options, as - well as the results of a crypto operation. */ + * well as the results of a crypto operation. */ struct gpgme_context; typedef struct gpgme_context *gpgme_ctx_t; @@ -124,7 +125,8 @@ typedef struct gpgme_data *gpgme_data_t; /* - * Wrappers for the libgpg-error library. + * Wrappers for the libgpg-error library. They are generally not + * needed and the gpg-error versions may be used instead. */ typedef gpg_error_t gpgme_error_t; @@ -140,7 +142,7 @@ gpgme_err_make (gpgme_err_source_t source, gpgme_err_code_t code) /* The user can define GPGME_ERR_SOURCE_DEFAULT before including this - file to specify a default source for gpgme_error. */ + * file to specify a default source for gpgme_error. */ #ifndef GPGME_ERR_SOURCE_DEFAULT #define GPGME_ERR_SOURCE_DEFAULT GPG_ERR_SOURCE_USER_1 #endif @@ -167,45 +169,46 @@ gpgme_err_source (gpgme_error_t err) /* Return a pointer to a string containing a description of the error - code in the error value ERR. This function is not thread safe. */ + * code in the error value ERR. This function is not thread safe. */ const char *gpgme_strerror (gpgme_error_t err); /* Return the error string for ERR in the user-supplied buffer BUF of - size BUFLEN. This function is, in contrast to gpg_strerror, - thread-safe if a thread-safe strerror_r() function is provided by - the system. If the function succeeds, 0 is returned and BUF - contains the string describing the error. If the buffer was not - large enough, ERANGE is returned and BUF contains as much of the - beginning of the error string as fits into the buffer. */ + * size BUFLEN. This function is, in contrast to gpg_strerror, + * thread-safe if a thread-safe strerror_r() function is provided by + * the system. If the function succeeds, 0 is returned and BUF + * contains the string describing the error. If the buffer was not + * large enough, ERANGE is returned and BUF contains as much of the + * beginning of the error string as fits into the buffer. */ int gpgme_strerror_r (gpg_error_t err, char *buf, size_t buflen); /* Return a pointer to a string containing a description of the error - source in the error value ERR. */ + * source in the error value ERR. */ const char *gpgme_strsource (gpgme_error_t err); /* Retrieve the error code for the system error ERR. This returns - GPG_ERR_UNKNOWN_ERRNO if the system error is not mapped (report - this). */ + * GPG_ERR_UNKNOWN_ERRNO if the system error is not mapped (report + * this). */ gpgme_err_code_t gpgme_err_code_from_errno (int err); /* Retrieve the system error for the error code CODE. This returns 0 - if CODE is not a system error code. */ + * if CODE is not a system error code. */ int gpgme_err_code_to_errno (gpgme_err_code_t code); /* Retrieve the error code directly from the ERRNO variable. This - returns GPG_ERR_UNKNOWN_ERRNO if the system error is not mapped - (report this) and GPG_ERR_MISSING_ERRNO if ERRNO has the value 0. */ + * returns GPG_ERR_UNKNOWN_ERRNO if the system error is not mapped + * (report this) and GPG_ERR_MISSING_ERRNO if ERRNO has the value 0. */ gpgme_err_code_t gpgme_err_code_from_syserror (void); /* Set the ERRNO variable. This function is the preferred way to set - ERRNO due to peculiarities on WindowsCE. */ + * ERRNO due to peculiarities on WindowsCE. */ void gpgme_err_set_errno (int err); /* Return an error value with the error source SOURCE and the system - error ERR. FIXME: Should be inline. */ + * error ERR. FIXME: Should be inline. */ gpgme_error_t gpgme_err_make_from_errno (gpgme_err_source_t source, int err); -/* Return an error value with the system error ERR. FIXME: Should be inline. */ +/* Return an error value with the system error ERR. + * inline. */ gpgme_error_t gpgme_error_from_errno (int err); @@ -373,6 +376,8 @@ gpgme_protocol_t; #define GPGME_KEYLIST_MODE_EPHEMERAL 128 #define GPGME_KEYLIST_MODE_VALIDATE 256 +#define GPGME_KEYLIST_MODE_LOCATE (1|2) + typedef unsigned int gpgme_keylist_mode_t; @@ -417,7 +422,7 @@ struct _gpgme_sig_notation struct _gpgme_sig_notation *next; /* If NAME is a null pointer, then VALUE contains a policy URL - rather than a notation. */ + * rather than a notation. */ char *name; /* The value of the notation data. */ @@ -632,10 +637,10 @@ struct _gpgme_key_sig /* Same as in gpgme_signature_t. */ gpgme_error_t status; + /* Deprecated; use SIG_CLASS instead. */ #ifdef __cplusplus unsigned int _obsolete_class _GPGME_DEPRECATED(0,4); #else - /* Must be set to SIG_CLASS below. */ unsigned int class _GPGME_DEPRECATED_OUTSIDE_GPGME(0,4); #endif @@ -874,10 +879,10 @@ gpgme_error_t gpgme_set_protocol (gpgme_ctx_t ctx, gpgme_protocol_t proto); gpgme_protocol_t gpgme_get_protocol (gpgme_ctx_t ctx); /* Set the crypto protocol to be used by CTX to PROTO. - gpgme_set_protocol actually sets the backend engine. This sets the - crypto protocol used in engines that support more than one crypto - prococol (for example, an UISERVER can support OpenPGP and CMS). - This is reset to the default with gpgme_set_protocol. */ + * gpgme_set_protocol actually sets the backend engine. This sets the + * crypto protocol used in engines that support more than one crypto + * prococol (for example, an UISERVER can support OpenPGP and CMS). + * This is reset to the default with gpgme_set_protocol. */ gpgme_error_t gpgme_set_sub_protocol (gpgme_ctx_t ctx, gpgme_protocol_t proto); @@ -929,47 +934,47 @@ gpgme_error_t gpgme_set_pinentry_mode (gpgme_ctx_t ctx, gpgme_pinentry_mode_t gpgme_get_pinentry_mode (gpgme_ctx_t ctx); /* Set the passphrase callback function in CTX to CB. HOOK_VALUE is - passed as first argument to the passphrase callback function. */ + * passed as first argument to the passphrase callback function. */ void gpgme_set_passphrase_cb (gpgme_ctx_t ctx, gpgme_passphrase_cb_t cb, void *hook_value); /* Get the current passphrase callback function in *CB and the current - hook value in *HOOK_VALUE. */ + * hook value in *HOOK_VALUE. */ void gpgme_get_passphrase_cb (gpgme_ctx_t ctx, gpgme_passphrase_cb_t *cb, void **hook_value); /* Set the progress callback function in CTX to CB. HOOK_VALUE is - passed as first argument to the progress callback function. */ + * passed as first argument to the progress callback function. */ void gpgme_set_progress_cb (gpgme_ctx_t c, gpgme_progress_cb_t cb, void *hook_value); /* Get the current progress callback function in *CB and the current - hook value in *HOOK_VALUE. */ + * hook value in *HOOK_VALUE. */ void gpgme_get_progress_cb (gpgme_ctx_t ctx, gpgme_progress_cb_t *cb, void **hook_value); /* Set the status callback function in CTX to CB. HOOK_VALUE is - passed as first argument to the status callback function. */ + * passed as first argument to the status callback function. */ void gpgme_set_status_cb (gpgme_ctx_t c, gpgme_status_cb_t cb, void *hook_value); /* Get the current status callback function in *CB and the current - hook value in *HOOK_VALUE. */ + * hook value in *HOOK_VALUE. */ void gpgme_get_status_cb (gpgme_ctx_t ctx, gpgme_status_cb_t *cb, void **hook_value); /* This function sets the locale for the context CTX, or the default - locale if CTX is a null pointer. */ + * locale if CTX is a null pointer. */ gpgme_error_t gpgme_set_locale (gpgme_ctx_t ctx, int category, const char *value); /* Get the information about the configured engines. A pointer to the - first engine in the statically allocated linked list is returned. - The returned data is valid until the next gpgme_ctx_set_engine_info. */ + * first engine in the statically allocated linked list is returned. + * The returned data is valid until the next gpgme_ctx_set_engine_info. */ gpgme_engine_info_t gpgme_ctx_get_engine_info (gpgme_ctx_t ctx); /* Set the engine info for the context CTX, protocol PROTO, to the - file name FILE_NAME and the home directory HOME_DIR. */ + * file name FILE_NAME and the home directory HOME_DIR. */ gpgme_error_t gpgme_ctx_set_engine_info (gpgme_ctx_t ctx, gpgme_protocol_t proto, const char *file_name, @@ -991,10 +996,10 @@ gpgme_key_t gpgme_signers_enum (const gpgme_ctx_t ctx, int seq); void gpgme_sig_notation_clear (gpgme_ctx_t ctx); /* Add the human-readable notation data with name NAME and value VALUE - to the context CTX, using the flags FLAGS. If NAME is NULL, then - VALUE should be a policy URL. The flag - GPGME_SIG_NOTATION_HUMAN_READABLE is forced to be true for notation - data, and false for policy URLs. */ + * to the context CTX, using the flags FLAGS. If NAME is NULL, then + * VALUE should be a policy URL. The flag + * GPGME_SIG_NOTATION_HUMAN_READABLE is forced to be true for notation + * data, and false for policy URLs. */ gpgme_error_t gpgme_sig_notation_add (gpgme_ctx_t ctx, const char *name, const char *value, gpgme_sig_notation_flags_t flags); @@ -1018,17 +1023,17 @@ const char *gpgme_get_sender (gpgme_ctx_t ctx); typedef gpgme_error_t (*gpgme_io_cb_t) (void *data, int fd); /* The type of a function that can register FNC as the I/O callback - function for the file descriptor FD with direction dir (0: for writing, - 1: for reading). FNC_DATA should be passed as DATA to FNC. The - function should return a TAG suitable for the corresponding - gpgme_remove_io_cb_t, and an error value. */ + * function for the file descriptor FD with direction dir (0: for writing, + * 1: for reading). FNC_DATA should be passed as DATA to FNC. The + * function should return a TAG suitable for the corresponding + * gpgme_remove_io_cb_t, and an error value. */ typedef gpgme_error_t (*gpgme_register_io_cb_t) (void *data, int fd, int dir, gpgme_io_cb_t fnc, void *fnc_data, void **tag); /* The type of a function that can remove a previously registered I/O - callback function given TAG as returned by the register - function. */ + * callback function given TAG as returned by the register + * function. */ typedef void (*gpgme_remove_io_cb_t) (void *tag); typedef enum @@ -1043,7 +1048,7 @@ gpgme_event_io_t; struct gpgme_io_event_done_data { /* A fatal IPC error or an operational error in state-less - protocols. */ + * protocols. */ gpgme_error_t err; /* An operational errors in session-based protocols. */ @@ -1052,7 +1057,7 @@ struct gpgme_io_event_done_data typedef struct gpgme_io_event_done_data *gpgme_io_event_done_data_t; /* The type of a function that is called when a context finished an - operation. */ + * operation. */ typedef void (*gpgme_event_io_cb_t) (void *data, gpgme_event_io_t type, void *type_data); @@ -1073,13 +1078,13 @@ void gpgme_set_io_cbs (gpgme_ctx_t ctx, gpgme_io_cbs_t io_cbs); void gpgme_get_io_cbs (gpgme_ctx_t ctx, gpgme_io_cbs_t io_cbs); /* Wrappers around the internal I/O functions for use with - gpgme_passphrase_cb_t and gpgme_interact_cb_t. */ + * gpgme_passphrase_cb_t and gpgme_interact_cb_t. */ @API__SSIZE_T@ gpgme_io_read (int fd, void *buffer, size_t count); @API__SSIZE_T@ gpgme_io_write (int fd, const void *buffer, size_t count); int gpgme_io_writen (int fd, const void *buffer, size_t count); /* Process the pending operation and, if HANG is non-zero, wait for - the pending operation to finish. */ + * the pending operation to finish. */ gpgme_ctx_t gpgme_wait (gpgme_ctx_t ctx, gpgme_error_t *status, int hang); gpgme_ctx_t gpgme_wait_ext (gpgme_ctx_t ctx, gpgme_error_t *status, @@ -1098,21 +1103,21 @@ gpgme_error_t gpgme_cancel_async (gpgme_ctx_t ctx); */ /* Read up to SIZE bytes into buffer BUFFER from the data object with - the handle HANDLE. Return the number of characters read, 0 on EOF - and -1 on error. If an error occurs, errno is set. */ + * the handle HANDLE. Return the number of characters read, 0 on EOF + * and -1 on error. If an error occurs, errno is set. */ typedef @API__SSIZE_T@ (*gpgme_data_read_cb_t) (void *handle, void *buffer, size_t size); /* Write up to SIZE bytes from buffer BUFFER to the data object with - the handle HANDLE. Return the number of characters written, or -1 - on error. If an error occurs, errno is set. */ + * the handle HANDLE. Return the number of characters written, or -1 + * on error. If an error occurs, errno is set. */ typedef @API__SSIZE_T@ (*gpgme_data_write_cb_t) (void *handle, const void *buffer, size_t size); /* Set the current position from where the next read or write starts - in the data object with the handle HANDLE to OFFSET, relativ to - WHENCE. Returns the new offset in bytes from the beginning of the - data object. */ + * in the data object with the handle HANDLE to OFFSET, relativ to + * WHENCE. Returns the new offset in bytes from the beginning of the + * data object. */ typedef @API__OFF_T@ (*gpgme_data_seek_cb_t) (void *handle, @API__OFF_T@ offset, int whence); @@ -1129,19 +1134,19 @@ struct gpgme_data_cbs typedef struct gpgme_data_cbs *gpgme_data_cbs_t; /* Read up to SIZE bytes into buffer BUFFER from the data object with - the handle DH. Return the number of characters read, 0 on EOF and - -1 on error. If an error occurs, errno is set. */ + * the handle DH. Return the number of characters read, 0 on EOF and + * -1 on error. If an error occurs, errno is set. */ @API__SSIZE_T@ gpgme_data_read (gpgme_data_t dh, void *buffer, size_t size); /* Write up to SIZE bytes from buffer BUFFER to the data object with - the handle DH. Return the number of characters written, or -1 on - error. If an error occurs, errno is set. */ + * the handle DH. Return the number of characters written, or -1 on + * error. If an error occurs, errno is set. */ @API__SSIZE_T@ gpgme_data_write (gpgme_data_t dh, const void *buffer, size_t size); /* Set the current position from where the next read or write starts - in the data object with the handle DH to OFFSET, relativ to WHENCE. - Returns the new offset in bytes from the beginning of the data - object. */ + * in the data object with the handle DH to OFFSET, relativ to WHENCE. + * Returns the new offset in bytes from the beginning of the data + * object. */ @API__OFF_T@ gpgme_data_seek (gpgme_data_t dh, @API__OFF_T@ offset, int whence); /* Create a new data buffer and return it in R_DH. */ @@ -1151,19 +1156,19 @@ gpgme_error_t gpgme_data_new (gpgme_data_t *r_dh); void gpgme_data_release (gpgme_data_t dh); /* Create a new data buffer filled with SIZE bytes starting from - BUFFER. If COPY is zero, copying is delayed until necessary, and - the data is taken from the original location when needed. */ + * BUFFER. If COPY is zero, copying is delayed until necessary, and + * the data is taken from the original location when needed. */ gpgme_error_t gpgme_data_new_from_mem (gpgme_data_t *r_dh, const char *buffer, size_t size, int copy); /* Destroy the data buffer DH and return a pointer to its content. - The memory has be to released with gpgme_free() by the user. It's - size is returned in R_LEN. */ + * The memory has be to released with gpgme_free() by the user. It's + * size is returned in R_LEN. */ char *gpgme_data_release_and_get_mem (gpgme_data_t dh, size_t *r_len); /* Release the memory returned by gpgme_data_release_and_get_mem() and - some other functions. */ + * some other functions. */ void gpgme_free (void *buffer); gpgme_error_t gpgme_data_new_from_cbs (gpgme_data_t *dh, @@ -1182,11 +1187,11 @@ gpgme_error_t gpgme_data_set_encoding (gpgme_data_t dh, gpgme_data_encoding_t enc); /* Get the file name associated with the data object with handle DH, or - NULL if there is none. */ + * NULL if there is none. */ char *gpgme_data_get_file_name (gpgme_data_t dh); /* Set the file name associated with the data object with handle DH to - FILE_NAME. */ + * FILE_NAME. */ gpgme_error_t gpgme_data_set_file_name (gpgme_data_t dh, const char *file_name); @@ -1199,15 +1204,15 @@ gpgme_data_type_t gpgme_data_identify (gpgme_data_t dh, int reserved); /* Create a new data buffer filled with the content of file FNAME. - COPY must be non-zero. For delayed read, please use - gpgme_data_new_from_fd or gpgme_data_new_from_stream instead. */ + * COPY must be non-zero. For delayed read, please use + * gpgme_data_new_from_fd or gpgme_data_new_from_stream instead. */ gpgme_error_t gpgme_data_new_from_file (gpgme_data_t *r_dh, const char *fname, int copy); /* Create a new data buffer filled with LENGTH bytes starting from - OFFSET within the file FNAME or stream FP (exactly one must be - non-zero). */ + * OFFSET within the file FNAME or stream FP (exactly one must be + * non-zero). */ gpgme_error_t gpgme_data_new_from_filepart (gpgme_data_t *r_dh, const char *fname, FILE *fp, @API__OFF_T@ offset, size_t length); @@ -1222,7 +1227,7 @@ gpgme_error_t gpgme_data_rewind (gpgme_data_t dh); */ /* Get the key with the fingerprint FPR from the crypto backend. If - SECRET is true, get the secret key. */ + * SECRET is true, get the secret key. */ gpgme_error_t gpgme_get_key (gpgme_ctx_t ctx, const char *fpr, gpgme_key_t *r_key, int secret); @@ -1233,7 +1238,7 @@ gpgme_error_t gpgme_key_from_uid (gpgme_key_t *key, const char *name); void gpgme_key_ref (gpgme_key_t key); /* Release a reference to KEY. If this was the last one the key is - destroyed. */ + * destroyed. */ void gpgme_key_unref (gpgme_key_t key); void gpgme_key_release (gpgme_key_t key); @@ -1266,22 +1271,35 @@ typedef enum GPGME_ENCRYPT_NO_COMPRESS = 16, GPGME_ENCRYPT_SYMMETRIC = 32, GPGME_ENCRYPT_THROW_KEYIDS = 64, - GPGME_ENCRYPT_WRAP = 128 + GPGME_ENCRYPT_WRAP = 128, + GPGME_ENCRYPT_WANT_ADDRESS = 256 } gpgme_encrypt_flags_t; /* Encrypt plaintext PLAIN within CTX for the recipients RECP and - store the resulting ciphertext in CIPHER. */ + * store the resulting ciphertext in CIPHER. */ gpgme_error_t gpgme_op_encrypt_start (gpgme_ctx_t ctx, gpgme_key_t recp[], gpgme_encrypt_flags_t flags, - gpgme_data_t plain, gpgme_data_t cipher); + gpgme_data_t plain, + gpgme_data_t cipher); gpgme_error_t gpgme_op_encrypt (gpgme_ctx_t ctx, gpgme_key_t recp[], gpgme_encrypt_flags_t flags, - gpgme_data_t plain, gpgme_data_t cipher); + gpgme_data_t plain, + gpgme_data_t cipher); +gpgme_error_t gpgme_op_encrypt_ext_start (gpgme_ctx_t ctx, gpgme_key_t recp[], + const char *recpstring, + gpgme_encrypt_flags_t flags, + gpgme_data_t plain, + gpgme_data_t cipher); +gpgme_error_t gpgme_op_encrypt_ext (gpgme_ctx_t ctx, gpgme_key_t recp[], + const char *recpstring, + gpgme_encrypt_flags_t flags, + gpgme_data_t plain, + gpgme_data_t cipher); /* Encrypt plaintext PLAIN within CTX for the recipients RECP and - store the resulting ciphertext in CIPHER. Also sign the ciphertext - with the signers in CTX. */ + * store the resulting ciphertext in CIPHER. Also sign the ciphertext + * with the signers in CTX. */ gpgme_error_t gpgme_op_encrypt_sign_start (gpgme_ctx_t ctx, gpgme_key_t recp[], gpgme_encrypt_flags_t flags, @@ -1289,7 +1307,19 @@ gpgme_error_t gpgme_op_encrypt_sign_start (gpgme_ctx_t ctx, gpgme_data_t cipher); gpgme_error_t gpgme_op_encrypt_sign (gpgme_ctx_t ctx, gpgme_key_t recp[], gpgme_encrypt_flags_t flags, - gpgme_data_t plain, gpgme_data_t cipher); + gpgme_data_t plain, + gpgme_data_t cipher); +gpgme_error_t gpgme_op_encrypt_sign_ext_start (gpgme_ctx_t ctx, + gpgme_key_t recp[], + const char *recpstring, + gpgme_encrypt_flags_t flags, + gpgme_data_t plain, + gpgme_data_t cipher); +gpgme_error_t gpgme_op_encrypt_sign_ext (gpgme_ctx_t ctx, gpgme_key_t recp[], + const char *recpstring, + gpgme_encrypt_flags_t flags, + gpgme_data_t plain, + gpgme_data_t cipher); /* @@ -1317,6 +1347,7 @@ struct _gpgme_recipient }; typedef struct _gpgme_recipient *gpgme_recipient_t; + /* An object to return results from a decryption operation. * This structure shall be considered read-only and an application * must not allocate such a structure on its own. */ @@ -1331,24 +1362,33 @@ struct _gpgme_op_decrypt_result * mode. */ unsigned int is_de_vs : 1; + /* The message claims that the content is a MIME object. */ + unsigned int is_mime : 1; + /* Internal to GPGME, do not use. */ - int _unused : 30; + int _unused : 29; gpgme_recipient_t recipients; /* The original file name of the plaintext message, if - available. */ + * available. */ char *file_name; /* A textual representation of the session key used to decrypt the * message, if available */ char *session_key; + + /* A string with the symmetric encryption algorithm and mode using + * the format "<algo>.<mode>". */ + char *symkey_algo; }; typedef struct _gpgme_op_decrypt_result *gpgme_decrypt_result_t; + /* Retrieve a pointer to the result of the decrypt operation. */ gpgme_decrypt_result_t gpgme_op_decrypt_result (gpgme_ctx_t ctx); + /* The valid decryption flags. */ typedef enum { @@ -1357,15 +1397,16 @@ typedef enum } gpgme_decrypt_flags_t; + /* Decrypt ciphertext CIPHER within CTX and store the resulting - plaintext in PLAIN. */ + * plaintext in PLAIN. */ gpgme_error_t gpgme_op_decrypt_start (gpgme_ctx_t ctx, gpgme_data_t cipher, gpgme_data_t plain); gpgme_error_t gpgme_op_decrypt (gpgme_ctx_t ctx, gpgme_data_t cipher, gpgme_data_t plain); /* Decrypt ciphertext CIPHER and make a signature verification within - CTX and store the resulting plaintext in PLAIN. */ + * CTX and store the resulting plaintext in PLAIN. */ gpgme_error_t gpgme_op_decrypt_verify_start (gpgme_ctx_t ctx, gpgme_data_t cipher, gpgme_data_t plain); @@ -1407,7 +1448,7 @@ struct _gpgme_new_signature gpgme_hash_algo_t hash_algo; /* Internal to GPGME, do not use. Must be set to the same value as - CLASS below. */ + * CLASS below. */ unsigned long _obsolete_class; /* Signature creation time. */ @@ -1416,10 +1457,10 @@ struct _gpgme_new_signature /* The fingerprint of the signature. */ char *fpr; + /* Deprecated; use SIG_CLASS instead. */ #ifdef __cplusplus unsigned int _obsolete_class_2; #else - /* Must be set to SIG_CLASS below. */ unsigned int class _GPGME_DEPRECATED_OUTSIDE_GPGME(0,4); #endif @@ -1440,6 +1481,7 @@ struct _gpgme_op_sign_result }; typedef struct _gpgme_op_sign_result *gpgme_sign_result_t; + /* Retrieve a pointer to the result of the signing operation. */ gpgme_sign_result_t gpgme_op_sign_result (gpgme_ctx_t ctx); @@ -1533,6 +1575,7 @@ struct _gpgme_signature }; typedef struct _gpgme_signature *gpgme_signature_t; + /* An object to return the results of a verify operation. * This structure shall be considered read-only and an application * must not allocate such a structure on its own. */ @@ -1543,9 +1586,16 @@ struct _gpgme_op_verify_result /* The original file name of the plaintext message, if available. */ char *file_name; + + /* The message claims that the content is a MIME object. */ + unsigned int is_mime : 1; + + /* Internal to GPGME; do not use. */ + unsigned int _unused : 31; }; typedef struct _gpgme_op_verify_result *gpgme_verify_result_t; + /* Retrieve a pointer to the result of the verify operation. */ gpgme_verify_result_t gpgme_op_verify_result (gpgme_ctx_t ctx); @@ -1590,6 +1640,7 @@ struct _gpgme_import_status }; typedef struct _gpgme_import_status *gpgme_import_status_t; + /* Import result object. * This structure shall be considered read-only and an application * must not allocate such a structure on its own. */ @@ -1639,9 +1690,13 @@ struct _gpgme_op_import_result /* List of keys for which an import was attempted. */ gpgme_import_status_t imports; + + /* Number of v3 keys skipped. */ + int skipped_v3_keys; }; typedef struct _gpgme_op_import_result *gpgme_import_result_t; + /* Retrieve a pointer to the result of the import operation. */ gpgme_import_result_t gpgme_op_import_result (gpgme_ctx_t ctx); @@ -1699,6 +1754,7 @@ gpgme_error_t gpgme_op_export_keys (gpgme_ctx_t ctx, #define GPGME_CREATE_FORCE (1 << 12) /* Force creation. */ #define GPGME_CREATE_NOEXPIRE (1 << 13) /* Create w/o expiration. */ + /* An object to return result from a key generation. * This structure shall be considered read-only and an application * must not allocate such a structure on its own. */ @@ -1729,9 +1785,10 @@ struct _gpgme_op_genkey_result }; typedef struct _gpgme_op_genkey_result *gpgme_genkey_result_t; + /* Generate a new keypair and add it to the keyring. PUBKEY and - SECKEY should be null for now. PARMS specifies what keys should be - generated. */ + * SECKEY should be null for now. PARMS specifies what keys should be + * generated. */ gpgme_error_t gpgme_op_genkey_start (gpgme_ctx_t ctx, const char *parms, gpgme_data_t pubkey, gpgme_data_t seckey); gpgme_error_t gpgme_op_genkey (gpgme_ctx_t ctx, const char *parms, @@ -1797,7 +1854,7 @@ gpgme_genkey_result_t gpgme_op_genkey_result (gpgme_ctx_t ctx); /* Delete KEY from the keyring. If ALLOW_SECRET is non-zero, secret - keys are also deleted. */ + * keys are also deleted. */ gpgme_error_t gpgme_op_delete_start (gpgme_ctx_t ctx, const gpgme_key_t key, int allow_secret); gpgme_error_t gpgme_op_delete (gpgme_ctx_t ctx, const gpgme_key_t key, @@ -1916,7 +1973,7 @@ gpgme_error_t gpgme_op_keylist_end (gpgme_ctx_t ctx); */ /* Change the passphrase for KEY. FLAGS is reserved for future use - and must be passed as 0. */ + * and must be passed as 0. */ gpgme_error_t gpgme_op_passwd_start (gpgme_ctx_t ctx, gpgme_key_t key, unsigned int flags); gpgme_error_t gpgme_op_passwd (gpgme_ctx_t ctx, gpgme_key_t key, @@ -1981,7 +2038,7 @@ gpgme_error_t gpgme_op_trustlist_end (gpgme_ctx_t ctx); void gpgme_trust_item_ref (gpgme_trust_item_t item); /* Release a reference to ITEM. If this was the last one the trust - item is destroyed. */ + * item is destroyed. */ void gpgme_trust_item_unref (gpgme_trust_item_t item); @@ -2007,11 +2064,12 @@ gpgme_error_t gpgme_op_getauditlog (gpgme_ctx_t ctx, gpgme_data_t output, /* Flags for the spawn operations. */ #define GPGME_SPAWN_DETACHED 1 #define GPGME_SPAWN_ALLOW_SET_FG 2 +#define GPGME_SPAWN_SHOW_WINDOW 4 /* Run the command FILE with the arguments in ARGV. Connect stdin to - DATAIN, stdout to DATAOUT, and STDERR to DATAERR. If one the data - streams is NULL, connect to /dev/null instead. */ + * DATAIN, stdout to DATAOUT, and STDERR to DATAERR. If one the data + * streams is NULL, connect to /dev/null instead. */ gpgme_error_t gpgme_op_spawn_start (gpgme_ctx_t ctx, const char *file, const char *argv[], gpgme_data_t datain, @@ -2027,6 +2085,7 @@ gpgme_error_t gpgme_op_spawn (gpgme_ctx_t ctx, /* * Low-level Assuan protocol access. */ + typedef gpgme_error_t (*gpgme_assuan_data_cb_t) (void *opaque, const void *data, size_t datalen); @@ -2038,7 +2097,7 @@ typedef gpgme_error_t (*gpgme_assuan_status_cb_t) (void *opaque, const char *status, const char *args); /* Send the Assuan COMMAND and return results via the callbacks. - Asynchronous variant. */ + * Asynchronous variant. */ gpgme_error_t gpgme_op_assuan_transact_start (gpgme_ctx_t ctx, const char *command, gpgme_assuan_data_cb_t data_cb, @@ -2049,7 +2108,7 @@ gpgme_error_t gpgme_op_assuan_transact_start (gpgme_ctx_t ctx, void *stat_cb_value); /* Send the Assuan COMMAND and return results via the callbacks. - Synchronous variant. */ + * Synchronous variant. */ gpgme_error_t gpgme_op_assuan_transact_ext (gpgme_ctx_t ctx, const char *command, gpgme_assuan_data_cb_t data_cb, @@ -2077,8 +2136,8 @@ typedef struct _gpgme_op_vfs_mount_result *gpgme_vfs_mount_result_t; gpgme_vfs_mount_result_t gpgme_op_vfs_mount_result (gpgme_ctx_t ctx); /* The container is automatically unmounted when the context is reset - or destroyed. Transmission errors are returned directly, - operational errors are returned in OP_ERR. */ + * or destroyed. Transmission errors are returned directly, + * operational errors are returned in OP_ERR. */ gpgme_error_t gpgme_op_vfs_mount (gpgme_ctx_t ctx, const char *container_file, const char *mount_dir, unsigned int flags, gpgme_error_t *op_err); @@ -2093,8 +2152,8 @@ gpgme_error_t gpgme_op_vfs_create (gpgme_ctx_t ctx, gpgme_key_t recp[], */ /* The expert level at which a configuration option or group of - options should be displayed. See the gpgconf(1) documentation for - more details. */ + * options should be displayed. See the gpgconf(1) documentation for + * more details. */ typedef enum { GPGME_CONF_BASIC = 0, @@ -2107,7 +2166,7 @@ gpgme_conf_level_t; /* The data type of a configuration option argument. See the gpgconf(1) - documentation for more details. */ + * documentation for more details. */ typedef enum { /* Basic types. */ @@ -2131,7 +2190,7 @@ gpgme_conf_type_t; /* This represents a single argument for a configuration option. - Which of the members of value is used depends on the ALT_TYPE. */ + * Which of the members of value is used depends on the ALT_TYPE. */ typedef struct gpgme_conf_arg { struct gpgme_conf_arg *next; @@ -2148,7 +2207,7 @@ typedef struct gpgme_conf_arg /* The flags of a configuration option. See the gpgconf - documentation for details. */ + * documentation for details. */ #define GPGME_CONF_GROUP (1 << 0) #define GPGME_CONF_OPTIONAL (1 << 1) #define GPGME_CONF_LIST (1 << 2) @@ -2160,7 +2219,7 @@ typedef struct gpgme_conf_arg /* The representation of a single configuration option. See the - gpg-conf documentation for details. */ + * gpg-conf documentation for details. */ typedef struct gpgme_conf_opt { struct gpgme_conf_opt *next; @@ -2205,7 +2264,7 @@ typedef struct gpgme_conf_opt /* The representation of a component that can be configured. See the - gpg-conf documentation for details. */ + * gpg-conf documentation for details. */ typedef struct gpgme_conf_comp { struct gpgme_conf_comp *next; @@ -2228,9 +2287,9 @@ typedef struct gpgme_conf_comp /* Allocate a new gpgme_conf_arg_t. If VALUE is NULL, a "no arg - default" is prepared. If type is a string type, VALUE should point - to the string. Else, it should point to an unsigned or signed - integer respectively. */ + * default" is prepared. If type is a string type, VALUE should point + * to the string. Else, it should point to an unsigned or signed + * integer respectively. */ gpgme_error_t gpgme_conf_arg_new (gpgme_conf_arg_t *arg_p, gpgme_conf_type_t type, const void *value); @@ -2238,10 +2297,10 @@ gpgme_error_t gpgme_conf_arg_new (gpgme_conf_arg_t *arg_p, void gpgme_conf_arg_release (gpgme_conf_arg_t arg, gpgme_conf_type_t type); /* Register a change for the value of OPT to ARG. If RESET is 1 (do - not use any values but 0 or 1), ARG is ignored and the option is - not changed (reverting a previous change). Otherwise, if ARG is - NULL, the option is cleared or reset to its default. The change - is done with gpgconf's --runtime option to immediately take effect. */ + * not use any values but 0 or 1), ARG is ignored and the option is + * not changed (reverting a previous change). Otherwise, if ARG is + * NULL, the option is cleared or reset to its default. The change + * is done with gpgconf's --runtime option to immediately take effect. */ gpgme_error_t gpgme_conf_opt_change (gpgme_conf_opt_t opt, int reset, gpgme_conf_arg_t arg); @@ -2332,16 +2391,17 @@ gpgme_query_swdb_result_t gpgme_op_query_swdb_result (gpgme_ctx_t ctx); int gpgme_set_global_flag (const char *name, const char *value); /* Check that the library fulfills the version requirement. Note: - This is here only for the case where a user takes a pointer from - the old version of this function. The new version and macro for - run-time checks are below. */ + * This is here only for the case where a user takes a pointer from + * the old version of this function. The new version and macro for + * run-time checks are below. */ const char *gpgme_check_version (const char *req_version); -/* Check that the library fulfills the version requirement and check - for struct layout mismatch involving bitfields. */ +/* Do not call this directly; use the macro below. */ const char *gpgme_check_version_internal (const char *req_version, size_t offset_sig_validity); +/* Check that the library fulfills the version requirement and check + * for struct layout mismatch involving bitfields. */ #define gpgme_check_version(req_version) \ gpgme_check_version_internal (req_version, \ offsetof (struct _gpgme_signature, validity)) @@ -2350,19 +2410,19 @@ const char *gpgme_check_version_internal (const char *req_version, const char *gpgme_get_dirinfo (const char *what); /* 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. */ + * 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 *engine_info); /* Set the default engine info for the protocol PROTO to the file name - FILE_NAME and the home directory HOME_DIR. */ + * 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); /* Verify that the engine implementing PROTO is installed and - available. */ + * available. */ gpgme_error_t gpgme_engine_check_version (gpgme_protocol_t proto); @@ -2371,15 +2431,15 @@ void gpgme_result_ref (void *result); void gpgme_result_unref (void *result); /* Return a public key algorithm string (e.g. "rsa2048"). Caller must - free using gpgme_free. */ + * free using gpgme_free. */ char *gpgme_pubkey_algo_string (gpgme_subkey_t subkey); /* Return a statically allocated string with the name of the public - key algorithm ALGO, or NULL if that name is not known. */ + * key algorithm ALGO, or NULL if that name is not known. */ const char *gpgme_pubkey_algo_name (gpgme_pubkey_algo_t algo); /* Return a statically allocated string with the name of the hash - algorithm ALGO, or NULL if that name is not known. */ + * algorithm ALGO, or NULL if that name is not known. */ const char *gpgme_hash_algo_name (gpgme_hash_algo_t algo); /* Return the addr-spec from a user id. Caller must free the result @@ -2531,7 +2591,7 @@ gpgme_error_t gpgme_op_card_edit (gpgme_ctx_t ctx, gpgme_key_t key, _GPGME_DEPRECATED(1,7); /* The possible signature stati. Deprecated, use error value in sig - status. */ + * status. */ typedef enum { GPGME_SIG_STAT_NONE = 0, @@ -2548,7 +2608,7 @@ _gpgme_sig_stat_t; typedef _gpgme_sig_stat_t gpgme_sig_stat_t _GPGME_DEPRECATED(0,4); /* The available key and signature attributes. Deprecated, use the - individual result structures instead. */ + * individual result structures instead. */ typedef enum { GPGME_ATTR_KEYID = 1, @@ -2588,18 +2648,18 @@ _gpgme_attr_t; typedef _gpgme_attr_t gpgme_attr_t _GPGME_DEPRECATED(0,4); /* Retrieve the signature status of signature IDX in CTX after a - successful verify operation in R_STAT (if non-null). The creation - time stamp of the signature is returned in R_CREATED (if non-null). - The function returns a string containing the fingerprint. - Deprecated, use verify result directly. */ + * successful verify operation in R_STAT (if non-null). The creation + * time stamp of the signature is returned in R_CREATED (if non-null). + * The function returns a string containing the fingerprint. + * Deprecated, use verify result directly. */ const char *gpgme_get_sig_status (gpgme_ctx_t ctx, int idx, _gpgme_sig_stat_t *r_stat, time_t *r_created) _GPGME_DEPRECATED(0,4); /* Retrieve certain attributes of a signature. IDX is the index - number of the signature after a successful verify operation. WHAT - is an attribute where GPGME_ATTR_EXPIRE is probably the most useful - one. WHATIDX is to be passed as 0 for most attributes . */ + * number of the signature after a successful verify operation. WHAT + * is an attribute where GPGME_ATTR_EXPIRE is probably the most useful + * one. WHATIDX is to be passed as 0 for most attributes . */ unsigned long gpgme_get_sig_ulong_attr (gpgme_ctx_t c, int idx, _gpgme_attr_t what, int whatidx) _GPGME_DEPRECATED(0,4); @@ -2609,13 +2669,13 @@ const char *gpgme_get_sig_string_attr (gpgme_ctx_t c, int idx, /* Get the key used to create signature IDX in CTX and return it in - R_KEY. */ + * R_KEY. */ gpgme_error_t gpgme_get_sig_key (gpgme_ctx_t ctx, int idx, gpgme_key_t *r_key) _GPGME_DEPRECATED(0,4); /* Create a new data buffer which retrieves the data from the callback - function READ_CB. Deprecated, please use gpgme_data_new_from_cbs - instead. */ + * function READ_CB. Deprecated, please use gpgme_data_new_from_cbs + * instead. */ gpgme_error_t gpgme_data_new_with_read_cb (gpgme_data_t *r_dh, int (*read_cb) (void*,char *, size_t,size_t*), @@ -2623,34 +2683,34 @@ gpgme_error_t gpgme_data_new_with_read_cb (gpgme_data_t *r_dh, _GPGME_DEPRECATED(0,4); /* Return the value of the attribute WHAT of KEY, which has to be - representable by a string. IDX specifies the sub key or user ID - for attributes related to sub keys or user IDs. Deprecated, use - key structure directly instead. */ + * representable by a string. IDX specifies the sub key or user ID + * for attributes related to sub keys or user IDs. Deprecated, use + * key structure directly instead. */ const char *gpgme_key_get_string_attr (gpgme_key_t key, _gpgme_attr_t what, const void *reserved, int idx) _GPGME_DEPRECATED(0,4); /* Return the value of the attribute WHAT of KEY, which has to be - representable by an unsigned integer. IDX specifies the sub key or - user ID for attributes related to sub keys or user IDs. - Deprecated, use key structure directly instead. */ + * representable by an unsigned integer. IDX specifies the sub key or + * user ID for attributes related to sub keys or user IDs. + * Deprecated, use key structure directly instead. */ unsigned long gpgme_key_get_ulong_attr (gpgme_key_t key, _gpgme_attr_t what, const void *reserved, int idx) _GPGME_DEPRECATED(0,4); /* Return the value of the attribute WHAT of a signature on user ID - UID_IDX in KEY, which has to be representable by a string. IDX - specifies the signature. Deprecated, use key structure directly - instead. */ + * UID_IDX in KEY, which has to be representable by a string. IDX + * specifies the signature. Deprecated, use key structure directly + * instead. */ const char *gpgme_key_sig_get_string_attr (gpgme_key_t key, int uid_idx, _gpgme_attr_t what, const void *reserved, int idx) _GPGME_DEPRECATED(0,4); /* Return the value of the attribute WHAT of a signature on user ID - UID_IDX in KEY, which has to be representable by an unsigned - integer string. IDX specifies the signature. Deprecated, use key - structure directly instead. */ + * UID_IDX in KEY, which has to be representable by an unsigned + * integer string. IDX specifies the signature. Deprecated, use key + * structure directly instead. */ unsigned long gpgme_key_sig_get_ulong_attr (gpgme_key_t key, int uid_idx, _gpgme_attr_t what, const void *reserved, int idx) @@ -2661,21 +2721,21 @@ gpgme_error_t gpgme_op_import_ext (gpgme_ctx_t ctx, gpgme_data_t keydata, int *nr) _GPGME_DEPRECATED(0,4); /* Release the trust item ITEM. Deprecated, use - gpgme_trust_item_unref. */ + * gpgme_trust_item_unref. */ void gpgme_trust_item_release (gpgme_trust_item_t item) _GPGME_DEPRECATED(0,4); /* Return the value of the attribute WHAT of ITEM, which has to be - representable by a string. Deprecated, use trust item structure - directly. */ + * representable by a string. Deprecated, use trust item structure + * directly. */ const char *gpgme_trust_item_get_string_attr (gpgme_trust_item_t item, _gpgme_attr_t what, const void *reserved, int idx) _GPGME_DEPRECATED(0,4); /* Return the value of the attribute WHAT of KEY, which has to be - representable by an integer. IDX specifies a running index if the - attribute appears more than once in the key. Deprecated, use trust - item structure directly. */ + * representable by an integer. IDX specifies a running index if the + * attribute appears more than once in the key. Deprecated, use trust + * item structure directly. */ int gpgme_trust_item_get_int_attr (gpgme_trust_item_t item, _gpgme_attr_t what, const void *reserved, int idx) _GPGME_DEPRECATED(0,4); diff --git a/src/import.c b/src/import.c index 386ca72..f0d9d9f 100644 --- a/src/import.c +++ b/src/import.c @@ -94,8 +94,9 @@ gpgme_op_import_result (gpgme_ctx_t ctx) TRACE_LOG3 ("%i secret keys, %i imported, %i unchanged", opd->result.secret_read, opd->result.secret_imported, opd->result.secret_unchanged); - TRACE_LOG2 ("%i skipped new keys, %i not imported", - opd->result.skipped_new_keys, opd->result.not_imported); + TRACE_LOG3 ("%i skipped new keys, %i not imported, %i v3 skipped", + opd->result.skipped_new_keys, opd->result.not_imported, + opd->result.skipped_v3_keys); impstat = opd->result.imports; i = 0; @@ -212,6 +213,10 @@ parse_import_res (char *args, gpgme_import_result_t result) PARSE_NEXT (result->secret_unchanged); PARSE_NEXT (result->skipped_new_keys); PARSE_NEXT (result->not_imported); + if (args && *args) + { + PARSE_NEXT (result->skipped_v3_keys); + } return 0; } diff --git a/src/keylist.c b/src/keylist.c index 24a9b0b..9c5bd4e 100644 --- a/src/keylist.c +++ b/src/keylist.c @@ -1261,7 +1261,7 @@ gpgme_get_key (gpgme_ctx_t ctx, const char *fpr, gpgme_key_t *r_key, { gpgme_ctx_t listctx; gpgme_error_t err; - gpgme_key_t key; + gpgme_key_t result, key; TRACE_BEG2 (DEBUG_CTX, "gpgme_get_key", ctx, "fpr=%s, secret=%i", fpr, secret); @@ -1269,6 +1269,8 @@ gpgme_get_key (gpgme_ctx_t ctx, const char *fpr, gpgme_key_t *r_key, if (!ctx || !r_key || !fpr) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); + *r_key = NULL; + if (strlen (fpr) < 8) /* We have at least a key ID. */ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); @@ -1295,7 +1297,7 @@ gpgme_get_key (gpgme_ctx_t ctx, const char *fpr, gpgme_key_t *r_key, err = gpgme_op_keylist_start (listctx, fpr, secret); if (!err) - err = gpgme_op_keylist_next (listctx, r_key); + err = gpgme_op_keylist_next (listctx, &result); if (!err) { try_next_key: @@ -1305,9 +1307,9 @@ gpgme_get_key (gpgme_ctx_t ctx, const char *fpr, gpgme_key_t *r_key, else { if (!err - && *r_key && (*r_key)->subkeys && (*r_key)->subkeys->fpr + && result && result->subkeys && result->subkeys->fpr && key && key->subkeys && key->subkeys->fpr - && !strcmp ((*r_key)->subkeys->fpr, key->subkeys->fpr)) + && !strcmp (result->subkeys->fpr, key->subkeys->fpr)) { /* The fingerprint is identical. We assume that this is the same key and don't mark it as an ambiguous. This @@ -1323,12 +1325,14 @@ gpgme_get_key (gpgme_ctx_t ctx, const char *fpr, gpgme_key_t *r_key, gpgme_key_unref (key); err = gpg_error (GPG_ERR_AMBIGUOUS_NAME); } - gpgme_key_unref (*r_key); + gpgme_key_unref (result); + result = NULL; } } gpgme_release (listctx); if (! err) { + *r_key = result; TRACE_LOG2 ("key=%p (%s)", *r_key, ((*r_key)->subkeys && (*r_key)->subkeys->fpr) ? (*r_key)->subkeys->fpr : "invalid"); diff --git a/src/libgpgme.vers b/src/libgpgme.vers index a95befb..b49c86d 100644 --- a/src/libgpgme.vers +++ b/src/libgpgme.vers @@ -215,10 +215,15 @@ GPGME_1.0 { gpgme_op_edit; gpgme_op_edit_start; gpgme_op_encrypt; - gpgme_op_encrypt_result; + gpgme_op_encrypt_start; + gpgme_op_encrypt_ext; + gpgme_op_encrypt_ext_start; gpgme_op_encrypt_sign; + gpgme_op_encrypt_sign_ext; gpgme_op_encrypt_sign_start; - gpgme_op_encrypt_start; + gpgme_op_encrypt_sign_ext_start; + gpgme_op_encrypt_result; + gpgme_op_export; gpgme_op_export_ext; gpgme_op_export_ext_start; diff --git a/src/op-support.c b/src/op-support.c index 817c569..03f274c 100644 --- a/src/op-support.c +++ b/src/op-support.c @@ -141,6 +141,8 @@ _gpgme_op_reset (gpgme_ctx_t ctx, int type) if (gpg_err_code (err) == GPG_ERR_NOT_IMPLEMENTED) err = 0; + _gpgme_engine_set_engine_flags (ctx->engine, ctx); + if (!err) { err = _gpgme_engine_set_pinentry_mode (ctx->engine, @@ -356,7 +358,7 @@ _gpgme_parse_key_considered (const char *args, /* Parse the PLAINTEXT status line in ARGS and return the result in FILENAMEP. */ gpgme_error_t -_gpgme_parse_plaintext (char *args, char **filenamep) +_gpgme_parse_plaintext (char *args, char **filenamep, int *r_mime) { char *tail; @@ -365,7 +367,9 @@ _gpgme_parse_plaintext (char *args, char **filenamep) if (*args == '\0') return 0; - /* First argument is file type. */ + /* First argument is file type (a one byte uppercase hex value). */ + if (args[0] == '6' && args[1] == 'D') + *r_mime = 1; while (*args != ' ' && *args != '\0') args++; while (*args == ' ') @@ -398,7 +402,13 @@ _gpgme_parse_plaintext (char *args, char **filenamep) /* Parse a FAILURE status line and return the error code. ARGS is - modified to contain the location part. */ + * modified to contain the location part. Note that for now we ignore + * failure codes with a location of gpg-exit; they are too trouble + * some. Instead we should eventually record that error in the + * context and provide a function to return a fuller error + * description; this could then also show the location of the error + * (e.g. "option- parser") to make it easier for the user to detect + * the actual error. */ gpgme_error_t _gpgme_parse_failure (char *args) { @@ -416,6 +426,8 @@ _gpgme_parse_failure (char *args) *where = '\0'; where = args; + if (!strcmp (where, "gpg-exit")) + return 0; return atoi (which); } @@ -68,8 +68,8 @@ gpgme_error_t _gpgme_parse_inv_recp (char *args, int for_signing, gpgme_invalid_key_t *key); /* Parse the PLAINTEXT status line in ARGS and return the result in - FILENAMEP. */ -gpgme_error_t _gpgme_parse_plaintext (char *args, char **filenamep); + FILENAMEP and R_MIME. */ +gpgme_error_t _gpgme_parse_plaintext (char *args, char **filenamep,int *r_mime); /* Parse a FAILURE status line and return the error code. ARGS is modified to contain the location part. */ diff --git a/src/priv-io.h b/src/priv-io.h index 2306175..bc9d3d5 100644 --- a/src/priv-io.h +++ b/src/priv-io.h @@ -83,6 +83,8 @@ int _gpgme_io_set_nonblocking (int fd); #define IOSPAWN_FLAG_ALLOW_SET_FG 2 /* Don't close any child FDs. */ #define IOSPAWN_FLAG_NOCLOSE 4 +/* Set show window to true for windows */ +#define IOSPAWN_FLAG_SHOW_WINDOW 8 /* Spawn the executable PATH with ARGV as arguments. After forking close all fds except for those in FD_LIST in the child, then @@ -165,10 +165,11 @@ time_t _gpgme_parse_timestamp (const char *timestamp, char **endp); * on error or missing timestamp. */ unsigned long _gpgme_parse_timestamp_ul (const char *timestamp); -gpgme_error_t _gpgme_map_gnupg_error (char *err); - int _gpgme_map_pk_algo (int algo, gpgme_protocol_t protocol); +const char *_gpgme_cipher_algo_name (int algo, gpgme_protocol_t protocol); +const char *_gpgme_cipher_mode_name (int algo, gpgme_protocol_t protocol); + /*-- b64dec.c --*/ diff --git a/src/verify.c b/src/verify.c index ee730a3..bd437c9 100644 --- a/src/verify.c +++ b/src/verify.c @@ -284,6 +284,7 @@ parse_new_sig (op_data_t opd, gpgme_status_code_t code, char *args, gpgme_signature_t sig; char *end = strchr (args, ' '); char *tail; + int got_fpr = 0; if (end) { @@ -370,7 +371,23 @@ parse_new_sig (op_data_t opd, gpgme_status_code_t code, char *args, if (!*end) goto parse_err_sig_fail; - sig->status = strtoul (end, NULL, 10); + gpg_err_set_errno (0); + sig->status = strtoul (end, &tail, 10); + if (errno || end == tail || (*tail && *tail != ' ')) + goto parse_err_sig_fail; + if (!*tail) + goto parse_err_sig_ok; + end = tail; + while (*end == ' ') + end++; + + /* Parse the new fingerprint (from the ISSUER_FPR subpacket). */ + if (!*end || (*end == '-' && (end[1] == ' ' || !end[1]))) + goto parse_err_sig_ok; /* Okay (just trailing spaces). */ + sig->fpr = strdup (end); + if (!sig->fpr) + return gpg_error_from_syserror (); + got_fpr = 1; goto parse_err_sig_ok; parse_err_sig_fail: @@ -382,7 +399,7 @@ parse_new_sig (op_data_t opd, gpgme_status_code_t code, char *args, return gpg_error (GPG_ERR_GENERAL); } - if (*args) + if (*args && !got_fpr) { sig->fpr = strdup (args); if (!sig->fpr) @@ -1074,9 +1091,15 @@ _gpgme_verify_status_handler (void *priv, gpgme_status_code_t code, char *args) case GPGME_STATUS_PLAINTEXT: if (++opd->plaintext_seen > 1) return gpg_error (GPG_ERR_BAD_DATA); - err = _gpgme_parse_plaintext (args, &opd->result.file_name); - if (err) - return err; + { + int mime = 0; + err = _gpgme_parse_plaintext (args, &opd->result.file_name, &mime); + if (err) + return err; + gpgrt_log_debug ("verify.c: setting mime to %d\n", mime); + opd->result.is_mime = !!mime; + } + break; case GPGME_STATUS_VERIFICATION_COMPLIANCE_MODE: PARSE_COMPLIANCE_FLAGS (args, opd->current_sig); diff --git a/src/versioninfo.rc.in b/src/versioninfo.rc.in index 2b1cc81..88b662e 100644 --- a/src/versioninfo.rc.in +++ b/src/versioninfo.rc.in @@ -39,7 +39,7 @@ BEGIN VALUE "FileDescription", "GPGME - GnuPG Made Easy\0" VALUE "FileVersion", "@LIBGPGME_LT_CURRENT@.@LIBGPGME_LT_AGE@.@LIBGPGME_LT_REVISION@.@BUILD_REVISION@\0" VALUE "InternalName", "gpgme\0" - VALUE "LegalCopyright", "Copyright © 2001-2017 g10 Code GmbH\0" + VALUE "LegalCopyright", "Copyright © 2001-2018 g10 Code GmbH\0" VALUE "LegalTrademarks", "\0" VALUE "OriginalFilename", "gpgme.dll\0" VALUE "PrivateBuild", "\0" diff --git a/src/w32-io.c b/src/w32-io.c index eed8a00..05e11ee 100644 --- a/src/w32-io.c +++ b/src/w32-io.c @@ -1724,8 +1724,8 @@ _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags, int written; size_t len; - if ((flags & IOSPAWN_FLAG_ALLOW_SET_FG)) - strcpy (line, "~1 \n"); + if (flags) + snprintf (line, BUFFER_MAX, "~%i \n", flags); else strcpy (line, "\n"); for (i = 0; fd_list[i].fd != -1; i++) diff --git a/tests/gpg/Makefile.am b/tests/gpg/Makefile.am index 642e0a0..b50f4b0 100644 --- a/tests/gpg/Makefile.am +++ b/tests/gpg/Makefile.am @@ -46,7 +46,7 @@ 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 .gpg-v21-migrated pubring-stamp \ - tofu.db *.conf.gpgconf.bak + gpg-sample.stamp tofu.db *.conf.gpgconf.bak private_keys = \ 13CD0F3BDF24BE53FE192D62F18737256FF6E4FD \ @@ -61,7 +61,7 @@ EXTRA_DIST = initial.test final.test \ geheim.txt pubkey-1.asc seckey-1.asc pinentry $(private_keys) BUILT_SOURCES = gpg.conf gpg-agent.conf pubring-stamp \ - private-keys-v1.d/gpg-sample.stamp + gpg-sample.stamp AM_CPPFLAGS = -I$(top_builddir)/src @GPG_ERROR_CFLAGS@ AM_LDFLAGS = -no-install LDADD = ../../src/libgpgme.la @@ -81,25 +81,21 @@ noinst_PROGRAMS = $(c_tests) $(tests_skipped) clean-local: - -$(top_srcdir)/tests/start-stop-agent --stop + -$(TESTS_ENVIRONMENT) $(top_srcdir)/tests/start-stop-agent --stop -rm -fR private-keys-v1.d -export GNUPGHOME := $(abs_builddir) - -export GPG_AGENT_INFO := - -private-keys-v1.d/gpg-sample.stamp: $(srcdir)/$(private_keys) - -gpgconf --kill all +gpg-sample.stamp: $(srcdir)/$(private_keys) + -$(TESTS_ENVIRONMENT) gpgconf --kill all $(MKDIR_P) ./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 + echo x > ./gpg-sample.stamp -pubring-stamp: $(srcdir)/pubdemo.asc ./private-keys-v1.d/gpg-sample.stamp - $(GPG) --batch --no-permission-warning \ +pubring-stamp: $(srcdir)/pubdemo.asc gpg-sample.stamp + $(TESTS_ENVIRONMENT) $(GPG) --batch --no-permission-warning \ --import $(srcdir)/pubdemo.asc - -$(GPG) --batch --no-permission-warning \ + -$(TESTS_ENVIRONMENT) $(GPG) --batch --no-permission-warning \ --import $(srcdir)/secdemo.asc echo x > ./pubring-stamp diff --git a/tests/gpg/Makefile.in b/tests/gpg/Makefile.in index d5ab6a7..f09f94a 100644 --- a/tests/gpg/Makefile.in +++ b/tests/gpg/Makefile.in @@ -552,7 +552,7 @@ c_tests = \ 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 .gpg-v21-migrated pubring-stamp \ - tofu.db *.conf.gpgconf.bak + gpg-sample.stamp tofu.db *.conf.gpgconf.bak private_keys = \ 13CD0F3BDF24BE53FE192D62F18737256FF6E4FD \ @@ -566,7 +566,7 @@ EXTRA_DIST = initial.test final.test \ geheim.txt pubkey-1.asc seckey-1.asc pinentry $(private_keys) BUILT_SOURCES = gpg.conf gpg-agent.conf pubring-stamp \ - private-keys-v1.d/gpg-sample.stamp + gpg-sample.stamp AM_CPPFLAGS = -I$(top_builddir)/src @GPG_ERROR_CFLAGS@ AM_LDFLAGS = -no-install @@ -1091,25 +1091,21 @@ uninstall-am: clean-local: - -$(top_srcdir)/tests/start-stop-agent --stop + -$(TESTS_ENVIRONMENT) $(top_srcdir)/tests/start-stop-agent --stop -rm -fR private-keys-v1.d -export GNUPGHOME := $(abs_builddir) - -export GPG_AGENT_INFO := - -private-keys-v1.d/gpg-sample.stamp: $(srcdir)/$(private_keys) - -gpgconf --kill all +gpg-sample.stamp: $(srcdir)/$(private_keys) + -$(TESTS_ENVIRONMENT) gpgconf --kill all $(MKDIR_P) ./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 + echo x > ./gpg-sample.stamp -pubring-stamp: $(srcdir)/pubdemo.asc ./private-keys-v1.d/gpg-sample.stamp - $(GPG) --batch --no-permission-warning \ +pubring-stamp: $(srcdir)/pubdemo.asc gpg-sample.stamp + $(TESTS_ENVIRONMENT) $(GPG) --batch --no-permission-warning \ --import $(srcdir)/pubdemo.asc - -$(GPG) --batch --no-permission-warning \ + -$(TESTS_ENVIRONMENT) $(GPG) --batch --no-permission-warning \ --import $(srcdir)/secdemo.asc echo x > ./pubring-stamp diff --git a/tests/gpg/pinentry b/tests/gpg/pinentry index 3b99726..b12caae 100755 --- a/tests/gpg/pinentry +++ b/tests/gpg/pinentry @@ -1,4 +1,4 @@ -#! /bin/bash +#! /bin/sh # Dummy pinentry # # Copyright 2008 g10 Code GmbH diff --git a/tests/gpg/t-support.h b/tests/gpg/t-support.h index f6dec68..ef5766a 100644 --- a/tests/gpg/t-support.h +++ b/tests/gpg/t-support.h @@ -196,7 +196,8 @@ print_import_result (gpgme_import_result_t r) " secret imported: %d\n" " secret unchanged: %d\n" " skipped new keys: %d\n" - " not imported: %d\n", + " not imported: %d\n" + " skipped v3 keys: %d\n", r->considered, r->no_user_id, r->imported, @@ -210,6 +211,7 @@ print_import_result (gpgme_import_result_t r) r->secret_imported, r->secret_unchanged, r->skipped_new_keys, - r->not_imported); + r->not_imported, + r->skipped_v3_keys); } diff --git a/tests/gpg/t-verify.c b/tests/gpg/t-verify.c index f955cc9..7c23406 100644 --- a/tests/gpg/t-verify.c +++ b/tests/gpg/t-verify.c @@ -31,31 +31,14 @@ #include <gpgme.h> +#define PGM "t-verify" #include "t-support.h" + static const char test_text1[] = "Just GNU it!\n"; static const char test_text1f[]= "Just GNU it?\n"; static const char test_sig1[] = -#if 0 -"-----BEGIN PGP SIGNATURE-----\n" -"\n" -"iEYEABECAAYFAjoKgjIACgkQLXJ8x2hpdzQMSwCeO/xUrhysZ7zJKPf/FyXA//u1\n" -"ZgIAn0204PBR7yxSdQx6CFxugstNqmRv\n" -"=yku6\n" -"-----END PGP SIGNATURE-----\n" -#elif 0 -"-----BEGIN PGP SIGNATURE-----\n" -"Version: GnuPG v1.0.4-2 (GNU/Linux)\n" -"Comment: For info see http://www.gnupg.org\n" -"\n" -"iJcEABECAFcFAjoS8/E1FIAAAAAACAAkZm9vYmFyLjF0aGlzIGlzIGEgbm90YXRp\n" -"b24gZGF0YSB3aXRoIDIgbGluZXMaGmh0dHA6Ly93d3cuZ3Uub3JnL3BvbGljeS8A\n" -"CgkQLXJ8x2hpdzQLyQCbBW/fgU8ZeWSlWPM1F8umHX17bAAAoIfSNDSp5zM85XcG\n" -"iwxMrf+u8v4r\n" -"=88Zo\n" -"-----END PGP SIGNATURE-----\n" -#elif 1 "-----BEGIN PGP SIGNATURE-----\n" "\n" "iN0EABECAJ0FAjoS+i9FFIAAAAAAAwA5YmFyw7bDpMO8w58gZGFzIHdhcmVuIFVt\n" @@ -64,9 +47,24 @@ static const char test_sig1[] = "Oi8vd3d3Lmd1Lm9yZy9wb2xpY3kvAAoJEC1yfMdoaXc0JBIAoIiLlUsvpMDOyGEc\n" "dADGKXF/Hcb+AKCJWPphZCphduxSvrzH0hgzHdeQaA==\n" "=nts1\n" -"-----END PGP SIGNATURE-----\n" -#endif -; +"-----END PGP SIGNATURE-----\n"; + +/* The same as test_sig1 but with a second signature for which we do + * not have the public key (deleted after signature creation). */ +static const char test_sig1_plus_unknown_key[] = +"-----BEGIN PGP SIGNATURE-----\n" +"\n" +"iN0EABECAJ0FAjoS+i9FFIAAAAAAAwA5YmFyw7bDpMO8w58gZGFzIHdhcmVuIFVt\n" +"bGF1dGUgdW5kIGpldHp0IGVpbiBwcm96ZW50JS1aZWljaGVuNRSAAAAAAAgAJGZv\n" +"b2Jhci4xdGhpcyBpcyBhIG5vdGF0aW9uIGRhdGEgd2l0aCAyIGxpbmVzGhpodHRw\n" +"Oi8vd3d3Lmd1Lm9yZy9wb2xpY3kvAAoJEC1yfMdoaXc0JBIAoIiLlUsvpMDOyGEc\n" +"dADGKXF/Hcb+AKCJWPphZCphduxSvrzH0hgzHdeQaIh1BAAWCAAdFiEENuwqcMZC\n" +"brD85btN+RyY8EnUIEwFAlrPR4cACgkQ+RyY8EnUIEyiuAEAm41LJTGUFDzhavRm\n" +"jNwqUZxGGOySduW+u/X1lEfV+MYA/2lJOo75rHtD1EG+tkFVWt4Ukj0rjhR132vZ\n" +"IOtrYAcG\n" +"=yYwZ\n" +"-----END PGP SIGNATURE-----\n"; + static const char test_sig2[] = "-----BEGIN PGP MESSAGE-----\n" "\n" @@ -91,37 +89,51 @@ static const char double_plaintext_sig[] = +/* NO_OF_SIGS is the expected number of signatures. SKIP_SKIPS is + * which of these signatures to check (0 based). */ static void -check_result (gpgme_verify_result_t result, unsigned int summary, - const char *fpr, +check_result (gpgme_verify_result_t result, int no_of_sigs, int skip_sigs, + unsigned int summary, const char *fpr, gpgme_error_t status, int notation) { gpgme_signature_t sig; + int n; sig = result->signatures; - if (!sig || sig->next) + for (n=0; sig; sig = sig->next) + n++; + if (n != no_of_sigs) + { + fprintf (stderr, "%s:%i: Unexpected number of signatures" + " (got %d expected %d)\n", PGM, __LINE__, n, no_of_sigs); + exit (1); + } + if (skip_sigs >= n) { - fprintf (stderr, "%s:%i: Unexpected number of signatures\n", - __FILE__, __LINE__); + fprintf (stderr, "%s:%i: oops SKIPP_SIGS to high\n", PGM, __LINE__); exit (1); } + + for (n=0, sig = result->signatures; n < skip_sigs; sig = sig->next, n++) + ; + if (sig->summary != summary) { - fprintf (stderr, "%s:%i: Unexpected signature summary: " + fprintf (stderr, "%s:%i:sig-%d: Unexpected signature summary: " "want=0x%x have=0x%x\n", - __FILE__, __LINE__, summary, sig->summary); + PGM, __LINE__, skip_sigs, summary, sig->summary); exit (1); } if (strcmp (sig->fpr, fpr)) { - fprintf (stderr, "%s:%i: Unexpected fingerprint: %s\n", - __FILE__, __LINE__, sig->fpr); + fprintf (stderr, "%s:%i:sig-%d: Unexpected fingerprint: %s\n", + PGM, __LINE__, skip_sigs, sig->fpr); exit (1); } if (gpgme_err_code (sig->status) != status) { - fprintf (stderr, "%s:%i: Unexpected signature status: %s\n", - __FILE__, __LINE__, gpgme_strerror (sig->status)); + fprintf (stderr, "%s:%i:sig-%d: Unexpected signature status: %s\n", + PGM, __LINE__, skip_sigs, gpgme_strerror (sig->status)); exit (1); } if (notation) @@ -166,8 +178,8 @@ check_result (gpgme_verify_result_t result, unsigned int summary, } if (!any) { - fprintf (stderr, "%s:%i: Unexpected notation data\n", - __FILE__, __LINE__); + fprintf (stderr, "%s:%i:sig-%d: Unexpected notation data\n", + PGM, __LINE__, skip_sigs); exit (1); } } @@ -175,28 +187,30 @@ check_result (gpgme_verify_result_t result, unsigned int summary, { if (expected_notations[i].seen != 1) { - fprintf (stderr, "%s:%i: Missing or duplicate notation data\n", - __FILE__, __LINE__); + fprintf (stderr, "%s:%i:sig-%d: " + "Missing or duplicate notation data\n", + PGM, __LINE__, skip_sigs); exit (1); } } } if (sig->wrong_key_usage) { - fprintf (stderr, "%s:%i: Unexpectedly wrong key usage\n", - __FILE__, __LINE__); + fprintf (stderr, "%s:%i:sig-%d: Unexpectedly wrong key usage\n", + PGM, __LINE__, skip_sigs); exit (1); } if (sig->validity != GPGME_VALIDITY_UNKNOWN) { - fprintf (stderr, "%s:%i: Unexpected validity: %i\n", - __FILE__, __LINE__, sig->validity); + fprintf (stderr, "%s:%i:sig-%d: Unexpected validity: %i\n", + PGM, __LINE__, skip_sigs, sig->validity); exit (1); } if (gpgme_err_code (sig->validity_reason) != GPG_ERR_NO_ERROR) { - fprintf (stderr, "%s:%i: Unexpected validity reason: %s\n", - __FILE__, __LINE__, gpgme_strerror (sig->validity_reason)); + fprintf (stderr, "%s:%i:sig-%d: Unexpected validity reason: %s\n", + PGM, __LINE__, skip_sigs, + gpgme_strerror (sig->validity_reason)); exit (1); } } @@ -227,7 +241,7 @@ main (int argc, char *argv[]) err = gpgme_op_verify (ctx, sig, text, NULL); fail_if_err (err); result = gpgme_op_verify_result (ctx); - check_result (result, 0, "A0FF4590BB6122EDEF6E3C542D727CC768697734", + check_result (result, 1, 0, 0, "A0FF4590BB6122EDEF6E3C542D727CC768697734", GPG_ERR_NO_ERROR, 1); /* Checking a manipulated message. */ @@ -238,9 +252,29 @@ main (int argc, char *argv[]) err = gpgme_op_verify (ctx, sig, text, NULL); fail_if_err (err); result = gpgme_op_verify_result (ctx); - check_result (result, GPGME_SIGSUM_RED, "2D727CC768697734", + check_result (result, 1, 0, GPGME_SIGSUM_RED, "2D727CC768697734", GPG_ERR_BAD_SIGNATURE, 0); + /* Checking a valid message. Bu that one has a second signature + * made by an unknown key. */ + gpgme_data_release (text); + gpgme_data_release (sig); + err = gpgme_data_new_from_mem (&text, test_text1, strlen (test_text1), 0); + fail_if_err (err); + err = gpgme_data_new_from_mem (&sig, test_sig1_plus_unknown_key, + strlen (test_sig1_plus_unknown_key), 0); + fail_if_err (err); + err = gpgme_op_verify (ctx, sig, text, NULL); + fail_if_err (err); + result = gpgme_op_verify_result (ctx); + check_result (result, 2, 0, 0, + "A0FF4590BB6122EDEF6E3C542D727CC768697734", + GPG_ERR_NO_ERROR, 1); + check_result (result, 2, 1, GPGME_SIGSUM_KEY_MISSING, + "36EC2A70C6426EB0FCE5BB4DF91C98F049D4204C", + GPG_ERR_NO_PUBKEY, 0); + + /* Checking a normal signature. */ gpgme_data_release (sig); gpgme_data_release (text); @@ -251,7 +285,7 @@ main (int argc, char *argv[]) err = gpgme_op_verify (ctx, sig, NULL, text); fail_if_err (err); result = gpgme_op_verify_result (ctx); - check_result (result, 0, "A0FF4590BB6122EDEF6E3C542D727CC768697734", + check_result (result, 1, 0, 0, "A0FF4590BB6122EDEF6E3C542D727CC768697734", GPG_ERR_NO_ERROR, 0); @@ -267,7 +301,7 @@ main (int argc, char *argv[]) if (gpgme_err_code (err) != GPG_ERR_BAD_DATA) { fprintf (stderr, "%s:%i: Double plaintext message not detected\n", - __FILE__, __LINE__); + PGM, __LINE__); exit (1); } @@ -278,7 +312,7 @@ main (int argc, char *argv[]) if (!s || strcmp (s, "foo@example.org")) { fprintf (stderr, "%s:%i: gpgme_{set,get}_sender mismatch\n", - __FILE__, __LINE__); + PGM, __LINE__); exit (1); } @@ -288,7 +322,7 @@ main (int argc, char *argv[]) if (!s || strcmp (s, "bar@example.org")) { fprintf (stderr, "%s:%i: gpgme_{set,get}_sender mismatch\n", - __FILE__, __LINE__); + PGM, __LINE__); exit (1); } @@ -298,7 +332,7 @@ main (int argc, char *argv[]) if (!s || strcmp (s, "foo@example.org")) { fprintf (stderr, "%s:%i: gpgme_{set,get}_sender mismatch\n", - __FILE__, __LINE__); + PGM, __LINE__); exit (1); } @@ -306,7 +340,7 @@ main (int argc, char *argv[]) if (gpgme_err_code (err) != GPG_ERR_INV_VALUE) { fprintf (stderr, "%s:%i: gpgme_set_sender didn't detect bogus address\n", - __FILE__, __LINE__); + PGM, __LINE__); exit (1); } /* (the former address should still be there.) */ @@ -314,7 +348,7 @@ main (int argc, char *argv[]) if (!s || strcmp (s, "foo@example.org")) { fprintf (stderr, "%s:%i: gpgme_{set,get}_sender mismatch\n", - __FILE__, __LINE__); + PGM, __LINE__); exit (1); } diff --git a/tests/gpgsm/Makefile.am b/tests/gpgsm/Makefile.am index 4ab2283..d2acd05 100644 --- a/tests/gpgsm/Makefile.am +++ b/tests/gpgsm/Makefile.am @@ -46,21 +46,17 @@ noinst_PROGRAMS = $(c_tests) t-genkey cms-keylist cms-decrypt key_id = 32100C27173EF6E9C4E9A25D3D69F86D37A4F939 CLEANFILES = pubring-stamp pubring.kbx pubring.kbx~ gpgsm.conf trustlist.txt \ - random_seed S.gpg-agent + random_seed S.gpg-agent gpg-sample.stamp clean-local: - -$(top_srcdir)/tests/start-stop-agent --stop + -$(TESTS_ENVIRONMENT) $(top_srcdir)/tests/start-stop-agent --stop -rm -fR private-keys-v1.d -export GNUPGHOME := $(abs_builddir) - -export GPG_AGENT_INFO := - BUILT_SOURCES = gpgsm.conf trustlist.txt pubring-stamp \ - private-keys-v1.d/gpg-sample.stamp + gpg-sample.stamp -pubring-stamp: $(srcdir)/cert_g10code_test1.der ./private-keys-v1.d/gpg-sample.stamp - $(GPGSM) --import $(srcdir)/cert_g10code_test1.der +pubring-stamp: $(srcdir)/cert_g10code_test1.der gpg-sample.stamp + $(TESTS_ENVIRONMENT) $(GPGSM) --import $(srcdir)/cert_g10code_test1.der touch pubring-stamp gpgsm.conf: @@ -68,11 +64,11 @@ gpgsm.conf: echo faked-system-time 1008241200 >> ./gpgsm.conf echo "agent-program `which $(GPG_AGENT)`|--debug-quick-random" >> ./gpgsm.conf -private-keys-v1.d/gpg-sample.stamp: $(srcdir)/$(key_id) - -gpgconf --kill all +gpg-sample.stamp: $(srcdir)/$(key_id) + -$(TESTS_ENVIRONMENT) gpgconf --kill all $(MKDIR_P) ./private-keys-v1.d cp $(srcdir)/$(key_id) private-keys-v1.d/$(key_id).key - echo x > ./private-keys-v1.d/gpg-sample.stamp + echo x > ./gpg-sample.stamp trustlist.txt: echo $(key_id) > ./trustlist.txt diff --git a/tests/gpgsm/Makefile.in b/tests/gpgsm/Makefile.in index 42ffb7a..307b3f1 100644 --- a/tests/gpgsm/Makefile.in +++ b/tests/gpgsm/Makefile.in @@ -468,10 +468,10 @@ AM_LDFLAGS = -no-install LDADD = ../../src/libgpgme.la key_id = 32100C27173EF6E9C4E9A25D3D69F86D37A4F939 CLEANFILES = pubring-stamp pubring.kbx pubring.kbx~ gpgsm.conf trustlist.txt \ - random_seed S.gpg-agent + random_seed S.gpg-agent gpg-sample.stamp BUILT_SOURCES = gpgsm.conf trustlist.txt pubring-stamp \ - private-keys-v1.d/gpg-sample.stamp + gpg-sample.stamp all: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) all-am @@ -905,15 +905,11 @@ uninstall-am: clean-local: - -$(top_srcdir)/tests/start-stop-agent --stop + -$(TESTS_ENVIRONMENT) $(top_srcdir)/tests/start-stop-agent --stop -rm -fR private-keys-v1.d -export GNUPGHOME := $(abs_builddir) - -export GPG_AGENT_INFO := - -pubring-stamp: $(srcdir)/cert_g10code_test1.der ./private-keys-v1.d/gpg-sample.stamp - $(GPGSM) --import $(srcdir)/cert_g10code_test1.der +pubring-stamp: $(srcdir)/cert_g10code_test1.der gpg-sample.stamp + $(TESTS_ENVIRONMENT) $(GPGSM) --import $(srcdir)/cert_g10code_test1.der touch pubring-stamp gpgsm.conf: @@ -921,11 +917,11 @@ gpgsm.conf: echo faked-system-time 1008241200 >> ./gpgsm.conf echo "agent-program `which $(GPG_AGENT)`|--debug-quick-random" >> ./gpgsm.conf -private-keys-v1.d/gpg-sample.stamp: $(srcdir)/$(key_id) - -gpgconf --kill all +gpg-sample.stamp: $(srcdir)/$(key_id) + -$(TESTS_ENVIRONMENT) gpgconf --kill all $(MKDIR_P) ./private-keys-v1.d cp $(srcdir)/$(key_id) private-keys-v1.d/$(key_id).key - echo x > ./private-keys-v1.d/gpg-sample.stamp + echo x > ./gpg-sample.stamp trustlist.txt: echo $(key_id) > ./trustlist.txt diff --git a/tests/run-decrypt.c b/tests/run-decrypt.c index e961293..69de139 100644 --- a/tests/run-decrypt.c +++ b/tests/run-decrypt.c @@ -53,19 +53,21 @@ print_result (gpgme_decrypt_result_t result) gpgme_recipient_t recp; int count = 0; - printf ("Original file name: %s\n", nonnull(result->file_name)); - printf ("Wrong key usage: %i\n", result->wrong_key_usage); - printf ("Unsupported algorithm: %s\n", - nonnull(result->unsupported_algorithm)); - if (result->session_key) - printf ("Session key: %s\n", result->session_key); - - for (recp = result->recipients; recp->next; recp = recp->next) + printf ("Original file name .: %s\n", nonnull(result->file_name)); + printf ("Wrong key usage ....: %s\n", result->wrong_key_usage? "yes":"no"); + printf ("Compliance de-vs ...: %s\n", result->is_de_vs? "yes":"no"); + printf ("MIME flag ..........: %s\n", result->is_mime? "yes":"no"); + printf ("Unsupported algo ...: %s\n", nonnull(result->unsupported_algorithm)); + printf ("Session key ........: %s\n", nonnull (result->session_key)); + printf ("Symmetric algorithm : %s\n", result->symkey_algo); + + for (recp = result->recipients; recp && recp->next; recp = recp->next) { - printf ("recipient %d\n", count++); + printf ("Recipient ...: %d\n", count++); printf (" status ....: %s\n", gpgme_strerror (recp->status)); - printf (" keyid: %s\n", nonnull (recp->keyid)); - printf (" algo ...: %s\n", gpgme_pubkey_algo_name (recp->pubkey_algo)); + printf (" keyid .....: %s\n", nonnull (recp->keyid)); + printf (" algo ......: %s\n", + gpgme_pubkey_algo_name (recp->pubkey_algo)); } } @@ -81,6 +83,8 @@ show_usage (int ex) " --cms use the CMS protocol\n" " --export-session-key show the session key\n" " --override-session-key STRING use STRING as session key\n" + " --request-origin STRING use STRING as request origin\n" + " --no-symkey-cache disable the use of that cache\n" " --unwrap remove only the encryption layer\n" , stderr); exit (ex); @@ -102,6 +106,8 @@ main (int argc, char **argv) int print_status = 0; int export_session_key = 0; const char *override_session_key = NULL; + const char *request_origin = NULL; + int no_symkey_cache = 0; int raw_output = 0; if (argc) @@ -150,6 +156,19 @@ main (int argc, char **argv) override_session_key = *argv; argc--; argv++; } + else if (!strcmp (*argv, "--request-origin")) + { + argc--; argv++; + if (!argc) + show_usage (1); + request_origin = *argv; + argc--; argv++; + } + else if (!strcmp (*argv, "--no-symkey-cache")) + { + no_symkey_cache = 1; + argc--; argv++; + } else if (!strcmp (*argv, "--unwrap")) { flags |= GPGME_DECRYPT_UNWRAP; @@ -199,7 +218,29 @@ main (int argc, char **argv) override_session_key); if (err) { - fprintf (stderr, PGM ": error overriding session key: %s\n", + fprintf (stderr, PGM ": error setting overriding session key: %s\n", + gpgme_strerror (err)); + exit (1); + } + } + + if (request_origin) + { + err = gpgme_set_ctx_flag (ctx, "request-origin", request_origin); + if (err) + { + fprintf (stderr, PGM ": error setting request_origin: %s\n", + gpgme_strerror (err)); + exit (1); + } + } + + if (no_symkey_cache) + { + err = gpgme_set_ctx_flag (ctx, "no-symkey-cache", "1"); + if (err) + { + fprintf (stderr, PGM ": error setting no-symkey-cache: %s\n", gpgme_strerror (err)); exit (1); } diff --git a/tests/run-encrypt.c b/tests/run-encrypt.c index e949d76..9408469 100644 --- a/tests/run-encrypt.c +++ b/tests/run-encrypt.c @@ -37,6 +37,19 @@ static int verbose; +static char * +xstrdup (const char *string) +{ + char *p = strdup (string); + if (!p) + { + fprintf (stderr, "strdup failed\n"); + exit (2); + } + return p; +} + + static gpg_error_t status_cb (void *opaque, const char *keyword, const char *value) { @@ -80,17 +93,19 @@ show_usage (int ex) { fputs ("usage: " PGM " [options] FILE\n\n" "Options:\n" - " --verbose run in verbose mode\n" - " --status print status lines from the backend\n" - " --progress print progress info\n" - " --openpgp use the OpenPGP protocol (default)\n" - " --cms use the CMS protocol\n" - " --uiserver use the UI server\n" - " --loopback use a loopback pinentry\n" - " --key NAME encrypt to key NAME\n" - " --throw-keyids use this option\n" - " --wrap assume input is valid OpenPGP message\n" - " --symmetric encrypt symmetric (OpenPGP only)\n" + " --verbose run in verbose mode\n" + " --status print status lines from the backend\n" + " --progress print progress info\n" + " --openpgp use the OpenPGP protocol (default)\n" + " --cms use the CMS protocol\n" + " --uiserver use the UI server\n" + " --loopback use a loopback pinentry\n" + " --key NAME encrypt to key NAME\n" + " --keystring NAMES encrypt to ';' delimited NAMES\n" + " --throw-keyids use this option\n" + " --no-symkey-cache disable the use of that cache\n" + " --wrap assume input is valid OpenPGP message\n" + " --symmetric encrypt symmetric (OpenPGP only)\n" , stderr); exit (ex); } @@ -102,7 +117,6 @@ main (int argc, char **argv) int last_argc = -1; gpgme_error_t err; gpgme_ctx_t ctx; - const char *key_string = NULL; gpgme_protocol_t protocol = GPGME_PROTOCOL_OpenPGP; gpgme_data_t in, out; gpgme_encrypt_result_t result; @@ -112,9 +126,11 @@ main (int argc, char **argv) char *keyargs[10]; gpgme_key_t keys[10+1]; int keycount = 0; + char *keystring = NULL; int i; gpgme_encrypt_flags_t flags = GPGME_ENCRYPT_ALWAYS_TRUST; gpgme_off_t offset; + int no_symkey_cache = 0; if (argc) { argc--; argv++; } @@ -172,6 +188,17 @@ main (int argc, char **argv) keyargs[keycount++] = *argv; argc--; argv++; } + else if (!strcmp (*argv, "--keystring")) + { + argc--; argv++; + if (!argc) + show_usage (1); + keystring = xstrdup (*argv); + for (i=0; keystring[i]; i++) + if (keystring[i] == ';') + keystring[i] = '\n'; + argc--; argv++; + } else if (!strcmp (*argv, "--throw-keyids")) { flags |= GPGME_ENCRYPT_THROW_KEYIDS; @@ -192,6 +219,11 @@ main (int argc, char **argv) flags |= GPGME_ENCRYPT_SYMMETRIC; argc--; argv++; } + else if (!strcmp (*argv, "--no-symkey-cache")) + { + no_symkey_cache = 1; + argc--; argv++; + } else if (!strncmp (*argv, "--", 2)) show_usage (1); @@ -200,15 +232,6 @@ main (int argc, char **argv) if (argc != 1) show_usage (1); - if (key_string && protocol == GPGME_PROTOCOL_UISERVER) - { - fprintf (stderr, PGM ": ignoring --key in UI-server mode\n"); - key_string = NULL; - } - - if (!key_string) - key_string = "test"; - init_gpgme (protocol); err = gpgme_new (&ctx); @@ -227,6 +250,16 @@ main (int argc, char **argv) gpgme_set_pinentry_mode (ctx, GPGME_PINENTRY_MODE_LOOPBACK); gpgme_set_passphrase_cb (ctx, passphrase_cb, NULL); } + if (no_symkey_cache) + { + err = gpgme_set_ctx_flag (ctx, "no-symkey-cache", "1"); + if (err) + { + fprintf (stderr, PGM ": error setting no-symkey-cache: %s\n", + gpgme_strerror (err)); + exit (1); + } + } for (i=0; i < keycount; i++) { @@ -281,7 +314,8 @@ main (int argc, char **argv) err = gpgme_data_new (&out); fail_if_err (err); - err = gpgme_op_encrypt (ctx, keycount ? keys : NULL, flags, in, out); + err = gpgme_op_encrypt_ext (ctx, keycount ? keys : NULL, keystring, + flags, in, out); result = gpgme_op_encrypt_result (ctx); if (result) print_result (result); @@ -301,5 +335,6 @@ main (int argc, char **argv) for (i=0; i < keycount; i++) gpgme_key_unref (keys[i]); gpgme_release (ctx); + free (keystring); return 0; } diff --git a/tests/run-support.h b/tests/run-support.h index 6a2170b..6c713a9 100644 --- a/tests/run-support.h +++ b/tests/run-support.h @@ -177,7 +177,8 @@ print_import_result (gpgme_import_result_t r) " secret imported: %d\n" " secret unchanged: %d\n" " skipped new keys: %d\n" - " not imported: %d\n", + " not imported: %d\n" + " skipped v3 keys: %d\n", r->considered, r->no_user_id, r->imported, @@ -191,6 +192,7 @@ print_import_result (gpgme_import_result_t r) r->secret_imported, r->secret_unchanged, r->skipped_new_keys, - r->not_imported); + r->not_imported, + r->skipped_v3_keys); } diff --git a/tests/run-verify.c b/tests/run-verify.c index b22e644..4a6c960 100644 --- a/tests/run-verify.c +++ b/tests/run-verify.c @@ -136,10 +136,11 @@ print_result (gpgme_verify_result_t result) gpgme_tofu_info_t ti; int count = 0; - printf ("Original file name: %s\n", nonnull(result->file_name)); + printf ("Original file name .: %s\n", nonnull(result->file_name)); + printf ("MIME flag ..........: %s\n", result->is_mime? "yes":"no"); for (sig = result->signatures; sig; sig = sig->next) { - printf ("Signature %d\n", count++); + printf ("Signature ...: %d\n", count++); printf (" status ....: %s\n", gpgme_strerror (sig->status)); printf (" summary ...:"); print_summary (sig->summary); putchar ('\n'); printf (" fingerprint: %s\n", nonnull (sig->fpr)); @@ -163,17 +164,24 @@ print_result (gpgme_verify_result_t result) ); for (nt = sig->notations; nt; nt = nt->next) { - printf (" notation ..: '%s'\n", nt->name); - if (strlen (nt->name) != nt->name_len) - printf (" warning : name larger (%d)\n", nt->name_len); - printf (" flags ...:%s%s (0x%02x)\n", - nt->critical? " critical":"", - nt->human_readable? " human":"", - nt->flags); - if (nt->value) - printf (" value ...: '%s'\n", nt->value); + if (nt->name) + { + printf (" notation ..: '%s'\n", nt->name); + if (strlen (nt->name) != nt->name_len) + printf (" warning .: name larger (%d)\n", nt->name_len); + printf (" flags ...:%s%s (0x%02x)\n", + nt->critical? " critical":"", + nt->human_readable? " human":"", + nt->flags); + if (nt->value) + printf (" value ...: '%s'\n", nt->value); + } + else + { + printf (" policy ....: '%s'\n", nt->value); + } if ((nt->value?strlen (nt->value):0) != nt->value_len) - printf (" warning : value larger (%d)\n", nt->value_len); + printf (" warning .: value larger (%d)\n", nt->value_len); } if (sig->key) { |