summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ChangeLog-20117148
-rw-r--r--src/Makefile.am230
-rw-r--r--src/Makefile.in1113
-rw-r--r--src/assuan-support.c256
-rw-r--r--src/ath-pthread.c186
-rw-r--r--src/ath.c214
-rw-r--r--src/ath.h101
-rw-r--r--src/context.h138
-rw-r--r--src/conversion.c414
-rw-r--r--src/data-compat.c254
-rw-r--r--src/data-fd.c142
-rw-r--r--src/data-mem.c282
-rw-r--r--src/data-stream.c108
-rw-r--r--src/data-user.c104
-rw-r--r--src/data.c336
-rw-r--r--src/data.h134
-rw-r--r--src/debug.c369
-rw-r--r--src/debug.h262
-rw-r--r--src/decrypt-verify.c121
-rw-r--r--src/decrypt.c403
-rw-r--r--src/delete.c131
-rw-r--r--src/dirinfo.c189
-rw-r--r--src/edit.c221
-rw-r--r--src/encrypt-sign.c157
-rw-r--r--src/encrypt.c288
-rw-r--r--src/engine-assuan.c785
-rw-r--r--src/engine-backend.h144
-rw-r--r--src/engine-g13.c801
-rw-r--r--src/engine-gpg.c2393
-rw-r--r--src/engine-gpgconf.c928
-rw-r--r--src/engine-gpgsm.c1989
-rw-r--r--src/engine-uiserver.c1343
-rw-r--r--src/engine.c924
-rw-r--r--src/engine.h164
-rw-r--r--src/error.c111
-rw-r--r--src/export.c325
-rw-r--r--src/funopen.c63
-rw-r--r--src/genkey.c235
-rw-r--r--src/get-env.c59
-rw-r--r--src/getauditlog.c99
-rw-r--r--src/gpgconf.c142
-rw-r--r--src/gpgme-config.in194
-rw-r--r--src/gpgme-tool.c3267
-rw-r--r--src/gpgme-w32spawn.c501
-rw-r--r--src/gpgme.c916
-rw-r--r--src/gpgme.def206
-rw-r--r--src/gpgme.h.in2116
-rw-r--r--src/gpgme.m4238
-rw-r--r--src/import.c448
-rw-r--r--src/kdpipeiodevice.cpp951
-rw-r--r--src/kdpipeiodevice.h73
-rw-r--r--src/kdpipeiodevice.moc183
-rw-r--r--src/key.c755
-rw-r--r--src/keylist.c1075
-rw-r--r--src/libgpgme.vers213
-rw-r--r--src/moc_kdpipeiodevice.cpp60
-rw-r--r--src/op-support.c322
-rw-r--r--src/opassuan.c230
-rw-r--r--src/ops.h179
-rw-r--r--src/passphrase.c154
-rw-r--r--src/passwd.c187
-rw-r--r--src/posix-io.c750
-rw-r--r--src/posix-sema.c68
-rw-r--r--src/posix-util.c108
-rw-r--r--src/priv-io.h115
-rw-r--r--src/progress.c81
-rw-r--r--src/sema.h67
-rw-r--r--src/setenv.c354
-rw-r--r--src/sig-notation.c260
-rw-r--r--src/sign.c422
-rw-r--r--src/signers.c110
-rw-r--r--src/status-table.c157
-rw-r--r--src/stpcpy.c55
-rw-r--r--src/trust-item.c171
-rw-r--r--src/trustlist.c276
-rw-r--r--src/ttyname_r.c130
-rw-r--r--src/util.h175
-rw-r--r--src/vasprintf.c206
-rw-r--r--src/verify.c1084
-rw-r--r--src/version.c353
-rw-r--r--src/versioninfo.rc.in52
-rw-r--r--src/vfs-create.c203
-rw-r--r--src/vfs-mount.c245
-rw-r--r--src/w32-ce.c507
-rw-r--r--src/w32-ce.h91
-rw-r--r--src/w32-glib-io.c1084
-rw-r--r--src/w32-io.c2064
-rw-r--r--src/w32-qt-io.cpp699
-rw-r--r--src/w32-sema.c117
-rw-r--r--src/w32-util.c639
-rw-r--r--src/wait-global.c400
-rw-r--r--src/wait-private.c179
-rw-r--r--src/wait-user.c130
-rw-r--r--src/wait.c223
-rw-r--r--src/wait.h96
95 files changed, 47745 insertions, 0 deletions
diff --git a/src/ChangeLog-2011 b/src/ChangeLog-2011
new file mode 100644
index 0000000..c1a901a
--- /dev/null
+++ b/src/ChangeLog-2011
@@ -0,0 +1,7148 @@
+2011-12-02 Werner Koch <wk@g10code.com>
+
+ NB: ChangeLog files are no longer manually maintained. Starting
+ on December 1st, 2011 we put change information only in the GIT
+ commit log, and generate a top-level ChangeLog file from logs at
+ "make dist". See doc/HACKING for details.
+
+2011-10-25 Marcus Brinkmann <marcus@g10code.com>
+
+ * Makefile.am: Remove build rules for libgpgme-pth.
+ * gpgme.m4: Remove support for libgpgme-pth.
+
+2011-05-26 Marcus Brinkmann <marcus@g10code.com>
+
+ * decrypt.c (gpgme_op_decrypt_start, gpgme_op_decrypt): Check CTX.
+ * decrypt-verify.c (gpgme_op_decrypt_verify_start)
+ (gpgme_op_decrypt_verify): Likewise.
+ * delete.c (gpgme_op_delete_start, gpgme_op_delete): Likewise.
+ * edit.c (gpgme_op_edit_start, gpgme_op_edit)
+ (gpgme_op_card_edit_start, gpgme_op_card_edit): Likewise.
+ * encrypt.c (gpgme_op_encrypt_start, gpgme_op_encrypt): Likewise.
+ * encrypt-sign.c (gpgme_op_encrypt_sign_start)
+ (gpgme_op_encrypt_sign): Likewise.
+ * export.c (gpgme_op_export_start, gpgme_op_export)
+ (gpgme_op_export_ext_start, gpgme_op_export_ext)
+ (gpgme_op_export_keys_start, gpgme_op_export_keys): Likewise.
+ * genkey.c (gpgme_op_genkey_start, gpgme_op_genkey): Likewise.
+ * getauditlog.c (gpgme_op_getauditlog_start)
+ (gpgme_op_getauditlog): Likewise.
+ * gpgconf.c (gpgme_op_conf_load, gpgme_op_conf_save): Likewise.
+ * import.c (gpgme_op_import_start, gpgme_op_import_keys_start)
+ (gpgme_op_import_keys, gpgme_op_import): Likewise.
+ * keylist.c (gpgme_op_keylist_start, gpgme_op_keylist_ext_start):
+ Likewise.
+ * opassuan.c (gpgme_op_assuan_transact_start)
+ (gpgme_op_assuan_transact_ext): Likewise.
+ * passwd.c (gpgme_op_passwd_start, gpgme_op_passwd): Likewise.
+ * sign.c (gpgme_op_sign_start, gpgme_op_sign): Likewise.
+ * trustlist.c (gpgme_op_trustlist_start)
+ (gpgme_op_trustlist_next): Likewise.
+ * verify.c (gpgme_op_verify_start, gpgme_get_sig_key): Likewise.
+ * op-support.c (_gpgme_op_data_lookup): Likewise.
+ * vfs-create.c (gpgme_op_vfs_transact, gpgme_op_vfs_create): Likewise.
+ * vfs-mount.c (gpgme_op_vfs_mount, gpgme_op_vfs_transact): Likewise.
+ * gpgme.c (gpgme_set_protocol)
+ (gpgme_set_sub_protocol)
+ (gpgme_set_armor, gpgme_set_include_certs)
+ (gpgme_set_keylist_mode, gpgme_set_passphrase_cb)
+ (gpgme_set_progress_cb, gpgme_set_io_cbs, gpgme_set_locale)
+ (gpgme_ctx_set_engine_info, gpgme_sig_notation_clear): Likewise.
+ * gpgme.c (gpgme_new): Check for valid R_CTX.
+ (gpgme_cancel, gpgme_cancel_async, gpgme_release): Likewise.
+
+2011-04-06 Werner Koch <wk@g10code.com>
+
+ * gpgme-config.in: Add option --host. Change options --cflags and
+ --libs to collapse duplicate include and lib dirs. Try to put
+ extra libs at the end.
+
+ * gpgme.h.in: Use INSERT__TYPEDEFS_FOR_GPGME_H to include platform
+ specific typedefs.
+
+2011-02-03 Werner Koch <wk@g10code.com>
+
+ * extra-stati.h: New.
+ * mkstatus: Extend to also process extra-stati.h
+ * Makefile.am (main_sources): Add extra-stati.h
+ (status-table.h): Depend on extra-stati.h and adjust rule.
+
+2011-02-03 Marcus Brinkmann <marcus@g10code.com>
+
+ * w32-io.c (_gpgme_io_socket): Return fd, not res.
+
+2011-02-02 Marcus Brinkmann <mb@g10code.com>
+
+ * assuan-support.c (my_socket, my_connect): New functions.
+ (_gpgme_assuan_system_hooks): Add my_Socket, my_connect.
+ * priv-io.h (_gpgme_io_socket): New prototype.
+ * w32-io.c (pid_to_handle, handle_to_oid, fd_to_handle): Remove macros.
+ (is_socket): Remove function.
+ (_gpgme_io_spawn) [HAVE_W32CE_SYSTEM]: Remove some dead code.
+ (_gpgme_io_spawn): Translate handles before DuplicateHandle them.
+
+2011-02-02 Marcus Brinkmann <mb@g10code.com>
+
+ * w32-util.c (mkstemp): Don't use CreateFile instead of open (the
+ function is not used on Windows CE, and the callers were not
+ adjusted).
+
+2011-01-21 Marcus Brinkmann <mb@g10code.com>
+
+ * engine-gpgconf.c (_gpgme_conf_opt_change): Fix the case that is
+ not self-assignment.
+
+2010-12-08 Werner Koch <wk@g10code.com>
+
+ * gpgme-tool.c (strcpy_escaped_plus): New.
+ (DIM, xtoi_1, xtoi_2): New.
+ (cmd_keylist): Allow for multiple patterns.
+
+2010-11-23 Marcus Brinkmann <mb@g10code.com>
+
+ * w32-io.c (create_reader, create_writer): Use small stack size on
+ Windows CE.
+
+2010-11-23 Marcus Brinkmann <mb@g10code.com>
+
+ * gpgme.h.in (gpgme_conf_arg_new): Make VALUE arg const void *.
+ * gpgconf.c (_gpgme_conf_arg_new): Likewise.
+ (gpgme_conf_arg_new): Likewise.
+ * engine-gpgconf.c (_gpgme_conf_arg_new): Likewise.
+ (gpgconf_write): Remove debug hack.
+
+2010-11-19 Marcus Brinkmann <mb@g10code.com>
+
+ * engine-gpgconf.c (_gpgme_conf_opt_change): Support
+ self-assignment. Requested by Marc Mutz.
+
+2010-11-17 Marcus Brinkmann <mb@g10code.com>
+
+ * vasprintf.c (int_vasprintf) [HAVE_W32CE_SYSTEM]: Just use a
+ fixed size buffer, as va_copy is not easy to fake.
+
+2010-11-15 Marcus Brinkmann <mb@g10code.com>
+
+ * w32-ce.h (strcasecmp, strdup) [_MSC_VER]: Define.
+ * genkey.c, passphrase.c: Include util.h.
+
+ * w32-util.c (_gpgme_w32ce_get_debug_envvar): Fix return value.
+
+2010-11-15 Werner Koch <wk@g10code.com>
+
+ * data-compat.c (gpgme_data_new_from_filepart)
+ (gpgme_data_new_from_file) [W32CE && _MSC_VER]: Return not
+ GPG_ERR_NOT_IMPLEMENTED.
+
+ * w32-ce.h (HKEY_PERFORMANCE_DATA, HKEY_CURRENT_CONFIG, _IOLBF)
+ (abort) [_MSC_VER]: Provide these macros.
+
+ * ath.h [W32CE && _MSC_VER]: Include winsock2.h.
+
+ * ath.c (ath_read, ath_write) [W32CE && _MSC_VER]: Do not call
+ non-available functions.
+
+2010-11-04 Werner Koch <wk@g10code.com>
+
+ * w32-ce.h [_MSC_VER && W32CE]: Undef leave.
+ * export.c: Include util.h so that we get the above undef.
+
+ * memrchr.c: Remove. Used to be a replacement function required
+ by the formerly included assuan code.
+
+2010-11-03 Werner Koch <wk@g10code.com>
+
+ * debug.c (_gpgme_debug) [W32CE]: Replace locatime by GetLocalTime.
+
+ * signers.c (gpgme_signers_clear): Remove useless return.
+ Reported by Patrick Spendrin.
+
+ * w32-util.c: s/__inline__/GPG_ERR_INLINE/
+
+ * setenv.c: Include string.h due to our strerror replacement.
+
+ * w32-ce.h (access, bsearch): New macros.
+ * w32-ce.c (_gpgme_wince_access): New.
+ (RegQueryValueExA): Change DATA to a void*.
+ (_gpgme_wince_bsearch): New. Taken from glibc 2.6.
+
+ Guard include of sys/stat.h and sys/types.h.
+
+2010-11-02 Werner Koch <wk@g10code.com>
+
+ * data-fd.c (read, write, lseek) [W32CE && ! __MINGW32CE__]: New.
+ Taken from Pedro Alves Public Domain code.
+
+ * w32-ce.h (SHGetSpecialFolderPath): Remove our defines and
+ prototypes. We use the system provided prototypes now.
+ * w32-ce.c: Include shlobj.h
+ (_WIN32_IE): Define to 0x0400
+ (CreateFileA): New.
+ * w32-util.c: Explicitly include windows headers before util.h.
+ (_gpgme_w32ce_get_debug_envvar): Do not use wchar_t strings for
+ read_w32_registry_string.
+ (mkstemp): Use CreateFile instead of open.
+
+ * w32-io.c (handle_to_fd, fd_tohandle): Add. We need them for W32.
+ * w32-util.c (_WIN32_IE): Define to 0x0400.
+
+ * util.h [W32]: Include windows.h.
+ * w32-sema.c: Do not include windows.h directly.
+ * ath.c (ssize_t, pid_t)[_MSC_VER]: Add new types.
+ * gpgme.c (gpgme_result_ref, gpgme_result_unref): Do not use a
+ void pointer in pointer arithmetic.
+ * w32-util.c: Include util.h prior to ath.h. Don't include
+ windows.h directly.
+ (F_OK): Define if not defined.
+ * w32-ce.c: Include string.h.
+ (RegQueryValueExA): Use WINAPI modifier to match the declaration.
+ * vfs-create.c: Include string.h because under W32CE with MSC we
+ get a warning related to our strerror replacement.
+ * encrypt-sign.c: Include stdlib.h, string.h and errno.h.
+ * priv-io.h [W32CE]: Include w32-ce.h
+ * w32-ce.h: Include winsock2.h and ws2tcpip.h.
+ (_MSV_VER): Remove useless macro.
+ (pid_t): Add typedef.
+
+ Guard all includes of unistd.h and sys/time.h.
+
+2010-10-28 Marcus Brinkmann <marcus@g10code.com>
+
+ * opassuan.c (gpgme_op_assuan_transact_ext): Fix uninitialized
+ value use. Reported by Marc Mutz.
+
+2010-10-07 Werner Koch <wk@g10code.com>
+
+ * gpgme-tool.c (ARGP_ERR_UNKNOWN): Use EDEADLK if available.
+
+ * w32-util.c (_gpgme_w32ce_get_debug_envvar) [W32CE]: New.
+ * debug.c (debug_init) [W32CE]: Use new function.
+
+2010-09-16 Werner Koch <wk@g10code.com>
+
+ * import.c: Include "util.h".
+ (parse_import): Return GPG_ERR_MISSING_ISSUER_CERT.
+
+ * util.h: Add fallback define for GPG_ERR_MISSING_ISSUER_CERT.
+ * op-support.c: Include "util.h".
+ (_gpgme_parse_inv_recp): Handle new code 12.
+
+2010-09-02 Marcus Brinkmann <marcus@g10code.de>
+
+ * error.c (gpgme_err_code_to_errno): Fix cut and paste bug (thanks
+ to Marc Mutz).
+
+2010-09-01 Marcus Brinkmann <marcus@g10code.de>
+
+ * w32-io.c: Revert change from 2009-06-18, as it created a race
+ condition.
+
+2010-08-18 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.def: Add gpgme_err_code_from_syserror and gpgme_err_set_errno.
+ * libgpgme.vers: Likewise.
+ * gpgme.h.in (gpgme_error_from_errno): Fix return type to
+ gpgme_error_t.
+ (gpgme_err_code_from_syserror, gpgme_err_set_errno): New prototype.
+ (gpgme_error_from_syserror): New inline function (why are
+ gpgme_err_make_from_errno and gpgme_error_from_errno not inline
+ functions?).
+ * error.c (gpgme_error_from_errno): Fix return type to gpgme_error_t.
+ (gpgme_err_set_errno, gpgme_err_code_from_syserror): New functions.
+
+2010-08-03 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme-tool.c (result_encrypt_to_xml, result_sign_to_xml)
+ (result_verify_to_xml, result_import_to_xml)
+ (result_genkey_to_xml): Check vigorously for null pointers.
+
+ * w32-io.c (GPGCEDEV_IOCTL_ASSIGN_RVID): New macro.
+ (_gpgme_io_spawn): Use ASSIGN_RVID.
+
+2010-06-11 Marcus Brinkmann <marcus@g10code.de>
+
+ * w32-io.c (_gpgme_io_spawn): Remove debug printf.
+
+2010-06-10 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme-tool.c (gpgme_server): Use special hack for Windows CE to
+ get at stdin and stdout.
+
+ * engine-gpgsm.c (gpgsm_new): Translate returned achild_fds back
+ to child_fds.
+
+ * debug.h (TRACE_SUC6): New macro.
+ * w32-io.c (MAX_SLAFD): New macro.
+ (fd_table): New static variable.
+ (new_fd, release_fd): New functions.
+ (fd_to_handle, handle_to_fd, handle_to_socket): Remove macros.
+ (MAX_READERS, MAX_WRITERS): Increase to 64.
+ (notify_table): Increase to MAX_SLAFD.
+ (struct reader_context_s, struct writer_context_s): Add member
+ file_sock.
+ (reader, writer): Use file_hd vs file_sock to decide if socket
+ operations to use. Remove auto-detect mode.
+ (create_reader, create_writer): Set file_sock. Unblock pending
+ thread only if this is a pipe fd.
+ (_gpgme_io_pipe): Allocate fds from table and return slot indices
+ instead of windows handles. This allows to properly handle RVIDs.
+ (_gpgme_io_close): Handle dup'ed file descriptors.
+ (build_commandline) [HAVE_W32_SYSTEM]: Use RVID from fd table now.
+ (_gpgme_io_spawn): Use fd table now.
+ (_gpgme_io_fd2str): Use RVID from fd table now.
+ (_gpgme_io_dup): Implement using fd table.
+ (_gpgme_io_socket): Allocate fds from table.
+ (_gpgme_io_connect): Use fd from table.
+
+ * w32-glib-io.c (find_channel): Check that the slot is used.
+
+2010-06-09 Marcus Brinkmann <marcus@g10code.de>
+
+ * w32-io.c [HAVE_W32CE_SYSTEM]: Include assuan.h and winioctl.h.
+ (GPGCEDEV_IOCTL_UNBLOCK) [HAVE_W32CE_SYSTEM]: Define.
+ (set_synchronize) [HAVE_W32CE_SYSTEM]: Stub it out.
+ (is_socket): Allow to return -1 for auto-detect (old behaviour).
+ (is_socket) [HAVE_W32CE_SYSTEM]: Return -1.
+ (reader): Handle auto-detect case. Handle ctx->stop_me before
+ checking for EOF.
+ (destroy_reader) [HAVE_W32CE_SYSTEM]: Unblock a pending reader.
+ (writer): Handle auto-detect case. Handle ctx->stop_me with
+ ERROR_BUSY.
+ (destroy_writer) [HAVE_W32CE_SYSTEM]: Unblock a pending writer.
+ (_gpgme_io_pipe) [HAVE_W32CE_SYSTEM]: Implement in terms of a
+ half-pipe.
+ (build_commandline) [HAVE_W32CE_SYSTEM]: New function.
+ (_gpgme_io_spawn) [HAVE_W32CE_SYSTEM]: Implement it differently
+ for this platform.
+ (_gpgme_io_fd2str) [HAVE_W32CE_SYSTEM]: Implement it for RVIDs.
+ (_gpgme_io_dup) [HAVE_W32CE_SYSTEM]: Stub it out.
+
+ * gpgme-tool.c (result_add_timestamp): Add missing NULL argument.
+ (result_sign_to_xml): Protect against NULL fingerprint.
+ (struct server): New members input_fd, input_filename,
+ input_stream output_fd, output_filename, output_stream,
+ message_filename, message_stream.
+ (server_reset_fds): Deallocate those.
+ (server_parse_fd): New function.
+ (server_data_obj): Take optional filename argument and direction
+ argument. Also take new argument to return a filestream that
+ needs to be closed after destroying the data object.
+ Change all callers, too.
+ (input_notify, output_notify): Removed.
+ (cmd_input, cmd_output): New functions.
+ (gpgme_server): Do not register input and output notifier.
+ (register_commands): Use cmd_input and cmd_output.
+ (cmd_message): Rewritten to use server_parse_fd.
+ (cmd_delete, cmd_keylist): Fix inverted option check.
+ (main) [HAVE_W32CE_SYSTEM]: Sleep a bit to work around bug in ssh.
+
+ * genkey.c (gpgme_op_genkey): Return err with TRACE_ERR.
+
+2010-05-12 Marcus Brinkmann <marcus@g10code.de>
+
+ * conversion.c (_gpgme_timegm) [HAVE_W32_SYSTEM]: New static
+ function.
+ (_gpgme_parse_timestamp) [HAVE_W32_SYSTEM]: Use it.
+
+ * gpgme-tool.c (main): Protect call to setlocale with
+ HAVE_SETLOCALE.
+
+ * Makefile.am (system_components): Remove custom cppflags from
+ RCCOMPILE (because gpg-error adds -idirafter that makes RC bail.
+ [HAVE_W32CE_SYSTEM]: Add w32-ce.h and w32-ce.c, clear
+ libexec_PROGRAMS.
+ * w32-ce.h, w32-ce.c: New files.
+
+ * priv-io.h: Include <sys/types.h>
+ * util.h: Likewise.
+
+2010-05-11 Marcus Brinkmann <marcus@g10code.de>
+
+ * w32-util.c: Include ath.h
+ (HAVE_ALLOW_SET_FOREGROUND_WINDOW) [!HAVE_W32CE_SYSTEM]: Define
+ it.
+ (RTLD_LAZY, dlopen, dlsym,
+ dlclose) [!HAVE_ALLOW_SET_FORGROUND_WINDOW]: Don't define anymore.
+ (_gpgme_allow_set_foreground_window) [!HAVE_ALLOW_SET_FOREGROUND_WINDOW]:
+ Make it a stub.
+ (read_w32_registry_string): Use FooA variants of Windows functions
+ instead of Foo (which dispatches depending on UNICODE).
+ [!HAVE_W32CE_SYSTEM]: Don't check environment.
+ (w32_shgetfolderpath): Remove.
+ (find_program_at_standard_place): Call
+ SHGetSpecialFolderPath (which is available on all Windows systems
+ and also Windows CE).
+ (mkstemp): Use ath_self instead of getpid.
+ (_gpgme_mkstemp): Use GetTempPathA instead of GetTempPath.
+
+ * gpgme.h.in: Use _WIN32 instead of _MSC_VER. Include time.h for
+ time_t.
+
+2010-05-07 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine-g13.c, gpgme.c, engine-gpgsm.c, engine-gpg.c,
+ op-support.c, engine-assuan.c, gpgme-tool.c: Include <locale.h>
+ only if available with HAVE_LOCALE_H and conditionalize use of
+ LC_CTYPE on its definition.
+ * engine-gpgconf.c: Do not include <locale.h>.
+
+ * engine-gpgsm.c (gpgsm_new, start): Cast between int and
+ assuan_fd_t.
+ * assuan-support.c (my_pipe, my_close, my_read, my_write): Likewise.
+ * gpgme-tool.c (server_data_obj, server_reset_fds, gpgme_server),
+ (my_recvmsg, my_sendmsg, my_spawn): Likewise.
+ * engine-assuan.c (start): Likewise.
+ * engine-g13.c (start): Likewise.
+
+2010-05-06 Marcus Brinkmann <marcus@g10code.de>
+
+ * w32-glib-io.c, w32-io.c, w32-qt-io.cpp, w32-sema.c, w32-util.c:
+ Do not include <signal.h>.
+
+ * sign.c, data-user.c, conversion.c, debug.c, verify.c, data.c,
+ decrypt.c, delete.c, assuan-support.c, import.c, engine-gpgsm.c,
+ data-mem.c, op-support.c, w32-io.c, w32-util.c, data-compat.c: Use
+ gpg_error_from_syserror instead gpg_error_from_errno, and use
+ gpg_err_set_errno to set error number.
+ * setenv.c: Include <gpg-error.h> and define __set_errno to use
+ gpg_err_set_errno.
+ * gpgme-tool.c (ARGP_ERR_UNKNOWN): Define to EDEADLOCK (which is
+ mapped in Windows CE) instead of E2BIG (which is not).
+ (gt_import_keys): Initialize err.
+
+2010-04-19 Marcus Brinkmann <marcus@g10code.de>
+
+ * assuan-support.c (my_spawn): Cast to avoid warning.
+ * engine-g13.c (g13_new): Make ARGV array of pointer to const
+ char.
+ (g13_assuan_simple_command) [!USE_DESCRIPTOR_FUNCTION]: Don't define.
+ * ops.h (_gpgme_key_append_name): Same in prototype.
+ * key.c (_gpgme_key_append_name): Make SRC argument pointer to
+ const char.
+ * posix-util.c (_gpgme_get_uiserver_socket_path): Make HOMEDIR
+ const.
+ * vfs-mount.c (gpgme_op_vfs_transact_start): Never define this
+ potentially useful but currently unused function.
+ * vfs-create.c (gpgme_op_vfs_transact_start): Likewise.
+
+2010-04-16 Werner Koch <wk@g10code.com>
+
+ * w32-io.c (is_socket): New.
+ (reader, writer): Use it to figure out the API to use.
+
+2010-03-15 Werner Koch <wk@g10code.com>
+
+ * gpgme.h.in: Add autoconf template to set generated file to
+ read-only in an emacs buffer.
+
+2010-03-12 Werner Koch <wk@g10code.com>
+
+ * gpgme.h.in (GPGME_STATUS_SUCCESS): Use the right file for the
+ change; see below.
+
+ * passwd.c (op_data_t): New.
+ (passwd_start): Setup OPD.
+ (passwd_status_handler): Return GPG_ERR_NOT_SUPPORTED if needed.
+ * context.h (OPDATA_PASSWD): New.
+ * gpgme.h (GPGME_STATUS_SUCCESS): New.
+
+2010-03-09 Werner Koch <wk@g10code.com>
+
+ * engine-gpgsm.c (gpgsm_keylist): Try to start the agent.
+
+2010-02-17 Werner Koch <wk@g10code.com>
+
+ * posix-io.c (notify_table): Change implementation.
+ (notify_table_item_t, notify_table_size, notify_table_lock): New.
+ (_gpgme_io_close, _gpgme_io_set_close_notify): Adjust for new
+ implementation.
+
+2010-02-16 Werner Koch <wk@g10code.com>
+
+ * gpgme-tool.c (spacep, has_option, skip_options): New.
+ (cmd_export): Implement option --minimal.
+
+ * gpgme.h.in (GPGME_EXPORT_MODE_MINIMAL): New.
+ * export.c (export_start, export_ext_start): Implement it.
+ * engine-gpg.c (export_common): Ditto.
+
+2010-01-25 Werner Koch <wk@g10code.com>
+
+ * w32-io.c (_gpgme_io_connect): Fix return code check to make it work.
+
+ * version.c (do_subsystem_inits): Remove superfluous second
+ WSAStartup.
+
+2010-01-22 Werner Koch <wk@g10code.com>
+
+ * w32-io.c (writer): Try to use send first.
+ (reader): Try to use recv first.
+
+2010-01-08 Werner Koch <wk@g10code.com>
+
+ * engine-gpg.c (gpg_passwd): New.
+ (_gpgme_engine_ops_gpg): Register.
+ * passwd.c (parse_error): New.
+ (passwd_status_handler): Use it.
+
+2010-01-07 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme-tool.c (result_xml_write_cb_t, struct result_xml_state):
+ New types.
+ (MAX_TAGS): New macro.
+ (result_init, result_xml_indent, result_xml_tag_start)
+ (result_xml_tag_data, result_xml_tag_end, result_add_error)
+ (result_add_pubkey_algo, result_add_hash_algo, result_add_keyid)
+ (result_add_fpr, result_add_timestamp, result_add_sig_mode)
+ (result_add_value, result_add_string, result_encrypt_to_xml)
+ (result_decrypt_to_xml, result_sign_to_xml)
+ (result_verify_to_xml, result_import_to_xml)
+ (result_genkey_to_xml, result_keylist_to_xml)
+ (result_vfs_mount_to_xml): New functions.
+ (gt_result): Rewritten.
+
+2010-01-05 Werner Koch <wk@g10code.com>
+
+ * gpgme-tool.c (gt_passwd, cmd_passwd): New.
+ (register_commands): Register.
+
+ * gpgme.h.in (gpgme_op_passwd_start, gpgme_op_passwd): New.
+ * libgpgme.vers, gpgme.def: Add new functions.
+ * passwd.c: New.
+ * Makefile.am (main_sources): Add passwd.c
+ * engine.c, engine.h (_gpgme_engine_op_passwd): New.
+ * engine-backend.h (struct engine_ops): Add PASSWD.
+ * engine-gpgsm.c (gpgsm_passwd): New.
+ (_gpgme_engine_ops_gpgsm): Register.
+ (gpgsm_reset): Reset only if we have a conenction.
+
+ * gpgme.h.in (GPGME_PK_ECDSA, GPGME_PK_ECDH): New.
+ * gpgme.c (gpgme_pubkey_algo_name): Add them.
+
+2009-12-22 Marcus Brinkmann <marcus@g10code.de>
+
+ * debug.c: Test for TLS, not __GNUC__
+
+2009-12-15 Marcus Brinkmann <marcus@g10code.de>
+
+ * assuan-support.c (my_spawn): Calloc, not malloc, the fd_items.
+
+2009-12-10 Werner Koch <wk@g10code.com>
+
+ * debug.c (debug_init): Test on sgid process.
+
+2009-12-08 Marcus Brinkmann <marcus@g10code.de>
+
+ * Makefile.am (LTRCCOMPILE): Refactor with ...
+ (RCCOMPILE): ... this new macro.
+ (SUFFIXES): Add .lo.
+ (gpgme_res_ldflag): Removed.
+ (gpgme_res): Use libtool object file name here.
+ (libgpgme_la_LDFLAGS): Remove gpgme_res_ldflag usage.
+ (libgpgme_la_LIBADD): Add gpgme_res.
+
+ * ath.c (ath_self) [HAVE_W32_SYSTEM]: Fix typo.
+
+2009-12-02 Werner Koch <wk@g10code.com>
+
+ * gpgconf.c (gpgme_conf_arg_release): No return in a function
+ returning void. Reported by Wyllys Ingersoll.
+
+2009-12-01 Werner Koch <wk@g10code.com>
+
+ * gpgme-tool.c (cmd_getauditlog): Add flag --html.
+ (hlp_getauditlog): New.
+
+ * gpgme-tool.c (GT_GCC_A_SENTINEL, GT_GCC_A_PRINTF): New.
+ (gt_write_status): Use sentinel.
+ (argp_error, log_error): Use printf attribute.
+ (argp_parse): Remove extra argument to argp_error.
+ (_gt_progress_cb, gt_get_engine_info, gt_get_keylist_mode)
+ (gt_result): Add NULL arg.
+
+2009-11-26 Marcus Brinkmann <marcus@g10code.de>
+
+ * opassuan.c (opassuan_start): Allocate result structure before
+ beginning operation.
+
+2009-11-25 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme-tool.c (gpgme_server): Use assuan_fd_t and assuan_fdopen
+ on fds.
+
+2009-11-13 <wk@g10code.com>
+
+ * sign.c (_gpgme_sign_status_handler): Handle SIG_CREATED_SEEN.
+ * engine-uiserver.c (uiserver_sign): Make sending SENDER optional.
+
+2009-11-10 Marcus Brinkmann <marcus@g10code.de>
+
+ * op-support.c (_gpgme_op_reset): Instead of last change, only set
+ sub protocol if it is not the default.
+
+2009-11-10 Werner Koch <wk@g10code.com>
+
+ * op-support.c (_gpgme_op_reset): Ignore GPG_ERR_NOT_IMPLEMENTED
+ while setting the sub protocol.
+
+ * engine-uiserver.c (uiserver_new): Pass fdpassing flag to
+ assuan_socket_connect.
+ (set_recipients): Replace fingerprint by user id.
+
+ * engine-gpgsm.c (set_recipients): Fix non-terminating loop in
+ case of a missing key.
+
+2009-11-10 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.c (gpgme_new): Set default sub protocol.
+ * gpgme-tool.c: Implement get sub protocol.
+
+ * gpgme.h.in (gpgme_get_sub_protocol): Add prototype.
+ * gpgme.def, libgpgme.vers: Add gpgme_get_sub_protocol.
+ * context.h (struct gpgme_context): New member sub_protocol.
+ * gpgme.c (gpgme_set_sub_protocol): Set CTX->sub_protocol.
+ (gpgme_get_sub_protocol): New function.
+ * op-support.c (_gpgme_op_reset): Set sub protocol.
+
+ * Makefile.am (uiserver_components): New variable.
+ (main_sources): Add it.
+ * ops.h, key.c (_gpgme_key_append_name): Take CONVERT argument,
+ implement it. Adjust callers.
+ (gpgme_key_from_uid): New function.
+ * gpgme.h.in (gpgme_protocol_t): Add GPGME_PROTOCOL_DEFAULT.
+ (gpgme_encrypt_flags_t): Add GPGME_ENCRYPT_PREPARE,
+ GPGME_ENCRYPT_EXPECT_SIGN.
+ (gpgme_set_sub_protocol, gpgme_key_from_uid): New functions.
+ * libgpgme.vers, gpgme.def: Add new functions.
+ * gpgme.c (gpgme_set_protocol): Add UIServer protocol.
+ (gpgme_set_sub_protocol): New function.
+ (gpgme_get_protocol_name): Add UIServer and default protocol.
+ * assuan-support.c: Return correct error values, implement
+ socketpair for POSIX.
+ * priv-io.h, posix-io.c, w32-io.c, w32-glib-io.c,
+ w32-qt-io.cpp (_gpgme_io_spawn): Add ATFORK and ATFORKVALUE
+ arguments. Implement it for POSIX. Adjust all callers.
+ * engine.h, engine-backend.h (_gpgme_engine_set_protocol)
+ (_gpgme_engine_op_decrypt_verify): New prototypes. Adjust all
+ users.
+ * engine.c (engine_ops, gpgme_get_engine_info): Add UIServer
+ engine.
+ (_gpgme_engine_set_protocol, _gpgme_engine_op_decrypt_verify): New
+ function.
+ * decrypt-verify.c (decrypt_verify_start): Call
+ _gpgme_engine_op_decrypt_verify.
+ * util.h, posix-util.c,
+ w32-util.c (_gpgme_get_uiserver_socket_path): New function.
+ * engine-gpgsm.c (gpgsm_set_fd): Fix _gpgme_io_pipe invocation.
+ * gpgme-tool.c: Some support for UIServer protocol.
+ * engine-uiserver.c: New file.
+
+2009-11-09 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine-gpgsm.c (gpgsm_new): Close server side FDs.
+
+2009-11-06 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme-tool.c (struct gpgme_tool): New members write_data and
+ write_data_hook.
+ (gt_write_data): New function.
+ (gt_result): Output vfs_mount result.
+ (server_write_data): New function.
+ (gpgme_server): Initialize write_data members.
+
+2009-11-05 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine-g13.c (struct engine_g13): Remove members RESULT_CB and
+ RESULT_CB_VALUE.
+ (g13_assuan_simple_command, status_handler): Don't use those anymore.
+ (g13_transact): Remove them from argument list, too.
+ * vfs-mount.c (_gpgme_vfs_mount_status_handler): New function.
+ (_gpgme_op_vfs_mount): Pass it to transact.
+
+ * engine-assuan.c (llass_new): Update use of assuan_socket_connect.
+ * engine-gpgsm.c (gpgsm_new): Update use of assuan_pipe_connect.
+ * engine-g13.c (g13_new): Likewise.
+
+ * priv-io.h (IOSPAWN_FLAG_NOCLOSE): New flag.
+ * w32-io.c (_gpgme_io_spawn): Implement this flag.
+ * posix-io.c (_gpgme_io_spawn): Likewise.
+ * w32-glib-io.c (_gpgme_io_spawn): Likewise.
+ * assuan-support.c (my_spawn): Set this flag.
+
+ * decrypt.c (gpgme_op_decrypt_start): Fix use of debug macro.
+ * decrypt-verify.c (gpgme_op_decrypt_verify_start): Likewise.
+ * delete.c (gpgme_op_delete_start): Likewise.
+ * edit.c (gpgme_op_edit_start, gpgme_op_card_edit_start):
+ Likewise.
+ * encrypt.c (gpgme_op_encrypt_start): Likewise.
+ * encrypt-sign.c (gpgme_op_encrypt_sign_start): Likewise.
+ * export.c (gpgme_op_export_start, gpgme_op_export_ext_start)
+ (gpgme_op_export_keys_start, gpgme_op_export_keys): Likewise.
+ * genkey.c (gpgme_op_genkey_start): Likewise.
+ * getauditlog.c (gpgme_op_getauditlog_start): Likewise.
+ * import.c (gpgme_op_import_start, gpgme_op_import_keys_start):
+ Likewise.
+ * opassuan.c (gpgme_op_assuan_transact_start): Likewise.
+ * sign.c (gpgme_op_sign_start): Likewise.
+ * verify.c (gpgme_op_verify_start): Likewise.
+ * vfs-create.c (gpgme_op_vfs_create): Likewise.
+ * vfs-mount.c (gpgme_op_vfs_mount): Likewise.
+
+2009-11-04 Marcus Brinkmann <marcus@g10code.de>
+
+ * ath.h (ath_self): New prototype. Include <stdint.h>
+ * ath.c, ath-pth.c, ath-pthread.c (ath_self): New function.
+ * debug.h: Rewrite most macros to beautify debug output.
+ (_gpgme_debug_buffer): Remove tagname and tag argument.
+ (_gpgme_debug_frame_begin, _gpgme_debug_frame_end): New prototypes.
+ * debug.c: Include <time.h>. Don't include assuan.h.
+ (frame_nr, FRAME_NR): New thread-specific variable and macro.
+ (debug_init): Do not initialize assuan. Call _gpgme_debug after
+ initialization instead using printf directly.
+ (_gpgme_debug): Do not call debug_init (we now ensure proper
+ initialization by user). Add timestamp and thread/process ID.
+ (_gpgme_debug_buffer): Do not take tagname and tag argument.
+ (_gpgme_debug_frame_begin, _gpgme_debug_frame_end): New functions.
+ * version.c (gpgme_check_version_internal, gpgme_check_version):
+ Fix debug string. Do not initialize assuan.
+ * posix-io.c (get_max_fds): Use 0 not NULL (nicer debug output).
+
+2009-11-04 Werner Koch <wk@g10code.com>
+
+ * gpgme-tool.c (register_commands): Add HELP feature.
+
+2009-11-03 Werner Koch <wk@g10code.com>
+
+ * gpgme.h.in (GPGME_PROTOCOL_UISERVER): New.
+
+2009-11-03 Marcus Brinkmann <marcus@g10code.de>
+
+ * Makefile.am (main_sources): Change g13.c to vfs-mount.c. Add
+ vfs-create.c
+ * vfs-create.c: New file.
+ * g13.c: Renamed to ...
+ * vfs-mount.c: ... this new file.
+ * gpgme.h.in (gpgme_op_vfs_create): New prototype.
+ * gpgme.def, libgpgme.vers: Add gpgme_op_vfs_create.
+ * gpgme-tool.c (gt_vfs_create, cmd_vfs_create): New functions.
+ (register_commands): Add VFS_CREATE and CREAET.
+
+2009-11-02 Marcus Brinkmann <marcus@g10code.de>
+
+ * debug.h (_gpgme_debug_buffer): Make TAG argument const const.
+ * debug.c (_gpgme_debug_buffer): Likewise.
+ * gpgme-tool.c (input_notify, output_notify): Adjust type to new
+ assuan interface.
+ * decrypt.c (gpgme_op_decrypt_result): Remove unused variable.
+ * opassuan.c (gpgme_op_assuan_transact): Fix return value.
+
+2009-10-30 Marcus Brinkmann <marcus@g10code.de>
+
+ * Makefile.am (noinst_PROGRAMS): New target gpgme-tool.
+ (gpgme_tool_LDADD): New variable.
+ * gpgme-tool.c: New file.
+ * ops.h (_gpgme_sig_notation_clearm _gpgme_signers_clear): New
+ prototypes.
+ * gpgme.c (gpgme_set_protocol): Allow GPGME_PROTOCOL_GPGCONF (when
+ had that gone missing?).
+ (_gpgme_sig_notation_clear): New function without debug output.
+ (gpgme_release): Call it and _gpgme_signers_clear.
+ * signers.c (_gpgme_signers_clear): New function without debug output.
+ * g13.c (gpgme_op_vfs_mount): Add debug output.
+ * assuan-support.c (my_spawn): Allow fd_child_list to be NULL.
+ * conversion.c (_gpgme_encode_percent_string): Fix infinite loop.
+ * debug.h: Put tag in front of debug lines, should make for nicer
+ output.
+ * engine-assuan.c (llass_new): Use our new system hooks for libassuan.
+ * engine-g13.c (g13_new): Remove redundant assuan context allocation.
+ * version.c (gpgme_check_version_internal): Delay debug output
+ until after gpgme_check_version was called.
+
+2009-10-28 Marcus Brinkmann <marcus@g10code.de>
+
+ * signers.c, encrypt-sign.c, encrypt.c, delete.c, keylist.c,
+ edit.c, import.c, export.c: Fix last change in debug output.
+
+2009-10-27 Marcus Brinkmann <marcus@g10code.de>
+
+ * edit.c (gpgme_op_edit_start, gpgme_op_edit)
+ (gpgme_op_card_edit_start, gpgme_op_card_edit): Add debug output.
+ * encrypt-sign.c (gpgme_op_encrypt_sign_start)
+ (gpgme_op_encrypt_sign): Likewise.
+ * encrypt.c (gpgme_op_encrypt_start, gpgme_op_encrypt)
+ (gpgme_op_encrypt_result): Likewise.
+ * export.c (gpgme_op_export_start, gpgme_op_export)
+ (gpgme_op_export_ext_start, gpgme_op_export_ext)
+ (gpgme_op_export_keys_start, gpgme_op_export_keys): Likewise.
+ * genkey.c (gpgme_op_genkey_start, gpgme_op_genkey)
+ (gpgme_op_genkey_result): Likewise.
+ * getauditlog.c (gpgme_op_getauditlog_start)
+ (gpgme_op_getauditlog): Likewise.
+ * import.c (gpgme_op_import_result, gpgme_op_import_start)
+ (gpgme_op_import): Likewise.
+ * keylist.c (gpgme_op_keylist_result, keylist_colon_handler)
+ (gpgme_op_keylist_start, gpgme_op_keylist_ext_start)
+ (gpgme_op_keylist_next, gpgme_op_keylist_end, gpgme_get_key): Likewise.
+ * opassuan.c (gpgme_op_assuan_transact_start)
+ (gpgme_op_assuan_transact_ext, gpgme_op_assuan_result)
+ (gpgme_op_assuan_transact): Likewise.
+ * signers.c (gpgme_signers_add, gpgme_signers_clear): Likewise.
+ * trustlist.c (gpgme_op_trustlist_start)
+ (gpgme_op_trustlist_next, gpgme_op_trustlist_end): Likewise.
+ * verify.c (gpgme_op_verify_start, gpgme_op_verify)
+ (gpgme_op_verify_result): Likewise.
+
+2009-10-26 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.h.in (struct gpgme_io_event_done_data)
+ (gpgme_io_event_done_data_t): New types.
+ (struct _gpgme_op_assuan_result): Deprecate the err member.
+ (gpgme_op_assuan_result): Deprecate (for now).
+ (gpgme_op_assuan_transact_ext): New prototype.
+ (gpgme_op_assuan_transact): Deprecate.
+ (struct _gpgme_op_g13_result): Replace with ...
+ (struct _gpgme_op_vfs_mount_result): ... this.
+ (gpgme_op_g13_mount): Replace with ...
+ (gpgme_op_vfs_mount): ... this.
+ * gpgme.def (gpgme_op_assuan_transact_ext, gpgme_wait_ext)
+ (gpgme_op_vfs_mount_result, gpgme_op_vfs_mount): New.
+ (gpgme_op_g13_mount): Remove.
+ * libgpgme.vers: Likewise.
+ * engine-backend.h (struct engine_ops): Remove RESULT_CB and
+ RESULT_CB_VALUE args in opassuan_transact member. Add CANCEL_OP
+ member.
+ * ops.h (_gpgme_cancel_with_err, _gpgme_wait_on_condition): Add
+ OP_ERR argument.
+ (_gpgme_wait_one_ext): New prototype.
+ * context.h (ctx_op_data_id_t): Add OPDATA_VFS_MOUNT.
+ * engine-g13.c (g13_cancel_op): New function.
+ (parse_status): Remove declaration.
+ (g13_assuan_simple_command): Do nothing with status lines for now.
+ (status_handler): Update opaque value access.
+ (_gpgme_engine_ops_g13): Add new cancel_op member.
+ * gpgme.c (_gpgme_cancel_with_err): Add new parameter OP_ERR.
+ Handle operational errors.
+ (gpgme_cancel, gpgme_io_read, gpgme_io_write): Add debug output.
+ * data.c (_gpgme_data_inbound_handler)
+ (_gpgme_data_outbound_handler): Adjust opaque value access.
+ * engine-gpg.c (command_handler, status_handler)
+ (colon_line_handler): Likewise.
+ * engine-gpgsm.c (status_handler): Likewise.
+ * engine-gpg.c (_gpgme_engine_ops_gpg): Add cancel_op member.
+ * engine-gpgsm.c (_gpgme_engine_ops_gpgsm): Likewise.
+ * g13.c: Rewritten (and will be rewritten again).
+ * engine.h (_gpgme_engine_op_assuan_transact): Remove result_cb
+ and result_cb_value parameters from prototype.
+ (_gpgme_engine_cancel_op): New prototype.
+ * engine.c (engine_ops) [! ENABLE_ASSUAN]: Add missing comma.
+ (_gpgme_engine_op_assuan_transact): Remove result_cb and
+ result_cb_value parameter.
+ (_gpgme_engine_cancel_op): New function.
+ * wait.h (_gpgme_run_io_cb): Add new argument OP_ERR.
+ (struct io_cb_data): New struct to pass opaque data and get a
+ op_err return value. Needed because we can't modify I/O callback
+ handler signature because it is exposed to the user.
+ * wait.c (_gpgme_run_io_cb): Add OP_ERR parameter. Handle
+ operational errors.
+ * wait-user.c (_gpgme_user_io_cb_handler): Handle operational
+ errors.
+ * wait-private.c (_gpgme_wait_on_condition): New argument to
+ retrieve the operational result. Handle operational errors in
+ session based protocols.
+ (_gpgme_wait_one_ext): New function.
+ (_gpgme_wait_one): Pass argument in invocation of
+ _gpgme_wait_on_condition.
+ * wait-global.c (struct ctx_list_item): Add member OP_ERR.
+ (ctx_done): New argument OP_ERR.
+ (ctx_wait): New argument OP_ERR.
+ (gpgme_wait_ext): New function based on gpgme_wait but handling
+ operational errors.
+ (gpgme_wait): Implement in term of gpgme_wait_ext.
+ * keylist.c (gpgme_op_keylist_next): Pass argument in invocation
+ of _gpgme_wait_on_condition.
+ * trustlist.c (gpgme_op_trustlist_next): Pass argument in
+ invocation of _gpgme_wait_on_condition.
+ * engine-assuan.c (struct engine_llass): Replace members RESULT_CB
+ and RESULT_CB_VALUE by LAST_OP_ERR.
+ (_gpgme_engine_assuan_last_op_err): Add this hack function.
+ (llass_cancel_op): New function.
+ (_gpgme_engine_llass_ops): Add cancel_op member.
+ (llass_status_handler): Update opaque value access.
+ (llass_transact): Remove RESULT_CB and RESULT_CB_VALUE arguments.
+ * opassuan.c: Move compat hacks to the end of file.
+ (opassuan_start): Do not set OPD->result.err.
+ Do not pass RESULT_Cb and CTX to _gpgme_engine_op_assuan_transact.
+ (gpgme_op_assuan_transact_ext): New function.
+
+ * debug.h (DEBUG_GLOBAL): New debug level.
+ * conversion.c (gnupg_errors, _gpgme_map_gnupg_error): Removed.
+ * data-user.c (gpgme_data_new_from_cbs): Add debug output.
+ * data-fd.c (gpgme_data_new_from_fd): Likewise.
+ * data-stream.c (gpgme_data_new_from_stream): Likewise.
+ * decrypt.c (gpgme_op_decrypt_result, gpgme_op_decrypt_start)
+ (gpgme_op_decrypt): Likewise.
+ * delete.c (gpgme_op_delete_start, gpgme_op_delete): Likewise.
+ * decrypt-verify.c (gpgme_op_decrypt_verify_start)
+ (gpgme_op_decrypt_verify): Likewise.
+ * sign.c (gpgme_op_sign_result): Fix debug message.
+ * data-mem.c (gpgme_data_new): Improve debug output.
+ * verify.c (parse_trust): Use atoi instead of
+ _gpgme_map_gnupg_error.
+ * decrypt.c (_gpgme_decrypt_status_handler): Likewise.
+
+2009-10-22 Marcus Brinkmann <marcus@g10code.de>
+
+ * Makefile.am: Remove @NETLIBS@ from LIBADDs.
+ (g13_components): New variable.
+ (main_sources): Add $(g13_components).
+ * g13.c, engine-g13.c: New files.
+ * engine.c (engine_ops): Check for assuan for assuan engine, add
+ g13 engine.
+ * util.h (_gpgme_get_g13_path, _gpgme_encode_percent_string): New
+ prototypes.
+ * conversion.c (_gpgme_encode_percent_string): New function.
+ * gpgme.h.in (gpgme_protocol_t): Add GPGME_PROTOCOL_G13.
+ (struct _gpgme_op_g13_result, gpgme_g13_result_t): New types.
+ (gpgme_op_g13_mount): New function.
+ * gpgme.def, libgpgme.vers: Add gpgme_op_g13_mount.
+ * gpgme.c (gpgme_set_protocol): Allow GPGME_PROTOCOL_G13.
+ (gpgme_get_protocol_name): Add GPGME_PROTOCOL_G13.
+ * posix-util.c (_gpgme_get_g13_path): New function.
+ * w32-util.c (_gpgme_get_g13_path): New function.
+ * engine-backend.h (_gpgme_engine_ops_g13): New declaration.
+
+2009-10-20 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme-config.in (netlibs): Remove.
+ (assuan_cflags, assuan_libs): Add.
+
+ * Makefile.am (assuan_cppflags, assuan_libobjs): Removed.
+ (gpgsm_components): Move engine-assuan.c to ...
+ (assuan_components): ... this new variable.
+ (main_sources): Add this new variable.
+ (AM_CPPFLAGS): Remove $(assuan_cppflags).
+ (AM_CFLAGS): Add @LIBASSUAN_CFLAGS@.
+ (libgpgme_la_DEPENDENCIES, libgpgme_pth_la_DEPENDENCIES)
+ (libgpgme_glib_la_DEPENDENCIES, libgpgme_qt_la_DEPENDENCIES)
+ (libgpgme_pthread_la_DEPENDENCIES): Remove $(assuan_libobjs).
+ (libgpgme_la_LIBADD, libgpgme_pth_la_LIBADD)
+ (libgpgme_glib_la_LIBADD, libgpgme_qt_la_LIBADD))
+ (libgpgme_pthread_la_LIBADD): Replace $(assuan_libobjs) by
+ @LIBASSUAN_LIBS@.
+ * priv-io.h [!HAVE_W32_SYSTEM]: Declare _gpgme_io_recvmsg,
+ _gpgme_io_sendmsg, _gpgme_io_waitpid.
+ * engine-backend.h: Define with [ENABLE_ASSUAN] instead
+ of [ENABLE_GPGSM].
+ * posix-io.c (_gpgme_io_waitpid): Make non-static.
+ * util.h (ENABLE_ASSUAN): Declar _gpgme_assuan_system_hooks,
+ _gpgme_assuan_malloc_hooks, _gpgme_assuan_log_cb.
+ * engine-gpgsm.c: Don't map assuan error codes. Use
+ assuan_release instead of assuan_disconnect.
+ (map_assuan_error): Remove function.
+ (gpgsm_new): Use new assuan context interface.
+ * engine-assuan.c: Use assuan_release instead of
+ assuan_disconnect.
+ (llass_new): Use new assuan context interface.
+
+2009-10-07 <wk@g10code.com>
+
+ * priv-io.h [W32]: Include windows.h instead of sys/socket.h.
+
+2009-08-06 Werner Koch <wk@g10code.com>
+
+ * op-support.c (_gpgme_parse_inv_recp): Allow for no fingerprint.
+
+ * engine-gpgsm.c (gpgsm_sign): Hook up the status func for the
+ SIGNER command.
+ * gpgme.h.in (GPGME_STATUS_INV_SGNR, GPGME_STATUS_NO_SGNR): New.
+ * sign.c (op_data_t): Add fields IGNORE_INV_RECP and INV_SGNR_SEEN.
+ (_gpgme_op_sign_init_result): Factor code out to ...
+ (sign_init_result): .. new. Init new fields.
+ (sign_start): Use sign_init_result.
+ (_gpgme_sign_status_handler): Take care of the new INV_SGNR.
+ Return an error if no signature has been created.
+
+2009-07-07 Werner Koch <wk@g10code.com>
+
+ * engine-gpgsm.c (struct engine_gpgsm): Add fields
+ input_helper_data and input_helper_memory.
+ (close_notify_handler): Release these new fields.
+ (gpgsm_import): Implement the keyarray feature.
+
+ * engine-gpg.c (gpg_import): Actually return GPG_ERR_INV_VALUE.
+
+ * engine-gpgsm.c (gpgsm_import): Return an error for unknown data
+ encodings.
+
+2009-06-22 Marcus Brinkmann <marcus@g10code.de>
+
+ * debug.h: Everywhere, use %p instead of 0x%x to print pointer.
+ [HAVE_STDINT_H]: Include <stdint.h>.
+ (_TRACE, TRACE, TRACE0, TRACE1, TRACE2, TRACE3, TRACE6): Cast tag
+ to (uintptr_t) before casting it to (void*) to silence GCC
+ warning.
+
+ * gpgme.h.in (_GPGME_DEPRECATED_OUTSIDE_GPGME): New macro.
+ * sign.c (_GPGME_IN_GPGME): Define it.
+ * keylist.c (_GPGME_IN_GPGME): Define it.
+
+ * debug.c (_gpgme_debug_begin, _gpgme_debug_add): Handle error in
+ vasprintf and asprintf.
+
+ * priv-io.h: Include <sys/socket.h>. Declare _gpgme_io_connect.
+
+2009-06-18 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.h.in (GPGME_CONF_PATHNAME): Revert last change, it's
+ back! (GPA still uses it...).
+
+ * gpgme.def: Fix stupid typo.
+ * w32-io.c (_gpgme_io_pipe): Add missing declaration.
+
+ * gpgme.h.in (GPGME_CONF_PATHNAME): Remove obsolete macro.
+
+ * w32-io.c (_gpgme_io_pipe): Allocate reader/writer thread right
+ away.
+ (_gpgme_io_read, _gpgme_io_write, _gpgme_io_select)
+ (_gpgme_io_dup): Never allocate threads here.
+ (find_writer, find_reader): Check return value of thread creation
+ function.
+
+ * context.h (CTX_OP_DATA_MAGIC): New macro.
+ (struct ctx_op_data): New member MAGIC.
+ * op-support.c (_gpgme_op_data_lookup): Initialize magic.
+ * gpgme.c (gpgme_result_unref, gpgme_result_ref): Check magic.
+
+2009-06-16 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.c (gpgme_result_unref): Hot fix to release a lock.
+
+ * gpgme.c (result_ref_lock): New global variable.
+ (gpgme_result_ref, gpgme_result_unref): use it.
+
+2009-06-16 Werner Koch <wk@g10code.com>
+
+ * version.c: Include stdlib.h.
+
+ * gpgme.h.in (gpgme_data_encoding_t): Add GPGME_DATA_ENCODING_URL,
+ GPGME_DATA_ENCODING_URLESC, GPGME_DATA_ENCODING_URL0.
+ * data.c (gpgme_data_set_encoding): Adjust for new values.
+ * engine-gpg.c (string_from_data): New.
+ (gpg_import): Implement --fetch-key feature.
+
+ * gpgme.h.in (gpgme_op_export_keys_start, gpgme_op_export_keys): New.
+ * gpgme.def, libgpgme.vers: Add them.
+ * export.c (gpgme_op_export_keys_start, gpgme_op_export_keys): New.
+ (export_keys_start): New.
+
+ * gpgme.h.in (gpgme_export_mode_t, GPGME_EXPORT_MODE_EXTERN): New.
+ (gpgme_op_export_start, gpgme_op_export, gpgme_op_export_ext_start)
+ (gpgme_op_export_ext): Change arg RESERVED to MODE of new
+ compatible type.
+ * export.c (gpgme_export_ext_start, gpgme_op_export)
+ (gpgme_op_export_ext_start, gpgme_op_export_ext): Ditto.
+ (export_start): Ditto.
+ * engine.c (_gpgme_engine_op_export): Ditto.
+ * engine-backend.h (struct engine_ops): Ditto.
+ * engine-gpgsm.c (gpgsm_export, gpgsm_export_ext): Ditto.
+ * engine-gpg.c (gpg_export, gpg_export_ext): Ditto. Implement
+ mode EXTERN.
+ (gpg_export, gpg_export_ext): Factor common code out to ..
+ (export_common): .. this.
+
+ * gpgme.h.in (gpgme_op_import_keys_start, gpgme_op_import_keys): New.
+ * gpgme.def, libgpgme.vers: Add them.
+ * import.c (gpgme_op_import_keys_start, gpgme_op_import_keys): New.
+ (_gpgme_op_import_keys_start): New.
+ * engine.c (_gpgme_engine_op_import): Add arg KEYARRAY.
+ * engine-backend.h (struct engine_ops): Ditto.
+ * engine-gpgsm.c (gpgsm_import): Ditto. Not functional.
+ * engine-gpg.c (gpg_import): Ditto. Implement it.
+
+2009-06-15 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.h.in (gpgme_result_ref, gpgme_result_unref): Add
+ prototypes.
+ * gpgme.def, libgpgme.vers (gpgme_result_ref, gpgme_result_unref):
+ Add these.
+ * context.h (struct ctx_op_data): Add member "references".
+ * gpgme.c (gpgme_result_ref, gpgme_result_unref): New functions.
+ (_gpgme_release_result): Use gpgme_result_unref.
+ * op-support.c (_gpgme_op_data_lookup): Initialize references.
+
+2009-06-12 Werner Koch <wk@g10code.com>
+
+ * gpgme-w32spawn.c (translate_get_from_file): Parse optional spawn
+ flags. Add new arg R_FLAGS. Fix segv on file w/o LF.
+ (translate_handles): Add new arg R_FLAGS. Avoid possible segv.
+ (main): Pass flags for my_spawn.
+ (my_spawn): Add arg FLAGS and implement AllowSetForegroundWindow.
+
+ * priv-io.h (IOSPAWN_FLAG_ALLOW_SET_FG): New.
+ * w32-io.c (_gpgme_io_spawn): Add arg FLAGS and implement it.
+ * w32-glib-io.c (_gpgme_io_spawn): Ditto.
+ * w32-qt-io.cpp (_gpgme_io_spawn): Ditto.
+ * posix-io.c (_gpgme_io_spawn): Add dummy arg FLAGS.
+ * engine-gpg.c (start): Call spawn with new flag.
+
+ * w32-util.c (_gpgme_allow_set_foregound_window): Rename to
+ _gpgme_allow_set_foreground_window. Change all callers.
+ * posix-util.c (_gpgme_allow_set_foreground_window): Ditto.
+
+2009-06-10 Werner Koch <wk@g10code.com>
+
+ * w32-util.c (_gpgme_allow_set_foregound_window): Add trace support.
+
+2009-06-09 Werner Koch <wk@g10code.com>
+
+ * engine-gpg.c (gpg_io_event): Test for cmd.fd.
+
+ * version.c (gpgme_check_version_internal): Make result const.
+
+ * gpgme.c: Include priv-io.h.
+ (gpgme_io_read, gpgme_io_write): New.
+ * libgpgme.vers (GPGME_1.1): Add them.
+ * gpgme.def: Ditto.
+
+ * Makefile.am (main_sources): Remove gpgme.h.
+ (include_HEADERS): Rename to nodist_include_HEADERS so that a
+ VPATH build won't use the distributed one.
+
+ * util.h (GPG_ERR_NOT_OPERATIONAL): Define.
+
+2009-05-28 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.h.in (gpgme_check_version_internal): New prototype.
+ (gpgme_check_version): New macro, overriding function of the same
+ name.
+ * libgpgme.vers, gpgme.def: Add gpgme_check_version_internal.o
+ * context.h (_gpgme_selftest): New variable declaration.
+ * version.c: Include "context.h".
+ (gpgme_check_version): Set _gpgme_selftest on success.
+ (gpgme_check_version_internal): New function.
+ * gpgme.c (_gpgme_selftest): Define it.
+ (gpgme_new): Check the selftest result.
+
+2009-05-18 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.h.in (gpgme_encrypt_flags_t): Add
+ GPGME_ENCRYPT_NO_ENCRYPT_TO.
+ * engine-gpg.c (gpg_encrypt): Pass --no-encrypt-to to gpg if
+ GPGME_ENCRYPT_NO_ENCRYPT_TO flag is set.
+
+2009-05-14 Werner Koch <wk@g10code.com>
+
+ * gpgme.h.in (gpgme_status_code_t): Explicitly initialize for
+ better maintainability and to help debugging.
+
+2009-05-05 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.h.in: Add compile time check for _FILE_OFFSET_BITS.
+
+2009-04-15 Marcus Brinkmann <marcus@g10code.de>
+
+ * posix-io.c (_gpgme_io_socket, _gpgme_io_connect): New functions.
+ * w32-io.c (_gpgme_io_connect): Fix stupid error.
+
+2009-04-08 Marcus Brinkmann <marcus@g10code.de>
+
+ * w32-glib-io.c (giochannel_table): New members used, fd, socket.
+ (find_channel): Drop CREATE argument.
+ (new_dummy_channel_from_fd, new_channel_from_fd)
+ (new_channel_from_socket): New functions.
+ (_gpgm_io_fd2str): Implement for sockets.
+ (_gpgme_io_write, _gpgme_io_read): Translate EAGAIN errors
+ correctly.
+ (_gpgme_io_pipe): Fix for new channel bookkeeping.
+ (_gpgme_io_close, _gpgme_io_dup): Likewise.
+ (wsa2errno, _gpgme_io_socket, _gpgme_io_connect): New.
+ * w32-io.c (MAX_READERS, MAX_WRITERS): Bump up to 40.
+ (wsa2errno, _gpgme_io_socket, _gpgme_io_connect): New.
+ * w32-qt-io.cpp (_gpgme_io_socket, _gpgme_io_connect): New stubs.
+ * version.c [HAVE_W32_SYSTEM]: Include "windows.h.
+ (do_subsystem_inits) [HAVE_W32_SYSTEM]: Call WSAStartup.
+ * engine-assuan.c (llass_status_handler): Ignore EAGAIN errors.
+
+2009-03-18 Werner Koch <wk@g10code.com>
+
+ * gpgme.h.in (GPGME_KEYLIST_MODE_EPHEMERAL): New.
+ * engine-gpgsm.c (gpgsm_keylist): Send new option.
+
+2009-03-13 Werner Koch <wk@g10code.com>
+
+ * gpgme-config.in: Make sure locale is set to C.
+
+2009-02-24 Werner Koch <wk@g10code.com>
+
+ * gpgme.h.in (struct _gpgme_op_assuan_result): New.
+ (gpgme_assuan_result_t): New.
+ (gpgme_op_assuan_result): Change return type.
+ (struct _gpgme_assuan_sendfnc_ctx)
+ (gpgme_assuan_sendfnc_ctx_t, gpgme_assuan_sendfnc_t):Remove.
+ (gpgme_assuan_inquire_cb_t): Changed.
+ * opassuan.c (op_data_t): Make use of a result structure.
+ (gpgme_op_assuan_result): Change return type.
+ (opassuan_start): Use result structure.
+ (result_cb): Ditto.
+ * engine-assuan.c (struct _gpgme_assuan_sendfnc_ctx): Remove.
+ (inquire_cb_sendfnc): Remove.
+ (inquire_cb): Change for new callback scheme. Not yet finished.
+ (llass_status_handler): Allow sending a CANCEL from the inquire CB.
+
+2009-02-04 Werner Koch <wk@g10code.com>
+
+ * w32-glib-io.c (_gpgme_io_spawn): Make ARGV argument const to
+ match prototype.
+ * w32-qt-io.cpp (_gpgme_io_spawn): Ditto.
+
+2009-02-03 Werner Koch <wk@g10code.com>
+
+ * gpgme.h.in (struct _gpgme_subkey): Add fields IS_CARDKEY and
+ CARD_NUMBER..
+ * key.c (gpgme_key_unref): Release field CARD_NUMBER.
+ * keylist.c (keylist_colon_handler): Factor common code out to ...
+ (parse_sec_field15): New. Set card number.
+
+2009-01-26 Werner Koch <wk@g10code.com>
+
+ * opassuan.c, dirinfo.c, engine-assuan.c: New.
+ * Makefile.am: Add them.
+ * engine-backend.h: Add _gpgme_engine_ops_assuan.
+ (struct engine_ops): Add field OPASSUAN_TRANSACT. Update all
+ engine intializers.
+ * Makefile.am (gpgsm_components): Add engine-assuan.c.
+ * gpgme.h.in (gpgme_protocol_t): Add GPGME_PROTOCOL_ASSUAN.
+ (gpgme_assuan_data_cb_t, gpgme_assuan_sendfnc_ctx_t)
+ (gpgme_assuan_inquire_cb_t, gpgme_assuan_status_cb_t): New.
+ (gpgme_op_assuan_transact_start, gpgme_op_assuan_transact): New.
+ * gpgme.c (gpgme_get_protocol_name): Ditto.
+ (gpgme_set_protocol): Support it.
+ * engine.c (gpgme_get_engine_info): Ditto.
+ (engine_ops): Register it.
+ (_gpgme_engine_op_assuan_transact): New.
+ * libgpgme.vers (gpgme_op_assuan_transact_start)
+ (gpgme_op_assuan_transact): New.
+ * gpgme.def (gpgme_op_assuan_transact_start)
+ (gpgme_op_assuan_transact): New.
+ * engine-backend.h (struct engine_ops): Add GET_HOME_DIR and
+ initialize to NULL for all engines.
+ * engine.c (engine_get_home_dir): New.
+ (gpgme_get_engine_info): Use it.
+ (_gpgme_set_engine_info): Use it.
+ * engine.h (engine_assuan_result_cb_t): New.
+ * context.h (ctx_op_data_id_t): Add OPDATA_ASSUAN.
+
+ * util.h (GPG_ERR_UNFINISHED): Define if not yet defined.
+
+ * version.c (gpgme_check_version): Protect trace arg against NULL.
+
+2009-01-19 Werner Koch <wk@g10code.com>
+
+ * rungpg.c: Rename to engine-gpg.c
+ * Makefile.am (main_sources): Ditto.
+
+2008-12-03 Marcus Brinkmann <marcus@g10code.de>
+
+ * Makefile.am (status-table.h): Use $(builddir) to find gpgme.h.
+
+2008-11-18 Werner Koch <wk@g10code.com>
+
+ * version.c (do_subsystem_inits): Always initialize I/O
+ subsystem. Fixes regression from 2007-08-02.
+
+ * decrypt.c (_gpgme_decrypt_status_handler): Use
+ _gpgme_map_gnupg_error to parse the error code for decrypt.algorithm.
+
+2008-10-30 Marcus Brinkmann <marcus@g10code.de>
+
+ * wait-private.c (_gpgme_wait_on_condition): Remove unused
+ variable IDX.
+ * wait-global.c: Include ops.h to silence gcc warning.
+ (_gpgme_wait_global_event_cb): Pass error value directly.
+ * wait-user.c: Include ops.h to silence gcc warning.
+
+ * posix-io.c (_gpgme_io_spawn): Make ARGV argument const to
+ silence gcc warning. Cast argument to execv to silence warning.
+ * w32-io.c (_gpgme_io_spawn): Likewise.
+ * priv-io.h (_gpgme_io_spawn): Likewise for prototype.
+
+2008-10-24 Werner Koch <wk@g10code.com>
+
+ * rungpg.c (gpg_keylist_preprocess): Escape backslashes too.
+
+2008-10-23 Marcus Brinkmann <marcus@g10code.de>
+
+ * rungpg.c (gpg_keylist_preprocess): Convert percent escaped
+ string to C coded string.
+
+2008-10-20 Werner Koch <wk@g10code.com>
+
+ * Makefile.am (EXTRA_DIST): Add gpgme.h.in.
+
+ * gpgme.h: Rename to gpgme.h.in.
+ * gpgme.h.in (GPGME_VERSION): Use autoconf substitution.
+
+ * posix-io.c: Include sys/uio.h. Fixes bug #818.
+
+2008-10-18 Marcus Brinkmann <marcus@g10code.com>
+
+ * w32-util.c (find_program_in_registry): Don't define.
+ (_gpgme_get_gpg_path, _gpgme_get_gpgsm_path)
+ (_gpgme_get_gpgconf_path): Do not check for fooProgram in the
+ registry anymore. It is now no longer possible to overwrite the
+ default location in that way.
+
+2008-10-17 Werner Koch <wk@g10code.com>
+
+ * w32-glib-io.c (_gpgme_io_fd2str): Use "%d" and not "%ld" to work
+ around a bug in mingw32.
+
+2008-09-23 Marcus Brinkmann <marcus@g10code.com>
+
+ * gpgme.c (gpgme_sig_notation_clear): Clear CTX->sig_notations.
+ Submitted by "Daniel Mueller" <daniel@danm.de>
+
+2008-09-16 Marcus Brinkmann <marcus@g10code.com>
+
+ * rungpg.c (gpg_new): Don't use errno with ttyname_r.
+
+2008-08-11 Marcus Brinkmann <marcus@g10code.com>
+
+ * rungpg.c (gpg_cancel): Remove cmd fd before status fd.
+ * gpgme.c (_gpgme_cancel_with_err): New function.
+ (gpgme_cancel): Reimplement in terms of _gpgme_cancel_with_err.
+ * wait-private.c (_gpgme_wait_on_condition): Use
+ _gpgme_cancel_with_err.
+ * wait-user.c (_gpgme_user_io_cb_handler): Likewise.
+ * wait-global.c (_gpgme_wait_global_event_cb, gpgme_wait): Likewise.
+
+2008-08-08 Marcus Brinkmann <marcus@g10code.com>
+
+ * rungpg.c (command_handler): Remove I/O callback on error, too.
+
+2008-06-29 Marcus Brinkmann <marcus@g10code.com>
+
+ * gpgme.c (gpgme_cancel_async): Remove unused variable.
+
+2008-06-27 Marcus Brinkmann <marcus@g10code.de>
+
+ * libgpgme.vers: Add gpgme_cancel_async.
+ * gpgme.def: Likewise.
+
+ * context.h: Include "sema.h".
+ (struct gpgme_context): New members lock and canceled.
+ * gpgme.c (gpgme_new): Initialize lock.
+ (gpgme_release): Destroy lock.
+ (gpgme_cancel_async): New function.
+ * op-support.c (_gpgme_op_reset): Reset the canceled flag.
+ * wait-global.c (gpgme_wait): Check cancel flag before processing
+ any I/O callbacks.
+ * wait-private.c (_gpgme_wait_on_condition): Likewise.
+ * wait-user.c (_gpgme_user_io_cb_handler): Likewise.
+
+2008-06-26 Werner Koch <wk@g10code.com>
+
+ * w32-util.c (_gpgme_mkstemp): Replace sprint by stpcpy.
+ (mkstemp): Need to use GetSystemTimeAsFileTime for better
+ compatibility.
+
+2008-06-25 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme-w32spawn.c: New file.
+ * Makefile.am (libexec_PROGRAMS) [HAVE_W32_SYSTEM]: New variable
+ with gpgme-w32spawn.
+ * engine-gpgsm.c (gpgsm_new): Use server translated handles.
+ (gpgsm_set_locale): Return early if locale value is NULL.
+ * util.h (_gpgme_mkstemp)
+ (_gpgme_get_w32spawn_path) [HAVE_W32_SYSTEM]: New function
+ prototypes.
+ * w32-util.c: Include <stdint.h>, <sys/stat.h> and <unistd.h>.
+ (letters, mkstemp, _gpgme_mkstemp, _gpgme_get_w32spawn_path): New
+ functions.
+ * rungpg.c (gpg_decrypt, gpg_encrypt, gpg_encrypt_sign)
+ (gpg_genkey, gpg_import, gpg_verify, gpg_sign): Pass data over
+ special filename FD rather than stdin.
+ (struct arg_and_data_s): Add member ARG_LOCP.
+ (struct fd_data_map_s): Add member ARG_LOC.
+ (struct engine_gpg): Add member ARG_LOC to status and colon.
+ (_add_arg, add_arg_with_locp): New function.
+ (add_arg_ext): Reimplement in terms of _add_arg.
+ (gpg_new): Remember argument location for status FD.
+ (build_argv): Set argument location if requested. Also set
+ argument location of fd_data_map for data items.
+ (start): Adjust caller of _gpgme_io_spawn.
+ * priv-io.h (struct spawn_fd_item_s): Add members peer_name and
+ arg_loc.
+ (_gpgme_io_spawn): Remove parent fd list argument.
+ * posix-io.c (get_max_fds): New function.
+ (_gpgme_io_dup): Add tracing.
+ (_gpgme_io_spawn): Remove parent fd list. Change meaning of child
+ fd list to contain all child fds that should be inherited. Close
+ all other file descriptors after fork.
+ * w32-io.c, w32-glib-io.c, w32-qt-io.c(_gpgme_io_spawn): Remove
+ parent fd list. Change meaning of child fd list to contain all
+ child fds that should be inherited. Do not inherit any file
+ descriptors, but DuplicateHandle them. Spawn process through
+ wrapper process. Provide wrapper process with a temporary file
+ containing handle translation data. Return translated handle
+ names.
+ * w32-io.c (reader): Add more tracing output.
+ (_gpgme_io_read): Likewise.
+ * engine-gpgconf.c (gpgconf_read): Adjust caller of
+ _gpgme_io_spawn.
+ * version.c (_gpgme_get_program_version): Likewise.
+
+2008-06-20 Werner Koch <wk@g10code.com>
+
+ * engine-gpgconf.c (gpgconf_read): Change ARGV initialization for
+ compatibility with old compilers. Fix amount of memmove. Fix
+ CR removal.
+
+2008-06-19 Werner Koch <wk@g10code.com>
+
+ * gpgme.h (GPGME_CONF_PATHNAME): Replace by GPGME_CONF_FILENAME,
+ change all callers and provide compatibilty macro.
+ (gpgme_conf_type_t): Add complex types 34..37.
+ * engine-gpgconf.c (gpgconf_parse_option, arg_to_data)
+ (_gpgme_conf_arg_new, _gpgme_conf_arg_release): Add new types.
+
+2008-06-19 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine-gpgconf.c (gpgconf_parse_option): Fix comma detection.
+
+2008-05-09 Werner Koch <wk@g10code.com>
+
+ * engine-gpgconf.c (gpgconf_read): Do not pass empty lines to the
+ callback.
+
+2008-05-07 Werner Koch <wk@g10code.com>
+
+ * engine-gpgconf.c (gpgconf_write): Change argv[0] to a
+ self-explaining string. Needs a proper fix, though.
+
+ * rungpg.c (gpg_keylist, gpg_keylist_ext): Factor common code out
+ to ..
+ (gpg_build_keylist_options): .. new. Allow combination of extern
+ and intern mode.
+ (gpg_new): DFT_TTYNAME is an array, thus check the first character.
+
+2008-05-06 Werner Koch <wk@g10code.com>
+
+ * version.c (extract_version_string): New.
+ (_gpgme_get_program_version): Use it to allow for suffixes in the
+ version line.
+
+2008-04-28 Werner Koch <wk@g10code.com>
+
+ * engine-gpgconf.c (gpgconf_read): Fixed segv. Avoid memmove for
+ each line.
+
+2008-04-22 Marcus Brinkmann <marcus@g10code.de>
+
+ * w32-qt-io.cpp, kdpipeiodevice.cpp: New versions from Frank
+ Osterfeld, implement blocking select.
+
+2008-03-11 Marcus Brinkmann <marcus@g10code.de>
+
+ * data.c (gpgme_data_read, gpgme_data_write): Retry on EINTR.
+
+2008-03-06 Marcus Brinkmann <marcus@g10code.de>
+
+ * key.c (_gpgme_key_add_sig): Terminate UID in case SRC is NULL.
+ Reported by Marc Mutz.
+
+2008-03-05 Marcus Brinkmann <marcus@g10code.de>
+
+ * decrypt.c (release_op_data): Release OPD->result.recipients.
+ * encrypt.c (release_op_data): Release invalid_recipient.
+
+2008-02-15 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine-gpgconf.c (gpgconf_read): Fix end-of-line handline.
+
+2008-02-14 Werner Koch <wk@g10code.com>
+
+ * w32-io.c (_gpgme_io_spawn): Add arg R_PID to return the pid.
+ * posix-io.c (_gpgme_io_spawn): Ditto.
+ * w32-glib-io.c (_gpgme_io_spawn): Ditto.
+ * w32-qt-io.cpp (_gpgme_io_spawn): Ditto.
+ * priv-io.h (_gpgme_io_spawn): Adjust prototyp and change all callers.
+ * rungpg.c (start): Call _gpgme_allow_set_foregound_window.
+
+ * w32-util.c (_gpgme_allow_set_foregound_window): New.
+ * posix-util.c (_gpgme_allow_set_foregound_window): New.
+ * engine-gpgsm.c (default_inq_cb): New.
+ (gpgsm_new) [W32]: Enable pinentry notifications.
+ (status_handler): Handle inquiries.
+
+2008-01-30 Marcus Brinkmann <marcus@g10code.de>
+
+ * kdpipeiodevice.cpp: New version by Frank Osterfeld, fixes race
+ condition.
+
+2008-01-28 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine-gpgsm.c (map_input_enc): Rename to ...
+ (map_data_enc): ... this. Also change all callers.
+ (gpgsm_encrypt, gpgsm_export, gpgsm_export_ext, gpgsm_genkey)
+ (gpgsm_sign): Set encoding for output.
+
+2008-01-28 Werner Koch <wk@g10code.com>
+
+ * keylist.c (gpgme_get_key): Skip duplicated keys. Fixes bug 876.
+
+2008-01-14 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine-gpgconf.c (gpgconf_config_load_cb): Fix program_name
+ field.
+
+2008-01-10 Marcus Brinkmann <marcus@g10code.de>
+
+ * kdpipeiodevice.cpp: New version from Frank Osterfeld.
+
+ * engine-gpgconf.c (gpgconf_config_load_cb2): Handle the flag
+ NO_ARG_DESC.
+
+2008-01-04 Marcus Brinkmann <marcus@g10code.de>
+
+ * Makefile.am (gpgconf_components): New variable.
+ (main_sources): Add gpgconf.c.
+ * gpgme.h (gpgme_protocol_t): New protocol GPGME_PROTOCOL_GPGCONF.
+ (gpgme_conf_level_t, gpgme_conf_type_t, gpgme_conf_arg_t)
+ (gpgme_conf_opt_t, gpgme_conf_comp_t, gpgme_conf_arg_new)
+ (gpgme_conf_arg_release, gpgme_conf_opt_change)
+ (gpgme_conf_release, gpgme_op_conf_load, gpgme_op_conf_save): New
+ types.
+ * gpgconf.c, engine-gpgconf.c: New files.
+ * engine.h: (_gpgme_engine_op_conf_load,
+ (_gpgme_engine_op_conf_save): New prototypes.
+ * op-support.c (_gpgme_op_reset): Ignore not implemented locale
+ function.
+ * posix-util.c (_gpgme_get_gpgconf_path): New function.
+ * w32-util.c (_gpgme_get_gpgconf_path): New function.
+ * engine-gpgsm.c:
+ (_gpgme_engine_ops_gpgsm): Add stubs for conf_load and conf_save.
+ * rungpg.c:
+ (_gpgme_engine_ops_gpg): Add stubs for conf_load and conf_save.
+ * gpgme.def: Add new gpgconf related interfaces.
+ * libgpgme.vers: Likewise.
+ * util.h (_gpgme_get_gpgconf_path): New prototype.
+ * gpgme.h (gpgme_protocol_t): Add GPGME_PROTOCOL_GPGCONF.
+ * engine-backend.h (_gpgme_engine_ops_gpgconf): New prototype.
+ (struct engine_ops): Add members for conf_load and conf_save.
+ * engine.c (engine_ops): Add _gpgme_engine_ops_gpgconf.
+ (_gpgme_engine_op_conf_load,
+ (_gpgme_engine_op_conf_save): New functions.
+ (gpgme_get_engine_info): Allow protocol GPGME_PROTOCOL_GPGCONF.
+
+2007-11-28 Marcus Brinkmann <marcus@g10code.de>
+
+ * w32-util.c (_gpgme_get_gpg_path, _gpgme_get_gpgsm_path): Search
+ for installation directory. Remove old fallback default.
+ (find_program_in_inst_dir): New function.
+
+2007-11-26 Werner Koch <wk@g10code.com>
+
+ * engine-gpgsm.c (struct engine_gpgsm): Add field INLINE_DATA and
+ always reset it before calling start.
+ (gpgsm_new): Clear it.
+ (status_handler): Implement it.
+ (gpgsm_getauditlog) [USE_DESCRIPTOR_PASSING]: Use INLINE_DATA.
+
+2007-11-23 Werner Koch <wk@g10code.com>
+
+ * op-support.c (_gpgme_op_reset): Implement a no-reset flag.
+ * getauditlog.c (getauditlog_start): Use that flag.
+
+2007-11-20 Werner Koch <wk@g10code.com>
+
+ * op-support.c (_gpgme_parse_inv_recp): Add new reason code 11.
+
+2007-11-22 Werner Koch <wk@g10code.com>
+
+ * gpgme.h (gpgme_op_getauditlog_start, gpgme_op_getauditlog): New.
+ * libgpgme.vers: Ditto.
+ * gpgme.def: Ditto.
+ * getauditlog.c: New.
+ * engine-backend.h (struct engine_ops): Add member GETAUDITLOG.
+ * engine-gpgsm.c (gpgsm_getauditlog): New.
+ (_gpgme_engine_ops_gpgsm): Insert new function.
+ (gpgsm_new): Try to enable audit log support.
+ * rungpg.c (_gpgme_engine_ops_gpg): Insert dummy entry.
+
+2007-11-12 Marcus Brinkmann <marcus@g10code.de>
+
+ * kdpipeiodevice.cpp: New version from Frank Osterfeld.
+
+2007-10-11 Marcus Brinkmann <marcus@g10code.de>
+
+ * kdpipeiodevice.cpp: New version from Frank Osterfeld.
+
+2007-10-09 Marcus Brinkmann <marcus@g10code.de>
+
+ * kdpipeiodevice.cpp: New version from Frank Osterfeld and Marc
+ Mutz.
+
+2007-10-05 Marcus Brinkmann <marcus@g10code.de>
+
+ * kdpipeiodevice.cpp, w32-qt-io.cpp: New versions from Frank
+ Osterfeld.
+
+2007-10-04 Marcus Brinkmann <marcus@g10code.de>
+
+ * kdpipeiodevice.h, kdpipeiodevice.cpp, kdpipeiodevice.moc,
+ w32-qt-io.cpp: New versions from Frank Osterfeld.
+
+2007-10-02 Marcus Brinkmann <marcus@g10code.de>
+
+ * kdpipeiodevice.cpp, kdpipeiodevice.moc: New versions.
+ * w32-qt-io.cpp (_gpgme_io_fd2str): Print actual_fd if available.
+ (_gpgme_io_dup): Only acquire a reference, do not actually dup.
+ Submitted by Frank Osterfeld.
+
+ * priv-io.h, engine-gpgsm.c: Add comments.
+ * w32-qt-io.cpp (_gpgme_io_select): Remove code handling frozen FDs.
+ * w32-glib-io.c (_gpgme_io_close): Always dereference the channel,
+ even if not primary.
+ (_gpgme_io_dup): Acquire a reference. Replace unused
+ implementation by assertion.
+
+2007-09-28 Werner Koch <wk@g10code.com>
+
+ * engine-gpgsm.c (iocb_data_t): Add SERVER_FD_STR.
+ (gpgsm_new): Set it.
+ (gpgsm_set_fd): Use it.
+
+ * w32-glib-io.c (find_channel): Add a new primary flag.
+ (_gpgme_io_close): Close channel only if primary.
+ (_gpgme_io_dup): Put newfd into the table as shallow copy.
+
+ * priv-io.h (struct io_select_fd_s): Remove member FROZEN.
+ * w32-io.c (_gpgme_io_select): Ditto.
+ * w32-glib-io.c (_gpgme_io_select): Ditto.
+
+ * posix-io.c (_gpgme_io_select): Ditto.
+ * rungpg.c (read_status): Ditto.
+ * wait.c (fd_table_put): Ditto.
+
+ * rungpg.c (gpg_io_event): Add tracing.
+ (start): Use gpg_io_event for sending the start event.
+ * engine-gpgsm.c (gpgsm_io_event): Add tracing.
+ (start): Use gpgsm_io_event for sending the start event.
+ * wait.c (_gpgme_add_io_cb, _gpgme_run_io_cb): Add tracing.
+
+2007-09-28 Marcus Brinkmann <marcus@g10code.de>
+
+ * kdpipeiodevice.moc, w32-qt-io.cpp, kdpipeiodevice.cpp: New
+ versions from Frank Osterfeld.
+
+2007-09-27 Marcus Brinkmann <marcus@g10code.de>
+
+ * w32-glib-io.c (_gpgme_io_spawn),
+ w32-qt-io.cpp (_gpgme_io_spawn), w32-io.c (_gpgme_io_spawn): Close
+ the process handle, return 0.
+
+ * gpgme.h (gpgme_protocol_t): Add GPGME_PROTOCOL_UNKNOWN.
+ * gpgme.c (gpgme_get_protocol_name): Implement support for
+ GPGME_PROTOCOL_UNKNOWN.
+
+ * kdpipeiodevice.h: Fix last change.
+
+ * w32-glib-io.c (_gpgme_io_pipe), w32-qt-io.c (_gpgme_io_pipe),
+ w32-io.c (_gpgme_io_pipe), posix-io.c (_gpgme_io_pipe): Fix debug
+ output.
+
+2007-09-25 Marcus Brinkmann <marcus@g10code.de>
+
+ * conversion.c, keylist.c: Include <sys/types.h>.
+
+ * kdpipeiodevice.h: Use namespace _gpgme_.
+ * kdpipeiodevice.cpp: Use namespace _gpgme_.
+ [Q_OS_WIN32 && NOMINMAX]: Do not define NOMINMAX again.
+ * w32-qt-io.cpp: Change namespace of KDPipeIODevice to
+ _gpgme_::KDPipeIODevice.
+
+2007-09-17 Werner Koch <wk@g10code.com>
+
+ * rungpg.c (gpg_new): Make robust against undefined ttyname or
+ ttytype.
+
+2007-09-14 Werner Koch <wk@g10code.com>
+
+ * data-mem.c (gpgme_data_release_and_get_mem): Fix tracing bug.
+
+2007-09-14 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.c (gpgme_release): Call gpgme_sig_notation_clear.
+
+2007-09-13 Marcus Brinkmann <marcus@g10code.de>
+
+ * rungpg.c (gpg_new): Handle return value of _gpgme_getenv (fixes
+ small memory leak).
+
+2007-09-07 Marcus Brinkmann <marcus@g10code.de>
+
+ * Makefile.am (libgpgme_qt_la_SOURCES): Move
+ moc_kdpipeiodevice.cpp to EXTRA_DIST, as this is only included by
+ another file (it's more like a header file than a cpp file, but
+ automake doesn't know that).
+
+ * w32-qt-io.cpp (_gpgme_io_spawn): Fix several cast errors and typos.
+ * w32-io.c (_gpgme_io_write): Use TRACE_SYSRES instead of TRACE_SYS.
+ (libgpgme_qt_la_LIBADD): Add QT4_CORE_LIBS, not QT4_CORE_LIB.
+
+ * kdpipeiodevice.h, kdpipeiodevice.cpp, moc_kdpipeiodevice.cpp,
+ kdpipeiodevice.moc, w32-qt-io.c: New files.
+ * Makefile.am (ltlib_gpgme_extra): Rename to ltlib_gpgme_glib.
+ (ltlib_gpgme_qt): New variable.
+ (lib_LTLIBRARIES): Add $(ltlib_gpgme_qt).
+ (libgpgme_qt_la_SOURCES): New variable.
+ (AM_CPPFLAGS): Add @QT4_CORE_INCLUDES@
+ (AM_CFLAGS): Add @QT4_CORE_CFLAGS@.
+ (libgpgme_qt_la_LDFLAGS, libgpgme_qt_la_DEPENDENCIES)
+ (libgpgme_qt_la_LIBADD): New variables.
+
+ * sema.h (struct critsect_s): Rename "private" to "priv" to make
+ C++ users happy. Change users.
+ * posix-sema.c (_gpgme_sema_cs_enter, _gpgme_sema_cs_leave)
+ (_gpgme_sema_cs_destroy): Likewise.
+ * w32-sema.c (critsect_init, _gpgme_sema_cs_enter)
+ (_gpgme_sema_cs_leave, _gpgme_sema_cs_destroy): Likewise.
+ * w32-glib-io.c (gpgme_get_giochannel): Change return type to
+ void*.
+ (gpgme_get_fdptr): New function.
+ * w32-io.c (gpgme_get_fdptr): New function
+ * gpgme.def: Add gpgme_get_fdptr.
+
+2007-08-22 Marcus Brinkmann <marcus@g10code.de>
+
+ * w32-io.c (_gpgme_io_write): Return early if COUNT is zero.
+ (writer): Remove superfluous check.
+
+2007-08-20 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.h: Move include of gpg-error.h out of extern "C".
+
+2007-08-07 Werner Koch <wk@g10code.com>
+
+ * gpgme.h (struct _gpgme_signature): Add member CHAIN_MODEL.
+ * verify.c (parse_trust): Set Chain_MODEL.
+
+2007-08-02 Werner Koch <wk@g10code.com>
+
+ * w32-glib-io.c (_gpgme_io_spawn): Use DETACHED_PROCESS flag.
+ * w32-io.c (_gpgme_io_spawn): Ditto.
+ (_gpgme_io_write): Map ERROR_NO_DATA to EPIPE.
+ * debug.c (_gpgme_debug): Enable assuan logging.
+ (_gpgme_debug_subsystem_init): New.
+ * version.c (do_subsystem_inits): Disable assuan logging and
+ initialize the debug system.
+ (gpgme_check_version): Do not trace before the subsystems are
+ initialized.
+
+2007-07-17 Marcus Brinkmann <marcus@g10code.de>
+
+ * debug.c: Include <errno.h> and "debug.h".
+ (_gpgme_debug): Save and restore ERRNO.
+ (TOHEX): New macro.
+ (_gpgme_debug_buffer): New function.
+ * conversion.c, data-compat.c, data-mem.c, data.c, engine-gpgsm.c,
+ gpgme.c, keylist.c, posix-io.c, rungpg.c, sign.c, version.c,
+ w32-io.c, wait.c: Replace DEBUG macros by TRACE_* variants. In
+ most of these files, add many more tracepoints.
+
+2007-07-16 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine-gpgsm.c (status_handler): Do not send BYE here.
+
+ * w32-io.c (struct reader_context_s, struct writer_context_s): New
+ members REFCOUNT.
+ (create_reader, create_writer): Initialize C->refcount to 1.
+ (destroy_reader, destroy_writer): Only destroy if C->refcount
+ drops to 0.
+ (find_reader, find_writer, kill_reader, kill_writer): Beautify.
+ * priv-io.h (_gpgme_io_dup): New prototype.
+ * posix-io.c (_gpgme_io_dup): New function.
+ * w32-io.c (_gpgme_io_dup): Likewise.
+ * w32-glib-io.c (_gpgme_io_dup): Likewise.
+ * engine-gpgsm.c (start): Reverting to version 2007-07-10.
+
+2007-07-13 Marcus Brinkmann <marcus@g10code.de>
+
+ * data-user.c (user_read, user_write, user_seek): Set errno and
+ return -1 instead returning the error code directly.
+ * data-compat.c (old_user_seek): Likewise.
+ * gpgme.c (gpgme_sig_notation_add): Return error properly.
+
+ * Revert the "close_notify_handler" returns int stuff. Always
+ close in the _gpgme_io_close implementations.
+ * engine-gpgsm.c (status_handler): Try to terminate the connection
+ in case of error.
+ * w32-io.c (_gpgme_io_read): Return C->error_code in ERRNO.
+ (_gpgme_io_write): Likewise.
+
+ * priv-io.h (_gpgme_io_set_close_notify): Change type of HANDLER
+ to _gpgme_close_notify_handler.
+ (_gpgme_close_notify_handler): New type.
+ (_gpgme_io_dup): Remove prototype.
+ * posix-io.c (notify_table, _gpgme_io_set_close_notify): Change
+ type of HANDLER to _gpgme_close_notify_handler_t.
+ (_gpgme_io_close): Do not close the FD if handler returns 0.
+ (_gpgme_io_dup): Remove function.
+ * w32-io.c (notify_table, _gpgme_io_set_close_notify,
+ _gpgme_io_close): Change type of HANDLER to
+ _gpgme_close_notify_handler_t.
+ (_gpgme_io_close): Do not close the FD if handler returns 0.
+ (_gpgme_io_dup): Remove function.
+ * w32-glib-io.c (_gpgme_io_dup): Remove function.
+ (_gpgme_io_set_close_notify, notify_table): Change type of HANDLER
+ to _gpgme_close_notify_handler_t.
+ (_gpgme_io_close): Do not close the FD if handler returns 0.
+ * rungpg.c (close_notify_handler): Change return type to int,
+ return 1.
+ * engine-gpgsm.c (close_notify_handler): Change return type to
+ int, return 0 for status FD and 1 for all other FDs.
+ (start): Do not duplicate the status FD.
+
+2007-07-12 Marcus Brinkmann <marcus@g10code.de>
+
+ * Makefile.am: Replace implicite rule by suffix rule. Add
+ SUFFIXES for that.
+
+2007-07-12 Werner Koch <wk@g10code.com>
+
+ * version.c (do_subsystem_inits) [W32]: Make sure that the socket
+ system has been started.
+
+2007-07-10 Marcus Brinkmann <marcus@g10code.de>
+
+ * priv-io.h (_gpgme_io_dup): New prototype.
+ * posix-io.c (_gpgme_io_dup): New function.
+ * w32-io.c (_gpgme_io_dup): Likewise.
+ * w32-glib-io.c (_gpgme_io_dup): Likewise.
+ * engine-gpgsm.c (start): Use _gpgme_dup() instead of dup().
+
+2007-07-08 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine-gpgsm.c [HAVE_W32_SYSTEM]: Enable the bunch of the file.
+ * funopen.c (funopen): Rename to _gpgme_funopen.
+
+2007-04-30 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine-gpgsm.c (gpgsm_new): Fix error handling for ttyname_r.
+ * rungpg.c (gpg_new): Likewise.
+ Submitted by Stephen Tether.
+
+2007-02-26 Werner Koch <wk@g10code.com>
+
+ * verify.c (op_data_t): New element PLAINTEXT_SEEN.
+ (_gpgme_verify_status_handler): Return an error if more than one
+ plaintext has been seen.
+ (parse_error): New arg SET_STATUS. Also detect it based on an
+ ERROR status (gpg > 1.4.6).
+
+2007-01-26 Werner Koch <wk@g10code.com>
+
+ * w32-io.c (build_commandline): Fixed stupid quoting bug.
+ * w32-glib-io.c (build_commandline): Ditto.
+
+ * rungpg.c (gpg_set_locale): Avoid dangling pointer after free.
+
+ * gpgme-config.in: New options --get-gpg and --get-gpgsm.
+
+2007-01-18 Marcus Brinkmann <marcus@g10code.de>
+
+ * data.h (_gpgme_data_get_fd): Add prototype.
+ (gpgme_data_get_fd_cb): New type.
+ (struct _gpgme_data_cbs): New member get_fd.
+ * data.c (_gpgme_data_get_fd): New function.
+ * data-fd.c (fd_get_fd): New function.
+ (fd_cbs): Add fd_get_fd.
+ * data-stream.c (stream_get_fd): New function.
+ (stream_cbs): Add stream_get_fd.
+ * data-mem.c (mem_cbs): Add NULL for get_fd callback.
+ * data-user.c (user_cbs): Likewise.
+ * engine-gpgsm.c (gpgsm_set_fd) [USE_DESCRIPTOR_PASSING]: Try to
+ short-cut by passing the data descriptor directly.
+
+2007-01-17 Marcus Brinkmann <marcus@g10code.de>
+
+ * w32-io.c (build_commandline): Quote all command line arguments.
+ * w32-glib-io.c (build_commandline): Likewise.
+
+2007-01-10 Werner Koch <wk@g10code.com>
+
+ * ttyname_r.c (ttyname_r) [W32]: Return a dummy name.
+
+2007-01-08 Werner Koch <wk@g10code.com>
+
+ * version.c (do_subsystem_inits): Do assuan init only if building
+ with Assuan.
+ * setenv.c: Include assuan-def.h only if building with Assuan
+ support.
+
+ * op-support.c (_gpgme_op_reset): Set LC_MESSAGES only if
+ if defined.
+ * engine-gpgsm.c (gpgsm_set_locale): Ditto.
+ * rungpg.c (gpg_set_locale): Ditto.
+
+2006-12-17 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.c (gpgme_set_protocol): Shut down the engine when
+ switching protocols.
+ (gpgme_ctx_set_engine_info): Likewise for engine info.
+ * engine.h (_gpgme_engine_reset): New function prototype.
+ * engine.c (_gpgme_engine_reset): New function.
+ * engine-backend.h (struct engine_ops): New member RESET.
+ * rungpg.c (_gpgme_engine_ops_gpg): Add NULL for reset function.
+ * engine-gpgsm.c (_gpgme_engine_ops_gpgsm)
+ [USE_DESCRIPTOR_PASSING]: Add gpgsm_reset for reset.
+ (_gpgme_engine_ops_gpgsm) [!USE_DESCRIPTOR_PASSING]: Add NULL for
+ reset function.
+ (gpgsm_reset) [USE_DESCRIPTOR_PASSING]: New function.
+ * op-support.c (_gpgme_op_reset): Try to use the engine's reset
+ function if available.
+ * engine-gpgsm.c (gpgsm_new): Move code to dup status_fd to ...
+ (start): ... here.
+ * posix-io.c (_gpgme_io_recvmsg, _gpgme_io_sendmsg): New functions.
+
+ * engine.h (_gpgme_engine_new): Remove arguments lc_ctype and
+ lc_messages from prototype.
+ (_gpgme_engine_set_locale): New prototype.
+ * engine.c (_gpgme_engine_set_locale): New function.
+ * op-support.c (_gpgme_op_reset): Call _gpgme_engine_set_locale.
+ * engine-backend.h (struct engine_ops): Add new member SET_LOCALE.
+ Remove arguments lc_messages and lc_ctype from member NEW.
+ * engine-gpgsm.c (struct engine_gpgsm): New members lc_ctype_set
+ and lc_messages_set.
+ (gpgsm_new): Remove lc_messages and lc_ctype
+ arguments.
+ (gpgsm_set_locale): New function.
+ (_gpgme_engine_ops_gpgsm): Add gpgsm_set_locale.
+ * rungpg.c (struct engine_gpg): Add new members lc_messages and
+ lc_ctype.
+ (gpg_release): Release lc_messages and lc_ctype if set.
+ (gpg_new): Remove lc_messages and lc_ctype arguments.
+ (gpg_set_locale): New function.
+ (_gpgme_engine_ops_gpg): Add gpg_set_locale.
+ (add_arg): Implement in terms of:
+ (add_arg_ext): New function.
+ (start): Set lc-messages and lc-ctype arguments here.
+
+2006-12-03 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine-gpgsm.c (struct engine_gpgsm): Move members
+ input_fd_server, output_fd_server, message_fd_server to ...
+ (iocb_data): ... here (as server_fd).
+ (close_notify_handler): Reset tags as well.
+ (gpgsm_new): Implement support for descriptor
+ passing.
+ (fd_type_t): New type.
+ (gpgsm_clear_fd): New function. Use it instead of _gpgsm_io_close
+ for unused communication channels.
+ (gpgsm_set_fd): Rewritten to support descriptor passing. All
+ relevant callers adjusted as well (previously of _gpgme_io_close).
+
+2006-12-02 Marcus Brinkmann <marcus@g10code.de>
+
+ * version.c: Include "assuan.h".
+ (do_subsystem_inits): Call assuan_set_assuan_err_source.
+
+2006-12-01 Marcus Brinkmann <marcus@g10code.de>
+
+ * Makefile.am (libgpgme_real_la_SOURCES): Rename to main_sources.
+ (libgpgme_la_SOURCES, libgpgme_pthread_la_SOURCES,
+ libgpgme_glib_la_SOURCES, libgpgme_pth_la_SOURCES): Add
+ $(main_sources).
+ (libgpgme_la_DEPENDENCIES, libgpgme_la_LIBADD,
+ libgpgme_pthread_la_DEPENDENCIES, libgpgme_pthread_la_LIBADD,
+ libgpgme_pth_la_DEPENDENCIES, libgpgme_pth_la_LIBADD,
+ libgpgme_glib_la_DEPENDENCIES, libgpgme_glib_la_LIBADD): Remove
+ libgpgme-real.la.
+ (noinst_LTLIBRARIES): Removed.
+ (libgpgme_glib_la_CFLAGS, libgpgme_pth_la_CFLAGS): Removed.
+ (AM_CFLAGS): New variable.
+
+2006-11-30 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine-gpgsm.c: Replace AssuanError with gpg_error_t and
+ ASSUAN_CONTEXT with assuan_context_t.
+
+2006-11-29 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine-gpgsm.c (gpgsm_new): Check return value of
+ assuan_pipe_connect.
+
+ * rungpg.c: Include <unistd.h>.
+ (gpg_new): Support --display, --ttyname, --ttytype, --lc-ctype and
+ --lc-messages. Fixes issue 734.
+
+2006-10-24 Marcus Brinkmann <marcus@g10code.de>
+
+ * trustlist.c (gpgme_op_trustlist_next): Return error if OPD is
+ NULL.
+
+2006-10-23 Marcus Brinkmann <marcus@g10code.de>
+
+ * wait-global.c (gpgme_wait): Unlock CTX_LIST_LOCK while calling
+ _gpgme_engine_io_event().
+
+ * keylist.c (gpgme_op_keylist_next): Return error if OPD is NULL.
+
+2006-09-25 Marcus Brinkmann <marcus@g10code.de>
+
+ * data-mem.c (gpgme_data_release_and_get_mem): Release the data
+ object properly.
+
+2006-09-22 Marcus Brinkmann <marcus@g10code.de>
+
+ * keylist.c (keylist_colon_handler): Move debug output after
+ initialising KEY.
+
+2006-07-29 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme-config.in (Options): Add NETLIBS.
+ * Makefile.am (libgpgme_la_LIBADD, libgpgme_pthread_la_LIBADD,
+ libgpgme_pth_la_LIBADD, libgpgme_glib_la_LIBADD): Add NETLIBS.
+
+ * rungpg.c (read_status): Fix comparison disguising as an
+ assignment.
+
+2005-03-24 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.c (gpgme_set_locale): Remove conditional on
+ HAVE_W32_SYSTEM, and just check for LC_MESSAGES.
+
+2006-07-16 Marcus Brinkmann <marcus@g10code.de>
+
+ * rungpg.c (read_status): Strip potential carriage return.
+ * genkey.c (get_key_parameter): Skip potential carriage return.
+ * version.c (_gpgme_get_program_version): Strip potential carriage
+ return.
+
+ * data.c (gpgme_data_set_file_name): Allow to clear the file name
+ by passing NULL.
+
+2006-06-22 Marcus Brinkmann <marcus@g10code.de>
+
+ * keylist.c (gpgme_get_key): Also clone the engine info.
+
+2006-03-06 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme-config.in (cflags_pth): Revert accidential removal of
+ pthread support with last change.
+
+2006-02-28 Marcus Brinkmann <marcus@g10code.de>
+
+ * w32-glib-io.c (O_BINARY) [!O_BINARY]: New macro.
+ (_gpgme_io_pipe): Open pipes in binary mode.
+
+2006-02-22 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine.c (gpgme_engine_check_version): Reimplemented to allow
+ checking the version correctly even after changing the engine
+ information. Bug reported by Stéphane Corthésy.
+
+ * rungpg.c (read_colon_line): Invoke colon preprocess handler if
+ it is set.
+ (colon_preprocessor_t): New type.
+ (struct engine_gpg): New member colon.preprocess_fnc.
+ (gpg_keylist_preprocess): New function.
+ * keylist.c (keylist_colon_handler): Allow short key IDs.
+
+2006-02-15 Marcus Brinkmann <marcus@g10code.de>
+
+ * w32-io.c (create_writer): Make C->have_data a manually resetted
+ event.
+ (writer): Move code from end of if block to beginning, so it
+ is also run the first time.
+ (_gpgme_io_write): Move assert check after error check. Reset
+ the is_empty event, and also do it eagerly.
+ (_gpgme_io_select): Unconditionally wait for the is_empty event.
+
+2006-01-26 Werner Koch <wk@g10code.com>
+
+ * w32-util.c (_gpgme_get_conf_int): New.
+ * posix-util.c (_gpgme_get_conf_int): New.
+ * w32-io.c (get_desired_thread_priority): New.
+ (create_reader, create_writer): Use it here.
+
+2006-01-04 Werner Koch <wk@g10code.com>
+
+ * debug.h (_gpgme_debug_srcname): New. Use it with the debug macros.
+
+ * w32-glib-io.c (_gpgme_io_set_nonblocking): Add debug
+ statements. Disable error return for failed nonblocking call.
+
+2006-01-03 Marcus Brinkmann <marcus@g10code.de>
+
+ * w32-glib-io.c (_gpgme_io_close): Only close fd if there is no
+ channel for it.
+
+2005-12-31 Marcus Brinkmann <marcus@g10code.de>
+
+ * w32-glib-io.c (find_channel): Set channel to unbuffered.
+ (_gpgme_io_select): Fix debug output.
+
+2005-12-23 Werner Koch <wk@g10code.com>
+
+ * gpgme.h (struct _gpgme_signature): Append field PKA_ADDRESS.
+ * verify.c (release_op_data, _gpgme_verify_status_handler): Set
+ this field.
+
+2005-12-20 Werner Koch <wk@g10code.com>
+
+ * gpgme.h (gpgme_status_code_t): Added GPGME_STATUS_PKA_TRUST_BAD
+ and GPGME_STATUS_PKA_TRUST_GOOD.
+ (struct _gpgme_signature): New field pka_trust.
+ * verify.c (_gpgme_verify_status_handler): Set pka_trust.
+
+2005-12-06 Werner Koch <wk@g10code.com>
+
+ * keylist.c (keylist_colon_handler): Store fingerprints of the
+ subkeys. Reset the secret flag of subkeys for stub secret keys.
+ (NR_FIELDS): Bumped up to 16
+
+2005-11-27 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine.c (_gpgme_set_engine_info): Use new_file_name in
+ engine_get_version invocation. Reported by Stéphane Corthésy.
+
+2005-11-24 Marcus Brinkmann <marcus@g10code.de>
+
+ * w32-glib-io.c (_gpgme_io_fd2str): Remove debug printf.
+
+2005-11-18 Werner Koch <wk@g10code.com>
+
+ * w32-glib-io.c: Include glib.h before windows to avoid a symbol
+ shadowing warning.
+ (find_channel): Better use g_io_channel_win32_new_fd instead of
+ the autodetection function g_io_channel_unix_new.
+ (_gpgme_io_select): Rewritten. It is now a fully working select
+ implementation.
+
+2005-11-18 Marcus Brinkmann <marcus@g10code.de>
+
+ * priv-io.h (_gpgme_io_fd2str): New prototype.
+ * posix-io.c (_gpgme_io_fd2str): New function.
+ * w32-io.c (_gpgme_io_fd2str): New function.
+ * rungpg.c: Use this new function.
+ * w32-glib-io.c (_gpgme_io_fd2str): Rewrote the file handle code
+ again. Two's company, three's the musketeers.
+
+ * w32-glib-io.c: Rewrote the file handle code. We don't create
+ system fds for every handle (doesn't work for inherited handles),
+ but we create pseudo fds in a private namespace that designate a
+ handle and potentially a giochannel.
+
+2005-11-18 Werner Koch <wk@g10code.com>
+
+ * versioninfo.rc.in: Set file version to LT-version + Svn-revision.
+
+2005-11-17 Marcus Brinkmann <marcus@g10code.de>
+
+ * w32-glib-io.c: New file.
+ * gpgme.def (gpgme_get_giochannel): Add symbol.
+ * Makefile.am (system_components) [HAVE_DOSISH_SYSTEM]: Remove
+ w32-io.c.
+ (ltlib_gpgme_extra): New variable.
+ (lib_LTLIBRARIES): Add $(ltlib_gpgme_extra).
+ (system_components_not_extra): New variable.
+ (libgpgme_la_SOURCES, libgpgme_pthread_la_SOURCES,
+ (libgpgme_pth_la_SOURCES): Add $(system_components_not_extra).
+ (libgpgme_glib_la_LDFLAGS, libgpgme_glib_la_DEPENDENCIES,
+ (libgpgme_glib_la_LIBADD, libgpgme_glib_la_CFLAGS)
+ [BUILD_W32_GLIB]: New variables.
+ * gpgme-config.in (glib): New option.
+ * gpgme.m4 (AM_PATH_GPGME_GLIB): New macro.
+
+2005-11-17 Marcus Brinkmann <marcus@g10code.de>
+
+ * priv-io.h (_gpgme_io_waitpid, _gpgme_io_kill): Removed.
+ * w32-io.c (_gpgme_io_waitpid, _gpgme_io_kill): Removed.
+ * posix-io.c (_gpgme_io_kill): Removed.
+ (_gpgme_io_waitpid): Declare static.
+
+2005-10-24 Marcus Brinkmann <marcus@g10code.de>
+
+ * w32-io.c (_gpgme_io_spawn): Don't minimize window, hide it.
+
+2005-10-21 Werner Koch <wk@g10code.com>
+
+ * Makefile.am: Fixed cut+paste problem
+
+2005-10-20 Marcus Brinkmann <marcus@g10code.de>
+
+ * Makefile.am: Build versioninfo.lo, not versioninfo.o. Also, fix
+ the whole mess.
+
+2005-10-16 Marcus Brinkmann <marcus@g10code.de>
+
+ * rungpg.c (gpg_edit): Don't add a key argument if in card edit
+ mode.
+
+2005-10-06 Marcus Brinkmann <marcus@g10code.de>
+
+ * Makefile.am (gpgme.dll gpgme.dll.a): Use $(srcdir) for
+ gpgme.def.
+
+ * gpgme.h (gpgme_free): New prototype.
+ * data-mem.c (gpgme_free): New function.
+ * libgpgme.vers (GPGME_1.1): Add gpgme_free.
+ * gpgme.def: Add gpgme_free.
+
+2005-10-02 Marcus Brinkmann <marcus@g10code.de>
+
+ * util.h (_gpgme_decode_percent_string): Add new argument BINARY
+ to prototype.
+ * verify.c (parse_notation): Likewise for invocation.
+ * conversion.c (_gpgme_decode_percent_string): Likewise to
+ declaration. If set, do not replace '\0' characters with a
+ printable string.
+ * gpgme.h (struct _gpgme_key_sig): New field notations.
+ * ops.h (_gpgme_parse_notation): New prototype.
+ * sig-notation.c (_gpgme_parse_notation): New function.
+ * key.c (gpgme_key_unref): Free all signature notations.
+ * keylist.c (op_data_t): New member tmp_keysig.
+ (finish_key): Clear OPD->tmp_keysig.
+ * gpgme.c (gpgme_set_keylist_mode): Remove check.
+ * rungpg.c (gpg_keylist): Support listing signature notations.
+ (gpg_keylist_ext): Likewise.
+
+2005-10-01 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine.h (_gpgme_set_engine_info): Add prototype.
+ * engine-backend.h (struct engine_ops): Change return type of
+ get_file_name() to const char * to silence gcc warning.
+ * engine.c (engine_get_file_name): Change return type to const
+ char * to silence gcc warning.
+ (gpgme_get_engine_info): Use transitional variable to go from
+ const char * to char * to silence gcc warning.
+ (_gpgme_set_engine_info): Likewise.
+ * engine-gpgsm.c (struct engine_gpgsm): Change type of LINE to
+ char * to silence gcc warning.
+ (gpgsm_new): Make ARGV a pointer to const char.
+ (status_handler): Change type of SRC, END, DST, ALINE and NEWLINE
+ to char * to silence gcc warning.
+
+ * gpgme.def: Add gpgme_data_set_file_name,
+ gpgme_data_get_file_name, gpgme_sig_notation_clear,
+ gpgme_sig_notation_add and gpgme_sig_notation_get.
+ * libgpgme.vers: Add gpgme_sig_notation_clear,
+ gpgme_sig_notation_add and gpgme_sig_notation_get.
+ * Makefile.am (libgpgme_real_la_SOURCES): Add sig-notation.c.
+ * context.h (struct gpgme_context): New field sig_notations.
+ * gpgme.h (struct _gpgme_sig_notation): New member value_len and
+ critical.
+ (GPGME_SIG_NOTATION_CRITICAL): New symbol.
+ (gpgme_sig_notation_flags_t): New type.
+ (gpgme_sig_notation_add, gpgme_sig_notation_clear,
+ gpgme_sig_notation_get): New prototypes.
+ * ops.h (_gpgme_sig_notation_create, _gpgme_sig_notation_free):
+ New prototypes.
+ * sig-notation.c (_gpgme_sig_notation_free): New file.
+ * verify.c (parse_notation): Use support functions.
+ (release_op_data): Likewise.
+ * rungpg.c (append_args_from_sig_notations): New function.
+ (gpg_encrypt_sign, gpg_sign): Call it.
+
+2005-09-30 Marcus Brinkmann <marcus@g10code.de>
+
+ * data.h (struct gpgme_data): New member file_name.
+ * data.c (gpgme_data_set_filename): New function.
+ (_gpgme_data_release): Free DH->filename if necessary.
+ (gpgme_data_get_filename): New function.
+ * rungpg.c (gpg_encrypt): Set filename option.
+ (gpg_encrypt_sign): Likewise.
+ (gpg_sign): Likewise.
+ * libgpgme.vers (GPGME_1.1): Add gpgme_data_set_file_name and
+ gpgme_data_get_file_name.
+
+ * decrpyt.c, verify.c, gpgme.h: Replace plaintext_filename with
+ file_name.
+
+2005-09-29 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.h (struct _gpgme_key): Add field is_qualified.
+ (struct _gpgme_subkey): Likewise.
+ * keylist.c (set_subkey_capability, set_mainkey_capability): Set
+ field is_qualified.
+
+2005-09-23 Werner Koch <wk@g10code.com>
+
+ * w32-io.c (_gpgme_io_pipe): Removed use of environment variable
+ again.
+ (create_reader, create_writer): Set thread priority higher.
+
+2005-09-19 Werner Koch <wk@g10code.com>
+
+ * w32-io.c (_gpgme_io_pipe): New environment variable to change
+ the size of the pipe buffer.
+
+2005-09-13 Werner Koch <wk@g10code.com>
+
+ * ath.c: Changes to make it work under W32.
+
+2005-09-12 Marcus Brinkmann <marcus@g10code.de>
+
+ * Makefile.am (libgpgme_la_SOURCES): Set to ath.h and ath.c.
+ (ath_pth_src, ath_pthread_src): Removed.
+ (w32_o_files): Replace ath-compat.o with ath.o.
+ (libgpgme_pth_la_CFLAGS): New variable.
+ * ath-compat.c, ath-pthread-compat.c, ath-pth-compat.c: Removed.
+ * ath.h (ath_pthread_available, ath_pth_available): Removed.
+ (ath_init) [!_ATH_EXT_SYM_PREFIX]: Do not define macro.
+ (struct ath_ops, ath_init) [_ATH_COMPAT]: Removed.
+ (_ATH_COMPAT): Macro removed.
+ * posix-sema.c (_gpgme_sema_subsystem_init): Do not call
+ _gpgme_ath_init.
+
+2005-09-12 Marcus Brinkmann <marcus@g10code.de>
+
+ * keylist.c (release_op_data): Do not free opd->tmp_uid.
+
+2005-09-07 Werner Koch <wk@g10code.com>
+
+ * w32-io.c (build_commandline): Quote argv[0].
+
+2005-08-26 Marcus Brinkmann <marcus@g10code.de>
+
+ * rungpg.c (command_handler): Use _gpgme_io_write instead of write.
+
+ * edit.c (command_handler): Do not depend on PROCESSED being
+ available.
+
+ * engine.h (engine_command_handler_t): Add new argument processed.
+ * ops.h (_gpgme_passphrase_command_handler_internal): Rename
+ prototype to ...
+ (_gpgme_passphrase_command_handler): ... this one.
+ * passphrase.c (_gpgme_passphrase_command_handler_internal):
+ Rename to ...
+ (_gpgme_passphrase_command_handler): ... this one.
+ * edit.c (command_handler): Add new argument processed. Remove
+ local variable with the same name. Always return processed as
+ true.
+ * rungpg.c (command_handler): Send a newline character if the
+ handler did not.
+
+2005-08-26 Werner Koch <wk@g10code.com>
+
+ * w32-util.c (read_w32_registry_string): Updated from code used by
+ GnuPG. This allows for expanding strings and features the
+ implicit fallback key.
+ (w32_shgetfolderpath, find_program_at_standard_place): New.
+ (_gpgme_get_gpg_path, _gpgme_get_gpgsm_path): With no registry
+ entry, locate the programs at the standard place.
+ (dlopen, dlsym, dlclose): New, so that we can keep on using what
+ we are accustomed to.
+
+ * debug.c (debug_init): Use PATHSEP_C so that under W32 a
+ semicolon is used which allows us to create files with drive
+ letters.
+
+ * w32-io.c (_gpgme_io_read, _gpgme_io_write): Print content in
+ debug mode too.
+
+2005-07-27 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.h (gpgme_status_code_t): Add GPGME_STATUS_PLAINTEXT.
+ (struct _gpgme_op_decrypt_result): New member plaintext_filename.
+ (struct _gpgme_op_verify_result): Likewise.
+ * ops.h (_gpgme_parse_plaintext): Add prototype.
+ * op-support.c (_gpgme_parse_plaintext): New function.
+ * decrypt.c (release_op_data): Release
+ OPD->result.plaintext_filename.
+ (_gpgme_decrypt_status_handler): Handle GPGME_STATUS_PLAINTEXT.
+ * verify.c (release_op_data): Release
+ OPD->result.plaintext_filename.
+ (_gpgme_verify_status_handler): Handle GPGME_STATUS_PLAINTEXT.
+
+2005-08-08 Werner Koch <wk@g10code.com>
+
+ * util.h (stpcpy): Renamed to ..
+ (_gpgme_stpcpy): .. this and made inline. This avoids duplicate
+ definitions when linking statically.
+ * stpcpy.c: Removed.
+
+2005-08-19 Werner Koch <wk@g10code.com>
+
+ * gpgme.def: New.
+ * versioninfo.rc.in: New.
+ * Makefile.am: Addes support for building a W32 DLL.
+
+ * ttyname_r.c (ttyname_r) [W32]: Return error.
+ * ath-compat.c [W32]: select and co are not yet supported; return
+ error.
+ * data-stream.c (stream_seek): Use ftell if ftello is not available.
+
+2005-07-26 Marcus Brinkmann <marcus@g10code.de>
+
+ * keylist.c (gpgme_get_key): Allow key IDs.
+
+2005-06-20 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.m4: Only call GPGME_CONFIG if found.
+
+2005-06-03 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.h (struct _gpgme_signature): New members pubkey_algo and
+ hash_algo.
+ * verify.c (parse_valid_sig): Parse pubkey and hash algo numbers.
+ (parse_new_sig): Parse pubkey, hash algo and timestamp for ERRSIG.
+
+ (_gpgme_decrypt_status_handler): Fix last change.
+
+ * gpgme.h (struct _gpgme_recipient): New structure.
+ (gpgme_recipient_t): New type.
+ (struct _gpgme_op_decrypt_result): Add member recipients.
+ * decrypt.c (op_data_t): New member last_recipient_p.
+ (_gpgme_op_decrypt_init_result): Initialize last_recipient_p.
+ (parse_enc_to): New function.
+ (_gpgme_decrypt_status_handler): Handle status ENC_TO and
+ NO_SECKEY.
+
+ * wait-global.c (gpgme_wait): Break out of the fd processing loop
+ after an error.
+ Reported by Igor Belyi <gpgme@katehok.ac93.org>.
+
+2005-06-02 Marcus Brinkmann <marcus@g10code.de>
+
+ * wait.h (_gpgme_run_io_cb): New prototype.
+ * wait.c (_gpgme_run_io_cb): New function.
+ * wait-global.c (gpgme_wait): Call it.
+ * wait-user.c (_gpgme_user_io_cb_handler): Likewise.
+ * wait-private.c (_gpgme_wait_on_condition): Likewise.
+
+2005-06-02 Werner Koch <wk@g10code.com>
+
+ * passphrase.c (_gpgme_passphrase_status_handler): Take care of
+ GPGME_STATUS_NEED_PASSPHRASE_PIN.
+ (_gpgme_passphrase_command_handler_internal): Also act on the key
+ "passphrase.pin.ask".
+
+ * gpgme.h: Added status codes GPGME_STATUS_SIG_SUBPACKET,
+ GPGME_STATUS_NEED_PASSPHRASE_PIN, GPGME_STATUS_SC_OP_FAILURE,
+ GPGME_STATUS_SC_OP_SUCCESS, GPGME_STATUS_CARDCTRL,
+ GPGME_STATUS_BACKUP_KEY_CREATED.
+
+2005-05-28 Marcus Brinkmann <marcus@g10code.de>
+
+ * data-user.c: Include <errno.h>.
+
+2005-05-17 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.c (gpgme_new): Set the CTX->include_certs default to the
+ default.
+
+2005-05-11 Marcus Brinkmann <marcus@g10code.de>
+
+ * w32-io.c (_gpgme_io_select): Fix loop increment.
+
+2005-05-05 Marcus Brinkmann <marcus@g10code.de>
+
+ * data-user.c (user_release): Only call user hook if provided.
+ (user_seek): Return EBADF if no user hook is provided.
+ (user_read): Likewise.
+ (user_write): Likewise.
+
+2005-04-28 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.h (GPGME_INCLUDE_CERTS_DEFAULT): New macro.
+ * engine-gpgsm.c (gpgsm_sign): Send the include-certs option after
+ the reset, just for cleanliness, and do not sent it at all if the
+ default is requested.
+ * gpgme.c (gpgme_set_include_certs): Allow to use
+ GPGME_INCLUDE_CERTS_DEFAULT.
+
+2005-04-21 Werner Koch <wk@g10code.com>
+
+ * verify.c (calc_sig_summary): Set the key revoked bit.
+
+2005-04-14 Marcus Brinkmann <marcus@g10code.de>
+
+ * wait-global.c (gpgme_wait): Use LI->ctx when checking a context
+ in the list, not the user-provided CTX.
+ Reported by Igor Belyi <gpgme@katehok.ac93.org>.
+
+ * wait-global.c (gpgme_wait): If no context is found, and we
+ should not hang, set *status to 0 and return NULL.
+ Reported by Igor Belyi <gpgme@katehok.ac93.org>.
+
+2005-03-24 Marcus Brinkmann <marcus@g10code.de>
+
+ * data.h (EOPNOTSUPP) [_WIN32]: Remove definition.
+ * data.c (EOPNOTSUPP) [HAVE_W32_SYSTEM]: Remove definition.
+ (gpgme_data_read, gpgme_data_write, gpgme_data_seek): Return
+ ENOSYS instead EOPNOTSUPP.
+ * data-compat.c (EOPNOTSUPP) [HAVE_W32_SYSTEM]: Remove definition.
+ (gpgme_error_to_errno): Map GPG_ERR_NOT_SUPPORTED
+ to ENOSYS.
+
+2005-03-24 Marcus Brinkmann <marcus@g10code.de>
+
+ * io.h: Rename to ...
+ * priv-io.h: ... this.
+ * Makefile.am (libgpgme_real_la_SOURCES): Change io.h to priv-io.h.
+ * data.c, engine-gpgsm.c, posix-io.c, rungpg.c, version.c,
+ w32-io.c, wait-private.c, wait-global.c, wait-user.c, wait.c:
+ Change all includes of "io.h" to "priv-io.h"
+
+2005-03-09 Werner Koch <wk@g10code.com>
+
+ * w32-util.c (_gpgme_get_gpg_path, _gpgme_get_gpgsm_path): Do not
+ cast away type checks.
+
+ * io.h [W32]: Do not include stdio.h. If it is needed do it at
+ the right place.
+
+ * data.h [W32]: Removed kludge for EOPNOTSUP.
+ * data.c, data-compat.c [W32]: Explicitly test for it here.
+
+ Replaced use of _WIN32 by HAVE_W32_SYSTEM except for public header
+ files.
+
+2005-03-07 Timo Schulz <twoaday@g10code.de>
+
+ * gpgme.h: [_WIN32] Removed ssize_t typedef.
+ * ath.h: [_WIN32] Added some (dummy) types.
+ * io.h: [_WIN32] include stdio.h.
+ * data.h: [_WIN32] Define EOPNOTSUPP.
+ * w32-io.c [_WIN32] (_gpgme_io_subsystem_init): New.
+ * gpgme.c [_WIN32] (gpgme_set_locale): Disabled.
+
+2004-12-12 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine.c (_gpgme_set_engine_info): Fix assertion.
+
+2004-12-11 Marcus Brinkmann <marcus@g10code.de>
+
+ * util.h [HAVE_CONFIG_H && HAVE_TTYNAME_R] (ttyname_r): Define
+ prototype.
+ * ttyname_r.c: New file.
+
+2004-12-07 Marcus Brinkmann <marcus@g10code.de>
+
+ * putc_unlocked.c, funopen.c: I just claim copyright on these
+ files and change their license to LGPL, because they are totally
+ trivial wrapper functions.
+ * isascii.c: Change copyright notice to the one from ctype/ctype.h
+ in the GNU C Library (CVS Head 2004-10-10), where isascii is
+ defined as a macro doing exactly the same as the function in this
+ file.
+ * memrchr.c: Update from the GNU C Library (CVS Head 2001-07-06).
+ * stpcpy.c: Update from the GNU C Library (CVS Head 2004-10-10).
+ * ath.c, ath-compat.c, ath.h, ath-pth.c, ath-pth-compat.c,
+ ath-pthread.c, ath-pthread-compat.c, context.h, conversion.c,
+ data.c, data-compat.c, data-fd.c, data.h, data-mem.c,
+ data-stream.c, data-user.c, debug.c, debug.h, decrypt.c,
+ decrypt-verify.c, delete.c, edit.c, encrypt.c, encrypt-sign.c,
+ engine-backend.h, engine.c, engine-gpgsm.c, engine.h, error.c,
+ export.c, genkey.c, get-env.c, gpgme.c, gpgme.h, import.c, io.h,
+ key.c, keylist.c, mkstatus, Makefile.am, ops.h, op-support.c,
+ passphrase.c, posix-io.c, posix-sema.c, posix-util.c, progress.c,
+ rungpg.c, sema.h, sign.c, signers.c, trust-item.c, trustlist.c,
+ util.h, verify.c, version.c, w32-io.c, w32-sema.c, w32-util.c,
+ wait.c, wait-global.c, wait.h, wait-private.c, wait-user.c: Change
+ license to LGPL.
+
+2004-12-07 Marcus Brinkmann <marcus@g10code.de>
+
+ * libgpgme.vers (GPGME_1.1): New version.
+ * engine-backend.h (struct engine_ops): Add argument FILE_NAME to
+ member get_version(). Add arguments FILE_NAME and HOME_DIR to
+ member new(). Change return type of get_file_name and get_version
+ to char *.
+ * engine-gpgsm.c (gpgsm_get_version): Change return type to char
+ pointer. Do not cache result.
+ (gpgsm_new): Add file_name and home_dir argument, and use them
+ instead of the defaults, if set.
+ * rungpg.c (struct engine_gpg): New member file_name.
+ (gpg_get_version): Change return type to char pointer, and do not
+ cache result.
+ (gpg_release): Free gpg->file_name.
+ (gpg_new): Take new arguments file_name and home_dir. Set the
+ --homedir argument if HOME_DIR is not NULL. Set gpg->file_name.
+ (start): Use gpg->file_name instead _gpgme_get_gpg_path, if set.
+ * engine.h (_gpgme_engine_info_copy, _gpgme_engine_info_release):
+ New prototypes.
+ (_gpgme_engine_new): Change first argument to gpgme_engine_info_t
+ info.
+ * engine.c: Include <assert.h>.
+ (gpgme_get_engine_info): Set *INFO within the lock. Move
+ ENGINE_INFO and ENGINE_INFO_LOCK to ....
+ (engine_info, engine_info_lock): ... here. New static variables.
+ (engine_get_version): Add file_name argument to
+ get_version invocation. Change return type to char pointer.
+ (gpgme_engine_check_version): Rewritten to free() the return value
+ of engine_get_version after using it.
+ (_gpgme_engine_info_release): New function.
+ (gpgme_get_engine_info): Rewritten.
+ (_gpgme_engine_info_copy): New function.
+ (_gpgme_set_engine_info): New function.
+ (gpgme_set_engine_info): New function.
+ (_gpgme_engine_new): Change first argument to gpgme_engine_info_t
+ info, and use that.
+ * gpgme.h (struct _gpgme_engine_info): Change type of file_name
+ and version to char * (remove the const). New member home_dir.
+ (gpgme_set_engine_info, gpgme_ctx_get_engine_info,
+ gpgme_ctx_set_engine_info): New prototypes.
+ * context.h (struct gpgme_context): New member engine_info.
+ * gpgme.c (gpgme_new): Allocate CTX->engine_info.
+ (gpgme_release): Deallocate CTX->engine_info.
+ (gpgme_ctx_get_engine_info, gpgme_ctx_set_engine_info): New
+ functions.
+ * op-support.c (_gpgme_op_reset): Look for correct engine info and
+ pass it to _gpgme_engine_new.
+ * version.c (gpgme_check_version): Adjust to
+ _gpgme_compare_versions returning an int.
+ (_gpgme_compare_versions): Return an int value, not a const char
+ pointer.
+ * ops.h (_gpgme_compare_versions): Same for prototype.
+
+2004-10-03 Marcus Brinkmann <marcus@g10code.de>
+
+ * verify.c (parse_trust): If no reason is provided, set
+ SIG->validity_reason to 0.
+ (calc_sig_summary): Set GPGME_SIGSUM_CRL_TOO_OLD if appropriate.
+
+2004-10-22 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine-gpgsm.c (map_assuan_error): Return 0 if ERR is 0.
+ (start): Call map_assuan_error on return value of
+ assuan_write_line.
+
+2004-10-05 Marcus Brinkmann <marcus@g10code.de>
+
+ * op-support.c (_gpgme_op_data_lookup): Use char pointer for
+ pointer arithmetic.
+
+2004-09-30 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.m4: Implement the --api-version check.
+
+ * rungpg.c (read_status): Move the polling of the output data pipe
+ to just before removing the command fd, from just before adding
+ it. This avoids buffering problems.
+
+ * data.c (_gpgme_data_inbound_handler): Use _gpgme_io_read, not
+ read, to improve debug output.
+
+2004-09-29 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.h (GPGME_IMPORT_NEW, GPGME_IMPORT_UID, GPGME_IMPORT_SIG,
+ GPGME_IMPORT_SUBKEY, GPGME_IMPORT_SECRET,
+ (GPGME_KEYLIST_MODE_LOCAL, GPGME_KEYLIST_MODERN_EXTERN,
+ GPGME_KEYLIST_MODE_SIGS, GPGME_KEYLIST_MODE_VALIDATE): Change from
+ enum to macros.
+ (gpgme_keylist_mode_t): Define as unsigned int.
+ (gpgme_key_t): Change type of keylist_mode to
+ gpgme_keylist_mode_t.
+
+2004-09-23 Marcus Brinkmann <marcus@g10code.de>
+
+ * data.c (_gpgme_data_outbound_handler): Close the file descriptor
+ if we get an EPIPE.
+
+ * data-stream.c (stream_seek): Call ftello and return the current
+ offset.
+ * data.h (struct gpgme_data): Change type of data.mem.offset to
+ off_t.
+ * data.c (gpgme_data_seek): Check dh->cbs->seek callback, not read
+ callback. If SEEK_CUR, adjust the offset by the pending buffer
+ size. Clear pending buffer on success.
+
+
+2004-09-14 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.m4: Add copyright notice.
+
+2004-08-18 Marcus Brinkmann <marcus@g10code.de>
+
+ * passphrase.c (_gpgme_passphrase_status_handler): Always run the
+ status handler.
+
+2004-08-17 Marcus Brinkmann <marcus@g10code.de>
+
+ * rungpg.c (build_argv): Use --no-sk-comment, not --no-comment.
+
+2004-06-23 Marcus Brinkmann <marcus@g10code.de>
+
+ * key.c (_gpgme_key_append_name): Make sure tail points to the
+ byte following the uid.
+ (_gpgme_key_add_sig): Likewise. Don't use calloc, but malloc and
+ memset.
+
+2004-06-02 Marcus Brinkmann <marcus@g10code.de>
+
+ * libgpgme.vers: Remove C-style comment, which is not supported by
+ older binutils.
+
+2004-05-21 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme-config.in (Options): Support --api-version.
+
+ * libgpgme.vers: List all gpgme symbols under version GPGME_1.0.
+
+ * decrypt.c (_gpgme_decrypt_status_handler): Fix last change.
+ * verify.c (parse_error): Likewise.
+
+ * verify.c (parse_error): Do not skip location of where token.
+
+ * gpgme.h (gpgme_status_code_t): Add GPGME_STATUS_REVKEYSIG.
+ * verify.c (_gpgme_verify_status_handler): Add handling of
+ GPGME_STATUS_REVKEYSIG.
+ (parse_trust): Likewise.
+
+2004-05-21 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.h (struct _gpgme_decrypt_result): New fields
+ wrong_key_usage and _unused.
+ * decrypt.c (_gpgme_decrypt_status_handler): Don't skip over
+ character after a matched string, as in a protocol error this
+ could skip over the trailing binary zero.
+ Handle decrypt.keyusage error notifications.
+
+ * gpgme.h (struct _gpgme_key): New member keylist_mode.
+ * keylist.c (keylist_colon_handler): Set the keylist_mode of KEY.
+
+2004-04-29 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.h (struct _gpgme_signature): Change member WRONG_KEY_USAGE
+ to unsigned int. Same for member _unused.
+
+ * keylist.c (set_mainkey_trust_info): Rewritten.
+ (set_subkey_capability): Handle 'd' (disabled).
+ (set_mainkey_capability): Rewritten.
+
+2004-04-22 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.m4: Quote first argument to AC_DEFUN.
+
+2004-04-21 Werner Koch <wk@gnupg.org>
+
+ * key.c (gpgme_key_unref): Allow passing NULL like free does.
+ The rule of least surprise.
+
+2004-04-15 Werner Koch <wk@gnupg.org>
+
+ * verify.c (prepare_new_sig, _gpgme_verify_status_handler): Remove
+ unused result.signatures items.
+
+ * keylist.c (gpgme_get_key): Return an error if FPR is NULL.
+
+2004-04-08 Werner Koch <wk@gnupg.org>
+
+ * verify.c (_gpgme_verify_status_handler): Ignore the error status
+ if we can't process it.
+ * decrypt-verify.c (decrypt_verify_status_handler): Backed out
+ yesterday's hack. It is not any longer required.
+
+2004-04-07 Werner Koch <wk@gnupg.org>
+
+ * decrypt-verify.c (decrypt_verify_status_handler): Hack to cope
+ with meaningless error codes from the verify status function.
+
+2004-04-05 Werner Koch <wk@gnupg.org>
+
+ * gpgme.h: Add GPGME_STATUS_NEWSIG.
+
+ * verify.c (parse_error): Compare only the last part of the where
+ token.
+ (prepare_new_sig): New.
+ (parse_new_sig): Use prepare_new_sig when required.
+ (_gpgme_verify_status_handler): Handle STATUS_NEWSIG.
+
+ * engine-gpgsm.c (gpgsm_keylist_ext): Send with-validation
+ option. Fixed pattern construction.
+ (status_handler): Add debugging output.
+
+2004-03-23 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine-gpgsm.c (gpgsm_new): Protect _only_ tty related code with
+ isatty(). Submitted by Bernhard Herzog.
+
+2004-03-11 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine-gpgsm.c (gpgsm_new): Protect all tty related code with
+ isatty().
+
+ * rungpg.c (gpg_cancel): Set GPG->fd_data_map to NULL after
+ releasing it.
+ * engine-gpgsm.c (gpgsm_cancel): Only call assuan_disconnect if
+ GPGSM->assuan_ctx is not NULL. Set it to NULL afterwards.
+
+2004-03-07 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme-config.in: Do not emit include and lib directory for
+ prefix "/usr" or "".
+
+2004-03-03 Werner Koch <wk@gnupg.org>
+
+ * engine-gpgsm.c (gpgsm_export_ext): Properly insert a space
+ beween patterns.
+
+2004-02-18 Werner Koch <wk@gnupg.org>
+
+ * gpgme-config.in: Ignore setting of --prefix.
+
+2004-02-25 Marcus Brinkmann <marcus@g10code.de>
+
+ * rungpg.c (gpg_cancel): New function.
+ (gpg_release): Call it here.
+ (_gpgme_engine_ops_gpg): Add it here.
+ * engine-gpgsm.c (gpgsm_cancel): Fix last change.
+
+2004-02-24 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.c (gpgme_cancel): New function.
+ * engine-backend.h (struct engine_ops): New member cancel.
+ * engine.h (_gpgme_engine_cancel): New prototype.
+ * engine.c (_gpgme_engine_cancel): New function.
+ * engine-gpgsm.c (_gpgme_engine_ops_gpgsm): Add new member cancel.
+ (gpgsm_cancel): New function.
+ (gpgsm_release): Use it.
+ * rungpg.c (_gpgme_engine_ops_gpg): Add new member cancel.
+
+2004-02-17 Werner Koch <wk@gnupg.org>
+
+ * gpgme.h: Add GPGME_KEYLIST_MODE_VALIDATE.
+ * engine-gpgsm.c (gpgsm_keylist): Send this to gpgsm.
+
+2004-02-15 Werner Koch <wk@gnupg.org>
+
+ * memrchr.c (memrchr): Fixed implementation. Problem pointed out
+ by Adriaan de Groot.
+
+2004-02-01 Marcus Brinkmann <marcus@g10code.de>
+
+ * rungpg.c (build_argv): Use --no-comment, not --comment "".
+
+ * data-compat.c (gpgme_data_new_from_filepart): Call fseeko if
+ available.
+ * data-stream.c (stream_seek): Likewise.
+
+2004-01-16 Werner Koch <wk@gnupg.org>
+
+ * conversion.c (_gpgme_map_gnupg_error): Handle numerical codes as
+ used by GnuPG 1.9.x
+
+2004-01-13 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.h (struct _gpgme_key_sig): Fix comment on REVOKED.
+
+2004-01-12 Werner Koch <wk@gnupg.org>
+
+ * sign.c: Include util.h for prototype of _gpgme_parse_timestamp.
+
+2003-12-25 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.h (_GPGME_D_CLASS): Revert this change.
+ (struct _gpgme_key_sig): For C++ compilers, rename class
+ member to _obsolete_class. Add new member sig_class.
+ (struct _gpgme_new_signature): Same here.
+ * key.c (gpgme_key_sig_get_ulong_attr): Use CERTSIG->sig_class,
+ not CERTSIG->class.
+ * keylist.c (keylist_colon_handler): Likewise for KEYSIG, but keep
+ setting KEYSIG->class, too. Rename variable CLASS to SIG_CLASS.
+ * sign.c (parse_sig_created): Set SIG->sig_class.
+
+2003-12-22 Werner Koch <wk@gnupg.org>
+
+ * gpgme.h (_GPGME_D_CLASS): Kludge for C++ compatibility without
+ changing the C API.
+
+2003-11-19 Werner Koch <wk@gnupg.org>
+
+ * conversion.c (_gpgme_parse_timestamp): New.
+ (atoi_1, atoi_2, atoi_4): New.
+ * keylist.c (parse_timestamp): Removed. Changed all callers to use
+ the new function.
+ * verify.c (parse_valid_sig): Ditto. Repalced the errno check.
+ * sign.c (parse_sig_created): Ditto.
+
+2003-10-31 Werner Koch <wk@gnupg.org>
+
+ * keylist.c (parse_timestamp): Detect ISO 8601 timestamps and try
+ to convert them.
+
+2003-10-10 Marcus Brinkmann <marcus@g10code.de>
+
+ * genkey.c (get_key_parameter): Make a copy of the key parameters.
+ Submitted by Miguel Coca <e970095@zipi.fi.upm.es>.
+
+2003-10-06 Marcus Brinkmann <marcus@g10code.de>
+
+ * data-compat.c: Include <sys/time.h> before <sys/stat.h> for
+ broken systems.
+
+ * engine-gpgsm.c (map_assuan_error): If ERR is -1, return sensible
+ error.
+
+ * io.h (_gpgme_io_subsystem_init): New prototype.
+ * posix-io.c (_gpgme_io_subsystem_init): Add function.
+ (_gpgme_io_spawn): Do not fixup signal handler here.
+ * version.c (do_subsystem_inits): Call _gpgme_io_subsystem_init.
+
+ * debug.c (debug_init): Drop const qualifier from E.
+
+ * ath.h (struct ath_ops): Make ADDR argument of CONNECT prototype
+ const.
+ (ath_connect): Make ADDR argument const.
+ * ath-pthread.c (ath_connect): Likewise.
+ * ath-pth.c (ath_connect): Likewise.
+ * ath-compat.c (ath_connect): Likewise.
+ * ath.c (ath_connect): Likewise.
+
+ * ath.h [HAVE_SYS_SELECT_H]: Include <sys/select.h> for fd_set.
+ [!HAVE_SYS_SELECT_H]: Include <sys/time.h>.
+
+ * conversion.c (_gpgme_hextobyte): Drop "unsigned" from type of
+ SRC argument.
+ * util.h (_gpgme_hextobyte): Likewise for prototype.
+
+ * gpgme.h: Remove trailing comma in enum.
+
+ * rungpg.c: Do not include <time.h>, <sys/time.h>, <sys/types.h>,
+ <signal.h>, <fcntl.h>, or "unistd.h".
+
+2003-10-02 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine-backend.h (struct engine_ops): Add argument TYPE.
+ * engine.c (_gpgme_engine_op_edit): Likewise.
+ * engine.h: Likewise.
+ * rungpg.c (gpg_edit): Likewise. Use it.
+ * edit.c (edit_start): Likewise. Pass it on.
+ (gpgme_op_edit_start, gpgme_op_edit): Likewise.
+ (gpgme_op_card_edit_start, gpgme_op_card_edit): New functions.
+
+2003-09-30 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.h (gpg_strerror_r): Change prototype to match
+ gpg_strerror_r change.
+ * error.c (gpg_strerror_r): Likewise, also update implementation.
+
+ * gpgme.c (gpgme_hash_algo_name): Change name of RMD160 to
+ RIPEMD160, name of TIGER to TIGER192, name of CRC32-RFC1510 to
+ CRC32RFC1510, and name of CRC24-RFC2440 to CRC24RFC2440.
+
+2003-09-14 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.h: Add prototype for gpgme_set_locale.
+
+ * gpgme.h: Define macro _GPGME_INLINE depending on the compiler
+ characteristics and use that instead __inline__.
+
+ * context.h (struct gpgme_context): New members lc_ctype and
+ lc_messages.
+ * gpgme.c: Include <locale.h>.
+ (def_lc_lock, def_lc_ctype, def_lc_messages): New static
+ variables.
+ (gpgme_set_locale): New function.
+ * engine.c (_gpgme_engine_new): Add arguments lc_ctype and
+ lc_messages.
+ * engine.h (_gpgme_engine_new): Likewise.
+ * engine-gpgsm.c (gpgsm_new): Likewise.
+ * rungpg.c (gpg_new): Likewise.
+ * engine-backend.h (struct engine_ops): Likewise to NEW.
+ * op-support.c (_gpgme_op_reset): Likewise to invocation of
+ _gpgme_engine_new.
+
+2003-09-13 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.h (gpgme_strerror_r): New prototype.
+ * error.c (gpgme_strerror_r): New function.
+
+ * get-env.c: New file.
+ * util.h (_gpgme_getenv): Add prototype.
+ * Makefile.am (libgpgme_real_la_SOURCES): Add get-env.c.
+ * rungpg.c (build_argv): Use _gpgme_getenv.
+ * debug.c (debug_init): Likewise.
+ * engine-gpgsm.c (gpgsm_new): Likewise.
+ (gpgsm_new): Use ttyname_r.
+ * w32-io.c (_gpgme_io_spawn): Disable debugging for now.
+
+2003-09-03 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme-config.in: Use $libdir, not @libdir@, for the echo
+ command.
+
+ * gpgme-config.in: Rewritten.
+ * gpgme.m4: Rewritten.
+
+2003-08-19 Marcus Brinkmann <marcus@g10code.de>
+
+ The ath files (ath.h, ath.c, ath-pth.c, ath-pthread.c,
+ ath-compat.c, ath-pth-compat.c and ath-pthread-compat.c) have been
+ updated to have better thread support, and the Makefile.am was
+ changed to reflect that.
+
+ * util.h [!HAVE_FOPENCOOKIE]: Remove fopencookie declaration.
+ * engine-gpgsm.c (gpgsm_assuan_simple_command): Set ERR to return
+ value of status_fnc.
+ * rungpg.c (start): Return SAVED_ERRNO, not errno.
+
+2003-08-18 Marcus Brinkmann <marcus@g10code.de>
+
+ * rungpg.c (start): Use saved_errno instead errno.
+
+2003-08-18 Marcus Brinkmann <marcus@g10code.de>
+
+ * funopen.c, putc_unlocked.c, isascii.c, memrchr.c: New files.
+ * fopencookie.c: File removed.
+
+2003-08-15 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme-config.in: Put gpg-error related flags after gpgme's.
+
+2003-08-14 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.h (struct _gpgme_new_signature): Rename member CLASS to
+ _OBSOLETE_CLASS, add member CLASS with type unsigned int.
+ * sign.c (parse_sig_created): Also set SIG->_unused_class for
+ backward compatibility.
+
+2003-08-04 Marcus Brinkmann <marcus@g10code.de>
+
+ * verify.c (parse_new_sig): Fix status parsing case.
+
+2003-07-31 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.h (struct _gpgme_subkey): Add flag CAN_AUTHENTICATE.
+ Lower _UNUSED to 23 bits.
+ (struct _gpgme_key): Likewise.
+ * keylist.c (set_mainkey_capability): Support 'a' and 'A'.
+ (set_subkey_capability): Support 'a'.
+
+ * keylist.c (gpgme_get_key): Check if there is more than one key
+ listed, and return GPG_ERR_AMBIGUOUS_NAME in that case.
+
+ * util.h (_gpgme_decode_c_string): Change type of LEN argument to
+ size_t.
+ (_gpgme_decode_percent_string): Likewise.
+ * conversion.c (_gpgme_decode_c_string): Likewise.
+ (_gpgme_decode_percent_string): Likewise.
+ (_gpgme_map_gnupg_error): Change type of I to unsigned int.
+ * signers.c (gpgme_signers_clear): Likewise.
+ (gpgme_signers_enum): New unsigned variable SEQNO, set to SEQ.
+ Use SEQNO instead SEQ.
+ * wait.c (fd_table_put): Change type of I and J to unsigned int.
+ * wait-global.c (_gpgme_wait_global_event_cb): Change type of IDX
+ to unsigned int.
+ (gpgme_wait): Change type of I and IDX to unsigned int.
+ * wait-private.c (_gpgme_wait_on_condition): Change type of IDX
+ and I to unsigned int.
+ * posix-io.c (_gpgme_io_close): Cast return value of macro DIM to
+ int to suppress gcc warning.
+ (_gpgme_io_set_close_notify): Likewise.
+ (_gpgme_io_select): Change type of I to unsigned int.
+ * engine.c (gpgme_get_engine_info): Change type of PROTO to
+ unsigned int.
+ * wait-user.c (_gpgme_user_io_cb_handler): Change type of IDX and
+ I to unsigned int.
+
+2003-07-29 Marcus Brinkmann <marcus@g10code.de>
+
+ * decrypt-verify.c (decrypt_verify_status_handler): Expand silly
+ and wrong expression.
+ * encrypt-sign.c (encrypt_sign_status_handler): Likewise.
+ * encrypt.c (encrypt_sym_status_handler): Likewise.
+ * sign.c (sign_status_handler): Likewise.
+ * verify.c (verify_status_handler): Likewise.
+ * decrypt.c (decrypt_status_handler): Likewise.
+
+ * engine.c (gpgme_get_engine_info): Initialize NULL.
+
+2003-07-23 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme-config.in (gpg_error_libs): Quote GPG_ERROR_CFLAGS and
+ GPG_ERROR_LIBS when setting the corresponding variables.
+ Reported by Stéphane Corthésy.
+
+2003-07-22 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine-gpgsm.c (set_recipients): Move declaration of NEWLEN to
+ the beginning of the block.
+
+2003-06-22 Marcus Brinkmann <marcus@g10code.de>
+
+ * data-mem.c (mem_write): Copy original buffer content.
+
+2003-06-22 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.h (gpgme_user_ids_release, gpgme_user_ids_append): Remove
+ prototypes.
+
+2003-06-06 Marcus Brinkmann <marcus@g10code.de>
+
+ * Makefile.am (AM_CPPFLAGS): Add @GPG_ERROR_CFLAGS@.
+ * gpgme-config.in (gpg_error_libs, gpg_error_cflags): New variables.
+ Print them.
+
+ * op-support.c (_gpgme_parse_inv_userid): Rename to
+ _gpgme_parse_inv_recp and change to new datatype.
+ * ops.h (_gpgme_parse_inv_key): Fix prototype.
+ * gpgme.h (struct _gpgme_invalid_user_id): Rename to
+ __gpgme_invalid_key. Rename field ID to KEY.
+ (gpgme_invalid_user_id_t): Rename to gpgme_invalid_key_t.
+ (struct _gpgme_op_encrypt_result): Here, too.
+ (struct _gpgme_op_sign_result): Likewise.
+ * encrypt.c (struct op_data): Likewise.
+ (release_op_data): Likewise.
+ * sign.c (struct op_data): Likewise.
+ (release_op_data): Likewise.
+
+ * posix-io.c (_gpgme_io_read): Save errno across debug calls.
+ (_gpgme_io_write): Likewise.
+ (_gpgme_io_pipe): Likewise.
+ (_gpgme_io_select): Likewise.
+
+ * rungpg.c (struct engine_gpg): Remove arg_error.
+ (add_arg): Don't set arg_error.
+ (add_data): Likewise.
+ (start): Don't check arg_error.
+ (gpg_new): Check return value of add_arg.
+ * verify.c (parse_notation): Free allocated memory at error.
+
+2003-06-05 Marcus Brinkmann <marcus@g10code.de>
+
+ Everywhere: Use libgpg-error error codes.
+
+ * Makefile.am (EXTRA_DIST): Remove mkerrors.
+ (BUILT_SOURCES): Remove errors.c.
+ (MOSTLYCLEANFILES): Likewise.
+ (libgpgme_la_SOURCES): Likewise. Add error.c.
+ (errors.c): Remove target.
+ * mkerrors: File removed.
+ * error.c: New file.
+
+ * gpgme.h (gpgme_error_t): Change to type gpg_error_t.
+ (gpgme_err_code_t, gpgme_err_source_t): New types.
+ (gpgme_err_code, gpgme_err_source, gpgme_error, gpgme_err_make):
+ New static inline functions.
+ (gpgme_strsource, gpgme_err_code_from_errno,
+ gpgme_err_code_to_errno, gpgme_err_make_from_errno,
+ gpgme_error_from_errno): New prototypes.
+
+2003-05-29 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.h (gpgme_op_export_start): Change second arg to const char *.
+ (gpgme_op_export): Likewise.
+ (gpgme_op_export_ext_start): New prototype.
+ (gpgme_op_export_ext): Likewise.
+ * engine.h: Likewise for _gpgme_engine_op_export and
+ _gpgme_engine_op_export_ext.
+ * engine-backend.h (struct engine_ops): Change second argument of
+ prototype of export to const char *, and add reserverd int as
+ third argument. Add prototype for export_ext.
+ * engine.c (_gpgme_engine_op_export_ext): New function.
+ (_gpgme_engine_op_export): Change second argument of prototype of
+ export to const char *, and add reserverd int as third argument.
+ * rungpg.c (gpg_export): Change second argument of prototype of
+ export to const char *, and add reserverd int as third argument.
+ (gpg_export_ext): New function.
+ (gpg_keylist_ext): Break loop at error.
+ (_gpgme_engine_ops_gpg): Add gpg_export_ext.
+ * engine-gpgsm.c (gpgsm_export): Change second argument of
+ prototype of export to const char *, and add reserverd int as
+ third argument.
+ (gpgsm_export_ext): New function.
+ (_gpgme_engine_ops_gpgsm): Add gpgsm_export_ext.
+ * export.c (export_start): Change second argument of prototype of
+ export to const char *, and add reserverd int as third argument.
+ (gpgme_op_export_start): Likewise.
+ (export_ext_start): New function.
+ (gpgme_op_export_ext_start): Likewise.
+ (gpgme_op_export_ext): Likewise.
+
+ * gpgme.h (gpgme_keylist_mode_t): New type for anonymous enum.
+ (gpgme_sigsum_t): New type for anonymous enum.
+
+ * encrypt-sign.c (encrypt_sign_start): Check for errors earlier,
+ and return an error if RECP is not set.
+
+ * Makefile.am (libgpgme_la_SOURCES): Remove user-id.c.
+ * user-id.c: Remove file.
+ * ops.h: Remove prototype for _gpgme_user_ids_all_valid.
+ * gpgme.h (gpgme_encrypt_flags_t): New type.
+ (gpgme_op_encrypt_start): Change second parameter to type
+ gpgme_key_t[], and add third parameter.
+ (gpgme_op_encrypt): Likewise.
+ (gpgme_op_encrypt_sign_start): Likewise.
+ (gpgme_op_encrypt_sign): Likewise.
+ * encrypt.c (encrypt_start): Likewise.
+ (gpgme_op_encrypt_start): Likewise.
+ (gpgme_op_encrypt): Likewise. Pass flags to engine.
+ * encrypt-sign.c (encrypt_sign_start): Likewise.
+ (gpgme_op_encrypt_sign_start): Likewise.
+ (gpgme_op_encrypt_sign): Likewise.
+ * engine-backend.h (struct engine_ops): Likewise for prototypes of
+ encrypt and encrypt_sign.
+ * engine.h: Likewise for prototypes of _gpgme_engine_op_encrypt
+ and _gpgme_engine_op_encrypt_sign.
+ * engine.c (_gpgme_engine_op_encrypt): Likewise.
+ (_gpgme_engine_op_encrypt_sign): Likewise.
+ * rungpg.c (gpg_encrypt): Likewise.
+ (gpg_encrypt_sign): Likewise.
+ * rungpg.c (gpg_encrypt): Check flags for always trust option.
+ * engine-gpgsm.c (gpgsm_encrypt): Likewise.
+ (set_recipients): Rewritten to use keys instead user IDs.
+ * rungpg.c (append_args_from_recipients): Rewritten to use keys
+ instead user IDs.
+ * encrypt.c (_gpgme_encrypt_status_handler): Change errors
+ returned to GPGME_Invalid_Key and GPGME_General_Error.
+
+2003-05-28 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine-gpgsm.c: Rename GpgsmObject to engine_gpgsm_t.
+ (struct gpgsm_object_s): Rename to struct engine_gpgsm.
+ * rungpg.c: Rename GpgObject to engine_gpg_t.
+ (struct gpg_object_s): Rename to struct engine_gpg.
+
+ * context.h (struct gpgme_context): Change EngineObject to
+ engine_object_t.
+ (enum ctx_op_data_type): Rename to ctx_op_data_id_t.
+ (ctx_op_data_t): New type.
+ (struct gpgme_context): Use it.
+ * ops.h (_gpgme_op_data_lookup): Use new type name.
+ * op-support.c (_gpgme_op_data_lookup): Likewise.
+ * engine.c: Rename EngineObject to engine_t in the file. Also
+ EngineStatusHandler to engine_status_handler_t,
+ EngineCommandHandler to engine_command_handler_t and
+ EngineColonLineHandler to engine_colon_line_handler.
+ * rungpg.c (start): Likewise.
+ * engine-gpgsm.c: Likewise.
+ * engine-backend.h (struct engine_ops): Likewise
+ * engine.h (struct engine_object_s): Rename to struct engine.
+ (EngineObject): Rename to engine_t. Also everywhere else in the
+ file.
+ (EngineStatusHandler): Rename to engine_status_handler_t.
+ (EngineColonLineHandler): Rename to engine_colon_line_handler_t.
+ (EngineCommandHandler): Rename to engine_command_handler_t.
+
+ * engine-gpgsm.c (gpgsm_export): Fix bug in last change.
+
+ * Makefile.am (libgpgme_la_SOURCES): Remove recipient.c, add
+ user-id.c.
+ * gpgme.h (gpgme_recipients_t): Removed.
+ (gpgme_recipients_new, gpgme_recipients_release,
+ gpgme_recipients_add_name,
+ gpgme_recipients_add_name_with_validity, gpgme_recipients_count,
+ gpgme_recipients_enum_open, gpgme_recipients_enum_read,
+ gpgme_recipients_enum_close): Removed.
+ (gpgme_op_encrypt, gpgme_op_encrypt_start, gpgme_op_encrypt_sign,
+ gpgme_op_encrypt_sign_start, gpgme_op_export_start,
+ gpgme_op_export): Change second argument to gpgme_user_id_t.
+ (gpgme_user_ids_release): New prototype.
+ (gpgme_user_ids_append): Likewise.
+ * ops.h (_gpgme_recipients_all_valid): Remove.
+ (_gpgme_user_ids_all_valid): Add.
+ * context.h (struct gpgme_recipients): Removed.
+ * user-id.c: New file.
+ * recipient.c: Removed file.
+ * rungpg.c (append_args_from_recipients): Change last arg to
+ gpgme_user_id_t. Reimplement.
+ (gpg_encrypt): Change second arg to gpgme_user_id_t.
+ (gpg_encrypt_sign): Likewise.
+ (gpg_export): Likewise. Rewrite user ID list code.
+ * engine.c (_gpgme_engine_op_encrypt): Change second arg to
+ gpgme_user_id_t.
+ (_gpgme_engine_op_encrypt_sign): Likewise.
+ (_gpgme_engine_op_export): Likewise.
+ * engine.h (_gpgme_engine_op_encrypt, _gpgme_engine_op_encrypt_sign,
+ _gpgme_engine_op_export): Likewise.
+ * engine-gpgsm.c (set_recipients): Likewise. Rewrite loop code.
+ (gpgsm_encrypt): Likewise.
+ (gpgsm_export): Likewise.
+ * engine-backend.h (struct engine_ops): Likewise for members
+ ENCRYPT, ENCRYPT_SIGN and EXPORT.
+ * export.c (export_start, gpgme_op_export_start, gpgme_op_export):
+ Likewise.
+ * encrypt.c (encrypt_start): Likewise. Don't check for count of
+ recipients.
+ (gpgme_op_encrypt_start): Likewise.
+ (gpgme_op_encrypt): Likewise.
+ * encrypt-sign.c (encrypt_sign_start): Likewise.
+ (gpgme_op_encrypt_sign): Likewise.
+ (gpgme_op_encrypt_sign_start): Likewise.
+
+2003-05-27 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.h (struct _gpgme_op_import_result): Add skipped_new_keys.
+ * import.c (parse_import_res): Add skipped_new_keys parser.
+
+ * op-support.c (_gpgme_parse_inv_userid): Add missing break
+ statements.
+ * encrypt.c (gpgme_op_encrypt): Use gpgme_error_t instead of int.
+
+2003-05-27 Marcus Brinkmann <marcus@g10code.de>
+
+ * encrypt.c (gpgme_op_encrypt_result): Use intermediate variable
+ HOOK to avoid compiler warning. Don't ask, you don't want to know.
+ (_gpgme_encrypt_status_handler): Likewise.
+ (_gpgme_op_encrypt_init_result): Likewise.
+ * decrypt.c (gpgme_op_decrypt_result): Likewise.
+ (_gpgme_decrypt_status_handler): Likewise.
+ (_gpgme_op_decrypt_init_result): Likewise.
+ * verify.c (gpgme_op_verify_result): Likewise.
+ (_gpgme_verify_status_handler): Likewise.
+ (_gpgme_op_verify_init_result): Likewise.
+ * edit.c (edit_status_handler): Likewise.
+ (command_handler): Likewise.
+ (edit_start): Likewise.
+ * genkey.c (gpgme_op_genkey_result): Likewise.
+ (genkey_status_handler): Likewise.
+ (genkey_start): Likewise.
+ * import.c (gpgme_op_import_result): Likewise.
+ (import_status_handler): Likewise.
+ (_gpgme_op_import_start): Likewise.
+ * trustlist.c (gpgme_op_trustlist_next): Likewise.
+ (_gpgme_op_trustlist_event_cb): Likewise.
+ (gpgme_op_trustlist_start): Likewise.
+ * keylist.c (gpgme_op_keylist_result): Likewise.
+ (keylist_colon_handler): Likewise.
+ (keylist_status_handler): Likewise.
+ (_gpgme_op_keylist_event_cb): Likewise.
+ (gpgme_op_keylist_start): Likewise.
+ (gpgme_op_keylist_ext_start): Likewise.
+ (gpgme_op_keylist_next): Likewise.
+ * passphrase.c (_gpgme_passphrase_status_handler): Likewise.
+ (_gpgme_passphrase_command_handler_internal): Likewise.
+ * sign.c (gpgme_op_sign_result): Likewise.
+ (_gpgme_sign_status_handler): Likewise.
+ (_gpgme_op_sign_init_result): Likewise.
+
+ * passphrase.c (_gpgme_passphrase_command_handler_internal): Fix
+ access to pointer type.
+
+2003-05-26 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine.h (EngineCommandHandler): Change last argument to int fd.
+ * gpgme.h (gpgme_passphrase_cb_t): Rewritten to take parts of the
+ description and fd.
+ (gpgme_edit_cb_t): Change last argument to int fd.
+ * ops.h (_gpgme_passphrase_command_handler_internal): New prototype.
+ * passphrase.c: Include <assert.h>.
+ (op_data_t): Rename userid_hint to uid_hint, remove last_pw_handle.
+ (release_op_data): Check values before calling free.
+ (_gpgme_passphrase_status_handler): Likewise.
+ (_gpgme_passphrase_command_handler_internal): New function.
+ (_gpgme_passphrase_command_handler): Rewritten.
+ * edit.c (edit_status_handler): Pass -1 as fd argument.
+ (command_handler): Update prototype. New variable processed. Use
+ it to store return value of
+ _gpgme_passphrase_command_handler_internal which is now used
+ instead _gpgme_passphrase_command_handler. Use it also to check
+ if we should call the user's edit function. Pass fd to user's
+ edit function.
+ * rungpg.c (struct gpg_object_s): Change type of cmd.cb_data to
+ void *.
+ (gpg_release): Check value before calling free. Do not release
+ cmd.cb_data.
+ (command_cb): Function removed.
+ (command_handler): New function. Thus we don't use a data object
+ for command handler stuff anymore, but handle it directly. This
+ allows proper error reporting (cancel of passphrase requests, for
+ example). Also all callbacks work via direct writes to the file
+ descriptor (so that passphrases are not kept in insecure memory).
+ (gpg_set_command_handler): Rewritten to use even more ugly hacks.
+ (read_status): Check cmd.keyword before calling free. Install
+ command_handler as the I/O callback handler with GPG as private
+ data.
+
+ * rungpg.c (gpg_new): Add --enable-progress-filter to gpg
+ invocation.
+ * decrypt-verify.c (_gpgme_op_decrypt_verify_start): Rename to
+ decrypt_verify_start.
+ (gpgme_op_decrypt_verify_start): Call decrypt_verify_start.
+ (gpgme_op_decrypt_verify): Likewise.
+ * verify.c (verify_status_handler): New function that also calls
+ progress status handler.
+ (_gpgme_op_verify_start): Set status handler to verify_status_handler.
+ Rename to (verify_start).
+ (gpgme_op_verify_start): Call verify_start.
+ (gpgme_op_verify): Likewise.
+ * encrypt.c (encrypt_status_handler): New function.
+ (_gpgme_encrypt_sym_status_handler): Call progress status handler.
+ Make static. Rename to encrypt_sym_status_handler.
+ (encrypt_start): Set status handler to encrypt_sym_status_handler
+ or encrypt_status_handler.
+ * sign.c (sign_status_handler): New function.
+ (sign_start): Set status handler to sign_status_handler.
+ * decrypt.c (decrypt_status_handler): New function that also calls
+ progress status handler.
+ (decrypt_start): Set status handler to decrypt_status_handler.
+ * encrypt-sign.c (encrypt_sign_status_handler): Likewise.
+ * decrypt-verify.c (decrypt_verify_status_handler): Call
+ _gpgme_progress_status_handler.
+
+ * conversion.c (_gpgme_decode_c_string): Add missing break
+ statement.
+
+ * recipient.c (gpgme_recipients_add_name_with_validity): Add one
+ to buffer to allocate.
+
+2003-05-19 Marcus Brinkmann <marcus@g10code.de>
+
+ * verify.c (parse_new_sig): Fix ERRSIG case.
+ Submitted by Benjamin Lee <benjaminlee@users.sf.net>.
+
+2003-05-18 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.h: The following types are renamed. The old name is kept
+ as a deprecated typedef.
+ (GpgmeCtx): Rename to gpgme_ctx_t.
+ (GpgmeData): Rename to gpgme_data_t.
+ (GpgmeRecipients): Rename to gpgme_recipients_t.
+ (GpgmeError): Rename to gpgme_error_t.
+ (GpgmeDataEncoding): Rename to gpgme_data_encoding_t.
+ (GpgmePubKeyAlgo): Rename to gpgme_pubkey_algo_t.
+ (GpgmeHashAlgo): Rename to gpgme_hash_algo_t.
+ (GpgmeSigStat): Rename to gpgme_sig_stat_t.
+ (GpgmeSigMode): Rename to gpgme_sig_mode_t.
+ (GpgmeAttr): Rename to gpgme_attr_t.
+ (GpgmeValidity): Rename to gpgme_validity_t.
+ (GpgmeProtocol): Rename to gpgme_protocol_t.
+ (GpgmeStatusCode): Rename to gpgme_status_code_t.
+ (GpgmeEngineInfo): Rename to gpgme_engine_info_t.
+ (GpgmeSubkey): Rename to gpgme_subkey_t.
+ (GpgmeKeySig): Rename to gpgme_keysig_t.
+ (GpgmeUserID): Rename to gpgme_user_id_t.
+ (GpgmePassphraseCb): Rename to gpgme_passphrase_cb_t.
+ (GpgmeProgressCb): Rename to gpgme_progress_cb_t.
+ (GpgmeEditCb): Rename to gpgme_edit_cb_t.
+ (GpgmeIOCb): Rename to gpgme_io_cb_t.
+ (GpgmeRegisterIOCb): Rename to gpgme_register_io_cb_t.
+ (GpgmeRemoveIOCb): Rename to gpgme_remove_io_cb_t.
+ (GpgmeEventIO): Rename to gpgme_event_io_t.
+ (GpgmeEventIOCb): Rename to gpgme_event_io_cb_t.
+ (GpgmeIOCbs): Rename to gpgme_io_cbs.
+ (gpgme_io_cbs_t): New type.
+ (GpgmeDataReadCb): Rename to gpgme_data_read_cb_t.
+ (GpgmeDataWriteCb): Rename to gpgme_data_write_cb_t.
+ (GpgmeDataSeekCb): Rename to gpgme_data_seek_cb_t.
+ (GpgmeDataReleaseCb): Rename to gpgme_data_release_cb_t.
+ (GpgmeDataCbs): Rename to gpgme_data_cbs.
+ (gpgme_data_cbs_t): New type.
+ (GpgmeInvalidUserID): Rename to gpgme_invalid_user_id_t.
+ (GpgmeEncryptResult): Rename to gpgme_encrypt_result_t.
+ (GpgmeDecryptResult): Rename to gpgme_decrypt_result_t.
+ (GpgmeNewSignature): Rename to gpgme_new_signature_t.
+ (GpgmeSignResult): Rename to gpgme_sign_result_t.
+ (GpgmeSigNotation): Rename to gpgme_sig_notation_t.
+ (GpgmeSignature): Rename to gpgme_signature_t.
+ (GpgmeVerifyResult): Rename to gpgme_verify_result_t.
+ (GpgmeImportStatus): Rename to gpgme_import_status_t.
+ (GpgmeImportResult): Rename to gpgme_import_result_t.
+ (GpgmeGenKeyResult): Rename to gpgme_genkey_result_t.
+ (GpgmeKeyListResult): Rename to gpgme_keylist_result_t.
+ (GpgmeTrustItem): Rename to gpgme_trust_item_t.
+ * gpgme.h (gpgme_deprecated_error_t): New type, swallowing macros
+ GPGME_No_Recipients, GPGME_Invalid_Recipient and
+ GPGME_No_Passphrase.
+ * data.h (struct gpgme_data_s): Rename to struct gpgme_data.
+ * context.h (struct gpgme_context_s): Rename to struct
+ gpgme_context.
+ (struct gpgme_recipients_s): Rename to gpgme_recipients.
+
+2003-05-18 Marcus Brinkmann <marcus@g10code.de>
+
+ * keylist.c (finish_key): Clear OPD->tmp_uid.
+
+2003-05-18 Marcus Brinkmann <marcus@g10code.de>
+
+ * verify.c (_gpgme_verify_status_handler): Return GPGME_No_Data
+ for NODATA status without signatures.
+
+2003-05-05 Marcus Brinkmann <marcus@g10code.de>
+
+ * key.c (_gpgme_key_append_name): Use decoded string to parse user id.
+ (_gpgme_key_add_sig): Likewise.
+
+2003-05-04 Marcus Brinkmann <marcus@g10code.de>
+
+ * context.h (struct gpgme_context_s): Remove member op_info.
+
+ * key.c (_gpgme_key_add_sig): Initialize SIG->uid.
+
+ * gpgme.h (GpgmeError): Add deprecated values for
+ GPGME_Invalid_Type and GPGME_Invalid_Mode.
+
+2003-04-30 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.h (gpgme_get_op_info): Remove prototype.
+ * ops.h (_gpgme_set_op_info,
+ _gpgme_data_release_and_return_string, _gpgme_data_get_as_string,
+ _gpgme_data_append, _gpgme_data_append_string,
+ _gpgme_data_append_string_for_xml, _gpgme_data_append_for_xml,
+ _gpgme_data_append_percentstring_for_xml): Likewise.
+ (_gpgme_progress_status_handler): Change first arg to void *.
+ * progress.c (_gpgme_progress_status_handler): Likewise.
+ * conversion.c: Do not include <string.h>, <errno.h>, <ctype.h>,
+ and <sys/types.h>, but <string.h>.
+ (_gpgme_data_append): Remove function.
+ (_gpgme_data_append_string): Likewise.
+ (_gpgme_data_append_for_xml): Likewise.
+ (_gpgme_data_append_string_for_xml): Likewise.
+ (_gpgme_data_append_percentstring_for_xml): Likewise.
+ * data-mem.c (_gpgme_data_get_as_string): Likewise.
+ (_gpgme_data_release_and_return_string): Likewise.
+ * gpgme.c (gpgme_get_op_info): Likewise.
+ (_gpgme_set_op_info): Likewise.
+
+ * gpgme.h (struct _gpgme_key): New structure.
+ (GpgmeKey): Define using _gpgme_key.
+ (struct _gpgme_subkey): New structure.
+ (GpgmeSubKey): New type.
+ (struct _gpgme_key_sig): New structure.
+ (GpgmeKeySig): New type.
+ (struct _gpgme_user_id): New structure.
+ (GpgmeUserID): New type.
+ (struct _gpgme_op_keylist_result): New structure.
+ (GpgmeKeyListResult): New type.
+ (gpgme_op_keylist_result): New function.
+ (gpgme_key_get_as_xml): Remove prototype.
+ * context.h (struct gpgme_context_s): Remove members tmp_key,
+ tmp_uid, key_cond and key_queue.
+ (struct key_queue_item_s): Remove structure.
+ (struct user_id_s): Remove structure.
+ (struct gpgme_recipients_s): Replace with simple
+ GpgmeUserID list.
+ * gpgme.c (gpgme_release): Do not release CTX->tmp_key.
+ * ops.h (_gpgme_key_add_subkey, _gpgme_key_append_name,
+ _gpgme_key_add_sig, _gpgme_trust_item_new): New prototypes.
+ * rungpg.c (command_cb): Return GpgmeError instead int.
+ New variable ERR. Use it to hold return value of cmd handler.
+ (gpg_delete): Access fingerprint of key directly.
+ (append_args_from_signers): Likewise.
+ (gpg_edit): Likewise.
+ (append_args_from_recipients): Use GpgmeUserID for recipient list.
+ * engine-gpgsm.c: Do not include "key.h".
+ (gpgsm_delete): Access fingerprint of key directly.
+ (gpgsm_sign): Likewise.
+ (set_recipients): Use GpgmeUserID for recipients. Invert invalid
+ user ID flag.
+ * key.h: File removed.
+ * key.c: Completely reworked to use exposed GpgmeKey data types.
+ * keylist.c: Likewise.
+ * recipient.c: Completely reworked to use GpgmeUserID.
+
+2003-04-29 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.h (gpgme_get_key): Remove force_update argument.
+ * key-cache.c: File removed.
+ * Makefile.am (libgpgme_la_SOURCES): Remove key-cache.c.
+ * ops.h (_gpgme_key_cache_add, _gpgme_key_cache_get): Remove
+ prototypes.
+ * keylist.c (_gpgme_op_keylist_event_cb): Don't call
+ _gpgme_key_cache_add.
+ (gpgme_get_key): New function.
+ * verify.c (gpgme_get_sig_key): Remove last argument to
+ gpgme_get_key invocation.
+
+ * gpgme.h (struct _gpgme_trust_item): New structure.
+ (GpgmeTrustItem): New type.
+ (gpgme_trust_item_ref, gpgme_trust_item_unref): New prototypes.
+ * context.h (struct trust_queue_item_s): Remove structure.
+ (struct gpgme_context_s): Remove trust_queue member.
+ * Makefile.am (libgpgme_la_SOURCES): Add trust-item.c.
+ * trust-item.c: New file.
+ * trustlist.c: Do not include <stdio.h> or <time.h>, but
+ "gpgme.h".
+ (struct trust_queue_item_s): Change to new type op_data_t.
+ (trust_status_handler): Change first argument to void *.
+ (trust_colon_handler): Likewise.
+ (_gpgme_op_trustlist_event_cb): Use op_data_t type.
+ (gpgme_op_trustlist_start): Use op_data_t and rework error
+ handling.
+ (gpgme_op_trustlist_next): Use op_data_t.
+ (gpgme_trust_item_release): Remove function.
+ (gpgme_trust_item_get_string_attr): Likewise.
+ (gpgme_trust_item_get_int_attr): Likewise.
+
+ * verify.c (calc_sig_summary): Do not set GPGME_SIGSUM_SYS_ERROR
+ for bad signatures.
+
+2003-04-28 Marcus Brinkmann <marcus@g10code.de>
+
+ * context.h: Remove OPDATA_VERIFY_COLLECTING.
+ (struct gpgme_context_s): Remove member notation.
+ * gpgme.h: Make enum for GPGME_KEYLIST_MODE_* values.
+
+ * gpgme.h (struct _gpgme_sig_notation): New structure.
+ (GpgmeSigNotation): New type.
+ (struct _gpgme_signature): New structure.
+ (GpgmeSignature): New type.
+ (struct _gpgme_op_verify_result): New structure.
+ (GpgmeVerifyResult): New type.
+ (gpgme_op_verify_result): New prototype.
+ (gpgme_get_notation): Remove prototype.
+ * ops.h (_gpgme_op_verify_init_result): New prototype.
+ (_gpgme_verify_status_handler): Change first argument to void *.
+ * util.h (_gpgme_decode_percent_string, _gpgme_map_gnupg_error):
+ New prototypes.
+ * conversion.c (_gpgme_decode_percent_string): New function.
+ (gnupg_errors): New static global.
+ (_gpgme_map_gnupg_error): New function.
+ * gpgme.c (gpgme_release): Don't release CTX->notation.
+ (gpgme_get_notation): Remove function.
+ * decrypt-verify.c (_gpgme_op_decrypt_verify_start): Call
+ _gpgme_op_verify_init_result.
+ * verify.c: Do not include <stdio.h>, <assert.h> and "key.h", but
+ do include "gpgme.h".
+ (struct verify_result): Replace with ...
+ (op_data_t): ... this type.
+ (release_verify_result): Remove function.
+ (release_op_data): New function.
+ (is_token): Remove function.
+ (skip_token): Remove function.
+ (copy_token): Remove function.
+ (gpgme_op_verify_result): New function.
+ (calc_sig_summary): Rewritten.
+ (finish_sig): Remove function.
+ (parse_new_sig): New function.
+ (parse_valid_sig): New function.
+ (parse_notation): New function.
+ (parse_trust): New function.
+ (parse_error): New function.
+ (_gpgme_verify_status_handler): Rewritten. Change first argument
+ to void *.
+ (_gpgme_op_verify_start): Rework error handling. Call
+ _gpgme_op_verify_init_result.
+ (gpgme_op_verify): Do not release or clear CTX->notation.
+ (gpgme_get_sig_status): Rewritten.
+ (gpgme_get_sig_string_attr): Likewise.
+ (gpgme_get_sig_ulong_attr): Likewise.
+ (gpgme_get_sig_key): Likewise.
+
+ * gpgme.h (struct _gpgme_op_decrypt_result): New structure.
+ (GpgmeDecryptResult): New type.
+ (gpgme_op_decrypt_result): New prototype.
+ * ops.h (_gpgme_op_decrypt_init_result): New prototype.
+ (_gpgme_decrypt_status_handler): Fix prototype.
+ (_gpgme_decrypt_start): Remove prototype.
+ * decrypt-verify.c: Do not include <stdio.h>, <stdlib.h>,
+ <string.h> and <assert.h>, "util.h" and "context.h", but
+ "gpgme.h".
+ (decrypt_verify_status_handler): Change first argument to void *,
+ and rework error handling.
+ (_gpgme_op_decrypt_verify_start): New function.
+ (gpgme_op_decrypt_verify_start): Rewrite using
+ _gpgme_op_decrypt_verify_start.
+ (gpgme_op_decrypt_verify): Likewise.
+ * decrypt.c: Include <string.h>, "gpgme.h" and "util.h".
+ (struct decrypt_result): Change to typedef op_data_t, rewritten.
+ (is_token): Remove function.
+ (release_op_data): New function.
+ (skip_token): Remove function.
+ (gpgme_op_decrypt_result): New function.
+ (_gpgme_decrypt_status_handler): Change first argument to void *.
+ Rework error handling.
+ (_gpgme_decrypt_start): Rename to ...
+ (decrypt_start): ... this. Call _gpgme_op_decrypt_init_result.
+ (_gpgme_op_decrypt_init_result): New function.
+ (gpgme_op_decrypt_start): Use decrypt_start.
+ (gpgme_op_decrypt): Likewise.
+
+2003-04-27 Marcus Brinkmann <marcus@g10code.de>
+
+ * encrypt-sign.c: Do not include <stddef.h>, <stdio.h>,
+ <stdlib.h>, <string.h>, <assert.h> and "util.h", but "gpgme.h".
+ (_gpgme_op_encrypt_sign_start): Rename to ...
+ (encrypt_sign_start): ... this.
+ (gpgme_op_encrypt_sign_start): Use encrypt_sign_start, not
+ _gpgme_op_encrypt_sign_start.
+ (gpgme_op_encrypt_sign): Likewise.
+
+ * gpgme.h (GpgmeEncryptResult): New data type.
+ (gpgme_op_encrypt_result): New prototype.
+ * ops.h (_gpgme_op_encrypt_init_result): New prototype.
+ (_gpgme_op_encrypt_status_handler): Fix prototype.
+ * encrypt-sign.c (_gpgme_op_encrypt_sign_start): Call
+ _gpgme_op_encrypt_init_result.
+ * encrypt.c: Do not include <stdio.h>, <assert.h>, "util.h" and
+ "wait.h". Include <errno.h> and "gpgme.h".
+ (SKIP_TOKEN_OR_RETURN): Remove macro.
+ (struct encrypt_result): Rename to ...
+ (op_data_t): ... new data type. Rewrite for user result data.
+ (append_xml_encinfo): Remove function.
+ (release_op_data): New function.
+ (gpgme_op_encrypt_result): New function.
+ (_gpgme_op_encrypt_status_handler): Change first argument to void *.
+ Rewrite result parsing.
+ (_gpgme_op_encrypt_sym_status_handler): Change first argument to
+ void *.
+ (_gpgme_op_encrypt_init_result): New function.
+ (_gpgme_op_encrypt_start): Rename to ...
+ (encrypt_start): ... this.
+ (gpgme_op_encrypt_start): Use encrypt_start, not
+ gpgme_op_encrypt_start.
+ (gpgme_op_encrypt): Likewise.
+
+ * gpgme.h (GpgmePubKeyAlgo, GpgmeHashAlgo, GpgmeInvalidUserID,
+ GpgmeNewSignature, GpgmeSignResult): New data types.
+ (gpgme_op_sign_result, gpgme_pubkey_algo_name,
+ gpgme_hash_algo_name): New prototypes.
+ * gpgme.c (gpgme_pubkey_algo_name): New function.
+ (gpgme_hash_algo_name): Likewise.
+ * ops.h (_gpgme_parse_inv_userid, _gpgme_op_sign_init_result): New
+ prototype.
+ (_gpgme_op_sign_status_handler): Fix prototype.
+ * op-support.c: Include <errno.h> and <string.h>.
+ (_gpgme_parse_inv_userid): New function.
+ * sign.c: Include <errno.h> and "gpgme.h", but not <stdio.h>,
+ <assert.h> and "util.h".
+ (SKIP_TOKEN_OR_RETURN): Remove macro.
+ (struct sign_result): Change to op_data_t type and rework it.
+ (release_sign_result): Rename to ...
+ (release_op_data): ... this and rewrite it.
+ (append_xml_info): Remove function.
+ (gpgme_op_sign_result): New function.
+ (parse_sig_created): New function.
+ (_gpgme_sign_status_handler): Change first argument to void *.
+ Rewrite the function to use the new result structure and functions.
+ (_gpgme_op_sign_init_result): New function.
+ (_gpgme_op_sign_start): Rename to ...
+ (sign_start): ... this. Call _gpgme_op_sign_init_result.
+ (gpgme_op_sign_start): Use sign_start instead _gpgme_op_sign_start.
+ (gpgme_op_sign): Likewise.
+ * encrypt-sign.c (_gpgme_op_encrypt_sign_start): Call
+ _gpgme_op_sign_init_result.
+
+ * delete.c: Include <errno.h> and "gpgme.h", but not "util.h" or
+ "key.h".
+ (enum delete_problem): Move into function delete_status_handler.
+ (delete_status_handler): Change first argument to void *. Parse
+ delete problem with strtol instead atoi. Return better error
+ values.
+ (_gpgme_op_delete_start): Rename to ...
+ (delete_start): ... this. Rework error handling.
+ (gpgme_op_delete_start): Use delete_start instead
+ _gpgme_op_delete_start.
+ (gpgme_op_delete): Likewise.
+ * gpgme.h (GpgmeDataType): Removed.
+
+2003-04-25 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.h: Change GPGME_IMPORT_PRIVATE to GPGME_IMPORT_SECRET.
+ * import.c (parse_import_res): Parse unchanged field.
+
+ * gpgme.h: New enum for GPGME_IMPORT_NEW, GPGME_IMPORT_UID,
+ GPGME_IMPORT_SIG, GPGME_IMPORT_SUBKEY, GPGME_IMPORT_PRIVATE.
+ (GpgmeError): GPGME_Unknown_Reason, GPGME_Not_Found,
+ GPGME_Ambiguous_Specification, GPGME_Wrong_Key_Usage,
+ GPGME_Key_Revoked, GPGME_Key_Expired, GPGME_No_CRL_Known,
+ GPGME_CRL_Too_Old, GPGME_Policy_Mismatch, GPGME_No_Secret_Key,
+ GPGME_Key_Not_Trusted, GPGME_Issuer_Missing, GPGME_Chain_Too_Long,
+ GPGME_Unsupported_Algorithm, GPGME_Sig_Expired,
+ GPGME_Bad_Signature, GPGME_No_Public_Key added as new error codes.
+ (struct _gpgme_import_status): New structure.
+ (GpgmeImportStatus): New type.
+ (struct _gpgme_op_import_result): New structure.
+ (GpgmeImportResult): New type.
+ (gpgme_op_import_result): New function.
+ * import.c: Include <errno.h> and "gpgme.h", but not "util.h".
+ (struct import_result): Change to type op_data_t.
+ (release_import_result): Rename to ...
+ (release_op_data): ... this.
+ (append_xml_impinfo): Function removed.
+ (gpgme_op_import_result): New function.
+ (parse_import): New function.
+ (parse_import_res): Likewise.
+ (import_status_handler): Change first argument to void *. Rewrite
+ to use new functions.
+ (_gpgme_op_import_start): Rework error handling.
+
+ * edit.c: Do not include <assert.h>, "util.h", but "gpgme.h".
+ (edit_resut): Change to typedef for op_data_t.
+ (edit_status_handler): Change first argument to void *.
+ Rework error handling.
+ (command_handler): Rework error handling.
+ (_gpgme_op_edit_start): Rename to ...
+ (edit_start): ... this. Rework error handling.
+ (gpgme_op_edit_start): Rewrite using edit_start.
+ (gpgme_op_edit): Likewise.
+
+ * ops.h (_gpgme_passphrase_start): Remove prototype.
+ * passphrase.c: Do not include <assert.h>, "util.h" or
+ "debug.h", but "gpgme.h".
+ (struct passphrase_result): Change to typedef for op_data_t.
+ (release_passphrase_result): Rename to release_op_data.
+ (_gpgme_passphrase_status_handler): Change first argument to void *.
+ Use new op_data_t type.
+ (_gpgme_passphrase_command_handler): Use new op_data_t type.
+ (_gpgme_passphrase_start): Remove function.
+ * decrypt.c (_gpgme_decrypt_start): Rewrite error handling. Do
+ not call _gpgme_passphrase_start, but install command handler.
+ * encrypt.c (_gpgme_op_encrypt_start): Likewise.
+ * encrypt-sign.c (_gpgme_op_encrypt_sign_start): Likewise.
+ * sign.c (_gpgme_op_sign_start): Likewise.
+
+ * context.h (struct gpgme_context_s): Remove member initialized,
+ use_cms and help_data_1. Add member protocol. Make use_armor and
+ use_textmode bit flags. Make keylist_mode, include_certs,
+ signers_len and signers_size unsigned.
+ * gpgme.c (gpgme_new): Initialize CTX->protocol.
+ (gpgme_set_protocol): Do not check CTX. Use CTX->protocol.
+ (gpgme_get_protocol): Likewise.
+ (gpgme_release): Do not release CTX->help_data_1.
+ * op-support.c (_gpgme_op_reset): Use CTX->protocol.
+
+ * wait-private.c (_gpgme_wait_private_event_cb): Remove variable CTX.
+
+ * data.c: Do not include <assert.h>, but "gpgme.h".
+ (_gpgme_data_inbound_handler): Expand _gpgme_data_append, because
+ it will go. Do not assert DH.
+ (_gpgme_data_outbound_handler): Do not assert DH.
+
+ * export.c: Do not include <stdlib.h>, "debug.h" and "util.h", but
+ "gpgme.h".
+ (export_status_handler): Change type of first argument to void *.
+ (_gpgme_op_export_start): Rename to ...
+ (export_start): ... this. Rework error handling.
+ (gpgme_op_export_start): Rewritten to use export_start instead
+ _gpgme_op_export_start.
+ (gpgme_op_export): Likewise.
+
+ * gpgme.h (GpgmeError): Add GPGME_Busy, GPGME_No_Request.
+ (GPGME_No_Recipients, GPGME_Invalid_Recipient,
+ GPGME_No_Passphrase): New macros.
+
+ * key.c (gpgme_key_get_string_attr): Fix validity attribute.
+
+2003-04-24 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.h (struct _gpgme_op_genkey_result): New structure.
+ (GpgmeGenKeyResult): New type.
+ (gpgme_op_genkey): Drop last argument.
+ (gpgme_op_genkey_result): New function.
+ * genkey.c: Do not include "util.h", but "gpgme.h".
+ (struct genkey_result): Replace with ...
+ (op_data_t): ... this new type.
+ (release_genkey_result): Replace with ...
+ (release_op_data): ... this new function.
+ (gpgme_op_genkey_result): New function.
+ (genkey_status_handler): Rewritten using new op_data_t type.
+ (get_key_parameter): New function.
+ (_gpgme_op_genkey_start): Renamed to
+ (genkey_start): ... this and rewritten.
+ (gpgme_op_genkey_start): Use genkey_start instead
+ _gpgme_op_genkey_start.
+ (gpgme_op_genkey): Rewritten. Remove FPR argument.
+
+ * context.h (struct gpgme_context_s): Remove member verbosity.
+ * gpgme.c (gpgme_new): Do not set member verbosity.
+ * engine.h (_gpgme_engine_set_verbosity): Remove prototype.
+ * engine.c (_gpgme_engine_set_verbosity): Remove function.
+ * engine-backend.h (struct engine_ops): Remove set_verbosity.
+ * engine-gpgsm.c (_gpgme_engine_ops_gpgsm): Remove set_verbosity member.
+ * rungpg.c (_gpgme_engine_ops_gpg): Likewise.
+ (gpg_set_verbosity): Remove function.
+ * decrypt.c (_gpgme_decrypt_start): Don't call
+ _gpgme_engine_set_verbosity.
+ * delete.c (_gpgme_op_delete_start): Likewise.
+ * edit.c (_gpgme_op_edit_start): Likewise.
+ * encrypt.c (_gpgme_op_encrypt_start): Likewise.
+ * encrypt-sign.c (_gpgme_op_encrypt_sign_start): Likewise.
+ * export.c (_gpgme_op_export_start): Likewise.
+ * genkey.c (_gpgme_op_genkey_start): Likewise.
+ * import.c (_gpgme_op_import_start): Likewise.
+ * keylist.c (gpgme_op_keylist_start): Likewise.
+ (gpgme_op_keylist_ext_start): Likewise.
+ * sign.c (_gpgme_op_sign_start): Likewise.
+ * verify.c (_gpgme_op_verify_start): Likewise.
+
+ * Makefile.am (libgpgme_la_SOURCES): Add key-cache.c.
+ * key.c (key_cache_initialized, key_cache_size,
+ key_cache_max_chain_length, ): Removed.
+ (struct key_cache_item_s, key_cache_lock, key_cache,
+ key_cache_unused_items, hash_key, _gpgme_key_cache_add,
+ _gpgme_key_cache_get, gpgme_get_key): Moved to ...
+ * key-cache.c: ... here. New file.
+ * key.h (_gpgme_key_cache_init): Remove prototypes.
+ (_gpgme_key_cache_add,_gpgme_key_cache_get): Move to ...
+ * ops.h: ... here.
+ * version.c: Do not include "key.h".
+ (do_subsystem_inits): Do not call _gpgme_key_cache_init.
+
+ * mkstatus: Strip trailing comma.
+ * gpgme.h (GpgmeStatus): Pretty print.
+
+ * gpgme.h (GpgmeError): Rename GPGME_No_Passphrase to
+ GPGME_Bad_Passphrase.
+ * passphrase.c (_gpgme_passphrase_status_handler): Use
+ GPGME_Bad_Passphrase instead GPGME_No_Passphrase.
+
+ * gpgme.h (GpgmeError): Rename GPGME_No_Recipients to
+ GPGME_No_UserID and GPGME_Invalid_Recipient to
+ GPGME_Invalid_UserID.
+ * encrypt.c (_gpgme_encrypt_status_handler): Use GPGME_No_UserID
+ instead GPGME_No_Recipients and GPGME_Invalid_UserID instead
+ GPGME_Invalid_Recipient.
+ (_gpgme_op_encrypt_start): Likewise.
+
+ * gpgme.h (GpgmeError): Remove GPGME_Busy and GPGME_No_Request.
+ * wait-user.c (_gpgme_wait_user_event_cb): Don't clear CTX->pending.
+ * wait-private.c (_gpgme_wait_private_event_cb): Likewise.
+ * wait-global.c (gpgme_wait): Likewise.
+ * verify.c (_gpgme_op_verify_start): Likewise.
+ (gpgme_get_sig_status): Don't check pending flag.
+ (gpgme_get_sig_string_attr): Likewise.
+ (gpgme_get_sig_ulong_attr): Likewise.
+ (gpgme_get_sig_key): Likewise.
+ * op-support.c (_gpgme_op_reset): Likewise.
+ * trustlist.c (gpgme_op_trustlist_start): Don't clear pending flag.
+ (gpgme_op_trustlist_next): Don't check or clear pending flag.
+ (gpgme_op_trustlist_end): Likewise.
+ * sign.c (_gpgme_op_sign_start): Likewise.
+ * context.h (struct gpgme_context_s): Remove member PENDING.
+ * decrypt.c (_gpgme_decrypt_start): Likewise.
+ * delete.c (_gpgme_op_delete_start): Likewise.
+ * edit.c (_gpgme_op_edit_start): Likewise.
+ * encrypt.c (_gpgme_op_encrypt_start): Likewise.
+ * encrypt-sign.c (_gpgme_op_encrypt_sign_start): Likewise.
+ * export.c (_gpgme_op_export_start): Likewise.
+ * genkey.c (_gpgme_op_genkey_start): Likewise.
+ * import.c (_gpgme_op_import_start): Likewise.
+ * key.c (gpgme_get_key): Likewise.
+ * keylist.c (gpgme_op_keylist_start): Likewise.
+ (gpgme_op_keylist_ext_start): Likewise.
+ (gpgme_op_keylist_next): Likewise.
+ (gpgme_op_keylist_end): Likewise.
+ * data-compat.c (gpgme_error_to_errno): Don't convert EBUSY.
+
+2003-02-06 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.h (GpgmePassphraseCb): Change type to return GpgmeError,
+ and add argument for returning the result string.
+ (gpgme_cancel): Remove prototype.
+ * gpgme.c (gpgme_cancel): Remove function.
+ * context.h (struct gpgme_context_s): Remove member cancel.
+ * passphrase.c (_gpgme_passphrase_command_handler): Call the
+ passphrase callback in the new way.
+
+2003-01-30 Marcus Brinkmann <marcus@g10code.de>
+
+ * edit.c (_gpgme_edit_status_handler): Call the progress status
+ handler.
+
+2003-02-05 Marcus Brinkmann <marcus@g10code.de>
+
+ * wait-user.c (_gpgme_wait_user_remove_io_cb): Move check for no
+ I/O handlers left to ...
+ (_gpgme_user_io_cb_handler): ... here.
+
+2003-02-04 Marcus Brinkmann <marcus@g10code.de>
+
+ * trustlist.c (trustlist_colon_handler): Release ITEM if name
+ could not be allocated.
+ (gpgme_trust_item_release): Only release name if it is allocated.
+ Reported by Marc Mutz <Marc.Mutz@uni-bielefeld.de>.
+
+2003-02-04 Marcus Brinkmann <marcus@g10code.de>
+
+ * rungpg.c (read_status): If he status handler returns an error,
+ return it.
+ (status_handler): If read_status fails, just return the error.
+
+2003-02-01 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine-gpgsm.c (start): Handle all errors, not only most of
+ them.
+ (xtoi_1, xtoi_2): Remove macro.
+ (status_handler): Replace use of xtoi_2 with _gpgme_hextobyte.
+
+2003-02-01 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine-gpgsm.c (map_assuan_error): Replace
+ ASSUAN_Bad_Certificate_Path with ASSUAN_Bad_Certificate_Chain.
+ (gpgsm_new): Use assuan_pipe_connect instead assuan_pipe_connect2.
+
+ * util.h (DIMof): Remove macro.
+
+ * ops.h (_gpgme_op_event_cb, _gpgme_op_event_cb_user,
+ _gpgme_data_unread): Prototypes removed.
+
+2003-01-30 Marcus Brinkmann <marcus@g10code.de>
+
+ * types.h: File removed.
+ * Makefile.am (libgpgme_la_SOURCES): Remove types.h.
+ * io.h (struct spawn_fd_item_s): Do not include "types.h".
+ * key.h: Likewise.
+ * context.h: Likewise.
+ * cengine-gpgsm.h: Likewise.
+ * engine.h: Include "gpgme.h" instead "types.h". Add prototypes
+ for EngineStatusHandler, EngineColonLineHandler and
+ EngineCommandHandler.
+ (_gpgme_engine_set_status_handler): Change parameter type from
+ GpgmeStatusHandler to EngineStatusHandler.
+ (_gpgme_engine_set_command_handler): Change parameter type from
+ GpgmeCommandHandler to EngineCommandHandler.
+ (_gpgme_engine_set_colon_line_handler): Change parameter type from
+ GpgmeColonLineHandler to EngineColonLineHandler.
+ * engine-backend.h: Include "engine.h" instead "types.h".
+ (struct engine_ops): Change Gpgme*Handler parameters in members
+ set_command_handler, set_colon_line_handler and set_status_handler
+ to Engine*Handler.
+ * engine.c (_gpgme_engine_set_status_handler): Change parameter
+ type from GpgmeStatusHandler to EngineStatusHandler.
+ (_gpgme_engine_set_command_handler): Change parameter type from
+ GpgmeCommandHandler to EngineCommandHandler.
+ (_gpgme_engine_set_colon_line_handler): Change parameter type from
+ GpgmeColonLineHandler to EngineColonLineHandler.
+ * rungpg.c (struct gpg_object_s): Change type of member status.fnc
+ from GpgmeStatusHandler to EngineStatusHandler. Change type of
+ member colon.fnc from GpgmeColonLineHandler to
+ EngineColonLineHandler. Change type of member cmd.fnc from
+ GpgmeCommandHandler to EngineCommandHandler.
+ * engine-gpgsm.c (struct gpgsm_object_s): Likewise.
+ * rungpg.c (gpg_set_status_handler): Change parameter type from
+ GpgmeStatusHandler to EngineStatusHandler.
+ * engine-gpgsm.c (gpgsm_set_status_handler): Likewise.
+ (assuan_simple_command): Likewise.
+ * rungpg.c (gpg_set_colon_line_handler): Change parameter type
+ from GpgmeColonLineHandler to EngineColonLineHandler.
+ * engine-gpgsm.c (gpgsm_set_colon_line_handler): Likewise.
+ * rungpg.c (gpg_set_command_handler): Change parameter type from
+ GpgmeCommandHandler to EngineCommandHandler.
+
+ * engine-gpgsm.c (status_handler): Do not close status fd at end
+ of function.
+
+ * ops.h (_gpgme_op_data_lookup): Add prototype.
+ * op-support.c: Include <stdlib.h>.
+ (_gpgme_op_data_lookup): New function.
+ * decrypt.c (_gpgme_release_decrypt_result): Function removed.
+ (struct decrypt_result_s): Rename to ...
+ (struct decrypt_resul): ... this.
+ (DecryptResult): New type.
+ (_gpgme_decrypt_status_handler): Don't use
+ test_and_allocate_result, but use _gpgme_op_data_lookup to
+ retrieve result data object.
+ * sign.c (_gpgme_release_sign_result): Function removed.
+ (release_sign_result): New function.
+ (struct sign_result_s): Rename to ...
+ (struct sign_result): ... this.
+ (SignResult): New type.
+ (_gpgme_sign_status_handler): Don't use
+ test_and_allocate_result, but use _gpgme_op_data_lookup to
+ retrieve result data object.
+ * encrypt.c (struct encrypt_result_s): Rename to ...
+ (struct encrypt_result): ... this.
+ (_gpgme_release_encrypt_result): Function removed.
+ (release_encrypt_result): New function.
+ (_gpgme_encrypt_status_handler): Don't use
+ test_and_allocate_result, but use _gpgme_op_data_lookup to
+ retrieve result data object.
+ * verify.c (struct verify_result_s): Rename to ...
+ (struct verify_result): ... this. Remove member next.
+ (VerifyResult): New type.
+ (_gpgme_release_verify_result): Function removed.
+ (release_verify_result): New function.
+ (finish_sig): Change first argument to type VerifyResult. Diddle
+ the type of the op_data structure.
+ (add_notation): Change first argument to type VerifyResult.
+ (_gpgme_verify_status_handler): Don't use
+ test_and_allocate_result, but use _gpgme_op_data_lookup to
+ retrieve result data object.
+ * passphrase.c (struct passphrase_result_s): Rename to ...
+ (struct passphrase_result): ... this. Remove member next.
+ (PassphraseResult): New type.
+ (_gpgme_release_passphrase_result): Function removed.
+ (release_passphrase_result): New function.
+ (_gpgme_passphrase_status_handler): Don't use
+ test_and_allocate_result, but use _gpgme_op_data_lookup to
+ retrieve result data object.
+ (_gpgme_passphrase_command_handler): Likewise.
+ * keylist.c (struct keylist_result_s): Rename to ...
+ (struct keylist_result): ... this. Remove member next.
+ (KeylistResult): New type.
+ (_gpgme_release_keylist_result): Function removed.
+ (release_keylist_result): New function.
+ (keylist_status_handler): Don't use
+ test_and_allocate_result, but use _gpgme_op_data_lookup to
+ retrieve result data object.
+ * edit.c (struct edit_result_s): Rename to ...
+ (struct edit_result): ... this. Remove member next.
+ (EditResult): New type.
+ (_gpgme_release_edit_result): Function removed.
+ (release_edit_result): New function.
+ (edit_status_handler): Don't use
+ test_and_allocate_result, but use _gpgme_op_data_lookup to
+ retrieve result data object.
+ (command_handler): Likewise.
+ * types.h (DecryptResult, SignResult, EncryptResult,
+ PassphraseResult, ImportResult, DeleteResult, GenKeyResult,
+ KeylistResult, EditResult): Types removed.
+ * ops.h: Don't include "types.h", but "gpgme.h" and "context.h".
+ (test_and_allocate_result): Remove macro.
+ (_gpgme_release_decrypt_result): Remove prototype.
+ (_gpgme_decrypt_result): Remove prototype.
+ (_gpgme_release_sign_result): Remove prototype.
+ (_gpgme_release_encrypt_result): Remove prototype.
+ (_gpgme_release_passphrase_result): Remove prototype.
+ (_gpgme_release_import_result): Remove prototype.
+ (_gpgme_release_delete_result): Remove prototype.
+ (_gpgme_release_genkey_result): Remove prototype.
+ (_gpgme_release_keylist_result): Remove prototype.
+ (_gpgme_release_edit_result): Remove prototype.
+ (_gpgme_release_verify_result): Remove prototype.
+ * gpgme.c (_gpgme_release_result): Rewritten.
+ * context.h (enum ctx_op_data_type): New enum.
+ (struct ctx_op_data): New structure.
+ (struct gpgme_context_s): Replace the member result with a member
+ op_data.
+ (fail_on_pending_request): Remove macro.
+ * op-support.c (_gpgme_op_reset): Expand macro
+ fail_on_pending_request.
+ * util.h: Don't include "types.h" or "debug.h", but include "gpgme.h".
+
+2003-01-19 Marcus Brinkmann <marcus@g10code.de>
+
+ * rungpg.c (_gpgme_engine_ops_gpg): Remove gpg_start.
+ (gpg_start): Rename to ...
+ (start): ... this function. Change arguments to GpgObject.
+ (gpg_decrypt): Call start.
+ (gpg_edit): Likewise.
+ (gpg_encrypt): Likewise.
+ (gpg_encrypt_sign): Likewise.
+ (gpg_export): Likewise.
+ (gpg_import): Likewise.
+ (gpg_keylist): Likewise.
+ (gpg_keylist_ext): Likewise.
+ (gpg_trustlist): Likewise.
+ (gpg_verify): Likewise.
+
+ * engine-gpgsm.c (_gpgme_engine_ops_encrypt): Remove gpgsm_start.
+ (gpgsm_start): Rename to ...
+ (struct gpgsm_object_s): Remove member command.
+ (gpgsm_release): Don't free command.
+ (start): ... this function. Change arguments to GpgsmObject and
+ const char *.
+ (gpgsm_decrypt): Call start.
+ (gpgsm_delete): Likewise.
+ (gpgsm_encrypt): Likewise.
+ (gpgsm_export): Likewise.
+ (gpgsm_genkey): Likewise.
+ (gpgsm_import): Likewise.
+ (gpgsm_keylist): Likewise.
+ (gpgsm_keylist_ext): Likewise.
+ (gpgsm_verify): Likewise.
+
+ * decrypt.c (_gpgme_decrypt_start): Don't call
+ _gpgme_engine_start.
+ * delete.c (_gpgme_op_delete_start): Likewise.
+ * edit.c (_gpgme_op_edit_start): Likewise.
+ * encrypt.c (_gpgme_op_encrypt_start):
+ * encrypt-sign.c (_gpgme_op_encrypt_sign_start):
+ * export.c (_gpgme_op_export_start): Likewise.
+ * genkey.c (_gpgme_op_genkey_start): Likewise.
+ * import.c (_gpgme_op_import_start): Likewise.
+ * keylist.c (gpgme_op_keylist_ext_start): Likewise.
+ (gpgme_op_keylist_start): Likewise.
+ * sign.c (_gpgme_op_sign_start): Likewise.
+ * trustlist.c (gpgme_op_trustlist_start): Likewise.
+ * verify.c (_gpgme_op_verify_start): Likewise.
+
+ * engine-backend.h (struct engine_ops): Remove member start.
+
+ * engine.h (_gpgme_engine_start): Remove prototype.
+ * engine.c (_gpgme_engine_start): Remove function.
+
+2003-01-19 Miguel Coca <mcoca@gnu.org>
+
+ * w32-io.c (_gpgme_io_select): Add missing argument in calls to
+ DEBUG_BEGIN.
+ * w32-util.c: Include "sema.h".
+ (find_program_in_registry): Change DEBUG1 to DEBUG2, fixes compilation
+ error.
+
+2003-01-29 Marcus Brinkmann <marcus@g10code.de>
+
+ * types.h: Remove byte and ulong types.
+ * util.h (_gpgme_hextobyte): Change prototype to unsigned char
+ instead byte.
+ * conversion.c (_gpgme_hextobyte): Change argument to unsigned
+ char instead byte.
+ (_gpgme_decode_c_string): Likewise, and beautify. Also support a
+ few more escaped characters. Be more strict about buffer size.
+ (_gpgme_data_append_percentstring_for_xml): Change type of SRC,
+ BUF and DST to unsigned char instead byte.
+ * progress.c (_gpgme_progress_status_handler): Use unsigned char
+ instead byte.
+ * debug.c (trim_spaces): Likewise.
+
+ * util.h (mk_error): Remove macro.
+ * conversion.c, data.c, data-compat.c, decrypt.c, delete.c,
+ edit.c, encrypt.c, encrypt-sign.c, engine.c, engine-gpgsm.c,
+ export.c, genkey.c, gpgme.c, import.c, key.c, keylist.c,
+ passphrase.c, progress.c, recipient.c, rungpg.c, sign.c,
+ signers.c, trustlist.c, verify.c, wait.c, wait-global.c,
+ wait-private (literally everywhere): Expand the mk_error macro.
+
+ * context.h (wait_on_request_or_fail): Remove macro.
+
+ * context.h (gpgme_context_s): Remove member ERROR.
+ * types.h (GpgmeStatusHandler): Change return type to GpgmeError.
+ (GpgmeCommandHandler): Change return type to GpgmeError and add
+ new argument RESULT.
+ * gpgme.h (GpgmeIOCb): Change return type to GpgmeError.
+ (GpgmeEventIO): New event GPGME_EVENT_START.
+ (GpgmeIdleFunc): Remove type.
+ (gpgme_register_idle): Remove prototype.
+ * data.c: Include <assert.h>.
+ (_gpgme_data_inbound_handler): Change return type to GpgmeError.
+ Return any error instead ignoring it, don't close file descriptor
+ on error.
+ (_gpgme_data_outbound_handler): Likewise.
+ * decrypt.c: Do not include <stdio.h>, <string.h> and <assert.h>.
+ (_gpgme_decrypt_status_handler): Change return type to GpgmeError.
+ Return error instead setting ctx->error. Return success at end of
+ function.
+ (gpgme_op_decrypt): Don't work around the old kludge anymore.
+ * decrypt-verify.c (decrypt_verify_status_handler): Change return
+ type to GpgmeError. Return possible errors.
+ * delete.c: Do not include <stdio.h>, <string.h>, <time.h> and
+ <assert.h>.
+ (delete_status_handler): Change return type to GpgmeError. Return
+ error instead setting ctx->error. Return success at end of
+ function.
+ * edit.c: Do not include <stdio.h> and <string.h>.
+ (_gpgme_edit_status_handler): Change type to GpgmeError,
+ make static and rename to ...
+ (edit_status_handler): ... this. Return error directly.
+ (command_handler): Change return type to GpgmeError, add result
+ argument. Return error directly.
+ * encrypt.c (status_handler_finish): Remove function.
+ (_gpgme_encrypt_status_handler): Change return type to GpgmeError.
+ Return error directly.
+ (_gpgme_encrypt_sym_status_handler): Likewise.
+ * encrypt-sign.c (encrypt_sign_status_handler): Likewise.
+ * engine-gpgsm.c (close_notify_handler): Do not signal done event
+ anymore.
+ (status_handler): Change return type to GpgmeError. Diddle things
+ around a bit to return errors directly.
+ (start): Send start event.
+ * export.c: Do not include <stdio.h>, <string.h> and <assert.h>.
+ (export_status_handler): Change return type to GpgmeError. Don't
+ check ctx->error.
+ * genkey.c: Do not include <stdio.h> and <assert.h>.
+ (genkey_status_handler): Change return type to GpgmeError. Don't
+ check ctx->error. Return errors directly.
+ * gpgme.c (_gpgme_release_result): Do not initialize ctx->error.
+ (_gpgme_op_event_cb): Function removed.
+ (_gpgme_op_event_cb_user): Likewise.
+ * import.c: Do not include <stdio.h>, <string.h> and <assert.h>.
+ (import_status_handler): Change return type to GpgmeError. Don't
+ check ctx->error.
+ * keylist.c (keylist_colon_handler, keylist_status_handler, finish_key):
+ Change return type to GpgmeError, return error directly.
+ * Makefile (libgpgme_la_SOURCES): Add wait-global.c,
+ wait-private.c and wait-user.c
+ * ops.h (test_and_allocate_result): Return error instead setting
+ ctx->error.
+ (_gpgme_data_inbound_handler, _gpgme_data_outbound_handler,
+ _gpgme_verify_status_handler, _gpgme_decrypt_status_handler,
+ _gpgme_sign_status_handler, _gpgme_encrypt_staus_handler,
+ _gpgme_passphrase_status_handler, _gpgme_progress_status_handler):
+ Change return type to GpgmeError.
+ (_gpgme_passphease_command_handler): Change return type to
+ GpgmeError and add new argument RESULT.
+ * op-support.c: Use new callback functions, and change private
+ data to ctx everywhere.
+ * passphrase.c (_gpgme_passphrase_status_handler): Change return
+ type to GpgmeError, return error directly.
+ (_gpgme_passphrase_command_handler): Change return type to
+ GpgmeError, add result argument. Return results accordingly.
+ * progress.c (_gpgme_progress_status_handler): Change return type
+ to GpgmeError, return errors directly.
+ * rungpg.c (status_handler): Change return type to GpgmeError.
+ Return error directly.
+ (close_notify_handler): Don't send done event.
+ (colon_line_handler): Change return type to GpgmeError, return
+ errors directly.
+ * rungpg.c (start): Send start event.
+ * sign.c (_gpgme_sign_status_handler): Change return type to
+ GpgmeError, return errors directly.
+ * trustlist.c (trustlist_status_handler): Change return type to
+ GpgmeError. Return 0.
+ (trustlist_colon_handler): Change return type GpgmeError. Return
+ errors directly.
+ * verify.c (add_notation): Change return type to GpgmeError,
+ return errors directly.
+ (_gpgme_verify_status_handler): Likewise.
+ * wait.h (struct fd_table): Remove lock member.
+ (struct wait_item_s): Moved here from wait.c.
+ (struct tag): New structure.
+ (_gpgme_wait_event_cb): Remove prototype.
+ (_gpgme_wait_private_event_cb, _gpgme_wait_global_event_cb,
+ _gpgme_wait_user_add_io_cb, _gpgme_wait_user_remove_io_cb,
+ _gpgme_wait_user_event_io_cb): New prototypes.
+ * wait.c: Don't include <stdio.h>.
+ (ftd_global, ctx_done_list, ctx_done_list_size,
+ ctx_done_list_length, ctx_done_list_lock, idle_function): Remove
+ global variable.
+ (gpgme_register_idle, do_select, _gpgme_wait_event_cb): Remove
+ function.
+ (gpgme_wait): Move to file wait-global.c.
+ (_gpgme_add_io_cb): Take ctx as private argument, initialize ctx
+ member in wait item and tag.
+ (_gpgme_remove_io_cb): Take ctx from tag. Don't use FDT lock.
+ (_gpgme_wait_one, _gpgme_wait_on_condition): Move to
+ wait-private.c.
+ (gpgme_fd_table_init): Don't initialize FDT->lock.
+ (gpgme_fd_table_deinit): Don't destroy FDT->lock.
+ (_gpgme_fd_table_put): Make static and rename to ...
+ (fd_table_put): ... this function. Don't use FDT->lock.
+ (struct wait_item_s): Move to wait.h.
+ * wait-global.c: New file.
+ * wait-private.c: New file.
+ * wait-user.c: New file.
+
+ * key.c (gpgme_key_sig_get_string_attr): Use validity_to_string
+ instead otrust_to_string to calculate validity.
+
+2003-01-30 Marcus Brinkmann <marcus@g10code.de>
+
+ * types.h (EngineObject): Move typedef to ...
+ * engine.h: ... here.
+ * types.h (GpgObject): Move typedef to ...
+ * rungpg.c: ... here.
+ * types.h (GpgsmObject): Move typedef to ...
+ * engine-gpgsm.c: ... here.
+
+ * util.h (return_if_fail, return_null_if_fail,
+ return_val_if_fail): Remove macro.
+ * gpgme.c (gpgme_cancel): Don't use return_if_fail.
+ * key.c (gpgme_key_ref): Likewise.
+ * signers.c (gpgme_signers_enum): Likewise.
+ (gpgme_signers_clear): Likewise.
+
+ * engine-backend.h (struct engine_ops): Rename get_path to
+ get_file_name.
+ * gpgme.h (struct _gpgme_engine_info): Rename member path to
+ file_name.
+ * version.c: Do not include <stdio.h>, <stdlib.h>, context.h and
+ util.h. Other clean ups.
+ (parse_version_number): Protect more seriously against
+ overflow.
+ (gpgme_get_engine_info): Move to ...
+ * engine.c (gpgme_get_engine_info): ... here.
+ (_gpgme_engine_get_info): Function removed.
+ (_gpgme_engine_get_path): Make static and rename to ...
+ (engine_get_file_name): .. this.
+ (_gpgme_engine_get_version): Make static and rename to ...
+ (engine_get_version): ... this.
+ (_gpgme_engine_get_req_version): Make static and rename to ...
+ (engine_get_req_version): ... this.
+ * engine.h (_gpgme_engine_get_path, _gpgme_engine_get_version,
+ _gpgme_engine_req_version, _gpgme_engine_get_info.): Remove
+ prototypes.
+
+ * gpgme.h (enum GpgmeProtocol): Remove GPGME_PROTOCOL_AUTO.
+ * gpgme.c (gpgme_set_protocol): Don't handle GPGME_PROTOCOL_AUTO.
+ (gpgme_get_protocol_name): New function.
+
+ * engine-backend.h (struct engine_ops): New member
+ get_req_version, remove member check_version.
+ * engine.h (_gpgme_Engine_get_version): New prototype.
+ * rungpg.c (gpg_get_req_version): New function.
+ (gpg_check_version): Function removed.
+ (_gpgme_engine_ops_gpg): Add gpg_get_req_version, remove
+ gpg_check_version.
+ * engine-gpgsm.c (gpgsm_get_req_version): New function.
+ (gpgsm_check_version): Function removed.
+ (_gpgme_engine_ops_gpgsm): Add gpgsm_get_req_version, remove
+ gpgsm_check_version.
+ * engine.c: Include ops.h.
+ (_gpgme_engine_get_req_version): New function.
+ (gpgme_engine_check_version): Rewritten.
+ * version.c (gpgme_get_engine_info): Rewritten.
+ * gpgme.h (gpgme_engine_info): New structure.
+ (GpgmeEngineInfo): New type.
+
+2003-01-06 Werner Koch <wk@gnupg.org>
+
+ * keylist.c (set_mainkey_capability): Handle 'd' and 'D' used
+ since gpg 1.3 to denote disabled keys.
+
+2003-01-06 Marcus Brinkmann <marcus@g10code.de>
+
+ * data-mem.c: Include <string.h>.
+ * engine.c: Likewise.
+
+2003-01-06 Marcus Brinkmann <marcus@g10code.de>
+
+ * Makefile.am (libgpgme_la_DEPENDENCIES): Correct bug in last change.
+
+2002-12-24 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.h (gpgme_op_verify, gpgme_op_decrypt_verify): Drop R_STAT
+ argument.
+ * decrypt-verify.c (gpgme_op_decrypt_verify): Drop R_STAT
+ argument.
+ * verify.c (gpgme_op_verify): Drop R_STAT argument.
+ (_gpgme_intersect_stati): Function removed.
+ * ops.h (_gpgme_intersect_stati): Remove prototype.
+
+2002-12-24 Marcus Brinkmann <marcus@g10code.de>
+
+ * libgpgme.vers: New file.
+ * Makefile.am (EXTRA_DIST): Add libgpgme.vers.
+ (libgpgme_version_script_cmd): New variable.
+ (libgpgme_la_LDFLAGS): Add libgpgme_version_script_cmd here.
+ (libgpgme_la_DEPENDENCIES): New variable.
+
+2002-12-23 Marcus Brinkmann <marcus@g10code.de>
+
+ * key.c (gpgme_key_get_string_attr): Don't accept GPGME_ATTR_IS_SECRET.
+ (otrust_to_string): New function.
+ (gpgme_key_get_as_xml): Use it.
+ (validity_to_string): New function.
+ (gpgme_key_get_string_attr): Beautify using above functions.
+ (gpgme_key_get_ulong_attr): Likewise.
+
+2002-12-23 Marcus Brinkmann <marcus@g10code.de>
+
+ * data-mem.c (mem_release): Fix gcc warning.
+ * data-user.c (user_release): Likewise.
+
+2002-12-06 Marcus Brinkmann <marcus@g10code.de>
+
+ * data.h (gpgme_data_release_cb): Change return type to void.
+ (gpgme_data_read_cb): Change return type to ssize_t.
+ * data.c (gpgme_data_read): Likewise.
+ * data-stream.c (stream_read): Likewise.
+ * data-fd.c (fd_read): Likewise.
+ * data-mem.c (mem_read): Likewise.
+ (mem_release): Change return type to void.
+ * data-user.c (user_read): Change return type to ssize_t.
+ (user_release): Change return type to void.
+ * data-compat.c (old_user_read): Change return type to ssize_t.
+ * gpgme.h (GpgmeDataReadCb): Likewise.
+ (gpgme_data_read): Likewise.
+ (GpgmeDataSeekCb): Change return type to off_t.
+
+2002-12-04 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.h: Add prototype for gpgme_get_key.
+ * key.c (gpgme_get_key): New function.
+ * verify.c (gpgme_get_sig_key): Rewrite using gpgme_get_key.
+
+ * gpgme.h: Add prototypes for new interfaces
+ gpgme_key_sig_get_string_attr and gpgme_key_get_ulong_attr.
+ (enum GpgmeAttr): New attribute GPGME_ATTR_SIG_CLASS.
+ * gpgme.c (gpgme_set_keylist_mode): Allow GPGME_KEYLIST_MODE_SIGS.
+ * key.h (struct certsig_s): New members ALGO, NAME_PART,
+ EMAIL_PART, COMMENT_PART, NAME, SIG_STAT and SIG_CLASS.
+
+ * conversion.c (_gpgme_decode_c_string): Add new parameter LEN.
+ Use that to determine if allocation is desired or not.
+ * util.h: Adjust prototype of _gpgme_decode_c_string.
+ * keylist.c (keylist_colon_handler): Adjust caller of
+ _gpgme_decode_c_string.
+
+ * key.h (struct gpgme_key_s): New member last_uid.
+ * key.c (_gpgme_key_append_name): Rewritten using
+ _gpgme_decode_c_string and the last_uid pointer.
+ (my_isdigit): Macro removed.
+ (ALLOC_CHUNK): Likewise.
+ * keylist.c (set_userid_flags): Use last_uid member of KEY.
+
+ * context.h (struct user_id_s): New member last_certsig.
+ * key.h: Add prototype for _gpgme_key_add_certsig.
+ * key.c (_gpgme_key_add_certsig): New function.
+ (set_user_id_part): Move function before _gpgme_key_add_certsig.
+ (parse_user_id): Change first argument to SRC, add new arguments
+ NAME, EMAIL and COMMENT. Change code to use these arguments
+ instead going through UID. Move function before
+ _gpgme_add_certsig.
+ (parse_x509_user_id): Likewise.
+ (_gpgme_key_append_name): Adjust arguments to parse_x509_user_id
+ and parse_user_id invocation.
+ (one_certsig_as_xml): New function.
+ (one_uid_as_xml): Print signatures.
+ * context.h (struct gpgme_context_s): New member TMP_UID.
+ * keylist.c (keylist_colon_handler): Rewritten, implement "sig"
+ record entries.
+
+ * key.c (get_certsig): New function.
+ (gpgme_key_sig_get_string_attr): Likewise.
+ (gpgme_key_sig_get_ulong_attr): Likewise.
+
+ * keylist.c: Include <ctype.h>.
+ (my_isdigit): Macro removed.
+ (set_mainkey_trust_info): Use isdigit, not my_isdigit.
+ (set_userid_flags): Likewise.
+ (set_subkey_trust_info): Likewise.
+ (set_ownertrust): Likewise.
+ (finish_key): Move function up a bit and remove prototype.
+
+ * rungpg.c (gpg_keylist_ext): Correct precedence of signature
+ listing mode.
+ (gpg_keylist_ext): Implement signature listing mode.
+
+2002-11-25 Marcus Brinkmann <marcus@g10code.de>
+
+ * rungpg.c (_gpgme_gpg_spawn): Do not set parent fds to -1.
+ * posix-io.c (_gpgme_io_spawn): Call _gpgme_io_close instead close
+ for parent fds.
+ * w32-io.c (_gpgme_io_spawn): Call _gpgme_io_close instead
+ CloseHandle for parent fds.
+
+2002-11-22 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.h [_MSC_VER]: Define ssize_t as long.
+
+2002-11-22 Werner Koch <wk@gnupg.org>
+
+ * engine-gpgsm.c (_gpgme_gpgsm_new): Save the result of a first
+ setlocale before doing another setlocale.
+
+2002-11-21 Marcus Brinkmann <marcus@g10code.de>
+
+ * decrypt.c: Some beautyfication.
+
+ * verify.c (_gpgme_verify_status_handler): Treat
+ GPGME_STATUS_UNEXPECTED like GPGME_STATUS_NODATA.
+ Reported by Miguel Coca <e970095@zipi.fi.upm.es>.
+
+2002-11-19 Marcus Brinkmann <marcus@g10code.de>
+
+ * genkey.c: Only include <config.h> if [HAVE_CONFIG_H].
+ (struct genkey_result_s): Add new member FPR.
+ (_gpgme_release_genkey_result): Free RESULT->fpr if set.
+ (genkey_status_handler): Extract the fingerprint from the status
+ line.
+ (gpgme_op_genkey): Add new argument FPR and return the fingerprint
+ in it.
+ * gpgme.h: Adjust prototype of gpgme_op_genkey.
+
+2002-11-19 Marcus Brinkmann <marcus@g10code.de>
+
+ * rungpg.c (gpg_keylist): Add --with-fingerprint to gpg invocation
+ twice, to get fingerprints on subkeys. Suggested by Timo Schulz
+ <twoaday@freakmail.de>.
+ (gpg_keylist_ext): Likewise.
+
+2002-11-05 Marcus Brinkmann <marcus@g10code.de>
+
+ * import.c (append_xml_impinfo): Use
+ _gpgme_data_append_string_for_xml rather than
+ _gpgme_data_append_string for the field content.
+ Submitted by Miguel Coca <e970095@zipi.fi.upm.es>.
+
+2002-10-10 Marcus Brinkmann <marcus@g10code.de>
+
+ * rungpg.h, engine-gpgsm.h: File removed.
+ * engine-backend.h: New file.
+ * Makefile.am (gpgsm_components): New variable, set depending on
+ automake conditional HAVE_GPGSM.
+ (libgpgme_la_SOURCES): Add engine-backend.h, remove rungpg.h and
+ engine-gpgsm.h. Replace engine-gpgsm.c with ${gpgsm_components}.
+ (status-table.h): Depend on gpgme.h, not rungpg.h.
+ * conversion.c: Include <stdlib.h>.
+ * engine-gpgsm.c: Do not set ENABLE_GPGSM here. Include
+ "engine-backend.h" instead "engine-gpgsm.h". Reorder some
+ functions and remove all function prototypes.
+ (_gpgme_gpgsm_get_version): Make static and rename to ...
+ (gpgsm_get_version): ... this.
+ (_gpgme_gpgsm_check_version): Make static and rename to ...
+ (gpgsm_check_version): ... this.
+ (_gpgme_gpgsm_new): Make static. Change argument type from
+ GpgsmObject * to void **. Call gpgsm_release instead
+ _gpgme_gpgsm_release.
+ (_gpgme_gpgsm_op_decrypt): Make static and rename to ...
+ (gpgsm_check_decrypt): ... this.
+ (_gpgme_gpgsm_op_delete): Make static and rename to ...
+ (gpgsm_check_delete): ... this.
+ (_gpgme_gpgsm_set_recipients): Make static and rename to ...
+ (gpgsm_check_set_recipients): ... this.
+ (_gpgme_gpgsm_op_encrypt): Make static and rename to ...
+ (gpgsm_encrypt): ... this.
+ (_gpgme_gpgsm_op_export): Make static and rename to ...
+ (gpgsm_export): ... this.
+ (_gpgme_gpgsm_op_genkey): Make static and rename to ...
+ (gpgsm_genkey): ... this.
+ (_gpgme_gpgsm_op_import): Make static and rename to ...
+ (gpgsm_import): ... this.
+ (_gpgme_gpgsm_op_keylist): Make static and rename to ...
+ (gpgsm_keylist): ... this.
+ (_gpgme_gpgsm_op_keylist_ext): Make static and rename to ...
+ (gpgsm_keylist_ext): ... this.
+ (_gpgme_gpgsm_op_sign): Make static and rename to ...
+ (gpgsm_sign): ... this.
+ (_gpgme_gpgsm_op_trustlist): Make static and rename to ...
+ (gpgsm_trustlist): ... this.
+ (_gpgme_gpgsm_op_verify): Make static and rename to ...
+ (gpgsm_verify): ... this.
+ (gpgsm_status_handler): Rename to ...
+ (status_handler): ... this.
+ (_gpgme_gpgsm_set_status_handler): Make static and rename to ...
+ (gpgsm_set_status_handler): ... this.
+ (_gpgme_gpgsm_set_colon_line_handler): Make static and rename to ...
+ (gpgsm_set_colon_line_handler): ... this.
+ (_gpgme_gpgsm_add_io_cb): Rename to ...
+ (add_io_cb): ... this.
+ (_gpgme_gpgsm_start): Make static and rename to ...
+ (gpgsm_start): ... this.
+ (_gpgme_gpgsm_set_io_cb): Make static and rename to ...
+ (gpgsm_set_io_cb): ... this.
+ (_gpgme_gpgsm_io_event): Make static and rename to ...
+ (gpgsm_io_event): ... this.
+ (struct _gpgme_engine_ops_gpgsm): New variable.
+ [!ENABLE_GPGSM]: Removed.
+ * engine.c: Do not include <time.h>, <sys/types.h>, <string.h>,
+ <assert.h>, "io.h", "rungpg.h" and "engine-gpgsm.h". Include
+ <stdlib.h> and "engine-backend.h".
+ (struct engine_object_s): Rewritten.
+ (engine_ops): New variable.
+ * engine.c (_gpgme_engine_get_path, _gpgme_engine_get_version,
+ _gpgme_engine_check_version, _gpgme_engine_new,
+ _gpgme_engine_release, _gpgme_engine_set_verbosity,
+ _gpgme_engine_set_status_handler,
+ _gpgme_engine_set_command_handler,
+ _gpgme_engine_set_colon_line_handler, _gpgme_engine_op_decrypt,
+ _gpgme_engine_op_delete, _gpgme_engine_op_edit,
+ _gpgme_engine_op_encrypt, _gpgme_engine_op_encrypt_sign,
+ _gpgme_engine_op_export, _gpgme_engine_op_genkey,
+ _gpgme_engine_op_import, _gpgme_engine_op_keylist,
+ _gpgme_engine_op_keylist_ext, _gpgme_engine_op_sign,
+ _gpgme_engine_op_trustlist, _gpgme_engine_op_verify,
+ _gpgme_engine_start, _gpgme_engine_set_io_cbs,
+ _gpgme_engine_io_event): Reimplement.
+ * engine.h: Fix a few comments and a variable name in a prototype.
+ * ops.h: Do not include "rungpg.h".
+ * passphrase.c: Include config.h only if [HAVE_CONFIG_H]. Do not
+ include "rungpg.h".
+ * recipient.c: Likewise.
+ * signers.c: Likewise.
+ * version.c: Likewise.
+ * rungpg.c: Likewise. Include "engine-backend.h". Reorder
+ functions and remove prototypes.
+ (_gpgme_gpg_get_version): Make static and rename to ...
+ (gpg_get_version): ... this.
+ (_gpgme_gpg_check_version): Make static and rename to ...
+ (gpg_check_version): ... this.
+ (_gpgme_gpg_new): Make static. Change argument type from
+ GpgObject * to void **. Call gpg_release instead
+ _gpgme_gpg_release.
+ (_gpgme_gpg_op_decrypt): Make static and rename to ...
+ (gpg_check_decrypt): ... this.
+ (_gpgme_gpg_op_delete): Make static and rename to ...
+ (gpg_check_delete): ... this.
+ (_gpgme_gpg_set_recipients): Make static and rename to ...
+ (gpg_check_set_recipients): ... this.
+ (_gpgme_gpg_op_encrypt): Make static and rename to ...
+ (gpg_encrypt): ... this.
+ (_gpgme_gpg_op_export): Make static and rename to ...
+ (gpg_export): ... this.
+ (_gpgme_gpg_op_genkey): Make static and rename to ...
+ (gpg_genkey): ... this.
+ (_gpgme_gpg_op_import): Make static and rename to ...
+ (gpg_import): ... this.
+ (_gpgme_gpg_op_keylist): Make static and rename to ...
+ (gpg_keylist): ... this.
+ (_gpgme_gpg_op_keylist_ext): Make static and rename to ...
+ (gpg_keylist_ext): ... this.
+ (_gpgme_gpg_op_sign): Make static and rename to ...
+ (gpg_sign): ... this.
+ (_gpgme_gpg_op_trustlist): Make static and rename to ...
+ (gpg_trustlist): ... this.
+ (_gpgme_gpg_op_verify): Make static and rename to ...
+ (gpg_verify): ... this.
+ (gpg_status_handler): Rename to ...
+ (status_handler): ... this.
+ (_gpgme_gpg_set_status_handler): Make static and rename to ...
+ (gpg_set_status_handler): ... this.
+ (_gpgme_gpg_set_colon_line_handler): Make static and rename to ...
+ (gpg_set_colon_line_handler): ... this.
+ (gpgme_gpg_add_io_cb): Rename to ...
+ (add_io_cb): ... this.
+ (_gpgme_gpg_start): Make static and rename to ...
+ (gpg_start): ... this.
+ (_gpgme_gpg_set_io_cb): Make static and rename to ...
+ (gpg_set_io_cb): ... this.
+ (_gpgme_gpg_io_event): Make static and rename to ...
+ (gpg_io_event): ... this.
+ (struct _gpgme_engine_ops_gpg): New variable.
+
+2002-10-10 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine-gpgsm.c (_gpgme_gpgsm_op_verify) [!ENABLE_GPGSM]: Add
+ missing argument.
+
+2002-10-09 Marcus Brinkmann <marcus@g10code.de>
+
+ * data.h, data-user.c, data-stream.c, data-mem.c, data-fd.c,
+ data-compat.c: New file. Really check them in this time, completes
+ 2002-10-08 change.
+
+ * rungpg.h (GpgStatusHandler): Rename type to GpgmeStatusHandler
+ and move to ...
+ * types.h (GpgmeStatusHandler): ... here.
+ * rungpg.h (GpgColonLineHandler): Rename type to GpgmeColonLineHandler.
+ and move to ...
+ * types.h (GpgmeColonLineHandler): ... here.
+ * rungpg.h (GpgCommandHandler): Rename type to GpgmeCommandHandler.
+ and move to ...
+ * types.h (GpgmeCommandHandler): ... here.
+ * engine.h: Don't include "rungpg.h".
+ (_gpgme_engine_set_status_handler): Change type of
+ argument from GpgStatusHandler to GpgmeStatusHandler.
+ (_gpgme_engine_set_colon_line_handler): Change type of
+ argument from GpgColonLineHandler to GpgmeColonLineHandler.
+ (_gpgme_engine_set_command_handler): Change type of
+ argument from GpgCommandHandler to GpgmeCommandHandler.
+ * engine-gpgsm.h: Don't include "rungpg.h".
+ (_gpgme_gpgsm_set_status_handler): Change type of
+ argument from GpgStatusHandler to GpgmeStatusHandler.
+ (_gpgme_gpgsm_set_colon_line_handler): Change type of
+ argument from GpgColonLineHandler to GpgmeColonLineHandler.
+ * engine-gpgsm.c: Do not include "rungpg.h".
+ (struct gpgsm_object_s): Change type of
+ status.fnc to GpgmeStatusHandler. Change type of colon.fnc to
+ GpgmeColonLineHandler.
+ (gpgsm_assuan_simple_command): Change type of argument from
+ GpgStatusHandler to GpgmeStatusHandler.
+ (_gpgme_gpgsm_set_status_handler): Likewise.
+ (_gpgme_gpgsm_set_colon_line_handler): Change type of argument from
+ GpgColonLineHandler to GpgmeColonLineHandler.
+ * rungpg.h (_gpgme_gpg_set_status_handler): Change type of
+ argument from GpgStatusHandler to GpgmeStatusHandler.
+ (_gpgme_gpg_set_colon_line_handler): Change type of
+ argument from GpgColonLineHandler to GpgmeColonLineHandler.
+ (_gpgme_gpg_set_command_handler): Change type of
+ argument from GpgCommandHandler to GpgmeCommandHandler.
+ * rungpg.c (struct gpg_object_s): Change type of status.fnc to
+ GpgmeStatusHandler. Change type of colon.fnc to
+ GpgmeColonLineHandler. Change type of cmd.fnc to
+ GpgmeCommandLineHandler.
+ (_gpgme_gpg_set_status_handler): Change type of argument FNC to
+ GpgmeStatusHandler.
+ (_gpgme_gpg_set_colon_line_handler): Change type of argument FNC
+ to GpgmeColonLineHandler.
+ (_gpgme_gpg_set_command_handler): Change type of argument FNC to
+ GpgmeCommandHandler.
+ * engine.c (_gpgme_engine_set_status_handler): Change type of
+ argument FNC to GpgmeStatusHandler.
+ (_gpgme_engine_set_colon_line_handler): Change type of argument FNC
+ to GpgmeColonLineHandler.
+ (_gpgme_engine_set_command_handler): Change type of argument FNC to
+ GpgmeCommandHandler.
+
+ * rungpg.h (_gpgme_gpg_enable_pipemode): Remove prototype.
+ * rungpg.c (struct gpg_object_s): Remove PM.
+ (pipemode_cb): Prototype removed.
+ (add_pm_data): Function removed.
+ (_gpgme_gpg_enable_pipemode): Likewise.
+ (pipemode_copy): Likewise.
+ (pipemode_cb): Likewise.
+ (add_arg): Don't check for pipemode.
+ (add_data): Likewise.
+ (_gpgme_gpg_set_status_handler): Likewise.
+ (_gpgme_gpg_set_colon_line_handler): Likewise.
+ (_gpgme_gpg_set_command_handler): Likewise.
+ (_gpgme_gpg_spawn): Likewise.
+ (_gpgme_gpg_spawn): Don't set PM.active.
+ (_gpgme_gpg_op_verify): Remove pipemode case.
+ * verify.c (_gpgme_op_verify_start): Remove pipemode case.
+
+ * rungpg.h (_gpgme_gpg_add_arg, _gpgme_gpg_add_data,
+ _gpgme_gpg_add_pm_data, _gpgme_gpg_housecleaning,
+ _gpgme_gpg_set_simple_line_handler): Prototype removed.
+ (_gpgme_gpg_set_verbosity): New prototype.
+ * rungpg.c (_gpgme_gpg_add_data): Make static and rename to ...
+ (add_data): ... this.
+ (_gpgme_gpg_add_pm_data): Call add_data, not _gpgme_gpg_add_data.
+ (_gpgme_gpg_set_command_handler): Likewise.
+ (_gpgme_gpg_op_decrypt, _gpgme_gpg_op_edit, _gpgme_gpg_op_encrypt,
+ _gpgme_gpg_op_encrypt_sign, _gpgme_gpg_op_export,
+ _gpgme_gpg_op_genkey, _gpgme_gpg_op_import, _gpgme_gpg_op_sign,
+ _gpgme_gpg_op_verify): Likewise.
+ (_gpgme_gpg_add_pm_data): Rename to ...
+ (add_pm_data): ... this.
+ (_gpgme_gpg_op_verify): Call add_pm_data, not
+ _gpgme_gpg_add_pm_data.
+ (_gpgme_gpg_add_arg): Make static and rename to ...
+ (add_arg): ... this.
+ (_gpgme_gpg_set_command_handler, _gpgme_gpg_new,
+ _gpgme_gpg_op_decrypt, _gpgme_gpg_op_delete,
+ _gpgme_append_gpg_args_from_signers, _gpgme_gpg_op_edit,
+ _gpgme_append_gpg_args_from_recipients, _gpgme_gpg_op_encrypt,
+ _gpgme_gpg_op_encrypt_sign, _gpgme_gpg_op_export,
+ _gpgme_gpg_op_genkey, _gpgme_gpg_op_import, _gpgme_gpg_op_keylist,
+ _gpgme_gpg_op_keylist_ext, _gpgme_gpg_op_trustlist,
+ _gpgme_gpg_op_sign, _gpgme_gpg_op_verify): Use add_arg, not
+ _gpgme_gpg_add_arg.
+ (_gpgme_gpg_set_verbosity): New function.
+ (struct gpg_object_s): Remove member simple from colon.
+ (_gpgme_gpg_set_colon_line_handler): Don't initialize simple.
+ (_gpgme_gpg_set_simple_line_handler): Removed function.
+ (read_colon_line): Don't check the GPG->colon.simple.
+ * engine.c (_gpgme_engine_set_verbosity): Call
+ _gpgme_gpg_set_verbosity instead _gpgme_gpg_add_arg.
+
+2002-10-08 Marcus Brinkmann <marcus@g10code.de>
+
+ * util.h (_gpgme_malloc, _gpgme_realloc, _gpgme_calloc,
+ _gpgme_strdup, _gpgme_free): Remove prototypes.
+ (xtrymalloc, xtrycalloc, xtryrealloc, xtrystrdup, xfree): Remove
+ macros.
+ * util.c: File removed.
+ * Makefile.am (libgpgme_la_SOURCES): Remove util.h.
+ * conversion.c (_gpgme_decode_c_string): Use malloc instead of
+ xtrymalloc, realloc instead of xtryrealloc, calloc instead of
+ xtrycalloc, free instead of xfree.
+ (_gpgme_data_append_percentstring_for_xml): Likewise.
+ * data.c (_gpgme_data_new, _gpgme_data_release): Likewise.
+ * data-compat.c (gpgme_data_new_from_filepart): Likewise.
+ * data-mem.c (mem_write, mem_release, gpgme_data_new_from_mem,
+ _gpgme_data_get_as_string): Likewise.
+ * debug.c (debug_init): Likewise.
+ * decrypt.c (_gpgme_release_decrypt_result): Likewise.
+ * delete.c (_gpgme_release_delete_result): Likewise.
+ * edit.c (_gpgme_release_edit_result, _gpgme_op_edit_start):
+ Likewise.
+ * encrypt.c (_gpgme_release_encrypt_result): Likewise.
+ * engine.c (_gpgme_engine_get_info, _gpgme_engine_new,
+ _gpgme_engine_release): Likewise.
+ * engine-gpgsm.c (_gpgme_gpgsm_new, _gpgme_gpgsm_release,
+ _gpgme_gpgsm_op_decrypt, _gpgme_gpgsm_op_delete,
+ gpgsm_set_recipients, _gpgme_gpgsm_op_encrypt,
+ _gpgme_gpgsm_op_export, _gpgme_gpgsm_op_genkey,
+ _gpgme_gpgsm_op_import, _gpgme_gpgsm_op_keylist,
+ _gpgme_gpgsm_op_keylist_ext, _gpgme_gpgsm_op_sign,
+ _gpgme_gpgsm_op_verify, gpgsm_status_handler): Likewise.
+ * genkey.c (_gpgme_release_genkey_result): Likewise.
+ * gpgme.c (gpgme_new, gpgme_release): Likewise.
+ * import.c (_gpgme_release_import_result): Likewise.
+ * key.c (_gpgme_key_cache_init, _gpgme_key_cache_add, key_new,
+ add_subkey, gpgme_key_release, _gpgme_key_append_name): Likewise.
+ * keylist.c (_gpgme_release_keylist_result, keylist_colon_handler,
+ _gpgme_op_keylist_event_cb, gpgme_op_keylist_next): Likewise.
+ * ops.h (test_and_allocate_result): Likewise.
+ * passphrase.c (_gpgme_release_passphrase_result,
+ _gpgme_passphrase_status_handler,
+ _gpgme_passphrase_command_handler): Likewise.
+ * progress.c (_gpgme_progress_status_handler): Likewise.
+ * recipient.c (gpgme_recipients_new, gpgme_recipients_release,
+ gpgme_recipients_add_name_with_validity): Likewise.
+ * rungpg.c (_gpgme_gpg_new, _gpgme_gpg_release,
+ _gpgme_gpg_add_arg, _gpgme_gpg_add_data,
+ _gpgme_gpg_set_colon_line_handler, free_argv, free_fd_data_map,
+ build_argv, _gpgme_gpg_spawn, read_status, read_colon_line):
+ Likewise.
+ * sign.c (_gpgme_release_sign_result): Likewise.
+ * signers.c (_gpgme_signers_add): Likewise.
+ * trustlist.c (trust_item_new, trustlist_colon_handler,
+ _gpgme_op_trustlist_event_cb, gpgme_op_trustlist_next,
+ gpgme_trustitem_release): Likewise.
+ * verify.c (_gpgme_release_verify_result, finish_sig): Likewise.
+ * version.c (gpgme_get_engine_info, _gpgme_get_program_version):
+ Likewise.
+ * w32-io.c (create_reader, create_writer, destroy_reader,
+ destroy_writer, build_commandline, _gpgme_io_spawn): Likewise.
+ * w32-sema.c (critsect_init, _gpgme_sema_cs_destroy): Likewise.
+ * w32-util.c (read_w32_registry_string): Likewise.
+ * wait.c (_gpgme_fd_table_deinit, _gpgme_fd_table_put,
+ _gpgme_wait_event_cb, _gpgme_add_io_cb, _gpgme_remove_io_cb)
+ * data-compat.c: Include <stdlib.h>.
+
+2002-10-08 Marcus Brinkmann <marcus@g10code.de>
+
+ New data object component:
+
+ * gpgme.h (GpgmeDataReadCb, GpgmeDataWriteCb, GpgmeDataSeekCb,
+ GpgmeDataReleaseCb): New types.
+ (struct GpgmeDataCbs): New structure.
+ (gpgme_data_read): Changed prototype to match that of read() closely.
+ (gpgme_data_write): Similar for write().
+ (gpgme_data_seek, gpgme_data_new_from_cbs, gpgme_data_new_from_fd,
+ gpgme_data_new_from_stream): New prototypes.
+ (gpgme_data_get_type, gpgme_check_engine): Prototype removed.
+
+ * Makefile.am (libgpgme_la_SOURCES): Add data.h, data-fd.c,
+ data-stream.c, data-mem.c, data-user.c and data-compat.c.
+ * data.c: Reimplemented from scratch.
+ * (data-compat.c, data-fd.c, data.h, data-mem.c, data-stream.c,
+ data-user.c): New file.
+ * context.h (struct gpgme_data_s): Removed.
+ * conversion.c: Include <errno.h> and <sys/types.h>.
+ (_gpgme_data_append): New function.
+ * data.c (_gpgme_data_append_string): Move to ...
+ * conversion.c (_gpgme_data_append_string): ... here.
+ * data.c (_gpgme_data_append_for_xml): Move to ...
+ * conversion.c (_gpgme_data_append_for_xml): ... here.
+ * data.c (_gpgme_data_append_string_for_xml): Move to ...
+ * conversion.c (_gpgme_data_append_string_for_xml): ... here.
+ * data.c (_gpgme_data_append_percentstring_for_xml): Move to ...
+ * conversion.c (_gpgme_data_append_percentstring_for_xml): ... here.
+
+ * ops.h (_gpgme_data_get_mode, _gpgme_data_set_mode): Prototype
+ removed.
+ * types.h (GpgmeDataMode): Type removed.
+
+ * decrypt.c (_gpgme_decrypt_start): Don't check data type or mode.
+ * edit.c (_gpgme_op_edit_start): Likewise.
+ * encrypt.c (_gpgme_op_encrypt_start): Likewise.
+ * encrypt-sign.c (_gpgme_op_encrypt_sign_start): Likewise.
+ * encrypt-sign.c (_gpgme_op_encrypt_sign_start): Likewise.
+ * export.c (_gpgme_op_export_start): Likewise.
+ * genkey.c (_gpgme_op_genkey_start): Likewise.
+ * import.c (_gpgme_op_import_start): Likewise.
+ * sign.c (_gpgme_op_sign_start): Likewise.
+ * verify.c (_gpgme_op_verify_start): Likewise.
+
+ * encrypt.c (gpgme_op_encrypt): Remove hack that returns invalid
+ no recipient if no data was returned.
+ * encrypt-sign.c (gpgme_op_encrypt_sign): Remove hack that returns
+ no recipient if no data was returned.
+ * encrypt-sign.c (gpgme_op_encrypt_sign): Remove hack that returns
+ no recipient if no data was returned.
+
+ * engine.c (_gpgme_engine_op_verify): Add new argument to
+ differentiate detached from normal signatures.
+ * engine.h (_gpgme_engine_op_verify): Likewise for prototype.
+ * engine-gpgsm.c (_gpgme_gpgsm_op_verify): Likewise. Don't check
+ mode of data argument.
+ * engine-gpgsm.h (_gpgme_gpgsm_op_verify): Likewise for prototype.
+ * gpgme.h (gpgme_op_verify_start): Likewise for prototype.
+ (gpgme_op_verify): Likewise for prototype.
+ * rungpg.c (_gpgme_gpg_op_verify): Likewise.
+ * rungpg.h (_gpgme_gpg_op_verify): Likewise for prototype.
+ * verify.c (_gpgme_op_verify_start): Likewise.
+ (gpgme_op_verify_start): Likewise.
+ (gpgme_op_verify): Likewise.
+
+ * rungpg.c (struct arg_and_data_s): New member INBOUND to hold
+ direction of data object.
+ (_gpgme_gpg_add_data): Add new argument INBOUND. Use it to
+ determine direction of data object.
+ (_gpgme_gpg_add_pm_data, _gpgme_gpg_set_command_handler,
+ _gpgme_gpg_op_decrypt, _gpgme_gpg_op_edit, _gpgme_gpg_op_encrypt,
+ _gpgme_gpg_op_encrypt_sign, _gpgme_gpg_op_export,
+ _gpgme_gpg_op_genkey, _gpgme_gpg_op_import, _gpgme_gpg_op_sign,
+ _gpgme_gpg_op_verify): Add new argument to _gpgme_gpg_add_data
+ invocation.
+ (build_argv): Use new member INBOUND to determine direction of
+ file descriptor. Don't check the data type.
+ * rungpg.h (_gpgme_gpg_add_data): Add new argument to prototype.
+
+ * gpgme.c (gpgme_get_op_info): Don't call
+ _gpgme_data_get_as_string if CTX->op_info is NULL.
+
+ * version.c (gpgme_check_engine): Function removed.
+
+2002-09-30 Werner Koch <wk@gnupg.org>
+
+ * keylist.c (keylist_colon_handler): Take care when printing a
+ NULL with the DEBUG.
+
+ * engine-gpgsm.c (struct gpgsm_object_s): New member ANY.
+ (gpgsm_status_handler): Run the colon function to indicate EOF.
+ (_gpgme_gpgsm_set_colon_line_handler): Better reset ANY here.
+
+2002-09-28 Marcus Brinkmann <marcus@g10code.de>
+
+ * conversion.c (_gpgme_hextobyte): Prevent superfluous
+ multiplication with base. Reported by Stéphane Corthésy.
+
+ * keylist.c (gpgme_op_keylist_ext_start): Use private asynchronous
+ operation type in invocation of _gpgme_op_reset.
+
+2002-09-20 Werner Koch <wk@gnupg.org>
+
+ * ath.c: Include sys/time.h if sys/select.h is not available.
+
+2002-09-13 Marcus Brinkmann <marcus@g10code.de>
+
+ * keylist.c (keylist_status_handler): Do not call finish_key() here.
+ (gpgme_op_keylist_ext_start): Set CTX->tmp_key to NULL.
+
+2002-09-03 Marcus Brinkmann <marcus@g10code.de>
+
+ * Makefile.am (assuan_libobjs): Remove @LTLIBOBJS@ as we link them
+ into gpgme unconditionally.
+ (libgpgme_la_LIBADD): Change @LIBOBJS@ into @LTLIBOBJS@.
+
+2002-09-02 Marcus Brinkmann <marcus@g10code.de>
+
+ * Makefile.am (assuan_libobjs): Use @LTLIBOBJS@ instead @LIBOBJS@.
+
+2002-09-02 Marcus Brinkmann <marcus@g10code.de>
+
+ * debug.c (_gpgme_debug_add): Test *LINE, not LINE.
+ (_gpgme_debug_end): Likewise.
+ Reported by Dr. Stefan Dalibor <Dr.Stefan.Dalibor@bfa.de>.
+
+2002-09-02 Marcus Brinkmann <marcus@g10code.de>
+
+ * posix-io.c (_gpgme_io_select): Don't use a non-constant struct
+ initializer.
+ * version.c (_gpgme_get_program_version): Likewise.
+ Reported by Dr. Stefan Dalibor <Dr.Stefan.Dalibor@bfa.de>.
+
+2002-09-02 Marcus Brinkmann <marcus@g10code.de>
+
+ * conversion.c (_gpgme_decode_c_string): Set DESTP before
+ modifying DEST.
+
+ * conversion.c (_gpgme_decode_c_string): Fix off by one error in
+ last change.
+ * rungpg.c (_gpgme_append_gpg_args_from_signers): Move before
+ _gpgme_op_edit so its prototype is known early on.
+
+ * conversion.c: New file.
+ * util.h: Add prototypes for _gpgme_decode_c_string and
+ _gpgme_hextobyte.
+ * keylist.c (keylist_colon_handler): Call _gpgme_decode_c_string
+ on issuer name.
+ * Makefile.am (libgpgme_la_SOURCES): Add conversion.c
+ * key.c (_gpgme_key_append_name): Replace calls to hextobyte by
+ calls to _gpgme_hextobyte.
+ (hash_key): Likewise.
+
+2002-09-01 Marcus Brinkmann <marcus@g10code.de>
+
+ * op-support.c (_gpgme_op_reset): Set CTX->pending after calling
+ _gpgme_engine_release, as this will reset pending to zero in the
+ event done callback on cancelled operations.
+
+2002-08-30 Marcus Brinkmann <marcus@g10code.de>
+
+ * rungpg.c (_gpgme_gpg_op_edit): Add args from signers.
+ Suggested by Miguel Coca <e970095@zipi.fi.upm.es>.
+
+ * rungpg.c (_gpgme_gpg_op_edit): Add bogus ctx argument.
+ * rungpg.h: Also to prototype.
+ * engine.c (_gpgme_engine_op_edit): Likewise.
+ * engine.h: Likewise.
+ * edit.c (_gpgme_op_edit_start): Likewise.
+
+2002-08-29 Werner Koch <wk@gnupg.org>
+
+ * engine-gpgsm.c (_gpgme_gpgsm_op_sign): Implement signer
+ selection.
+ * vasprintf.c (va_copy): Define macro if not yet defined.
+
+2002-08-29 Marcus Brinkmann <marcus@g10code.de>
+
+ * passphrase.c (_gpgme_passphrase_status_handler): Reset
+ CTX->result.passphrase->no_passphrase if passphrase is given (good
+ or bad). Submitted by Jean DIRAISON <jean.diraison@free.fr>.
+
+2002-08-28 Marcus Brinkmann <marcus@g10code.de>
+
+ * posix-io.c (_gpgme_io_spawn): Use a double-fork approach.
+ Return 0 on success, -1 on error.
+ * version.c (_gpgme_get_program_version): Don't wait for the child.
+ * engine.c (_gpgme_engine_housecleaning): Function removed.
+ (do_reaping): Likewise.
+ (_gpgme_engine_add_child_to_reap_list): Likewise.
+ (struct reap_s): Removed.
+ (reap_list): Likewise.
+ (reap_list_lock): Likewise.
+ * engine.h (_gpgme_engine_io_event): Remove prototypes for
+ _gpgme_engine_housecleaning and
+ _gpgme_engine_add_child_to_reap_list.
+ * rungpg.c (_gpgme_gpg_release): Don't add child to reap list.
+ (struct gpg_object_s): Remove PID member.
+ (_gpgme_gpg_new): Don't initialize GPG->pid.
+ (_gpgme_gpg_spawn): Don't set GPG->pid.
+ * wait.c (run_idle): Removed.
+ (gpgme_wait): Run idle_function directly.
+
+2002-08-21 Marcus Brinkmann <marcus@g10code.de>
+
+ * encrypt-sign.c (encrypt_sign_status_handler): Remove dead
+ variables encrypt_info and encrypt_info_len.
+ * trustlist.c (gpgme_op_trustlist_start): Set colon line handler.
+ * posix-sema.c (sema_fatal): Remove function.
+ All these reported by Stéphane Corthésy.
+
+2002-08-23 Werner Koch <wk@gnupg.org>
+
+ * gpgme-config.in: Made --prefix work for --libs.
+
+2002-08-21 Marcus Brinkmann <marcus@g10code.de>
+
+ * ath.h: Update list of symbols that get a prefix: Rename the
+ ath_mutex_*_available symbols to ath_*_available.
+
+2002-08-21 Marcus Brinkmann <marcus@g10code.de>
+
+ * stpcpy.c: New file from gnulib.
+ * Makefile.am (assuan_libobjs): Remove jnlib.
+
+2002-08-20 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.h: Add prototype for gpgme_op_import_ext.
+ * import.c (struct import_result_s): New member `nr_considered'.
+ Rename `any_imported' to `nr_imported'.
+ (import_status_handler): Increment nr_imported. Set nr_considered
+ if appropriate.
+ (gpgme_op_import_ext): New function.
+ (gpgme_op_import): Implement in terms of gpgme_op_import_ext.
+
+2002-08-20 Werner Koch <wk@gnupg.org>
+
+ * gpgme.m4: Replaced with a new and faster version. This does not
+ anymore try to build test programs. If we really need test
+ programs, we should add an option to gpgme-config to do so.
+
+ * vasprintf.c (int_vasprintf): Hack to handle NULL passed for %s.
+
+2002-08-20 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.c (_gpgme_set_op_info): Append data on subsequent calls.
+ * encrypt-sign.c (encrypt_sign_status_handler): Remove op_info
+ handling.
+
+2002-08-19 Werner Koch <wk@gnupg.org>
+
+ * decrypt.c (is_token,skip_token): Duplicated from verify.c
+ (gpgme_op_decrypt): Hack to properly return Decryption_Failed..
+ (_gpgme_decrypt_status_handler): Create an operation info.
+
+2002-08-14 Werner Koch <wk@gnupg.org>
+
+ * key.h (struct certsig_s): New. Use it in gpgme_key_s.
+ * key.c (gpgme_key_release): Release it. We need to add more code
+ of course.
+ (_gpgme_key_append_name): Use memset to intialize the struct.
+ * gpgme.h (GPGME_KEYLIST_MODE_SIGS): New.
+ * rungpg.c (_gpgme_gpg_op_keylist): Include sigs in listing depending
+ non the list mode.
+
+ * key.c (gpgme_key_get_string_attr): Use GPGME_ATTR_TYPE to return
+ information about the key type (PGP or X.509).
+ (gpgme_key_get_ulong_attr): Likewise.
+
+ * keylist.c (keylist_colon_handler): Include 1 in the check for
+ valid algorithms so that RSA is usable. Store the issuer name and
+ serial number also for "crs" records. Parse the expire date for
+ subkeys.
+ (set_userid_flags): Put them onto the last appended key.
+
+2002-07-29 Marcus Brinkmann <marcus@g10code.de>
+
+ * rungpg.c (_gpgme_gpg_op_edit): Use --with-colons.
+
+2002-07-28 Marcus Brinkmann <marcus@g10code.de>
+
+ * data.c (gpgme_data_read): For GPGME_DATA_TYPE_NONE, return EOF
+ instead an error.
+
+ The following changes make it possible to flush an inbound data
+ pipe before invoking a command handler:
+
+ * posix-io.c (_gpgme_io_select): Accept new argument NONBLOCK to
+ _gpgme_io_select. Set timeout of 0 if this is set.
+ * w32-io.c (_gpgme_io_select): Likewise.
+ * io.h: Add new argument NONBLOCK to _gpgme_io_select prototype.
+ * wait.c (do_select): Add new argument to _gpgme_io_select
+ invocation.
+ * rungpg.h (_gpgme_gpg_set_command_handler): Add new argument
+ linked_data to prototype.
+ * engine.h (_gpgme_engine_set_command_handler): Likewise.
+ * engine.c (_gpgme_engine_set_command_handler): Likewise.
+ * passphrase.c (_gpgme_passphrase_start): Pass NULL as linked_data
+ argument to _gpgme_engine_set_command_handler.
+ * rungpg.c (struct gpg_object_s): New members linked_data and
+ linked_idx in CMD.
+ (_gpgme_gpg_new): Initialize those new members.
+ (_gpgme_gpg_set_command_handler): Accept new argument linked_data.
+ (build_argv): Handle linked_data in the same hack as cb_data.
+ (read_status): If linked_data is in use, flush the pipe before
+ activating the command handler.
+ * gpgme.h: Add prototypes for gpgme_op_edit_start and
+ gpgme_op_edit.
+
+ The next changes export the status codes to the user:
+
+ * decrypt.c (_gpgme_decrypt_status_handler): Likewise, also prefix
+ all STATUS_ with GPGME_.
+ * delete.c (delete_status_handler): Likewise.
+ * decrypt-verify.c (decrypt_verify_status_handler): Likewise.
+ * encrypt.c (_gpgme_encrypt_status_handler): Likewise.
+ (_gpgme_encrypt_sym_status_handler): Likewise.
+ * encrypt-sign.c (encrypt_sign_status_handler): Likewise.
+ * engine-gpgsm.c (parse_status): Likewise.
+ (gpgsm_status_handler): Likewise.
+ (gpgsm_set_recipients): Likewise.
+ * export.c (export_status_handler): Likewise.
+ * genkey.c (genkey_status_handler): Likewise.
+ * import.c (append_xml_impinfo): Likewise.
+ (import_status_handler): Likewise.
+ * keylist.c (keylist_status_handler): Likewise.
+ * passphrase.c (_gpgme_passphrase_status_handler): Likewise.
+ (command_handler): Likewise.
+ * progress.c (_gpgme_progress_status_handler): Likewise.
+ * sign.c (_gpgme_sign_status_handler): Likewise.
+ * trustlist.c (trustlist_status_handler): Likewise.
+ * verify.c (_gpgme_verify_status_handler): Likewise.
+ * gpgme.h (GpgmeEditCb): New type.
+ * rungpg.h (GpgStatusCode): Rename and move to ...
+ * gpgme.h (GpgmeStatusCode): ... this and here.
+ * Makefile.am (status-table.h): Run mkstatus on gpgme.h, not rungpg.h.
+ * mkstatus: Prefix STATUS with GPGME_.
+ * rungpg.h (GpgStatusHandler, GpgCommandHandler): Change type
+ accordingly.
+ * ops.h (_gpgme_verify_status_handler,
+ _gpgme_decrypt_status_handler, _gpgme_sign_status_handler,
+ _gpgme_encrypt_status_handler, _gpgme_passphrase_status_handler,
+ _gpgme_progress_status_handler): Likewise.
+ * rungpg.c (struct gpg_object_s): Likewise for CMD.code.
+
+ These changes add an edit operation to GPGME:
+
+ * context.h (struct gpgme_context_s): New member RESULT.edit. *
+ ops.h: Add prototype for _gpgme_release_edit_result and
+ _gpgme_passphrase_command_handler.
+ * passphrase.c (command_handler): Make non-static and rename to ...
+ (_gpgme_passphrase_command_handler): ... this.
+ (_gpgme_passphrase_start): Use new name for command handler.
+ * types.h: Add EditResult type.
+ * gpgme.c (_gpgme_release_result): Release EDIT result.
+ * edit.c: New file.
+ * Makefile.am (libgpgme_la_SOURCES): Add edit.c.
+ (libgpgme_la_LDADD): Rename to libgpgme_la_LIBADD, and include
+ assuan_libobjs.
+ (assuan_libobjs): New variable, set this instead
+ libgpgme_la_LIBADD.
+ * engine.h (_gpgme_engine_op_edit): New prototype.
+ * engine.c (_gpgme_engine_op_edit): New function.
+ * rungpg.h (_gpgme_gpg_op_edit): New prototype.
+ * rungpg.c (_gpgme_gpg_op_edit): New function.
+
+2002-07-27 Marcus Brinkmann <marcus@g10code.de>
+
+ * delete.c (delete_problem): New case ambigious specification.
+ (delete_status_handler): Handle new case (poorly).
+
+2002-07-25 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine-gpgsm.c (_gpgme_gpgsm_op_delete): Implement this.
+
+2002-07-25 Marcus Brinkmann <marcus@g10code.de>
+
+ * Makefile.am (libgpgme_la_LDADD): Add @LIBOBJS@ for vasprintf and
+ fopencookie.
+ * vasprintf.c: Update to more recent libiberty version.
+ * debug.h: Replace #elsif with #elif.
+
+ Submitted by Stéphane Corthésy:
+ * util.h (vasprintf): Correct prototype.
+ * encrypt-sign.c: Include <stddef.h>.
+ (encrypt_sign_status_handler): Change type of ENCRYPT_INFO_LEN to
+ size_t.
+ * ath-pthread.c: Include <stdlib.h>, not <malloc.h>.
+ * ath-pth.c: Likewise.
+
+2002-07-25 Marcus Brinkmann <marcus@g10code.de>
+
+ * wait.c (fdt_global): Make static. Reported by Stéphane
+ Corthésy.
+
+ * rungpg.c (_gpgme_gpg_op_keylist_ext): Skip empty string
+ patterns. Reported by Stéphane Corthésy.
+
+ * key.c (gpgme_key_get_as_xml): Add OTRUST attribute. Requested
+ by Stéphane Corthésy.
+ (gpgme_key_get_string_attr): Add GPGME_ATTR_SIG_SUMMARY case to
+ silence gcc warning.
+
+ * rungpg.c (_gpgme_gpg_new): Always set utf8 as charset.
+
+2002-07-03 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.c (gpgme_set_io_cbs): Deal with CTX being NULL.
+
+ * gpgme.c (_gpgme_op_event_cb_user): New function.
+ * op-support.c (_gpgme_op_reset): Support a new mode of operation
+ for private or user event loop. Use new user event callback
+ wrapper.
+ * trustlist.c (gpgme_op_trustlist_start): Use this new mode.
+ * keylist.c (gpgme_op_keylist_start): Likewise.
+
+ * rungpg.c (_gpgme_gpg_io_event): New function.
+ * rungpg.h (_gpgme_gpg_io_event): New prototype.
+ * engine-gpgsm.c (_gpgme_gpg_io_event): New function.
+ * engine-gpgsm.h (_gpgme_gpgsm_io_event): New prototype.
+ * engine.c (_gpgme_engine_io_event): New function.
+ * engine.h (_gpgme_engine_io_event): New prototype.
+ * keylist.c (finish_key): Call _gpgme_engine_io_event, and move
+ the real work for the default IO callback routines to ...
+ (_gpgme_op_keylist_event_cb): ... here. New function.
+ * trustlist.c (trustlist_colon_handler): Signal
+ GPGME_EVENT_NEXT_TRUSTITEM. Move queue manipulation to ...
+ (_gpgme_op_trustlist_event_cb): ... here. New function.
+ * gpgme.c (_gpgme_op_event_cb): Call _gpgme_op_keylist_event_cb
+ and _gpgme_op_trustlist_event_cb when appropriate.
+ * ops.h (_gpgme_op_keylist_event_cb): New prototype.
+ (_gpgme_op_trustlist_event_cb): Likewise.
+ * op-support.c (_gpgme_op_reset): Add comment why we don't use the
+ user provided event handler directly.
+ * gpgme.h (GpgmeRegisterIOCb): Return GpgmeError value, and TAG in
+ a pointer argument.
+ * wait.c (_gpgme_add_io_cb): Likewise.
+ * wait.h (_gpgme_add_io_cb): Likewise for prototype.
+ * rungpg.c (_gpgme_gpg_add_io_cb): Call IO_CBS->add with new
+ argument. Fix up error handling.
+ * engine-gpgsm.c (_gpgme_gpgsm_add_io_cb): Call IO_CBS->add with
+ new argument, fix up error handling.
+
+2002-07-03 Werner Koch <wk@gnupg.org>
+
+ * encrypt.c (status_handler_finish): New.
+ (_gpgme_encrypt_status_handler): Moved some code out to the new
+ function and call this function also in case we get into the
+ status handler with an error which might happen due to a kludge in
+ engine-gpgsm.c
+
+2002-06-28 Marcus Brinkmann <marcus@g10code.de>
+
+ * keylist.c (gpgme_op_keylist_ext_start): Always use our own FD
+ table (eg use synchronous mode).
+
+2002-06-27 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.h: Fix documentation of key attribute retrieval functions.
+
+2002-06-28 Marcus Brinkmann <marcus@g10code.de>
+
+ * ops.h (_gpgme_wait_on_condition): Remove HANG argument from
+ prototype and change return type to GpgmeError.
+ (_gpgme_wait_one): New prototype.
+ * wait.c (gpgme_wait): Replace with the meat from
+ _gpgme_wait_on_condition here, and remove the support for
+ conditions.
+ (_gpgme_wait_on_condition): Remove HANG argument from prototype
+ and change return type to GpgmeError. Replace with meat from
+ _gpgme_wait_one and add support for conditions.
+ (_gpgme_wait_one): Just call _gpgme_wait_on_condition without
+ condition.
+ * keylist.c (gpgme_op_keylist_ext_start): Always use our own FD
+ table (eg use synchronous mode).
+ (gpgme_op_keylist_next): Remove HANG argument from
+ _gpgme_wait_on_condition. Check its return value.
+ * trustlist.c (gpgme_op_trustlist_start): Always use our own FD
+ table (eg use synchronous mode).
+ (gpgme_op_trustlist_next): Remove HANG argument from
+ _gpgme_wait_on_condition. Check its return value.
+
+2002-06-26 Werner Koch <wk@gnupg.org>
+
+ * engine-gpgsm.c (map_assuan_error): Map No_Data_Available to EOF.
+
+ * import.c (append_xml_impinfo): Kludge to print fingerprint
+ instead of keyid for use with gpgsm.
+ (import_status_handler): Set a flag to know whether any import
+ occured.
+ (gpgme_op_import): Reurn -1 if no certificate ewas imported.
+
+2002-06-25 Werner Koch <wk@gnupg.org>
+
+ * engine-gpgsm.c (_gpgme_gpgsm_set_io_cbs) [ENABLE_GPGSM]: Fixed
+ function arguments.
+
+2002-06-25 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine-gpgsm.c (_gpgme_gpgsm_op_export): Only export the keys
+ listed in RECP.
+ * export.c (gpgme_op_export): If no data was returned, return
+ GPGME_No_Recipients.
+
+2002-06-25 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine-gpgsm.c (_gpgme_gpgsm_op_export): Implement.
+
+2002-06-21 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine-gpgsm.c (gpgsm_assuan_simple_command): Return ERR.
+ (parse_status): New function.
+ (gpgsm_status_handler): Use parse_status.
+ (gpgsm_assuan_simple_command): Accept new arguments STATUS_FNC and
+ STATUS_FNC_VALUE and process status messages.
+ (gpgsm_set_recipients): Pass new arugments to gpgsm_assuan_simple_command.
+ (gpgsm_set_fd): Likewise.
+ (_gpgme_gpgsm_op_keylist): Likewise.
+ (_gpgme_gpgsm_op_keylist_ext): Likewise.
+ (_gpgme_gpgsm_op_sign): Likewise.
+
+2002-06-21 Marcus Brinkmann <marcus@g10code.de>
+
+ * wait.c (_gpgme_remove_io_cb): Unlock FDT->lock.
+
+2002-06-20 Werner Koch <wk@gnupg.org>
+
+ * rungpg.c (build_argv): Ignore GPG_AGENT_INFO if set but empty.
+
+ * verify.c (calc_sig_summary): Set bad policy for wrong key usage.
+ (skip_token): New.
+ (_gpgme_verify_status_handler): Watch out for wrong key usage.
+ (gpgme_get_sig_string_attr): Hack to return info on the key
+ usage. Does now make use of the former RESERVED argument which
+ has been renamed to WHATIDX.
+ (gpgme_get_sig_ulong_attr): Renamed RESERVED to WHATIDX.
+
+2002-06-14 Marcus Brinkmann <marcus@g10code.de>
+
+ * wait.c (do_select): Return -1 on error, and 0 if nothing to run.
+ (_gpgme_wait_one): Only set HANG to zero if do_select returned an
+ error, or there are no more file descriptors to wait on.
+ (_gpgme_wait_on_condition): Ignore return value from do_select for
+ now.
+
+2002-06-13 Werner Koch <wk@gnupg.org>
+
+ * verify.c (gpgme_op_verify): Make sure that we never access an
+ unitialized result structure.
+
+2002-06-12 Werner Koch <wk@gnupg.org>
+
+ * keylist.c (struct keylist_result_s): New.
+ (_gpgme_release_keylist_result): Release it here
+ (keylist_status_handler): Handle truncated.
+ (append_xml_keylistinfo): New.
+ * gpgme.c (_gpgme_release_result): and use it here.
+ * types.h: Declare the new type here.
+ * context.h (struct gpgme_context_s): Use it here.
+
+2002-06-11 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine-gpgsm.c (_gpgme_gpgsm_release): Close status_cb.fd.
+ (_gpgme_gpgsm_new): Duplicate status file descriptor, so we can
+ use our own close notification mechanism without interfering with
+ assuan.
+
+2002-06-11 Werner Koch <wk@gnupg.org>
+
+ * gpgme.h: Add GPGME_ATTR_SIG_SUMMARY and the GPGME_SIGSUM_
+ constants.
+ * verify.c (calc_sig_summary): New.
+ (gpgme_get_sig_ulong_attr): And use it here.
+
+2002-06-10 Werner Koch <wk@gnupg.org>
+
+ * rungpg.h: Add new status codes TRUNCATED and ERROR.
+ * verify.c (is_token, copy_token): New.
+ (_gpgme_verify_status_handler): Use copy_token, handle the new
+ ERROR status and store the errorcode used withgpgsm and trust
+ status codes.
+ * gpgme.h: New attribute ERRTOK.
+ * key.c (gpgme_key_get_string_attr): Add dummy case for it.
+ (gpgme_get_sig_string_attr): Use it here to return the last error.
+
+2002-06-10 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine-gpgsm.c (_gpgme_gpgsm_start): Move the code that sets the
+ close notification for the status fd to ...
+ (_gpgme_gpgsm_new): ... here.
+ * wait.h: Include "sema.h". Remove prototypes of
+ _gpgme_remove_proc_from_wait_queue and
+ _gpgme_register_pipe_handler. Add prototypes of
+ _gpgme_fd_table_init, _gpgme_fd_table_deinit, _gpgme_fd_table_put,
+ _gpgme_add_io_cb, _gpgme_remove_io_cb, _gpgme_wait_event_cb and
+ _gpgme_wait_one..
+ * wait.c: Remove global variables PROC_QUEUE, PROC_QUEUE_LOCK,
+ FD_TABLE_SIZE, FD_TABLE, FD_TABLE_LOCK. New global variables
+ FDT_GLOBAL, CTX_DONE_LIST, CTX_DONE_LIST_SIZE,
+ CTX_DONE_LIST_LENGTH and CTX_DONE_LIST_LOCK. Remove struct
+ proc_s. Replace struct wait_item_s.
+ (_gpgme_fd_table_init): New function.
+ (_gpgme_fd_table_deinit): Likewise.
+ (_gpgme_fd_table_put): Likewise.
+ (set_process_done): Remove function.
+ (do_select): Take argument FDT. Use that to decide which fds to
+ select on.
+ (_gpgme_remove_proc_from_wait_queue): Remove function.
+ (_gpgme_wait_event_cb): New function.
+ (_gpgme_wait_one): Likewise.
+ (_gpgme_register_pipe_hanldler): Remove function.
+ (_gpgme_add_io_cb): New function.
+ (_gpgme_remove_io_cb): Likewise.
+ (_gpgme_freeze_fd): Remove function.
+ (_gpgme_thaw_fd): Remove function.
+ * rungpg.c (struct fd_data_map_s): Add new member TAG.
+ (struct gpg_object_s): Likewise for STATUS and COLON. Add member
+ IDX to CMD. Add new member IO_CBS.
+ (close_notify_handler): New variables POSSIBLY_DONE and NOT_DONE.
+ For each I/O callback, check if it should be unregistered. If all
+ callbacks have been unregistered, trigger GPGME_EVENT_DONE.
+ Remove member RUNNING.
+ (_gpgme_gpg_new): Initialize new members.
+ (_gpgme_gpg_release): Check PID not RUNNING. Don't call
+ _gpgme_remove_proc_from_wait_queue. Close GPG->CMD.FD if set.
+ (build_argv): Store away the index instead the file descriptor for
+ CMD.
+ (_gpgme_gpg_add_io_cb): New function.
+ (_gpgme_gpg_spawn): Use _gpgme_gpg_add_io_cb to register IO
+ callbacks.
+ (gpg_status_handler): Change return type to void, remove PID
+ argument, close filedescriptor if EOF or error occurs.
+ (read_status): Use _gpgme_gpg_add_io_cb instead _gpgme_thaw_fd.
+ Use IO_CBS->remove instead _gpgme_freeze_fd.
+ (gpg_colon_line_handler): Change return type to void, remove PID
+ argument, close filedescriptor if EOF or error occurs.
+ (command_cb): Use IO_CBS->remove instead _gpgme_freeze_fd.
+ (_gpgme_gpg_set_io_cbs): New function.
+ * rungpg.h (_gpgme_gpg_set_io_cbs): Prototype for
+ _gpgme_gpg_set_io_cbs.
+ * gpgme.h (GpgmeIOCb): New type.
+ (GpgmeRegisterIOCb): Likewise.
+ (GpgmeRemoveIOCb): Likewise.
+ (GpgmeEventIO): Likewise.
+ (GpgmeEventIOCb): Likewise.
+ (struct GpgmeIOCbs): New structure to hold I/O callbacks.
+ (gpgme_set_op_io_cbs): New prototype.
+ (gpgme_get_op_io_cbs): Likewise.
+ * ops.h: New prototype for _gpgme_op_event_cb. Remove prototypes
+ for _gpgme_freeze_fd and _gpgme_thaw_fd. Remove PID argument from
+ _gpgme_data_inbound_handler and _gpgme_data_outbound_handler
+ prototype. Add prototype for _gpgme_op_reset.
+ Add synchronous argument to _gpgme_decrypt_start prototype.
+ * io.h: Beautification.
+ * gpgme.c: Include "wait.h".
+ (gpgme_new): Initialize FDT.
+ (gpgme_set_io_cbs): New function.
+ (gpgme_get_io_cbs): Likewise.
+ (_gpgme_op_event_cb): Likewise.
+ * data.c (_gpgme_data_inbound_handler): Change return type to
+ void. Drop PID argument. Close FD on error and EOF.
+ (write_mem_data): Don't close FD here ...
+ (write_cb_data): ... or here ...
+ (_gpgme_data_outbound_handler): ... but here. Change return type
+ to void. Drop PID argument.
+ * context.h: Include "wait.h".
+ (struct gpgme_context_s): New members FDT and IO_CBS.
+ * op-support.c: New file.
+ * Makefile.am (libgpgme_la_SOURCES): Add op-support.c.
+ * ops.h: Add prototype for _gpgme_op_reset().
+ * decrypt.c (_gpgme_decrypt_start): New argument SYNCHRONOUS. Use
+ _gpgme_op_reset.
+ (gpgme_op_decrypt_start): Add synchronous argument.
+ (gpgme_op_decrypt): Likewise. Use _gpgme_wait_one instead
+ gpgme_wait.
+ * delete.c (gpgme_op_delete_start): Rename to ...
+ (_gpgme_op_delete_start): ... this. New argument SYNCHRONOUS.
+ Use _gpgme_op_reset. Make function static.
+ (gpgme_op_delete_start): Just a wrapper around
+ _gpgme_op_delete_start now.
+ (gpgme_op_delete): Add synchronous argument. Use _gpgme_wait_one
+ instead gpgme_wait.
+ * encrypt.c: Include "wait.h".
+ (ggpgme_op_encrypt_start): Rename to ...
+ (_gpgme_op_encrypt_start): ... this. New argument SYNCHRONOUS.
+ Use _gpgme_op_reset. Make function static.
+ (gpgme_op_encrypt_start): Just a wrapper around
+ _gpgme_op_encrypt_start now.
+ (gpgme_op_encrypt): Add synchronous argument. Use _gpgme_wait_one
+ instead gpgme_wait.
+ * encrypt_sign.c (gpgme_op_encrypt_sign_start): Rename to ...
+ (_gpgme_op_encrypt_sign_start): ... this. New argument
+ SYNCHRONOUS. Use _gpgme_op_reset. Make function static.
+ (gpgme_op_encrypt_sign_start): Just a wrapper around
+ _gpgme_op_encrypt_sign_start now.
+ (gpgme_op_encrypt_sign): Add synchronous argument. Use
+ _gpgme_wait_one instead gpgme_wait.
+ * export.c (gpgme_op_export_start): Rename to ...
+ (_gpgme_op_export_start): ... this. New argument SYNCHRONOUS.
+ Use _gpgme_op_reset. Make function static.
+ (gpgme_op_export_start): Just a wrapper around
+ _gpgme_op_export_start now.
+ (gpgme_op_export): Add synchronous argument. Use _gpgme_wait_one
+ instead gpgme_wait.
+ * genkey.c (gpgme_op_genkey_start): Rename to ...
+ (_gpgme_op_genkey_start): ... this. New argument SYNCHRONOUS.
+ Use _gpgme_op_reset. Make function static.
+ (gpgme_op_genkey_start): Just a wrapper around
+ _gpgme_op_genkey_start now.
+ (gpgme_op_genkey): Add synchronous argument. Use _gpgme_wait_one
+ instead gpgme_wait.
+ * import.c (gpgme_op_import_start): Rename to ...
+ (_gpgme_op_import_start): ... this. New argument SYNCHRONOUS.
+ Use _gpgme_op_reset. Make function static.
+ (gpgme_op_import_start): Just a wrapper around
+ _gpgme_op_import_start now.
+ (gpgme_op_import): Add synchronous argument. Use _gpgme_wait_one
+ instead gpgme_wait.
+ * keylist.c (gpgme_op_keylist_start): Use _gpgme_op_reset.
+ (gpgme_op_keylist_ext_start): Likewise.
+ * sign.c (gpgme_op_sign_start): Rename to ...
+ (_gpgme_op_sign_start): ... this. New argument SYNCHRONOUS. Use
+ _gpgme_op_reset. Make function static.
+ (gpgme_op_sign_start): Just a wrapper around _gpgme_op_sign_start
+ now.
+ (gpgme_op_sign): Add synchronous argument. Use _gpgme_wait_one
+ instead gpgme_wait.
+ * trustlist.c (gpgme_op_trustlist_start): Use _gpgme_op_reset.
+ * verify.c (gpgme_op_verify_start): Rename to ...
+ (_gpgme_op_verify_start): ... this. New argument SYNCHRONOUS.
+ Use _gpgme_op_reset. Make function static.
+ (gpgme_op_verify_start): Just a wrapper around
+ _gpgme_op_verify_start now.
+ (gpgme_op_verify): Add synchronous argument. Use _gpgme_wait_one
+ instead gpgme_wait.
+ * engine-gpgsm.c (iocb_data_t): New type.
+ (struct gpgsm_object_s): New member status_cb. Replace input_fd
+ and input_data with input_cb. Replace output_fd and output_data
+ with output_cb. Replace message_fd and message_data with
+ message_cb. New member io_cbs.
+ (_gpgme_gpgsm_new): Initialize all new members (and drop the old
+ ones).
+ (close_notify_handler): New variable POSSIBLY_DONE. For each I/O
+ callback, check if it should be unregistered. If all callbacks
+ have been unregistered, trigger GPGME_EVENT_DONE.
+ (_gpgme_gpgsm_release): Remove variable PID. Use new variable
+ names to close the file descriptors.
+ (_gpgme_gpgsm_op_decrypt): Use new variable names,
+ (_gpgme_gpgsm_op_encrypt): Likewise.
+ (_gpgme_gpgsm_op_genkey): Likewise.
+ (_gpgme_gpgsm_op_import): Likewise.
+ (_gpgme_gpgsm_op_keylist): Likewise.
+ (_gpgme_gpgsm_op_keylist_ext): Likewise.
+ (_gpgme_gpgsm_op_sign): Likewise.
+ (_gpgme_gpgsm_op_verify): Likewise.
+ (gpgsm_status_handler): Drop argument PID. Change return type to
+ void. Close status pipe before returning because of EOF or error.
+ (_gpgme_gpgsm_add_io_cb): New function.
+ (_gpgme_gpgsm_start): Use _gpgme_gpgsm_add_io_cb to register
+ callback function.
+ (_gpgme_gpgsm_set_io_cbs): New function.
+ * engine-gpgsm.h: New prototype for _gpgme_gpgsm_set_io_cbs.
+ * engine.c (_gpgme_engine_set_io_cbs): New function.
+ * engine.h: New prototype for _gpgme_engine_set_io_cbs.
+
+2002-06-04 Marcus Brinkmann <marcus@g10code.de>
+
+ * Makefile.am (libgpgme_la_SOURCES): Remove mutex.h.
+
+2002-06-03 Marcus Brinkmann <marcus@g10code.de>
+
+ * key.c: Include <ctype.h>.
+ (_gpgme_key_append_name): Skip one more char when
+ processing escaped char. Submitted by Marc Mutz <mutz@kde.org>.
+ Handle hexadecimal encodings. Also reported by Marc. Thanks!
+
+2002-06-02 Marcus Brinkmann <marcus@g10code.de>
+
+ * ath.h: Enable the _gpgme_ prefix. Fix all those prefix macros.
+ * posix-sema.c: Use that prefix here.
+ * posix-io.c: Include "ath.h".
+ (_gpgme_io_read): Use _gpgme_ath_read instead read.
+ (_gpgme_io_write): Use _gpgme_ath_write instead write.
+ (_gpgme_io_waitpid): Use _gpgme_ath_waitpid instead waitpid.
+ (_gpgme_io_select): Use _gpgme_ath_select instead select.
+
+2002-06-02 Marcus Brinkmann <marcus@g10code.de>
+
+ * Makefile.am (ath_components): New variable.
+ (ath_components_pthread): Likewise.
+ (ath_components_pth): Likewise.
+ (system_components): Add ath_componentes.
+
+ * ath.h: New file.
+ * ath.c: Likewise.
+ * ath-pthread.c: Likewise.
+ * ath-pth.c: Likewise.
+ * posix-sema.c (_gpgme_sema_cs_enter): Rework to use the ATH
+ interface.
+ * mutex.h: Remove file.
+
+2002-05-30 Werner Koch <wk@gnupg.org>
+
+ * key.c (gpgme_key_get_string_attr): Return NULL when asking for
+ an issuer with IDX > 0. We don't support altIssuerNames for now.
+
+2002-05-22 Werner Koch <wk@gnupg.org>
+
+ * engine-gpgsm.c (_gpgme_gpgsm_op_keylist_ext): Aehmm, added
+ missing variable definition. Oohh - Marcus was faster.
+
+2002-05-22 Marcus Brinkmann <marcus@gnu.org>
+
+ * engine-gpgsm.c (_gpgme_gpgsm_op_keylist_ext): Fix last change.
+
+2002-05-21 Werner Koch <wk@gnupg.org>
+
+ * engine-gpgsm.c (_gpgme_gpgsm_op_keylist)
+ (_gpgme_gpgsm_op_keylist_ext): Pass the keylist mode to gpgsm.
+
+2002-05-10 Werner Koch <wk@gnupg.org>
+
+ * key.h (gpgme_key_s): Add OTRUST.
+ * keylist.c (set_ownertrust): New.
+ (keylist_colon_handler): Get the ownertrust value
+ * key.c (gpgme_key_get_string_attr,gpgme_key_get_ulong_attr):
+ Return that value.
+
+2002-05-08 Marcus Brinkmann <marcus@g10code.de>
+
+ * w32-util.c: New static variable GET_PATH_LOCK.
+ (_gpgme_get_gpg_path): Remove superfluous NULL initializer.
+ Take lock while determining path.
+ (_gpgme_get_gpgsm_path): Likewise.
+ * version.c (do_subsystem_inits): Set DONE to 1 after
+ initialization.
+ (gpgme_get_engine_info): New variable ENGINE_INFO_LOCK. Take lock
+ while determining engine info.
+ * rungpg.c (_gpgme_gpg_get_version): New variable
+ GPG_VERSION_LOCK. Take the lock while determining the program
+ version.
+ * posix-io.c: Include "sema.h".
+ (_gpgme_io_spawn): New variable FIXED_SIGNALS_LOCK. Take the lock
+ while fixing the signals.
+ (_gpgme_io_select): Make READFDS and WRITEFDS non-static.
+ * key.c: Include "sema.h". New globals KEY_CACHE_LOCK and
+ KEY_REF_LOCK.
+ (capabilities_to_string): Make STRINGS very const.
+ (_gpgme_key_cache_add): Lock the key cache.
+ (_gpgme_key_cache_get): Likewise.
+ (gpgme_key_ref, gpgme_key_release): Lock the key_ref_lock.
+ * import.c (append_xml_impinfo): Make IMPORTED_FIELDS and
+ IMPORT_RES_FIELDS very const. Make FIELD and FIELD_NAME a litle
+ const.
+ * engine.c (_gpgme_engine_get_info): New variable
+ ENGINE_INFO_LOCK. Take lock while determining engine info.
+ * engine-gpgsm.c: Include "sema.h".
+ (_gpgme_gpgsm_get_version): New variable GPGSM_VERSION_LOCK. Take
+ lock while getting program version.
+
+2002-05-08 Marcus Brinkmann <marcus@g10code.de>
+
+ * debug.h: New file.
+ * Makefile.am (libgpgme_la_SOURCES): Add debug.h.
+ * util.h: Removed all prototypes and declarations related to
+ debugging. Include "debug.h".
+
+ * debug.c (debug_level): Comment variable and remove superfluous
+ zero initializer.
+ (errfp): Likewise.
+ (_gpgme_debug_enabled): Function removed.
+ (struct debug_control_s): Definition removed.
+ (_gpgme_debug_level): Function removed.
+ (_gpgme_debug_begin): Rewritten to use vasprintf. Accept a
+ pritnf-style format specification and a variable number of
+ arguments.
+ (_gpgme_debug_add): Rewritten using vasprintf. Expect that format
+ starts out with "%s" for simplicity.
+ (_gpgme_debug_end): Rewritten using vasprintf. Do not accept a
+ TEXT argument anymore.
+
+ * posix-io.c (_gpgme_io_select): Use new level argument for
+ DEBUG_BEGIN instead explicit if construct.
+
+ * debug.c (debug_init): Remove superfluous zero initializer,
+ remove volatile flag of INITIALIZED. Do not use the
+ double-checked locking algorithm, it is fundamentally flawed and
+ will empty your fridge (on a more serious note, despite the
+ volatile flag it doesn't give you the guarantee you would expect,
+ for example on a DEC Alpha or an SMP machine. The volatile only
+ serializes accesses to the volatile variable, but not to the other
+ variables).
+
+2002-05-03 Werner Koch <wk@gnupg.org>
+
+ * engine-gpgsm.c (_gpgme_gpgsm_new): Redirect any gpgsm error
+ output to /dev/null.
+
+ * verify.c (gpgme_get_sig_key): Set the protocol of the listctx.
+ * gpgme.c (gpgme_get_protocol): New.
+
+ * data.c (gpgme_data_write): Changed type of BUFFER to void*.
+ (gpgme_data_read): Ditto.
+
+ * verify.c (_gpgme_verify_status_handler): Handle TRUST_* status
+ lines so that a claim can be made without looking up the key.
+ (gpgme_get_sig_string_attr): New.
+ (gpgme_get_sig_ulong_attr): New.
+
+ * gpgme.h (GpgmeAttr): Added GPGME_ATTR_SIG_STATUS.
+
+ * rungpg.h: Add new status codes from gpg 1.0.7 and formatted the
+ list to align with the status.h file from gnupg.
+
+ * gpgme.h (GpgmeSigStat): Add _GOOD_EXP and _GOOD_EXPKEY.
+ * verify.c (_gpgme_verify_status_handler, finish_sig): Handle
+ these new status codes. Store the expiration time
+
+2002-04-27 Werner Koch <wk@gnupg.org>
+
+ * gpgme.h (GpgmeData_Encoding): New.
+ * data.c (gpgme_data_get_encoding,gpgme_data_set_encoding): New.
+ * engine-gpgsm.c (map_input_enc): New. Use it in all local
+ functions where the INPUT command gets send.
+
+2002-04-27 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine-gpgsm.c (_gpgme_gpgsm_op_verify): Close the output
+ descriptor only when we don't need it anymore. Close the message
+ descriptor if we don't need it.
+
+2002-04-26 Werner Koch <wk@gnupg.org>
+
+ * Makefile.am (libgpgme_la_LIBADD): Use libtool libraries.
+
+2002-04-25 Marcus Brinkmann <marcus@g10code.de>
+
+ * rungpg.c (_gpgme_gpg_release): Call gpgme_data_release on
+ GPG->cmd.cb_data, not xfree.
+
+2002-04-25 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine-gpgsm.c (_gpgme_gpgsm_new): Set the display, ttyname,
+ ttytype, lc_ctype and lc_messages options in the server.
+
+2002-04-24 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine-gpgsm.c (map_assuan_error): Add new error codes.
+
+2002-04-23 Werner Koch <wk@gnupg.org>
+
+ * key.c (gpgme_key_get_ulong_attr): Swapped use of can_encrypt and
+ can_certify to return the requested values.
+
+2002-04-23 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.c (gpgme_get_progress_cb): Allow either return parameter
+ to be NULL.
+ (gpgme_get_passphrase_cb): Likewise.
+
+2002-04-22 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.c (gpgme_get_passphrase_cb): New function.
+ (gpgme_get_progress_cb): New function.
+ * gpgme.h: Add new prototypes for gpgme_get_passphrase_cb and
+ gpgme_get_progress_cb.
+
+2002-03-28 Werner Koch <wk@gnupg.org>
+
+ * gpgme.h (GpgmeAttr): Add values for issuer and chaining.
+ * key.h (gpgme_key_s): Add issuer and chaining elements for X509.
+ * keylist.c (keylist_colon_handler): Store them.
+ * key.c (gpgme_key_release): Free them.
+ (gpgme_key_get_as_xml,gpgme_key_get_string_attr): Print them.
+
+2002-03-26 Werner Koch <wk@gnupg.org>
+
+ * Makefile.am (libgpgme_la_SOURCES): Add mutex.h
+
+2002-03-21 Werner Koch <wk@gnupg.org>
+
+ * util.h [!HAVE_FOPENCOOKIE]: Make sure off_t and ssize_t are
+ defined.
+
+2002-03-18 Marcus Brinkmann <marcus@g10code.de>
+
+ * Makefile.am (system_components): New variable, set depending on
+ HAVE_DOSISH_SYSTEM.
+ (libgpgme_la_SOURCES): Use system_components. Remove `syshdr.h'.
+ * syshdr.h: File removed.
+
+ * posix-io.c: Remove !HAVE_DOSISH_SYSTEM safeguard. Clean up source.
+ * posix-sema.c: Likewise.
+ * posix-util.c: Likewise.
+
+ * w32-io.c: Remove HAVE_DOSISH_SYSTEM safeguard.
+ * w32-sema.c: Likewise.
+ * w32-util.c: Likewise.
+
+ * posix-io.c: Include `unistd.h', do not include `syshdr.h'.
+ * posix-sema.c: Likewise.
+ * w32-io.c: Include `io.h', do not include `syshdr.h'
+ * w32-sema.c: Likewise.
+ * w32-util.c: Likewise.
+ * data.c: Do not include `syshdr.h'.
+ * wait.c: Likewise.
+ * wait.h: Code cleanup.
+
+ * mutex.h: New file.
+ * posix-sema.c: Implement.
+
+2002-03-08 Werner Koch <wk@gnupg.org>
+
+ * util.h [!HAVE_FOPENCOOKIE]: Fixed type. Thanks to Frank Heckenbach.
+
+2002-03-07 Werner Koch <wk@gnupg.org>
+
+ * gpgme.h (gpgme_op_keylist_ext_start): Add prototype.
+
+2002-03-06 Marcus Brinkmann <marcus@g10code.de>
+
+ * encrypt.c (_gpgme_encrypt_sym_status_handler): New function.
+ (gpgme_op_encrypt_start): New variable SYMMETRIC, set it if RECP
+ is null, and if it is set, use _gpgme_encrypt_sym_status_handler
+ as status handler and run _gpgme_passphrase_start.
+ * rungpg.c (_gpgme_gpg_op_encrypt): If RECP is zero, do symmetric
+ encryption.
+ * engine-gpgsm.c (_gpgme_gpgsm_op_encrypt): If RECP is zero,
+ return error value.
+
+ * rungpg.c (_gpgme_gpg_op_verify): Add "--" argument.
+
+2002-03-03 Marcus Brinkmann <marcus@g10code.de>
+
+ * passphrase.c (_gpgme_passphrase_status_handler): Also set the
+ error No_Passphrase if only a bad passphrase was provided.
+
+2002-03-03 Marcus Brinkmann <marcus@g10code.de>
+
+ * rungpg.c (_gpgme_gpg_op_verify): If TEXT is of mode
+ GPGME_DATA_MODE_IN, construct a command line that stores the
+ plaintext in TEXT.
+ * verify.c (gpgme_op_verify_start): Accept TEXT being
+ uninitialized, and in this case interpret SIG as a normal or
+ cleartext signature and TEXT as a return data object.
+ * engine-gpgsm.c (_gpgme_gpgsm_op_verify): Likewise.
+
+2002-03-03 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine-gpgsm.c (_gpgme_gpgsm_op_keylist_ext) [!ENABLE_GPGSM]:
+ Add stub function.
+
+2002-02-28 Werner Koch <wk@gnupg.org>
+
+ * key.h (subkey_s): New member expires_at.
+ * keylist.c (keylist_colon_handler): Set it here
+ * key.c (gpgme_key_get_as_xml,gpgme_key_get_ulong_attr): Return it.
+
+2002-02-27 Marcus Brinkmann <marcus@g10code.de>
+
+ * rungpg.h (_gpgme_gpg_op_keylist_ext): New prototype.
+ * rungpg.c (_gpgme_gpg_op_keylist_ext): New function.
+ * engine-gpgsm.h (_gpgme_gpgsm_op_keylist_ext): New prototype.
+ * engine-gpgsm.c (_gpgme_gpgsm_op_keylist_ext): New function.
+ * engine.h (_gpgme_engine_op_keylist_ext): New prototype.
+ * engine.c (_gpgme_engine_op_keylist_ext): New function.
+ * keylist.c (gpgme_op_keylist_ext_start): New function.
+
+2002-02-27 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.h: Add new error code GPGME_Invalid_Recipient.
+ * encrypt.c (struct encrypt_result_s): New member invalid_recipients,
+ rename no_recipients to no_valid_recipients.
+ (_gpgme_encrypt_status_handler): Include error for invalid
+ recipients.
+ * engine-gpgsm.c (gpgsm_set_recipients): Change type of first
+ argument to GpgsmObject. Use that to report back the status about
+ the recipients.
+
+2002-02-26 Marcus Brinkmann <marcus@g10code.de>
+
+ * verify.c (_gpgme_verify_status_handler): Fix the last change.
+
+2002-02-25 Marcus Brinkmann <marcus@g10code.de>
+
+ * verify.c (_gpgme_verify_status_handler): Parse the args line to
+ see if the problem is due to a missing key, and report that back
+ to the user.
+
+2002-02-25 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine.c (_gpgme_engine_op_encrypt_sign): New function.
+ * engine.h (_gpgme_engine_op_encrypt_sign): New prototype.
+ * rungpg.c (_gpgme_append_gpg_args_from_signers): New function.
+ (_gpgme_gpg_op_sign): Use that new function.
+ (_gpgme_gpg_op_encrypt_sign): New function.
+ * rungpg.h (_gpgme_gpg_op_encrypt_sign): New prototype.
+ * gpgme.h (gpgme_op_encrypt_sign_start): New prototype.
+ (gpgme_op_encrypt_sign): Likewise.
+ * Makefile.am (libgpgme_la_SOURCES): Add encrypt-sign.c.
+ * ops.h (_gpgme_encrypt_status_handler): Add prototype.
+ (_gpgme_sign_status_handler): Add prototype.
+ * sign.c (sign_status_handler): Rename to ...
+ (_gpgme_sign_status_handler): ... this and make non-static.
+ * encrypt.c (encrypt_status_handler): Rename to ...
+ (_gpgme_encrypt_status_handler): ... this and make non-static.
+ * encrypt.c (gpgme_op_encrypt_start): Use new status handler name.
+ * sign.c (gpgme_op_sign_start): Likewise.
+
+2002-02-25 Marcus Brinkmann <marcus@g10code.de>
+
+ * context.h (struct gpgme_context_s): New member include_certs.
+ * gpgme.h (gpgme_set_include_certs): Add prototype.
+ (gpgme_get_include_certs): Likewise.
+ * gpgme.c (gpgme_set_include_certs): New function.
+ (gpgme_get_include_certs): Likewise.
+ (gpgme_new): Set include_certs to 1 (the default).
+ * engine.c (_gpgme_engine_op_sign): Accept new argument include_certs,
+ and pass it to _gpgme_gpgsm_op_sign.
+ * engine.h (_gpgme_engine_op_sign): Likewise for prototype.
+ * engine-gpgsm.c (_gpgme_gpgsm_op_sign): Accept new argument
+ include_certs and handle it.
+ * engine-gpgsm.h (_gpgme_gpgsm_start): Add new argument include_certs.
+ * sign.c (gpgme_op_sign_start): Add new argument to
+ _gpgme_engine_op_sign call.
+
+2002-02-14 Werner Koch <wk@gnupg.org>
+
+ * keylist.c (gpgme_op_keylist_start): Do not use a verbose listing.
+
+2002-02-13 Werner Koch <wk@gnupg.org>
+
+ * vasprintf.c, fopencookie.c: Add replacement functions.
+ * util.h: Add prototypes for them.
+
+2002-02-09 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine-gpgsm.c (gpgsm_assuan_simple_command): Return 0 if we
+ reach the end of the function.
+
+2002-02-09 Marcus Brinkmann <marcus@g10code.de>
+
+ * genkey.c (gpgme_op_genkey_start): Fix logic in validity check.
+ (gpgme_op_genkey_start): Skip newlines after opening tag.
+
+ * engine-gpgsm.c (_gpgme_gpgsm_start): Remove cruft.
+
+2002-02-08 Marcus Brinkmann <marcus@g10code.de>
+
+ * genkey.c (gpgme_op_genkey_start): Allow PUBKEY and SECKEY to be
+ set, and pass them down to the crypto engine.
+ * engine-gpgsm.h (_gpgme_gpgsm_start): New arguments PUBKEY and SECKEY.
+ * engine.h: Likewise.
+ * rungpg.h (_gpgme_gpg_spawn): Likewise.
+ * engine.c (_gpgme_engine_op_genkey): Likewise. Use those
+ arguments.
+ * rungpg.c (_gpgme_gpg_op_genkey): Likewise. Complain if those
+ arguments are set.
+ * engine-gpgsm.c (_gpgme_gpgsm_op_genkey): Likewise. Implement
+ function.
+
+ * engine-gpgsm.c (_gpgme_gpgsm_op_keylist): Beautify comment.
+
+2002-02-06 Marcus Brinkmann <marcus@g10code.de>
+
+ * rungpg.c (_gpgme_gpg_op_keylist): Remove handling of keylist
+ mode (for now).
+
+2002-02-06 Marcus Brinkmann <marcus@g10code.de>
+
+ * wait.c (gpgme_wait): Add new argument STATUS, in which the
+ status of the returned context is returned.
+ (_gpgme_wait_on_condition): Rework the function a bit, to make it
+ aware of cancelled processes, and to allow to use gpgme_wait with
+ CTX being NULL (as documented in the source).
+ (struct proc_s): New member REPORTED.
+ * gpgme.h: Fix prototype.
+ * verify.c (gpgme_op_verify): Fix use of gpgme_wait.
+ * sign.c (gpgme_op_sign): Likewise.
+ * import.c (gpgme_op_import): Likewise.
+ * genkey.c (gpgme_op_genkey): Likewise.
+ * export.c (gpgme_op_export): Likewise.
+ * encrypt.c (gpgme_op_encrypt): Likewise.
+ * delete.c (gpgme_op_delete): Likewise.
+ * decrypt-verify.c (gpgme_op_decrypt_verify): Likewise.
+
+2002-02-06 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.c (gpgme_set_keylist_mode): Possibly return an error
+ value.
+ (gpgme_get_keylist_mode): New function.
+ (gpgme_new): Set the default for keylist_mode member of CTX.
+
+ * gpgme.h (gpgme_set_keylist_mode): Fix prototype.
+ (gpgme_get_keylist_mode): New prototype.
+ (GPGME_KEYLIST_MODE_LOCAL): New macro.
+ (GPGME_KEYLIST_MODE_EXTERN): Likewise..
+
+2002-02-02 Marcus Brinkmann <marcus@g10code.de>
+
+ This patch has gotten a bit large... mmh. The main thing that
+ happens here is that error values are now not determined in the
+ operation function after gpgme_wait completed, but in the status
+ handler when EOF is received. It should always be the case that
+ either an error is flagged or EOF is received, so that after a
+ gpgme_wait you should never have the situation that no error is
+ flagged and EOF is not received. One problem is that the engine
+ status handlers don't have access to the context, a horrible
+ kludge works around this for now. All errors that happen during a
+ pending operation should be catched and reported in ctx->error,
+ including out-of-core and cancellation. This rounds up neatly a
+ couple of loose ends, and makes it possible to pass up any errors
+ in the communication with the backend as well. As a bonus, there
+ will be a function to access gpgme->wait, so that the operations
+ can truly be implemented with their _start function.
+
+ * engine-gpgsm.c (gpgsm_status_handler): Horrible kludge to report
+ error back to the context.
+ * rungpg.c (gpg_status_handler): Same horrible kludge applied here.
+
+ * engine-gpgsm.c (gpgsm_assuan_simple_command): Add error checking.
+
+ * wait.c (_gpgme_wait_on_condition): If canceled, set CTX->error
+ to a value indication that.
+
+ * verify.c (add_notation): Set error, not out_of_core.
+ (finish_sig): Likewise.
+ (gpgme_op_verify_start): Don't clear out_of_core.
+ (_gpgme_verify_status_handler): At EOF, clean up the notation data.
+ (gpgme_op_verify): And don't do it here.
+
+ * trustlist.c (trustlist_status_handler): Check error, not out_of_core.
+ (gpgme_op_trustlist_start): Don't clear out_of_core.
+ (gpgme_op_trustlist_next): Check error, not out_of_core.
+ (gpgme_op_trustlist_end): Likewise.
+
+ * ops.h (test_and_allocate_result): New macro.
+ (_gpgme_passphrase_result): Remove prototype.
+ * delete.c (gpgme_op_delete): Return error from context.
+ (delete_status_handler): Use macro test_and_allocate_result.
+ Perform error checking at EOF.
+ (gpgme_op_delete_start): Release result.
+ * passphrase.c (_gpgme_passphrase_status_handler): Use macro
+ test_and_allocate_result, and perform error checking here.
+ (_gpgme_passphrase_result): Function removed.
+ * sign.c (gpgme_op_sign_start): Do not set out_of_core to zero.
+ (gpgme_op_sign): Just return the error value from the context.
+ (sign_status_handler): Only progress if no error is set yet. If
+ we process an EOF, set the resulting error value (if any).
+ * decrypt.c (_gpgme_decrypt_result): Function removed.
+ (create_result_struct): Function removed.
+ (_gpgme_decrypt_status_handler): Use macro test_and_allocate_result,
+ caclulate error on EOF, do not progress with errors.
+ (_gpgme_decrypt_start): Do not set out_of_core to zero.
+ (gpgme_op_decrypt): Just return the error value from the context.
+ * encrypt.c (encrypt_status_handler): Perform the error checking
+ here.
+ (gpgme_op_encrypt_start): Do not clear out_of_core.
+ * export.c (export_status_handler): Return if error is set in context.
+ (gpgme_op_export_start): Release result.
+ (gpgme_op_export): Return error from context.
+ * decrypt-verify.c (gpgme_op_decrypt_verify): Return the error in
+ the context.
+ * genkey.c (genkey_status_handler): Use macro
+ test_and_allocate_result. Perform error checking at EOF.
+ (gpgme_op_genkey): Just return the error from context.
+ * import.c (gpgme_op_import): Return the error from context.
+ (import_status_handler): Use macro test_and_allocate_result.
+ * keylist.c (gpgme_op_keylist_start): Do not clear out_of_core.
+ (gpgme_op_keylist_next): Return error of context.
+ (keylist_colon_handler): Set error instead out_of_code.
+ (finish_key): Likewise.
+
+ * context.h: Remove member out_of_core, add member error.
+ * gpgme.c (_gpgme_release_result): Clear error flag.
+
+ * engine.h (_gpgme_engine_get_error): New prototype.
+ * engine.c (_gpgme_engine_get_error): New function.
+ * engine-gpgsm.c (_gpgme_gpgsm_get_error): New function.
+
+ * engine-gpgsm.c (map_assuan_error): New function.
+ (gpgsm_assuan_simple_command): Change return type to GpgmeError,
+ use the new function to map error values.
+ (gpgsm_set_fd): Change return type tp GpgmeError.
+ (_gpgme_gpgsm_op_decrypt): Change type of ERR to GpgmeError.
+ (gpgsm_set_recipients): Likewise. Change type of return value
+ equivalently. Adjust error values.
+ (_gpgme_gpgsm_op_import): Likewise.
+ (_gpgme_gpgsm_op_sign): Likewise.
+ (struct gpgsm_object_s): New member error.
+ (gpgsm_status_handler): Set error if error occurs. Determine
+ error number from ERR line received. If assuan_read_line fails,
+ terminate the connection.
+
+2002-02-01 Marcus Brinkmann <marcus@g10code.de>
+
+ * Makefile.am (MOSTLYCLEANFILES): New variable.
+
+2002-02-01 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine-gpgsm.c (gpgsm_status_handler): At error, terminate the
+ connection to the server.
+
+2002-01-31 Marcus Brinkmann <marcus@g10code.de>
+
+ * rungpg.h: Add STATUS_KEY_CREATED.
+
+ * progress.c: New file.
+ * Makefile.am (libgpgme_la_SOURCES): Add progress.c.
+
+ * genkey.c (genkey_status_handler): Use
+ _gpgme_progress_status_handler. Add check for status.
+ (struct genkey_result_s): New structure.
+ (_gpgme_release_genkey_result): New function.
+ (gpgme_op_genkey): Check for error.
+ * gpgme.c (_gpgme_release_result): Call
+ _gpgme_release_genkey_result.
+ * ops.h (_gpgme_release_genkey_result): Add prototype.
+ * types.h (GenKeyResult): New type.
+ * context.h (gpgme_context_s): Add GenKeyResult to member result.
+
+2002-01-30 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.c (_gpgme_release_result): Call
+ _gpgme_release_delete_result.
+ * ops.h (_gpgme_release_delete_result): Add prototype.
+ * types.h (DeleteResult): New type.
+ * context.h (gpgme_context_s): Add DeleteResult to member result.
+
+ * delete.c (enum delete_problem): New type.
+ (struct delete_result_s): New structure.
+ (_gpgme_release_delete_result): New function.
+ (delete_status_handler): Implement more status codes.
+ (gpgme_op_delete): Return error on failure.
+
+ * import.c (MAX_IMPORTED_FIELDS): Bump up to 14.
+
+2002-01-30 Marcus Brinkmann <marcus@g10code.de>
+
+ * import.c (struct import_result_s): New structure.
+ (_gpgme_release_import_result): New function.
+ (append_xml_impinfo): Likewise.
+ (import_status_handler): Implement.
+ * gpgme.c (_gpgme_release_result): Add call to
+ _gpgme_release_import_result.
+ * ops.h (_gpgme_release_import_result): Add prototype.
+ * types.h (ImportResult): New type.
+ * context.h (gpgme_context_s): Add ImportResult to member result.
+
+ * encrypt.c (gpgme_op_encrypt): Code clean up.
+
+2002-01-30 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.h: Add lots of comment and fix the formatting. Add
+ gpgme_trustlist_end prototype.
+
+2002-01-29 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.h: Add new type GpgmeIdleFunc. Change type of
+ gpgme_register_idle to return and accept this type.
+ * wait.c (gpgme_register_idle): Fix type.
+ Save and return old value of idle_function.
+
+2002-01-29 Werner Koch <wk@gnupg.org>
+
+ * engine-gpgsm.c (_gpgme_gpgsm_op_keylist): Implement secret only mode.
+
+ * keylist.c (keylist_colon_handler): Add support for the new "crs"
+ record type.
+
+2002-01-22 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine-gpgsm.c (_gpgme_gpgsm_release): Call assuan_disconnect,
+ not assuan_pipe_disconnect.
+
+ * Makefile.am (libgpgme_la_LIBADD): Change to link assuan and
+ jnlib (needed by assuan) statically into libgpgme. Linking a
+ static library into a shared library this way is not portable.
+
+2002-01-22 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.h (GpgmePassphraseCb): Change type of R_HD from void* to
+ void**.
+
+2002-01-22 Marcus Brinkmann <marcus@g10code.de>
+
+ * data.c (gpgme_data_new_from_filepart): Change type of LENGTH
+ from off_t to size_t.
+ * gpgme.h: Likewise.
+
+2002-01-22 Marcus Brinkmann <marcus@g10code.de>
+
+ * wait.c (_gpgme_wait_on_condition): If the process finished,
+ reset the pending flag. Also if the operation was cancelled.
+
+ (struct proc_s): Rename READY to DONE.
+ (wait_item_s): Likewise.
+ (set_process_ready): Rename to ...
+ (set_process_done): ... this.
+ (_gpgme_remove_proc_from_wait_queue): Call set_process_done
+ instead set_process_ready.
+ (_gpgme_wait_on_condition): Likewise.
+ (do_select): Rename READY to DONE.
+
+ * verify.c (gpgme_op_verify): Do not set pending to zero here.
+ * sign.c (gpgme_op_sign): Likewise.
+ * import.c (gpgme_op_import): Likewise.
+ * genkey.c (gpgme_op_genkey): Likewise.
+ * export.c (gpgme_op_export): Likewise.
+ * encrypt.c (gpgme_op_encrypt): Likewise.
+ * delete.c (gpgme_op_delete): Likewise.
+ * decrypt-verify.c (gpgme_op_decrypt_verify): Likewise.
+ * decrypt.c (gpgme_op_decrypt): Likewise.
+
+2002-01-22 Marcus Brinkmann <marcus@g10code.de>
+
+ * export.c: Cleanup.
+
+2002-01-15 Marcus Brinkmann <marcus@g10code.de>
+
+ * trustlist.c: Various source clean ups.
+ (my_isdigit): Removed.
+ (gpgme_op_trustlist_end): New function.
+
+2002-01-13 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.c: Various source clean ups, like renaming C to CTX where
+ appropriate.
+ (gpgme_new): Clear R_CTX before starting the work.
+ (my_isdigit): Removed.
+ (my_isxdigit): Likewise.
+
+ * data.c: Various source clean ups.
+ (gpgme_data_new_from_mem): Check BUFFER after clearing R_DH.
+ (gpgme_data_new_with_read_cb): Similar for READ_CB.
+ (gpgme_data_new_from_file): Loop over fread while EINTR.
+ (gpgme_data_new_from_filepart): Rediddled a bit. Allow LENGTH to
+ be zero. Loop over fread while EINTR.
+
+ (my_isdigit): Removed.
+ (my_isxdigit): Likewise.
+
+2001-12-21 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine-gpgsm.c (_gpgme_gpgsm_new): Replace General_Error with
+ Pipe_Error where appropriate.
+
+2001-12-19 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine.c: Include `string.h'. Reported by Stéphane Corthésy.
+
+ * version.c (get_engine_info): Remove prototype.
+
+2001-12-19 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine-gpgsm.c (_gpgme_gpgsm_new): New variable CHILD_FDS.
+ Fill it with the servers fds, and pass it to assuan_pipe_connect.
+
+2001-12-18 Marcus Brinkmann <marcus@g10code.de>
+
+ * keylist.c (gpgme_op_keylist_end): New function.
+ * gpgme.h (gpgme_op_keylist_end): New prototype.
+
+ * engine.h (gpgme_engine_check_version): Move prototype to ...
+ * gpgme.h (gpgme_engine_check_version): ... here.
+
+ * genkey.c (gpgme_op_genkey_start): Remove unused variable.
+
+2001-12-18 Marcus Brinkmann <marcus@g10code.de>
+
+ * version.c (gpgme_get_engine_info): Reimplemented.
+ (gpgme_check_engine): Reimplemented.
+ (_gpgme_compare_versions): Return NULL if MY_VERSION is NULL.
+
+ * engine.c: Include `io.h'.
+ (gpgme_engine_get_info): New function.
+ * engine.h (gpgme_engine_check_version, _gpgme_engine_get_info):
+ Add prototype.
+
+2001-12-18 Marcus Brinkmann <marcus@g10code.de>
+
+ * rungpg.c (struct reap_s, reap_list, reap_list_lock): Moved to ...
+ * engine.c (struct reap_s, reap_list, reap_list_lock): ... here.
+ Include `time.h', `sys/types.h', `assert.h', and `sema.h'.
+
+ * rungpg.c (_gpgme_engine_add_child_to_reap_list): New function.
+ (do_reaping, _gpgme_gpg_housecleaning): Moved to ...
+ * engine.c (do_reaping, _gpgme_engine_housecleaning): ... here.
+ * rungpg.c (_gpgme_gpg_release): Replace code that is now in its
+ own function by call to _gpgme_engine_add_child_to_reap_list().
+
+ * wait.c: Include `engine.h'.
+ (run_idle): Call _gpgme_engine_housecleaning(), not
+ _gpgme_gpg_housecleaning().
+
+2001-12-18 Marcus Brinkmann <marcus@g10code.de>
+
+ * key.c (_gpgme_key_append_name): Append, not prepend, the uid.
+ Initialize the next field of the uid structure.
+ (gpgme_key_get_as_xml): Do not list last uid first.
+
+2001-12-17 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine-gpgsm.c (_gpgme_gpgsm_set_colon_line_handler): New
+ function [!ENABLE_GPGSM].
+
+2001-12-14 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine-gpgsm.c (_gpgme_gpgsm_op_verify): Put TEXT into
+ message_data, not SIG.
+ (_gpgme_gpgsm_op_sign): Use `--detached', not `--detach'.
+
+ * sign.c (sign_status_handler): Call
+ _gpgme_passphrase_status_handler early.
+
+2001-12-14 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine-gpgsm.c: Revert last change.
+
+2001-12-14 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine-gpgsm.c (gpgsm_status_handler): Freeze the output file
+ handler when ending this operation, otherwise the wait function
+ will sit on it.
+
+2001-12-14 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine-gpgsm.c (struct gpgsm_object_s): New member colon.attic.
+ (_gpgme_gpgsm_new): Initialize some more members.
+ (_gpgme_gpgsm_release): Free the colon line handler's attic line.
+ (gpgsm_status_handler): Rework the inline-data processing.
+
+2001-12-13 Marcus Brinkmann <marcus@g10code.de>
+
+ * rungpg.c (_gpgme_gpg_spawn): Do not add the fds to the child
+ list that are not dup'ed, for those the close-on-exec flag is set
+ now.
+ * version.c (_gpgme_get_program_version): Remove first entry in
+ CFD, as the close-on-exec flag is now set for this fd.
+
+2001-12-13 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine-gpgsm.c (_gpgme_gpgsm_op_encrypt): Do not add `armor'
+ option to `ENCRYPT'.
+ * engine-gpgsm.c (gpgsm_set_recipients): Free LINE when returning
+ successfully.
+
+2001-12-13 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine-gpgsm.c (close_notify_handler): New function.
+ (_gpgme_gpgsm_new): Manage the file descriptors a
+ bit differently. Do not set close-on-exec flags.
+ (_gpgme_gpgsm_op_decrypt): Do not set message_fd
+ to -1, this is done by the close handler.
+ (_gpgme_gpgsm_op_encrypt): Likewise.
+ (_gpgme_gpgsm_op_import): Likewise (also for output_fd).
+ (_gpgme_gpgsm_op_keylist): Likewise (also for input_fd and output_fd).
+ (_gpgme_gpgsm_op_sign): Likewise.
+ (_gpgme_gpgsm_op_verify): Likewise, but for output_fd.
+
+ * posix-io.c (_gpgme_io_pipe): Set the close-on-exec flag for the
+ non-inherited file descriptor index of the pipe.
+
+2001-12-13 Werner Koch <wk@gnupg.org>
+
+ * engine-gpgsm.c (_gpgme_gpgsm_set_colon_line_handler): New.
+ (gpgsm_status_handler): Pass datalines to a colon handler
+ * engine.c (_gpgme_engine_set_colon_line_handler): Set the colon
+ handler for gpgsm.
+
+ * engine-gpgsm.c (_gpgme_gpgsm_op_keylist): Allow NULL for
+ pattern.
+ (gpgsm_assuan_simple_command): Removed underscore from
+ assuan_write_line.
+ (_gpgme_gpgsm_start): Ditto.
+ (gpgsm_assuan_simple_command): Replaced interal Assuan read
+ function by the new assuan_read_line. Removed the use of the
+ internal header.
+ (gpgsm_status_handler): Ditto. Use the new assuan_pending_line.
+ (_gpgme_gpgsm_start): Use the documented way to get an fd from
+ assuan.
+
+ * keylist.c (keylist_colon_handler): Handle "crt" records
+ * key.h (gpgme_key_s): Add an x509 flag.
+ * key.c (parse_x509_user_id): New.
+ (_gpgme_key_append_name): Handle x.509 names.
+
+2001-12-05 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine-gpgsm.c (gpgsm_status_handler): Make it work with current
+ version of assuan.
+
+2001-12-05 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine-gpgsm.c (gpgsm_set_fd): Accept one more argument OPT.
+ (_gpgme_gpgsm_op_encrypt): Pass armor argument to gpgsm_set_fd for
+ output descriptor.
+ (_gpgme_gpgsm_op_sign): Likewise.
+
+2001-12-05 Marcus Brinkmann <marcus@g10code.de>
+
+ * keylist.c (gpgme_op_keylist_next): Set pending to 0 if EOF
+ occurs.
+
+2001-11-26 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine-gpgsm.c (_gpgme_gpgsm_op_sign): Fix stupid typo.
+
+2001-11-24 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine-gpgsm.c (gpgsm_status_handler): Don't break if bsearch fails.
+ Deal with assuan read line returning more than one line (for now).
+
+2001-11-23 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine-gpgsm.c (_gpgme_gpgsm_op_sign): Implement it according to
+ the current protocol definition.
+
+2001-11-23 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine-gpgsm.c (_gpgme_gpgsm_new): Set CLOEXEC flag for parent
+ ends of the pipe.
+
+2001-11-22 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine-gpgsm.c: Include stdlib.h and string.h. Also include,
+ for now, rungpg.h and status-table.h.
+ (gpgsm_status_handler): Implement more of the status handler.
+
+2001-11-22 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine.c (_gpgme_engine_op_decrypt): Implement CMS case.
+ (_gpgme_engine_op_delete): Likewise.
+ (_gpgme_engine_op_encrypt): Likewise.
+ (_gpgme_engine_op_export): Likewise.
+ (_gpgme_engine_op_genkey): Likewise.
+ (_gpgme_engine_op_keylist): Likewise.
+ (_gpgme_engine_op_sign): Likewise.
+ (_gpgme_engine_op_trustlist): Likewise.
+
+ * engine-gpgsm.c (_gpgme_gpgsm_op_encrypt): New function.
+ (gpgsm_assuan_simple_command): Likewise.
+ (gpgsm_set_recipients): Likewise.
+ (gpgsm_set_fd): Reimplement using gpgsm_assuan_simple_command.
+ (_gpgme_gpgsm_op_delete): New function.
+ (_gpgme_gpgsm_op_export): Likewise.
+ (_gpgme_gpgsm_op_genkey): Likewise.
+ (_gpgme_gpgsm_op_sign): Likewise.
+ (_gpgme_gpgsm_op_keylist): Likewise.
+ (_gpgme_gpgsm_op_trustlist): Likewise.
+ (_gpgme_gpgsm_release): Release command.
+ (_gpgme_gpgsm_op_decrypt): Allocate command.
+ (_gpgme_gpgsm_op_import): Likewise.
+ (gpgsm_status_handler): Also treat `ERR' strings as EOF.
+
+2001-11-22 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.h (gpgme_set_protocol): New prototype.
+
+2001-11-22 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine-gpgsm.c (_gpgme_gpgsm_op_decrypt): New function.
+ (_gpgme_gpgsm_op_import): Likewise.
+
+2001-11-22 Marcus Brinkmann <marcus@g10code.de>
+
+ * engine-gpgsm.c: Shuffle around header inclusion a bit, to still
+ keep them seperate.
+ (_gpgme_set_status_handler) [!ENABLE_GPGSM]: New function.
+
+2001-11-22 Werner Koch <wk@gnupg.org>
+
+ * engine-gpgsm.c: Include more headers so that NULL and mk_error
+ is defined even with an undefined GPGSM_PATH.
+
+2001-11-22 Marcus Brinkmann <marcus@g10code.de>
+
+ * rungpg.c (gpg_inbound_handler, write_mem_data, write_cb_data,
+ gpg_outbound_handler): Moved to ...
+ * data.c (_gpgme_data_inbound_handler, write_mem_data,
+ write_cb_data, _gpgme_data_outbound_handler): ... here. Make the
+ _gpgme_* ones non-static.
+ * data.c: Include io.h.
+
+ * ops.h (_gpgme_data_inbound_handler): New prototype.
+ (_gpgme_data_outbound_handler): Likewise.
+ (_gpgme_gpg_spawn): Use these new functions.
+
+ * engine-gpgsm.h (_gpgme_gpgsm_op_decrypt, _gpgme_gpgsm_op_delete,
+ _gpgme_gpgsm_op_encrypt, _gpgme_gpgsm_op_export,
+ _gpgme_gpgsm_op_genkey, _gpgme_gpgsm_op_import,
+ _gpgme_gpgsm_op_keylist, _gpgme_gpgsm_op_sign,
+ _gpgme_gpgsm_op_trustlist, _gpgme_gpgsm_op_verify,
+ _gpgme_gpgsm_start, _gpgme_gpgsm_set_status_handler): New prototype.
+ Include <rungpg.h> for status handler function.
+
+ * engine-gpgsm.c (struct gpgsm_object_s): New members input_fd,
+ input_data, output_fd, output_data, message_fd, message_data, command
+ and status.
+ (_gpgme_gpgsm_new): Open input, output and message pipes before
+ connecting to the client. Close server's ends afterwards.
+ (_gpgme_gpgsm_release): Close open file descriptors. Remove
+ server process from wait queue.
+ (_gpgme_gpgsm_op_verify, _gpgme_gpgsm_start,
+ _gpgme_gpgsm_set_status_handler, gpgms_status_handler): New function.
+
+ * engine.c (_gpgme_engine_start): Implement for GPGME_PROTOCOL_CMS.
+ (_gpgme_engine_set_status_handler): Likewise.
+ (_gpgme_engine_op_verify): Likewise.
+
+2001-11-21 Marcus Brinkmann <marcus@g10code.de>
+
+ * context.h: Do not include rungpg.h, but engine.h.
+ (struct gpgme_context_s): Replace member gpg with engine.
+ * gpgme.c (gpgme_release): Release engine, not gpg.
+
+ * recipient.c (_gpgme_append_gpg_args_from_recifgpients): Function
+ moved ...
+ * rungpg.c (_gpgme_append_gpg_args_from_recipients): ... here.
+ Make static, change order of arguments, and return an error value.
+ * ops.h (_gpgme_append_gpg_args_from_recipients): Removed prototype.
+
+ * rungpg.h (_gpgme_gpg_op_verify): Add prototype.
+ (_gpgme_gpg_op_encrypt): Likewise.
+ (_gpgme_gpg_op_decrypt): Likewise.
+ (_gpgme_gpg_op_delete): Likewise.
+ (_gpgme_gpg_op_export): Likewise.
+ (_gpgme_gpg_op_genkey): Likewise.
+ (_gpgme_gpg_op_import): Likewise.
+ (_gpgme_gpg_op_keylist): Likewise.
+ (_gpgme_gpg_op_sign): Likewise.
+ (_gpgme_gpg_op_trustlist): Likewise.
+ * rungpg.c (_gpgme_gpg_op_verify): New function.
+ (_gpgme_gpg_op_encrypt): Likewise.
+ (_gpgme_gpg_op_decrypt): Likewise.
+ (_gpgme_gpg_op_delete): Likewise.
+ (_gpgme_gpg_op_export): Likewise.
+ (_gpgme_gpg_op_genkey): Likewise.
+ (_gpgme_gpg_op_import): Likewise.
+ (_gpgme_gpg_op_keylist): Likewise.
+ (_gpgme_gpg_op_sign): Likewise.
+ (_gpgme_gpg_op_trustlist): Likewise.
+
+ * engine.h (_gpgme_engine_set_status_handler): Add prototype.
+ (_gpgme_engine_set_command_handler): Likewise.
+ (_gpgme_engine_set_colon_line_handler): Likewise.
+ (_gpgme_engine_op_decrypt): Likewise.
+ (_gpgme_engine_op_delete): Likewise.
+ (_gpgme_engine_op_encrypt): Likewise.
+ (_gpgme_engine_op_export): Likewise.
+ (_gpgme_engine_op_genkey): Likewise.
+ (_gpgme_engine_op_import): Likewise.
+ (_gpgme_engine_op_keylist): Likewise.
+ (_gpgme_engine_op_sign): Likewise.
+ (_gpgme_engine_op_trustlist): Likewise.
+ (_gpgme_engine_op_verify): Likewise.
+ (_gpgme_engine_start): Likewise.
+ * engine.c (_gpgme_engine_set_status_handler): New function.
+ (_gpgme_engine_set_command_handler): Likewise.
+ (_gpgme_engine_set_colon_line_handler): Likewise.
+ (_gpgme_engine_op_decrypt): Likewise.
+ (_gpgme_engine_op_delete): Likewise.
+ (_gpgme_engine_op_encrypt): Likewise.
+ (_gpgme_engine_op_export): Likewise.
+ (_gpgme_engine_op_genkey): Likewise.
+ (_gpgme_engine_op_import): Likewise.
+ (_gpgme_engine_op_keylist): Likewise.
+ (_gpgme_engine_op_sign): Likewise.
+ (_gpgme_engine_op_trustlist): Likewise.
+ (_gpgme_engine_op_verify): Likewise.
+ (_gpgme_engine_start): Likewise.
+
+ * verify.c (gpgme_op_verify_start): Reimplement in terms of above
+ functions.
+ * encrypt.c (gpgme_op_encrypt_start): Likewise.
+ * decrypt.c (_gpgme_decrypt_start): Likewise.
+ * passphrase.c (_gpgme_passphrase_start): Likewise.
+ * keylist.c (gpgme_op_keylist_start): Likewise.
+
+2001-11-20 Marcus Brinkmann <marcus@g10code.de>
+
+ * types.h: Add types EngineObject and GpgsmObject.
+
+ * Makefile.am (libgpgme_la_SOURCES): Add engine-gpgsm.h,
+ engine-gpgsm.c, engine.h and engine.c.
+ * engine.h: New file.
+ * engine.c: Likewise.
+ * engine-gpgsm.h: Likewise.
+ * engine-gpgsm.c: Likewise.
+
+ * rungpg.c (_gpgme_gpg_get_version): New function.
+ (_gpgme_gpg_check_version): Likewise.
+ * rungpg.h: Add prototypes for _gpgme_gpg_get_version and
+ _gpgme_gpg_check_version.
+
+ * version.c (compare_versions): Rename to ...
+ (_gpgme_compare_versions): ... this. Make non-static.
+ (gpgme_check_version): Use _gpgme_compare_versions rather than
+ compare_versions.
+ (gpgme_check_engine): Likewise.
+ * ops.h (_gpgme_get_program_version): Add prototype.
+
+2001-11-20 Marcus Brinkmann <marcus@g10code.de>
+
+ * Makefile.am (libgpgme_la_INCLUDES): Remove obsolete directive.
+ (AM_CPPFLAGS): New directive [BUILD_ASSUAN].
+ (libgpgme_la_LIBADD): Likewise.
+
+2001-11-20 Marcus Brinkmann <marcus@g10code.de>
+
+ * version.c: Remove global variables lineno and
+ tmp_engine_version.
+ (version_handler): Removed.
+ (_gpgme_get_program_version): New function.
+ (get_engine_info): Don't use context and version_handler,
+ but _gpgme_get_program_version.
+ * ops.h (_gpgme_get_program_version): Add prototype for
+ _gpgme_get_program_version (we expect to use it elsewhere soon).
+
+2001-11-18 Marcus Brinkmann <marcus@g10code.de>
+
+ * version.c (get_engine_info): If GnuPG is not available, return
+ an error message.
+ * posix-util.c (_gpgme_get_gpg_path): Allow GPG_PATH to be
+ undefined.
+ (_gpgme_get_gpgsm_path): New function.
+ * w32-util.c (find_program_in_registry): New static function.
+ (_gpgme_get_gpg_path): Allow GPG_PATH to be undefined. Rework
+ to use find_program_in_registry.
+ (_gpgme_get_gpgsm_path): New function.
+ (util.h): Prototype _gpgme_get_gpgsm_path).
+ * rungpg.c (_gpgme_gpg_spawn): Verify that _gpgme_get_gpg_path()
+ returns non-null.
+
+2001-11-16 Marcus Brinkmann <marcus@g10code.de>
+
+ * decrypt-verify.c: New file.
+ * Makefile.am (libgpgme_la_SOURCES): Add decrypt-verify.c.
+ * types.h: Add decrypt-verify types.
+ * ops.h: Likewise.
+ * context.h: Add result type for decrypt-verify.
+ * gpgme.h: Add decrypt-verify prototypes.
+
+ * decrypt.c (decrypt_status_handler): Renamed to ...
+ (_gpgme_decrypt_status_handler): ... this. Make non-static.
+ (_gpgme_decrypt_start): New function, derived from
+ gpgme_op_decrypt_start.
+ (gpgme_op_decrypt_start): Reimplement in terms of
+ _gpgme_decrypt_start.
+ (_gpgme_decrypt_result): New function to retrieve error value.
+ (gpgme_op_decrypt): Use _gpgme_decrypt_result.
+ * ops.h: Add prototypes for new functions.
+
+ * verify.c (verify_status_handler): Renamed to ...
+ (_gpgme_verify_status_handler): ... this. Make non-static.
+ (gpgme_op_verify_start): Use new function name.
+ (intersect_stati): Renamed to ...
+ (_gpgme_intersect_stati): ... this. Make non-static.
+ (gpgme_op_verify): Use new name.
+ * ops.h: Add prototypes for new functions.
+
+2001-11-16 Marcus Brinkmann <marcus@g10code.de>
+
+ * passphrase.c: New file.
+ * Makefile.am (libgpgme_la_SOURCES): Add passphrase.c.
+ * ops.h (_gpgme_passphrase_result): Add prototypes from
+ passphrase.c.
+ * types.h: Likewise.
+ * context.h: Add member passphrase to result.
+ * gpgme.c (_gpgme_release_result): Release passphrase member.
+
+ * decrypt.c: Some formatting and variable name changes (like
+ CTX instead C).
+ (struct decrypt_result_s): Remove members now found in
+ passphrase result.
+ (_gpgme_release_decrypt_result): Don't release removed members.
+ (decrypt_status_handler): Call _gpgme_passphrase_status_handler,
+ and don't handle the cases catched there.
+ (command_handler): Removed.
+ (gpgme_op_decrypt_start): Don't set command handler, but invoke
+ _gpgme_passphrase_start which does it.
+ (gpgme_op_decrypt): Invoke _gpgme_passphrase_result and drop the
+ cases covered by it.
+
+ * sign.c Some formatting and variable name changes (like
+ CTX instead C).
+ (struct sign_result_s): Remove members now found in
+ passphrase result.
+ (_gpgme_release_sign_result): Don't release removed members.
+ (sign_status_handler): Call _gpgme_passphrase_status_handler,
+ and don't handle the cases catched there.
+ (command_handler): Removed.
+ (gpgme_op_sign_start): Don't set command handler, but invoke
+ _gpgme_passphrase_start which does it.
+ (gpgme_op_sign): Invoke _gpgme_passphrase_result and drop the
+ cases covered by it.
+
+2001-11-15 Marcus Brinkmann <marcus@g10code.de>
+
+ * decrypt.c (command_handler): Fix last change.
+
+2001-11-15 Marcus Brinkmann <marcus@g10code.de>
+
+ * verify.c (_gpgme_release_verify_result): Rename RES to RESULT.
+ Rename R2 to NEXT_RESULT.
+ (intersect_stati): Rename RES to RESULT.
+ (gpgme_get_sig_status): Likewise. Do not check return_type, but
+ the member verify of result.
+ (gpgme_get_sig_key): Likewise.
+
+ * sign.c (_gpgme_release_sign_result): Rename RES to RESULT. If
+ RESULT is zero, return.
+ (sign_status_handler, command_handler): Do not check return_type,
+ but the member sign of result.
+ (gpgme_op_sign): Likewise. Drop assertion.
+
+ * encrypt.c (_gpgme_release_encrypt_result): Rename RES to RESULT.
+ If RESULT is zero, return.
+ (encrypt_status_handler): Do not check return_type, but the member
+ encrypt of result.
+ (gpgme_op_encrypt): Likewise. Drop assertion.
+
+ * decrypt.c (_gpgme_release_decrypt_result): Rename RES to RESULT.
+ (create_result_struct): Do not set result_type.
+ (command_handler, decrypt_status_handler): Do not check
+ return_type, but the member decrypt of result.
+ (gpgme_op_decrypt): Likewise. Drop assertion.
+
+ * context.h (enum ResultType): Removed.
+ (struct gpgme_context_s): Remove member result_type.
+ (struct result): Replaces union result.
+ * gpgme.c: Include string.h.
+ (_gpgme_release_result): Release all members of c->result, which
+ is now a struct. Zero out all members of the struct afterwards.
+
+2001-11-11 Marcus Brinkmann <marcus@g10code.de>
+
+ * rungpg.c (_gpgme_gpg_release): Release GPG->cmd.cb_data.
+ Release all members of the list GPG->arglist.
+ Reported by Michael Schmidt <mschmidt@cs.uni-sb.de>.
+
+2001-11-02 Marcus Brinkmann <marcus@g10code.de>
+
+ * rungpg.c (pipemode_copy): Change type of NBYTES to size_t.
+
+ * key.c: Include string.h.
+ * data.c: Likewise.
+ * recipient.c: Likewise.
+
+2001-10-29 Marcus Brinkmann <marcus@g10code.de>
+
+ * context.h: New member signers_len.
+ * signers.c (gpgme_signers_clear): Require that signers are
+ non-NULL with assertion. Use signers_len to determine how much
+ keys to release. Add documentation.
+ (gpgme_signers_add): Use signers_len to determine if the buffer is
+ large enough. Use xtryrealloc rather than xtrymalloc and copying.
+ Add documentation.
+ (gpgme_signers_enum): Use signers_len to determine if key is
+ available. Add documentation.
+
+2001-10-22 Marcus Brinkmann <marcus@g10code.de>
+
+ * data.c (_gpgme_data_append): Check if LENGTH is smaller than
+ ALLOC_CHUNK, not DH->length.
+
+2001-10-17 Marcus Brinkmann <marcus@g10code.de>
+
+ * gpgme.c (gpgme_set_protocol): Fix last change.
+
+2001-10-15 Werner Koch <wk@gnupg.org>
+
+ * gpgme.h (GpgmeProtocol): New.
+ * gpgme.c (gpgme_set_protocol): New.
+
+2001-09-26 Werner Koch <wk@gnupg.org>
+
+ * gpgme.c (gpgme_set_passphrase_cb): Ignore a NULL context.
+ (gpgme_set_progress_cb): Ditto. Suggested by Mark Mutz.
+
+2001-09-17 Werner Koch <wk@gnupg.org>
+
+ * keylist.c (finish_key): Shortcut for no tmp_key. Changed all
+ callers to use this function without a check for tmp_key.
+
+ * keylist.c (gpgme_op_keylist_next): Reset the key_cond after
+ emptying the queue. Bug reported by Stéphane Corthésy.
+
+2001-09-12 Werner Koch <wk@gnupg.org>
+
+ * data.c (gpgme_data_rewind): Allow rewind for callbacks.
+
+2001-09-07 Werner Koch <wk@gnupg.org>
+
+ * rungpg.h: Add NO_RECP.
+ * encrypt.c (encrypt_status_handler): Take on No_RECP.
+ (gpgme_op_encrypt): Better error return.
+
+ * verify.c (verify_status_handler): Take on NODATA.
+
+2001-09-03 Werner Koch <wk@gnupg.org>
+
+ * rungpg.h: Added STATUS_INV_RECP.
+ * gpgme.c (_gpgme_release_result): Add support for new
+ EncryptResult object.
+ * encrypt.c (append_xml_encinfo): New.
+ (encrypt_status_handler): Add some status parsing.
+ (_gpgme_release_encrypt_result): New.
+
+2001-08-29 Werner Koch <wk@gnupg.org>
+
+ * recipient.c (gpgme_recipients_release): Free the list. By Timo.
+
+ * keylist.c (keylist_colon_handler): Do a finish key if we receive
+ an EOF here. This is probably the reason for a lot of bugs
+ related to keylisting. It is so obvious. Kudos to Enno Cramer
+ for pointing that out.
+
+2001-08-28 Werner Koch <wk@gnupg.org>
+
+ * gpgme.c, gpgme.h (gpgme_get_op_info): New.
+ (_gpgme_set_op_info): New.
+ (_gpgme_release_result): Reset the op_info here.
+ * sign.c (append_xml_siginfo): New.
+ (sign_status_handler): Store the sig create information.
+
+2001-07-31 Werner Koch <wk@gnupg.org>
+
+ * encrypt.c (gpgme_op_encrypt): Hack to detect no valid recipients.
+
+2001-07-30 Werner Koch <wk@gnupg.org>
+
+ * gpgme.c (gpgme_get_armor,gpgme_get_textmode): New.
+
+ * rungpg.c (build_argv): Disable armor comments
+ * w32-io.c (build_commandline): Need to add quotes here
+
+2001-07-24 Werner Koch <wk@gnupg.org>
+
+ * data.c (gpgme_data_read): Add a a way to return the available bytes.
+
+2001-07-23 Werner Koch <wk@gnupg.org>
+
+ * util.c: Removed stpcpy() because we use the version from jnlib.
+
+2001-07-19 Werner Koch <wk@gnupg.org>
+
+ * mkstatus: Define the collating sequence for sort.
+
+2001-06-26 Werner Koch <wk@gnupg.org>
+
+ * rungpg.h: Add STATUS_UNEXPECTED as suggested by Timo.
+
+2001-06-15 Werner Koch <wk@gnupg.org>
+
+ * keylist.c (set_userid_flags): Fixed the assigned values. Kudos
+ to Timo for pointing this out.
+
+2001-06-01 Werner Koch <wk@gnupg.org>
+
+ * debug.c (_gpgme_debug_begin): Fixed a /tmp race. Noted by
+ Johannes Poehlmann.
+
+2001-05-28 Werner Koch <wk@gnupg.org>
+
+ * version.c (gpgme_check_engine): Stop version number parsing at
+ the opening angle and not the closing one. By Tommy Reynolds.
+
+2001-05-01 José Carlos García Sogo <jose@jaimedelamo.eu.org>
+
+ * encrypt.c (gpgme_op_encrypt_start): Deleted the assert ( !c->gpg )
+ line, because it gave an error if another operation had been made
+ before using the same context.
+
+ * decrypt.c (gpgme_op_decrypt_start): The same as above. Also added
+ one line to release the gpg object in the context (if any).
+
+2001-04-26 Werner Koch <wk@gnupg.org>
+
+ * key.c, key.h (_gpgme_key_cache_init): New.
+ (_gpgme_key_cache_add): New.
+ (_gpgme_key_cache_get): New.
+ * version.c (do_subsystem_inits): Init the cache.
+ * keylist.c (finish_key): Put key into the cache
+ * verify.c (gpgme_get_sig_key): First look into the cache.
+
+2001-04-19 Werner Koch <wk@gnupg.org>
+
+ * keylist.c (parse_timestamp): Adjusted for the changed
+ --fixed-list-mode of gpg 1.0.4h.
+
+2001-04-05 Werner Koch <wk@gnupg.org>
+
+ * verify.c (gpgme_op_verify_start): Enabled pipemode for detached sigs.
+
+2001-04-04 Werner Koch <wk@gnupg.org>
+
+ * w32-io.c (_gpgme_io_select): Don't select on the writer if there
+ are still bytes pending. Timo found this not easy to track down
+ race condition.
+
+2001-04-02 Werner Koch <wk@gnupg.org>
+
+ * gpgme.h: Add GPGME_ATTR_KEY_{EXPIRED,DISABLED}.
+ * key.c (gpgme_key_get_ulong_attr): And return those attribs.
+
+ * verify.c (gpgme_get_sig_key): Set keyliosting mode depending on
+ the mode set in the current context. Suggested by Timo.
+
+ * key.c (gpgme_key_get_ulong_attr): Return can_certify and not
+ can_encrypt. By Timo.
+
+2001-03-30 Werner Koch <wk@gnupg.org>
+
+ * debug.c (debug_init): Allow to specify a debug file.
+ (_gpgme_debug_level): New.
+
+ * posix-io.c (_gpgme_io_read, _gpgme_io_write): Print output.
+ (_gpgme_io_select): Debug only with level > 2.
+
+2001-03-15 Werner Koch <wk@gnupg.org>
+
+ * rungpg.c: Included time.h.
+
+ * key.h: New keyflags for capabilities.
+ * keylist.c (set_mainkey_capability, set_subkey_capability): New.
+ (keylist_colon_handler): Parse them.
+ * gpgme.h: New attribute values for capabilties.
+ * key.c (gpgme_key_get_string_attr): Return them.
+ (capabilities_to_string): New.
+ (gpgme_key_get_ulong_attr): Return the global caps.
+
+2001-03-14 Werner Koch <wk@gnupg.org>
+
+ * w32-io.c (destroy_reader,destroy_writer): Fixed syntax error.
+ Thanks to Jan Oliver Wagner.
+
+2001-03-13 Werner Koch <wk@gnupg.org>
+
+ * context.h: Add invalid and revoke flags to user_id structure.
+ * keylist.c (gpgme_op_keylist_start): Use --fixed-list-mode.
+ (keylist_colon_handler): Adjust for that.
+ (set_userid_flags): New.
+ (set_mainkey_trust_info): Handle new key invalid flag
+ (set_subkey_trust_info): Ditto.
+ * gpgme.h: Add new attributes for key and user ID flags.
+ * key.c (_gpgme_key_append_name): Init these flags
+ (gpgme_key_get_as_xml): Print them.
+ (one_uid_as_xml): New helper for above.
+ (gpgme_key_get_string_attr, gpgme_key_get_ulong_attr):
+ Return the new attributes. Enhanced, so that subkey information
+ can be returned now.
+
+2001-02-28 Werner Koch <wk@gnupg.org>
+
+ * w32-io.c (destroy_reader): Set stop_me flag.
+ (writer,create_writer,destroy_writer,find_writer,kill_writer): New.
+ (_gpgme_io_write): Use a writer thread to avaoid blocking.
+ (_gpgme_io_close): Cleanup a writer thread
+ (_gpgme_io_select): Repalce tthe faked wait on writing by a real
+ waiting which is now possible due to the use of a writer thread.
+
+2001-02-20 Werner Koch <wk@gnupg.org>
+
+ * w32-io.c (destroy_reader,kill_reader): New.
+ (create_reader, reader): Add a new event to stop the thread.
+ (_gpgme_io_close): Kill the reader thread.
+
+ * posix-io.c (_gpgme_io_select): Handle frozen fds here.
+ * 32-io.c (_gpgme_io_select): Ditto. Removed a bunch of unused code.
+
+ * wait.c: Reworked the whole thing.
+ * rungpg.c (_gpgme_gpg_new): Init pid to -1.
+ (_gpgme_gpg_release): Remove the process from the wait queue.
+
+2001-02-19 Werner Koch <wk@gnupg.org>
+
+ * w32-io.c (_gpgme_io_set_close_notify): New.
+ (_gpgme_io_close): Do the notification.
+
+ * posix-io.c (_gpgme_io_select): Use a 1 sec timeout and not 200
+ microseconds.
+
+ * wait.c (remove_process): Don't close the fd here.
+ (do_select): Set the fd to -1 and remove the is_closed flag everywhere.
+ (_gpgme_wait_on_condition): Remove the assert on the queue and
+ break out if we could not find the queue. The whole thing should
+ be reworked.
+
+ * posix-io.c (_gpgme_io_set_close_notify): New.
+ (_gpgme_io_close): Do the notification.
+
+ * rungpg.c (close_notify_handler): New.
+ (_gpgme_gpg_new): Register a callback for the fd.
+ (_gpgme_gpg_set_colon_line_handler): Ditto.
+ (build_argv): Ditto
+
+2001-02-13 Werner Koch <wk@gnupg.org>
+
+ * rungpg.c (struct reap_s): Replaced pid_t by int.
+
+ * types.h: Add ulong typedef.
+
+ * rungpg.c (do_reaping,_gpgme_gpg_housecleaning): New.
+ (_gpgme_gpg_release): Reap children.
+ * io.h, posix-io.c (_gpgme_io_kill): New.
+ * w32-io.c (_gpgme_io_kill): New (dummy).
+
+ * keylist.c (gpgme_op_keylist_start): Cancel a pending request.
+
+ * posix-io.c (_gpgme_io_read): Add some debug output.
+ (_gpgme_io_write): Ditto.
+ (_gpgme_io_select): Increased the timeout.
+
+2001-02-12 Werner Koch <wk@gnupg.org>
+
+ Enhanced the signature verification, so that it can how handle
+ more than one signature and is able to return more information on
+ the signatures.
+ * verify.c (gpgme_get_sig_key): New.
+ (gpgme_get_sig_status): New.
+
+ * gpgme.h: Add stdio.h.
+ (GpgmeSigStat): New status DIFF.
+
+2001-02-01 Werner Koch <wk@gnupg.org>
+
+ * w32-io.c (set_synchronize): Add EVENT_MODIFY_STATE. Add Debug
+ code to all Set/ResetEvent().
+
+ * rungpg.c (read_status): Check for end of stream only if we have
+ an r. By Timo.
+
+2001-01-31 Werner Koch <wk@gnupg.org>
+
+ * wait.c (_gpgme_wait_on_condition): Removed all exit code processing.
+ (propagate_term_results,clear_active_fds): Removed.
+ (count_active_fds): Renamed to ..
+ (count_active_and_thawed_fds): .. this and count only thawed fds.
+
+ * rungpg.c (gpg_colon_line_handler): Return colon.eof and not
+ status.eof ;-)
+
+2001-01-30 Werner Koch <wk@gnupg.org>
+
+ * w32-io.c (_gpgme_io_spawn): Use the supplied path arg.
+
+ * version.c (get_engine_info): Return better error information.
+
+ * posix-util.c, w32-util.c: New.
+ (_gpgme_get_gpg_path): New, suggested by Jan-Oliver.
+ * rungpg.c (_gpgme_gpg_spawn): Use new function to get GPG's path.
+
+ * signers.c (gpgme_signers_add): Ooops, one should test code and
+ not just write it; the newarr was not assigned. Thanks to José
+ for pointing this out. Hmmm, still not tested, why should a coder
+ test his fix :-)
+
+ * w32-io.c: Does now use reader threads, so that we can use
+ WaitForMultipleObjects.
+ * sema.h, posix-sema.c, w32-sema.c: Support for Critcial sections.
+ Does currently only work for W32.
+
+ * debug.c, util.h : New. Changed all fprintfs to use this new
+ set of debugging functions.
+
+2001-01-23 Werner Koch <wk@gnupg.org>
+
+ * data.c (_gpgme_data_release_and_return_string): Fixed string
+ termination.
+
+2001-01-22 Werner Koch <wk@gnupg.org>
+
+ * delete.c: New.
+
+ * signers.c: New.
+ * key.c (gpgme_key_ref, gpgme_key_unref): New.
+ * sign.c (gpgme_op_sign_start): Allow the use of other keys.
+
+ * version.c (gpgme_get_engine_info,gpgme_check_engine): New.
+ * rungpg.c (_gpgme_gpg_set_simple_line_handler): New.
+
+2001-01-05 Werner Koch <wk@gnupg.org>
+
+ * data.c (gpgme_data_rewind): Allow to rewind data_type_none.
+
+
+ Copyright (C) 2001,2002,2003,2004,2005,2006,2007,2008,2009,2010,
+ 2011 g10 Code GmbH
+
+ This file is free software; as a special exception the author gives
+ unlimited permission to copy and/or distribute it, with or without
+ modifications, as long as this notice is preserved.
+
+ This file is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
+ implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..ea8e70e
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,230 @@
+# Copyright (C) 2000 Werner Koch (dd9jn)
+# Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007 g10 Code GmbH
+#
+# This file is part of GPGME.
+#
+# GPGME is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2.1 of the
+# License, or (at your option) any later version.
+#
+# GPGME is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
+# Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+## Process this file with automake to produce Makefile.in
+
+# Note: moc_kdpipeiodevice should actually be a dependcy below.
+EXTRA_DIST = gpgme-config.in gpgme.m4 libgpgme.vers ChangeLog-2011 \
+ gpgme.h.in versioninfo.rc.in gpgme.def moc_kdpipeiodevice.cpp
+
+bin_SCRIPTS = gpgme-config
+m4datadir = $(datadir)/aclocal
+m4data_DATA = gpgme.m4
+nodist_include_HEADERS = gpgme.h
+
+if HAVE_PTHREAD
+ltlib_gpgme_pthread = libgpgme-pthread.la
+else
+ltlib_gpgme_pthread =
+endif
+
+if BUILD_W32_GLIB
+ltlib_gpgme_glib = libgpgme-glib.la
+else
+ltlib_gpgme_glib =
+endif
+
+if BUILD_W32_QT
+ltlib_gpgme_qt = libgpgme-qt.la
+else
+ltlib_gpgme_qt =
+endif
+
+lib_LTLIBRARIES = libgpgme.la $(ltlib_gpgme_glib) $(ltlib_gpgme_qt) \
+ $(ltlib_gpgme_pthread)
+
+if HAVE_LD_VERSION_SCRIPT
+libgpgme_version_script_cmd = -Wl,--version-script=$(srcdir)/libgpgme.vers
+else
+libgpgme_version_script_cmd =
+endif
+
+if HAVE_DOSISH_SYSTEM
+system_components = w32-util.c w32-sema.c
+system_components_not_extra = w32-io.c
+else
+system_components = ath.h posix-util.c posix-sema.c posix-io.c
+system_components_not_extra =
+endif
+
+if HAVE_W32CE_SYSTEM
+system_components += w32-ce.h w32-ce.c
+endif
+
+if HAVE_GPGSM
+gpgsm_components = engine-gpgsm.c
+else
+gpgsm_components =
+endif
+
+if HAVE_ASSUAN
+assuan_components = assuan-support.c engine-assuan.c
+else
+assuan_components =
+endif
+
+if HAVE_GPGCONF
+gpgconf_components = engine-gpgconf.c
+else
+gpgconf_components =
+endif
+
+if HAVE_G13
+g13_components = engine-g13.c
+else
+g13_components =
+endif
+
+if HAVE_UISERVER
+uiserver_components = engine-uiserver.c
+else
+uiserver_components =
+endif
+
+# These are the source files common to all library versions. We used
+# to build a non-installed library for that, but that does not work
+# correctly on all platforms (in particular, one can not specify the
+# right linking order with libtool, as the non-installed version has
+# unresolved symbols to the thread module.
+main_sources = \
+ util.h conversion.c get-env.c context.h ops.h \
+ data.h data.c data-fd.c data-stream.c data-mem.c data-user.c \
+ data-compat.c \
+ signers.c sig-notation.c \
+ wait.c wait-global.c wait-private.c wait-user.c wait.h \
+ op-support.c \
+ encrypt.c encrypt-sign.c decrypt.c decrypt-verify.c verify.c \
+ sign.c passphrase.c progress.c \
+ key.c keylist.c trust-item.c trustlist.c \
+ import.c export.c genkey.c delete.c edit.c getauditlog.c \
+ opassuan.c passwd.c \
+ engine.h engine-backend.h engine.c engine-gpg.c status-table.c \
+ $(gpgsm_components) $(assuan_components) $(gpgconf_components) \
+ $(uiserver_components) \
+ $(g13_components) vfs-mount.c vfs-create.c \
+ gpgconf.c \
+ sema.h priv-io.h $(system_components) dirinfo.c \
+ debug.c debug.h gpgme.c version.c error.c
+
+libgpgme_la_SOURCES = $(main_sources) \
+ ath.h ath.c $(system_components_not_extra)
+libgpgme_pthread_la_SOURCES = $(main_sources) \
+ ath.h ath-pthread.c $(system_components_not_extra)
+
+if BUILD_W32_GLIB
+libgpgme_glib_la_SOURCES = $(main_sources) ath.h ath.c w32-glib-io.c
+endif
+
+if BUILD_W32_QT
+libgpgme_qt_la_SOURCES = $(main_sources) ath.h ath.c w32-qt-io.cpp \
+ kdpipeiodevice.h kdpipeiodevice.cpp kdpipeiodevice.moc
+# FIXME: Add extra depedency: moc_kdpipeiodevice.cpp
+
+# These are built sources (normally).
+# moc_kdpipeiodevice.cpp: kdpipeiodevice.h
+# $(MOC4) -o $@ $<
+#
+# kdpipeiodevice.moc: kdpipeiodevice.cpp
+# $(MOC4) -o $@ $<
+endif
+
+# We use a global CFLAGS and CPPFLAGS setting for all library
+# versions, because then every object file is only compiled once.
+AM_CPPFLAGS = @GPG_ERROR_CFLAGS@ @QT4_CORE_CFLAGS@
+AM_CFLAGS = @LIBASSUAN_CFLAGS@ @GLIB_CFLAGS@ @QT4_CORE_CFLAGS@
+
+if HAVE_W32_SYSTEM
+# Windows provides us with an endless stream of Tough Love. To spawn
+# processes with a controlled set of inherited handles, we need a
+# wrapper process.
+# Except on Windows CE. There nothing is inheritable anyway.
+if HAVE_W32CE_SYSTEM
+libexec_PROGRAMS =
+else
+libexec_PROGRAMS = gpgme-w32spawn
+endif
+
+RCCOMPILE = $(RC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES)
+LTRCCOMPILE = $(LIBTOOL) --mode=compile --tag=RC $(RCCOMPILE)
+
+SUFFIXES = .rc .lo
+
+.rc.lo:
+ $(LTRCCOMPILE) -i "$<" -o "$@"
+
+gpgme_res = versioninfo.lo
+no_undefined = -no-undefined
+export_symbols = -export-symbols $(srcdir)/gpgme.def
+
+install-def-file:
+ $(INSTALL) $(srcdir)/gpgme.def $(DESTDIR)$(libdir)/gpgme.def
+
+uninstall-def-file:
+ -rm $(DESTDIR)$(libdir)/gpgme.def
+
+gpgme_deps = $(gpgme_res) gpgme.def
+
+else
+gpgme_res =
+no_undefined =
+export_symbols =
+install-def-file:
+uninstall-def-file:
+
+gpgme_deps =
+endif
+
+libgpgme_la_LDFLAGS = $(no_undefined) $(export_symbols) \
+ $(libgpgme_version_script_cmd) -version-info \
+ @LIBGPGME_LT_CURRENT@:@LIBGPGME_LT_REVISION@:@LIBGPGME_LT_AGE@
+libgpgme_la_DEPENDENCIES = @LTLIBOBJS@ $(srcdir)/libgpgme.vers $(gpgme_deps)
+libgpgme_la_LIBADD = $(gpgme_res) @LIBASSUAN_LIBS@ @LTLIBOBJS@ \
+ @GPG_ERROR_LIBS@
+
+libgpgme_pthread_la_LDFLAGS = $(no_undefined) $(export_symbols) \
+ $(libgpgme_version_script_cmd) -version-info \
+ @LIBGPGME_LT_CURRENT@:@LIBGPGME_LT_REVISION@:@LIBGPGME_LT_AGE@
+libgpgme_pthread_la_DEPENDENCIES = @LTLIBOBJS@ $(srcdir)/libgpgme.vers
+libgpgme_pthread_la_LIBADD = $(gpgme_res) @LIBASSUAN_LIBS@ @LTLIBOBJS@ \
+ -lpthread @GPG_ERROR_LIBS@
+
+if BUILD_W32_GLIB
+libgpgme_glib_la_LDFLAGS = $(no_undefined) \
+ $(export_symbols) $(libgpgme_version_script_cmd) -version-info \
+ @LIBGPGME_LT_CURRENT@:@LIBGPGME_LT_REVISION@:@LIBGPGME_LT_AGE@
+libgpgme_glib_la_DEPENDENCIES = @LTLIBOBJS@ \
+ $(srcdir)/libgpgme.vers $(gpgme_deps)
+libgpgme_glib_la_LIBADD = $(gpgme_res) @LIBASSUAN_LIBS@ @LTLIBOBJS@ \
+ @GPG_ERROR_LIBS@ @GLIB_LIBS@
+endif
+
+if BUILD_W32_QT
+libgpgme_qt_la_LDFLAGS = $(no_undefined) \
+ $(export_symbols) $(libgpgme_version_script_cmd) -version-info \
+ @LIBGPGME_LT_CURRENT@:@LIBGPGME_LT_REVISION@:@LIBGPGME_LT_AGE@
+libgpgme_qt_la_DEPENDENCIES = @LTLIBOBJS@ $(srcdir)/libgpgme.vers $(gpgme_deps)
+libgpgme_qt_la_LIBADD = $(gpgme_res) @LIBASSUAN_LIBS@ @LTLIBOBJS@ \
+ @GPG_ERROR_LIBS@ @QT4_CORE_LIBS@
+endif
+
+noinst_PROGRAMS = gpgme-tool
+gpgme_tool_LDADD = libgpgme.la @LIBASSUAN_LIBS@
+
+install-data-local: install-def-file
+
+uninstall-local: uninstall-def-file
diff --git a/src/Makefile.in b/src/Makefile.in
new file mode 100644
index 0000000..5766ab7
--- /dev/null
+++ b/src/Makefile.in
@@ -0,0 +1,1113 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# Copyright (C) 2000 Werner Koch (dd9jn)
+# Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007 g10 Code GmbH
+#
+# This file is part of GPGME.
+#
+# GPGME is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2.1 of the
+# License, or (at your option) any later version.
+#
+# GPGME is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
+# Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+
+
+
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+@HAVE_W32CE_SYSTEM_TRUE@am__append_1 = w32-ce.h w32-ce.c
+@HAVE_W32CE_SYSTEM_FALSE@@HAVE_W32_SYSTEM_TRUE@libexec_PROGRAMS = gpgme-w32spawn$(EXEEXT)
+noinst_PROGRAMS = gpgme-tool$(EXEEXT)
+subdir = src
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in \
+ $(srcdir)/gpgme-config.in $(srcdir)/gpgme.h.in \
+ $(srcdir)/versioninfo.rc.in funopen.c setenv.c stpcpy.c \
+ ttyname_r.c vasprintf.c
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/glib-2.0.m4 \
+ $(top_srcdir)/m4/glibc21.m4 $(top_srcdir)/m4/gnupg-ttyname.m4 \
+ $(top_srcdir)/m4/gpg-error.m4 $(top_srcdir)/m4/libassuan.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES = versioninfo.rc gpgme.h gpgme-config
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(libexecdir)" \
+ "$(DESTDIR)$(bindir)" "$(DESTDIR)$(m4datadir)" \
+ "$(DESTDIR)$(includedir)"
+LTLIBRARIES = $(lib_LTLIBRARIES)
+@HAVE_W32_SYSTEM_TRUE@am__DEPENDENCIES_1 = versioninfo.lo
+am__libgpgme_glib_la_SOURCES_DIST = util.h conversion.c get-env.c \
+ context.h ops.h data.h data.c data-fd.c data-stream.c \
+ data-mem.c data-user.c data-compat.c signers.c sig-notation.c \
+ wait.c wait-global.c wait-private.c wait-user.c wait.h \
+ op-support.c encrypt.c encrypt-sign.c decrypt.c \
+ decrypt-verify.c verify.c sign.c passphrase.c progress.c key.c \
+ keylist.c trust-item.c trustlist.c import.c export.c genkey.c \
+ delete.c edit.c getauditlog.c opassuan.c passwd.c engine.h \
+ engine-backend.h engine.c engine-gpg.c status-table.c \
+ engine-gpgsm.c assuan-support.c engine-assuan.c \
+ engine-gpgconf.c engine-uiserver.c engine-g13.c vfs-mount.c \
+ vfs-create.c gpgconf.c sema.h priv-io.h ath.h posix-util.c \
+ posix-sema.c posix-io.c w32-ce.h w32-ce.c w32-util.c \
+ w32-sema.c dirinfo.c debug.c debug.h gpgme.c version.c error.c \
+ ath.c w32-glib-io.c
+@HAVE_GPGSM_TRUE@am__objects_1 = engine-gpgsm.lo
+@HAVE_ASSUAN_TRUE@am__objects_2 = assuan-support.lo engine-assuan.lo
+@HAVE_GPGCONF_TRUE@am__objects_3 = engine-gpgconf.lo
+@HAVE_UISERVER_TRUE@am__objects_4 = engine-uiserver.lo
+@HAVE_G13_TRUE@am__objects_5 = engine-g13.lo
+@HAVE_W32CE_SYSTEM_TRUE@am__objects_6 = w32-ce.lo
+@HAVE_DOSISH_SYSTEM_FALSE@am__objects_7 = posix-util.lo posix-sema.lo \
+@HAVE_DOSISH_SYSTEM_FALSE@ posix-io.lo $(am__objects_6)
+@HAVE_DOSISH_SYSTEM_TRUE@am__objects_7 = w32-util.lo w32-sema.lo \
+@HAVE_DOSISH_SYSTEM_TRUE@ $(am__objects_6)
+am__objects_8 = conversion.lo get-env.lo data.lo data-fd.lo \
+ data-stream.lo data-mem.lo data-user.lo data-compat.lo \
+ signers.lo sig-notation.lo wait.lo wait-global.lo \
+ wait-private.lo wait-user.lo op-support.lo encrypt.lo \
+ encrypt-sign.lo decrypt.lo decrypt-verify.lo verify.lo sign.lo \
+ passphrase.lo progress.lo key.lo keylist.lo trust-item.lo \
+ trustlist.lo import.lo export.lo genkey.lo delete.lo edit.lo \
+ getauditlog.lo opassuan.lo passwd.lo engine.lo engine-gpg.lo \
+ status-table.lo $(am__objects_1) $(am__objects_2) \
+ $(am__objects_3) $(am__objects_4) $(am__objects_5) \
+ vfs-mount.lo vfs-create.lo gpgconf.lo $(am__objects_7) \
+ dirinfo.lo debug.lo gpgme.lo version.lo error.lo
+@BUILD_W32_GLIB_TRUE@am_libgpgme_glib_la_OBJECTS = $(am__objects_8) \
+@BUILD_W32_GLIB_TRUE@ ath.lo w32-glib-io.lo
+libgpgme_glib_la_OBJECTS = $(am_libgpgme_glib_la_OBJECTS)
+libgpgme_glib_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libgpgme_glib_la_LDFLAGS) $(LDFLAGS) -o $@
+@BUILD_W32_GLIB_TRUE@am_libgpgme_glib_la_rpath = -rpath $(libdir)
+am__libgpgme_pthread_la_SOURCES_DIST = util.h conversion.c get-env.c \
+ context.h ops.h data.h data.c data-fd.c data-stream.c \
+ data-mem.c data-user.c data-compat.c signers.c sig-notation.c \
+ wait.c wait-global.c wait-private.c wait-user.c wait.h \
+ op-support.c encrypt.c encrypt-sign.c decrypt.c \
+ decrypt-verify.c verify.c sign.c passphrase.c progress.c key.c \
+ keylist.c trust-item.c trustlist.c import.c export.c genkey.c \
+ delete.c edit.c getauditlog.c opassuan.c passwd.c engine.h \
+ engine-backend.h engine.c engine-gpg.c status-table.c \
+ engine-gpgsm.c assuan-support.c engine-assuan.c \
+ engine-gpgconf.c engine-uiserver.c engine-g13.c vfs-mount.c \
+ vfs-create.c gpgconf.c sema.h priv-io.h ath.h posix-util.c \
+ posix-sema.c posix-io.c w32-ce.h w32-ce.c w32-util.c \
+ w32-sema.c dirinfo.c debug.c debug.h gpgme.c version.c error.c \
+ ath-pthread.c w32-io.c
+@HAVE_DOSISH_SYSTEM_TRUE@am__objects_9 = w32-io.lo
+am_libgpgme_pthread_la_OBJECTS = $(am__objects_8) ath-pthread.lo \
+ $(am__objects_9)
+libgpgme_pthread_la_OBJECTS = $(am_libgpgme_pthread_la_OBJECTS)
+libgpgme_pthread_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libgpgme_pthread_la_LDFLAGS) $(LDFLAGS) -o $@
+@HAVE_PTHREAD_TRUE@am_libgpgme_pthread_la_rpath = -rpath $(libdir)
+am__libgpgme_qt_la_SOURCES_DIST = util.h conversion.c get-env.c \
+ context.h ops.h data.h data.c data-fd.c data-stream.c \
+ data-mem.c data-user.c data-compat.c signers.c sig-notation.c \
+ wait.c wait-global.c wait-private.c wait-user.c wait.h \
+ op-support.c encrypt.c encrypt-sign.c decrypt.c \
+ decrypt-verify.c verify.c sign.c passphrase.c progress.c key.c \
+ keylist.c trust-item.c trustlist.c import.c export.c genkey.c \
+ delete.c edit.c getauditlog.c opassuan.c passwd.c engine.h \
+ engine-backend.h engine.c engine-gpg.c status-table.c \
+ engine-gpgsm.c assuan-support.c engine-assuan.c \
+ engine-gpgconf.c engine-uiserver.c engine-g13.c vfs-mount.c \
+ vfs-create.c gpgconf.c sema.h priv-io.h ath.h posix-util.c \
+ posix-sema.c posix-io.c w32-ce.h w32-ce.c w32-util.c \
+ w32-sema.c dirinfo.c debug.c debug.h gpgme.c version.c error.c \
+ ath.c w32-qt-io.cpp kdpipeiodevice.h kdpipeiodevice.cpp \
+ kdpipeiodevice.moc
+@BUILD_W32_QT_TRUE@am_libgpgme_qt_la_OBJECTS = $(am__objects_8) ath.lo \
+@BUILD_W32_QT_TRUE@ w32-qt-io.lo kdpipeiodevice.lo
+libgpgme_qt_la_OBJECTS = $(am_libgpgme_qt_la_OBJECTS)
+libgpgme_qt_la_LINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \
+ $(CXXFLAGS) $(libgpgme_qt_la_LDFLAGS) $(LDFLAGS) -o $@
+@BUILD_W32_QT_TRUE@am_libgpgme_qt_la_rpath = -rpath $(libdir)
+am__libgpgme_la_SOURCES_DIST = util.h conversion.c get-env.c context.h \
+ ops.h data.h data.c data-fd.c data-stream.c data-mem.c \
+ data-user.c data-compat.c signers.c sig-notation.c wait.c \
+ wait-global.c wait-private.c wait-user.c wait.h op-support.c \
+ encrypt.c encrypt-sign.c decrypt.c decrypt-verify.c verify.c \
+ sign.c passphrase.c progress.c key.c keylist.c trust-item.c \
+ trustlist.c import.c export.c genkey.c delete.c edit.c \
+ getauditlog.c opassuan.c passwd.c engine.h engine-backend.h \
+ engine.c engine-gpg.c status-table.c engine-gpgsm.c \
+ assuan-support.c engine-assuan.c engine-gpgconf.c \
+ engine-uiserver.c engine-g13.c vfs-mount.c vfs-create.c \
+ gpgconf.c sema.h priv-io.h ath.h posix-util.c posix-sema.c \
+ posix-io.c w32-ce.h w32-ce.c w32-util.c w32-sema.c dirinfo.c \
+ debug.c debug.h gpgme.c version.c error.c ath.c w32-io.c
+am_libgpgme_la_OBJECTS = $(am__objects_8) ath.lo $(am__objects_9)
+libgpgme_la_OBJECTS = $(am_libgpgme_la_OBJECTS)
+libgpgme_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libgpgme_la_LDFLAGS) $(LDFLAGS) -o $@
+PROGRAMS = $(libexec_PROGRAMS) $(noinst_PROGRAMS)
+gpgme_tool_SOURCES = gpgme-tool.c
+gpgme_tool_OBJECTS = gpgme-tool.$(OBJEXT)
+gpgme_tool_DEPENDENCIES = libgpgme.la
+gpgme_w32spawn_SOURCES = gpgme-w32spawn.c
+gpgme_w32spawn_OBJECTS = gpgme-w32spawn.$(OBJEXT)
+gpgme_w32spawn_LDADD = $(LDADD)
+SCRIPTS = $(bin_SCRIPTS)
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+ $(LDFLAGS) -o $@
+CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
+LTCXXCOMPILE = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
+CXXLD = $(CXX)
+CXXLINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=link $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) \
+ $(LDFLAGS) -o $@
+SOURCES = $(libgpgme_glib_la_SOURCES) $(libgpgme_pthread_la_SOURCES) \
+ $(libgpgme_qt_la_SOURCES) $(libgpgme_la_SOURCES) gpgme-tool.c \
+ gpgme-w32spawn.c
+DIST_SOURCES = $(am__libgpgme_glib_la_SOURCES_DIST) \
+ $(am__libgpgme_pthread_la_SOURCES_DIST) \
+ $(am__libgpgme_qt_la_SOURCES_DIST) \
+ $(am__libgpgme_la_SOURCES_DIST) gpgme-tool.c gpgme-w32spawn.c
+DATA = $(m4data_DATA)
+HEADERS = $(nodist_include_HEADERS)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AR = @AR@
+AS = @AS@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BUILD_FILEVERSION = @BUILD_FILEVERSION@
+BUILD_NUMBER = @BUILD_NUMBER@
+BUILD_TIMESTAMP = @BUILD_TIMESTAMP@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+G13 = @G13@
+GITLOG_TO_CHANGELOG = @GITLOG_TO_CHANGELOG@
+GLIBC21 = @GLIBC21@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GPG = @GPG@
+GPGCONF = @GPGCONF@
+GPGME_CONFIG_API_VERSION = @GPGME_CONFIG_API_VERSION@
+GPGME_CONFIG_CFLAGS = @GPGME_CONFIG_CFLAGS@
+GPGME_CONFIG_HOST = @GPGME_CONFIG_HOST@
+GPGME_CONFIG_LIBS = @GPGME_CONFIG_LIBS@
+GPGSM = @GPGSM@
+GPG_ERROR_CFLAGS = @GPG_ERROR_CFLAGS@
+GPG_ERROR_CONFIG = @GPG_ERROR_CONFIG@
+GPG_ERROR_LIBS = @GPG_ERROR_LIBS@
+GPG_PATH = @GPG_PATH@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBASSUAN_CFLAGS = @LIBASSUAN_CFLAGS@
+LIBASSUAN_CONFIG = @LIBASSUAN_CONFIG@
+LIBASSUAN_LIBS = @LIBASSUAN_LIBS@
+LIBGPGME_LT_AGE = @LIBGPGME_LT_AGE@
+LIBGPGME_LT_CURRENT = @LIBGPGME_LT_CURRENT@
+LIBGPGME_LT_REVISION = @LIBGPGME_LT_REVISION@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NEED__FILE_OFFSET_BITS = @NEED__FILE_OFFSET_BITS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+QT4_CORE_CFLAGS = @QT4_CORE_CFLAGS@
+QT4_CORE_LIBS = @QT4_CORE_LIBS@
+RANLIB = @RANLIB@
+RC = @RC@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+emacs_local_vars_begin = @emacs_local_vars_begin@
+emacs_local_vars_end = @emacs_local_vars_end@
+emacs_local_vars_read_only = @emacs_local_vars_read_only@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+
+# Note: moc_kdpipeiodevice should actually be a dependcy below.
+EXTRA_DIST = gpgme-config.in gpgme.m4 libgpgme.vers ChangeLog-2011 \
+ gpgme.h.in versioninfo.rc.in gpgme.def moc_kdpipeiodevice.cpp
+
+bin_SCRIPTS = gpgme-config
+m4datadir = $(datadir)/aclocal
+m4data_DATA = gpgme.m4
+nodist_include_HEADERS = gpgme.h
+@HAVE_PTHREAD_FALSE@ltlib_gpgme_pthread =
+@HAVE_PTHREAD_TRUE@ltlib_gpgme_pthread = libgpgme-pthread.la
+@BUILD_W32_GLIB_FALSE@ltlib_gpgme_glib =
+@BUILD_W32_GLIB_TRUE@ltlib_gpgme_glib = libgpgme-glib.la
+@BUILD_W32_QT_FALSE@ltlib_gpgme_qt =
+@BUILD_W32_QT_TRUE@ltlib_gpgme_qt = libgpgme-qt.la
+lib_LTLIBRARIES = libgpgme.la $(ltlib_gpgme_glib) $(ltlib_gpgme_qt) \
+ $(ltlib_gpgme_pthread)
+
+@HAVE_LD_VERSION_SCRIPT_FALSE@libgpgme_version_script_cmd =
+@HAVE_LD_VERSION_SCRIPT_TRUE@libgpgme_version_script_cmd = -Wl,--version-script=$(srcdir)/libgpgme.vers
+@HAVE_DOSISH_SYSTEM_FALSE@system_components = ath.h posix-util.c \
+@HAVE_DOSISH_SYSTEM_FALSE@ posix-sema.c posix-io.c \
+@HAVE_DOSISH_SYSTEM_FALSE@ $(am__append_1)
+@HAVE_DOSISH_SYSTEM_TRUE@system_components = w32-util.c w32-sema.c \
+@HAVE_DOSISH_SYSTEM_TRUE@ $(am__append_1)
+@HAVE_DOSISH_SYSTEM_FALSE@system_components_not_extra =
+@HAVE_DOSISH_SYSTEM_TRUE@system_components_not_extra = w32-io.c
+@HAVE_GPGSM_FALSE@gpgsm_components =
+@HAVE_GPGSM_TRUE@gpgsm_components = engine-gpgsm.c
+@HAVE_ASSUAN_FALSE@assuan_components =
+@HAVE_ASSUAN_TRUE@assuan_components = assuan-support.c engine-assuan.c
+@HAVE_GPGCONF_FALSE@gpgconf_components =
+@HAVE_GPGCONF_TRUE@gpgconf_components = engine-gpgconf.c
+@HAVE_G13_FALSE@g13_components =
+@HAVE_G13_TRUE@g13_components = engine-g13.c
+@HAVE_UISERVER_FALSE@uiserver_components =
+@HAVE_UISERVER_TRUE@uiserver_components = engine-uiserver.c
+
+# These are the source files common to all library versions. We used
+# to build a non-installed library for that, but that does not work
+# correctly on all platforms (in particular, one can not specify the
+# right linking order with libtool, as the non-installed version has
+# unresolved symbols to the thread module.
+main_sources = \
+ util.h conversion.c get-env.c context.h ops.h \
+ data.h data.c data-fd.c data-stream.c data-mem.c data-user.c \
+ data-compat.c \
+ signers.c sig-notation.c \
+ wait.c wait-global.c wait-private.c wait-user.c wait.h \
+ op-support.c \
+ encrypt.c encrypt-sign.c decrypt.c decrypt-verify.c verify.c \
+ sign.c passphrase.c progress.c \
+ key.c keylist.c trust-item.c trustlist.c \
+ import.c export.c genkey.c delete.c edit.c getauditlog.c \
+ opassuan.c passwd.c \
+ engine.h engine-backend.h engine.c engine-gpg.c status-table.c \
+ $(gpgsm_components) $(assuan_components) $(gpgconf_components) \
+ $(uiserver_components) \
+ $(g13_components) vfs-mount.c vfs-create.c \
+ gpgconf.c \
+ sema.h priv-io.h $(system_components) dirinfo.c \
+ debug.c debug.h gpgme.c version.c error.c
+
+libgpgme_la_SOURCES = $(main_sources) \
+ ath.h ath.c $(system_components_not_extra)
+
+libgpgme_pthread_la_SOURCES = $(main_sources) \
+ ath.h ath-pthread.c $(system_components_not_extra)
+
+@BUILD_W32_GLIB_TRUE@libgpgme_glib_la_SOURCES = $(main_sources) ath.h ath.c w32-glib-io.c
+@BUILD_W32_QT_TRUE@libgpgme_qt_la_SOURCES = $(main_sources) ath.h ath.c w32-qt-io.cpp \
+@BUILD_W32_QT_TRUE@ kdpipeiodevice.h kdpipeiodevice.cpp kdpipeiodevice.moc
+
+# FIXME: Add extra depedency: moc_kdpipeiodevice.cpp
+
+# These are built sources (normally).
+# moc_kdpipeiodevice.cpp: kdpipeiodevice.h
+# $(MOC4) -o $@ $<
+#
+# kdpipeiodevice.moc: kdpipeiodevice.cpp
+# $(MOC4) -o $@ $<
+
+# We use a global CFLAGS and CPPFLAGS setting for all library
+# versions, because then every object file is only compiled once.
+AM_CPPFLAGS = @GPG_ERROR_CFLAGS@ @QT4_CORE_CFLAGS@
+AM_CFLAGS = @LIBASSUAN_CFLAGS@ @GLIB_CFLAGS@ @QT4_CORE_CFLAGS@
+@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
+@HAVE_W32_SYSTEM_FALSE@gpgme_res =
+@HAVE_W32_SYSTEM_TRUE@gpgme_res = versioninfo.lo
+@HAVE_W32_SYSTEM_FALSE@no_undefined =
+@HAVE_W32_SYSTEM_TRUE@no_undefined = -no-undefined
+@HAVE_W32_SYSTEM_FALSE@export_symbols =
+@HAVE_W32_SYSTEM_TRUE@export_symbols = -export-symbols $(srcdir)/gpgme.def
+@HAVE_W32_SYSTEM_FALSE@gpgme_deps =
+@HAVE_W32_SYSTEM_TRUE@gpgme_deps = $(gpgme_res) gpgme.def
+libgpgme_la_LDFLAGS = $(no_undefined) $(export_symbols) \
+ $(libgpgme_version_script_cmd) -version-info \
+ @LIBGPGME_LT_CURRENT@:@LIBGPGME_LT_REVISION@:@LIBGPGME_LT_AGE@
+
+libgpgme_la_DEPENDENCIES = @LTLIBOBJS@ $(srcdir)/libgpgme.vers $(gpgme_deps)
+libgpgme_la_LIBADD = $(gpgme_res) @LIBASSUAN_LIBS@ @LTLIBOBJS@ \
+ @GPG_ERROR_LIBS@
+
+libgpgme_pthread_la_LDFLAGS = $(no_undefined) $(export_symbols) \
+ $(libgpgme_version_script_cmd) -version-info \
+ @LIBGPGME_LT_CURRENT@:@LIBGPGME_LT_REVISION@:@LIBGPGME_LT_AGE@
+
+libgpgme_pthread_la_DEPENDENCIES = @LTLIBOBJS@ $(srcdir)/libgpgme.vers
+libgpgme_pthread_la_LIBADD = $(gpgme_res) @LIBASSUAN_LIBS@ @LTLIBOBJS@ \
+ -lpthread @GPG_ERROR_LIBS@
+
+@BUILD_W32_GLIB_TRUE@libgpgme_glib_la_LDFLAGS = $(no_undefined) \
+@BUILD_W32_GLIB_TRUE@ $(export_symbols) $(libgpgme_version_script_cmd) -version-info \
+@BUILD_W32_GLIB_TRUE@ @LIBGPGME_LT_CURRENT@:@LIBGPGME_LT_REVISION@:@LIBGPGME_LT_AGE@
+
+@BUILD_W32_GLIB_TRUE@libgpgme_glib_la_DEPENDENCIES = @LTLIBOBJS@ \
+@BUILD_W32_GLIB_TRUE@ $(srcdir)/libgpgme.vers $(gpgme_deps)
+
+@BUILD_W32_GLIB_TRUE@libgpgme_glib_la_LIBADD = $(gpgme_res) @LIBASSUAN_LIBS@ @LTLIBOBJS@ \
+@BUILD_W32_GLIB_TRUE@ @GPG_ERROR_LIBS@ @GLIB_LIBS@
+
+@BUILD_W32_QT_TRUE@libgpgme_qt_la_LDFLAGS = $(no_undefined) \
+@BUILD_W32_QT_TRUE@ $(export_symbols) $(libgpgme_version_script_cmd) -version-info \
+@BUILD_W32_QT_TRUE@ @LIBGPGME_LT_CURRENT@:@LIBGPGME_LT_REVISION@:@LIBGPGME_LT_AGE@
+
+@BUILD_W32_QT_TRUE@libgpgme_qt_la_DEPENDENCIES = @LTLIBOBJS@ $(srcdir)/libgpgme.vers $(gpgme_deps)
+@BUILD_W32_QT_TRUE@libgpgme_qt_la_LIBADD = $(gpgme_res) @LIBASSUAN_LIBS@ @LTLIBOBJS@ \
+@BUILD_W32_QT_TRUE@ @GPG_ERROR_LIBS@ @QT4_CORE_LIBS@
+
+gpgme_tool_LDADD = libgpgme.la @LIBASSUAN_LIBS@
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .rc .lo .c .cpp .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu src/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+versioninfo.rc: $(top_builddir)/config.status $(srcdir)/versioninfo.rc.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+gpgme.h: $(top_builddir)/config.status $(srcdir)/gpgme.h.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+gpgme-config: $(top_builddir)/config.status $(srcdir)/gpgme-config.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)"
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ list2=; for p in $$list; do \
+ if test -f $$p; then \
+ list2="$$list2 $$p"; \
+ else :; fi; \
+ done; \
+ test -z "$$list2" || { \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
+ }
+
+uninstall-libLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ for p in $$list; do \
+ $(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \
+ done
+
+clean-libLTLIBRARIES:
+ -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+ @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+ test "$$dir" != "$$p" || dir=.; \
+ echo "rm -f \"$${dir}/so_locations\""; \
+ rm -f "$${dir}/so_locations"; \
+ done
+libgpgme-glib.la: $(libgpgme_glib_la_OBJECTS) $(libgpgme_glib_la_DEPENDENCIES)
+ $(libgpgme_glib_la_LINK) $(am_libgpgme_glib_la_rpath) $(libgpgme_glib_la_OBJECTS) $(libgpgme_glib_la_LIBADD) $(LIBS)
+libgpgme-pthread.la: $(libgpgme_pthread_la_OBJECTS) $(libgpgme_pthread_la_DEPENDENCIES)
+ $(libgpgme_pthread_la_LINK) $(am_libgpgme_pthread_la_rpath) $(libgpgme_pthread_la_OBJECTS) $(libgpgme_pthread_la_LIBADD) $(LIBS)
+libgpgme-qt.la: $(libgpgme_qt_la_OBJECTS) $(libgpgme_qt_la_DEPENDENCIES)
+ $(libgpgme_qt_la_LINK) $(am_libgpgme_qt_la_rpath) $(libgpgme_qt_la_OBJECTS) $(libgpgme_qt_la_LIBADD) $(LIBS)
+libgpgme.la: $(libgpgme_la_OBJECTS) $(libgpgme_la_DEPENDENCIES)
+ $(libgpgme_la_LINK) -rpath $(libdir) $(libgpgme_la_OBJECTS) $(libgpgme_la_LIBADD) $(LIBS)
+install-libexecPROGRAMS: $(libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ test -z "$(libexecdir)" || $(MKDIR_P) "$(DESTDIR)$(libexecdir)"
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p || test -f $$p1; \
+ then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(libexecdir)" && rm -f $$files
+
+clean-libexecPROGRAMS:
+ @list='$(libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+clean-noinstPROGRAMS:
+ @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+gpgme-tool$(EXEEXT): $(gpgme_tool_OBJECTS) $(gpgme_tool_DEPENDENCIES)
+ @rm -f gpgme-tool$(EXEEXT)
+ $(LINK) $(gpgme_tool_OBJECTS) $(gpgme_tool_LDADD) $(LIBS)
+gpgme-w32spawn$(EXEEXT): $(gpgme_w32spawn_OBJECTS) $(gpgme_w32spawn_DEPENDENCIES)
+ @rm -f gpgme-w32spawn$(EXEEXT)
+ $(LINK) $(gpgme_w32spawn_OBJECTS) $(gpgme_w32spawn_LDADD) $(LIBS)
+install-binSCRIPTS: $(bin_SCRIPTS)
+ @$(NORMAL_INSTALL)
+ test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)"
+ @list='$(bin_SCRIPTS)'; test -n "$(bindir)" || list=; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n' \
+ -e 'h;s|.*|.|' \
+ -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) { files[d] = files[d] " " $$1; \
+ if (++n[d] == $(am__install_max)) { \
+ print "f", d, files[d]; n[d] = 0; files[d] = "" } } \
+ else { print "f", d "/" $$4, $$1 } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(bindir)$$dir'"; \
+ $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-binSCRIPTS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(bin_SCRIPTS)'; test -n "$(bindir)" || exit 0; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 's,.*/,,;$(transform)'`; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(bindir)" && rm -f $$files
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/funopen.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/setenv.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/stpcpy.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/ttyname_r.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/vasprintf.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/assuan-support.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ath-pthread.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ath.Plo@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@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/data-mem.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/data-stream.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/data-user.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/data.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/debug.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/decrypt-verify.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/decrypt.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/delete.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dirinfo.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/edit.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/encrypt-sign.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/encrypt.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/engine-assuan.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/engine-g13.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/engine-gpg.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/engine-gpgconf.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/engine-gpgsm.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/engine-uiserver.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/engine.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/error.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/export.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/genkey.Plo@am__quote@
+@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-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@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/import.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/kdpipeiodevice.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/key.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/keylist.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/op-support.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/opassuan.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/passphrase.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/passwd.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/posix-io.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/posix-sema.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/posix-util.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/progress.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sig-notation.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sign.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/signers.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/status-table.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/trust-item.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/trustlist.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/verify.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/version.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vfs-create.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vfs-mount.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/w32-ce.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/w32-glib-io.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/w32-io.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/w32-qt-io.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/w32-sema.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/w32-util.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wait-global.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wait-private.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wait-user.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wait.Plo@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+
+.cpp.o:
+@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $<
+
+.cpp.obj:
+@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.cpp.lo:
+@am__fastdepCXX_TRUE@ $(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(LTCXXCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-m4dataDATA: $(m4data_DATA)
+ @$(NORMAL_INSTALL)
+ test -z "$(m4datadir)" || $(MKDIR_P) "$(DESTDIR)$(m4datadir)"
+ @list='$(m4data_DATA)'; test -n "$(m4datadir)" || list=; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(m4datadir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(m4datadir)" || exit $$?; \
+ done
+
+uninstall-m4dataDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(m4data_DATA)'; test -n "$(m4datadir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ test -n "$$files" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(m4datadir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(m4datadir)" && rm -f $$files
+install-nodist_includeHEADERS: $(nodist_include_HEADERS)
+ @$(NORMAL_INSTALL)
+ test -z "$(includedir)" || $(MKDIR_P) "$(DESTDIR)$(includedir)"
+ @list='$(nodist_include_HEADERS)'; test -n "$(includedir)" || list=; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(includedir)'"; \
+ $(INSTALL_HEADER) $$files "$(DESTDIR)$(includedir)" || exit $$?; \
+ done
+
+uninstall-nodist_includeHEADERS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(nodist_include_HEADERS)'; test -n "$(includedir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ test -n "$$files" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(includedir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(includedir)" && rm -f $$files
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ set x; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(SCRIPTS) $(DATA) \
+ $(HEADERS)
+installdirs:
+ for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(libexecdir)" "$(DESTDIR)$(bindir)" "$(DESTDIR)$(m4datadir)" "$(DESTDIR)$(includedir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libLTLIBRARIES clean-libexecPROGRAMS \
+ clean-libtool clean-noinstPROGRAMS mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf $(DEPDIR) ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-data-local install-m4dataDATA \
+ install-nodist_includeHEADERS
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-binSCRIPTS install-libLTLIBRARIES \
+ install-libexecPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf $(DEPDIR) ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-binSCRIPTS uninstall-libLTLIBRARIES \
+ uninstall-libexecPROGRAMS uninstall-local uninstall-m4dataDATA \
+ uninstall-nodist_includeHEADERS
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+ clean-libLTLIBRARIES clean-libexecPROGRAMS clean-libtool \
+ clean-noinstPROGRAMS ctags distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-binSCRIPTS install-data install-data-am \
+ install-data-local install-dvi install-dvi-am install-exec \
+ install-exec-am install-html install-html-am install-info \
+ install-info-am install-libLTLIBRARIES install-libexecPROGRAMS \
+ install-m4dataDATA install-man install-nodist_includeHEADERS \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
+ pdf pdf-am ps ps-am tags uninstall uninstall-am \
+ uninstall-binSCRIPTS uninstall-libLTLIBRARIES \
+ uninstall-libexecPROGRAMS uninstall-local uninstall-m4dataDATA \
+ uninstall-nodist_includeHEADERS
+
+
+@HAVE_W32_SYSTEM_TRUE@.rc.lo:
+@HAVE_W32_SYSTEM_TRUE@ $(LTRCCOMPILE) -i "$<" -o "$@"
+
+@HAVE_W32_SYSTEM_TRUE@install-def-file:
+@HAVE_W32_SYSTEM_TRUE@ $(INSTALL) $(srcdir)/gpgme.def $(DESTDIR)$(libdir)/gpgme.def
+
+@HAVE_W32_SYSTEM_TRUE@uninstall-def-file:
+@HAVE_W32_SYSTEM_TRUE@ -rm $(DESTDIR)$(libdir)/gpgme.def
+@HAVE_W32_SYSTEM_FALSE@install-def-file:
+@HAVE_W32_SYSTEM_FALSE@uninstall-def-file:
+
+install-data-local: install-def-file
+
+uninstall-local: uninstall-def-file
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/assuan-support.c b/src/assuan-support.c
new file mode 100644
index 0000000..5264346
--- /dev/null
+++ b/src/assuan-support.c
@@ -0,0 +1,256 @@
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "assuan.h"
+
+#include "gpgme.h"
+#include "ath.h"
+#include "priv-io.h"
+#include "debug.h"
+
+
+struct assuan_malloc_hooks _gpgme_assuan_malloc_hooks =
+ {
+ malloc,
+ realloc,
+ free
+ };
+
+
+int
+_gpgme_assuan_log_cb (assuan_context_t ctx, void *hook,
+ unsigned int cat, const char *msg)
+{
+ if (msg == NULL)
+ return 1;
+
+ _gpgme_debug (DEBUG_ASSUAN, "%s", msg);
+ return 0;
+}
+
+
+static void
+my_usleep (assuan_context_t ctx, unsigned int usec)
+{
+ /* FIXME: Add to ath. */
+ __assuan_usleep (ctx, usec);
+}
+
+
+/* Create a pipe with an inheritable end. */
+static int
+my_pipe (assuan_context_t ctx, assuan_fd_t fds[2], int inherit_idx)
+{
+ int res;
+ int gfds[2];
+
+ res = _gpgme_io_pipe (gfds, inherit_idx);
+
+ /* For now... */
+ fds[0] = (assuan_fd_t) gfds[0];
+ fds[1] = (assuan_fd_t) gfds[1];
+
+ return res;
+}
+
+
+/* Close the given file descriptor, created with _assuan_pipe or one
+ of the socket functions. */
+static int
+my_close (assuan_context_t ctx, assuan_fd_t fd)
+{
+ return _gpgme_io_close ((int) fd);
+}
+
+
+static ssize_t
+my_read (assuan_context_t ctx, assuan_fd_t fd, void *buffer, size_t size)
+{
+ return _gpgme_io_read ((int) fd, buffer, size);
+}
+
+
+static ssize_t
+my_write (assuan_context_t ctx, assuan_fd_t fd, const void *buffer, size_t size)
+{
+ return _gpgme_io_write ((int) fd, buffer, size);
+}
+
+
+static int
+my_recvmsg (assuan_context_t ctx, assuan_fd_t fd, assuan_msghdr_t msg,
+ int flags)
+{
+#ifdef HAVE_W32_SYSTEM
+ gpg_err_set_errno (ENOSYS);
+ return -1;
+#else
+ return _gpgme_io_recvmsg ((int) fd, msg, flags);
+#endif
+}
+
+
+
+static int
+my_sendmsg (assuan_context_t ctx, assuan_fd_t fd, const assuan_msghdr_t msg,
+ int flags)
+{
+#ifdef HAVE_W32_SYSTEM
+ gpg_err_set_errno (ENOSYS);
+ return -1;
+#else
+ return _gpgme_io_sendmsg ((int) fd, msg, flags);
+#endif
+}
+
+
+/* If NAME is NULL, don't exec, just fork. FD_CHILD_LIST is modified
+ to reflect the value of the FD in the peer process (on
+ Windows). */
+static int
+my_spawn (assuan_context_t ctx, pid_t *r_pid, const char *name,
+ const char **argv,
+ assuan_fd_t fd_in, assuan_fd_t fd_out,
+ assuan_fd_t *fd_child_list,
+ void (*atfork) (void *opaque, int reserved),
+ void *atforkvalue, unsigned int flags)
+{
+ int err;
+ struct spawn_fd_item_s *fd_items;
+ int i;
+
+ assert (name);
+
+ if (! name)
+ {
+ gpg_err_set_errno (ENOSYS);
+ return -1;
+ }
+
+ i = 0;
+ if (fd_child_list)
+ {
+ while (fd_child_list[i] != ASSUAN_INVALID_FD)
+ i++;
+ }
+ /* fd_in, fd_out, terminator */
+ i += 3;
+ fd_items = calloc (i, sizeof (struct spawn_fd_item_s));
+ if (! fd_items)
+ return -1;
+ i = 0;
+ if (fd_child_list)
+ {
+ while (fd_child_list[i] != ASSUAN_INVALID_FD)
+ {
+ fd_items[i].fd = (int) fd_child_list[i];
+ fd_items[i].dup_to = -1;
+ i++;
+ }
+ }
+ if (fd_in != ASSUAN_INVALID_FD)
+ {
+ fd_items[i].fd = (int) fd_in;
+ fd_items[i].dup_to = 0;
+ i++;
+ }
+ if (fd_out != ASSUAN_INVALID_FD)
+ {
+ fd_items[i].fd = (int) fd_out;
+ fd_items[i].dup_to = 1;
+ i++;
+ }
+ fd_items[i].fd = -1;
+ fd_items[i].dup_to = -1;
+
+ err = _gpgme_io_spawn (name, (char*const*)argv, IOSPAWN_FLAG_NOCLOSE,
+ fd_items, atfork, atforkvalue, r_pid);
+ if (! err)
+ {
+ i = 0;
+
+ if (fd_child_list)
+ {
+ while (fd_child_list[i] != ASSUAN_INVALID_FD)
+ {
+ fd_child_list[i] = (assuan_fd_t) fd_items[i].peer_name;
+ i++;
+ }
+ }
+ }
+ free (fd_items);
+ return err;
+}
+
+
+/* If action is 0, like waitpid. If action is 1, just release the PID? */
+static pid_t
+my_waitpid (assuan_context_t ctx, pid_t pid,
+ int nowait, int *status, int options)
+{
+#ifdef HAVE_W32_SYSTEM
+ CloseHandle ((HANDLE) pid);
+#else
+ /* We can't just release the PID, a waitpid is mandatory. But
+ NOWAIT in POSIX systems just means the caller already did the
+ waitpid for this child. */
+ if (! nowait)
+ return _gpgme_ath_waitpid (pid, status, options);
+#endif
+ return 0;
+}
+
+
+
+
+static int
+my_socketpair (assuan_context_t ctx, int namespace, int style,
+ int protocol, assuan_fd_t filedes[2])
+{
+#ifdef HAVE_W32_SYSTEM
+ gpg_err_set_errno (ENOSYS);
+ return -1;
+#else
+ /* FIXME: Debug output missing. */
+ return __assuan_socketpair (ctx, namespace, style, protocol, filedes);
+#endif
+}
+
+
+static int
+my_socket (assuan_context_t ctx, int namespace, int style, int protocol)
+{
+ return _gpgme_io_socket (namespace, style, protocol);
+}
+
+
+static int
+my_connect (assuan_context_t ctx, int sock, struct sockaddr *addr,
+ socklen_t length)
+{
+ return _gpgme_io_connect (sock, addr, length);
+}
+
+
+struct assuan_system_hooks _gpgme_assuan_system_hooks =
+ {
+ ASSUAN_SYSTEM_HOOKS_VERSION,
+ my_usleep,
+ my_pipe,
+ my_close,
+ my_read,
+ my_write,
+ my_recvmsg,
+ my_sendmsg,
+ my_spawn,
+ my_waitpid,
+ my_socketpair,
+ my_socket,
+ my_connect
+ };
+
diff --git a/src/ath-pthread.c b/src/ath-pthread.c
new file mode 100644
index 0000000..9684afb
--- /dev/null
+++ b/src/ath-pthread.c
@@ -0,0 +1,186 @@
+/* ath-pthread.c - pthread module for self-adapting thread-safeness library
+ Copyright (C) 2002, 2003, 2004 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <errno.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#ifdef HAVE_SYS_SELECT_H
+# include <sys/select.h>
+#else
+# ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+# endif
+#endif
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <pthread.h>
+
+#include "ath.h"
+
+
+/* The lock we take while checking for lazy lock initialization. */
+static pthread_mutex_t check_init_lock = PTHREAD_MUTEX_INITIALIZER;
+
+/* Initialize the mutex *PRIV. If JUST_CHECK is true, only do this if
+ it is not already initialized. */
+static int
+mutex_pthread_init (ath_mutex_t *priv, int just_check)
+{
+ int err = 0;
+
+ if (just_check)
+ pthread_mutex_lock (&check_init_lock);
+ if (!*priv || !just_check)
+ {
+ pthread_mutex_t *lock = malloc (sizeof (pthread_mutex_t));
+ if (!lock)
+ err = ENOMEM;
+ if (!err)
+ {
+ err = pthread_mutex_init (lock, NULL);
+ if (err)
+ free (lock);
+ else
+ *priv = (ath_mutex_t) lock;
+ }
+ }
+ if (just_check)
+ pthread_mutex_unlock (&check_init_lock);
+ return err;
+}
+
+
+void
+ath_init (void)
+{
+ /* Nothing to do. */
+}
+
+
+uintptr_t
+ath_self (void)
+{
+ return (uintptr_t) pthread_self ();
+}
+
+
+int
+ath_mutex_init (ath_mutex_t *lock)
+{
+ return mutex_pthread_init (lock, 0);
+}
+
+
+int
+ath_mutex_destroy (ath_mutex_t *lock)
+{
+ int err = mutex_pthread_init (lock, 1);
+ if (!err)
+ {
+ err = pthread_mutex_destroy ((pthread_mutex_t *) *lock);
+ free (*lock);
+ }
+ return err;
+}
+
+
+int
+ath_mutex_lock (ath_mutex_t *lock)
+{
+ int ret = mutex_pthread_init (lock, 1);
+ if (ret)
+ return ret;
+
+ return pthread_mutex_lock ((pthread_mutex_t *) *lock);
+}
+
+
+int
+ath_mutex_unlock (ath_mutex_t *lock)
+{
+ int ret = mutex_pthread_init (lock, 1);
+ if (ret)
+ return ret;
+
+ return pthread_mutex_unlock ((pthread_mutex_t *) *lock);
+}
+
+
+ssize_t
+ath_read (int fd, void *buf, size_t nbytes)
+{
+ return read (fd, buf, nbytes);
+}
+
+
+ssize_t
+ath_write (int fd, const void *buf, size_t nbytes)
+{
+ return write (fd, buf, nbytes);
+}
+
+
+ssize_t
+ath_select (int nfd, fd_set *rset, fd_set *wset, fd_set *eset,
+ struct timeval *timeout)
+{
+ return select (nfd, rset, wset, eset, timeout);
+}
+
+
+ssize_t
+ath_waitpid (pid_t pid, int *status, int options)
+{
+ return waitpid (pid, status, options);
+}
+
+
+int
+ath_accept (int s, struct sockaddr *addr, socklen_t *length_ptr)
+{
+ return accept (s, addr, length_ptr);
+}
+
+
+int
+ath_connect (int s, const struct sockaddr *addr, socklen_t length)
+{
+ return connect (s, addr, length);
+}
+
+int
+ath_sendmsg (int s, const struct msghdr *msg, int flags)
+{
+ return sendmsg (s, msg, flags);
+}
+
+
+int
+ath_recvmsg (int s, struct msghdr *msg, int flags)
+{
+ return recvmsg (s, msg, flags);
+}
diff --git a/src/ath.c b/src/ath.c
new file mode 100644
index 0000000..ab65f7e
--- /dev/null
+++ b/src/ath.c
@@ -0,0 +1,214 @@
+/* ath.c - Thread-safeness library.
+ Copyright (C) 2002, 2003, 2004 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#ifdef HAVE_SYS_SELECT_H
+# include <sys/select.h>
+#else
+# ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+# endif
+#endif
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#ifndef HAVE_W32_SYSTEM
+#include <sys/wait.h>
+#endif
+
+#ifdef _MSC_VER
+ typedef long ssize_t;
+ typedef int pid_t;
+#endif
+
+#include "ath.h"
+
+
+#define MUTEX_UNLOCKED ((ath_mutex_t) 0)
+#define MUTEX_LOCKED ((ath_mutex_t) 1)
+#define MUTEX_DESTROYED ((ath_mutex_t) 2)
+
+
+#ifdef HAVE_W32_SYSTEM
+#include <windows.h>
+uintptr_t
+ath_self (void)
+{
+ return (uintptr_t) GetCurrentThreadId ();
+}
+#else
+# ifdef __linux
+#include <sys/syscall.h>
+uintptr_t
+ath_self (void)
+{
+ /* Just to catch users who don't use gpgme-pthread. */
+ return (uintptr_t) syscall (__NR_gettid);
+}
+# else
+uintptr_t
+ath_self (void)
+{
+ return (uintptr_t) getpid ();
+}
+# endif
+#endif
+
+
+int
+ath_mutex_init (ath_mutex_t *lock)
+{
+#ifndef NDEBUG
+ *lock = MUTEX_UNLOCKED;
+#endif
+ return 0;
+}
+
+
+int
+ath_mutex_destroy (ath_mutex_t *lock)
+{
+#ifndef NDEBUG
+ assert (*lock == MUTEX_UNLOCKED);
+
+ *lock = MUTEX_DESTROYED;
+#endif
+ return 0;
+}
+
+
+int
+ath_mutex_lock (ath_mutex_t *lock)
+{
+#ifndef NDEBUG
+ assert (*lock == MUTEX_UNLOCKED);
+
+ *lock = MUTEX_LOCKED;
+#endif
+ return 0;
+}
+
+
+int
+ath_mutex_unlock (ath_mutex_t *lock)
+{
+#ifndef NDEBUG
+ assert (*lock == MUTEX_LOCKED);
+
+ *lock = MUTEX_UNLOCKED;
+#endif
+ return 0;
+}
+
+
+ssize_t
+ath_read (int fd, void *buf, size_t nbytes)
+{
+#if defined(HAVE_W32CE_SYSTEM) && defined(_MSC_VER)
+ return -1; /* Not supported. */
+#else
+ return read (fd, buf, nbytes);
+#endif
+}
+
+
+ssize_t
+ath_write (int fd, const void *buf, size_t nbytes)
+{
+#if defined(HAVE_W32CE_SYSTEM) && defined(_MSC_VER)
+ return -1; /* Not supported. */
+#else
+ return write (fd, buf, nbytes);
+#endif
+}
+
+
+ssize_t
+ath_select (int nfd, fd_set *rset, fd_set *wset, fd_set *eset,
+ struct timeval *timeout)
+{
+#ifdef HAVE_W32_SYSTEM
+ return -1; /* Not supported. */
+#else
+ return select (nfd, rset, wset, eset, timeout);
+#endif
+}
+
+
+ssize_t
+ath_waitpid (pid_t pid, int *status, int options)
+{
+#ifdef HAVE_W32_SYSTEM
+ return -1; /* Not supported. */
+#else
+ return waitpid (pid, status, options);
+#endif
+}
+
+
+int
+ath_accept (int s, struct sockaddr *addr, socklen_t *length_ptr)
+{
+#ifdef HAVE_W32_SYSTEM
+ return -1; /* Not supported. */
+#else
+ return accept (s, addr, length_ptr);
+#endif
+}
+
+
+int
+ath_connect (int s, const struct sockaddr *addr, socklen_t length)
+{
+#ifdef HAVE_W32_SYSTEM
+ return -1; /* Not supported. */
+#else
+ return connect (s, addr, length);
+#endif
+}
+
+
+int
+ath_sendmsg (int s, const struct msghdr *msg, int flags)
+{
+#ifdef HAVE_W32_SYSTEM
+ return -1; /* Not supported. */
+#else
+ return sendmsg (s, msg, flags);
+#endif
+}
+
+
+int
+ath_recvmsg (int s, struct msghdr *msg, int flags)
+{
+#ifdef HAVE_W32_SYSTEM
+ return -1; /* Not supported. */
+#else
+ return recvmsg (s, msg, flags);
+#endif
+}
diff --git a/src/ath.h b/src/ath.h
new file mode 100644
index 0000000..9ada550
--- /dev/null
+++ b/src/ath.h
@@ -0,0 +1,101 @@
+/* ath.h - Interfaces for thread-safeness library.
+ Copyright (C) 2002, 2003, 2004 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#ifndef ATH_H
+#define ATH_H
+
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#ifdef HAVE_W32_SYSTEM
+ /* fixme: Check how we did it in libgcrypt. */
+ struct msghdr { int dummy; };
+ typedef int socklen_t;
+# if defined(HAVE_W32CE_SYSTEM) && defined(_MSC_VER)
+# include <winsock2.h>
+# endif
+# include <windows.h>
+# include <io.h>
+
+#else /*!HAVE_W32_SYSTEM*/
+
+# ifdef HAVE_SYS_SELECT_H
+# include <sys/select.h>
+# else
+# ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+# endif
+# endif
+# ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+# endif
+# include <sys/socket.h>
+
+#endif /*!HAVE_W32_SYSTEM*/
+
+
+
+/* Define _ATH_EXT_SYM_PREFIX if you want to give all external symbols
+ a prefix. */
+#define _ATH_EXT_SYM_PREFIX _gpgme_
+
+#ifdef _ATH_EXT_SYM_PREFIX
+#define _ATH_PREFIX1(x,y) x ## y
+#define _ATH_PREFIX2(x,y) _ATH_PREFIX1(x,y)
+#define _ATH_PREFIX(x) _ATH_PREFIX2(_ATH_EXT_SYM_PREFIX,x)
+#define ath_mutex_init _ATH_PREFIX(ath_mutex_init)
+#define ath_mutex_destroy _ATH_PREFIX(ath_mutex_destroy)
+#define ath_mutex_lock _ATH_PREFIX(ath_mutex_lock)
+#define ath_mutex_unlock _ATH_PREFIX(ath_mutex_unlock)
+#define ath_read _ATH_PREFIX(ath_read)
+#define ath_write _ATH_PREFIX(ath_write)
+#define ath_select _ATH_PREFIX(ath_select)
+#define ath_waitpid _ATH_PREFIX(ath_waitpid)
+#define ath_connect _ATH_PREFIX(ath_connect)
+#define ath_accept _ATH_PREFIX(ath_accept)
+#define ath_sendmsg _ATH_PREFIX(ath_sendmsg)
+#define ath_recvmsg _ATH_PREFIX(ath_recvmsg)
+#endif
+
+
+typedef void *ath_mutex_t;
+#define ATH_MUTEX_INITIALIZER 0;
+
+uintptr_t ath_self (void);
+
+/* Functions for mutual exclusion. */
+int ath_mutex_init (ath_mutex_t *mutex);
+int ath_mutex_destroy (ath_mutex_t *mutex);
+int ath_mutex_lock (ath_mutex_t *mutex);
+int ath_mutex_unlock (ath_mutex_t *mutex);
+
+/* Replacement for the POSIX functions, which can be used to allow
+ other (user-level) threads to run. */
+ssize_t ath_read (int fd, void *buf, size_t nbytes);
+ssize_t ath_write (int fd, const void *buf, size_t nbytes);
+ssize_t ath_select (int nfd, fd_set *rset, fd_set *wset, fd_set *eset,
+ struct timeval *timeout);
+ssize_t ath_waitpid (pid_t pid, int *status, int options);
+int ath_accept (int s, struct sockaddr *addr, socklen_t *length_ptr);
+int ath_connect (int s, const struct sockaddr *addr, socklen_t length);
+int ath_sendmsg (int s, const struct msghdr *msg, int flags);
+int ath_recvmsg (int s, struct msghdr *msg, int flags);
+
+#endif /* ATH_H */
diff --git a/src/context.h b/src/context.h
new file mode 100644
index 0000000..69d8d9b
--- /dev/null
+++ b/src/context.h
@@ -0,0 +1,138 @@
+/* context.h - Definitions for a GPGME context.
+ Copyright (C) 2000 Werner Koch (dd9jn)
+ Copyright (C) 2001, 2002, 2003, 2004, 2005, 2010 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CONTEXT_H
+#define CONTEXT_H
+
+#include "gpgme.h"
+#include "engine.h"
+#include "wait.h"
+#include "sema.h"
+
+
+extern gpgme_error_t _gpgme_selftest;
+
+/* Operations might require to remember arbitrary information and data
+ objects during invocations of the status handler. The
+ ctx_op_data structure provides a generic framework to hook in
+ such additional data. */
+typedef enum
+ {
+ OPDATA_DECRYPT, OPDATA_SIGN, OPDATA_ENCRYPT, OPDATA_PASSPHRASE,
+ OPDATA_IMPORT, OPDATA_GENKEY, OPDATA_KEYLIST, OPDATA_EDIT,
+ OPDATA_VERIFY, OPDATA_TRUSTLIST, OPDATA_ASSUAN, OPDATA_VFS_MOUNT,
+ OPDATA_PASSWD
+ } ctx_op_data_id_t;
+
+
+/* "gpgmeres" in ASCII. */
+#define CTX_OP_DATA_MAGIC 0x736572656d677067ULL
+struct ctx_op_data
+{
+ /* A magic word just to make sure people don't deallocate something
+ that ain't a result structure. */
+ unsigned long long magic;
+
+ /* The next element in the linked list, or NULL if this is the last
+ element. Used by op data structures linked into a context. */
+ struct ctx_op_data *next;
+
+ /* The type of the hook data, which can be used by a routine to
+ lookup the hook data. */
+ ctx_op_data_id_t type;
+
+ /* The function to release HOOK and all its associated resources.
+ Can be NULL if no special deallocation routine is necessary. */
+ void (*cleanup) (void *hook);
+
+ /* The hook that points to the operation data. */
+ void *hook;
+
+ /* The number of outstanding references. */
+ int references;
+};
+typedef struct ctx_op_data *ctx_op_data_t;
+
+
+/* The context defines an environment in which crypto operations can
+ be performed (sequentially). */
+struct gpgme_context
+{
+ DECLARE_LOCK (lock);
+
+ /* True if the context was canceled asynchronously. */
+ int canceled;
+
+ /* The engine info for this context. */
+ gpgme_engine_info_t engine_info;
+
+ /* The protocol used by this context. */
+ gpgme_protocol_t protocol;
+
+ /* The running engine process. */
+ engine_t engine;
+
+ /* Engine's sub protocol. */
+ gpgme_protocol_t sub_protocol;
+
+ /* True if armor mode should be used. */
+ unsigned int use_armor : 1;
+
+ /* True if text mode should be used. */
+ unsigned int use_textmode : 1;
+
+ /* Flags for keylist mode. */
+ gpgme_keylist_mode_t keylist_mode;
+
+ /* Number of certs to be included. */
+ unsigned int include_certs;
+
+ /* The number of keys in signers. */
+ unsigned int signers_len;
+
+ /* Size of the following array. */
+ unsigned int signers_size;
+ gpgme_key_t *signers;
+
+ /* The signature notations for this context. */
+ gpgme_sig_notation_t sig_notations;
+
+ /* The locale for the pinentry. */
+ char *lc_ctype;
+ char *lc_messages;
+
+ /* The operation data hooked into the context. */
+ ctx_op_data_t op_data;
+
+ /* The user provided passphrase callback and its hook value. */
+ gpgme_passphrase_cb_t passphrase_cb;
+ void *passphrase_cb_value;
+
+ /* The user provided progress callback and its hook value. */
+ gpgme_progress_cb_t progress_cb;
+ void *progress_cb_value;
+
+ /* A list of file descriptors in active use by the current
+ operation. */
+ struct fd_table fdt;
+ struct gpgme_io_cbs io_cbs;
+};
+
+#endif /* CONTEXT_H */
diff --git a/src/conversion.c b/src/conversion.c
new file mode 100644
index 0000000..356200c
--- /dev/null
+++ b/src/conversion.c
@@ -0,0 +1,414 @@
+/* conversion.c - String conversion helper functions.
+ Copyright (C) 2000 Werner Koch (dd9jn)
+ Copyright (C) 2001, 2002, 2003, 2004, 2007 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_SYS_TYPES_H
+ /* Solaris 8 needs sys/types.h before time.h. */
+# include <sys/types.h>
+#endif
+#include <time.h>
+#include <errno.h>
+
+#include "gpgme.h"
+#include "util.h"
+#include "debug.h"
+
+#define atoi_1(p) (*(p) - '0' )
+#define atoi_2(p) ((atoi_1(p) * 10) + atoi_1((p)+1))
+#define atoi_4(p) ((atoi_2(p) * 100) + atoi_2((p)+2))
+
+
+
+/* Convert two hexadecimal digits from STR to the value they
+ represent. Returns -1 if one of the characters is not a
+ hexadecimal digit. */
+int
+_gpgme_hextobyte (const char *str)
+{
+ int val = 0;
+ int i;
+
+#define NROFHEXDIGITS 2
+ for (i = 0; i < NROFHEXDIGITS; i++)
+ {
+ if (*str >= '0' && *str <= '9')
+ val += *str - '0';
+ else if (*str >= 'A' && *str <= 'F')
+ val += 10 + *str - 'A';
+ else if (*str >= 'a' && *str <= 'f')
+ val += 10 + *str - 'a';
+ else
+ return -1;
+ if (i < NROFHEXDIGITS - 1)
+ val *= 16;
+ str++;
+ }
+ return val;
+}
+
+
+/* Decode the C formatted string SRC and store the result in the
+ buffer *DESTP which is LEN bytes long. If LEN is zero, then a
+ large enough buffer is allocated with malloc and *DESTP is set to
+ the result. Currently, LEN is only used to specify if allocation
+ is desired or not, the caller is expected to make sure that *DESTP
+ is large enough if LEN is not zero. */
+gpgme_error_t
+_gpgme_decode_c_string (const char *src, char **destp, size_t len)
+{
+ char *dest;
+
+ /* Set up the destination buffer. */
+ if (len)
+ {
+ if (len < strlen (src) + 1)
+ return gpg_error (GPG_ERR_INTERNAL);
+
+ dest = *destp;
+ }
+ else
+ {
+ /* The converted string will never be larger than the original
+ string. */
+ dest = malloc (strlen (src) + 1);
+ if (!dest)
+ return gpg_error_from_syserror ();
+
+ *destp = dest;
+ }
+
+ /* Convert the string. */
+ while (*src)
+ {
+ if (*src != '\\')
+ {
+ *(dest++) = *(src++);
+ continue;
+ }
+
+ switch (src[1])
+ {
+#define DECODE_ONE(match,result) \
+ case match: \
+ src += 2; \
+ *(dest++) = result; \
+ break;
+
+ DECODE_ONE ('\'', '\'');
+ DECODE_ONE ('\"', '\"');
+ DECODE_ONE ('\?', '\?');
+ DECODE_ONE ('\\', '\\');
+ DECODE_ONE ('a', '\a');
+ DECODE_ONE ('b', '\b');
+ DECODE_ONE ('f', '\f');
+ DECODE_ONE ('n', '\n');
+ DECODE_ONE ('r', '\r');
+ DECODE_ONE ('t', '\t');
+ DECODE_ONE ('v', '\v');
+
+ case 'x':
+ {
+ int val = _gpgme_hextobyte (&src[2]);
+
+ if (val == -1)
+ {
+ /* Should not happen. */
+ *(dest++) = *(src++);
+ *(dest++) = *(src++);
+ if (*src)
+ *(dest++) = *(src++);
+ if (*src)
+ *(dest++) = *(src++);
+ }
+ else
+ {
+ if (!val)
+ {
+ /* A binary zero is not representable in a C
+ string. */
+ *(dest++) = '\\';
+ *(dest++) = '0';
+ }
+ else
+ *((unsigned char *) dest++) = val;
+ src += 4;
+ }
+ }
+ break;
+
+ default:
+ {
+ /* Should not happen. */
+ *(dest++) = *(src++);
+ *(dest++) = *(src++);
+ }
+ }
+ }
+ *(dest++) = 0;
+
+ return 0;
+}
+
+
+/* Decode the percent escaped string SRC and store the result in the
+ buffer *DESTP which is LEN bytes long. If LEN is zero, then a
+ large enough buffer is allocated with malloc and *DESTP is set to
+ the result. Currently, LEN is only used to specify if allocation
+ is desired or not, the caller is expected to make sure that *DESTP
+ is large enough if LEN is not zero. If BINARY is 1, then '\0'
+ characters are allowed in the output. */
+gpgme_error_t
+_gpgme_decode_percent_string (const char *src, char **destp, size_t len,
+ int binary)
+{
+ char *dest;
+
+ /* Set up the destination buffer. */
+ if (len)
+ {
+ if (len < strlen (src) + 1)
+ return gpg_error (GPG_ERR_INTERNAL);
+
+ dest = *destp;
+ }
+ else
+ {
+ /* The converted string will never be larger than the original
+ string. */
+ dest = malloc (strlen (src) + 1);
+ if (!dest)
+ return gpg_error_from_syserror ();
+
+ *destp = dest;
+ }
+
+ /* Convert the string. */
+ while (*src)
+ {
+ if (*src != '%')
+ {
+ *(dest++) = *(src++);
+ continue;
+ }
+ else
+ {
+ int val = _gpgme_hextobyte (&src[1]);
+
+ if (val == -1)
+ {
+ /* Should not happen. */
+ *(dest++) = *(src++);
+ if (*src)
+ *(dest++) = *(src++);
+ if (*src)
+ *(dest++) = *(src++);
+ }
+ else
+ {
+ if (!val && !binary)
+ {
+ /* A binary zero is not representable in a C
+ string. */
+ *(dest++) = '\\';
+ *(dest++) = '0';
+ }
+ else
+ *((unsigned char *) dest++) = val;
+ src += 3;
+ }
+ }
+ }
+ *(dest++) = 0;
+
+ return 0;
+}
+
+
+/* Encode the string SRC with percent escaping and store the result in
+ the buffer *DESTP which is LEN bytes long. If LEN is zero, then a
+ large enough buffer is allocated with malloc and *DESTP is set to
+ the result. Currently, LEN is only used to specify if allocation
+ is desired or not, the caller is expected to make sure that *DESTP
+ is large enough if LEN is not zero. If BINARY is 1, then '\0'
+ characters are allowed in the output. */
+gpgme_error_t
+_gpgme_encode_percent_string (const char *src, char **destp, size_t len)
+{
+ size_t destlen;
+ char *dest;
+ const char *str;
+
+ destlen = 0;
+ str = src;
+ /* We percent-escape the + character because the user might need a
+ "percent plus" escaped string (special gpg format). But we
+ percent-escape the space character, which works with and without
+ the special plus format. */
+ while (*str)
+ {
+ if (*str == '+' || *str == '\"' || *str == '%'
+ || *(const unsigned char *)str <= 0x20)
+ destlen += 3;
+ else
+ destlen++;
+ str++;
+ }
+ /* Terminating nul byte. */
+ destlen++;
+
+ /* Set up the destination buffer. */
+ if (len)
+ {
+ if (len < destlen);
+ return gpg_error (GPG_ERR_INTERNAL);
+
+ dest = *destp;
+ }
+ else
+ {
+ /* The converted string will never be larger than the original
+ string. */
+ dest = malloc (destlen);
+ if (!dest)
+ return gpg_error_from_syserror ();
+
+ *destp = dest;
+ }
+
+ /* Convert the string. */
+ while (*src)
+ {
+ if (*src == '+' || *src == '\"' || *src == '%'
+ || *(const unsigned char *)src <= 0x20)
+ {
+ snprintf (dest, 4, "%%%02X", *(unsigned char *)src);
+ dest += 3;
+ }
+ else
+ *(dest++) = *src;
+ src++;
+ }
+ *(dest++) = 0;
+
+ return 0;
+}
+
+
+#ifdef HAVE_W32_SYSTEM
+static time_t
+_gpgme_timegm (struct tm *tm)
+{
+ /* This one is thread safe. */
+ SYSTEMTIME st;
+ FILETIME ft;
+ unsigned long long cnsecs;
+
+ st.wYear = tm->tm_year + 1900;
+ st.wMonth = tm->tm_mon + 1;
+ st.wDay = tm->tm_mday;
+ st.wHour = tm->tm_hour;
+ st.wMinute = tm->tm_min;
+ st.wSecond = tm->tm_sec;
+ st.wMilliseconds = 0; /* Not available. */
+ st.wDayOfWeek = 0; /* Ignored. */
+
+ /* System time is UTC thus the conversion is pretty easy. */
+ if (!SystemTimeToFileTime (&st, &ft))
+ {
+ gpg_err_set_errno (EINVAL);
+ return (time_t)(-1);
+ }
+
+ cnsecs = (((unsigned long long)ft.dwHighDateTime << 32)
+ | ft.dwLowDateTime);
+ cnsecs -= 116444736000000000ULL; /* The filetime epoch is 1601-01-01. */
+ return (time_t)(cnsecs / 10000000ULL);
+}
+#endif
+
+
+/* Parse the string TIMESTAMP into a time_t. The string may either be
+ seconds since Epoch or in the ISO 8601 format like
+ "20390815T143012". Returns 0 for an empty string or seconds since
+ Epoch. Leading spaces are skipped. If ENDP is not NULL, it will
+ point to the next non-parsed character in TIMESTRING. */
+time_t
+_gpgme_parse_timestamp (const char *timestamp, char **endp)
+{
+ /* Need to skip leading spaces, because that is what strtoul does
+ but not our ISO 8601 checking code. */
+ while (*timestamp && *timestamp== ' ')
+ timestamp++;
+ if (!*timestamp)
+ return 0;
+
+ if (strlen (timestamp) >= 15 && timestamp[8] == 'T')
+ {
+ struct tm buf;
+ int year;
+
+ year = atoi_4 (timestamp);
+ if (year < 1900)
+ return (time_t)(-1);
+
+ if (endp)
+ *endp = (char*)(timestamp + 15);
+
+ /* Fixme: We would better use a configure test to see whether
+ mktime can handle dates beyond 2038. */
+ if (sizeof (time_t) <= 4 && year >= 2038)
+ return (time_t)2145914603; /* 2037-12-31 23:23:23 */
+
+ memset (&buf, 0, sizeof buf);
+ buf.tm_year = year - 1900;
+ buf.tm_mon = atoi_2 (timestamp+4) - 1;
+ buf.tm_mday = atoi_2 (timestamp+6);
+ buf.tm_hour = atoi_2 (timestamp+9);
+ buf.tm_min = atoi_2 (timestamp+11);
+ buf.tm_sec = atoi_2 (timestamp+13);
+
+#ifdef HAVE_W32_SYSTEM
+ return _gpgme_timegm (&buf);
+#else
+#ifdef HAVE_TIMEGM
+ return timegm (&buf);
+#else
+ {
+ time_t tim;
+
+ putenv ("TZ=UTC");
+ tim = mktime (&buf);
+#ifdef __GNUC__
+#warning fixme: we must somehow reset TZ here. It is not threadsafe anyway.
+#endif
+ return tim;
+ }
+#endif /* !HAVE_TIMEGM */
+#endif /* !HAVE_W32_SYSTEM */
+ }
+ else
+ return (time_t)strtoul (timestamp, endp, 10);
+}
diff --git a/src/data-compat.c b/src/data-compat.c
new file mode 100644
index 0000000..39c743e
--- /dev/null
+++ b/src/data-compat.c
@@ -0,0 +1,254 @@
+/* data-compat.c - Compatibility interfaces for data objects.
+ Copyright (C) 2002, 2003, 2004, 2007 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#include <stdlib.h>
+
+#include "data.h"
+#include "util.h"
+#include "debug.h"
+
+
+/* Create a new data buffer filled with LENGTH bytes starting from
+ OFFSET within the file FNAME or stream STREAM (exactly one must be
+ non-zero). */
+gpgme_error_t
+gpgme_data_new_from_filepart (gpgme_data_t *r_dh, const char *fname,
+ FILE *stream, off_t offset, size_t length)
+{
+#if defined (HAVE_W32CE_SYSTEM) && defined (_MSC_VER)
+ return gpgme_error (GPG_ERR_NOT_IMPLEMENTED);
+#else
+ gpgme_error_t err;
+ char *buf = NULL;
+ int res;
+
+ TRACE_BEG4 (DEBUG_DATA, "gpgme_data_new_from_filepart", r_dh,
+ "file_name=%s, stream=%p, offset=%lli, length=%u",
+ fname, stream, offset, length);
+
+ if (stream && fname)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ if (fname)
+ stream = fopen (fname, "rb");
+ if (!stream)
+ return TRACE_ERR (gpg_error_from_errno (errno));
+
+#ifdef HAVE_FSEEKO
+ res = fseeko (stream, offset, SEEK_SET);
+#else
+ /* FIXME: Check for overflow, or at least bail at compilation. */
+ res = fseek (stream, offset, SEEK_SET);
+#endif
+
+ if (res)
+ {
+ int saved_errno = errno;
+ if (fname)
+ fclose (stream);
+ return TRACE_ERR (gpg_error_from_errno (saved_errno));
+ }
+
+ buf = malloc (length);
+ if (!buf)
+ {
+ int saved_errno = errno;
+ if (fname)
+ fclose (stream);
+ return TRACE_ERR (gpg_error_from_errno (saved_errno));
+ }
+
+ while (fread (buf, length, 1, stream) < 1
+ && ferror (stream) && errno == EINTR);
+ if (ferror (stream))
+ {
+ int saved_errno = errno;
+ if (buf)
+ free (buf);
+ if (fname)
+ fclose (stream);
+ return TRACE_ERR (gpg_error_from_errno (saved_errno));
+ }
+
+ if (fname)
+ fclose (stream);
+
+ err = gpgme_data_new (r_dh);
+ if (err)
+ {
+ if (buf)
+ free (buf);
+ return err;
+ }
+
+ (*r_dh)->data.mem.buffer = buf;
+ (*r_dh)->data.mem.size = length;
+ (*r_dh)->data.mem.length = length;
+
+ return TRACE_SUC1 ("r_dh=%p", *r_dh);
+#endif
+}
+
+
+/* Create a new data buffer filled with the content of file FNAME.
+ COPY must be non-zero (delayed reads are not supported yet). */
+gpgme_error_t
+gpgme_data_new_from_file (gpgme_data_t *r_dh, const char *fname, int copy)
+{
+#if defined (HAVE_W32CE_SYSTEM) && defined (_MSC_VER)
+ return gpgme_error (GPG_ERR_NOT_IMPLEMENTED);
+#else
+ gpgme_error_t err;
+ struct stat statbuf;
+ TRACE_BEG3 (DEBUG_DATA, "gpgme_data_new_from_filepart", r_dh,
+ "file_name=%s, copy=%i (%s)", fname, copy, copy ? "yes" : "no");
+
+ if (!fname || !copy)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ if (stat (fname, &statbuf) < 0)
+ return TRACE_ERR (gpg_error_from_errno (errno));
+
+ err = gpgme_data_new_from_filepart (r_dh, fname, NULL, 0, statbuf.st_size);
+ return TRACE_ERR (err);
+#endif
+}
+
+
+static int
+gpgme_error_to_errno (gpgme_error_t err)
+{
+ int res = gpg_err_code_to_errno (err);
+
+ if (!err)
+ {
+ switch (gpg_err_code (err))
+ {
+ case GPG_ERR_EOF:
+ res = 0;
+ break;
+ case GPG_ERR_INV_VALUE:
+ res = EINVAL;
+ break;
+ case GPG_ERR_NOT_SUPPORTED:
+ res = ENOSYS;
+ break;
+ default:
+ /* FIXME: Yeah, well. */
+ res = EINVAL;
+ break;
+ }
+ }
+ TRACE3 (DEBUG_DATA, "gpgme:gpgme_error_to_errno", 0,
+ "mapping %s <%s> to: %s", gpgme_strerror (err),
+ gpgme_strsource (err), strerror (res));
+ gpg_err_set_errno (res);
+ return res ? -1 : 0;
+}
+
+
+static ssize_t
+old_user_read (gpgme_data_t dh, void *buffer, size_t size)
+{
+ gpgme_error_t err;
+ size_t amt;
+ TRACE_BEG2 (DEBUG_DATA, "gpgme:old_user_read", dh,
+ "buffer=%p, size=%u", buffer, size);
+
+ err = (*dh->data.old_user.cb) (dh->data.old_user.handle,
+ buffer, size, &amt);
+ if (err)
+ return TRACE_SYSRES (gpgme_error_to_errno (err));
+ return TRACE_SYSRES (amt);
+}
+
+
+static off_t
+old_user_seek (gpgme_data_t dh, off_t offset, int whence)
+{
+ gpgme_error_t err;
+ TRACE_BEG2 (DEBUG_DATA, "gpgme:old_user_seek", dh,
+ "offset=%llu, whence=%i", offset, whence);
+
+ if (whence != SEEK_SET || offset)
+ {
+ gpg_err_set_errno (EINVAL);
+ return TRACE_SYSRES (-1);
+ }
+ err = (*dh->data.old_user.cb) (dh->data.old_user.handle, NULL, 0, NULL);
+ if (err)
+ return TRACE_SYSRES (gpgme_error_to_errno (err));
+ return TRACE_SYSRES (0);
+}
+
+
+static struct _gpgme_data_cbs old_user_cbs =
+ {
+ old_user_read,
+ NULL,
+ old_user_seek,
+ NULL
+ };
+
+
+/* Create a new data buffer which retrieves the data from the callback
+ function READ_CB. */
+gpgme_error_t
+gpgme_data_new_with_read_cb (gpgme_data_t *r_dh,
+ int (*read_cb) (void *, char *, size_t, size_t *),
+ void *read_cb_value)
+{
+ gpgme_error_t err;
+ TRACE_BEG2 (DEBUG_DATA, "gpgme_data_new_with_read_cb", r_dh,
+ "read_cb=%p/%p", read_cb, read_cb_value);
+
+ err = _gpgme_data_new (r_dh, &old_user_cbs);
+
+ if (err)
+ return TRACE_ERR (err);
+
+ (*r_dh)->data.old_user.cb = read_cb;
+ (*r_dh)->data.old_user.handle = read_cb_value;
+ return TRACE_ERR (0);
+}
+
+
+gpgme_error_t
+gpgme_data_rewind (gpgme_data_t dh)
+{
+ gpgme_error_t err;
+ TRACE_BEG (DEBUG_DATA, "gpgme_data_rewind", dh);
+
+ err = (gpgme_data_seek (dh, 0, SEEK_SET) == -1)
+ ? gpg_error_from_errno (errno) : 0;
+
+ return TRACE_ERR (err);
+}
diff --git a/src/data-fd.c b/src/data-fd.c
new file mode 100644
index 0000000..388b45c
--- /dev/null
+++ b/src/data-fd.c
@@ -0,0 +1,142 @@
+/* data-fd.c - A file descripor based data object.
+ Copyright (C) 2002, 2004 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+#include "debug.h"
+#include "data.h"
+
+
+
+#if defined(HAVE_W32CE_SYSTEM) && !defined(__MINGW32CE__)
+/* We need to provide replacements for read, write and lseek. They
+ are taken from the cegcc runtime files, written by Pedro Alves
+ <pedro_alves@portugalmail.pt> in Feb 2007 and placed in the public
+ domain. (cf. cegcc/src/mingw/mingwex/wince/) */
+
+#include <windows.h>
+
+static int
+read (int fildes, void *buf, unsigned int bufsize)
+{
+ DWORD NumberOfBytesRead;
+ if (bufsize > 0x7fffffff)
+ bufsize = 0x7fffffff;
+ if (!ReadFile ((HANDLE) fildes, buf, bufsize, &NumberOfBytesRead, NULL))
+ return -1;
+ return (int) NumberOfBytesRead;
+}
+
+static int
+write (int fildes, const void *buf, unsigned int bufsize)
+{
+ DWORD NumberOfBytesWritten;
+ if (bufsize > 0x7fffffff)
+ bufsize = 0x7fffffff;
+ if (!WriteFile ((HANDLE) fildes, buf, bufsize, &NumberOfBytesWritten, NULL))
+ return -1;
+ return (int) NumberOfBytesWritten;
+}
+
+static long
+lseek (int fildes, long offset, int whence)
+{
+ DWORD mode;
+ switch (whence)
+ {
+ case SEEK_SET:
+ mode = FILE_BEGIN;
+ break;
+ case SEEK_CUR:
+ mode = FILE_CURRENT;
+ break;
+ case SEEK_END:
+ mode = FILE_END;
+ break;
+ default:
+ /* Specify an invalid mode so SetFilePointer catches it. */
+ mode = (DWORD)-1;
+ }
+ return (long) SetFilePointer ((HANDLE) fildes, offset, NULL, mode);
+}
+#endif /*HAVE_W32CE_SYSTEM && !__MINGW32CE__*/
+
+
+
+static ssize_t
+fd_read (gpgme_data_t dh, void *buffer, size_t size)
+{
+ return read (dh->data.fd, buffer, size);
+}
+
+
+static ssize_t
+fd_write (gpgme_data_t dh, const void *buffer, size_t size)
+{
+ return write (dh->data.fd, buffer, size);
+}
+
+
+static off_t
+fd_seek (gpgme_data_t dh, off_t offset, int whence)
+{
+ return lseek (dh->data.fd, offset, whence);
+}
+
+
+static int
+fd_get_fd (gpgme_data_t dh)
+{
+ return (dh->data.fd);
+}
+
+
+static struct _gpgme_data_cbs fd_cbs =
+ {
+ fd_read,
+ fd_write,
+ fd_seek,
+ NULL,
+ fd_get_fd
+ };
+
+
+gpgme_error_t
+gpgme_data_new_from_fd (gpgme_data_t *r_dh, int fd)
+{
+ gpgme_error_t err;
+ TRACE_BEG1 (DEBUG_DATA, "gpgme_data_new_from_fd", r_dh, "fd=0x%x", fd);
+
+ err = _gpgme_data_new (r_dh, &fd_cbs);
+ if (err)
+ return TRACE_ERR (err);
+
+ (*r_dh)->data.fd = fd;
+ return TRACE_SUC1 ("dh=%p", *r_dh);
+}
diff --git a/src/data-mem.c b/src/data-mem.c
new file mode 100644
index 0000000..eda695a
--- /dev/null
+++ b/src/data-mem.c
@@ -0,0 +1,282 @@
+/* data-mem.c - A memory based data object.
+ Copyright (C) 2002, 2003, 2004, 2007 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#include <assert.h>
+#include <string.h>
+
+#include "data.h"
+#include "util.h"
+#include "debug.h"
+
+
+static ssize_t
+mem_read (gpgme_data_t dh, void *buffer, size_t size)
+{
+ size_t amt = dh->data.mem.length - dh->data.mem.offset;
+ const char *src;
+
+ if (!amt)
+ return 0;
+
+ if (size < amt)
+ amt = size;
+
+ src = dh->data.mem.buffer ? dh->data.mem.buffer : dh->data.mem.orig_buffer;
+ memcpy (buffer, src + dh->data.mem.offset, amt);
+ dh->data.mem.offset += amt;
+ return amt;
+}
+
+
+static ssize_t
+mem_write (gpgme_data_t dh, const void *buffer, size_t size)
+{
+ size_t unused;
+
+ if (!dh->data.mem.buffer && dh->data.mem.orig_buffer)
+ {
+ size_t new_size = dh->data.mem.size;
+ char *new_buffer;
+
+ if (new_size < dh->data.mem.offset + size)
+ new_size = dh->data.mem.offset + size;
+
+ new_buffer = malloc (new_size);
+ if (!new_buffer)
+ return -1;
+ memcpy (new_buffer, dh->data.mem.orig_buffer, dh->data.mem.length);
+
+ dh->data.mem.buffer = new_buffer;
+ dh->data.mem.size = new_size;
+ }
+
+ unused = dh->data.mem.size - dh->data.mem.offset;
+ if (unused < size)
+ {
+ /* Allocate a large enough buffer with exponential backoff. */
+#define INITIAL_ALLOC 512
+ size_t new_size = dh->data.mem.size
+ ? (2 * dh->data.mem.size) : INITIAL_ALLOC;
+ char *new_buffer;
+
+ if (new_size < dh->data.mem.offset + size)
+ new_size = dh->data.mem.offset + size;
+
+ new_buffer = realloc (dh->data.mem.buffer, new_size);
+ if (!new_buffer && new_size > dh->data.mem.offset + size)
+ {
+ /* Maybe we were too greedy, try again. */
+ new_size = dh->data.mem.offset + size;
+ new_buffer = realloc (dh->data.mem.buffer, new_size);
+ }
+ if (!new_buffer)
+ return -1;
+ dh->data.mem.buffer = new_buffer;
+ dh->data.mem.size = new_size;
+ }
+
+ memcpy (dh->data.mem.buffer + dh->data.mem.offset, buffer, size);
+ dh->data.mem.offset += size;
+ if (dh->data.mem.length < dh->data.mem.offset)
+ dh->data.mem.length = dh->data.mem.offset;
+ return size;
+}
+
+
+static off_t
+mem_seek (gpgme_data_t dh, off_t offset, int whence)
+{
+ switch (whence)
+ {
+ case SEEK_SET:
+ if (offset < 0 || offset > dh->data.mem.length)
+ {
+ gpg_err_set_errno (EINVAL);
+ return -1;
+ }
+ dh->data.mem.offset = offset;
+ break;
+ case SEEK_CUR:
+ if ((offset > 0 && dh->data.mem.length - dh->data.mem.offset < offset)
+ || (offset < 0 && dh->data.mem.offset < -offset))
+ {
+ gpg_err_set_errno (EINVAL);
+ return -1;
+ }
+ dh->data.mem.offset += offset;
+ break;
+ case SEEK_END:
+ if (offset > 0 || -offset > dh->data.mem.length)
+ {
+ gpg_err_set_errno (EINVAL);
+ return -1;
+ }
+ dh->data.mem.offset = dh->data.mem.length - offset;
+ break;
+ default:
+ gpg_err_set_errno (EINVAL);
+ return -1;
+ }
+ return dh->data.mem.offset;
+}
+
+
+static void
+mem_release (gpgme_data_t dh)
+{
+ if (dh->data.mem.buffer)
+ free (dh->data.mem.buffer);
+}
+
+
+static struct _gpgme_data_cbs mem_cbs =
+ {
+ mem_read,
+ mem_write,
+ mem_seek,
+ mem_release,
+ NULL
+ };
+
+
+/* Create a new data buffer and return it in R_DH. */
+gpgme_error_t
+gpgme_data_new (gpgme_data_t *r_dh)
+{
+ gpgme_error_t err;
+ TRACE_BEG (DEBUG_DATA, "gpgme_data_new", r_dh);
+
+ err = _gpgme_data_new (r_dh, &mem_cbs);
+
+ if (err)
+ return TRACE_ERR (err);
+
+ return TRACE_SUC1 ("dh=%p", *r_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. */
+gpgme_error_t
+gpgme_data_new_from_mem (gpgme_data_t *r_dh, const char *buffer,
+ size_t size, int copy)
+{
+ gpgme_error_t err;
+ TRACE_BEG4 (DEBUG_DATA, "gpgme_data_new_from_mem", r_dh,
+ "buffer=%p, size=%u, copy=%i (%s)", buffer, size,
+ copy, copy ? "yes" : "no");
+
+ err = _gpgme_data_new (r_dh, &mem_cbs);
+ if (err)
+ return TRACE_ERR (err);
+
+ if (copy)
+ {
+ char *bufcpy = malloc (size);
+ if (!bufcpy)
+ {
+ int saved_errno = errno;
+ _gpgme_data_release (*r_dh);
+ return TRACE_ERR (gpg_error_from_errno (saved_errno));
+ }
+ memcpy (bufcpy, buffer, size);
+ (*r_dh)->data.mem.buffer = bufcpy;
+ }
+ else
+ (*r_dh)->data.mem.orig_buffer = buffer;
+
+ (*r_dh)->data.mem.size = size;
+ (*r_dh)->data.mem.length = size;
+ return TRACE_SUC1 ("dh=%p", *r_dh);
+}
+
+
+/* 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. */
+char *
+gpgme_data_release_and_get_mem (gpgme_data_t dh, size_t *r_len)
+{
+ char *str = NULL;
+
+ TRACE_BEG1 (DEBUG_DATA, "gpgme_data_release_and_get_mem", dh,
+ "r_len=%p", r_len);
+
+ if (!dh || dh->cbs != &mem_cbs)
+ {
+ gpgme_data_release (dh);
+ TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+ return NULL;
+ }
+
+ str = dh->data.mem.buffer;
+ if (!str && dh->data.mem.orig_buffer)
+ {
+ str = malloc (dh->data.mem.length);
+ if (!str)
+ {
+ int saved_errno = errno;
+ gpgme_data_release (dh);
+ TRACE_ERR (gpg_error_from_errno (saved_errno));
+ return NULL;
+ }
+ memcpy (str, dh->data.mem.orig_buffer, dh->data.mem.length);
+ }
+ else
+ /* Prevent mem_release from releasing the buffer memory. We must
+ not fail from this point. */
+ dh->data.mem.buffer = NULL;
+
+ if (r_len)
+ *r_len = dh->data.mem.length;
+
+ gpgme_data_release (dh);
+
+ if (r_len)
+ {
+ TRACE_SUC2 ("buffer=%p, len=%u", str, *r_len);
+ }
+ else
+ {
+ TRACE_SUC1 ("buffer=%p", str);
+ }
+ return str;
+}
+
+
+/* Release the memory returned by gpgme_data_release_and_get_mem(). */
+void
+gpgme_free (void *buffer)
+{
+ TRACE (DEBUG_DATA, "gpgme_free", buffer);
+
+ if (buffer)
+ free (buffer);
+}
diff --git a/src/data-stream.c b/src/data-stream.c
new file mode 100644
index 0000000..0e84065
--- /dev/null
+++ b/src/data-stream.c
@@ -0,0 +1,108 @@
+/* data-stream.c - A stream based data object.
+ Copyright (C) 2002, 2004 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+#include "debug.h"
+#include "data.h"
+
+
+static ssize_t
+stream_read (gpgme_data_t dh, void *buffer, size_t size)
+{
+ size_t amt = fread (buffer, 1, size, dh->data.stream);
+ if (amt > 0)
+ return amt;
+ return ferror (dh->data.stream) ? -1 : 0;
+}
+
+
+static ssize_t
+stream_write (gpgme_data_t dh, const void *buffer, size_t size)
+{
+ size_t amt = fwrite (buffer, 1, size, dh->data.stream);
+ if (amt > 0)
+ return amt;
+ return ferror (dh->data.stream) ? -1 : 0;
+}
+
+
+static off_t
+stream_seek (gpgme_data_t dh, off_t offset, int whence)
+{
+ int err;
+
+#ifdef HAVE_FSEEKO
+ err = fseeko (dh->data.stream, offset, whence);
+#else
+ /* FIXME: Check for overflow, or at least bail at compilation. */
+ err = fseek (dh->data.stream, offset, whence);
+#endif
+
+ if (err)
+ return -1;
+
+#ifdef HAVE_FSEEKO
+ return ftello (dh->data.stream);
+#else
+ return ftell (dh->data.stream);
+#endif
+}
+
+
+static int
+stream_get_fd (gpgme_data_t dh)
+{
+ fflush (dh->data.stream);
+ return fileno (dh->data.stream);
+}
+
+
+static struct _gpgme_data_cbs stream_cbs =
+ {
+ stream_read,
+ stream_write,
+ stream_seek,
+ NULL,
+ stream_get_fd
+ };
+
+
+gpgme_error_t
+gpgme_data_new_from_stream (gpgme_data_t *r_dh, FILE *stream)
+{
+ gpgme_error_t err;
+ TRACE_BEG1 (DEBUG_DATA, "gpgme_data_new_from_stream", r_dh, "stream=%p",
+ stream);
+
+ err = _gpgme_data_new (r_dh, &stream_cbs);
+ if (err)
+ return TRACE_ERR (err);
+
+ (*r_dh)->data.stream = stream;
+ return TRACE_SUC1 ("dh=%p", *r_dh);
+}
diff --git a/src/data-user.c b/src/data-user.c
new file mode 100644
index 0000000..65065e7
--- /dev/null
+++ b/src/data-user.c
@@ -0,0 +1,104 @@
+/* data-user.c - A user callback based data object.
+ Copyright (C) 2002, 2004 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#include <errno.h>
+
+#include "debug.h"
+#include "data.h"
+
+
+static ssize_t
+user_read (gpgme_data_t dh, void *buffer, size_t size)
+{
+ if (!dh->data.user.cbs->read)
+ {
+ gpg_err_set_errno (EBADF);
+ return -1;
+ }
+
+ return (*dh->data.user.cbs->read) (dh->data.user.handle, buffer, size);
+}
+
+
+static ssize_t
+user_write (gpgme_data_t dh, const void *buffer, size_t size)
+{
+ if (!dh->data.user.cbs->write)
+ {
+ gpg_err_set_errno (EBADF);
+ return -1;
+ }
+
+ return (*dh->data.user.cbs->write) (dh->data.user.handle, buffer, size);
+}
+
+
+static off_t
+user_seek (gpgme_data_t dh, off_t offset, int whence)
+{
+ if (!dh->data.user.cbs->seek)
+ {
+ gpg_err_set_errno (EBADF);
+ return -1;
+ }
+
+ return (*dh->data.user.cbs->seek) (dh->data.user.handle, offset, whence);
+}
+
+
+static void
+user_release (gpgme_data_t dh)
+{
+ if (dh->data.user.cbs->release)
+ (*dh->data.user.cbs->release) (dh->data.user.handle);
+}
+
+
+static struct _gpgme_data_cbs user_cbs =
+ {
+ user_read,
+ user_write,
+ user_seek,
+ user_release,
+ NULL
+ };
+
+
+gpgme_error_t
+gpgme_data_new_from_cbs (gpgme_data_t *r_dh, gpgme_data_cbs_t cbs, void *handle)
+{
+ gpgme_error_t err;
+ TRACE_BEG1 (DEBUG_DATA, "gpgme_data_new_from_cbs", r_dh, "handle=%p", handle);
+
+ err = _gpgme_data_new (r_dh, &user_cbs);
+ if (err)
+ return TRACE_ERR (err);
+
+ (*r_dh)->data.user.cbs = cbs;
+ (*r_dh)->data.user.handle = handle;
+ return TRACE_SUC1 ("dh=%p", *r_dh);
+}
diff --git a/src/data.c b/src/data.c
new file mode 100644
index 0000000..0a62910
--- /dev/null
+++ b/src/data.c
@@ -0,0 +1,336 @@
+/* data.c - An abstraction for data objects.
+ Copyright (C) 2002, 2003, 2004, 2005, 2007 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#include <errno.h>
+#include <string.h>
+
+#include "gpgme.h"
+#include "data.h"
+#include "util.h"
+#include "ops.h"
+#include "priv-io.h"
+#include "debug.h"
+
+
+gpgme_error_t
+_gpgme_data_new (gpgme_data_t *r_dh, struct _gpgme_data_cbs *cbs)
+{
+ gpgme_data_t dh;
+
+ if (!r_dh)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ *r_dh = NULL;
+ dh = calloc (1, sizeof (*dh));
+ if (!dh)
+ return gpg_error_from_syserror ();
+
+ dh->cbs = cbs;
+
+ *r_dh = dh;
+ return 0;
+}
+
+
+void
+_gpgme_data_release (gpgme_data_t dh)
+{
+ if (!dh)
+ return;
+
+ if (dh->file_name)
+ free (dh->file_name);
+ free (dh);
+}
+
+
+/* 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. */
+ssize_t
+gpgme_data_read (gpgme_data_t dh, void *buffer, size_t size)
+{
+ ssize_t res;
+ TRACE_BEG2 (DEBUG_DATA, "gpgme_data_read", dh,
+ "buffer=%p, size=%u", buffer, size);
+
+ if (!dh)
+ {
+ gpg_err_set_errno (EINVAL);
+ return TRACE_SYSRES (-1);
+ }
+ if (!dh->cbs->read)
+ {
+ gpg_err_set_errno (ENOSYS);
+ return TRACE_SYSRES (-1);
+ }
+ do
+ res = (*dh->cbs->read) (dh, buffer, size);
+ while (res < 0 && errno == EINTR);
+
+ return TRACE_SYSRES (res);
+}
+
+
+/* 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. */
+ssize_t
+gpgme_data_write (gpgme_data_t dh, const void *buffer, size_t size)
+{
+ ssize_t res;
+ TRACE_BEG2 (DEBUG_DATA, "gpgme_data_write", dh,
+ "buffer=%p, size=%u", buffer, size);
+
+ if (!dh)
+ {
+ gpg_err_set_errno (EINVAL);
+ return TRACE_SYSRES (-1);
+ }
+ if (!dh->cbs->write)
+ {
+ gpg_err_set_errno (ENOSYS);
+ return TRACE_SYSRES (-1);
+ }
+ do
+ res = (*dh->cbs->write) (dh, buffer, size);
+ while (res < 0 && errno == EINTR);
+
+ return TRACE_SYSRES (res);
+}
+
+
+/* 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. */
+off_t
+gpgme_data_seek (gpgme_data_t dh, off_t offset, int whence)
+{
+ TRACE_BEG2 (DEBUG_DATA, "gpgme_data_seek", dh,
+ "offset=%lli, whence=%i", offset, whence);
+
+ if (!dh)
+ {
+ gpg_err_set_errno (EINVAL);
+ return TRACE_SYSRES (-1);
+ }
+ if (!dh->cbs->seek)
+ {
+ gpg_err_set_errno (ENOSYS);
+ return TRACE_SYSRES (-1);
+ }
+
+ /* For relative movement, we must take into account the actual
+ position of the read counter. */
+ if (whence == SEEK_CUR)
+ offset -= dh->pending_len;
+
+ offset = (*dh->cbs->seek) (dh, offset, whence);
+ if (offset >= 0)
+ dh->pending_len = 0;
+
+ return TRACE_SYSRES (offset);
+}
+
+
+/* Release the data object with the handle DH. */
+void
+gpgme_data_release (gpgme_data_t dh)
+{
+ TRACE (DEBUG_DATA, "gpgme_data_release", dh);
+
+ if (!dh)
+ return;
+
+ if (dh->cbs->release)
+ (*dh->cbs->release) (dh);
+ _gpgme_data_release (dh);
+}
+
+
+/* Get the current encoding meta information for the data object with
+ handle DH. */
+gpgme_data_encoding_t
+gpgme_data_get_encoding (gpgme_data_t dh)
+{
+ TRACE1 (DEBUG_DATA, "gpgme_data_get_encoding", dh,
+ "dh->encoding=%i", dh ? dh->encoding : GPGME_DATA_ENCODING_NONE);
+ return dh ? dh->encoding : GPGME_DATA_ENCODING_NONE;
+}
+
+
+/* Set the encoding meta information for the data object with handle
+ DH to ENC. */
+gpgme_error_t
+gpgme_data_set_encoding (gpgme_data_t dh, gpgme_data_encoding_t enc)
+{
+ TRACE_BEG1 (DEBUG_DATA, "gpgme_data_set_encoding", dh,
+ "encoding=%i", enc);
+ if (!dh)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+ if (enc < 0 || enc > GPGME_DATA_ENCODING_URL0)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+ dh->encoding = enc;
+ return TRACE_ERR (0);
+}
+
+
+/* Set the file name associated with the data object with handle DH to
+ FILE_NAME. */
+gpgme_error_t
+gpgme_data_set_file_name (gpgme_data_t dh, const char *file_name)
+{
+ TRACE_BEG1 (DEBUG_DATA, "gpgme_data_set_file_name", dh,
+ "file_name=%s", file_name);
+
+ if (!dh)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ if (dh->file_name)
+ free (dh->file_name);
+
+ if (file_name)
+ {
+ dh->file_name = strdup (file_name);
+ if (!dh->file_name)
+ return TRACE_ERR (gpg_error_from_syserror ());
+ }
+ else
+ dh->file_name = 0;
+
+ return TRACE_ERR (0);
+}
+
+
+/* Get the file name associated with the data object with handle DH,
+ or NULL if there is none. */
+char *
+gpgme_data_get_file_name (gpgme_data_t dh)
+{
+ if (!dh)
+ {
+ TRACE (DEBUG_DATA, "gpgme_data_get_file_name", dh);
+ return NULL;
+ }
+
+ TRACE1 (DEBUG_DATA, "gpgme_data_get_file_name", dh,
+ "dh->file_name=%s", dh->file_name);
+ return dh->file_name;
+}
+
+
+/* Functions to support the wait interface. */
+
+gpgme_error_t
+_gpgme_data_inbound_handler (void *opaque, int fd)
+{
+ struct io_cb_data *data = (struct io_cb_data *) opaque;
+ gpgme_data_t dh = (gpgme_data_t) data->handler_value;
+ char buffer[BUFFER_SIZE];
+ char *bufp = buffer;
+ ssize_t buflen;
+ TRACE_BEG1 (DEBUG_CTX, "_gpgme_data_inbound_handler", dh,
+ "fd=0x%x", fd);
+
+ buflen = _gpgme_io_read (fd, buffer, BUFFER_SIZE);
+ if (buflen < 0)
+ return gpg_error_from_syserror ();
+ if (buflen == 0)
+ {
+ _gpgme_io_close (fd);
+ return TRACE_ERR (0);
+ }
+
+ do
+ {
+ ssize_t amt = gpgme_data_write (dh, bufp, buflen);
+ if (amt == 0 || (amt < 0 && errno != EINTR))
+ return TRACE_ERR (gpg_error_from_syserror ());
+ bufp += amt;
+ buflen -= amt;
+ }
+ while (buflen > 0);
+ return TRACE_ERR (0);
+}
+
+
+gpgme_error_t
+_gpgme_data_outbound_handler (void *opaque, int fd)
+{
+ struct io_cb_data *data = (struct io_cb_data *) opaque;
+ gpgme_data_t dh = (gpgme_data_t) data->handler_value;
+ ssize_t nwritten;
+ TRACE_BEG1 (DEBUG_CTX, "_gpgme_data_outbound_handler", dh,
+ "fd=0x%x", fd);
+
+ if (!dh->pending_len)
+ {
+ ssize_t amt = gpgme_data_read (dh, dh->pending, BUFFER_SIZE);
+ if (amt < 0)
+ return TRACE_ERR (gpg_error_from_syserror ());
+ if (amt == 0)
+ {
+ _gpgme_io_close (fd);
+ return TRACE_ERR (0);
+ }
+ dh->pending_len = amt;
+ }
+
+ nwritten = _gpgme_io_write (fd, dh->pending, dh->pending_len);
+ if (nwritten == -1 && errno == EAGAIN)
+ return TRACE_ERR (0);
+
+ if (nwritten == -1 && errno == EPIPE)
+ {
+ /* Not much we can do. The other end closed the pipe, but we
+ still have data. This should only ever happen if the other
+ end is going to tell us what happened on some other channel.
+ Silently close our end. */
+ _gpgme_io_close (fd);
+ return TRACE_ERR (0);
+ }
+
+ if (nwritten <= 0)
+ return TRACE_ERR (gpg_error_from_syserror ());
+
+ if (nwritten < dh->pending_len)
+ memmove (dh->pending, dh->pending + nwritten, dh->pending_len - nwritten);
+ dh->pending_len -= nwritten;
+ return TRACE_ERR (0);
+}
+
+
+/* Get the file descriptor associated with DH, if possible. Otherwise
+ return -1. */
+int
+_gpgme_data_get_fd (gpgme_data_t dh)
+{
+ if (!dh || !dh->cbs->get_fd)
+ return -1;
+ return (*dh->cbs->get_fd) (dh);
+}
diff --git a/src/data.h b/src/data.h
new file mode 100644
index 0000000..1257a8d
--- /dev/null
+++ b/src/data.h
@@ -0,0 +1,134 @@
+/* data.h - Internal data object abstraction interface.
+ Copyright (C) 2002, 2004, 2005 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#ifndef DATA_H
+#define DATA_H
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#include <limits.h>
+
+#include "gpgme.h"
+
+
+/* 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. */
+typedef ssize_t (*gpgme_data_read_cb) (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. */
+typedef ssize_t (*gpgme_data_write_cb) (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. */
+typedef off_t (*gpgme_data_seek_cb) (gpgme_data_t dh, off_t offset,
+ int whence);
+
+/* Release the data object with the handle DH. */
+typedef void (*gpgme_data_release_cb) (gpgme_data_t dh);
+
+/* Get the FD associated with the handle DH, or -1. */
+typedef int (*gpgme_data_get_fd_cb) (gpgme_data_t dh);
+
+struct _gpgme_data_cbs
+{
+ gpgme_data_read_cb read;
+ gpgme_data_write_cb write;
+ gpgme_data_seek_cb seek;
+ gpgme_data_release_cb release;
+ gpgme_data_get_fd_cb get_fd;
+};
+
+struct gpgme_data
+{
+ struct _gpgme_data_cbs *cbs;
+ gpgme_data_encoding_t encoding;
+
+#ifdef PIPE_BUF
+#define BUFFER_SIZE PIPE_BUF
+#else
+#ifdef _POSIX_PIPE_BUF
+#define BUFFER_SIZE _POSIX_PIPE_BUF
+#else
+#define BUFFER_SIZE 512
+#endif
+#endif
+ char pending[BUFFER_SIZE];
+ int pending_len;
+
+ /* File name of the data object. */
+ char *file_name;
+
+ union
+ {
+ /* For gpgme_data_new_from_fd. */
+ int fd;
+
+ /* For gpgme_data_new_from_stream. */
+ FILE *stream;
+
+ /* For gpgme_data_new_from_cbs. */
+ struct
+ {
+ gpgme_data_cbs_t cbs;
+ void *handle;
+ } user;
+
+ /* For gpgme_data_new_from_mem. */
+ struct
+ {
+ char *buffer;
+ const char *orig_buffer;
+ /* Allocated size of BUFFER. */
+ size_t size;
+ size_t length;
+ off_t offset;
+ } mem;
+
+ /* For gpgme_data_new_from_read_cb. */
+ struct
+ {
+ int (*cb) (void *, char *, size_t, size_t *);
+ void *handle;
+ } old_user;
+ } data;
+};
+
+
+gpgme_error_t _gpgme_data_new (gpgme_data_t *r_dh,
+ struct _gpgme_data_cbs *cbs);
+
+void _gpgme_data_release (gpgme_data_t dh);
+
+/* Get the file descriptor associated with DH, if possible. Otherwise
+ return -1. */
+int _gpgme_data_get_fd (gpgme_data_t dh);
+
+#endif /* DATA_H */
diff --git a/src/debug.c b/src/debug.c
new file mode 100644
index 0000000..34c5d18
--- /dev/null
+++ b/src/debug.c
@@ -0,0 +1,369 @@
+/* debug.c - helpful output in desperate situations
+ Copyright (C) 2000 Werner Koch (dd9jn)
+ Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2009 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ MA 02110-1301, USA. */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#include <ctype.h>
+#include <errno.h>
+#include <time.h>
+#ifndef HAVE_DOSISH_SYSTEM
+# ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+# endif
+# ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+# endif
+# include <fcntl.h>
+#endif
+#include <assert.h>
+
+#include "util.h"
+#include "ath.h"
+#include "sema.h"
+#include "debug.h"
+
+
+/* Lock to serialize initialization of the debug output subsystem and
+ output of actual debug messages. */
+DEFINE_STATIC_LOCK (debug_lock);
+
+/* The amount of detail requested by the user, per environment
+ variable GPGME_DEBUG. */
+static int debug_level;
+
+/* The output stream for the debug messages. */
+static FILE *errfp;
+
+
+#ifdef HAVE_TLS
+#define FRAME_NR
+static __thread int frame_nr = 0;
+#endif
+
+void
+_gpgme_debug_frame_begin (void)
+{
+#ifdef FRAME_NR
+ frame_nr++;
+#endif
+}
+
+void _gpgme_debug_frame_end (void)
+{
+#ifdef FRAME_NR
+ frame_nr--;
+#endif
+}
+
+
+
+/* Remove leading and trailing white spaces. */
+static char *
+trim_spaces (char *str)
+{
+ char *string, *p, *mark;
+
+ string = str;
+ /* Find first non space character. */
+ for (p = string; *p && isspace (*(unsigned char *) p); p++)
+ ;
+ /* Move characters. */
+ for (mark = NULL; (*string = *p); string++, p++)
+ if (isspace (*(unsigned char *) p))
+ {
+ if (!mark)
+ mark = string;
+ }
+ else
+ mark = NULL;
+ if (mark)
+ *mark = '\0'; /* Remove trailing spaces. */
+
+ return str;
+}
+
+
+static void
+debug_init (void)
+{
+ static int initialized;
+
+ LOCK (debug_lock);
+ if (!initialized)
+ {
+ gpgme_error_t err;
+ char *e;
+ const char *s1, *s2;;
+
+#ifdef HAVE_W32CE_SYSTEM
+ e = _gpgme_w32ce_get_debug_envvar ();
+#else /*!HAVE_W32CE_SYSTEM*/
+ err = _gpgme_getenv ("GPGME_DEBUG", &e);
+ if (err)
+ {
+ UNLOCK (debug_lock);
+ return;
+ }
+#endif /*!HAVE_W32CE_SYSTEM*/
+
+ initialized = 1;
+ errfp = stderr;
+ if (e)
+ {
+ debug_level = atoi (e);
+ s1 = strchr (e, PATHSEP_C);
+ if (s1)
+ {
+#ifndef HAVE_DOSISH_SYSTEM
+ if (getuid () == geteuid ()
+#if defined(HAVE_GETGID) && defined(HAVE_GETEGID)
+ && getgid () == getegid ()
+#endif
+ )
+ {
+#endif
+ char *p;
+ FILE *fp;
+
+ s1++;
+ if (!(s2 = strchr (s1, PATHSEP_C)))
+ s2 = s1 + strlen (s1);
+ p = malloc (s2 - s1 + 1);
+ if (p)
+ {
+ memcpy (p, s1, s2 - s1);
+ p[s2-s1] = 0;
+ trim_spaces (p);
+ fp = fopen (p,"a");
+ if (fp)
+ {
+ setvbuf (fp, NULL, _IOLBF, 0);
+ errfp = fp;
+ }
+ free (p);
+ }
+#ifndef HAVE_DOSISH_SYSTEM
+ }
+#endif
+ }
+ free (e);
+ }
+ }
+ UNLOCK (debug_lock);
+
+ if (debug_level > 0)
+ _gpgme_debug (DEBUG_INIT, "gpgme_debug: level=%d\n", debug_level);
+}
+
+
+
+/* This should be called as soon as the locks are intialized. It is
+ required so that the assuan logging gets conncted to the gpgme log
+ stream as early as possible. */
+void
+_gpgme_debug_subsystem_init (void)
+{
+ debug_init ();
+}
+
+
+
+
+/* Log the formatted string FORMAT at debug level LEVEL or higher. */
+void
+_gpgme_debug (int level, const char *format, ...)
+{
+ va_list arg_ptr;
+ int saved_errno;
+
+ saved_errno = errno;
+ if (debug_level < level)
+ return;
+
+ va_start (arg_ptr, format);
+ LOCK (debug_lock);
+ {
+#ifdef HAVE_W32CE_SYSTEM
+ SYSTEMTIME t;
+
+ GetLocalTime (&t);
+ fprintf (errfp, "GPGME %04d-%02d-%02d %02d:%02d:%02d <0x%04llx> ",
+ t.wYear, t.wMonth, t.wDay,
+ t.wHour, t.wMinute, t.wSecond,
+ (unsigned long long) ath_self ());
+#else
+ struct tm *tp;
+ time_t atime = time (NULL);
+
+ tp = localtime (&atime);
+ fprintf (errfp, "GPGME %04d-%02d-%02d %02d:%02d:%02d <0x%04llx> ",
+ 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday,
+ tp->tm_hour, tp->tm_min, tp->tm_sec,
+ (unsigned long long) ath_self ());
+#endif
+ }
+#ifdef FRAME_NR
+ {
+ char spaces[] = " ";
+ int nr_spaces = sizeof (spaces) - 1;
+ int nr_columns;
+
+ nr_columns = 2 * (frame_nr - 1);
+ if (nr_columns > nr_spaces)
+ nr_columns = nr_spaces;
+ if (nr_columns < 0)
+ nr_columns = 0;
+ spaces[nr_columns] = '\0';
+ fprintf (errfp, "%s", spaces);
+ }
+#endif
+
+ vfprintf (errfp, format, arg_ptr);
+ va_end (arg_ptr);
+ if(format && *format && format[strlen (format) - 1] != '\n')
+ putc ('\n', errfp);
+ UNLOCK (debug_lock);
+ fflush (errfp);
+
+ gpg_err_set_errno (saved_errno);
+}
+
+
+/* Start a new debug line in *LINE, logged at level LEVEL or higher,
+ and starting with the formatted string FORMAT. */
+void
+_gpgme_debug_begin (void **line, int level, const char *format, ...)
+{
+ va_list arg_ptr;
+ int res;
+
+ if (debug_level < level)
+ {
+ /* Disable logging of this line. */
+ *line = NULL;
+ return;
+ }
+
+ va_start (arg_ptr, format);
+ res = vasprintf ((char **) line, format, arg_ptr);
+ va_end (arg_ptr);
+ if (res < 0)
+ *line = NULL;
+}
+
+
+/* Add the formatted string FORMAT to the debug line *LINE. */
+void
+_gpgme_debug_add (void **line, const char *format, ...)
+{
+ va_list arg_ptr;
+ char *toadd;
+ char *result;
+ int res;
+
+ if (!*line)
+ return;
+
+ va_start (arg_ptr, format);
+ res = vasprintf (&toadd, format, arg_ptr);
+ va_end (arg_ptr);
+ if (res < 0)
+ {
+ free (*line);
+ *line = NULL;
+ }
+ res = asprintf (&result, "%s%s", *(char **) line, toadd);
+ free (toadd);
+ free (*line);
+ if (res < 0)
+ *line = NULL;
+ else
+ *line = result;
+}
+
+
+/* Finish construction of *LINE and send it to the debug output
+ stream. */
+void
+_gpgme_debug_end (void **line)
+{
+ if (!*line)
+ return;
+
+ /* The smallest possible level is 1, so force logging here by
+ using that. */
+ _gpgme_debug (1, "%s", *line);
+ free (*line);
+ *line = NULL;
+}
+
+
+#define TOHEX(val) (((val) < 10) ? ((val) + '0') : ((val) - 10 + 'a'))
+
+void
+_gpgme_debug_buffer (int lvl, const char *const fmt,
+ const char *const func, const char *const buffer,
+ size_t len)
+{
+ int idx = 0;
+ int j;
+
+ if (!_gpgme_debug_trace ())
+ return;
+
+ while (idx < len)
+ {
+ char str[51];
+ char *strp = str;
+ char *strp2 = &str[34];
+
+ for (j = 0; j < 16; j++)
+ {
+ unsigned char val;
+ if (idx < len)
+ {
+ val = buffer[idx++];
+ *(strp++) = TOHEX (val >> 4);
+ *(strp++) = TOHEX (val % 16);
+ *(strp2++) = isprint (val) ? val : '.';
+ }
+ else
+ {
+ *(strp++) = ' ';
+ *(strp++) = ' ';
+ }
+ if (j == 7)
+ *(strp++) = ' ';
+ }
+ *(strp++) = ' ';
+ *(strp2) = '\0';
+
+ _gpgme_debug (lvl, fmt, func, str);
+ }
+}
diff --git a/src/debug.h b/src/debug.h
new file mode 100644
index 0000000..cbfccca
--- /dev/null
+++ b/src/debug.h
@@ -0,0 +1,262 @@
+/* debug.h - interface to debugging functions
+ Copyright (C) 2002, 2004, 2005, 2007 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#ifndef DEBUG_H
+#define DEBUG_H
+
+#include <string.h>
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+
+/* Indirect stringification, requires __STDC__ to work. */
+#define STRINGIFY(v) #v
+#define XSTRINGIFY(v) STRINGIFY(v)
+
+
+/* The debug levels. */
+
+#define DEBUG_INIT 1
+#define DEBUG_GLOBAL 2
+#define DEBUG_CTX 3
+#define DEBUG_ENGINE 4
+#define DEBUG_DATA 5
+#define DEBUG_ASSUAN 6
+#define DEBUG_SYSIO 7
+
+
+/* Remove path components from filenames (i.e. __FILE__) for cleaner
+ logs. */
+static inline const char *_gpgme_debug_srcname (const char *file)
+ GPGME_GCC_A_PURE;
+
+static inline const char *
+_gpgme_debug_srcname (const char *file)
+{
+ const char *s = strrchr (file, '/');
+ return s? s+1:file;
+}
+
+/* Called early to initialize the logging. */
+void _gpgme_debug_subsystem_init (void);
+
+/* Log the formatted string FORMAT at debug level LEVEL or higher. */
+void _gpgme_debug (int level, const char *format, ...);
+
+/* Start a new debug line in *LINE, logged at level LEVEL or higher,
+ and starting with the formatted string FORMAT. */
+void _gpgme_debug_begin (void **helper, int level, const char *format, ...);
+
+/* Add the formatted string FORMAT to the debug line *LINE. */
+void _gpgme_debug_add (void **helper, const char *format, ...);
+
+/* Finish construction of *LINE and send it to the debug output
+ stream. */
+void _gpgme_debug_end (void **helper);
+
+void _gpgme_debug_buffer (int lvl, const char *const fmt,
+ const char *const func, const char *const buffer,
+ size_t len);
+
+void _gpgme_debug_frame_begin (void);
+void _gpgme_debug_frame_end (void);
+
+
+
+/* Trace support. */
+
+/* FIXME: For now. */
+#define _gpgme_debug_trace() 1
+
+#define _TRACE(lvl, name, tag) \
+ int _gpgme_trace_level = lvl; \
+ const char *const _gpgme_trace_func = name; \
+ const char *const _gpgme_trace_tagname = STRINGIFY (tag); \
+ void *_gpgme_trace_tag = (void *) (uintptr_t) tag; \
+ _gpgme_debug_frame_begin ()
+
+#define TRACE_BEG(lvl, name, tag) \
+ _TRACE (lvl, name, tag); \
+ _gpgme_debug (_gpgme_trace_level, "%s: enter: %s=%p\n", \
+ _gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag), 0
+#define TRACE_BEG0(lvl, name, tag, fmt) \
+ _TRACE (lvl, name, tag); \
+ _gpgme_debug (_gpgme_trace_level, "%s: enter: %s=%p, " fmt "\n", \
+ _gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag), 0
+#define TRACE_BEG1(lvl, name, tag, fmt, arg1) \
+ _TRACE (lvl, name, tag); \
+ _gpgme_debug (_gpgme_trace_level, "%s: enter: %s=%p, " fmt "\n", \
+ _gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag, \
+ arg1), 0
+#define TRACE_BEG2(lvl, name, tag, fmt, arg1, arg2) \
+ _TRACE (lvl, name, tag); \
+ _gpgme_debug (_gpgme_trace_level, "%s: enter: %s=%p, " fmt "\n", \
+ _gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag, \
+ arg1, arg2), 0
+#define TRACE_BEG3(lvl, name, tag, fmt, arg1, arg2, arg3) \
+ _TRACE (lvl, name, tag); \
+ _gpgme_debug (_gpgme_trace_level, "%s: enter: %s=%p, " fmt "\n", \
+ _gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag, \
+ arg1, arg2, arg3), 0
+#define TRACE_BEG4(lvl, name, tag, fmt, arg1, arg2, arg3, arg4) \
+ _TRACE (lvl, name, tag); \
+ _gpgme_debug (_gpgme_trace_level, "%s: enter: %s=%p, " fmt "\n", \
+ _gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag, \
+ arg1, arg2, arg3, arg4), 0
+#define TRACE_BEG5(lvl, name, tag, fmt, arg1, arg2, arg3, arg4, arg5) \
+ _TRACE (lvl, name, tag); \
+ _gpgme_debug (_gpgme_trace_level, "%s: enter: %s=%p, " fmt "\n", \
+ _gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag, \
+ arg1, arg2, arg3, arg4, arg5), 0
+#define TRACE_BEG7(lvl, name, tag, fmt, arg1, arg2, arg3, arg4, \
+ arg5, arg6, arg7) \
+ _TRACE (lvl, name, tag); \
+ _gpgme_debug (_gpgme_trace_level, "%s: enter: %s=%p, " fmt "\n", \
+ _gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag, \
+ arg1, arg2, arg3, arg4, arg5, \
+ arg6, arg7), 0
+#define TRACE_BEG8(lvl, name, tag, fmt, arg1, arg2, arg3, arg4, \
+ arg5, arg6, arg7, arg8) \
+ _TRACE (lvl, name, tag); \
+ _gpgme_debug (_gpgme_trace_level, "%s: enter: %s=%p, " fmt "\n", \
+ _gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag, \
+ arg1, arg2, arg3, arg4, arg5, \
+ arg6, arg7, arg8), 0
+
+#define TRACE(lvl, name, tag) \
+ _gpgme_debug_frame_begin (), \
+ _gpgme_debug (lvl, "%s: call: %s=%p\n", \
+ name, STRINGIFY (tag), (void *) (uintptr_t) tag), \
+ _gpgme_debug_frame_end (), 0
+#define TRACE0(lvl, name, tag, fmt) \
+ _gpgme_debug_frame_begin (), \
+ _gpgme_debug (lvl, "%s: call: %s=%p, " fmt "\n", \
+ name, STRINGIFY (tag), (void *) (uintptr_t) tag), \
+ _gpgme_debug_frame_end (), 0
+#define TRACE1(lvl, name, tag, fmt, arg1) \
+ _gpgme_debug_frame_begin (), \
+ _gpgme_debug (lvl, "%s: call: %s=%p, " fmt "\n", \
+ name, STRINGIFY (tag), (void *) (uintptr_t) tag, arg1), \
+ _gpgme_debug_frame_end (), 0
+#define TRACE2(lvl, name, tag, fmt, arg1, arg2) \
+ _gpgme_debug_frame_begin (), \
+ _gpgme_debug (lvl, "%s: call: %s=%p, " fmt "\n", \
+ name, STRINGIFY (tag), (void *) (uintptr_t) tag, arg1, \
+ arg2), _gpgme_debug_frame_end (), 0
+#define TRACE3(lvl, name, tag, fmt, arg1, arg2, arg3) \
+ _gpgme_debug_frame_begin (), \
+ _gpgme_debug (lvl, "%s: call: %s=%p, " fmt "\n", \
+ name, STRINGIFY (tag), (void *) (uintptr_t) tag, arg1, \
+ arg2, arg3), _gpgme_debug_frame_end (), 0
+#define TRACE6(lvl, name, tag, fmt, arg1, arg2, arg3, arg4, arg5, arg6) \
+ _gpgme_debug_frame_begin (), \
+ _gpgme_debug (lvl, "%s: call: %s=%p, " fmt "\n", \
+ name, STRINGIFY (tag), (void *) (uintptr_t) tag, arg1, \
+ arg2, arg3, arg4, arg5, arg6), \
+ _gpgme_debug_frame_end (), 0
+
+#define TRACE_ERR(err) \
+ err == 0 ? (TRACE_SUC ()) : \
+ (_gpgme_debug (_gpgme_trace_level, "%s: error: %s <%s>\n", \
+ _gpgme_trace_func, gpgme_strerror (err), \
+ gpgme_strsource (err)), _gpgme_debug_frame_end (), (err))
+/* The cast to void suppresses GCC warnings. */
+#define TRACE_SYSRES(res) \
+ res >= 0 ? ((void) (TRACE_SUC1 ("result=%i", res)), (res)) : \
+ (_gpgme_debug (_gpgme_trace_level, "%s: error: %s\n", \
+ _gpgme_trace_func, strerror (errno)), _gpgme_debug_frame_end (), (res))
+#define TRACE_SYSERR(res) \
+ res == 0 ? ((void) (TRACE_SUC1 ("result=%i", res)), (res)) : \
+ (_gpgme_debug (_gpgme_trace_level, "%s: error: %s\n", \
+ _gpgme_trace_func, strerror (res)), \
+ _gpgme_debug_frame_end (), (res))
+
+#define TRACE_SUC() \
+ _gpgme_debug (_gpgme_trace_level, "%s: leave\n", \
+ _gpgme_trace_func), _gpgme_debug_frame_end (), 0
+#define TRACE_SUC0(fmt) \
+ _gpgme_debug (_gpgme_trace_level, "%s: leave: " fmt "\n", \
+ _gpgme_trace_func), _gpgme_debug_frame_end (), 0
+#define TRACE_SUC1(fmt, arg1) \
+ _gpgme_debug (_gpgme_trace_level, "%s: leave: " fmt "\n", \
+ _gpgme_trace_func, arg1), _gpgme_debug_frame_end (), 0
+#define TRACE_SUC2(fmt, arg1, arg2) \
+ _gpgme_debug (_gpgme_trace_level, "%s: leave: " fmt "\n", \
+ _gpgme_trace_func, arg1, arg2), _gpgme_debug_frame_end (), 0
+#define TRACE_SUC5(fmt, arg1, arg2, arg3, arg4, arg5) \
+ _gpgme_debug (_gpgme_trace_level, "%s: leave: " fmt "\n", \
+ _gpgme_trace_func, arg1, arg2, arg3, arg4, arg5), \
+ _gpgme_debug_frame_end (), 0
+#define TRACE_SUC6(fmt, arg1, arg2, arg3, arg4, arg5, arg6) \
+ _gpgme_debug (_gpgme_trace_level, "%s: leave: " fmt "\n", \
+ _gpgme_trace_func, arg1, arg2, arg3, arg4, arg5, arg6), \
+ _gpgme_debug_frame_end (), 0
+
+#define TRACE_LOG(fmt) \
+ _gpgme_debug (_gpgme_trace_level, "%s: check: %s=%p, " fmt "\n", \
+ _gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag), 0
+#define TRACE_LOG1(fmt, arg1) \
+ _gpgme_debug (_gpgme_trace_level, "%s: check: %s=%p, " fmt "\n", \
+ _gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag, \
+ arg1), 0
+#define TRACE_LOG2(fmt, arg1, arg2) \
+ _gpgme_debug (_gpgme_trace_level, "%s: check: %s=%p, " fmt "\n", \
+ _gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag, \
+ arg1, arg2), 0
+#define TRACE_LOG3(fmt, arg1, arg2, arg3) \
+ _gpgme_debug (_gpgme_trace_level, "%s: check: %s=%p, " fmt "\n", \
+ _gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag, \
+ arg1, arg2, arg3), 0
+#define TRACE_LOG4(fmt, arg1, arg2, arg3, arg4) \
+ _gpgme_debug (_gpgme_trace_level, "%s: check: %s=%p, " fmt "\n", \
+ _gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag, \
+ arg1, arg2, arg3, arg4), 0
+#define TRACE_LOG5(fmt, arg1, arg2, arg3, arg4, arg5) \
+ _gpgme_debug (_gpgme_trace_level, "%s: check: %s=%p, " fmt "\n", \
+ _gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag, \
+ arg1, arg2, arg3, arg4, arg5), 0
+#define TRACE_LOG6(fmt, arg1, arg2, arg3, arg4, arg5, arg6) \
+ _gpgme_debug (_gpgme_trace_level, "%s: check: %s=%p, " fmt "\n", \
+ _gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag, \
+ arg1, arg2, arg3, arg4, arg5, \
+ arg6), 0
+
+#define TRACE_LOGBUF(buf, len) \
+ _gpgme_debug_buffer (_gpgme_trace_level, "%s: check: %s", \
+ _gpgme_trace_func, buf, len)
+
+#define TRACE_SEQ(hlp,fmt) \
+ _gpgme_debug_begin (&(hlp), _gpgme_trace_level, \
+ "%s: check: %s=%p, " fmt, _gpgme_trace_func, \
+ _gpgme_trace_tagname, _gpgme_trace_tag)
+#define TRACE_ADD0(hlp,fmt) \
+ _gpgme_debug_add (&(hlp), fmt)
+#define TRACE_ADD1(hlp,fmt,a) \
+ _gpgme_debug_add (&(hlp), fmt, (a))
+#define TRACE_ADD2(hlp,fmt,a,b) \
+ _gpgme_debug_add (&(hlp), fmt, (a), (b))
+#define TRACE_ADD3(hlp,fmt,a,b,c) \
+ _gpgme_debug_add (&(hlp), fmt, (a), (b), (c))
+#define TRACE_END(hlp,fmt) \
+ _gpgme_debug_add (&(hlp), fmt); \
+ _gpgme_debug_end (&(hlp))
+#define TRACE_ENABLED(hlp) (!!(hlp))
+
+#endif /* DEBUG_H */
diff --git a/src/decrypt-verify.c b/src/decrypt-verify.c
new file mode 100644
index 0000000..a5a751b
--- /dev/null
+++ b/src/decrypt-verify.c
@@ -0,0 +1,121 @@
+/* decrypt-verify.c - Decrypt and verify function.
+ Copyright (C) 2000 Werner Koch (dd9jn)
+ Copyright (C) 2001, 2002, 2003, 2004 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "debug.h"
+#include "gpgme.h"
+#include "ops.h"
+
+
+static gpgme_error_t
+decrypt_verify_status_handler (void *priv, gpgme_status_code_t code,
+ char *args)
+{
+ gpgme_error_t err;
+
+ err = _gpgme_progress_status_handler (priv, code, args);
+ if (!err)
+ err = _gpgme_decrypt_status_handler (priv, code, args);
+ if (!err)
+ err = _gpgme_verify_status_handler (priv, code, args);
+ return err;
+}
+
+
+static gpgme_error_t
+decrypt_verify_start (gpgme_ctx_t ctx, int synchronous,
+ gpgme_data_t cipher, gpgme_data_t plain)
+{
+ gpgme_error_t err;
+
+ err = _gpgme_op_reset (ctx, synchronous);
+ if (err)
+ return err;
+
+ err = _gpgme_op_decrypt_init_result (ctx);
+ if (err)
+ return err;
+
+ err = _gpgme_op_verify_init_result (ctx);
+ if (err)
+ return err;
+
+ if (!cipher)
+ return gpg_error (GPG_ERR_NO_DATA);
+ if (!plain)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if (ctx->passphrase_cb)
+ {
+ err = _gpgme_engine_set_command_handler
+ (ctx->engine, _gpgme_passphrase_command_handler, ctx, NULL);
+ if (err)
+ return err;
+ }
+
+ _gpgme_engine_set_status_handler (ctx->engine,
+ decrypt_verify_status_handler, ctx);
+
+ return _gpgme_engine_op_decrypt_verify (ctx->engine, cipher, plain);
+}
+
+
+/* Decrypt ciphertext CIPHER and make a signature verification within
+ 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)
+{
+ gpgme_error_t err;
+
+ TRACE_BEG2 (DEBUG_CTX, "gpgme_op_decrypt_verify_start", ctx,
+ "cipher=%p, plain=%p", cipher, plain);
+
+ if (!ctx)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ err = decrypt_verify_start (ctx, 0, cipher, plain);
+ return TRACE_ERR (err);
+}
+
+
+/* Decrypt ciphertext CIPHER and make a signature verification within
+ CTX and store the resulting plaintext in PLAIN. */
+gpgme_error_t
+gpgme_op_decrypt_verify (gpgme_ctx_t ctx, gpgme_data_t cipher,
+ gpgme_data_t plain)
+{
+ gpgme_error_t err;
+
+ TRACE_BEG2 (DEBUG_CTX, "gpgme_op_decrypt_verify", ctx,
+ "cipher=%p, plain=%p", cipher, plain);
+
+ if (!ctx)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ err = decrypt_verify_start (ctx, 1, cipher, plain);
+ if (!err)
+ err = _gpgme_wait_one (ctx);
+ return TRACE_ERR (err);
+}
diff --git a/src/decrypt.c b/src/decrypt.c
new file mode 100644
index 0000000..43945ec
--- /dev/null
+++ b/src/decrypt.c
@@ -0,0 +1,403 @@
+/* decrypt.c - Decrypt function.
+ Copyright (C) 2000 Werner Koch (dd9jn)
+ Copyright (C) 2001, 2002, 2003, 2004 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "debug.h"
+#include "gpgme.h"
+#include "util.h"
+#include "context.h"
+#include "ops.h"
+
+
+
+typedef struct
+{
+ struct _gpgme_op_decrypt_result result;
+
+ int okay;
+ int failed;
+
+ /* A pointer to the next pointer of the last recipient in the list.
+ This makes appending new invalid signers painless while
+ preserving the order. */
+ gpgme_recipient_t *last_recipient_p;
+} *op_data_t;
+
+
+static void
+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);
+
+ while (recipient)
+ {
+ gpgme_recipient_t next = recipient->next;
+ free (recipient);
+ recipient = next;
+ }
+}
+
+
+gpgme_decrypt_result_t
+gpgme_op_decrypt_result (gpgme_ctx_t ctx)
+{
+ void *hook;
+ op_data_t opd;
+ gpgme_error_t err;
+
+ TRACE_BEG (DEBUG_CTX, "gpgme_op_decrypt_result", ctx);
+
+ err = _gpgme_op_data_lookup (ctx, OPDATA_DECRYPT, &hook, -1, NULL);
+ opd = hook;
+ if (err || !opd)
+ {
+ TRACE_SUC0 ("result=(null)");
+ return NULL;
+ }
+
+ if (_gpgme_debug_trace ())
+ {
+ gpgme_recipient_t rcp;
+
+ if (opd->result.unsupported_algorithm)
+ {
+ TRACE_LOG1 ("result: unsupported_algorithm: %s",
+ opd->result.unsupported_algorithm);
+ }
+ if (opd->result.wrong_key_usage)
+ {
+ TRACE_LOG ("result: wrong key usage");
+ }
+ rcp = opd->result.recipients;
+ while (rcp)
+ {
+ TRACE_LOG3 ("result: recipient: keyid=%s, pubkey_algo=%i, "
+ "status=%s", rcp->keyid, rcp->pubkey_algo,
+ gpg_strerror (rcp->status));
+ rcp = rcp->next;
+ }
+ if (opd->result.file_name)
+ {
+ TRACE_LOG1 ("result: original file name: %s", opd->result.file_name);
+ }
+ }
+
+ TRACE_SUC1 ("result=%p", &opd->result);
+ return &opd->result;
+}
+
+
+static gpgme_error_t
+parse_enc_to (char *args, gpgme_recipient_t *recp)
+{
+ gpgme_recipient_t rec;
+ char *tail;
+ int i;
+
+ rec = malloc (sizeof (*rec));
+ if (!rec)
+ return gpg_error_from_syserror ();
+
+ rec->next = NULL;
+ rec->keyid = rec->_keyid;
+ rec->status = 0;
+
+ for (i = 0; i < sizeof (rec->_keyid) - 1; i++)
+ {
+ if (args[i] == '\0' || args[i] == ' ')
+ break;
+
+ rec->_keyid[i] = args[i];
+ }
+ rec->_keyid[i] = '\0';
+
+ args = &args[i];
+ if (*args != '\0' && *args != ' ')
+ {
+ free (rec);
+ return gpg_error (GPG_ERR_INV_ENGINE);
+ }
+
+ while (*args == ' ')
+ args++;
+
+ if (*args)
+ {
+ gpg_err_set_errno (0);
+ rec->pubkey_algo = strtol (args, &tail, 0);
+ if (errno || args == tail || *tail != ' ')
+ {
+ /* The crypto backend does not behave. */
+ free (rec);
+ return gpg_error (GPG_ERR_INV_ENGINE);
+ }
+ }
+
+ /* FIXME: The key length is always 0 right now, so no need to parse
+ it. */
+
+ *recp = rec;
+ return 0;
+}
+
+
+gpgme_error_t
+_gpgme_decrypt_status_handler (void *priv, gpgme_status_code_t code,
+ char *args)
+{
+ gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
+ gpgme_error_t err;
+ void *hook;
+ op_data_t opd;
+
+ err = _gpgme_passphrase_status_handler (priv, code, args);
+ if (err)
+ return err;
+
+ err = _gpgme_op_data_lookup (ctx, OPDATA_DECRYPT, &hook, -1, NULL);
+ opd = hook;
+ if (err)
+ return err;
+
+ switch (code)
+ {
+ case GPGME_STATUS_EOF:
+ /* FIXME: These error values should probably be attributed to
+ the underlying crypto engine (as error source). */
+ if (opd->failed)
+ return gpg_error (GPG_ERR_DECRYPT_FAILED);
+ else if (!opd->okay)
+ return gpg_error (GPG_ERR_NO_DATA);
+ break;
+
+ case GPGME_STATUS_DECRYPTION_INFO:
+ /* Fixme: Provide a way to return the used symmetric algorithm. */
+ break;
+
+ case GPGME_STATUS_DECRYPTION_OKAY:
+ opd->okay = 1;
+ break;
+
+ case GPGME_STATUS_DECRYPTION_FAILED:
+ opd->failed = 1;
+ break;
+
+ case GPGME_STATUS_ERROR:
+ /* Note that this is an informational status code which should
+ not lead to an error return unless it is something not
+ related to the backend. */
+ {
+ const char d_alg[] = "decrypt.algorithm";
+ const char k_alg[] = "decrypt.keyusage";
+
+ if (!strncmp (args, d_alg, sizeof (d_alg) - 1))
+ {
+ args += sizeof (d_alg) - 1;
+ while (*args == ' ')
+ args++;
+
+ if (gpg_err_code (atoi (args)) == GPG_ERR_UNSUPPORTED_ALGORITHM)
+ {
+ char *end;
+
+ while (*args && *args != ' ')
+ args++;
+ while (*args == ' ')
+ args++;
+
+ end = strchr (args, ' ');
+ if (end)
+ *end = '\0';
+
+ if (!(*args == '?' && *(args + 1) == '\0'))
+ {
+ opd->result.unsupported_algorithm = strdup (args);
+ if (!opd->result.unsupported_algorithm)
+ return gpg_error_from_syserror ();
+ }
+ }
+ }
+ else if (!strncmp (args, k_alg, sizeof (k_alg) - 1))
+ {
+ args += sizeof (k_alg) - 1;
+ while (*args == ' ')
+ args++;
+
+ if (gpg_err_code (atoi (args)) == GPG_ERR_WRONG_KEY_USAGE)
+ opd->result.wrong_key_usage = 1;
+ }
+ }
+ break;
+
+ case GPGME_STATUS_ENC_TO:
+ err = parse_enc_to (args, opd->last_recipient_p);
+ if (err)
+ return err;
+
+ opd->last_recipient_p = &(*opd->last_recipient_p)->next;
+ break;
+
+ case GPGME_STATUS_NO_SECKEY:
+ {
+ gpgme_recipient_t rec = opd->result.recipients;
+
+ while (rec)
+ {
+ if (!strcmp (rec->keyid, args))
+ {
+ rec->status = gpg_error (GPG_ERR_NO_SECKEY);
+ break;
+ }
+ rec = rec->next;
+ }
+ /* FIXME: Is this ok? */
+ if (!rec)
+ return gpg_error (GPG_ERR_INV_ENGINE);
+ }
+ break;
+
+ case GPGME_STATUS_PLAINTEXT:
+ err = _gpgme_parse_plaintext (args, &opd->result.file_name);
+ if (err)
+ return err;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+
+static gpgme_error_t
+decrypt_status_handler (void *priv, gpgme_status_code_t code, char *args)
+{
+ gpgme_error_t err;
+
+ err = _gpgme_progress_status_handler (priv, code, args);
+ if (!err)
+ err = _gpgme_decrypt_status_handler (priv, code, args);
+ return err;
+}
+
+
+gpgme_error_t
+_gpgme_op_decrypt_init_result (gpgme_ctx_t ctx)
+{
+ gpgme_error_t err;
+ void *hook;
+ op_data_t opd;
+
+ err = _gpgme_op_data_lookup (ctx, OPDATA_DECRYPT, &hook,
+ sizeof (*opd), release_op_data);
+ opd = hook;
+ if (err)
+ return err;
+
+ opd->last_recipient_p = &opd->result.recipients;
+ return 0;
+}
+
+
+static gpgme_error_t
+decrypt_start (gpgme_ctx_t ctx, int synchronous,
+ gpgme_data_t cipher, gpgme_data_t plain)
+{
+ gpgme_error_t err;
+
+ err = _gpgme_op_reset (ctx, synchronous);
+ if (err)
+ return err;
+
+ err = _gpgme_op_decrypt_init_result (ctx);
+ if (err)
+ return err;
+
+ if (!cipher)
+ return gpg_error (GPG_ERR_NO_DATA);
+ if (!plain)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if (err)
+ return err;
+
+ if (ctx->passphrase_cb)
+ {
+ err = _gpgme_engine_set_command_handler
+ (ctx->engine, _gpgme_passphrase_command_handler, ctx, NULL);
+ if (err)
+ return err;
+ }
+
+ _gpgme_engine_set_status_handler (ctx->engine, decrypt_status_handler, ctx);
+
+ return _gpgme_engine_op_decrypt (ctx->engine, cipher, plain);
+}
+
+
+gpgme_error_t
+gpgme_op_decrypt_start (gpgme_ctx_t ctx, gpgme_data_t cipher,
+ gpgme_data_t plain)
+{
+ gpgme_error_t err;
+
+ TRACE_BEG2 (DEBUG_CTX, "gpgme_op_decrypt_start", ctx,
+ "cipher=%p, plain=%p", cipher, plain);
+
+ if (!ctx)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ err = decrypt_start (ctx, 0, cipher, plain);
+ return TRACE_ERR (err);
+}
+
+
+/* Decrypt ciphertext CIPHER within CTX and store the resulting
+ plaintext in PLAIN. */
+gpgme_error_t
+gpgme_op_decrypt (gpgme_ctx_t ctx, gpgme_data_t cipher, gpgme_data_t plain)
+{
+ gpgme_error_t err;
+
+ TRACE_BEG2 (DEBUG_CTX, "gpgme_op_decrypt", ctx,
+ "cipher=%p, plain=%p", cipher, plain);
+
+ if (!ctx)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ err = decrypt_start (ctx, 1, cipher, plain);
+ if (!err)
+ err = _gpgme_wait_one (ctx);
+ return TRACE_ERR (err);
+}
diff --git a/src/delete.c b/src/delete.c
new file mode 100644
index 0000000..1a501d8
--- /dev/null
+++ b/src/delete.c
@@ -0,0 +1,131 @@
+/* delete.c - Delete a key.
+ Copyright (C) 2001, 2002, 2003, 2004 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdlib.h>
+#include <errno.h>
+
+#include "debug.h"
+#include "gpgme.h"
+#include "context.h"
+#include "ops.h"
+
+
+static gpgme_error_t
+delete_status_handler (void *priv, gpgme_status_code_t code, char *args)
+{
+ if (code == GPGME_STATUS_DELETE_PROBLEM)
+ {
+ enum delete_problem
+ {
+ DELETE_No_Problem = 0,
+ DELETE_No_Such_Key = 1,
+ DELETE_Must_Delete_Secret_Key = 2,
+ DELETE_Ambiguous_Specification = 3
+ };
+ long problem;
+ char *tail;
+
+ gpg_err_set_errno (0);
+ problem = strtol (args, &tail, 0);
+ if (errno || (*tail && *tail != ' '))
+ return gpg_error (GPG_ERR_INV_ENGINE);
+
+ switch (problem)
+ {
+ case DELETE_No_Problem:
+ break;
+
+ case DELETE_No_Such_Key:
+ return gpg_error (GPG_ERR_NO_PUBKEY);
+
+ case DELETE_Must_Delete_Secret_Key:
+ return gpg_error (GPG_ERR_CONFLICT);
+
+ case DELETE_Ambiguous_Specification:
+ return gpg_error (GPG_ERR_AMBIGUOUS_NAME);
+
+ default:
+ return gpg_error (GPG_ERR_GENERAL);
+ }
+ }
+ return 0;
+}
+
+
+static gpgme_error_t
+delete_start (gpgme_ctx_t ctx, int synchronous, const gpgme_key_t key,
+ int allow_secret)
+{
+ gpgme_error_t err;
+
+ err = _gpgme_op_reset (ctx, synchronous);
+ if (err)
+ return err;
+
+ _gpgme_engine_set_status_handler (ctx->engine, delete_status_handler, ctx);
+
+ return _gpgme_engine_op_delete (ctx->engine, key, allow_secret);
+}
+
+
+/* Delete KEY from the keyring. If ALLOW_SECRET is non-zero, secret
+ 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 err;
+
+ TRACE_BEG3 (DEBUG_CTX, "gpgme_op_delete", ctx,
+ "key=%p (%s), allow_secret=%i", key,
+ (key->subkeys && key->subkeys->fpr) ?
+ key->subkeys->fpr : "invalid", allow_secret);
+
+ if (!ctx)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ err = delete_start (ctx, 0, key, allow_secret);
+ return TRACE_ERR (err);
+}
+
+
+/* Delete KEY from the keyring. If ALLOW_SECRET is non-zero, secret
+ keys are also deleted. */
+gpgme_error_t
+gpgme_op_delete (gpgme_ctx_t ctx, const gpgme_key_t key, int allow_secret)
+{
+ gpgme_error_t err;
+
+ TRACE_BEG3 (DEBUG_CTX, "gpgme_op_delete", ctx,
+ "key=%p (%s), allow_secret=%i", key,
+ (key->subkeys && key->subkeys->fpr) ?
+ key->subkeys->fpr : "invalid", allow_secret);
+
+ if (!ctx)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ err = delete_start (ctx, 1, key, allow_secret);
+ if (!err)
+ err = _gpgme_wait_one (ctx);
+ return err;
+}
diff --git a/src/dirinfo.c b/src/dirinfo.c
new file mode 100644
index 0000000..b1e438e
--- /dev/null
+++ b/src/dirinfo.c
@@ -0,0 +1,189 @@
+/* dirinfo.c - Get directory information
+ * Copyright (C) 2009 g10 Code GmbH
+ *
+ * This file is part of GPGME.
+ *
+ * GPGME is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * GPGME is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "gpgme.h"
+#include "util.h"
+#include "priv-io.h"
+#include "debug.h"
+#include "sema.h"
+
+DEFINE_STATIC_LOCK (dirinfo_lock);
+
+/* Constants used internally to select the data. */
+enum
+ {
+ WANT_HOMEDIR,
+ WANT_AGENT_SOCKET
+ };
+
+/* Values retrieved via gpgconf and cached here. */
+static struct {
+ int valid; /* Cached information is valid. */
+ char *homedir;
+ char *agent_socket;
+} dirinfo;
+
+
+/* Parse the output of "gpgconf --list-dirs". This function expects
+ that DIRINFO_LOCK is held by the caller. */
+static void
+parse_output (char *line)
+{
+ char *value, *p;
+
+ value = strchr (line, ':');
+ if (!value)
+ return;
+ *value++ = 0;
+ p = strchr (value, ':');
+ if (p)
+ *p = 0;
+ if (_gpgme_decode_percent_string (value, &value, strlen (value)+1, 0))
+ return;
+ if (!*value)
+ return;
+
+ if (!strcmp (line, "homedir") && !dirinfo.homedir)
+ dirinfo.homedir = strdup (value);
+ else if (!strcmp (line, "agent-socket") && !dirinfo.agent_socket)
+ dirinfo.agent_socket = strdup (value);
+}
+
+
+/* Read the directory information from gpgconf. This function expects
+ that DIRINFO_LOCK is held by the caller. */
+static void
+read_gpgconf_dirs (void)
+{
+ const char *pgmname;
+ char linebuf[1024] = {0};
+ int linelen = 0;
+ char * argv[3];
+ int rp[2];
+ struct spawn_fd_item_s cfd[] = { {-1, 1 /* STDOUT_FILENO */, -1, 0},
+ {-1, -1} };
+ int status;
+ int nread;
+ char *mark = NULL;
+
+ pgmname = _gpgme_get_gpgconf_path ();
+ if (!pgmname)
+ return; /* No way. */
+
+ argv[0] = (char *)pgmname;
+ argv[1] = "--list-dirs";
+ argv[2] = NULL;
+
+ if (_gpgme_io_pipe (rp, 1) < 0)
+ return;
+
+ cfd[0].fd = rp[1];
+
+ status = _gpgme_io_spawn (pgmname, argv, 0, cfd, NULL, NULL, NULL);
+ if (status < 0)
+ {
+ _gpgme_io_close (rp[0]);
+ _gpgme_io_close (rp[1]);
+ return;
+ }
+
+ do
+ {
+ nread = _gpgme_io_read (rp[0],
+ linebuf + linelen,
+ sizeof linebuf - linelen - 1);
+ if (nread > 0)
+ {
+ char *line;
+ const char *lastmark = NULL;
+ size_t nused;
+
+ linelen += nread;
+ linebuf[linelen] = '\0';
+
+ for (line=linebuf; (mark = strchr (line, '\n')); line = mark+1 )
+ {
+ lastmark = mark;
+ if (mark > line && mark[-1] == '\r')
+ mark[-1] = '\0';
+ else
+ mark[0] = '\0';
+
+ parse_output (line);
+ }
+
+ nused = lastmark? (lastmark + 1 - linebuf) : 0;
+ memmove (linebuf, linebuf + nused, linelen - nused);
+ linelen -= nused;
+ }
+ }
+ while (nread > 0 && linelen < sizeof linebuf - 1);
+
+ _gpgme_io_close (rp[0]);
+}
+
+
+static const char *
+get_gpgconf_dir (int what)
+{
+ const char *result = NULL;
+
+ LOCK (dirinfo_lock);
+ if (!dirinfo.valid)
+ {
+ read_gpgconf_dirs ();
+ /* Even if the reading of the directories failed (e.g. due to an
+ too old version gpgconf or no gpgconf at all), we need to
+ mark the entries as valid so that we won't try over and over
+ to read them. Note further that we are not able to change
+ the read values later because they are practically statically
+ allocated. */
+ dirinfo.valid = 1;
+ }
+ switch (what)
+ {
+ case WANT_HOMEDIR: result = dirinfo.homedir; break;
+ case WANT_AGENT_SOCKET: result = dirinfo.agent_socket; break;
+ }
+ UNLOCK (dirinfo_lock);
+ return result;
+}
+
+
+/* Return the default home directory. Returns NULL if not known. */
+const char *
+_gpgme_get_default_homedir (void)
+{
+ return get_gpgconf_dir (WANT_HOMEDIR);
+}
+
+/* Return the default gpg-agent socket name. Returns NULL if not known. */
+const char *
+_gpgme_get_default_agent_socket (void)
+{
+ return get_gpgconf_dir (WANT_AGENT_SOCKET);
+}
+
diff --git a/src/edit.c b/src/edit.c
new file mode 100644
index 0000000..4abf24f
--- /dev/null
+++ b/src/edit.c
@@ -0,0 +1,221 @@
+/* edit.c - Key edit function.
+ Copyright (C) 2002, 2003, 2004 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdlib.h>
+
+#include "gpgme.h"
+#include "debug.h"
+#include "context.h"
+#include "ops.h"
+
+
+typedef struct
+{
+ /* The user callback function and its hook value. */
+ gpgme_edit_cb_t fnc;
+ void *fnc_value;
+} *op_data_t;
+
+
+static gpgme_error_t
+edit_status_handler (void *priv, gpgme_status_code_t status, char *args)
+{
+ gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
+ gpgme_error_t err;
+ void *hook;
+ op_data_t opd;
+
+ err = _gpgme_passphrase_status_handler (priv, status, args);
+ if (err)
+ return err;
+
+ err = _gpgme_progress_status_handler (priv, status, args);
+ if (err)
+ return err;
+
+ err = _gpgme_op_data_lookup (ctx, OPDATA_EDIT, &hook, -1, NULL);
+ opd = hook;
+ if (err)
+ return err;
+
+ return (*opd->fnc) (opd->fnc_value, status, args, -1);
+}
+
+
+static gpgme_error_t
+command_handler (void *priv, gpgme_status_code_t status, const char *args,
+ int fd, int *processed_r)
+{
+ gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
+ gpgme_error_t err;
+ int processed = 0;
+
+ if (ctx->passphrase_cb)
+ {
+ err = _gpgme_passphrase_command_handler (ctx, status, args,
+ fd, &processed);
+ if (err)
+ return err;
+ }
+
+ if (!processed)
+ {
+ void *hook;
+ op_data_t opd;
+
+ err = _gpgme_op_data_lookup (ctx, OPDATA_EDIT, &hook, -1, NULL);
+ opd = hook;
+ if (err)
+ return err;
+
+ /* FIXME: We expect the user to handle _all_ status codes.
+ Later, we may fix the callback interface to allow the user
+ indicate if it processed the status code or not. */
+ *processed_r = 1;
+
+ return (*opd->fnc) (opd->fnc_value, status, args, fd);
+ }
+
+ *processed_r = processed;
+ return 0;
+}
+
+
+static gpgme_error_t
+edit_start (gpgme_ctx_t ctx, int synchronous, int type, gpgme_key_t key,
+ gpgme_edit_cb_t fnc, void *fnc_value, gpgme_data_t out)
+{
+ gpgme_error_t err;
+ void *hook;
+ op_data_t opd;
+
+ err = _gpgme_op_reset (ctx, synchronous);
+ if (err)
+ return err;
+
+ if (!fnc || !out)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ err = _gpgme_op_data_lookup (ctx, OPDATA_EDIT, &hook, sizeof (*opd), NULL);
+ opd = hook;
+ if (err)
+ return err;
+
+ opd->fnc = fnc;
+ opd->fnc_value = fnc_value;
+
+ err = _gpgme_engine_set_command_handler (ctx->engine, command_handler,
+ ctx, out);
+ if (err)
+ return err;
+
+ _gpgme_engine_set_status_handler (ctx->engine, edit_status_handler, ctx);
+
+ return _gpgme_engine_op_edit (ctx->engine, type, key, out, ctx);
+}
+
+
+gpgme_error_t
+gpgme_op_edit_start (gpgme_ctx_t ctx, gpgme_key_t key,
+ gpgme_edit_cb_t fnc, void *fnc_value, gpgme_data_t out)
+{
+ gpgme_error_t err;
+
+ TRACE_BEG5 (DEBUG_CTX, "gpgme_op_edit_start", ctx,
+ "key=%p (%s), fnc=%p fnc_value=%p, out=%p", key,
+ (key->subkeys && key->subkeys->fpr) ?
+ key->subkeys->fpr : "invalid", fnc, fnc_value, out);
+
+ if (!ctx)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ err = edit_start (ctx, 0, 0, key, fnc, fnc_value, out);
+ return err;
+}
+
+
+/* Edit the key KEY. Send status and command requests to FNC and
+ output of edit commands to OUT. */
+gpgme_error_t
+gpgme_op_edit (gpgme_ctx_t ctx, gpgme_key_t key,
+ gpgme_edit_cb_t fnc, void *fnc_value, gpgme_data_t out)
+{
+ gpgme_error_t err;
+
+ TRACE_BEG5 (DEBUG_CTX, "gpgme_op_edit", ctx,
+ "key=%p (%s), fnc=%p fnc_value=%p, out=%p", key,
+ (key->subkeys && key->subkeys->fpr) ?
+ key->subkeys->fpr : "invalid", fnc, fnc_value, out);
+
+ if (!ctx)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ err = edit_start (ctx, 1, 0, key, fnc, fnc_value, out);
+
+ if (!err)
+ err = _gpgme_wait_one (ctx);
+ return TRACE_ERR (err);
+}
+
+
+gpgme_error_t
+gpgme_op_card_edit_start (gpgme_ctx_t ctx, gpgme_key_t key,
+ gpgme_edit_cb_t fnc, void *fnc_value,
+ gpgme_data_t out)
+{
+ gpgme_error_t err;
+
+ TRACE_BEG5 (DEBUG_CTX, "gpgme_op_card_edit_start", ctx,
+ "key=%p (%s), fnc=%p fnc_value=%p, out=%p", key,
+ (key->subkeys && key->subkeys->fpr) ?
+ key->subkeys->fpr : "invalid", fnc, fnc_value, out);
+
+ if (!ctx)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ err = edit_start (ctx, 0, 1, key, fnc, fnc_value, out);
+ return err;
+}
+
+
+/* Edit the card for the key KEY. Send status and command requests to
+ FNC and output of edit commands to OUT. */
+gpgme_error_t
+gpgme_op_card_edit (gpgme_ctx_t ctx, gpgme_key_t key,
+ gpgme_edit_cb_t fnc, void *fnc_value, gpgme_data_t out)
+{
+ gpgme_error_t err;
+
+ TRACE_BEG5 (DEBUG_CTX, "gpgme_op_card_edit", ctx,
+ "key=%p (%s), fnc=%p fnc_value=%p, out=%p", key,
+ (key->subkeys && key->subkeys->fpr) ?
+ key->subkeys->fpr : "invalid", fnc, fnc_value, out);
+
+ if (!ctx)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ err = edit_start (ctx, 1, 1, key, fnc, fnc_value, out);
+ if (!err)
+ err = _gpgme_wait_one (ctx);
+ return TRACE_ERR (err);
+}
diff --git a/src/encrypt-sign.c b/src/encrypt-sign.c
new file mode 100644
index 0000000..7828600
--- /dev/null
+++ b/src/encrypt-sign.c
@@ -0,0 +1,157 @@
+/* encrypt-sign.c - encrypt and verify functions
+ Copyright (C) 2000 Werner Koch (dd9jn)
+ Copyright (C) 2001, 2002, 2003, 2004 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "gpgme.h"
+#include "debug.h"
+#include "context.h"
+#include "ops.h"
+
+
+static gpgme_error_t
+encrypt_sign_status_handler (void *priv, gpgme_status_code_t code, char *args)
+{
+ gpgme_error_t err;
+
+ err = _gpgme_progress_status_handler (priv, code, args);
+ if (!err)
+ err = _gpgme_encrypt_status_handler (priv, code, args);
+ if (!err)
+ err = _gpgme_sign_status_handler (priv, code, args);
+ return err;
+}
+
+
+static gpgme_error_t
+encrypt_sign_start (gpgme_ctx_t ctx, int synchronous, gpgme_key_t recp[],
+ gpgme_encrypt_flags_t flags,
+ gpgme_data_t plain, gpgme_data_t cipher)
+{
+ gpgme_error_t err;
+
+ err = _gpgme_op_reset (ctx, synchronous);
+ if (err)
+ return err;
+
+ if (!plain)
+ return gpg_error (GPG_ERR_NO_DATA);
+ if (!cipher || !recp)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ err = _gpgme_op_encrypt_init_result (ctx);
+ if (err)
+ return err;
+
+ err = _gpgme_op_sign_init_result (ctx);
+ if (err)
+ return err;
+
+ if (ctx->passphrase_cb)
+ {
+ err = _gpgme_engine_set_command_handler
+ (ctx->engine, _gpgme_passphrase_command_handler, ctx, NULL);
+ if (err)
+ return err;
+ }
+
+ _gpgme_engine_set_status_handler (ctx->engine,
+ encrypt_sign_status_handler, ctx);
+
+ return _gpgme_engine_op_encrypt_sign (ctx->engine, recp, 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. */
+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)
+{
+ gpgme_error_t err;
+
+ 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)
+ {
+ 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++;
+ }
+ }
+
+ err = encrypt_sign_start (ctx, 0, recp, flags, plain, cipher);
+ return err;
+}
+
+
+/* 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 (gpgme_ctx_t ctx, gpgme_key_t recp[],
+ 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,
+ "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)
+ {
+ 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++;
+ }
+ }
+
+ err = encrypt_sign_start (ctx, 1, recp, flags, plain, cipher);
+ if (!err)
+ err = _gpgme_wait_one (ctx);
+ return TRACE_ERR (err);
+}
diff --git a/src/encrypt.c b/src/encrypt.c
new file mode 100644
index 0000000..641e900
--- /dev/null
+++ b/src/encrypt.c
@@ -0,0 +1,288 @@
+/* encrypt.c - Encrypt function.
+ Copyright (C) 2000 Werner Koch (dd9jn)
+ Copyright (C) 2001, 2002, 2003, 2004 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "gpgme.h"
+#include "debug.h"
+#include "context.h"
+#include "ops.h"
+
+
+typedef struct
+{
+ struct _gpgme_op_encrypt_result result;
+
+ /* A pointer to the next pointer of the last invalid recipient in
+ the list. This makes appending new invalid recipients painless
+ while preserving the order. */
+ gpgme_invalid_key_t *lastp;
+} *op_data_t;
+
+
+static void
+release_op_data (void *hook)
+{
+ op_data_t opd = (op_data_t) hook;
+ gpgme_invalid_key_t invalid_recipient = opd->result.invalid_recipients;
+
+ while (invalid_recipient)
+ {
+ gpgme_invalid_key_t next = invalid_recipient->next;
+ if (invalid_recipient->fpr)
+ free (invalid_recipient->fpr);
+ free (invalid_recipient);
+ invalid_recipient = next;
+ }
+}
+
+
+gpgme_encrypt_result_t
+gpgme_op_encrypt_result (gpgme_ctx_t ctx)
+{
+ void *hook;
+ op_data_t opd;
+ gpgme_error_t err;
+
+ TRACE_BEG (DEBUG_CTX, "gpgme_op_encrypt_result", ctx);
+
+ err = _gpgme_op_data_lookup (ctx, OPDATA_ENCRYPT, &hook, -1, NULL);
+ opd = hook;
+
+ if (err || !opd)
+ {
+ TRACE_SUC0 ("result=(null)");
+ return NULL;
+ }
+
+ if (_gpgme_debug_trace ())
+ {
+ gpgme_invalid_key_t invkeys = opd->result.invalid_recipients;
+ int i = 0;
+
+ while (invkeys)
+ {
+ TRACE_LOG3 ("invalid_recipients[%i] = %s (%s)",
+ i, invkeys->fpr ? invkeys->fpr : "(null)",
+ gpg_strerror (invkeys->reason));
+ invkeys = invkeys->next;
+ i++;
+ }
+ }
+
+ TRACE_SUC1 ("result=%p", &opd->result);
+ return &opd->result;
+}
+
+
+gpgme_error_t
+_gpgme_encrypt_status_handler (void *priv, gpgme_status_code_t code,
+ char *args)
+{
+ gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
+ gpgme_error_t err;
+ void *hook;
+ op_data_t opd;
+
+ err = _gpgme_op_data_lookup (ctx, OPDATA_ENCRYPT, &hook, -1, NULL);
+ opd = hook;
+ if (err)
+ return err;
+
+ switch (code)
+ {
+ case GPGME_STATUS_EOF:
+ if (opd->result.invalid_recipients)
+ return gpg_error (GPG_ERR_UNUSABLE_PUBKEY);
+ break;
+
+ case GPGME_STATUS_INV_RECP:
+ err = _gpgme_parse_inv_recp (args, opd->lastp);
+ if (err)
+ return err;
+
+ opd->lastp = &(*opd->lastp)->next;
+ break;
+
+ case GPGME_STATUS_NO_RECP:
+ /* Should not happen, because we require at least one recipient. */
+ return gpg_error (GPG_ERR_GENERAL);
+
+ default:
+ break;
+ }
+ return 0;
+}
+
+
+static gpgme_error_t
+encrypt_sym_status_handler (void *priv, gpgme_status_code_t code, char *args)
+{
+ gpgme_error_t err;
+
+ err = _gpgme_progress_status_handler (priv, code, args);
+ if (!err)
+ err = _gpgme_passphrase_status_handler (priv, code, args);
+ return err;
+}
+
+
+static gpgme_error_t
+encrypt_status_handler (void *priv, gpgme_status_code_t code, char *args)
+{
+ return _gpgme_progress_status_handler (priv, code, args)
+ || _gpgme_encrypt_status_handler (priv, code, args);
+}
+
+
+gpgme_error_t
+_gpgme_op_encrypt_init_result (gpgme_ctx_t ctx)
+{
+ gpgme_error_t err;
+ void *hook;
+ op_data_t opd;
+
+ err = _gpgme_op_data_lookup (ctx, OPDATA_ENCRYPT, &hook, sizeof (*opd),
+ release_op_data);
+ opd = hook;
+ if (err)
+ return err;
+
+ opd->lastp = &opd->result.invalid_recipients;
+ return 0;
+}
+
+
+static gpgme_error_t
+encrypt_start (gpgme_ctx_t ctx, int synchronous, gpgme_key_t recp[],
+ gpgme_encrypt_flags_t flags,
+ gpgme_data_t plain, gpgme_data_t cipher)
+{
+ gpgme_error_t err;
+ int symmetric = 0;
+
+ err = _gpgme_op_reset (ctx, synchronous);
+ if (err)
+ return err;
+
+ err = _gpgme_op_encrypt_init_result (ctx);
+ if (err)
+ return err;
+
+ if (!recp)
+ symmetric = 1;
+
+ if (!plain)
+ return gpg_error (GPG_ERR_NO_DATA);
+ if (!cipher)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (recp && ! *recp)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if (symmetric && ctx->passphrase_cb)
+ {
+ /* Symmetric encryption requires a passphrase. */
+ err = _gpgme_engine_set_command_handler
+ (ctx->engine, _gpgme_passphrase_command_handler, ctx, NULL);
+ if (err)
+ return err;
+ }
+
+ _gpgme_engine_set_status_handler (ctx->engine,
+ symmetric
+ ? encrypt_sym_status_handler
+ : encrypt_status_handler,
+ ctx);
+
+ return _gpgme_engine_op_encrypt (ctx->engine, recp, flags, plain, cipher,
+ ctx->use_armor);
+}
+
+
+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_error_t err;
+
+ 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)
+ {
+ 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++;
+ }
+ }
+
+ err = encrypt_start (ctx, 0, recp, flags, plain, cipher);
+ 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_error_t err;
+
+ 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)
+ {
+ 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++;
+ }
+ }
+
+ err = encrypt_start (ctx, 1, recp, flags, plain, cipher);
+ if (!err)
+ err = _gpgme_wait_one (ctx);
+ return TRACE_ERR (err);
+}
diff --git a/src/engine-assuan.c b/src/engine-assuan.c
new file mode 100644
index 0000000..dedb8a1
--- /dev/null
+++ b/src/engine-assuan.c
@@ -0,0 +1,785 @@
+/* engine-assuan.c - Low-level Assuan protocol engine
+ * Copyright (C) 2009 g10 Code GmbH
+ *
+ * This file is part of GPGME.
+ *
+ * GPGME is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * GPGME is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ Note: This engine requires a modern Assuan server which uses
+ gpg-error codes. In particular there is no backward compatible
+ mapping of old Assuan error codes implemented.
+*/
+
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#include <assert.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#ifdef HAVE_LOCALE_H
+#include <locale.h>
+#endif
+#include <errno.h>
+
+#include "gpgme.h"
+#include "util.h"
+#include "ops.h"
+#include "wait.h"
+#include "priv-io.h"
+#include "sema.h"
+
+#include "assuan.h"
+#include "debug.h"
+
+#include "engine-backend.h"
+
+
+typedef struct
+{
+ int fd; /* FD we talk about. */
+ int server_fd;/* Server FD for this connection. */
+ int dir; /* Inbound/Outbound, maybe given implicit? */
+ void *data; /* Handler-specific data. */
+ void *tag; /* ID from the user for gpgme_remove_io_callback. */
+} iocb_data_t;
+
+/* Engine instance data. */
+struct engine_llass
+{
+ assuan_context_t assuan_ctx;
+
+ int lc_ctype_set;
+ int lc_messages_set;
+
+ iocb_data_t status_cb;
+
+ struct gpgme_io_cbs io_cbs;
+
+ /* Hack for old opassuan.c interface, see there the result struct. */
+ gpg_error_t last_op_err;
+
+ /* User provided callbacks. */
+ struct {
+ gpgme_assuan_data_cb_t data_cb;
+ void *data_cb_value;
+
+ gpgme_assuan_inquire_cb_t inq_cb;
+ void *inq_cb_value;
+
+ gpgme_assuan_status_cb_t status_cb;
+ void *status_cb_value;
+ } user;
+
+ /* Option flags. */
+ struct {
+ int gpg_agent:1; /* Assume this is a gpg-agent connection. */
+ } opt;
+
+};
+typedef struct engine_llass *engine_llass_t;
+
+
+gpg_error_t _gpgme_engine_assuan_last_op_err (void *engine)
+{
+ engine_llass_t llass = engine;
+ return llass->last_op_err;
+}
+
+
+/* Prototypes. */
+static void llass_io_event (void *engine,
+ gpgme_event_io_t type, void *type_data);
+
+
+
+
+
+/* return the default home directory. */
+static const char *
+llass_get_home_dir (void)
+{
+ /* For this engine the home directory is not a filename but a string
+ used to convey options. The exclamation mark is a marker to show
+ that this is not a directory name. Options are strings delimited
+ by a space. The only option defined for now is GPG_AGENT to
+ enable GPG_AGENT specific commands to send to the server at
+ connection startup. */
+ return "!GPG_AGENT";
+}
+
+static char *
+llass_get_version (const char *file_name)
+{
+ return strdup ("1.0");
+}
+
+
+static const char *
+llass_get_req_version (void)
+{
+ return "1.0";
+}
+
+
+static void
+close_notify_handler (int fd, void *opaque)
+{
+ engine_llass_t llass = opaque;
+
+ assert (fd != -1);
+ if (llass->status_cb.fd == fd)
+ {
+ if (llass->status_cb.tag)
+ llass->io_cbs.remove (llass->status_cb.tag);
+ llass->status_cb.fd = -1;
+ llass->status_cb.tag = NULL;
+ }
+}
+
+
+
+static gpgme_error_t
+llass_cancel (void *engine)
+{
+ engine_llass_t llass = engine;
+
+ if (!llass)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if (llass->status_cb.fd != -1)
+ _gpgme_io_close (llass->status_cb.fd);
+
+ if (llass->assuan_ctx)
+ {
+ assuan_release (llass->assuan_ctx);
+ llass->assuan_ctx = NULL;
+ }
+
+ return 0;
+}
+
+
+static gpgme_error_t
+llass_cancel_op (void *engine)
+{
+ engine_llass_t llass = engine;
+
+ if (!llass)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if (llass->status_cb.fd != -1)
+ _gpgme_io_close (llass->status_cb.fd);
+
+ return 0;
+}
+
+
+static void
+llass_release (void *engine)
+{
+ engine_llass_t llass = engine;
+
+ if (!llass)
+ return;
+
+ llass_cancel (engine);
+
+ free (llass);
+}
+
+
+/* Create a new instance. If HOME_DIR is NULL standard options for use
+ with gpg-agent are issued. */
+static gpgme_error_t
+llass_new (void **engine, const char *file_name, const char *home_dir)
+{
+ gpgme_error_t err = 0;
+ engine_llass_t llass;
+ char *optstr;
+
+ llass = calloc (1, sizeof *llass);
+ if (!llass)
+ return gpg_error_from_syserror ();
+
+ llass->status_cb.fd = -1;
+ llass->status_cb.dir = 1;
+ llass->status_cb.tag = 0;
+ llass->status_cb.data = llass;
+
+ /* Parse_options. */
+ if (home_dir && *home_dir == '!')
+ {
+ home_dir++;
+ /* Very simple parser only working for the one option we support. */
+ /* Note that wk promised to write a regression test if this
+ parser will be extended. */
+ if (!strncmp (home_dir, "GPG_AGENT", 9)
+ && (!home_dir[9] || home_dir[9] == ' '))
+ llass->opt.gpg_agent = 1;
+ }
+
+ err = assuan_new_ext (&llass->assuan_ctx, GPG_ERR_SOURCE_GPGME,
+ &_gpgme_assuan_malloc_hooks, _gpgme_assuan_log_cb,
+ NULL);
+ if (err)
+ goto leave;
+ assuan_ctx_set_system_hooks (llass->assuan_ctx, &_gpgme_assuan_system_hooks);
+
+ err = assuan_socket_connect (llass->assuan_ctx, file_name, 0, 0);
+ if (err)
+ goto leave;
+
+ if (llass->opt.gpg_agent)
+ {
+ char *dft_display = NULL;
+
+ err = _gpgme_getenv ("DISPLAY", &dft_display);
+ if (err)
+ goto leave;
+ if (dft_display)
+ {
+ if (asprintf (&optstr, "OPTION display=%s", dft_display) < 0)
+ {
+ err = gpg_error_from_syserror ();
+ free (dft_display);
+ goto leave;
+ }
+ free (dft_display);
+
+ err = assuan_transact (llass->assuan_ctx, optstr, NULL, NULL, NULL,
+ NULL, NULL, NULL);
+ free (optstr);
+ if (err)
+ goto leave;
+ }
+ }
+
+ if (llass->opt.gpg_agent && isatty (1))
+ {
+ int rc;
+ char dft_ttyname[64];
+ char *dft_ttytype = NULL;
+
+ rc = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname));
+ if (rc)
+ {
+ err = gpg_error_from_errno (rc);
+ goto leave;
+ }
+ else
+ {
+ if (asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ err = assuan_transact (llass->assuan_ctx, optstr, NULL, NULL, NULL,
+ NULL, NULL, NULL);
+ free (optstr);
+ if (err)
+ goto leave;
+
+ err = _gpgme_getenv ("TERM", &dft_ttytype);
+ if (err)
+ goto leave;
+ if (dft_ttytype)
+ {
+ if (asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype) < 0)
+ {
+ err = gpg_error_from_syserror ();
+ free (dft_ttytype);
+ goto leave;
+ }
+ free (dft_ttytype);
+
+ err = assuan_transact (llass->assuan_ctx, optstr, NULL, NULL,
+ NULL, NULL, NULL, NULL);
+ free (optstr);
+ if (err)
+ goto leave;
+ }
+ }
+ }
+
+
+#ifdef HAVE_W32_SYSTEM
+ /* Under Windows we need to use AllowSetForegroundWindow. Tell
+ llass to tell us when it needs it. */
+ if (!err && llass->opt.gpg_agent)
+ {
+ err = assuan_transact (llass->assuan_ctx, "OPTION allow-pinentry-notify",
+ NULL, NULL, NULL, NULL, NULL, NULL);
+ if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
+ err = 0; /* This work only with recent gpg-agents. */
+ }
+#endif /*HAVE_W32_SYSTEM*/
+
+
+ leave:
+ /* Close the server ends of the pipes (because of this, we must use
+ the stored server_fd_str in the function start). Our ends are
+ closed in llass_release(). */
+
+ if (err)
+ llass_release (llass);
+ else
+ *engine = llass;
+
+ return err;
+}
+
+
+static gpgme_error_t
+llass_set_locale (void *engine, int category, const char *value)
+{
+ gpgme_error_t err;
+ engine_llass_t llass = engine;
+ char *optstr;
+ char *catstr;
+
+ if (!llass->opt.gpg_agent)
+ return 0;
+
+ /* FIXME: If value is NULL, we need to reset the option to default.
+ But we can't do this. So we error out here. gpg-agent needs
+ support for this. */
+ if (0)
+ ;
+#ifdef LC_CTYPE
+ else if (category == LC_CTYPE)
+ {
+ catstr = "lc-ctype";
+ if (!value && llass->lc_ctype_set)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (value)
+ llass->lc_ctype_set = 1;
+ }
+#endif
+#ifdef LC_MESSAGES
+ else if (category == LC_MESSAGES)
+ {
+ catstr = "lc-messages";
+ if (!value && llass->lc_messages_set)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (value)
+ llass->lc_messages_set = 1;
+ }
+#endif /* LC_MESSAGES */
+ else
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ /* FIXME: Reset value to default. */
+ if (!value)
+ return 0;
+
+ if (asprintf (&optstr, "OPTION %s=%s", catstr, value) < 0)
+ err = gpg_error_from_errno (errno);
+ else
+ {
+ err = assuan_transact (llass->assuan_ctx, optstr, NULL, NULL,
+ NULL, NULL, NULL, NULL);
+ free (optstr);
+ }
+ return err;
+}
+
+
+/* This is the inquiry callback. It handles stuff which ee need to
+ handle here and passes everything on to the user callback. */
+static gpgme_error_t
+inquire_cb (engine_llass_t llass, const char *keyword, const char *args)
+{
+ gpg_error_t err;
+
+ if (llass->opt.gpg_agent && !strcmp (keyword, "PINENTRY_LAUNCHED"))
+ {
+ _gpgme_allow_set_foreground_window ((pid_t)strtoul (args, NULL, 10));
+ }
+
+ if (llass->user.inq_cb)
+ {
+ gpgme_data_t data = NULL;
+
+ err = llass->user.inq_cb (llass->user.inq_cb_value,
+ keyword, args, &data);
+ if (!err && data)
+ {
+ /* FIXME: Returning data is not yet implemented. However we
+ need to allow the caller to cleanup his data object.
+ Thus we run the callback in finish mode immediately. */
+ err = llass->user.inq_cb (llass->user.inq_cb_value,
+ NULL, NULL, &data);
+ }
+ }
+ else
+ err = 0;
+
+ return err;
+}
+
+
+static gpgme_error_t
+llass_status_handler (void *opaque, int fd)
+{
+ struct io_cb_data *data = (struct io_cb_data *) opaque;
+ engine_llass_t llass = (engine_llass_t) data->handler_value;
+ gpgme_error_t err = 0;
+ char *line;
+ size_t linelen;
+
+ do
+ {
+ err = assuan_read_line (llass->assuan_ctx, &line, &linelen);
+ if (err)
+ {
+ /* Reading a full line may not be possible when
+ communicating over a socket in nonblocking mode. In this
+ case, we are done for now. */
+ if (gpg_err_code (err) == GPG_ERR_EAGAIN)
+ {
+ TRACE1 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
+ "fd 0x%x: EAGAIN reading assuan line (ignored)", fd);
+ err = 0;
+ continue;
+ }
+
+ TRACE2 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
+ "fd 0x%x: error reading assuan line: %s",
+ fd, gpg_strerror (err));
+ }
+ else if (linelen >= 2 && line[0] == 'D' && line[1] == ' ')
+ {
+ char *src = line + 2;
+ char *end = line + linelen;
+ char *dst = src;
+
+ linelen = 0;
+ while (src < end)
+ {
+ if (*src == '%' && src + 2 < end)
+ {
+ /* Handle escaped characters. */
+ ++src;
+ *dst++ = _gpgme_hextobyte (src);
+ src += 2;
+ }
+ else
+ *dst++ = *src++;
+
+ linelen++;
+ }
+
+ src = line + 2;
+ if (linelen && llass->user.data_cb)
+ err = llass->user.data_cb (llass->user.data_cb_value,
+ src, linelen);
+
+ TRACE2 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
+ "fd 0x%x: D inlinedata; status from cb: %s",
+ fd, (llass->user.data_cb ?
+ (err? gpg_strerror (err):"ok"):"no callback"));
+ }
+ else if (linelen >= 3
+ && line[0] == 'E' && line[1] == 'N' && line[2] == 'D'
+ && (line[3] == '\0' || line[3] == ' '))
+ {
+ /* END received. Tell the data callback. */
+ if (llass->user.data_cb)
+ err = llass->user.data_cb (llass->user.data_cb_value, NULL, 0);
+
+ TRACE2 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
+ "fd 0x%x: END line; status from cb: %s",
+ fd, (llass->user.data_cb ?
+ (err? gpg_strerror (err):"ok"):"no callback"));
+ }
+ else if (linelen > 2 && line[0] == 'S' && line[1] == ' ')
+ {
+ char *args;
+ char *src;
+
+ for (src=line+2; *src == ' '; src++)
+ ;
+
+ args = strchr (src, ' ');
+ if (!args)
+ args = line + linelen; /* Let it point to an empty string. */
+ else
+ *(args++) = 0;
+
+ while (*args == ' ')
+ args++;
+
+ if (llass->user.status_cb)
+ err = llass->user.status_cb (llass->user.status_cb_value,
+ src, args);
+
+ TRACE3 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
+ "fd 0x%x: S line (%s) - status from cb: %s",
+ fd, line+2, (llass->user.status_cb ?
+ (err? gpg_strerror (err):"ok"):"no callback"));
+ }
+ else if (linelen >= 7
+ && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
+ && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
+ && line[6] == 'E'
+ && (line[7] == '\0' || line[7] == ' '))
+ {
+ char *src;
+ char *args;
+
+ for (src=line+7; *src == ' '; src++)
+ ;
+
+ args = strchr (src, ' ');
+ if (!args)
+ args = line + linelen; /* Let it point to an empty string. */
+ else
+ *(args++) = 0;
+
+ while (*args == ' ')
+ args++;
+
+ err = inquire_cb (llass, src, args);
+ if (!err)
+ {
+ /* Flush and send END. */
+ err = assuan_send_data (llass->assuan_ctx, NULL, 0);
+ }
+ else if (gpg_err_code (err) == GPG_ERR_ASS_CANCELED)
+ {
+ /* Flush and send CANcel. */
+ err = assuan_send_data (llass->assuan_ctx, NULL, 1);
+ }
+ }
+ else if (linelen >= 3
+ && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
+ && (line[3] == '\0' || line[3] == ' '))
+ {
+ if (line[3] == ' ')
+ err = atoi (line+4);
+ else
+ err = gpg_error (GPG_ERR_GENERAL);
+ TRACE2 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
+ "fd 0x%x: ERR line: %s",
+ fd, err ? gpg_strerror (err) : "ok");
+
+ /* Command execution errors are not fatal, as we use
+ a session based protocol. */
+ data->op_err = err;
+ llass->last_op_err = err;
+
+ /* The caller will do the rest (namely, call cancel_op,
+ which closes status_fd). */
+ return 0;
+ }
+ else if (linelen >= 2
+ && line[0] == 'O' && line[1] == 'K'
+ && (line[2] == '\0' || line[2] == ' '))
+ {
+ TRACE1 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
+ "fd 0x%x: OK line", fd);
+
+ llass->last_op_err = 0;
+
+ _gpgme_io_close (llass->status_cb.fd);
+ return 0;
+ }
+ else
+ {
+ /* Comment line or invalid line. */
+ }
+
+ }
+ while (!err && assuan_pending_line (llass->assuan_ctx));
+
+ return err;
+}
+
+
+static gpgme_error_t
+add_io_cb (engine_llass_t llass, iocb_data_t *iocbd, gpgme_io_cb_t handler)
+{
+ gpgme_error_t err;
+
+ TRACE_BEG2 (DEBUG_ENGINE, "engine-assuan:add_io_cb", llass,
+ "fd %d, dir %d", iocbd->fd, iocbd->dir);
+ err = (*llass->io_cbs.add) (llass->io_cbs.add_priv,
+ iocbd->fd, iocbd->dir,
+ handler, iocbd->data, &iocbd->tag);
+ if (err)
+ return TRACE_ERR (err);
+ if (!iocbd->dir)
+ /* FIXME Kludge around poll() problem. */
+ err = _gpgme_io_set_nonblocking (iocbd->fd);
+ return TRACE_ERR (err);
+}
+
+
+static gpgme_error_t
+start (engine_llass_t llass, const char *command)
+{
+ gpgme_error_t err;
+ assuan_fd_t afdlist[5];
+ int fdlist[5];
+ int nfds;
+ int i;
+
+ /* We need to know the fd used by assuan for reads. We do this by
+ using the assumption that the first returned fd from
+ assuan_get_active_fds() is always this one. */
+ nfds = assuan_get_active_fds (llass->assuan_ctx, 0 /* read fds */,
+ afdlist, DIM (afdlist));
+ if (nfds < 1)
+ return gpg_error (GPG_ERR_GENERAL); /* FIXME */
+ /* For now... */
+ for (i = 0; i < nfds; i++)
+ fdlist[i] = (int) afdlist[i];
+
+ /* We "duplicate" the file descriptor, so we can close it here (we
+ can't close fdlist[0], as that is closed by libassuan, and
+ closing it here might cause libassuan to close some unrelated FD
+ later). Alternatively, we could special case status_fd and
+ register/unregister it manually as needed, but this increases
+ code duplication and is more complicated as we can not use the
+ close notifications etc. A third alternative would be to let
+ Assuan know that we closed the FD, but that complicates the
+ Assuan interface. */
+
+ llass->status_cb.fd = _gpgme_io_dup (fdlist[0]);
+ if (llass->status_cb.fd < 0)
+ return gpg_error_from_syserror ();
+
+ if (_gpgme_io_set_close_notify (llass->status_cb.fd,
+ close_notify_handler, llass))
+ {
+ _gpgme_io_close (llass->status_cb.fd);
+ llass->status_cb.fd = -1;
+ return gpg_error (GPG_ERR_GENERAL);
+ }
+
+ err = add_io_cb (llass, &llass->status_cb, llass_status_handler);
+ if (!err)
+ err = assuan_write_line (llass->assuan_ctx, command);
+
+ /* FIXME: If *command == '#' no answer is expected. */
+
+ if (!err)
+ llass_io_event (llass, GPGME_EVENT_START, NULL);
+
+ return err;
+}
+
+
+
+static gpgme_error_t
+llass_transact (void *engine,
+ const char *command,
+ gpgme_assuan_data_cb_t data_cb,
+ void *data_cb_value,
+ gpgme_assuan_inquire_cb_t inq_cb,
+ void *inq_cb_value,
+ gpgme_assuan_status_cb_t status_cb,
+ void *status_cb_value)
+{
+ engine_llass_t llass = engine;
+ gpgme_error_t err;
+
+ if (!llass || !command || !*command)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ llass->user.data_cb = data_cb;
+ llass->user.data_cb_value = data_cb_value;
+ llass->user.inq_cb = inq_cb;
+ llass->user.inq_cb_value = inq_cb_value;
+ llass->user.status_cb = status_cb;
+ llass->user.status_cb_value = status_cb_value;
+
+ err = start (llass, command);
+ return err;
+}
+
+
+
+static void
+llass_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
+{
+ engine_llass_t llass = engine;
+ llass->io_cbs = *io_cbs;
+}
+
+
+static void
+llass_io_event (void *engine, gpgme_event_io_t type, void *type_data)
+{
+ engine_llass_t llass = engine;
+
+ TRACE3 (DEBUG_ENGINE, "gpgme:llass_io_event", llass,
+ "event %p, type %d, type_data %p",
+ llass->io_cbs.event, type, type_data);
+ if (llass->io_cbs.event)
+ (*llass->io_cbs.event) (llass->io_cbs.event_priv, type, type_data);
+}
+
+
+struct engine_ops _gpgme_engine_ops_assuan =
+ {
+ /* Static functions. */
+ _gpgme_get_default_agent_socket,
+ llass_get_home_dir,
+ llass_get_version,
+ llass_get_req_version,
+ llass_new,
+
+ /* Member functions. */
+ llass_release,
+ NULL, /* reset */
+ NULL, /* set_status_handler */
+ NULL, /* set_command_handler */
+ NULL, /* set_colon_line_handler */
+ llass_set_locale,
+ NULL, /* set_protocol */
+ NULL, /* decrypt */
+ NULL, /* decrypt_verify */
+ NULL, /* delete */
+ NULL, /* edit */
+ NULL, /* encrypt */
+ NULL, /* encrypt_sign */
+ NULL, /* export */
+ NULL, /* export_ext */
+ NULL, /* genkey */
+ NULL, /* import */
+ NULL, /* keylist */
+ NULL, /* keylist_ext */
+ NULL, /* sign */
+ NULL, /* trustlist */
+ NULL, /* verify */
+ NULL, /* getauditlog */
+ llass_transact, /* opassuan_transact */
+ NULL, /* conf_load */
+ NULL, /* conf_save */
+ llass_set_io_cbs,
+ llass_io_event,
+ llass_cancel,
+ llass_cancel_op
+ };
diff --git a/src/engine-backend.h b/src/engine-backend.h
new file mode 100644
index 0000000..e540acb
--- /dev/null
+++ b/src/engine-backend.h
@@ -0,0 +1,144 @@
+/* engine-backend.h - A crypto backend for the engine interface.
+ Copyright (C) 2002, 2003, 2004, 2009 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef ENGINE_BACKEND_H
+#define ENGINE_BACKEND_H
+
+#include "engine.h"
+
+struct engine_ops
+{
+ /* Static functions. */
+
+ /* Return the default file name for the binary of this engine. */
+ const char *(*get_file_name) (void);
+
+ /* Return the default home dir for the binary of this engine. If
+ this function pointer is not set, the standard default home dir
+ of the engine is used. */
+ const char *(*get_home_dir) (void);
+
+ /* Returns a malloced string containing the version of the engine
+ with the given binary file name (or the default if FILE_NAME is
+ NULL. */
+ char *(*get_version) (const char *file_name);
+
+ /* Returns a statically allocated string containing the required
+ version. */
+ const char *(*get_req_version) (void);
+
+ gpgme_error_t (*new) (void **r_engine,
+ const char *file_name, const char *home_dir);
+
+ /* Member functions. */
+ void (*release) (void *engine);
+ gpgme_error_t (*reset) (void *engine);
+ void (*set_status_handler) (void *engine, engine_status_handler_t fnc,
+ void *fnc_value);
+ gpgme_error_t (*set_command_handler) (void *engine,
+ engine_command_handler_t fnc,
+ void *fnc_value, gpgme_data_t data);
+ gpgme_error_t (*set_colon_line_handler) (void *engine,
+ engine_colon_line_handler_t fnc,
+ void *fnc_value);
+ gpgme_error_t (*set_locale) (void *engine, int category, const char *value);
+ gpgme_error_t (*set_protocol) (void *engine, gpgme_protocol_t protocol);
+ gpgme_error_t (*decrypt) (void *engine, gpgme_data_t ciph,
+ gpgme_data_t plain);
+ gpgme_error_t (*decrypt_verify) (void *engine, gpgme_data_t ciph,
+ gpgme_data_t plain);
+ gpgme_error_t (*delete) (void *engine, gpgme_key_t key, int allow_secret);
+ gpgme_error_t (*edit) (void *engine, int type, gpgme_key_t key,
+ gpgme_data_t out, gpgme_ctx_t ctx /* FIXME */);
+ gpgme_error_t (*encrypt) (void *engine, gpgme_key_t recp[],
+ gpgme_encrypt_flags_t flags,
+ gpgme_data_t plain, gpgme_data_t ciph,
+ int use_armor);
+ gpgme_error_t (*encrypt_sign) (void *engine, gpgme_key_t recp[],
+ gpgme_encrypt_flags_t flags,
+ gpgme_data_t plain, gpgme_data_t ciph,
+ int use_armor, gpgme_ctx_t ctx /* FIXME */);
+ gpgme_error_t (*export) (void *engine, const char *pattern,
+ gpgme_export_mode_t mode, gpgme_data_t keydata,
+ int use_armor);
+ gpgme_error_t (*export_ext) (void *engine, const char *pattern[],
+ gpgme_export_mode_t mode, gpgme_data_t keydata,
+ int use_armor);
+ gpgme_error_t (*genkey) (void *engine, gpgme_data_t help_data, int use_armor,
+ gpgme_data_t pubkey, gpgme_data_t seckey);
+ gpgme_error_t (*import) (void *engine, gpgme_data_t keydata,
+ gpgme_key_t *keyarray);
+ gpgme_error_t (*keylist) (void *engine, const char *pattern,
+ int secret_only, gpgme_keylist_mode_t mode);
+ gpgme_error_t (*keylist_ext) (void *engine, const char *pattern[],
+ int secret_only, int reserved,
+ gpgme_keylist_mode_t mode);
+ gpgme_error_t (*sign) (void *engine, gpgme_data_t in, gpgme_data_t out,
+ gpgme_sig_mode_t mode, int use_armor,
+ int use_textmode, int include_certs,
+ gpgme_ctx_t ctx /* FIXME */);
+ gpgme_error_t (*trustlist) (void *engine, const char *pattern);
+ gpgme_error_t (*verify) (void *engine, gpgme_data_t sig,
+ gpgme_data_t signed_text, gpgme_data_t plaintext);
+ gpgme_error_t (*getauditlog) (void *engine, gpgme_data_t output,
+ unsigned int flags);
+ gpgme_error_t (*opassuan_transact) (void *engine,
+ const char *command,
+ gpgme_assuan_data_cb_t data_cb,
+ void *data_cb_value,
+ gpgme_assuan_inquire_cb_t inq_cb,
+ void *inq_cb_value,
+ gpgme_assuan_status_cb_t status_cb,
+ void *status_cb_value);
+
+ gpgme_error_t (*conf_load) (void *engine, gpgme_conf_comp_t *conf_p);
+ gpgme_error_t (*conf_save) (void *engine, gpgme_conf_comp_t conf);
+
+ void (*set_io_cbs) (void *engine, gpgme_io_cbs_t io_cbs);
+ void (*io_event) (void *engine, gpgme_event_io_t type, void *type_data);
+
+ /* Cancel the whole engine session. */
+ gpgme_error_t (*cancel) (void *engine);
+
+ /* Cancel only the current operation, not the whole session. */
+ gpgme_error_t (*cancel_op) (void *engine);
+
+ /* Change the passphrase for KEY. */
+ gpgme_error_t (*passwd) (void *engine, gpgme_key_t key, unsigned int flags);
+};
+
+
+extern struct engine_ops _gpgme_engine_ops_gpg; /* OpenPGP. */
+#ifdef ENABLE_GPGSM
+extern struct engine_ops _gpgme_engine_ops_gpgsm; /* CMS. */
+#endif
+#ifdef ENABLE_GPGCONF
+extern struct engine_ops _gpgme_engine_ops_gpgconf; /* gpg-conf. */
+#endif
+#ifdef ENABLE_ASSUAN
+extern struct engine_ops _gpgme_engine_ops_assuan; /* Low-level Assuan. */
+#endif
+#ifdef ENABLE_G13
+extern struct engine_ops _gpgme_engine_ops_g13; /* Crypto VFS. */
+#endif
+#ifdef ENABLE_UISERVER
+extern struct engine_ops _gpgme_engine_ops_uiserver;
+#endif
+
+#endif /* ENGINE_BACKEND_H */
diff --git a/src/engine-g13.c b/src/engine-g13.c
new file mode 100644
index 0000000..6ba49c4
--- /dev/null
+++ b/src/engine-g13.c
@@ -0,0 +1,801 @@
+/* engine-g13.c - G13 engine.
+ Copyright (C) 2000 Werner Koch (dd9jn)
+ Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2009 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#include <assert.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#ifdef HAVE_LOCALE_H
+#include <locale.h>
+#endif
+#include <fcntl.h> /* FIXME */
+#include <errno.h>
+
+#include "gpgme.h"
+#include "util.h"
+#include "ops.h"
+#include "wait.h"
+#include "priv-io.h"
+#include "sema.h"
+
+#include "assuan.h"
+#include "debug.h"
+
+#include "engine-backend.h"
+
+
+typedef struct
+{
+ int fd; /* FD we talk about. */
+ int server_fd;/* Server FD for this connection. */
+ int dir; /* Inbound/Outbound, maybe given implicit? */
+ void *data; /* Handler-specific data. */
+ void *tag; /* ID from the user for gpgme_remove_io_callback. */
+ char server_fd_str[15]; /* Same as SERVER_FD but as a string. We
+ need this because _gpgme_io_fd2str can't
+ be used on a closed descriptor. */
+} iocb_data_t;
+
+
+struct engine_g13
+{
+ assuan_context_t assuan_ctx;
+
+ int lc_ctype_set;
+ int lc_messages_set;
+
+ iocb_data_t status_cb;
+
+ struct gpgme_io_cbs io_cbs;
+
+ /* User provided callbacks. */
+ struct {
+ gpgme_assuan_data_cb_t data_cb;
+ void *data_cb_value;
+
+ gpgme_assuan_inquire_cb_t inq_cb;
+ void *inq_cb_value;
+
+ gpgme_assuan_status_cb_t status_cb;
+ void *status_cb_value;
+ } user;
+};
+
+typedef struct engine_g13 *engine_g13_t;
+
+
+static void g13_io_event (void *engine,
+ gpgme_event_io_t type, void *type_data);
+
+
+
+static char *
+g13_get_version (const char *file_name)
+{
+ return _gpgme_get_program_version (file_name ? file_name
+ : _gpgme_get_g13_path ());
+}
+
+
+static const char *
+g13_get_req_version (void)
+{
+ return NEED_G13_VERSION;
+}
+
+
+static void
+close_notify_handler (int fd, void *opaque)
+{
+ engine_g13_t g13 = opaque;
+
+ assert (fd != -1);
+ if (g13->status_cb.fd == fd)
+ {
+ if (g13->status_cb.tag)
+ (*g13->io_cbs.remove) (g13->status_cb.tag);
+ g13->status_cb.fd = -1;
+ g13->status_cb.tag = NULL;
+ }
+}
+
+
+/* This is the default inquiry callback. We use it to handle the
+ Pinentry notifications. */
+static gpgme_error_t
+default_inq_cb (engine_g13_t g13, const char *keyword, const char *args)
+{
+ gpg_error_t err;
+
+ if (!strcmp (keyword, "PINENTRY_LAUNCHED"))
+ {
+ _gpgme_allow_set_foreground_window ((pid_t)strtoul (args, NULL, 10));
+ }
+
+ if (g13->user.inq_cb)
+ {
+ gpgme_data_t data = NULL;
+
+ err = g13->user.inq_cb (g13->user.inq_cb_value,
+ keyword, args, &data);
+ if (!err && data)
+ {
+ /* FIXME: Returning data is not yet implemented. However we
+ need to allow the caller to cleanup his data object.
+ Thus we run the callback in finish mode immediately. */
+ err = g13->user.inq_cb (g13->user.inq_cb_value,
+ NULL, NULL, &data);
+ }
+ }
+ else
+ err = 0;
+
+ return err;
+}
+
+
+static gpgme_error_t
+g13_cancel (void *engine)
+{
+ engine_g13_t g13 = engine;
+
+ if (!g13)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if (g13->status_cb.fd != -1)
+ _gpgme_io_close (g13->status_cb.fd);
+
+ if (g13->assuan_ctx)
+ {
+ assuan_release (g13->assuan_ctx);
+ g13->assuan_ctx = NULL;
+ }
+
+ return 0;
+}
+
+
+static gpgme_error_t
+g13_cancel_op (void *engine)
+{
+ engine_g13_t g13 = engine;
+
+ if (!g13)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if (g13->status_cb.fd != -1)
+ _gpgme_io_close (g13->status_cb.fd);
+
+ return 0;
+}
+
+
+static void
+g13_release (void *engine)
+{
+ engine_g13_t g13 = engine;
+
+ if (!g13)
+ return;
+
+ g13_cancel (engine);
+
+ free (g13);
+}
+
+
+static gpgme_error_t
+g13_new (void **engine, const char *file_name, const char *home_dir)
+{
+ gpgme_error_t err = 0;
+ engine_g13_t g13;
+ int argc;
+ const char *argv[5];
+ char *dft_display = NULL;
+ char dft_ttyname[64];
+ char *dft_ttytype = NULL;
+ char *optstr;
+
+ g13 = calloc (1, sizeof *g13);
+ if (!g13)
+ return gpg_error_from_errno (errno);
+
+ g13->status_cb.fd = -1;
+ g13->status_cb.dir = 1;
+ g13->status_cb.tag = 0;
+ g13->status_cb.data = g13;
+
+ argc = 0;
+ argv[argc++] = "g13";
+ if (home_dir)
+ {
+ argv[argc++] = "--homedir";
+ argv[argc++] = home_dir;
+ }
+ argv[argc++] = "--server";
+ argv[argc++] = NULL;
+
+ err = assuan_new_ext (&g13->assuan_ctx, GPG_ERR_SOURCE_GPGME,
+ &_gpgme_assuan_malloc_hooks, _gpgme_assuan_log_cb,
+ NULL);
+ if (err)
+ goto leave;
+ assuan_ctx_set_system_hooks (g13->assuan_ctx, &_gpgme_assuan_system_hooks);
+
+#if USE_DESCRIPTOR_PASSING
+ err = assuan_pipe_connect
+ (g13->assuan_ctx, file_name ? file_name : _gpgme_get_g13_path (),
+ argv, NULL, NULL, NULL, ASSUAN_PIPE_CONNECT_FDPASSING);
+#else
+ err = assuan_pipe_connect
+ (g13->assuan_ctx, file_name ? file_name : _gpgme_get_g13_path (),
+ argv, NULL, NULL, NULL, 0);
+#endif
+ if (err)
+ goto leave;
+
+ err = _gpgme_getenv ("DISPLAY", &dft_display);
+ if (err)
+ goto leave;
+ if (dft_display)
+ {
+ if (asprintf (&optstr, "OPTION display=%s", dft_display) < 0)
+ {
+ free (dft_display);
+ err = gpg_error_from_errno (errno);
+ goto leave;
+ }
+ free (dft_display);
+
+ err = assuan_transact (g13->assuan_ctx, optstr, NULL, NULL, NULL,
+ NULL, NULL, NULL);
+ free (optstr);
+ if (err)
+ goto leave;
+ }
+
+ if (isatty (1))
+ {
+ int rc;
+
+ rc = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname));
+ if (rc)
+ {
+ err = gpg_error_from_errno (rc);
+ goto leave;
+ }
+ else
+ {
+ if (asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0)
+ {
+ err = gpg_error_from_errno (errno);
+ goto leave;
+ }
+ err = assuan_transact (g13->assuan_ctx, optstr, NULL, NULL, NULL,
+ NULL, NULL, NULL);
+ free (optstr);
+ if (err)
+ goto leave;
+
+ err = _gpgme_getenv ("TERM", &dft_ttytype);
+ if (err)
+ goto leave;
+ if (dft_ttytype)
+ {
+ if (asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype) < 0)
+ {
+ free (dft_ttytype);
+ err = gpg_error_from_errno (errno);
+ goto leave;
+ }
+ free (dft_ttytype);
+
+ err = assuan_transact (g13->assuan_ctx, optstr, NULL, NULL,
+ NULL, NULL, NULL, NULL);
+ free (optstr);
+ if (err)
+ goto leave;
+ }
+ }
+ }
+
+#ifdef HAVE_W32_SYSTEM
+ /* Under Windows we need to use AllowSetForegroundWindow. Tell
+ g13 to tell us when it needs it. */
+ if (!err)
+ {
+ err = assuan_transact (g13->assuan_ctx, "OPTION allow-pinentry-notify",
+ NULL, NULL, NULL, NULL, NULL, NULL);
+ if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
+ err = 0; /* This is a new feature of g13. */
+ }
+#endif /*HAVE_W32_SYSTEM*/
+
+ leave:
+
+ if (err)
+ g13_release (g13);
+ else
+ *engine = g13;
+
+ return err;
+}
+
+
+static gpgme_error_t
+g13_set_locale (void *engine, int category, const char *value)
+{
+ engine_g13_t g13 = engine;
+ gpgme_error_t err;
+ char *optstr;
+ char *catstr;
+
+ /* FIXME: If value is NULL, we need to reset the option to default.
+ But we can't do this. So we error out here. G13 needs support
+ for this. */
+ if (0)
+ ;
+#ifdef LC_CTYPE
+ else if (category == LC_CTYPE)
+ {
+ catstr = "lc-ctype";
+ if (!value && g13->lc_ctype_set)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (value)
+ g13->lc_ctype_set = 1;
+ }
+#endif
+#ifdef LC_MESSAGES
+ else if (category == LC_MESSAGES)
+ {
+ catstr = "lc-messages";
+ if (!value && g13->lc_messages_set)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (value)
+ g13->lc_messages_set = 1;
+ }
+#endif /* LC_MESSAGES */
+ else
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ /* FIXME: Reset value to default. */
+ if (!value)
+ return 0;
+
+ if (asprintf (&optstr, "OPTION %s=%s", catstr, value) < 0)
+ err = gpg_error_from_errno (errno);
+ else
+ {
+ err = assuan_transact (g13->assuan_ctx, optstr, NULL, NULL,
+ NULL, NULL, NULL, NULL);
+ free (optstr);
+ }
+
+ return err;
+}
+
+
+#if USE_DESCRIPTOR_PASSING
+static gpgme_error_t
+g13_assuan_simple_command (assuan_context_t ctx, char *cmd,
+ engine_status_handler_t status_fnc,
+ void *status_fnc_value)
+{
+ gpg_error_t err;
+ char *line;
+ size_t linelen;
+
+ err = assuan_write_line (ctx, cmd);
+ if (err)
+ return err;
+
+ do
+ {
+ err = assuan_read_line (ctx, &line, &linelen);
+ if (err)
+ return err;
+
+ if (*line == '#' || !linelen)
+ continue;
+
+ if (linelen >= 2
+ && line[0] == 'O' && line[1] == 'K'
+ && (line[2] == '\0' || line[2] == ' '))
+ return 0;
+ else if (linelen >= 4
+ && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
+ && line[3] == ' ')
+ err = atoi (&line[4]);
+ else if (linelen >= 2
+ && line[0] == 'S' && line[1] == ' ')
+ {
+ char *rest;
+
+ rest = strchr (line + 2, ' ');
+ if (!rest)
+ rest = line + linelen; /* set to an empty string */
+ else
+ *(rest++) = 0;
+
+ /* Nothing to do with status lines. */
+ }
+ else
+ err = gpg_error (GPG_ERR_GENERAL);
+ }
+ while (!err);
+
+ return err;
+}
+#endif
+
+
+static gpgme_error_t
+status_handler (void *opaque, int fd)
+{
+ struct io_cb_data *data = (struct io_cb_data *) opaque;
+ engine_g13_t g13 = (engine_g13_t) data->handler_value;
+ gpgme_error_t err = 0;
+ char *line;
+ size_t linelen;
+
+ do
+ {
+ err = assuan_read_line (g13->assuan_ctx, &line, &linelen);
+ if (err)
+ {
+ /* Try our best to terminate the connection friendly. */
+ /* assuan_write_line (g13->assuan_ctx, "BYE"); */
+ TRACE2 (DEBUG_CTX, "gpgme:status_handler", g13,
+ "fd 0x%x: error reading assuan line: %s",
+ fd, gpg_strerror (err));
+ }
+ else if (linelen >= 3
+ && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
+ && (line[3] == '\0' || line[3] == ' '))
+ {
+ if (line[3] == ' ')
+ err = atoi (&line[4]);
+ if (! err)
+ err = gpg_error (GPG_ERR_GENERAL);
+ TRACE2 (DEBUG_CTX, "gpgme:status_handler", g13,
+ "fd 0x%x: ERR line: %s",
+ fd, err ? gpg_strerror (err) : "ok");
+
+ /* Command execution errors are not fatal, as we use
+ a session based protocol. */
+ data->op_err = err;
+
+ /* The caller will do the rest (namely, call cancel_op,
+ which closes status_fd). */
+ return 0;
+ }
+ else if (linelen >= 2
+ && line[0] == 'O' && line[1] == 'K'
+ && (line[2] == '\0' || line[2] == ' '))
+ {
+ TRACE1 (DEBUG_CTX, "gpgme:status_handler", g13,
+ "fd 0x%x: OK line", fd);
+
+ _gpgme_io_close (g13->status_cb.fd);
+ return 0;
+ }
+ else if (linelen > 2
+ && line[0] == 'D' && line[1] == ' ')
+ {
+ /* We are using the colon handler even for plain inline data
+ - strange name for that function but for historic reasons
+ we keep it. */
+ /* FIXME We can't use this for binary data because we
+ assume this is a string. For the current usage of colon
+ output it is correct. */
+ char *src = line + 2;
+ char *end = line + linelen;
+ char *dst = src;
+
+ linelen = 0;
+ while (src < end)
+ {
+ if (*src == '%' && src + 2 < end)
+ {
+ /* Handle escaped characters. */
+ ++src;
+ *dst++ = _gpgme_hextobyte (src);
+ src += 2;
+ }
+ else
+ *dst++ = *src++;
+
+ linelen++;
+ }
+
+ src = line + 2;
+ if (linelen && g13->user.data_cb)
+ err = g13->user.data_cb (g13->user.data_cb_value,
+ src, linelen);
+ else
+ err = 0;
+
+ TRACE2 (DEBUG_CTX, "gpgme:g13_status_handler", g13,
+ "fd 0x%x: D inlinedata; status from cb: %s",
+ fd, (g13->user.data_cb ?
+ (err? gpg_strerror (err):"ok"):"no callback"));
+
+ }
+ else if (linelen > 2
+ && line[0] == 'S' && line[1] == ' ')
+ {
+ char *src;
+ char *args;
+
+ src = line + 2;
+ while (*src == ' ')
+ src++;
+
+ args = strchr (line + 2, ' ');
+ if (!args)
+ args = line + linelen; /* set to an empty string */
+ else
+ *(args++) = 0;
+
+ while (*args == ' ')
+ args++;
+
+ if (g13->user.status_cb)
+ err = g13->user.status_cb (g13->user.status_cb_value,
+ src, args);
+ else
+ err = 0;
+
+ TRACE3 (DEBUG_CTX, "gpgme:g13_status_handler", g13,
+ "fd 0x%x: S line (%s) - status from cb: %s",
+ fd, line+2, (g13->user.status_cb ?
+ (err? gpg_strerror (err):"ok"):"no callback"));
+ }
+ else if (linelen >= 7
+ && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
+ && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
+ && line[6] == 'E'
+ && (line[7] == '\0' || line[7] == ' '))
+ {
+ char *src;
+ char *args;
+
+ for (src=line+7; *src == ' '; src++)
+ ;
+
+ args = strchr (src, ' ');
+ if (!args)
+ args = line + linelen; /* Let it point to an empty string. */
+ else
+ *(args++) = 0;
+
+ while (*args == ' ')
+ args++;
+
+ err = default_inq_cb (g13, src, args);
+ if (!err)
+ {
+ /* Flush and send END. */
+ err = assuan_send_data (g13->assuan_ctx, NULL, 0);
+ }
+ else if (gpg_err_code (err) == GPG_ERR_ASS_CANCELED)
+ {
+ /* Flush and send CANcel. */
+ err = assuan_send_data (g13->assuan_ctx, NULL, 1);
+ }
+ assuan_write_line (g13->assuan_ctx, "END");
+ }
+ }
+ while (!err && assuan_pending_line (g13->assuan_ctx));
+
+ return err;
+}
+
+
+static gpgme_error_t
+add_io_cb (engine_g13_t g13, iocb_data_t *iocbd, gpgme_io_cb_t handler)
+{
+ gpgme_error_t err;
+
+ TRACE_BEG2 (DEBUG_ENGINE, "engine-g13:add_io_cb", g13,
+ "fd %d, dir %d", iocbd->fd, iocbd->dir);
+ err = (*g13->io_cbs.add) (g13->io_cbs.add_priv,
+ iocbd->fd, iocbd->dir,
+ handler, iocbd->data, &iocbd->tag);
+ if (err)
+ return TRACE_ERR (err);
+ if (!iocbd->dir)
+ /* FIXME Kludge around poll() problem. */
+ err = _gpgme_io_set_nonblocking (iocbd->fd);
+ return TRACE_ERR (err);
+}
+
+
+static gpgme_error_t
+start (engine_g13_t g13, const char *command)
+{
+ gpgme_error_t err;
+ assuan_fd_t afdlist[5];
+ int fdlist[5];
+ int nfds;
+ int i;
+
+ /* We need to know the fd used by assuan for reads. We do this by
+ using the assumption that the first returned fd from
+ assuan_get_active_fds() is always this one. */
+ nfds = assuan_get_active_fds (g13->assuan_ctx, 0 /* read fds */,
+ afdlist, DIM (afdlist));
+ if (nfds < 1)
+ return gpg_error (GPG_ERR_GENERAL); /* FIXME */
+ /* For now... */
+ for (i = 0; i < nfds; i++)
+ fdlist[i] = (int) afdlist[i];
+
+ /* We "duplicate" the file descriptor, so we can close it here (we
+ can't close fdlist[0], as that is closed by libassuan, and
+ closing it here might cause libassuan to close some unrelated FD
+ later). Alternatively, we could special case status_fd and
+ register/unregister it manually as needed, but this increases
+ code duplication and is more complicated as we can not use the
+ close notifications etc. A third alternative would be to let
+ Assuan know that we closed the FD, but that complicates the
+ Assuan interface. */
+
+ g13->status_cb.fd = _gpgme_io_dup (fdlist[0]);
+ if (g13->status_cb.fd < 0)
+ return gpg_error_from_syserror ();
+
+ if (_gpgme_io_set_close_notify (g13->status_cb.fd,
+ close_notify_handler, g13))
+ {
+ _gpgme_io_close (g13->status_cb.fd);
+ g13->status_cb.fd = -1;
+ return gpg_error (GPG_ERR_GENERAL);
+ }
+
+ err = add_io_cb (g13, &g13->status_cb, status_handler);
+ if (!err)
+ err = assuan_write_line (g13->assuan_ctx, command);
+
+ if (!err)
+ g13_io_event (g13, GPGME_EVENT_START, NULL);
+
+ return err;
+}
+
+
+#if USE_DESCRIPTOR_PASSING
+static gpgme_error_t
+g13_reset (void *engine)
+{
+ engine_g13_t g13 = engine;
+
+ /* We must send a reset because we need to reset the list of
+ signers. Note that RESET does not reset OPTION commands. */
+ return g13_assuan_simple_command (g13->assuan_ctx, "RESET", NULL, NULL);
+}
+#endif
+
+
+static gpgme_error_t
+g13_transact (void *engine,
+ const char *command,
+ gpgme_assuan_data_cb_t data_cb,
+ void *data_cb_value,
+ gpgme_assuan_inquire_cb_t inq_cb,
+ void *inq_cb_value,
+ gpgme_assuan_status_cb_t status_cb,
+ void *status_cb_value)
+{
+ engine_g13_t g13 = engine;
+ gpgme_error_t err;
+
+ if (!g13 || !command || !*command)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ g13->user.data_cb = data_cb;
+ g13->user.data_cb_value = data_cb_value;
+ g13->user.inq_cb = inq_cb;
+ g13->user.inq_cb_value = inq_cb_value;
+ g13->user.status_cb = status_cb;
+ g13->user.status_cb_value = status_cb_value;
+
+ err = start (g13, command);
+ return err;
+}
+
+
+
+static void
+g13_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
+{
+ engine_g13_t g13 = engine;
+ g13->io_cbs = *io_cbs;
+}
+
+
+static void
+g13_io_event (void *engine, gpgme_event_io_t type, void *type_data)
+{
+ engine_g13_t g13 = engine;
+
+ TRACE3 (DEBUG_ENGINE, "gpgme:g13_io_event", g13,
+ "event %p, type %d, type_data %p",
+ g13->io_cbs.event, type, type_data);
+ if (g13->io_cbs.event)
+ (*g13->io_cbs.event) (g13->io_cbs.event_priv, type, type_data);
+}
+
+
+struct engine_ops _gpgme_engine_ops_g13 =
+ {
+ /* Static functions. */
+ _gpgme_get_g13_path,
+ NULL,
+ g13_get_version,
+ g13_get_req_version,
+ g13_new,
+
+ /* Member functions. */
+ g13_release,
+#if USE_DESCRIPTOR_PASSING
+ g13_reset,
+#else
+ NULL, /* reset */
+#endif
+ NULL, /* set_status_handler */
+ NULL, /* set_command_handler */
+ NULL, /* set_colon_line_handler */
+ g13_set_locale,
+ NULL, /* set_protocol */
+ NULL, /* decrypt */
+ NULL, /* decrypt_verify */
+ NULL, /* delete */
+ NULL, /* edit */
+ NULL, /* encrypt */
+ NULL, /* encrypt_sign */
+ NULL, /* export */
+ NULL, /* export_ext */
+ NULL, /* genkey */
+ NULL, /* import */
+ NULL, /* keylist */
+ NULL, /* keylist_ext */
+ NULL, /* sign */
+ NULL, /* trustlist */
+ NULL, /* verify */
+ NULL, /* getauditlog */
+ g13_transact,
+ NULL, /* conf_load */
+ NULL, /* conf_save */
+ g13_set_io_cbs,
+ g13_io_event,
+ g13_cancel,
+ g13_cancel_op,
+ };
diff --git a/src/engine-gpg.c b/src/engine-gpg.c
new file mode 100644
index 0000000..cbb456e
--- /dev/null
+++ b/src/engine-gpg.c
@@ -0,0 +1,2393 @@
+/* engine-gpg.c - Gpg Engine.
+ Copyright (C) 2000 Werner Koch (dd9jn)
+ Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007,
+ 2009, 2010, 2012 g10 Code GmbH
+
+ 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/>.
+*/
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#ifdef HAVE_LOCALE_H
+#include <locale.h>
+#endif
+
+#include "gpgme.h"
+#include "util.h"
+#include "ops.h"
+#include "wait.h"
+#include "context.h" /*temp hack until we have GpmeData methods to do I/O */
+#include "priv-io.h"
+#include "sema.h"
+#include "debug.h"
+
+#include "engine-backend.h"
+
+
+/* This type is used to build a list of gpg arguments and data
+ sources/sinks. */
+struct arg_and_data_s
+{
+ struct arg_and_data_s *next;
+ gpgme_data_t data; /* If this is not NULL, use arg below. */
+ int inbound; /* True if this is used for reading from gpg. */
+ int dup_to;
+ int print_fd; /* Print the fd number and not the special form of it. */
+ int *arg_locp; /* Write back the argv idx of this argument when
+ building command line to this location. */
+ char arg[1]; /* Used if data above is not used. */
+};
+
+
+struct fd_data_map_s
+{
+ gpgme_data_t data;
+ int inbound; /* true if this is used for reading from gpg */
+ int dup_to;
+ int fd; /* the fd to use */
+ int peer_fd; /* the other side of the pipe */
+ int arg_loc; /* The index into the argv for translation purposes. */
+ void *tag;
+};
+
+
+typedef gpgme_error_t (*colon_preprocessor_t) (char *line, char **rline);
+
+struct engine_gpg
+{
+ char *file_name;
+
+ char *lc_messages;
+ char *lc_ctype;
+
+ struct arg_and_data_s *arglist;
+ struct arg_and_data_s **argtail;
+
+ struct
+ {
+ int fd[2];
+ int arg_loc;
+ size_t bufsize;
+ char *buffer;
+ size_t readpos;
+ int eof;
+ engine_status_handler_t fnc;
+ void *fnc_value;
+ void *tag;
+ } status;
+
+ /* This is a kludge - see the comment at colon_line_handler. */
+ struct
+ {
+ int fd[2];
+ int arg_loc;
+ size_t bufsize;
+ char *buffer;
+ size_t readpos;
+ int eof;
+ engine_colon_line_handler_t fnc; /* this indicate use of this structrue */
+ void *fnc_value;
+ void *tag;
+ colon_preprocessor_t preprocess_fnc;
+ } colon;
+
+ char **argv;
+ struct fd_data_map_s *fd_data_map;
+
+ /* stuff needed for interactive (command) mode */
+ struct
+ {
+ int used;
+ int fd;
+ void *cb_data;
+ int idx; /* Index in fd_data_map */
+ gpgme_status_code_t code; /* last code */
+ char *keyword; /* what has been requested (malloced) */
+ engine_command_handler_t fnc;
+ void *fnc_value;
+ /* The kludges never end. This is used to couple command handlers
+ with output data in edit key mode. */
+ gpgme_data_t linked_data;
+ int linked_idx;
+ } cmd;
+
+ struct gpgme_io_cbs io_cbs;
+};
+
+typedef struct engine_gpg *engine_gpg_t;
+
+
+static void
+gpg_io_event (void *engine, gpgme_event_io_t type, void *type_data)
+{
+ engine_gpg_t gpg = engine;
+
+ TRACE3 (DEBUG_ENGINE, "gpgme:gpg_io_event", gpg,
+ "event %p, type %d, type_data %p",
+ gpg->io_cbs.event, type, type_data);
+ if (gpg->io_cbs.event)
+ (*gpg->io_cbs.event) (gpg->io_cbs.event_priv, type, type_data);
+}
+
+
+static void
+close_notify_handler (int fd, void *opaque)
+{
+ engine_gpg_t gpg = opaque;
+ assert (fd != -1);
+
+ if (gpg->status.fd[0] == fd)
+ {
+ if (gpg->status.tag)
+ (*gpg->io_cbs.remove) (gpg->status.tag);
+ gpg->status.fd[0] = -1;
+ }
+ else if (gpg->status.fd[1] == fd)
+ gpg->status.fd[1] = -1;
+ else if (gpg->colon.fd[0] == fd)
+ {
+ if (gpg->colon.tag)
+ (*gpg->io_cbs.remove) (gpg->colon.tag);
+ gpg->colon.fd[0] = -1;
+ }
+ else if (gpg->colon.fd[1] == fd)
+ gpg->colon.fd[1] = -1;
+ else if (gpg->cmd.fd == fd)
+ gpg->cmd.fd = -1;
+ else if (gpg->fd_data_map)
+ {
+ int i;
+
+ for (i = 0; gpg->fd_data_map[i].data; i++)
+ {
+ if (gpg->fd_data_map[i].fd == fd)
+ {
+ if (gpg->fd_data_map[i].tag)
+ (*gpg->io_cbs.remove) (gpg->fd_data_map[i].tag);
+ gpg->fd_data_map[i].fd = -1;
+ break;
+ }
+ if (gpg->fd_data_map[i].peer_fd == fd)
+ {
+ gpg->fd_data_map[i].peer_fd = -1;
+ break;
+ }
+ }
+ }
+}
+
+/* If FRONT is true, push at the front of the list. Use this for
+ options added late in the process. */
+static gpgme_error_t
+_add_arg (engine_gpg_t gpg, const char *arg, int front, int *arg_locp)
+{
+ struct arg_and_data_s *a;
+
+ assert (gpg);
+ assert (arg);
+
+ a = malloc (sizeof *a + strlen (arg));
+ if (!a)
+ return gpg_error_from_errno (errno);
+
+ a->data = NULL;
+ a->dup_to = -1;
+ a->arg_locp = arg_locp;
+
+ strcpy (a->arg, arg);
+ if (front)
+ {
+ a->next = gpg->arglist;
+ if (!gpg->arglist)
+ {
+ /* If this is the first argument, we need to update the tail
+ pointer. */
+ gpg->argtail = &a->next;
+ }
+ gpg->arglist = a;
+ }
+ else
+ {
+ a->next = NULL;
+ *gpg->argtail = a;
+ gpg->argtail = &a->next;
+ }
+
+ return 0;
+}
+
+static gpgme_error_t
+add_arg_ext (engine_gpg_t gpg, const char *arg, int front)
+{
+ return _add_arg (gpg, arg, front, NULL);
+}
+
+
+static gpgme_error_t
+add_arg_with_locp (engine_gpg_t gpg, const char *arg, int *locp)
+{
+ return _add_arg (gpg, arg, 0, locp);
+}
+
+
+static gpgme_error_t
+add_arg (engine_gpg_t gpg, const char *arg)
+{
+ return add_arg_ext (gpg, arg, 0);
+}
+
+
+static gpgme_error_t
+add_data (engine_gpg_t gpg, gpgme_data_t data, int dup_to, int inbound)
+{
+ struct arg_and_data_s *a;
+
+ assert (gpg);
+ assert (data);
+
+ a = malloc (sizeof *a - 1);
+ if (!a)
+ return gpg_error_from_errno (errno);
+ a->next = NULL;
+ a->data = data;
+ a->inbound = inbound;
+ a->arg_locp = NULL;
+
+ if (dup_to == -2)
+ {
+ a->print_fd = 1;
+ a->dup_to = -1;
+ }
+ else
+ {
+ a->print_fd = 0;
+ a->dup_to = dup_to;
+ }
+ *gpg->argtail = a;
+ gpg->argtail = &a->next;
+ return 0;
+}
+
+
+static char *
+gpg_get_version (const char *file_name)
+{
+ return _gpgme_get_program_version (file_name ? file_name
+ : _gpgme_get_gpg_path ());
+}
+
+
+static const char *
+gpg_get_req_version (void)
+{
+ return NEED_GPG_VERSION;
+}
+
+
+static void
+free_argv (char **argv)
+{
+ int i;
+
+ for (i = 0; argv[i]; i++)
+ free (argv[i]);
+ free (argv);
+}
+
+
+static void
+free_fd_data_map (struct fd_data_map_s *fd_data_map)
+{
+ int i;
+
+ if (!fd_data_map)
+ return;
+
+ for (i = 0; fd_data_map[i].data; i++)
+ {
+ if (fd_data_map[i].fd != -1)
+ _gpgme_io_close (fd_data_map[i].fd);
+ if (fd_data_map[i].peer_fd != -1)
+ _gpgme_io_close (fd_data_map[i].peer_fd);
+ /* Don't release data because this is only a reference. */
+ }
+ free (fd_data_map);
+}
+
+
+static gpgme_error_t
+gpg_cancel (void *engine)
+{
+ engine_gpg_t gpg = engine;
+
+ if (!gpg)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ /* If gpg may be waiting for a cmd, close the cmd fd first. On
+ Windows, close operations block on the reader/writer thread. */
+ if (gpg->cmd.used)
+ {
+ if (gpg->cmd.fd != -1)
+ _gpgme_io_close (gpg->cmd.fd);
+ else if (gpg->fd_data_map
+ && gpg->fd_data_map[gpg->cmd.idx].fd != -1)
+ _gpgme_io_close (gpg->fd_data_map[gpg->cmd.idx].fd);
+ }
+
+ if (gpg->status.fd[0] != -1)
+ _gpgme_io_close (gpg->status.fd[0]);
+ if (gpg->status.fd[1] != -1)
+ _gpgme_io_close (gpg->status.fd[1]);
+ if (gpg->colon.fd[0] != -1)
+ _gpgme_io_close (gpg->colon.fd[0]);
+ if (gpg->colon.fd[1] != -1)
+ _gpgme_io_close (gpg->colon.fd[1]);
+ if (gpg->fd_data_map)
+ {
+ free_fd_data_map (gpg->fd_data_map);
+ gpg->fd_data_map = NULL;
+ }
+
+ return 0;
+}
+
+static void
+gpg_release (void *engine)
+{
+ engine_gpg_t gpg = engine;
+
+ if (!gpg)
+ return;
+
+ gpg_cancel (engine);
+
+ if (gpg->file_name)
+ free (gpg->file_name);
+
+ if (gpg->lc_messages)
+ free (gpg->lc_messages);
+ if (gpg->lc_ctype)
+ free (gpg->lc_ctype);
+
+ while (gpg->arglist)
+ {
+ struct arg_and_data_s *next = gpg->arglist->next;
+
+ if (gpg->arglist)
+ free (gpg->arglist);
+ gpg->arglist = next;
+ }
+
+ if (gpg->status.buffer)
+ free (gpg->status.buffer);
+ if (gpg->colon.buffer)
+ free (gpg->colon.buffer);
+ if (gpg->argv)
+ free_argv (gpg->argv);
+ if (gpg->cmd.keyword)
+ free (gpg->cmd.keyword);
+
+ free (gpg);
+}
+
+
+static gpgme_error_t
+gpg_new (void **engine, const char *file_name, const char *home_dir)
+{
+ engine_gpg_t gpg;
+ gpgme_error_t rc = 0;
+ char *dft_display = NULL;
+ char dft_ttyname[64];
+ char *dft_ttytype = NULL;
+
+ gpg = calloc (1, sizeof *gpg);
+ if (!gpg)
+ return gpg_error_from_errno (errno);
+
+ if (file_name)
+ {
+ gpg->file_name = strdup (file_name);
+ if (!gpg->file_name)
+ {
+ rc = gpg_error_from_errno (errno);
+ goto leave;
+ }
+ }
+
+ gpg->argtail = &gpg->arglist;
+ gpg->status.fd[0] = -1;
+ gpg->status.fd[1] = -1;
+ gpg->colon.fd[0] = -1;
+ gpg->colon.fd[1] = -1;
+ gpg->cmd.fd = -1;
+ gpg->cmd.idx = -1;
+ gpg->cmd.linked_data = NULL;
+ gpg->cmd.linked_idx = -1;
+
+ /* Allocate the read buffer for the status pipe. */
+ gpg->status.bufsize = 1024;
+ gpg->status.readpos = 0;
+ gpg->status.buffer = malloc (gpg->status.bufsize);
+ if (!gpg->status.buffer)
+ {
+ rc = gpg_error_from_errno (errno);
+ goto leave;
+ }
+ /* In any case we need a status pipe - create it right here and
+ don't handle it with our generic gpgme_data_t mechanism. */
+ if (_gpgme_io_pipe (gpg->status.fd, 1) == -1)
+ {
+ rc = gpg_error_from_errno (errno);
+ goto leave;
+ }
+ if (_gpgme_io_set_close_notify (gpg->status.fd[0],
+ close_notify_handler, gpg)
+ || _gpgme_io_set_close_notify (gpg->status.fd[1],
+ close_notify_handler, gpg))
+ {
+ rc = gpg_error (GPG_ERR_GENERAL);
+ goto leave;
+ }
+ gpg->status.eof = 0;
+
+ if (home_dir)
+ {
+ rc = add_arg (gpg, "--homedir");
+ if (!rc)
+ rc = add_arg (gpg, home_dir);
+ if (rc)
+ goto leave;
+ }
+
+ rc = add_arg (gpg, "--status-fd");
+ if (rc)
+ goto leave;
+
+ {
+ char buf[25];
+ _gpgme_io_fd2str (buf, sizeof (buf), gpg->status.fd[1]);
+ rc = add_arg_with_locp (gpg, buf, &gpg->status.arg_loc);
+ if (rc)
+ goto leave;
+ }
+
+ rc = add_arg (gpg, "--no-tty");
+ if (!rc)
+ rc = add_arg (gpg, "--charset");
+ if (!rc)
+ rc = add_arg (gpg, "utf8");
+ if (!rc)
+ rc = add_arg (gpg, "--enable-progress-filter");
+ if (rc)
+ goto leave;
+
+ rc = _gpgme_getenv ("DISPLAY", &dft_display);
+ if (rc)
+ goto leave;
+ if (dft_display)
+ {
+ rc = add_arg (gpg, "--display");
+ if (!rc)
+ rc = add_arg (gpg, dft_display);
+
+ free (dft_display);
+ }
+
+ if (isatty (1))
+ {
+ int err;
+
+ err = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname));
+ if (err)
+ rc = gpg_error_from_errno (err);
+ else
+ {
+ if (*dft_ttyname)
+ {
+ rc = add_arg (gpg, "--ttyname");
+ if (!rc)
+ rc = add_arg (gpg, dft_ttyname);
+ }
+ else
+ rc = 0;
+ if (!rc)
+ {
+ rc = _gpgme_getenv ("TERM", &dft_ttytype);
+ if (rc)
+ goto leave;
+
+ if (dft_ttytype)
+ {
+ rc = add_arg (gpg, "--ttytype");
+ if (!rc)
+ rc = add_arg (gpg, dft_ttytype);
+ }
+
+ free (dft_ttytype);
+ }
+ }
+ if (rc)
+ goto leave;
+ }
+
+ leave:
+ if (rc)
+ gpg_release (gpg);
+ else
+ *engine = gpg;
+ return rc;
+}
+
+
+static gpgme_error_t
+gpg_set_locale (void *engine, int category, const char *value)
+{
+ engine_gpg_t gpg = engine;
+
+ if (0)
+ ;
+#ifdef LC_CTYPE
+ else if (category == LC_CTYPE)
+ {
+ if (gpg->lc_ctype)
+ {
+ free (gpg->lc_ctype);
+ gpg->lc_ctype = NULL;
+ }
+ if (value)
+ {
+ gpg->lc_ctype = strdup (value);
+ if (!gpg->lc_ctype)
+ return gpg_error_from_syserror ();
+ }
+ }
+#endif
+#ifdef LC_MESSAGES
+ else if (category == LC_MESSAGES)
+ {
+ if (gpg->lc_messages)
+ {
+ free (gpg->lc_messages);
+ gpg->lc_messages = NULL;
+ }
+ if (value)
+ {
+ gpg->lc_messages = strdup (value);
+ if (!gpg->lc_messages)
+ return gpg_error_from_syserror ();
+ }
+ }
+#endif /* LC_MESSAGES */
+ else
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ return 0;
+}
+
+
+/* Note, that the status_handler is allowed to modifiy the args
+ value. */
+static void
+gpg_set_status_handler (void *engine, engine_status_handler_t fnc,
+ void *fnc_value)
+{
+ engine_gpg_t gpg = engine;
+
+ gpg->status.fnc = fnc;
+ gpg->status.fnc_value = fnc_value;
+}
+
+/* Kludge to process --with-colon output. */
+static gpgme_error_t
+gpg_set_colon_line_handler (void *engine, engine_colon_line_handler_t fnc,
+ void *fnc_value)
+{
+ engine_gpg_t gpg = engine;
+
+ gpg->colon.bufsize = 1024;
+ gpg->colon.readpos = 0;
+ gpg->colon.buffer = malloc (gpg->colon.bufsize);
+ if (!gpg->colon.buffer)
+ return gpg_error_from_errno (errno);
+
+ if (_gpgme_io_pipe (gpg->colon.fd, 1) == -1)
+ {
+ int saved_errno = errno;
+ free (gpg->colon.buffer);
+ gpg->colon.buffer = NULL;
+ return gpg_error_from_errno (saved_errno);
+ }
+ if (_gpgme_io_set_close_notify (gpg->colon.fd[0], close_notify_handler, gpg)
+ || _gpgme_io_set_close_notify (gpg->colon.fd[1],
+ close_notify_handler, gpg))
+ return gpg_error (GPG_ERR_GENERAL);
+ gpg->colon.eof = 0;
+ gpg->colon.fnc = fnc;
+ gpg->colon.fnc_value = fnc_value;
+ return 0;
+}
+
+
+static gpgme_error_t
+command_handler (void *opaque, int fd)
+{
+ struct io_cb_data *data = (struct io_cb_data *) opaque;
+ engine_gpg_t gpg = (engine_gpg_t) data->handler_value;
+ gpgme_error_t err;
+ int processed = 0;
+ assert (gpg->cmd.used);
+ assert (gpg->cmd.code);
+ assert (gpg->cmd.fnc);
+
+ err = gpg->cmd.fnc (gpg->cmd.fnc_value, gpg->cmd.code, gpg->cmd.keyword, fd,
+ &processed);
+
+ gpg->cmd.code = 0;
+ /* And sleep again until read_status will wake us up again. */
+ /* XXX We must check if there are any more fds active after removing
+ this one. */
+ (*gpg->io_cbs.remove) (gpg->fd_data_map[gpg->cmd.idx].tag);
+ gpg->cmd.fd = gpg->fd_data_map[gpg->cmd.idx].fd;
+ gpg->fd_data_map[gpg->cmd.idx].fd = -1;
+
+ if (err)
+ return err;
+
+ /* We always need to send at least a newline character. */
+ if (!processed)
+ _gpgme_io_write (fd, "\n", 1);
+
+ return 0;
+}
+
+
+
+/* The Fnc will be called to get a value for one of the commands with
+ a key KEY. If the Code passed to FNC is 0, the function may release
+ resources associated with the returned value from another call. To
+ match such a second call to a first call, the returned value from
+ the first call is passed as keyword. */
+static gpgme_error_t
+gpg_set_command_handler (void *engine, engine_command_handler_t fnc,
+ void *fnc_value, gpgme_data_t linked_data)
+{
+ engine_gpg_t gpg = engine;
+ gpgme_error_t rc;
+
+ rc = add_arg (gpg, "--command-fd");
+ if (rc)
+ return rc;
+
+ /* This is a hack. We don't have a real data object. The only
+ thing that matters is that we use something unique, so we use the
+ address of the cmd structure in the gpg object. */
+ rc = add_data (gpg, (void *) &gpg->cmd, -2, 0);
+ if (rc)
+ return rc;
+
+ gpg->cmd.fnc = fnc;
+ gpg->cmd.cb_data = (void *) &gpg->cmd;
+ gpg->cmd.fnc_value = fnc_value;
+ gpg->cmd.linked_data = linked_data;
+ gpg->cmd.used = 1;
+ return 0;
+}
+
+
+static gpgme_error_t
+build_argv (engine_gpg_t gpg)
+{
+ gpgme_error_t err;
+ struct arg_and_data_s *a;
+ struct fd_data_map_s *fd_data_map;
+ size_t datac=0, argc=0;
+ char **argv;
+ int need_special = 0;
+ int use_agent = 0;
+ char *p;
+
+ /* We don't want to use the agent with a malformed environment
+ variable. This is only a very basic test but sufficient to make
+ our life in the regression tests easier. */
+ err = _gpgme_getenv ("GPG_AGENT_INFO", &p);
+ if (err)
+ return err;
+ use_agent = (p && strchr (p, ':'));
+ if (p)
+ free (p);
+
+ if (gpg->argv)
+ {
+ free_argv (gpg->argv);
+ gpg->argv = NULL;
+ }
+ if (gpg->fd_data_map)
+ {
+ free_fd_data_map (gpg->fd_data_map);
+ gpg->fd_data_map = NULL;
+ }
+
+ argc++; /* For argv[0]. */
+ for (a = gpg->arglist; a; a = a->next)
+ {
+ argc++;
+ if (a->data)
+ {
+ /*fprintf (stderr, "build_argv: data\n" );*/
+ datac++;
+ if (a->dup_to == -1 && !a->print_fd)
+ need_special = 1;
+ }
+ else
+ {
+ /* fprintf (stderr, "build_argv: arg=`%s'\n", a->arg );*/
+ }
+ }
+ if (need_special)
+ argc++;
+ if (use_agent)
+ argc++;
+ if (!gpg->cmd.used)
+ argc++; /* --batch */
+ argc += 1; /* --no-sk-comment */
+
+ argv = calloc (argc + 1, sizeof *argv);
+ if (!argv)
+ return gpg_error_from_errno (errno);
+ fd_data_map = calloc (datac + 1, sizeof *fd_data_map);
+ if (!fd_data_map)
+ {
+ int saved_errno = errno;
+ free_argv (argv);
+ return gpg_error_from_errno (saved_errno);
+ }
+
+ argc = datac = 0;
+ argv[argc] = strdup ("gpg"); /* argv[0] */
+ if (!argv[argc])
+ {
+ int saved_errno = errno;
+ free (fd_data_map);
+ free_argv (argv);
+ return gpg_error_from_errno (saved_errno);
+ }
+ argc++;
+ if (need_special)
+ {
+ argv[argc] = strdup ("--enable-special-filenames");
+ if (!argv[argc])
+ {
+ int saved_errno = errno;
+ free (fd_data_map);
+ free_argv (argv);
+ return gpg_error_from_errno (saved_errno);
+ }
+ argc++;
+ }
+ if (use_agent)
+ {
+ argv[argc] = strdup ("--use-agent");
+ if (!argv[argc])
+ {
+ int saved_errno = errno;
+ free (fd_data_map);
+ free_argv (argv);
+ return gpg_error_from_errno (saved_errno);
+ }
+ argc++;
+ }
+ if (!gpg->cmd.used)
+ {
+ argv[argc] = strdup ("--batch");
+ if (!argv[argc])
+ {
+ int saved_errno = errno;
+ free (fd_data_map);
+ free_argv (argv);
+ return gpg_error_from_errno (saved_errno);
+ }
+ argc++;
+ }
+ argv[argc] = strdup ("--no-sk-comment");
+ if (!argv[argc])
+ {
+ int saved_errno = errno;
+ free (fd_data_map);
+ free_argv (argv);
+ return gpg_error_from_errno (saved_errno);
+ }
+ argc++;
+ for (a = gpg->arglist; a; a = a->next)
+ {
+ if (a->arg_locp)
+ *(a->arg_locp) = argc;
+
+ if (a->data)
+ {
+ /* Create a pipe to pass it down to gpg. */
+ fd_data_map[datac].inbound = a->inbound;
+
+ /* Create a pipe. */
+ {
+ int fds[2];
+
+ if (_gpgme_io_pipe (fds, fd_data_map[datac].inbound ? 1 : 0)
+ == -1)
+ {
+ int saved_errno = errno;
+ free (fd_data_map);
+ free_argv (argv);
+ return gpg_error (saved_errno);
+ }
+ if (_gpgme_io_set_close_notify (fds[0],
+ close_notify_handler, gpg)
+ || _gpgme_io_set_close_notify (fds[1],
+ close_notify_handler,
+ gpg))
+ {
+ return gpg_error (GPG_ERR_GENERAL);
+ }
+ /* If the data_type is FD, we have to do a dup2 here. */
+ if (fd_data_map[datac].inbound)
+ {
+ fd_data_map[datac].fd = fds[0];
+ fd_data_map[datac].peer_fd = fds[1];
+ }
+ else
+ {
+ fd_data_map[datac].fd = fds[1];
+ fd_data_map[datac].peer_fd = fds[0];
+ }
+ }
+
+ /* Hack to get hands on the fd later. */
+ if (gpg->cmd.used)
+ {
+ if (gpg->cmd.cb_data == a->data)
+ {
+ assert (gpg->cmd.idx == -1);
+ gpg->cmd.idx = datac;
+ }
+ else if (gpg->cmd.linked_data == a->data)
+ {
+ assert (gpg->cmd.linked_idx == -1);
+ gpg->cmd.linked_idx = datac;
+ }
+ }
+
+ fd_data_map[datac].data = a->data;
+ fd_data_map[datac].dup_to = a->dup_to;
+
+ if (a->dup_to == -1)
+ {
+ char *ptr;
+ int buflen = 25;
+
+ argv[argc] = malloc (buflen);
+ if (!argv[argc])
+ {
+ int saved_errno = errno;
+ free (fd_data_map);
+ free_argv (argv);
+ return gpg_error_from_errno (saved_errno);
+ }
+
+ ptr = argv[argc];
+ if (!a->print_fd)
+ {
+ *(ptr++) = '-';
+ *(ptr++) = '&';
+ buflen -= 2;
+ }
+
+ _gpgme_io_fd2str (ptr, buflen, fd_data_map[datac].peer_fd);
+ fd_data_map[datac].arg_loc = argc;
+ argc++;
+ }
+ datac++;
+ }
+ else
+ {
+ argv[argc] = strdup (a->arg);
+ if (!argv[argc])
+ {
+ int saved_errno = errno;
+ free (fd_data_map);
+ free_argv (argv);
+ return gpg_error_from_errno (saved_errno);
+ }
+ argc++;
+ }
+ }
+
+ gpg->argv = argv;
+ gpg->fd_data_map = fd_data_map;
+ return 0;
+}
+
+
+static gpgme_error_t
+add_io_cb (engine_gpg_t gpg, int fd, int dir, gpgme_io_cb_t handler, void *data,
+ void **tag)
+{
+ gpgme_error_t err;
+
+ err = (*gpg->io_cbs.add) (gpg->io_cbs.add_priv, fd, dir, handler, data, tag);
+ if (err)
+ return err;
+ if (!dir)
+ /* FIXME Kludge around poll() problem. */
+ err = _gpgme_io_set_nonblocking (fd);
+ return err;
+}
+
+
+/* Handle the status output of GnuPG. This function does read entire
+ lines and passes them as C strings to the callback function (we can
+ use C Strings because the status output is always UTF-8 encoded).
+ Of course we have to buffer the lines to cope with long lines
+ e.g. with a large user ID. Note: We can optimize this to only cope
+ with status line code we know about and skip all other stuff
+ without buffering (i.e. without extending the buffer). */
+static gpgme_error_t
+read_status (engine_gpg_t gpg)
+{
+ char *p;
+ int nread;
+ size_t bufsize = gpg->status.bufsize;
+ char *buffer = gpg->status.buffer;
+ size_t readpos = gpg->status.readpos;
+
+ assert (buffer);
+ if (bufsize - readpos < 256)
+ {
+ /* Need more room for the read. */
+ bufsize += 1024;
+ buffer = realloc (buffer, bufsize);
+ if (!buffer)
+ return gpg_error_from_errno (errno);
+ }
+
+ nread = _gpgme_io_read (gpg->status.fd[0],
+ buffer + readpos, bufsize-readpos);
+ if (nread == -1)
+ return gpg_error_from_errno (errno);
+
+ if (!nread)
+ {
+ gpg->status.eof = 1;
+ if (gpg->status.fnc)
+ {
+ gpgme_error_t err;
+ err = gpg->status.fnc (gpg->status.fnc_value, GPGME_STATUS_EOF, "");
+ if (err)
+ return err;
+ }
+ return 0;
+ }
+
+ while (nread > 0)
+ {
+ for (p = buffer + readpos; nread; nread--, p++)
+ {
+ if (*p == '\n')
+ {
+ /* (we require that the last line is terminated by a LF) */
+ if (p > buffer && p[-1] == '\r')
+ p[-1] = 0;
+ *p = 0;
+ if (!strncmp (buffer, "[GNUPG:] ", 9)
+ && buffer[9] >= 'A' && buffer[9] <= 'Z')
+ {
+ char *rest;
+ gpgme_status_code_t r;
+
+ rest = strchr (buffer + 9, ' ');
+ if (!rest)
+ rest = p; /* Set to an empty string. */
+ else
+ *rest++ = 0;
+
+ r = _gpgme_parse_status (buffer + 9);
+ if (r >= 0)
+ {
+ if (gpg->cmd.used
+ && (r == GPGME_STATUS_GET_BOOL
+ || r == GPGME_STATUS_GET_LINE
+ || r == GPGME_STATUS_GET_HIDDEN))
+ {
+ gpg->cmd.code = r;
+ if (gpg->cmd.keyword)
+ free (gpg->cmd.keyword);
+ gpg->cmd.keyword = strdup (rest);
+ if (!gpg->cmd.keyword)
+ return gpg_error_from_errno (errno);
+ /* This should be the last thing we have
+ received and the next thing will be that
+ the command handler does its action. */
+ if (nread > 1)
+ TRACE0 (DEBUG_CTX, "gpgme:read_status", 0,
+ "error: unexpected data");
+
+ add_io_cb (gpg, gpg->cmd.fd, 0,
+ command_handler, gpg,
+ &gpg->fd_data_map[gpg->cmd.idx].tag);
+ gpg->fd_data_map[gpg->cmd.idx].fd = gpg->cmd.fd;
+ gpg->cmd.fd = -1;
+ }
+ else if (gpg->status.fnc)
+ {
+ gpgme_error_t err;
+ err = gpg->status.fnc (gpg->status.fnc_value,
+ r, rest);
+ if (err)
+ return err;
+ }
+
+ if (r == GPGME_STATUS_END_STREAM)
+ {
+ if (gpg->cmd.used)
+ {
+ /* Before we can actually add the
+ command fd, we might have to flush
+ the linked output data pipe. */
+ if (gpg->cmd.linked_idx != -1
+ && gpg->fd_data_map[gpg->cmd.linked_idx].fd
+ != -1)
+ {
+ struct io_select_fd_s fds;
+ fds.fd =
+ gpg->fd_data_map[gpg->cmd.linked_idx].fd;
+ fds.for_read = 1;
+ fds.for_write = 0;
+ fds.opaque = NULL;
+ do
+ {
+ fds.signaled = 0;
+ _gpgme_io_select (&fds, 1, 1);
+ if (fds.signaled)
+ _gpgme_data_inbound_handler
+ (gpg->cmd.linked_data, fds.fd);
+ }
+ while (fds.signaled);
+ }
+
+ /* XXX We must check if there are any
+ more fds active after removing this
+ one. */
+ (*gpg->io_cbs.remove)
+ (gpg->fd_data_map[gpg->cmd.idx].tag);
+ gpg->cmd.fd = gpg->fd_data_map[gpg->cmd.idx].fd;
+ gpg->fd_data_map[gpg->cmd.idx].fd = -1;
+ }
+ }
+ }
+ }
+ /* To reuse the buffer for the next line we have to
+ shift the remaining data to the buffer start and
+ restart the loop Hmmm: We can optimize this function
+ by looking forward in the buffer to see whether a
+ second complete line is available and in this case
+ avoid the memmove for this line. */
+ nread--; p++;
+ if (nread)
+ memmove (buffer, p, nread);
+ readpos = 0;
+ break; /* the for loop */
+ }
+ else
+ readpos++;
+ }
+ }
+
+ /* Update the gpg object. */
+ gpg->status.bufsize = bufsize;
+ gpg->status.buffer = buffer;
+ gpg->status.readpos = readpos;
+ return 0;
+}
+
+
+static gpgme_error_t
+status_handler (void *opaque, int fd)
+{
+ struct io_cb_data *data = (struct io_cb_data *) opaque;
+ engine_gpg_t gpg = (engine_gpg_t) data->handler_value;
+ int err;
+
+ assert (fd == gpg->status.fd[0]);
+ err = read_status (gpg);
+ if (err)
+ return err;
+ if (gpg->status.eof)
+ _gpgme_io_close (fd);
+ return 0;
+}
+
+
+static gpgme_error_t
+read_colon_line (engine_gpg_t gpg)
+{
+ char *p;
+ int nread;
+ size_t bufsize = gpg->colon.bufsize;
+ char *buffer = gpg->colon.buffer;
+ size_t readpos = gpg->colon.readpos;
+
+ assert (buffer);
+ if (bufsize - readpos < 256)
+ {
+ /* Need more room for the read. */
+ bufsize += 1024;
+ buffer = realloc (buffer, bufsize);
+ if (!buffer)
+ return gpg_error_from_errno (errno);
+ }
+
+ nread = _gpgme_io_read (gpg->colon.fd[0], buffer+readpos, bufsize-readpos);
+ if (nread == -1)
+ return gpg_error_from_errno (errno);
+
+ if (!nread)
+ {
+ gpg->colon.eof = 1;
+ assert (gpg->colon.fnc);
+ gpg->colon.fnc (gpg->colon.fnc_value, NULL);
+ return 0;
+ }
+
+ while (nread > 0)
+ {
+ for (p = buffer + readpos; nread; nread--, p++)
+ {
+ if ( *p == '\n' )
+ {
+ /* (we require that the last line is terminated by a LF)
+ and we skip empty lines. Note: we use UTF8 encoding
+ and escaping of special characters. We require at
+ least one colon to cope with some other printed
+ information. */
+ *p = 0;
+ if (*buffer && strchr (buffer, ':'))
+ {
+ char *line = NULL;
+
+ if (gpg->colon.preprocess_fnc)
+ {
+ gpgme_error_t err;
+
+ err = gpg->colon.preprocess_fnc (buffer, &line);
+ if (err)
+ return err;
+ }
+
+ assert (gpg->colon.fnc);
+ gpg->colon.fnc (gpg->colon.fnc_value, line ? line : buffer);
+ if (line)
+ free (line);
+ }
+
+ /* To reuse the buffer for the next line we have to
+ shift the remaining data to the buffer start and
+ restart the loop Hmmm: We can optimize this function
+ by looking forward in the buffer to see whether a
+ second complete line is available and in this case
+ avoid the memmove for this line. */
+ nread--; p++;
+ if (nread)
+ memmove (buffer, p, nread);
+ readpos = 0;
+ break; /* The for loop. */
+ }
+ else
+ readpos++;
+ }
+ }
+
+ /* Update the gpg object. */
+ gpg->colon.bufsize = bufsize;
+ gpg->colon.buffer = buffer;
+ gpg->colon.readpos = readpos;
+ return 0;
+}
+
+
+/* This colonline handler thing is not the clean way to do it. It
+ might be better to enhance the gpgme_data_t object to act as a wrapper
+ for a callback. Same goes for the status thing. For now we use
+ this thing here because it is easier to implement. */
+static gpgme_error_t
+colon_line_handler (void *opaque, int fd)
+{
+ struct io_cb_data *data = (struct io_cb_data *) opaque;
+ engine_gpg_t gpg = (engine_gpg_t) data->handler_value;
+ gpgme_error_t rc = 0;
+
+ assert (fd == gpg->colon.fd[0]);
+ rc = read_colon_line (gpg);
+ if (rc)
+ return rc;
+ if (gpg->colon.eof)
+ _gpgme_io_close (fd);
+ return 0;
+}
+
+
+static gpgme_error_t
+start (engine_gpg_t gpg)
+{
+ gpgme_error_t rc;
+ int saved_errno;
+ int i, n;
+ int status;
+ struct spawn_fd_item_s *fd_list;
+ pid_t pid;
+
+ if (!gpg)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if (!gpg->file_name && !_gpgme_get_gpg_path ())
+ return gpg_error (GPG_ERR_INV_ENGINE);
+
+ if (gpg->lc_ctype)
+ {
+ rc = add_arg_ext (gpg, gpg->lc_ctype, 1);
+ if (!rc)
+ rc = add_arg_ext (gpg, "--lc-ctype", 1);
+ if (rc)
+ return rc;
+ }
+
+ if (gpg->lc_messages)
+ {
+ rc = add_arg_ext (gpg, gpg->lc_messages, 1);
+ if (!rc)
+ rc = add_arg_ext (gpg, "--lc-messages", 1);
+ if (rc)
+ return rc;
+ }
+
+ rc = build_argv (gpg);
+ if (rc)
+ return rc;
+
+ /* status_fd, colon_fd and end of list. */
+ n = 3;
+ for (i = 0; gpg->fd_data_map[i].data; i++)
+ n++;
+ fd_list = calloc (n, sizeof *fd_list);
+ if (! fd_list)
+ return gpg_error_from_errno (errno);
+
+ /* Build the fd list for the child. */
+ n = 0;
+ fd_list[n].fd = gpg->status.fd[1];
+ fd_list[n].dup_to = -1;
+ fd_list[n].arg_loc = gpg->status.arg_loc;
+ n++;
+ if (gpg->colon.fnc)
+ {
+ fd_list[n].fd = gpg->colon.fd[1];
+ fd_list[n].dup_to = 1;
+ n++;
+ }
+ for (i = 0; gpg->fd_data_map[i].data; i++)
+ {
+ fd_list[n].fd = gpg->fd_data_map[i].peer_fd;
+ fd_list[n].dup_to = gpg->fd_data_map[i].dup_to;
+ fd_list[n].arg_loc = gpg->fd_data_map[i].arg_loc;
+ n++;
+ }
+ fd_list[n].fd = -1;
+ fd_list[n].dup_to = -1;
+
+ status = _gpgme_io_spawn (gpg->file_name ? gpg->file_name :
+ _gpgme_get_gpg_path (), gpg->argv,
+ IOSPAWN_FLAG_ALLOW_SET_FG,
+ fd_list, NULL, NULL, &pid);
+ saved_errno = errno;
+
+ free (fd_list);
+ if (status == -1)
+ return gpg_error_from_errno (saved_errno);
+
+ /*_gpgme_register_term_handler ( closure, closure_value, pid );*/
+
+ rc = add_io_cb (gpg, gpg->status.fd[0], 1, status_handler, gpg,
+ &gpg->status.tag);
+ if (rc)
+ /* FIXME: kill the child */
+ return rc;
+
+ if (gpg->colon.fnc)
+ {
+ assert (gpg->colon.fd[0] != -1);
+ rc = add_io_cb (gpg, gpg->colon.fd[0], 1, colon_line_handler, gpg,
+ &gpg->colon.tag);
+ if (rc)
+ /* FIXME: kill the child */
+ return rc;
+ }
+
+ for (i = 0; gpg->fd_data_map[i].data; i++)
+ {
+ if (gpg->cmd.used && i == gpg->cmd.idx)
+ {
+ /* Park the cmd fd. */
+ gpg->cmd.fd = gpg->fd_data_map[i].fd;
+ gpg->fd_data_map[i].fd = -1;
+ }
+ else
+ {
+ rc = add_io_cb (gpg, gpg->fd_data_map[i].fd,
+ gpg->fd_data_map[i].inbound,
+ gpg->fd_data_map[i].inbound
+ ? _gpgme_data_inbound_handler
+ : _gpgme_data_outbound_handler,
+ gpg->fd_data_map[i].data, &gpg->fd_data_map[i].tag);
+
+ if (rc)
+ /* FIXME: kill the child */
+ return rc;
+ }
+ }
+
+ gpg_io_event (gpg, GPGME_EVENT_START, NULL);
+
+ /* fixme: check what data we can release here */
+ return 0;
+}
+
+
+static gpgme_error_t
+gpg_decrypt (void *engine, gpgme_data_t ciph, gpgme_data_t plain)
+{
+ engine_gpg_t gpg = engine;
+ gpgme_error_t err;
+
+ err = add_arg (gpg, "--decrypt");
+
+ /* Tell the gpg object about the data. */
+ if (!err)
+ err = add_arg (gpg, "--output");
+ if (!err)
+ err = add_arg (gpg, "-");
+ if (!err)
+ err = add_data (gpg, plain, 1, 1);
+ if (!err)
+ err = add_arg (gpg, "--");
+ if (!err)
+ err = add_data (gpg, ciph, -1, 0);
+
+ if (!err)
+ start (gpg);
+ return err;
+}
+
+static gpgme_error_t
+gpg_delete (void *engine, gpgme_key_t key, int allow_secret)
+{
+ engine_gpg_t gpg = engine;
+ gpgme_error_t err;
+
+ err = add_arg (gpg, allow_secret ? "--delete-secret-and-public-key"
+ : "--delete-key");
+ if (!err)
+ err = add_arg (gpg, "--");
+ if (!err)
+ {
+ if (!key->subkeys || !key->subkeys->fpr)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ else
+ err = add_arg (gpg, key->subkeys->fpr);
+ }
+
+ if (!err)
+ start (gpg);
+ return err;
+}
+
+
+static gpgme_error_t
+gpg_passwd (void *engine, gpgme_key_t key, unsigned int flags)
+{
+ engine_gpg_t gpg = engine;
+ gpgme_error_t err;
+
+ if (!key || !key->subkeys || !key->subkeys->fpr)
+ return gpg_error (GPG_ERR_INV_CERT_OBJ);
+
+ err = add_arg (gpg, "--passwd");
+ if (!err)
+ err = add_arg (gpg, key->subkeys->fpr);
+ if (!err)
+ start (gpg);
+ return err;
+}
+
+
+static gpgme_error_t
+append_args_from_signers (engine_gpg_t gpg, gpgme_ctx_t ctx /* FIXME */)
+{
+ gpgme_error_t err = 0;
+ int i;
+ gpgme_key_t key;
+
+ for (i = 0; (key = gpgme_signers_enum (ctx, i)); i++)
+ {
+ const char *s = key->subkeys ? key->subkeys->keyid : NULL;
+ if (s)
+ {
+ if (!err)
+ err = add_arg (gpg, "-u");
+ if (!err)
+ err = add_arg (gpg, s);
+ }
+ gpgme_key_unref (key);
+ if (err) break;
+ }
+ return err;
+}
+
+
+static gpgme_error_t
+append_args_from_sig_notations (engine_gpg_t gpg, gpgme_ctx_t ctx /* FIXME */)
+{
+ gpgme_error_t err = 0;
+ gpgme_sig_notation_t notation;
+
+ notation = gpgme_sig_notation_get (ctx);
+
+ while (!err && notation)
+ {
+ if (notation->name
+ && !(notation->flags & GPGME_SIG_NOTATION_HUMAN_READABLE))
+ err = gpg_error (GPG_ERR_INV_VALUE);
+ else if (notation->name)
+ {
+ char *arg;
+
+ /* Maximum space needed is one byte for the "critical" flag,
+ the name, one byte for '=', the value, and a terminating
+ '\0'. */
+
+ arg = malloc (1 + notation->name_len + 1 + notation->value_len + 1);
+ if (!arg)
+ err = gpg_error_from_errno (errno);
+
+ if (!err)
+ {
+ char *argp = arg;
+
+ if (notation->critical)
+ *(argp++) = '!';
+
+ memcpy (argp, notation->name, notation->name_len);
+ argp += notation->name_len;
+
+ *(argp++) = '=';
+
+ /* We know that notation->name is '\0' terminated. */
+ strcpy (argp, notation->value);
+ }
+
+ if (!err)
+ err = add_arg (gpg, "--sig-notation");
+ if (!err)
+ err = add_arg (gpg, arg);
+
+ if (arg)
+ free (arg);
+ }
+ else
+ {
+ /* This is a policy URL. */
+
+ char *value;
+
+ if (notation->critical)
+ {
+ value = malloc (1 + notation->value_len + 1);
+ if (!value)
+ err = gpg_error_from_errno (errno);
+ else
+ {
+ value[0] = '!';
+ /* We know that notation->value is '\0' terminated. */
+ strcpy (&value[1], notation->value);
+ }
+ }
+ else
+ value = notation->value;
+
+ if (!err)
+ err = add_arg (gpg, "--sig-policy-url");
+ if (!err)
+ err = add_arg (gpg, value);
+
+ if (value != notation->value)
+ free (value);
+ }
+
+ notation = notation->next;
+ }
+ return err;
+}
+
+
+static gpgme_error_t
+gpg_edit (void *engine, int type, gpgme_key_t key, gpgme_data_t out,
+ gpgme_ctx_t ctx /* FIXME */)
+{
+ engine_gpg_t gpg = engine;
+ gpgme_error_t err;
+
+ err = add_arg (gpg, "--with-colons");
+ if (!err)
+ err = append_args_from_signers (gpg, ctx);
+ if (!err)
+ err = add_arg (gpg, type == 0 ? "--edit-key" : "--card-edit");
+ if (!err)
+ err = add_data (gpg, out, 1, 1);
+ if (!err)
+ err = add_arg (gpg, "--");
+ if (!err && type == 0)
+ {
+ const char *s = key->subkeys ? key->subkeys->fpr : NULL;
+ if (!s)
+ err = gpg_error (GPG_ERR_INV_VALUE);
+ else
+ err = add_arg (gpg, s);
+ }
+ if (!err)
+ err = start (gpg);
+
+ return err;
+}
+
+
+static gpgme_error_t
+append_args_from_recipients (engine_gpg_t gpg, gpgme_key_t recp[])
+{
+ gpgme_error_t err = 0;
+ int i = 0;
+
+ while (recp[i])
+ {
+ if (!recp[i]->subkeys || !recp[i]->subkeys->fpr)
+ err = gpg_error (GPG_ERR_INV_VALUE);
+ if (!err)
+ err = add_arg (gpg, "-r");
+ if (!err)
+ err = add_arg (gpg, recp[i]->subkeys->fpr);
+ if (err)
+ break;
+ i++;
+ }
+ return err;
+}
+
+
+static gpgme_error_t
+gpg_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags,
+ gpgme_data_t plain, gpgme_data_t ciph, int use_armor)
+{
+ engine_gpg_t gpg = engine;
+ gpgme_error_t err;
+ int symmetric = !recp;
+
+ err = add_arg (gpg, symmetric ? "--symmetric" : "--encrypt");
+
+ if (!err && use_armor)
+ err = add_arg (gpg, "--armor");
+
+ if (!symmetric)
+ {
+ /* If we know that all recipients are valid (full or ultimate trust)
+ we can suppress further checks. */
+ if (!err && (flags & GPGME_ENCRYPT_ALWAYS_TRUST))
+ err = add_arg (gpg, "--always-trust");
+
+ if (!err && (flags & GPGME_ENCRYPT_NO_ENCRYPT_TO))
+ err = add_arg (gpg, "--no-encrypt-to");
+
+ if (!err)
+ err = append_args_from_recipients (gpg, recp);
+ }
+
+ /* Tell the gpg object about the data. */
+ if (!err)
+ err = add_arg (gpg, "--output");
+ if (!err)
+ err = add_arg (gpg, "-");
+ if (!err)
+ err = add_data (gpg, ciph, 1, 1);
+ if (gpgme_data_get_file_name (plain))
+ {
+ if (!err)
+ err = add_arg (gpg, "--set-filename");
+ if (!err)
+ err = add_arg (gpg, gpgme_data_get_file_name (plain));
+ }
+ if (!err)
+ err = add_arg (gpg, "--");
+ if (!err)
+ err = add_data (gpg, plain, -1, 0);
+
+ if (!err)
+ err = start (gpg);
+
+ return err;
+}
+
+
+static gpgme_error_t
+gpg_encrypt_sign (void *engine, gpgme_key_t recp[],
+ gpgme_encrypt_flags_t flags, gpgme_data_t plain,
+ gpgme_data_t ciph, int use_armor,
+ gpgme_ctx_t ctx /* FIXME */)
+{
+ engine_gpg_t gpg = engine;
+ gpgme_error_t err;
+
+ err = add_arg (gpg, "--encrypt");
+ if (!err)
+ err = add_arg (gpg, "--sign");
+ if (!err && use_armor)
+ err = add_arg (gpg, "--armor");
+
+ /* If we know that all recipients are valid (full or ultimate trust)
+ we can suppress further checks. */
+ if (!err && (flags & GPGME_ENCRYPT_ALWAYS_TRUST))
+ err = add_arg (gpg, "--always-trust");
+
+ if (!err)
+ err = append_args_from_recipients (gpg, recp);
+
+ if (!err)
+ err = append_args_from_signers (gpg, ctx);
+ if (!err)
+ err = append_args_from_sig_notations (gpg, ctx);
+
+ /* Tell the gpg object about the data. */
+ if (!err)
+ err = add_arg (gpg, "--output");
+ if (!err)
+ err = add_arg (gpg, "-");
+ if (!err)
+ err = add_data (gpg, ciph, 1, 1);
+ if (gpgme_data_get_file_name (plain))
+ {
+ if (!err)
+ err = add_arg (gpg, "--set-filename");
+ if (!err)
+ err = add_arg (gpg, gpgme_data_get_file_name (plain));
+ }
+ if (!err)
+ err = add_arg (gpg, "--");
+ if (!err)
+ err = add_data (gpg, plain, -1, 0);
+
+ if (!err)
+ err = start (gpg);
+
+ return err;
+}
+
+
+static gpgme_error_t
+export_common (engine_gpg_t gpg, gpgme_export_mode_t mode,
+ gpgme_data_t keydata, int use_armor)
+{
+ gpgme_error_t err = 0;
+
+ if ((mode & ~(GPGME_EXPORT_MODE_EXTERN
+ |GPGME_EXPORT_MODE_MINIMAL)))
+ return gpg_error (GPG_ERR_NOT_SUPPORTED);
+
+ if ((mode & GPGME_EXPORT_MODE_MINIMAL))
+ err = add_arg (gpg, "--export-options=export-minimal");
+
+ if (err)
+ ;
+ else if ((mode & GPGME_EXPORT_MODE_EXTERN))
+ {
+ err = add_arg (gpg, "--send-keys");
+ }
+ else
+ {
+ err = add_arg (gpg, "--export");
+ if (!err && use_armor)
+ err = add_arg (gpg, "--armor");
+ if (!err)
+ err = add_data (gpg, keydata, 1, 1);
+ }
+ if (!err)
+ err = add_arg (gpg, "--");
+
+ return err;
+}
+
+
+static gpgme_error_t
+gpg_export (void *engine, const char *pattern, gpgme_export_mode_t mode,
+ gpgme_data_t keydata, int use_armor)
+{
+ engine_gpg_t gpg = engine;
+ gpgme_error_t err;
+
+ err = export_common (gpg, mode, keydata, use_armor);
+
+ if (!err && pattern && *pattern)
+ err = add_arg (gpg, pattern);
+
+ if (!err)
+ err = start (gpg);
+
+ return err;
+}
+
+
+static gpgme_error_t
+gpg_export_ext (void *engine, const char *pattern[], gpgme_export_mode_t mode,
+ gpgme_data_t keydata, int use_armor)
+{
+ engine_gpg_t gpg = engine;
+ gpgme_error_t err;
+
+ err = export_common (gpg, mode, keydata, use_armor);
+
+ if (pattern)
+ {
+ while (!err && *pattern && **pattern)
+ err = add_arg (gpg, *(pattern++));
+ }
+
+ if (!err)
+ err = start (gpg);
+
+ return err;
+}
+
+
+static gpgme_error_t
+gpg_genkey (void *engine, gpgme_data_t help_data, int use_armor,
+ gpgme_data_t pubkey, gpgme_data_t seckey)
+{
+ engine_gpg_t gpg = engine;
+ gpgme_error_t err;
+
+ if (!gpg)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ /* We need a special mechanism to get the fd of a pipe here, so that
+ we can use this for the %pubring and %secring parameters. We
+ don't have this yet, so we implement only the adding to the
+ standard keyrings. */
+ if (pubkey || seckey)
+ return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+
+ err = add_arg (gpg, "--gen-key");
+ if (!err && use_armor)
+ err = add_arg (gpg, "--armor");
+ if (!err)
+ err = add_arg (gpg, "--");
+ if (!err)
+ err = add_data (gpg, help_data, -1, 0);
+
+ if (!err)
+ err = start (gpg);
+
+ return err;
+}
+
+/* Return the next DELIM delimited string from DATA as a C-string.
+ The caller needs to provide the address of a pointer variable which
+ he has to set to NULL before the first call. After the last call
+ to this function, this function needs to be called once more with
+ DATA set to NULL so that the function can release its internal
+ state. After that the pointer variable is free for use again.
+ Note that we use a delimiter and thus a trailing delimiter is not
+ required. DELIM may not be changed after the first call. */
+static const char *
+string_from_data (gpgme_data_t data, int delim,
+ void **helpptr, gpgme_error_t *r_err)
+{
+#define MYBUFLEN 2000 /* Fixme: We don't support URLs longer than that. */
+ struct {
+ int eof_seen;
+ int nbytes; /* Length of the last returned string including
+ the delimiter. */
+ int buflen; /* Valid length of BUF. */
+ char buf[MYBUFLEN+1]; /* Buffer with one byte extra space. */
+ } *self;
+ char *p;
+ int nread;
+
+ *r_err = 0;
+ if (!data)
+ {
+ if (*helpptr)
+ {
+ free (*helpptr);
+ *helpptr = NULL;
+ }
+ return NULL;
+ }
+
+ if (*helpptr)
+ self = *helpptr;
+ else
+ {
+ self = malloc (sizeof *self);
+ if (!self)
+ {
+ *r_err = gpg_error_from_syserror ();
+ return NULL;
+ }
+ *helpptr = self;
+ self->eof_seen = 0;
+ self->nbytes = 0;
+ self->buflen = 0;
+ }
+
+ if (self->eof_seen)
+ return NULL;
+
+ assert (self->nbytes <= self->buflen);
+ memmove (self->buf, self->buf + self->nbytes, self->buflen - self->nbytes);
+ self->buflen -= self->nbytes;
+ self->nbytes = 0;
+
+ do
+ {
+ /* Fixme: This is fairly infective scanning because we may scan
+ the buffer several times. */
+ p = memchr (self->buf, delim, self->buflen);
+ if (p)
+ {
+ *p = 0;
+ self->nbytes = p - self->buf + 1;
+ return self->buf;
+ }
+
+ if ( !(MYBUFLEN - self->buflen) )
+ {
+ /* Not enough space - URL too long. */
+ *r_err = gpg_error (GPG_ERR_TOO_LARGE);
+ return NULL;
+ }
+
+ nread = gpgme_data_read (data, self->buf + self->buflen,
+ MYBUFLEN - self->buflen);
+ if (nread < 0)
+ {
+ *r_err = gpg_error_from_syserror ();
+ return NULL;
+ }
+ self->buflen += nread;
+ }
+ while (nread);
+
+ /* EOF reached. If we have anything in the buffer, append a Nul and
+ return it. */
+ self->eof_seen = 1;
+ if (self->buflen)
+ {
+ self->buf[self->buflen] = 0; /* (we allocated one extra byte) */
+ return self->buf;
+ }
+ return NULL;
+#undef MYBUFLEN
+}
+
+
+
+static gpgme_error_t
+gpg_import (void *engine, gpgme_data_t keydata, gpgme_key_t *keyarray)
+{
+ engine_gpg_t gpg = engine;
+ gpgme_error_t err;
+ int idx;
+ gpgme_data_encoding_t dataenc;
+
+ if (keydata && keyarray)
+ return gpg_error (GPG_ERR_INV_VALUE); /* Only one is allowed. */
+
+ dataenc = gpgme_data_get_encoding (keydata);
+
+ if (keyarray)
+ {
+ err = add_arg (gpg, "--recv-keys");
+ if (!err)
+ err = add_arg (gpg, "--");
+ for (idx=0; !err && keyarray[idx]; idx++)
+ {
+ if (keyarray[idx]->protocol != GPGME_PROTOCOL_OpenPGP)
+ ;
+ else if (!keyarray[idx]->subkeys)
+ ;
+ else if (keyarray[idx]->subkeys->fpr && *keyarray[idx]->subkeys->fpr)
+ err = add_arg (gpg, keyarray[idx]->subkeys->fpr);
+ else if (*keyarray[idx]->subkeys->keyid)
+ err = add_arg (gpg, keyarray[idx]->subkeys->keyid);
+ }
+ }
+ else if (dataenc == GPGME_DATA_ENCODING_URL
+ || dataenc == GPGME_DATA_ENCODING_URL0)
+ {
+ void *helpptr;
+ const char *string;
+ gpgme_error_t xerr;
+ int delim = (dataenc == GPGME_DATA_ENCODING_URL)? '\n': 0;
+
+ /* FIXME: --fetch-keys is probably not correct because it can't
+ grok all kinds of URLs. On Unix it should just work but on
+ Windows we will build the command line and that may fail for
+ some embedded control characters. It is anyway limited to
+ the maximum size of the command line. We need another
+ command which can take its input from a file. Maybe we
+ should use an option to gpg to modify such commands (ala
+ --multifile). */
+ err = add_arg (gpg, "--fetch-keys");
+ if (!err)
+ err = add_arg (gpg, "--");
+ helpptr = NULL;
+ while (!err
+ && (string = string_from_data (keydata, delim, &helpptr, &xerr)))
+ err = add_arg (gpg, string);
+ if (!err)
+ err = xerr;
+ string_from_data (NULL, delim, &helpptr, &xerr);
+ }
+ else if (dataenc == GPGME_DATA_ENCODING_URLESC)
+ {
+ /* Already escaped URLs are not yet supported. */
+ err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+ }
+ else
+ {
+ err = add_arg (gpg, "--import");
+ if (!err)
+ err = add_arg (gpg, "--");
+ if (!err)
+ err = add_data (gpg, keydata, -1, 0);
+ }
+
+ if (!err)
+ err = start (gpg);
+
+ return err;
+}
+
+
+/* The output for external keylistings in GnuPG is different from all
+ the other key listings. We catch this here with a special
+ preprocessor that reformats the colon handler lines. */
+static gpgme_error_t
+gpg_keylist_preprocess (char *line, char **r_line)
+{
+ enum
+ {
+ RT_NONE, RT_INFO, RT_PUB, RT_UID
+ }
+ rectype = RT_NONE;
+#define NR_FIELDS 16
+ char *field[NR_FIELDS];
+ int fields = 0;
+
+ *r_line = NULL;
+
+ while (line && fields < NR_FIELDS)
+ {
+ field[fields++] = line;
+ line = strchr (line, ':');
+ if (line)
+ *(line++) = '\0';
+ }
+
+ if (!strcmp (field[0], "info"))
+ rectype = RT_INFO;
+ else if (!strcmp (field[0], "pub"))
+ rectype = RT_PUB;
+ else if (!strcmp (field[0], "uid"))
+ rectype = RT_UID;
+ else
+ rectype = RT_NONE;
+
+ switch (rectype)
+ {
+ case RT_INFO:
+ /* FIXME: Eventually, check the version number at least. */
+ return 0;
+
+ case RT_PUB:
+ if (fields < 7)
+ return 0;
+
+ /* The format is:
+
+ pub:<keyid>:<algo>:<keylen>:<creationdate>:<expirationdate>:<flags>
+
+ as defined in 5.2. Machine Readable Indexes of the OpenPGP
+ HTTP Keyserver Protocol (draft).
+
+ We want:
+ pub:o<flags>:<keylen>:<algo>:<keyid>:<creatdate>:<expdate>::::::::
+ */
+
+ if (asprintf (r_line, "pub:o%s:%s:%s:%s:%s:%s::::::::",
+ field[6], field[3], field[2], field[1],
+ field[4], field[5]) < 0)
+ return gpg_error_from_errno (errno);
+ return 0;
+
+ case RT_UID:
+ /* The format is:
+
+ uid:<escaped uid string>:<creationdate>:<expirationdate>:<flags>
+
+ as defined in 5.2. Machine Readable Indexes of the OpenPGP
+ HTTP Keyserver Protocol (draft).
+
+ We want:
+ uid:o<flags>::::<creatdate>:<expdate>:::<c-coded uid>:
+ */
+
+ {
+ /* The user ID is percent escaped, but we want c-coded.
+ Because we have to replace each '%HL' by '\xHL', we need at
+ most 4/3 th the number of bytes. But because we also need
+ to escape the backslashes we allocate twice as much. */
+ char *uid = malloc (2 * strlen (field[1]) + 1);
+ char *src;
+ char *dst;
+
+ if (! uid)
+ return gpg_error_from_errno (errno);
+ src = field[1];
+ dst = uid;
+ while (*src)
+ {
+ if (*src == '%')
+ {
+ *(dst++) = '\\';
+ *(dst++) = 'x';
+ src++;
+ /* Copy the next two bytes unconditionally. */
+ if (*src)
+ *(dst++) = *(src++);
+ if (*src)
+ *(dst++) = *(src++);
+ }
+ else if (*src == '\\')
+ {
+ *dst++ = '\\';
+ *dst++ = '\\';
+ }
+ else
+ *(dst++) = *(src++);
+ }
+ *dst = '\0';
+
+ if (asprintf (r_line, "uid:o%s::::%s:%s:::%s:",
+ field[4], field[2], field[3], uid) < 0)
+ return gpg_error_from_errno (errno);
+ }
+ return 0;
+
+ case RT_NONE:
+ /* Unknown record. */
+ break;
+ }
+ return 0;
+
+}
+
+
+static gpg_error_t
+gpg_keylist_build_options (engine_gpg_t gpg, int secret_only,
+ gpgme_keylist_mode_t mode)
+{
+ gpg_error_t err;
+
+ err = add_arg (gpg, "--with-colons");
+ if (!err)
+ err = add_arg (gpg, "--fixed-list-mode");
+ if (!err)
+ err = add_arg (gpg, "--with-fingerprint");
+ if (!err)
+ err = add_arg (gpg, "--with-fingerprint");
+ if (!err
+ && (mode & GPGME_KEYLIST_MODE_SIGS)
+ && (mode & GPGME_KEYLIST_MODE_SIG_NOTATIONS))
+ {
+ err = add_arg (gpg, "--list-options");
+ if (!err)
+ err = add_arg (gpg, "show-sig-subpackets=\"20,26\"");
+ }
+ if (!err)
+ {
+ if ( (mode & GPGME_KEYLIST_MODE_EXTERN) )
+ {
+ if (secret_only)
+ err = gpg_error (GPG_ERR_NOT_SUPPORTED);
+ else if ( (mode & GPGME_KEYLIST_MODE_LOCAL))
+ {
+ /* The local+extern mode is special. It works only with
+ gpg >= 2.0.10. FIXME: We should check that we have
+ such a version to that we can return a proper error
+ code. The problem is that we don't know the context
+ here and thus can't access the cached version number
+ for the engine info structure. */
+ err = add_arg (gpg, "--locate-keys");
+ if ((mode & GPGME_KEYLIST_MODE_SIGS))
+ err = add_arg (gpg, "--with-sig-check");
+ }
+ else
+ {
+ err = add_arg (gpg, "--search-keys");
+ gpg->colon.preprocess_fnc = gpg_keylist_preprocess;
+ }
+ }
+ else
+ {
+ err = add_arg (gpg, secret_only ? "--list-secret-keys"
+ : ((mode & GPGME_KEYLIST_MODE_SIGS)
+ ? "--check-sigs" : "--list-keys"));
+ }
+ }
+ if (!err)
+ err = add_arg (gpg, "--");
+
+ return err;
+}
+
+
+static gpgme_error_t
+gpg_keylist (void *engine, const char *pattern, int secret_only,
+ gpgme_keylist_mode_t mode)
+{
+ engine_gpg_t gpg = engine;
+ gpgme_error_t err;
+
+ err = gpg_keylist_build_options (gpg, secret_only, mode);
+
+ if (!err && pattern && *pattern)
+ err = add_arg (gpg, pattern);
+
+ if (!err)
+ err = start (gpg);
+
+ return err;
+}
+
+
+static gpgme_error_t
+gpg_keylist_ext (void *engine, const char *pattern[], int secret_only,
+ int reserved, gpgme_keylist_mode_t mode)
+{
+ engine_gpg_t gpg = engine;
+ gpgme_error_t err;
+
+ if (reserved)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ err = gpg_keylist_build_options (gpg, secret_only, mode);
+
+ if (pattern)
+ {
+ while (!err && *pattern && **pattern)
+ err = add_arg (gpg, *(pattern++));
+ }
+
+ if (!err)
+ err = start (gpg);
+
+ return err;
+}
+
+
+static gpgme_error_t
+gpg_sign (void *engine, gpgme_data_t in, gpgme_data_t out,
+ gpgme_sig_mode_t mode, int use_armor, int use_textmode,
+ int include_certs, gpgme_ctx_t ctx /* FIXME */)
+{
+ engine_gpg_t gpg = engine;
+ gpgme_error_t err;
+
+ if (mode == GPGME_SIG_MODE_CLEAR)
+ err = add_arg (gpg, "--clearsign");
+ else
+ {
+ err = add_arg (gpg, "--sign");
+ if (!err && mode == GPGME_SIG_MODE_DETACH)
+ err = add_arg (gpg, "--detach");
+ if (!err && use_armor)
+ err = add_arg (gpg, "--armor");
+ if (!err && use_textmode)
+ err = add_arg (gpg, "--textmode");
+ }
+
+ if (!err)
+ err = append_args_from_signers (gpg, ctx);
+ if (!err)
+ err = append_args_from_sig_notations (gpg, ctx);
+
+ if (gpgme_data_get_file_name (in))
+ {
+ if (!err)
+ err = add_arg (gpg, "--set-filename");
+ if (!err)
+ err = add_arg (gpg, gpgme_data_get_file_name (in));
+ }
+
+ /* Tell the gpg object about the data. */
+ if (!err)
+ err = add_arg (gpg, "--");
+ if (!err)
+ err = add_data (gpg, in, -1, 0);
+ if (!err)
+ err = add_data (gpg, out, 1, 1);
+
+ if (!err)
+ start (gpg);
+
+ return err;
+}
+
+static gpgme_error_t
+gpg_trustlist (void *engine, const char *pattern)
+{
+ engine_gpg_t gpg = engine;
+ gpgme_error_t err;
+
+ err = add_arg (gpg, "--with-colons");
+ if (!err)
+ err = add_arg (gpg, "--list-trust-path");
+
+ /* Tell the gpg object about the data. */
+ if (!err)
+ err = add_arg (gpg, "--");
+ if (!err)
+ err = add_arg (gpg, pattern);
+
+ if (!err)
+ err = start (gpg);
+
+ return err;
+}
+
+
+static gpgme_error_t
+gpg_verify (void *engine, gpgme_data_t sig, gpgme_data_t signed_text,
+ gpgme_data_t plaintext)
+{
+ engine_gpg_t gpg = engine;
+ gpgme_error_t err = 0;
+
+ if (plaintext)
+ {
+ /* Normal or cleartext signature. */
+
+ err = add_arg (gpg, "--output");
+ if (!err)
+ err = add_arg (gpg, "-");
+ if (!err)
+ err = add_arg (gpg, "--");
+ if (!err)
+ err = add_data (gpg, sig, -1, 0);
+ if (!err)
+ err = add_data (gpg, plaintext, 1, 1);
+ }
+ else
+ {
+ err = add_arg (gpg, "--verify");
+ if (!err)
+ err = add_arg (gpg, "--");
+ if (!err)
+ err = add_data (gpg, sig, -1, 0);
+ if (!err && signed_text)
+ err = add_data (gpg, signed_text, -1, 0);
+ }
+
+ if (!err)
+ err = start (gpg);
+
+ return err;
+}
+
+
+static void
+gpg_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
+{
+ engine_gpg_t gpg = engine;
+
+ gpg->io_cbs = *io_cbs;
+}
+
+
+struct engine_ops _gpgme_engine_ops_gpg =
+ {
+ /* Static functions. */
+ _gpgme_get_gpg_path,
+ NULL,
+ gpg_get_version,
+ gpg_get_req_version,
+ gpg_new,
+
+ /* Member functions. */
+ gpg_release,
+ NULL, /* reset */
+ gpg_set_status_handler,
+ gpg_set_command_handler,
+ gpg_set_colon_line_handler,
+ gpg_set_locale,
+ NULL, /* set_protocol */
+ gpg_decrypt,
+ gpg_decrypt, /* decrypt_verify */
+ gpg_delete,
+ gpg_edit,
+ gpg_encrypt,
+ gpg_encrypt_sign,
+ gpg_export,
+ gpg_export_ext,
+ gpg_genkey,
+ gpg_import,
+ gpg_keylist,
+ gpg_keylist_ext,
+ gpg_sign,
+ gpg_trustlist,
+ gpg_verify,
+ NULL, /* getauditlog */
+ NULL, /* opassuan_transact */
+ NULL, /* conf_load */
+ NULL, /* conf_save */
+ gpg_set_io_cbs,
+ gpg_io_event,
+ gpg_cancel,
+ NULL, /* cancel_op */
+ gpg_passwd
+ };
diff --git a/src/engine-gpgconf.c b/src/engine-gpgconf.c
new file mode 100644
index 0000000..6807dce
--- /dev/null
+++ b/src/engine-gpgconf.c
@@ -0,0 +1,928 @@
+/* engine-gpgconf.c - gpg-conf engine.
+ Copyright (C) 2000 Werner Koch (dd9jn)
+ Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2008 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/>.
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#include <assert.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#include <fcntl.h> /* FIXME */
+#include <errno.h>
+
+#include "gpgme.h"
+#include "util.h"
+#include "ops.h"
+#include "wait.h"
+#include "priv-io.h"
+#include "sema.h"
+
+#include "assuan.h"
+#include "debug.h"
+
+#include "engine-backend.h"
+
+
+struct engine_gpgconf
+{
+ char *file_name;
+ char *home_dir;
+};
+
+typedef struct engine_gpgconf *engine_gpgconf_t;
+
+
+static char *
+gpgconf_get_version (const char *file_name)
+{
+ return _gpgme_get_program_version (file_name ? file_name
+ : _gpgme_get_gpgconf_path ());
+}
+
+
+static const char *
+gpgconf_get_req_version (void)
+{
+ return NEED_GPGCONF_VERSION;
+}
+
+
+static void
+gpgconf_release (void *engine)
+{
+ engine_gpgconf_t gpgconf = engine;
+
+ if (!gpgconf)
+ return;
+
+ if (gpgconf->file_name)
+ free (gpgconf->file_name);
+ if (gpgconf->home_dir)
+ free (gpgconf->home_dir);
+
+ free (gpgconf);
+}
+
+
+static gpgme_error_t
+gpgconf_new (void **engine, const char *file_name, const char *home_dir)
+{
+ gpgme_error_t err = 0;
+ engine_gpgconf_t gpgconf;
+
+ gpgconf = calloc (1, sizeof *gpgconf);
+ if (!gpgconf)
+ return gpg_error_from_errno (errno);
+
+ gpgconf->file_name = strdup (file_name ? file_name
+ : _gpgme_get_gpgconf_path ());
+ if (!gpgconf->file_name)
+ err = gpg_error_from_syserror ();
+
+ if (!err && home_dir)
+ {
+ gpgconf->home_dir = strdup (home_dir);
+ if (!gpgconf->home_dir)
+ err = gpg_error_from_syserror ();
+ }
+
+ if (err)
+ gpgconf_release (gpgconf);
+ else
+ *engine = gpgconf;
+
+ return err;
+}
+
+
+static void
+release_arg (gpgme_conf_arg_t arg, gpgme_conf_type_t alt_type)
+{
+ while (arg)
+ {
+ gpgme_conf_arg_t next = arg->next;
+
+ if (alt_type == GPGME_CONF_STRING)
+ free (arg->value.string);
+ free (arg);
+ arg = next;
+ }
+}
+
+
+static void
+release_opt (gpgme_conf_opt_t opt)
+{
+ if (opt->name)
+ free (opt->name);
+ if (opt->description)
+ free (opt->description);
+ if (opt->argname)
+ free (opt->argname);
+
+ release_arg (opt->default_value, opt->alt_type);
+ if (opt->default_description)
+ free (opt->default_description);
+
+ release_arg (opt->no_arg_value, opt->alt_type);
+ release_arg (opt->value, opt->alt_type);
+ release_arg (opt->new_value, opt->alt_type);
+
+ free (opt);
+}
+
+
+static void
+release_comp (gpgme_conf_comp_t comp)
+{
+ gpgme_conf_opt_t opt;
+
+ if (comp->name)
+ free (comp->name);
+ if (comp->description)
+ free (comp->description);
+ if (comp->program_name)
+ free (comp->program_name);
+
+ opt = comp->options;
+ while (opt)
+ {
+ gpgme_conf_opt_t next = opt->next;
+ release_opt (opt);
+ opt = next;
+ }
+
+ free (comp);
+}
+
+
+static void
+gpgconf_config_release (gpgme_conf_comp_t conf)
+{
+ while (conf)
+ {
+ gpgme_conf_comp_t next = conf->next;
+ release_comp (conf);
+ conf = next;
+ }
+}
+
+
+static gpgme_error_t
+gpgconf_read (void *engine, char *arg1, char *arg2,
+ gpgme_error_t (*cb) (void *hook, char *line),
+ void *hook)
+{
+ struct engine_gpgconf *gpgconf = engine;
+ gpgme_error_t err = 0;
+#define LINELENGTH 1024
+ char linebuf[LINELENGTH] = "";
+ int linelen = 0;
+ char *argv[4] = { NULL /* file_name */, NULL, NULL, NULL };
+ int rp[2];
+ struct spawn_fd_item_s cfd[] = { {-1, 1 /* STDOUT_FILENO */, -1, 0},
+ {-1, -1} };
+ int status;
+ int nread;
+ char *mark = NULL;
+
+ argv[1] = arg1;
+ argv[2] = arg2;
+
+
+ /* FIXME: Deal with engine->home_dir. */
+
+ /* _gpgme_engine_new guarantees that this is not NULL. */
+ argv[0] = gpgconf->file_name;
+
+ if (_gpgme_io_pipe (rp, 1) < 0)
+ return gpg_error_from_syserror ();
+
+ cfd[0].fd = rp[1];
+
+ status = _gpgme_io_spawn (gpgconf->file_name, argv, 0, cfd, NULL, NULL, NULL);
+ if (status < 0)
+ {
+ _gpgme_io_close (rp[0]);
+ _gpgme_io_close (rp[1]);
+ return gpg_error_from_syserror ();
+ }
+
+ do
+ {
+ nread = _gpgme_io_read (rp[0],
+ linebuf + linelen, LINELENGTH - linelen - 1);
+ if (nread > 0)
+ {
+ char *line;
+ const char *lastmark = NULL;
+ size_t nused;
+
+ linelen += nread;
+ linebuf[linelen] = '\0';
+
+ for (line=linebuf; (mark = strchr (line, '\n')); line = mark+1 )
+ {
+ lastmark = mark;
+ if (mark > line && mark[-1] == '\r')
+ mark[-1] = '\0';
+ else
+ mark[0] = '\0';
+
+ /* Got a full line. Due to the CR removal code (which
+ occurs only on Windows) we might be one-off and thus
+ would see empty lines. Don't pass them to the
+ callback. */
+ err = *line? (*cb) (hook, line) : 0;
+ if (err)
+ goto leave;
+ }
+
+ nused = lastmark? (lastmark + 1 - linebuf) : 0;
+ memmove (linebuf, linebuf + nused, linelen - nused);
+ linelen -= nused;
+ }
+ }
+ while (nread > 0 && linelen < LINELENGTH - 1);
+
+ if (!err && nread < 0)
+ err = gpg_error_from_syserror ();
+ if (!err && nread > 0)
+ err = gpg_error (GPG_ERR_LINE_TOO_LONG);
+
+ leave:
+ _gpgme_io_close (rp[0]);
+
+ return err;
+}
+
+
+static gpgme_error_t
+gpgconf_config_load_cb (void *hook, char *line)
+{
+ gpgme_conf_comp_t *comp_p = hook;
+ gpgme_conf_comp_t comp = *comp_p;
+#define NR_FIELDS 16
+ char *field[NR_FIELDS];
+ int fields = 0;
+
+ while (line && fields < NR_FIELDS)
+ {
+ field[fields++] = line;
+ line = strchr (line, ':');
+ if (line)
+ *(line++) = '\0';
+ }
+
+ /* We require at least the first 3 fields. */
+ if (fields < 2)
+ return gpg_error (GPG_ERR_INV_ENGINE);
+
+ /* Find the pointer to the new component in the list. */
+ while (comp && comp->next)
+ comp = comp->next;
+ if (comp)
+ comp_p = &comp->next;
+
+ comp = calloc (1, sizeof (*comp));
+ if (!comp)
+ return gpg_error_from_syserror ();
+ /* Prepare return value. */
+ comp->_last_opt_p = &comp->options;
+ *comp_p = comp;
+
+ comp->name = strdup (field[0]);
+ if (!comp->name)
+ return gpg_error_from_syserror ();
+
+ comp->description = strdup (field[1]);
+ if (!comp->description)
+ return gpg_error_from_syserror ();
+
+ if (fields >= 3)
+ {
+ comp->program_name = strdup (field[2]);
+ if (!comp->program_name)
+ return gpg_error_from_syserror ();
+ }
+
+ return 0;
+}
+
+
+static gpgme_error_t
+gpgconf_parse_option (gpgme_conf_opt_t opt,
+ gpgme_conf_arg_t *arg_p, char *line)
+{
+ gpgme_error_t err;
+ char *mark;
+
+ if (!line[0])
+ return 0;
+
+ while (line)
+ {
+ gpgme_conf_arg_t arg;
+
+ mark = strchr (line, ',');
+ if (mark)
+ *mark = '\0';
+
+ arg = calloc (1, sizeof (*arg));
+ if (!arg)
+ return gpg_error_from_syserror ();
+ *arg_p = arg;
+ arg_p = &arg->next;
+
+ if (*line == '\0')
+ arg->no_arg = 1;
+ else
+ {
+ switch (opt->alt_type)
+ {
+ /* arg->value.count is an alias for arg->value.uint32. */
+ case GPGME_CONF_NONE:
+ case GPGME_CONF_UINT32:
+ arg->value.uint32 = strtoul (line, NULL, 0);
+ break;
+
+ case GPGME_CONF_INT32:
+ arg->value.uint32 = strtol (line, NULL, 0);
+ break;
+
+ case GPGME_CONF_STRING:
+ /* The complex types below are only here to silent the
+ compiler warning. */
+ case GPGME_CONF_FILENAME:
+ case GPGME_CONF_LDAP_SERVER:
+ case GPGME_CONF_KEY_FPR:
+ case GPGME_CONF_PUB_KEY:
+ case GPGME_CONF_SEC_KEY:
+ case GPGME_CONF_ALIAS_LIST:
+ /* Skip quote character. */
+ line++;
+
+ err = _gpgme_decode_percent_string (line, &arg->value.string,
+ 0, 0);
+ if (err)
+ return err;
+ break;
+ }
+ }
+
+ /* Find beginning of next value. */
+ if (mark++ && *mark)
+ line = mark;
+ else
+ line = NULL;
+ }
+
+ return 0;
+}
+
+
+static gpgme_error_t
+gpgconf_config_load_cb2 (void *hook, char *line)
+{
+ gpgme_error_t err;
+ gpgme_conf_comp_t comp = hook;
+ gpgme_conf_opt_t *opt_p = comp->_last_opt_p;
+ gpgme_conf_opt_t opt;
+#define NR_FIELDS 16
+ char *field[NR_FIELDS];
+ int fields = 0;
+
+ while (line && fields < NR_FIELDS)
+ {
+ field[fields++] = line;
+ line = strchr (line, ':');
+ if (line)
+ *(line++) = '\0';
+ }
+
+ /* We require at least the first 10 fields. */
+ if (fields < 10)
+ return gpg_error (GPG_ERR_INV_ENGINE);
+
+ opt = calloc (1, sizeof (*opt));
+ if (!opt)
+ return gpg_error_from_syserror ();
+
+ comp->_last_opt_p = &opt->next;
+ *opt_p = opt;
+
+ if (field[0][0])
+ {
+ opt->name = strdup (field[0]);
+ if (!opt->name)
+ return gpg_error_from_syserror ();
+ }
+
+ opt->flags = strtoul (field[1], NULL, 0);
+
+ opt->level = strtoul (field[2], NULL, 0);
+
+ if (field[3][0])
+ {
+ opt->description = strdup (field[3]);
+ if (!opt->description)
+ return gpg_error_from_syserror ();
+ }
+
+ opt->type = strtoul (field[4], NULL, 0);
+
+ opt->alt_type = strtoul (field[5], NULL, 0);
+
+ if (field[6][0])
+ {
+ opt->argname = strdup (field[6]);
+ if (!opt->argname)
+ return gpg_error_from_syserror ();
+ }
+
+ if (opt->flags & GPGME_CONF_DEFAULT)
+ {
+ err = gpgconf_parse_option (opt, &opt->default_value, field[7]);
+ if (err)
+ return err;
+ }
+ else if ((opt->flags & GPGME_CONF_DEFAULT_DESC) && field[7][0])
+ {
+ opt->default_description = strdup (field[7]);
+ if (!opt->default_description)
+ return gpg_error_from_syserror ();
+ }
+
+ if (opt->flags & GPGME_CONF_NO_ARG_DESC)
+ {
+ opt->no_arg_description = strdup (field[8]);
+ if (!opt->no_arg_description)
+ return gpg_error_from_syserror ();
+ }
+ else
+ {
+ err = gpgconf_parse_option (opt, &opt->no_arg_value, field[8]);
+ if (err)
+ return err;
+ }
+
+ err = gpgconf_parse_option (opt, &opt->value, field[9]);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+
+static gpgme_error_t
+gpgconf_conf_load (void *engine, gpgme_conf_comp_t *comp_p)
+{
+ gpgme_error_t err;
+ gpgme_conf_comp_t comp = NULL;
+ gpgme_conf_comp_t cur_comp;
+
+ *comp_p = NULL;
+
+ err = gpgconf_read (engine, "--list-components", NULL,
+ gpgconf_config_load_cb, &comp);
+ if (err)
+ {
+ gpgconf_release (comp);
+ return err;
+ }
+
+ cur_comp = comp;
+ while (!err && cur_comp)
+ {
+ err = gpgconf_read (engine, "--list-options", cur_comp->name,
+ gpgconf_config_load_cb2, cur_comp);
+ cur_comp = cur_comp->next;
+ }
+
+ if (err)
+ {
+ gpgconf_release (comp);
+ return err;
+ }
+
+ *comp_p = comp;
+ return 0;
+}
+
+
+
+gpgme_error_t
+_gpgme_conf_arg_new (gpgme_conf_arg_t *arg_p,
+ gpgme_conf_type_t type, const void *value)
+{
+ gpgme_conf_arg_t arg;
+
+ arg = calloc (1, sizeof (*arg));
+ if (!arg)
+ return gpg_error_from_syserror ();
+
+ if (!value)
+ arg->no_arg = 1;
+ else
+ {
+ /* We need to switch on type here because the alt-type is not
+ yet known. */
+ switch (type)
+ {
+ case GPGME_CONF_NONE:
+ case GPGME_CONF_UINT32:
+ arg->value.uint32 = *((unsigned int *) value);
+ break;
+
+ case GPGME_CONF_INT32:
+ arg->value.int32 = *((int *) value);
+ break;
+
+ case GPGME_CONF_STRING:
+ case GPGME_CONF_FILENAME:
+ case GPGME_CONF_LDAP_SERVER:
+ case GPGME_CONF_KEY_FPR:
+ case GPGME_CONF_PUB_KEY:
+ case GPGME_CONF_SEC_KEY:
+ case GPGME_CONF_ALIAS_LIST:
+ arg->value.string = strdup (value);
+ if (!arg->value.string)
+ {
+ free (arg);
+ return gpg_error_from_syserror ();
+ }
+ break;
+
+ default:
+ free (arg);
+ return gpg_error (GPG_ERR_INV_VALUE);
+ }
+ }
+
+ *arg_p = arg;
+ return 0;
+}
+
+
+void
+_gpgme_conf_arg_release (gpgme_conf_arg_t arg, gpgme_conf_type_t type)
+{
+ /* Lacking the alt_type we need to switch on type here. */
+ switch (type)
+ {
+ case GPGME_CONF_NONE:
+ case GPGME_CONF_UINT32:
+ case GPGME_CONF_INT32:
+ case GPGME_CONF_STRING:
+ default:
+ break;
+
+ case GPGME_CONF_FILENAME:
+ case GPGME_CONF_LDAP_SERVER:
+ case GPGME_CONF_KEY_FPR:
+ case GPGME_CONF_PUB_KEY:
+ case GPGME_CONF_SEC_KEY:
+ case GPGME_CONF_ALIAS_LIST:
+ type = GPGME_CONF_STRING;
+ break;
+ }
+
+ release_arg (arg, type);
+}
+
+
+gpgme_error_t
+_gpgme_conf_opt_change (gpgme_conf_opt_t opt, int reset, gpgme_conf_arg_t arg)
+{
+ if (reset)
+ {
+ if (opt->new_value)
+ release_arg (opt->new_value, opt->alt_type);
+ opt->new_value = NULL;
+ opt->change_value = 0;
+ }
+ else
+ {
+ /* Support self-assignment, for example for adding an item to an
+ existing list. */
+ if (opt->new_value && arg != opt->new_value)
+ release_arg (opt->new_value, opt->alt_type);
+ opt->new_value = arg;
+ opt->change_value = 1;
+ }
+ return 0;
+}
+
+
+/* FIXME: Major problem: We don't get errors from gpgconf. */
+
+static gpgme_error_t
+gpgconf_write (void *engine, char *arg1, char *arg2, gpgme_data_t conf)
+{
+ struct engine_gpgconf *gpgconf = engine;
+ gpgme_error_t err = 0;
+#define BUFLEN 1024
+ char buf[BUFLEN];
+ int buflen = 0;
+ char *argv[] = { NULL /* file_name */, arg1, arg2, 0 };
+ int rp[2];
+ struct spawn_fd_item_s cfd[] = { {-1, 0 /* STDIN_FILENO */}, {-1, -1} };
+ int status;
+ int nwrite;
+
+ /* FIXME: Deal with engine->home_dir. */
+
+ /* _gpgme_engine_new guarantees that this is not NULL. */
+ argv[0] = gpgconf->file_name;
+
+ if (_gpgme_io_pipe (rp, 0) < 0)
+ return gpg_error_from_syserror ();
+
+ cfd[0].fd = rp[0];
+
+ status = _gpgme_io_spawn (gpgconf->file_name, argv, 0, cfd, NULL, NULL, NULL);
+ if (status < 0)
+ {
+ _gpgme_io_close (rp[0]);
+ _gpgme_io_close (rp[1]);
+ return gpg_error_from_syserror ();
+ }
+
+ for (;;)
+ {
+ if (buflen == 0)
+ {
+ do
+ {
+ buflen = gpgme_data_read (conf, buf, BUFLEN);
+ }
+ while (buflen < 0 && errno == EAGAIN);
+
+ if (buflen < 0)
+ {
+ err = gpg_error_from_syserror ();
+ _gpgme_io_close (rp[1]);
+ return err;
+ }
+ else if (buflen == 0)
+ {
+ /* All is written. */
+ _gpgme_io_close (rp[1]);
+ return 0;
+ }
+ }
+
+ do
+ {
+ nwrite = _gpgme_io_write (rp[1], buf, buflen);
+ }
+ while (nwrite < 0 && errno == EAGAIN);
+
+ if (nwrite > 0)
+ {
+ buflen -= nwrite;
+ if (buflen > 0)
+ memmove (&buf[0], &buf[nwrite], buflen);
+ }
+ else if (nwrite < 0)
+ {
+ _gpgme_io_close (rp[1]);
+ return gpg_error_from_syserror ();
+ }
+ }
+
+ return 0;
+}
+
+
+static gpgme_error_t
+arg_to_data (gpgme_data_t conf, gpgme_conf_opt_t option, gpgme_conf_arg_t arg)
+{
+ gpgme_error_t err = 0;
+ int amt = 0;
+ char buf[16];
+
+ while (amt >= 0 && arg)
+ {
+ switch (option->alt_type)
+ {
+ case GPGME_CONF_NONE:
+ case GPGME_CONF_UINT32:
+ default:
+ snprintf (buf, sizeof (buf), "%u", arg->value.uint32);
+ buf[sizeof (buf) - 1] = '\0';
+ amt = gpgme_data_write (conf, buf, strlen (buf));
+ break;
+
+ case GPGME_CONF_INT32:
+ snprintf (buf, sizeof (buf), "%i", arg->value.uint32);
+ buf[sizeof (buf) - 1] = '\0';
+ amt = gpgme_data_write (conf, buf, strlen (buf));
+ break;
+
+
+ case GPGME_CONF_STRING:
+ /* The complex types below are only here to silent the
+ compiler warning. */
+ case GPGME_CONF_FILENAME:
+ case GPGME_CONF_LDAP_SERVER:
+ case GPGME_CONF_KEY_FPR:
+ case GPGME_CONF_PUB_KEY:
+ case GPGME_CONF_SEC_KEY:
+ case GPGME_CONF_ALIAS_LIST:
+ /* One quote character, and three times to allow
+ for percent escaping. */
+ {
+ char *ptr = arg->value.string;
+ amt = gpgme_data_write (conf, "\"", 1);
+ if (amt < 0)
+ break;
+
+ while (!err && *ptr)
+ {
+ switch (*ptr)
+ {
+ case '%':
+ amt = gpgme_data_write (conf, "%25", 3);
+ break;
+
+ case ':':
+ amt = gpgme_data_write (conf, "%3a", 3);
+ break;
+
+ case ',':
+ amt = gpgme_data_write (conf, "%2c", 3);
+ break;
+
+ default:
+ amt = gpgme_data_write (conf, ptr, 1);
+ }
+ ptr++;
+ }
+ }
+ break;
+ }
+
+ if (amt < 0)
+ break;
+
+ arg = arg->next;
+ /* Comma separator. */
+ if (arg)
+ amt = gpgme_data_write (conf, ",", 1);
+ }
+
+ if (amt < 0)
+ return gpg_error_from_syserror ();
+
+ return 0;
+}
+
+
+static gpgme_error_t
+gpgconf_conf_save (void *engine, gpgme_conf_comp_t comp)
+{
+ gpgme_error_t err;
+ int amt = 0;
+ /* We use a data object to store the new configuration. */
+ gpgme_data_t conf;
+ gpgme_conf_opt_t option;
+ int something_changed = 0;
+
+ err = gpgme_data_new (&conf);
+ if (err)
+ return err;
+
+ option = comp->options;
+ while (!err && amt >= 0 && option)
+ {
+ if (option->change_value)
+ {
+ unsigned int flags = 0;
+ char buf[16];
+
+ something_changed = 1;
+
+ amt = gpgme_data_write (conf, option->name, strlen (option->name));
+ if (amt >= 0)
+ amt = gpgme_data_write (conf, ":", 1);
+ if (amt < 0)
+ break;
+
+ if (!option->new_value)
+ flags |= GPGME_CONF_DEFAULT;
+ snprintf (buf, sizeof (buf), "%u", flags);
+ buf[sizeof (buf) - 1] = '\0';
+
+ amt = gpgme_data_write (conf, buf, strlen (buf));
+ if (amt >= 0)
+ amt = gpgme_data_write (conf, ":", 1);
+ if (amt < 0)
+ break;
+
+ if (option->new_value)
+ {
+ err = arg_to_data (conf, option, option->new_value);
+ if (err)
+ break;
+ }
+ amt = gpgme_data_write (conf, "\n", 1);
+ }
+ option = option->next;
+ }
+ if (!err && amt < 0)
+ err = gpg_error_from_syserror ();
+ if (err || !something_changed)
+ goto bail;
+
+ err = gpgme_data_seek (conf, 0, SEEK_SET);
+ if (err)
+ goto bail;
+
+ err = gpgconf_write (engine, "--change-options", comp->name, conf);
+ bail:
+ gpgme_data_release (conf);
+ return err;
+}
+
+
+static void
+gpgconf_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
+{
+ /* Nothing to do. */
+}
+
+
+/* Currently, we do not use the engine interface for the various
+ operations. */
+void
+_gpgme_conf_release (gpgme_conf_comp_t conf)
+{
+ gpgconf_config_release (conf);
+}
+
+
+struct engine_ops _gpgme_engine_ops_gpgconf =
+ {
+ /* Static functions. */
+ _gpgme_get_gpgconf_path,
+ NULL,
+ gpgconf_get_version,
+ gpgconf_get_req_version,
+ gpgconf_new,
+
+ /* Member functions. */
+ gpgconf_release,
+ NULL, /* reset */
+ NULL, /* set_status_handler */
+ NULL, /* set_command_handler */
+ NULL, /* set_colon_line_handler */
+ NULL, /* set_locale */
+ NULL, /* set_protocol */
+ NULL, /* decrypt */
+ NULL, /* decrypt_verify */
+ NULL, /* delete */
+ NULL, /* edit */
+ NULL, /* encrypt */
+ NULL, /* encrypt_sign */
+ NULL, /* export */
+ NULL, /* export_ext */
+ NULL, /* genkey */
+ NULL, /* import */
+ NULL, /* keylist */
+ NULL, /* keylist_ext */
+ NULL, /* sign */
+ NULL, /* trustlist */
+ NULL, /* verify */
+ NULL, /* getauditlog */
+ NULL, /* opassuan_transact */
+ gpgconf_conf_load,
+ gpgconf_conf_save,
+ gpgconf_set_io_cbs,
+ NULL, /* io_event */
+ NULL /* cancel */
+ };
diff --git a/src/engine-gpgsm.c b/src/engine-gpgsm.c
new file mode 100644
index 0000000..0620458
--- /dev/null
+++ b/src/engine-gpgsm.c
@@ -0,0 +1,1989 @@
+/* engine-gpgsm.c - GpgSM engine.
+ Copyright (C) 2000 Werner Koch (dd9jn)
+ Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2009,
+ 2010 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#include <assert.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#ifdef HAVE_LOCALE_H
+#include <locale.h>
+#endif
+#include <fcntl.h> /* FIXME */
+#include <errno.h>
+
+#include "gpgme.h"
+#include "util.h"
+#include "ops.h"
+#include "wait.h"
+#include "priv-io.h"
+#include "sema.h"
+#include "data.h"
+
+#include "assuan.h"
+#include "debug.h"
+
+#include "engine-backend.h"
+
+
+typedef struct
+{
+ int fd; /* FD we talk about. */
+ int server_fd;/* Server FD for this connection. */
+ int dir; /* Inbound/Outbound, maybe given implicit? */
+ void *data; /* Handler-specific data. */
+ void *tag; /* ID from the user for gpgme_remove_io_callback. */
+ char server_fd_str[15]; /* Same as SERVER_FD but as a string. We
+ need this because _gpgme_io_fd2str can't
+ be used on a closed descriptor. */
+} iocb_data_t;
+
+
+struct engine_gpgsm
+{
+ assuan_context_t assuan_ctx;
+
+ int lc_ctype_set;
+ int lc_messages_set;
+
+ iocb_data_t status_cb;
+
+ /* Input, output etc are from the servers perspective. */
+ iocb_data_t input_cb;
+ gpgme_data_t input_helper_data; /* Input helper data object. */
+ void *input_helper_memory; /* Input helper memory block. */
+
+ iocb_data_t output_cb;
+
+ iocb_data_t message_cb;
+
+ struct
+ {
+ engine_status_handler_t fnc;
+ void *fnc_value;
+ } status;
+
+ struct
+ {
+ engine_colon_line_handler_t fnc;
+ void *fnc_value;
+ struct
+ {
+ char *line;
+ int linesize;
+ int linelen;
+ } attic;
+ int any; /* any data line seen */
+ } colon;
+
+ gpgme_data_t inline_data; /* Used to collect D lines. */
+
+ struct gpgme_io_cbs io_cbs;
+};
+
+typedef struct engine_gpgsm *engine_gpgsm_t;
+
+
+static void gpgsm_io_event (void *engine,
+ gpgme_event_io_t type, void *type_data);
+
+
+
+static char *
+gpgsm_get_version (const char *file_name)
+{
+ return _gpgme_get_program_version (file_name ? file_name
+ : _gpgme_get_gpgsm_path ());
+}
+
+
+static const char *
+gpgsm_get_req_version (void)
+{
+ return NEED_GPGSM_VERSION;
+}
+
+
+static void
+close_notify_handler (int fd, void *opaque)
+{
+ engine_gpgsm_t gpgsm = opaque;
+
+ assert (fd != -1);
+ if (gpgsm->status_cb.fd == fd)
+ {
+ if (gpgsm->status_cb.tag)
+ (*gpgsm->io_cbs.remove) (gpgsm->status_cb.tag);
+ gpgsm->status_cb.fd = -1;
+ gpgsm->status_cb.tag = NULL;
+ }
+ else if (gpgsm->input_cb.fd == fd)
+ {
+ if (gpgsm->input_cb.tag)
+ (*gpgsm->io_cbs.remove) (gpgsm->input_cb.tag);
+ gpgsm->input_cb.fd = -1;
+ gpgsm->input_cb.tag = NULL;
+ if (gpgsm->input_helper_data)
+ {
+ gpgme_data_release (gpgsm->input_helper_data);
+ gpgsm->input_helper_data = NULL;
+ }
+ if (gpgsm->input_helper_memory)
+ {
+ free (gpgsm->input_helper_memory);
+ gpgsm->input_helper_memory = NULL;
+ }
+ }
+ else if (gpgsm->output_cb.fd == fd)
+ {
+ if (gpgsm->output_cb.tag)
+ (*gpgsm->io_cbs.remove) (gpgsm->output_cb.tag);
+ gpgsm->output_cb.fd = -1;
+ gpgsm->output_cb.tag = NULL;
+ }
+ else if (gpgsm->message_cb.fd == fd)
+ {
+ if (gpgsm->message_cb.tag)
+ (*gpgsm->io_cbs.remove) (gpgsm->message_cb.tag);
+ gpgsm->message_cb.fd = -1;
+ gpgsm->message_cb.tag = NULL;
+ }
+}
+
+
+/* This is the default inquiry callback. We use it to handle the
+ Pinentry notifications. */
+static gpgme_error_t
+default_inq_cb (engine_gpgsm_t gpgsm, const char *line)
+{
+ if (!strncmp (line, "PINENTRY_LAUNCHED", 17) && (line[17]==' '||!line[17]))
+ {
+ _gpgme_allow_set_foreground_window ((pid_t)strtoul (line+17, NULL, 10));
+ }
+
+ return 0;
+}
+
+
+static gpgme_error_t
+gpgsm_cancel (void *engine)
+{
+ engine_gpgsm_t gpgsm = engine;
+
+ if (!gpgsm)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if (gpgsm->status_cb.fd != -1)
+ _gpgme_io_close (gpgsm->status_cb.fd);
+ if (gpgsm->input_cb.fd != -1)
+ _gpgme_io_close (gpgsm->input_cb.fd);
+ if (gpgsm->output_cb.fd != -1)
+ _gpgme_io_close (gpgsm->output_cb.fd);
+ if (gpgsm->message_cb.fd != -1)
+ _gpgme_io_close (gpgsm->message_cb.fd);
+
+ if (gpgsm->assuan_ctx)
+ {
+ assuan_release (gpgsm->assuan_ctx);
+ gpgsm->assuan_ctx = NULL;
+ }
+
+ return 0;
+}
+
+
+static void
+gpgsm_release (void *engine)
+{
+ engine_gpgsm_t gpgsm = engine;
+
+ if (!gpgsm)
+ return;
+
+ gpgsm_cancel (engine);
+
+ free (gpgsm->colon.attic.line);
+ free (gpgsm);
+}
+
+
+static gpgme_error_t
+gpgsm_new (void **engine, const char *file_name, const char *home_dir)
+{
+ gpgme_error_t err = 0;
+ engine_gpgsm_t gpgsm;
+ const char *argv[5];
+ int argc;
+#if !USE_DESCRIPTOR_PASSING
+ int fds[2];
+ int child_fds[4];
+#endif
+ char *dft_display = NULL;
+ char dft_ttyname[64];
+ char *dft_ttytype = NULL;
+ char *optstr;
+
+ gpgsm = calloc (1, sizeof *gpgsm);
+ if (!gpgsm)
+ return gpg_error_from_syserror ();
+
+ gpgsm->status_cb.fd = -1;
+ gpgsm->status_cb.dir = 1;
+ gpgsm->status_cb.tag = 0;
+ gpgsm->status_cb.data = gpgsm;
+
+ gpgsm->input_cb.fd = -1;
+ gpgsm->input_cb.dir = 0;
+ gpgsm->input_cb.tag = 0;
+ gpgsm->input_cb.server_fd = -1;
+ *gpgsm->input_cb.server_fd_str = 0;
+ gpgsm->output_cb.fd = -1;
+ gpgsm->output_cb.dir = 1;
+ gpgsm->output_cb.tag = 0;
+ gpgsm->output_cb.server_fd = -1;
+ *gpgsm->output_cb.server_fd_str = 0;
+ gpgsm->message_cb.fd = -1;
+ gpgsm->message_cb.dir = 0;
+ gpgsm->message_cb.tag = 0;
+ gpgsm->message_cb.server_fd = -1;
+ *gpgsm->message_cb.server_fd_str = 0;
+
+ gpgsm->status.fnc = 0;
+ gpgsm->colon.fnc = 0;
+ gpgsm->colon.attic.line = 0;
+ gpgsm->colon.attic.linesize = 0;
+ gpgsm->colon.attic.linelen = 0;
+ gpgsm->colon.any = 0;
+
+ gpgsm->inline_data = NULL;
+
+ gpgsm->io_cbs.add = NULL;
+ gpgsm->io_cbs.add_priv = NULL;
+ gpgsm->io_cbs.remove = NULL;
+ gpgsm->io_cbs.event = NULL;
+ gpgsm->io_cbs.event_priv = NULL;
+
+#if !USE_DESCRIPTOR_PASSING
+ if (_gpgme_io_pipe (fds, 0) < 0)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ gpgsm->input_cb.fd = fds[1];
+ gpgsm->input_cb.server_fd = fds[0];
+
+ if (_gpgme_io_pipe (fds, 1) < 0)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ gpgsm->output_cb.fd = fds[0];
+ gpgsm->output_cb.server_fd = fds[1];
+
+ if (_gpgme_io_pipe (fds, 0) < 0)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ gpgsm->message_cb.fd = fds[1];
+ gpgsm->message_cb.server_fd = fds[0];
+
+ child_fds[0] = gpgsm->input_cb.server_fd;
+ child_fds[1] = gpgsm->output_cb.server_fd;
+ child_fds[2] = gpgsm->message_cb.server_fd;
+ child_fds[3] = -1;
+#endif
+
+ argc = 0;
+ argv[argc++] = "gpgsm";
+ if (home_dir)
+ {
+ argv[argc++] = "--homedir";
+ argv[argc++] = home_dir;
+ }
+ argv[argc++] = "--server";
+ argv[argc++] = NULL;
+
+ err = assuan_new_ext (&gpgsm->assuan_ctx, GPG_ERR_SOURCE_GPGME,
+ &_gpgme_assuan_malloc_hooks, _gpgme_assuan_log_cb,
+ NULL);
+ if (err)
+ goto leave;
+ assuan_ctx_set_system_hooks (gpgsm->assuan_ctx, &_gpgme_assuan_system_hooks);
+
+#if USE_DESCRIPTOR_PASSING
+ err = assuan_pipe_connect
+ (gpgsm->assuan_ctx, file_name ? file_name : _gpgme_get_gpgsm_path (),
+ argv, NULL, NULL, NULL, ASSUAN_PIPE_CONNECT_FDPASSING);
+#else
+ {
+ assuan_fd_t achild_fds[4];
+ int i;
+
+ /* For now... */
+ for (i = 0; i < 4; i++)
+ achild_fds[i] = (assuan_fd_t) child_fds[i];
+
+ err = assuan_pipe_connect
+ (gpgsm->assuan_ctx, file_name ? file_name : _gpgme_get_gpgsm_path (),
+ argv, achild_fds, NULL, NULL, 0);
+
+ /* For now... */
+ for (i = 0; i < 4; i++)
+ child_fds[i] = (int) achild_fds[i];
+ }
+
+ /* On Windows, handles are inserted in the spawned process with
+ DuplicateHandle, and child_fds contains the server-local names
+ for the inserted handles when assuan_pipe_connect returns. */
+ if (!err)
+ {
+ /* Note: We don't use _gpgme_io_fd2str here. On W32 the
+ returned handles are real W32 system handles, not whatever
+ GPGME uses internally (which may be a system handle, a C
+ library handle or a GLib/Qt channel. Confusing, yes, but
+ remember these are server-local names, so they are not part
+ of GPGME at all. */
+ snprintf (gpgsm->input_cb.server_fd_str,
+ sizeof gpgsm->input_cb.server_fd_str, "%d", child_fds[0]);
+ snprintf (gpgsm->output_cb.server_fd_str,
+ sizeof gpgsm->output_cb.server_fd_str, "%d", child_fds[1]);
+ snprintf (gpgsm->message_cb.server_fd_str,
+ sizeof gpgsm->message_cb.server_fd_str, "%d", child_fds[2]);
+ }
+#endif
+ if (err)
+ goto leave;
+
+ err = _gpgme_getenv ("DISPLAY", &dft_display);
+ if (err)
+ goto leave;
+ if (dft_display)
+ {
+ if (asprintf (&optstr, "OPTION display=%s", dft_display) < 0)
+ {
+ free (dft_display);
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ free (dft_display);
+
+ err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL,
+ NULL, NULL, NULL);
+ free (optstr);
+ if (err)
+ goto leave;
+ }
+
+ if (isatty (1))
+ {
+ int rc;
+
+ rc = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname));
+ if (rc)
+ {
+ err = gpg_error_from_errno (rc);
+ goto leave;
+ }
+ else
+ {
+ if (asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL,
+ NULL, NULL, NULL);
+ free (optstr);
+ if (err)
+ goto leave;
+
+ err = _gpgme_getenv ("TERM", &dft_ttytype);
+ if (err)
+ goto leave;
+ if (dft_ttytype)
+ {
+ if (asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype) < 0)
+ {
+ free (dft_ttytype);
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ free (dft_ttytype);
+
+ err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL,
+ NULL, NULL, NULL, NULL);
+ free (optstr);
+ if (err)
+ goto leave;
+ }
+ }
+ }
+
+ /* Ask gpgsm to enable the audit log support. */
+ if (!err)
+ {
+ err = assuan_transact (gpgsm->assuan_ctx, "OPTION enable-audit-log=1",
+ NULL, NULL, NULL, NULL, NULL, NULL);
+ if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
+ err = 0; /* This is an optional feature of gpgsm. */
+ }
+
+
+#ifdef HAVE_W32_SYSTEM
+ /* Under Windows we need to use AllowSetForegroundWindow. Tell
+ gpgsm to tell us when it needs it. */
+ if (!err)
+ {
+ err = assuan_transact (gpgsm->assuan_ctx, "OPTION allow-pinentry-notify",
+ NULL, NULL, NULL, NULL, NULL, NULL);
+ if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
+ err = 0; /* This is a new feature of gpgsm. */
+ }
+#endif /*HAVE_W32_SYSTEM*/
+
+#if !USE_DESCRIPTOR_PASSING
+ if (!err
+ && (_gpgme_io_set_close_notify (gpgsm->input_cb.fd,
+ close_notify_handler, gpgsm)
+ || _gpgme_io_set_close_notify (gpgsm->output_cb.fd,
+ close_notify_handler, gpgsm)
+ || _gpgme_io_set_close_notify (gpgsm->message_cb.fd,
+ close_notify_handler, gpgsm)))
+ {
+ err = gpg_error (GPG_ERR_GENERAL);
+ goto leave;
+ }
+#endif
+
+ leave:
+ /* Close the server ends of the pipes (because of this, we must use
+ the stored server_fd_str in the function start). Our ends are
+ closed in gpgsm_release(). */
+#if !USE_DESCRIPTOR_PASSING
+ if (gpgsm->input_cb.server_fd != -1)
+ _gpgme_io_close (gpgsm->input_cb.server_fd);
+ if (gpgsm->output_cb.server_fd != -1)
+ _gpgme_io_close (gpgsm->output_cb.server_fd);
+ if (gpgsm->message_cb.server_fd != -1)
+ _gpgme_io_close (gpgsm->message_cb.server_fd);
+#endif
+
+ if (err)
+ gpgsm_release (gpgsm);
+ else
+ *engine = gpgsm;
+
+ return err;
+}
+
+
+static gpgme_error_t
+gpgsm_set_locale (void *engine, int category, const char *value)
+{
+ engine_gpgsm_t gpgsm = engine;
+ gpgme_error_t err;
+ char *optstr;
+ char *catstr;
+
+ /* FIXME: If value is NULL, we need to reset the option to default.
+ But we can't do this. So we error out here. GPGSM needs support
+ for this. */
+ if (0)
+ ;
+#ifdef LC_CTYPE
+ else if (category == LC_CTYPE)
+ {
+ catstr = "lc-ctype";
+ if (!value && gpgsm->lc_ctype_set)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (value)
+ gpgsm->lc_ctype_set = 1;
+ }
+#endif
+#ifdef LC_MESSAGES
+ else if (category == LC_MESSAGES)
+ {
+ catstr = "lc-messages";
+ if (!value && gpgsm->lc_messages_set)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (value)
+ gpgsm->lc_messages_set = 1;
+ }
+#endif /* LC_MESSAGES */
+ else
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ /* FIXME: Reset value to default. */
+ if (!value)
+ return 0;
+
+ if (asprintf (&optstr, "OPTION %s=%s", catstr, value) < 0)
+ err = gpg_error_from_syserror ();
+ else
+ {
+ err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL,
+ NULL, NULL, NULL, NULL);
+ free (optstr);
+ }
+
+ return err;
+}
+
+
+static gpgme_error_t
+gpgsm_assuan_simple_command (assuan_context_t ctx, char *cmd,
+ engine_status_handler_t status_fnc,
+ void *status_fnc_value)
+{
+ gpg_error_t err;
+ char *line;
+ size_t linelen;
+
+ err = assuan_write_line (ctx, cmd);
+ if (err)
+ return err;
+
+ do
+ {
+ err = assuan_read_line (ctx, &line, &linelen);
+ if (err)
+ return err;
+
+ if (*line == '#' || !linelen)
+ continue;
+
+ if (linelen >= 2
+ && line[0] == 'O' && line[1] == 'K'
+ && (line[2] == '\0' || line[2] == ' '))
+ return 0;
+ else if (linelen >= 4
+ && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
+ && line[3] == ' ')
+ err = atoi (&line[4]);
+ else if (linelen >= 2
+ && line[0] == 'S' && line[1] == ' ')
+ {
+ char *rest;
+ gpgme_status_code_t r;
+
+ rest = strchr (line + 2, ' ');
+ if (!rest)
+ rest = line + linelen; /* set to an empty string */
+ else
+ *(rest++) = 0;
+
+ r = _gpgme_parse_status (line + 2);
+
+ if (r >= 0 && status_fnc)
+ err = status_fnc (status_fnc_value, r, rest);
+ else
+ err = gpg_error (GPG_ERR_GENERAL);
+ }
+ else
+ err = gpg_error (GPG_ERR_GENERAL);
+ }
+ while (!err);
+
+ return err;
+}
+
+
+typedef enum { INPUT_FD, OUTPUT_FD, MESSAGE_FD } fd_type_t;
+
+static void
+gpgsm_clear_fd (engine_gpgsm_t gpgsm, fd_type_t fd_type)
+{
+#if !USE_DESCRIPTOR_PASSING
+ switch (fd_type)
+ {
+ case INPUT_FD:
+ _gpgme_io_close (gpgsm->input_cb.fd);
+ break;
+ case OUTPUT_FD:
+ _gpgme_io_close (gpgsm->output_cb.fd);
+ break;
+ case MESSAGE_FD:
+ _gpgme_io_close (gpgsm->message_cb.fd);
+ break;
+ }
+#endif
+}
+
+#define COMMANDLINELEN 40
+static gpgme_error_t
+gpgsm_set_fd (engine_gpgsm_t gpgsm, fd_type_t fd_type, const char *opt)
+{
+ gpg_error_t err = 0;
+ char line[COMMANDLINELEN];
+ char *which;
+ iocb_data_t *iocb_data;
+ int dir;
+
+ switch (fd_type)
+ {
+ case INPUT_FD:
+ which = "INPUT";
+ iocb_data = &gpgsm->input_cb;
+ break;
+
+ case OUTPUT_FD:
+ which = "OUTPUT";
+ iocb_data = &gpgsm->output_cb;
+ break;
+
+ case MESSAGE_FD:
+ which = "MESSAGE";
+ iocb_data = &gpgsm->message_cb;
+ break;
+
+ default:
+ return gpg_error (GPG_ERR_INV_VALUE);
+ }
+
+ dir = iocb_data->dir;
+
+#if USE_DESCRIPTOR_PASSING
+ /* We try to short-cut the communication by giving GPGSM direct
+ access to the file descriptor, rather than using a pipe. */
+ iocb_data->server_fd = _gpgme_data_get_fd (iocb_data->data);
+ if (iocb_data->server_fd < 0)
+ {
+ int fds[2];
+
+ if (_gpgme_io_pipe (fds, dir) < 0)
+ return gpg_error_from_syserror ();
+
+ iocb_data->fd = dir ? fds[0] : fds[1];
+ iocb_data->server_fd = dir ? fds[1] : fds[0];
+
+ if (_gpgme_io_set_close_notify (iocb_data->fd,
+ close_notify_handler, gpgsm))
+ {
+ err = gpg_error (GPG_ERR_GENERAL);
+ goto leave_set_fd;
+ }
+ }
+
+ err = assuan_sendfd (gpgsm->assuan_ctx, iocb_data->server_fd);
+ if (err)
+ goto leave_set_fd;
+
+ _gpgme_io_close (iocb_data->server_fd);
+ iocb_data->server_fd = -1;
+
+ if (opt)
+ snprintf (line, COMMANDLINELEN, "%s FD %s", which, opt);
+ else
+ snprintf (line, COMMANDLINELEN, "%s FD", which);
+#else
+ if (opt)
+ snprintf (line, COMMANDLINELEN, "%s FD=%s %s",
+ which, iocb_data->server_fd_str, opt);
+ else
+ snprintf (line, COMMANDLINELEN, "%s FD=%s",
+ which, iocb_data->server_fd_str);
+#endif
+
+ err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, line, NULL, NULL);
+
+#if USE_DESCRIPTOR_PASSING
+ leave_set_fd:
+ if (err)
+ {
+ _gpgme_io_close (iocb_data->fd);
+ iocb_data->fd = -1;
+ if (iocb_data->server_fd != -1)
+ {
+ _gpgme_io_close (iocb_data->server_fd);
+ iocb_data->server_fd = -1;
+ }
+ }
+#endif
+
+ return err;
+}
+
+
+static const char *
+map_data_enc (gpgme_data_t d)
+{
+ switch (gpgme_data_get_encoding (d))
+ {
+ case GPGME_DATA_ENCODING_NONE:
+ break;
+ case GPGME_DATA_ENCODING_BINARY:
+ return "--binary";
+ case GPGME_DATA_ENCODING_BASE64:
+ return "--base64";
+ case GPGME_DATA_ENCODING_ARMOR:
+ return "--armor";
+ default:
+ break;
+ }
+ return NULL;
+}
+
+
+static gpgme_error_t
+status_handler (void *opaque, int fd)
+{
+ struct io_cb_data *data = (struct io_cb_data *) opaque;
+ engine_gpgsm_t gpgsm = (engine_gpgsm_t) data->handler_value;
+ gpgme_error_t err = 0;
+ char *line;
+ size_t linelen;
+
+ do
+ {
+ err = assuan_read_line (gpgsm->assuan_ctx, &line, &linelen);
+ if (err)
+ {
+ /* Try our best to terminate the connection friendly. */
+ /* assuan_write_line (gpgsm->assuan_ctx, "BYE"); */
+ TRACE3 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
+ "fd 0x%x: error from assuan (%d) getting status line : %s",
+ fd, err, gpg_strerror (err));
+ }
+ else if (linelen >= 3
+ && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
+ && (line[3] == '\0' || line[3] == ' '))
+ {
+ if (line[3] == ' ')
+ err = atoi (&line[4]);
+ if (! err)
+ err = gpg_error (GPG_ERR_GENERAL);
+ TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
+ "fd 0x%x: ERR line - mapped to: %s",
+ fd, err ? gpg_strerror (err) : "ok");
+ /* Try our best to terminate the connection friendly. */
+ /* assuan_write_line (gpgsm->assuan_ctx, "BYE"); */
+ }
+ else if (linelen >= 2
+ && line[0] == 'O' && line[1] == 'K'
+ && (line[2] == '\0' || line[2] == ' '))
+ {
+ if (gpgsm->status.fnc)
+ err = gpgsm->status.fnc (gpgsm->status.fnc_value,
+ GPGME_STATUS_EOF, "");
+
+ if (!err && gpgsm->colon.fnc && gpgsm->colon.any)
+ {
+ /* We must tell a colon function about the EOF. We do
+ this only when we have seen any data lines. Note
+ that this inlined use of colon data lines will
+ eventually be changed into using a regular data
+ channel. */
+ gpgsm->colon.any = 0;
+ err = gpgsm->colon.fnc (gpgsm->colon.fnc_value, NULL);
+ }
+ TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
+ "fd 0x%x: OK line - final status: %s",
+ fd, err ? gpg_strerror (err) : "ok");
+ _gpgme_io_close (gpgsm->status_cb.fd);
+ return err;
+ }
+ else if (linelen > 2
+ && line[0] == 'D' && line[1] == ' '
+ && gpgsm->colon.fnc)
+ {
+ /* We are using the colon handler even for plain inline data
+ - strange name for that function but for historic reasons
+ we keep it. */
+ /* FIXME We can't use this for binary data because we
+ assume this is a string. For the current usage of colon
+ output it is correct. */
+ char *src = line + 2;
+ char *end = line + linelen;
+ char *dst;
+ char **aline = &gpgsm->colon.attic.line;
+ int *alinelen = &gpgsm->colon.attic.linelen;
+
+ if (gpgsm->colon.attic.linesize < *alinelen + linelen + 1)
+ {
+ char *newline = realloc (*aline, *alinelen + linelen + 1);
+ if (!newline)
+ err = gpg_error_from_syserror ();
+ else
+ {
+ *aline = newline;
+ gpgsm->colon.attic.linesize += linelen + 1;
+ }
+ }
+ if (!err)
+ {
+ dst = *aline + *alinelen;
+
+ while (!err && src < end)
+ {
+ if (*src == '%' && src + 2 < end)
+ {
+ /* Handle escaped characters. */
+ ++src;
+ *dst = _gpgme_hextobyte (src);
+ (*alinelen)++;
+ src += 2;
+ }
+ else
+ {
+ *dst = *src++;
+ (*alinelen)++;
+ }
+
+ if (*dst == '\n')
+ {
+ /* Terminate the pending line, pass it to the colon
+ handler and reset it. */
+
+ gpgsm->colon.any = 1;
+ if (*alinelen > 1 && *(dst - 1) == '\r')
+ dst--;
+ *dst = '\0';
+
+ /* FIXME How should we handle the return code? */
+ err = gpgsm->colon.fnc (gpgsm->colon.fnc_value, *aline);
+ if (!err)
+ {
+ dst = *aline;
+ *alinelen = 0;
+ }
+ }
+ else
+ dst++;
+ }
+ }
+ TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
+ "fd 0x%x: D line; final status: %s",
+ fd, err? gpg_strerror (err):"ok");
+ }
+ else if (linelen > 2
+ && line[0] == 'D' && line[1] == ' '
+ && gpgsm->inline_data)
+ {
+ char *src = line + 2;
+ char *end = line + linelen;
+ char *dst = src;
+ ssize_t nwritten;
+
+ linelen = 0;
+ while (src < end)
+ {
+ if (*src == '%' && src + 2 < end)
+ {
+ /* Handle escaped characters. */
+ ++src;
+ *dst++ = _gpgme_hextobyte (src);
+ src += 2;
+ }
+ else
+ *dst++ = *src++;
+
+ linelen++;
+ }
+
+ src = line + 2;
+ while (linelen > 0)
+ {
+ nwritten = gpgme_data_write (gpgsm->inline_data, src, linelen);
+ if (!nwritten || (nwritten < 0 && errno != EINTR)
+ || nwritten > linelen)
+ {
+ err = gpg_error_from_syserror ();
+ break;
+ }
+ src += nwritten;
+ linelen -= nwritten;
+ }
+
+ TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
+ "fd 0x%x: D inlinedata; final status: %s",
+ fd, err? gpg_strerror (err):"ok");
+ }
+ else if (linelen > 2
+ && line[0] == 'S' && line[1] == ' ')
+ {
+ char *rest;
+ gpgme_status_code_t r;
+
+ rest = strchr (line + 2, ' ');
+ if (!rest)
+ rest = line + linelen; /* set to an empty string */
+ else
+ *(rest++) = 0;
+
+ r = _gpgme_parse_status (line + 2);
+
+ if (r >= 0)
+ {
+ if (gpgsm->status.fnc)
+ err = gpgsm->status.fnc (gpgsm->status.fnc_value, r, rest);
+ }
+ else
+ fprintf (stderr, "[UNKNOWN STATUS]%s %s", line + 2, rest);
+ TRACE3 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
+ "fd 0x%x: S line (%s) - final status: %s",
+ fd, line+2, err? gpg_strerror (err):"ok");
+ }
+ else if (linelen >= 7
+ && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
+ && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
+ && line[6] == 'E'
+ && (line[7] == '\0' || line[7] == ' '))
+ {
+ char *keyword = line+7;
+
+ while (*keyword == ' ')
+ keyword++;;
+ default_inq_cb (gpgsm, keyword);
+ assuan_write_line (gpgsm->assuan_ctx, "END");
+ }
+
+ }
+ while (!err && assuan_pending_line (gpgsm->assuan_ctx));
+
+ return err;
+}
+
+
+static gpgme_error_t
+add_io_cb (engine_gpgsm_t gpgsm, iocb_data_t *iocbd, gpgme_io_cb_t handler)
+{
+ gpgme_error_t err;
+
+ TRACE_BEG2 (DEBUG_ENGINE, "engine-gpgsm:add_io_cb", gpgsm,
+ "fd %d, dir %d", iocbd->fd, iocbd->dir);
+ err = (*gpgsm->io_cbs.add) (gpgsm->io_cbs.add_priv,
+ iocbd->fd, iocbd->dir,
+ handler, iocbd->data, &iocbd->tag);
+ if (err)
+ return TRACE_ERR (err);
+ if (!iocbd->dir)
+ /* FIXME Kludge around poll() problem. */
+ err = _gpgme_io_set_nonblocking (iocbd->fd);
+ return TRACE_ERR (err);
+}
+
+
+static gpgme_error_t
+start (engine_gpgsm_t gpgsm, const char *command)
+{
+ gpgme_error_t err;
+ assuan_fd_t afdlist[5];
+ int fdlist[5];
+ int nfds;
+ int i;
+
+ /* We need to know the fd used by assuan for reads. We do this by
+ using the assumption that the first returned fd from
+ assuan_get_active_fds() is always this one. */
+ nfds = assuan_get_active_fds (gpgsm->assuan_ctx, 0 /* read fds */,
+ afdlist, DIM (afdlist));
+ if (nfds < 1)
+ return gpg_error (GPG_ERR_GENERAL); /* FIXME */
+ /* For now... */
+ for (i = 0; i < nfds; i++)
+ fdlist[i] = (int) afdlist[i];
+
+ /* We "duplicate" the file descriptor, so we can close it here (we
+ can't close fdlist[0], as that is closed by libassuan, and
+ closing it here might cause libassuan to close some unrelated FD
+ later). Alternatively, we could special case status_fd and
+ register/unregister it manually as needed, but this increases
+ code duplication and is more complicated as we can not use the
+ close notifications etc. A third alternative would be to let
+ Assuan know that we closed the FD, but that complicates the
+ Assuan interface. */
+
+ gpgsm->status_cb.fd = _gpgme_io_dup (fdlist[0]);
+ if (gpgsm->status_cb.fd < 0)
+ return gpg_error_from_syserror ();
+
+ if (_gpgme_io_set_close_notify (gpgsm->status_cb.fd,
+ close_notify_handler, gpgsm))
+ {
+ _gpgme_io_close (gpgsm->status_cb.fd);
+ gpgsm->status_cb.fd = -1;
+ return gpg_error (GPG_ERR_GENERAL);
+ }
+
+ err = add_io_cb (gpgsm, &gpgsm->status_cb, status_handler);
+ if (!err && gpgsm->input_cb.fd != -1)
+ err = add_io_cb (gpgsm, &gpgsm->input_cb, _gpgme_data_outbound_handler);
+ if (!err && gpgsm->output_cb.fd != -1)
+ err = add_io_cb (gpgsm, &gpgsm->output_cb, _gpgme_data_inbound_handler);
+ if (!err && gpgsm->message_cb.fd != -1)
+ err = add_io_cb (gpgsm, &gpgsm->message_cb, _gpgme_data_outbound_handler);
+
+ if (!err)
+ err = assuan_write_line (gpgsm->assuan_ctx, command);
+
+ if (!err)
+ gpgsm_io_event (gpgsm, GPGME_EVENT_START, NULL);
+
+ return err;
+}
+
+
+#if USE_DESCRIPTOR_PASSING
+static gpgme_error_t
+gpgsm_reset (void *engine)
+{
+ engine_gpgsm_t gpgsm = engine;
+
+ /* IF we have an active connection we must send a reset because we
+ need to reset the list of signers. Note that RESET does not
+ reset OPTION commands. */
+ return (gpgsm->assuan_ctx
+ ? gpgsm_assuan_simple_command (gpgsm->assuan_ctx, "RESET",
+ NULL, NULL)
+ : 0);
+}
+#endif
+
+
+
+static gpgme_error_t
+gpgsm_decrypt (void *engine, gpgme_data_t ciph, gpgme_data_t plain)
+{
+ engine_gpgsm_t gpgsm = engine;
+ gpgme_error_t err;
+
+ if (!gpgsm)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ gpgsm->input_cb.data = ciph;
+ err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
+ if (err)
+ return gpg_error (GPG_ERR_GENERAL); /* FIXME */
+ gpgsm->output_cb.data = plain;
+ err = gpgsm_set_fd (gpgsm, OUTPUT_FD, 0);
+ if (err)
+ return gpg_error (GPG_ERR_GENERAL); /* FIXME */
+ gpgsm_clear_fd (gpgsm, MESSAGE_FD);
+ gpgsm->inline_data = NULL;
+
+ err = start (engine, "DECRYPT");
+ return err;
+}
+
+
+static gpgme_error_t
+gpgsm_delete (void *engine, gpgme_key_t key, int allow_secret)
+{
+ engine_gpgsm_t gpgsm = engine;
+ gpgme_error_t err;
+ char *fpr = key->subkeys ? key->subkeys->fpr : NULL;
+ char *linep = fpr;
+ char *line;
+ int length = 8; /* "DELKEYS " */
+
+ if (!fpr)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ while (*linep)
+ {
+ length++;
+ if (*linep == '%' || *linep == ' ' || *linep == '+')
+ length += 2;
+ linep++;
+ }
+ length++;
+
+ line = malloc (length);
+ if (!line)
+ return gpg_error_from_syserror ();
+
+ strcpy (line, "DELKEYS ");
+ linep = &line[8];
+
+ while (*fpr)
+ {
+ switch (*fpr)
+ {
+ case '%':
+ *(linep++) = '%';
+ *(linep++) = '2';
+ *(linep++) = '5';
+ break;
+ case ' ':
+ *(linep++) = '%';
+ *(linep++) = '2';
+ *(linep++) = '0';
+ break;
+ case '+':
+ *(linep++) = '%';
+ *(linep++) = '2';
+ *(linep++) = 'B';
+ break;
+ default:
+ *(linep++) = *fpr;
+ break;
+ }
+ fpr++;
+ }
+ *linep = '\0';
+
+ gpgsm_clear_fd (gpgsm, OUTPUT_FD);
+ gpgsm_clear_fd (gpgsm, INPUT_FD);
+ gpgsm_clear_fd (gpgsm, MESSAGE_FD);
+ gpgsm->inline_data = NULL;
+
+ err = start (gpgsm, line);
+ free (line);
+
+ return err;
+}
+
+
+static gpgme_error_t
+set_recipients (engine_gpgsm_t gpgsm, gpgme_key_t recp[])
+{
+ gpgme_error_t err = 0;
+ assuan_context_t ctx = gpgsm->assuan_ctx;
+ char *line;
+ int linelen;
+ int invalid_recipients = 0;
+ int i;
+
+ linelen = 10 + 40 + 1; /* "RECIPIENT " + guess + '\0'. */
+ line = malloc (10 + 40 + 1);
+ if (!line)
+ return gpg_error_from_syserror ();
+ strcpy (line, "RECIPIENT ");
+ for (i =0; !err && recp[i]; i++)
+ {
+ char *fpr;
+ int newlen;
+
+ if (!recp[i]->subkeys || !recp[i]->subkeys->fpr)
+ {
+ invalid_recipients++;
+ continue;
+ }
+ fpr = recp[i]->subkeys->fpr;
+
+ newlen = 11 + strlen (fpr);
+ if (linelen < newlen)
+ {
+ char *newline = realloc (line, newlen);
+ if (! newline)
+ {
+ int saved_errno = errno;
+ free (line);
+ return gpg_error_from_errno (saved_errno);
+ }
+ line = newline;
+ linelen = newlen;
+ }
+ strcpy (&line[10], fpr);
+
+ err = gpgsm_assuan_simple_command (ctx, line, gpgsm->status.fnc,
+ gpgsm->status.fnc_value);
+ /* FIXME: This requires more work. */
+ if (gpg_err_code (err) == GPG_ERR_NO_PUBKEY)
+ invalid_recipients++;
+ else if (err)
+ {
+ free (line);
+ return err;
+ }
+ }
+ free (line);
+ return gpg_error (invalid_recipients
+ ? GPG_ERR_UNUSABLE_PUBKEY : GPG_ERR_NO_ERROR);
+}
+
+
+static gpgme_error_t
+gpgsm_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags,
+ gpgme_data_t plain, gpgme_data_t ciph, int use_armor)
+{
+ engine_gpgsm_t gpgsm = engine;
+ gpgme_error_t err;
+
+ if (!gpgsm)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (!recp)
+ return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+
+ if (flags & GPGME_ENCRYPT_NO_ENCRYPT_TO)
+ {
+ err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx,
+ "OPTION no-encrypt-to", NULL, NULL);
+ if (err)
+ return err;
+ }
+
+ gpgsm->input_cb.data = plain;
+ err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
+ if (err)
+ return err;
+ gpgsm->output_cb.data = ciph;
+ err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor"
+ : map_data_enc (gpgsm->output_cb.data));
+ if (err)
+ return err;
+ gpgsm_clear_fd (gpgsm, MESSAGE_FD);
+ gpgsm->inline_data = NULL;
+
+ err = set_recipients (gpgsm, recp);
+
+ if (!err)
+ err = start (gpgsm, "ENCRYPT");
+
+ return err;
+}
+
+
+static gpgme_error_t
+gpgsm_export (void *engine, const char *pattern, gpgme_export_mode_t mode,
+ gpgme_data_t keydata, int use_armor)
+{
+ engine_gpgsm_t gpgsm = engine;
+ gpgme_error_t err = 0;
+ char *cmd;
+
+ if (!gpgsm)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if (mode)
+ return gpg_error (GPG_ERR_NOT_SUPPORTED);
+
+ if (!pattern)
+ pattern = "";
+
+ cmd = malloc (7 + strlen (pattern) + 1);
+ if (!cmd)
+ return gpg_error_from_syserror ();
+ strcpy (cmd, "EXPORT ");
+ strcpy (&cmd[7], pattern);
+
+ gpgsm->output_cb.data = keydata;
+ err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor"
+ : map_data_enc (gpgsm->output_cb.data));
+ if (err)
+ return err;
+ gpgsm_clear_fd (gpgsm, INPUT_FD);
+ gpgsm_clear_fd (gpgsm, MESSAGE_FD);
+ gpgsm->inline_data = NULL;
+
+ err = start (gpgsm, cmd);
+ free (cmd);
+ return err;
+}
+
+
+static gpgme_error_t
+gpgsm_export_ext (void *engine, const char *pattern[], gpgme_export_mode_t mode,
+ gpgme_data_t keydata, int use_armor)
+{
+ engine_gpgsm_t gpgsm = engine;
+ gpgme_error_t err = 0;
+ char *line;
+ /* Length is "EXPORT " + p + '\0'. */
+ int length = 7 + 1;
+ char *linep;
+
+ if (!gpgsm)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if (mode)
+ return gpg_error (GPG_ERR_NOT_SUPPORTED);
+
+ if (pattern && *pattern)
+ {
+ const char **pat = pattern;
+
+ while (*pat)
+ {
+ const char *patlet = *pat;
+
+ while (*patlet)
+ {
+ length++;
+ if (*patlet == '%' || *patlet == ' ' || *patlet == '+')
+ length += 2;
+ patlet++;
+ }
+ pat++;
+ length++;
+ }
+ }
+ line = malloc (length);
+ if (!line)
+ return gpg_error_from_syserror ();
+
+ strcpy (line, "EXPORT ");
+ linep = &line[7];
+
+ if (pattern && *pattern)
+ {
+ while (*pattern)
+ {
+ const char *patlet = *pattern;
+
+ while (*patlet)
+ {
+ switch (*patlet)
+ {
+ case '%':
+ *(linep++) = '%';
+ *(linep++) = '2';
+ *(linep++) = '5';
+ break;
+ case ' ':
+ *(linep++) = '%';
+ *(linep++) = '2';
+ *(linep++) = '0';
+ break;
+ case '+':
+ *(linep++) = '%';
+ *(linep++) = '2';
+ *(linep++) = 'B';
+ break;
+ default:
+ *(linep++) = *patlet;
+ break;
+ }
+ patlet++;
+ }
+ pattern++;
+ if (*pattern)
+ *linep++ = ' ';
+ }
+ }
+ *linep = '\0';
+
+ gpgsm->output_cb.data = keydata;
+ err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor"
+ : map_data_enc (gpgsm->output_cb.data));
+ if (err)
+ return err;
+ gpgsm_clear_fd (gpgsm, INPUT_FD);
+ gpgsm_clear_fd (gpgsm, MESSAGE_FD);
+ gpgsm->inline_data = NULL;
+
+ err = start (gpgsm, line);
+ free (line);
+ return err;
+}
+
+
+static gpgme_error_t
+gpgsm_genkey (void *engine, gpgme_data_t help_data, int use_armor,
+ gpgme_data_t pubkey, gpgme_data_t seckey)
+{
+ engine_gpgsm_t gpgsm = engine;
+ gpgme_error_t err;
+
+ if (!gpgsm || !pubkey || seckey)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ gpgsm->input_cb.data = help_data;
+ err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
+ if (err)
+ return err;
+ gpgsm->output_cb.data = pubkey;
+ err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor"
+ : map_data_enc (gpgsm->output_cb.data));
+ if (err)
+ return err;
+ gpgsm_clear_fd (gpgsm, MESSAGE_FD);
+ gpgsm->inline_data = NULL;
+
+ err = start (gpgsm, "GENKEY");
+ return err;
+}
+
+
+static gpgme_error_t
+gpgsm_import (void *engine, gpgme_data_t keydata, gpgme_key_t *keyarray)
+{
+ engine_gpgsm_t gpgsm = engine;
+ gpgme_error_t err;
+ gpgme_data_encoding_t dataenc;
+ int idx;
+
+ if (!gpgsm)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if (keydata && keyarray)
+ return gpg_error (GPG_ERR_INV_VALUE); /* Only one is allowed. */
+
+ dataenc = gpgme_data_get_encoding (keydata);
+
+ if (keyarray)
+ {
+ size_t buflen;
+ char *buffer, *p;
+
+ /* Fist check whether the engine already features the
+ --re-import option. */
+ err = gpgsm_assuan_simple_command
+ (gpgsm->assuan_ctx,
+ "GETINFO cmd_has_option IMPORT re-import", NULL, NULL);
+ if (err)
+ return gpg_error (GPG_ERR_NOT_SUPPORTED);
+
+ /* Create an internal data object with a list of all
+ fingerprints. The data object and its memory (to avoid an
+ extra copy by gpgme_data_new_from_mem) are stored in two
+ variables which are released by the close_notify_handler. */
+ for (idx=0, buflen=0; keyarray[idx]; idx++)
+ {
+ if (keyarray[idx]->protocol == GPGME_PROTOCOL_CMS
+ && keyarray[idx]->subkeys
+ && keyarray[idx]->subkeys->fpr
+ && *keyarray[idx]->subkeys->fpr)
+ buflen += strlen (keyarray[idx]->subkeys->fpr) + 1;
+ }
+ /* Allocate a bufer with extra space for the trailing Nul
+ introduced by the use of stpcpy. */
+ buffer = malloc (buflen+1);
+ if (!buffer)
+ return gpg_error_from_syserror ();
+ for (idx=0, p = buffer; keyarray[idx]; idx++)
+ {
+ if (keyarray[idx]->protocol == GPGME_PROTOCOL_CMS
+ && keyarray[idx]->subkeys
+ && keyarray[idx]->subkeys->fpr
+ && *keyarray[idx]->subkeys->fpr)
+ p = stpcpy (stpcpy (p, keyarray[idx]->subkeys->fpr), "\n");
+ }
+
+ err = gpgme_data_new_from_mem (&gpgsm->input_helper_data,
+ buffer, buflen, 0);
+ if (err)
+ {
+ free (buffer);
+ return err;
+ }
+ gpgsm->input_helper_memory = buffer;
+
+ gpgsm->input_cb.data = gpgsm->input_helper_data;
+ err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
+ if (err)
+ {
+ gpgme_data_release (gpgsm->input_helper_data);
+ gpgsm->input_helper_data = NULL;
+ free (gpgsm->input_helper_memory);
+ gpgsm->input_helper_memory = NULL;
+ return err;
+ }
+ gpgsm_clear_fd (gpgsm, OUTPUT_FD);
+ gpgsm_clear_fd (gpgsm, MESSAGE_FD);
+ gpgsm->inline_data = NULL;
+
+ return start (gpgsm, "IMPORT --re-import");
+ }
+ else if (dataenc == GPGME_DATA_ENCODING_URL
+ || dataenc == GPGME_DATA_ENCODING_URL0
+ || dataenc == GPGME_DATA_ENCODING_URLESC)
+ {
+ return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+ }
+ else
+ {
+ gpgsm->input_cb.data = keydata;
+ err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
+ if (err)
+ return err;
+ gpgsm_clear_fd (gpgsm, OUTPUT_FD);
+ gpgsm_clear_fd (gpgsm, MESSAGE_FD);
+ gpgsm->inline_data = NULL;
+
+ return start (gpgsm, "IMPORT");
+ }
+}
+
+
+static gpgme_error_t
+gpgsm_keylist (void *engine, const char *pattern, int secret_only,
+ gpgme_keylist_mode_t mode)
+{
+ engine_gpgsm_t gpgsm = engine;
+ char *line;
+ gpgme_error_t err;
+ int list_mode = 0;
+
+ if (mode & GPGME_KEYLIST_MODE_LOCAL)
+ list_mode |= 1;
+ if (mode & GPGME_KEYLIST_MODE_EXTERN)
+ list_mode |= 2;
+
+ if (!pattern)
+ pattern = "";
+
+ /* Hack to make sure that the agent is started. Only if the agent
+ has been started an application may connect to the agent via
+ GPGME_PROTOCOL_ASSUAN - for example to look for smartcards. We
+ do this only if a secret key listing has been requested. In
+ general this is not needed because a secret key listing starts
+ the agent. However on a fresh installation no public keys are
+ available and thus there is no need for gpgsm to ask the agent
+ whether a secret key exists for the public key. */
+ if (secret_only)
+ gpgsm_assuan_simple_command (gpgsm->assuan_ctx, "GETINFO agent-check",
+ NULL, NULL);
+
+ /* Always send list-mode option because RESET does not reset it. */
+ if (asprintf (&line, "OPTION list-mode=%d", (list_mode & 3)) < 0)
+ return gpg_error_from_syserror ();
+ err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, line, NULL, NULL);
+ free (line);
+ if (err)
+ return err;
+
+
+ /* Always send key validation because RESET does not reset it. */
+
+ /* Use the validation mode if requested. We don't check for an error
+ yet because this is a pretty fresh gpgsm features. */
+ gpgsm_assuan_simple_command (gpgsm->assuan_ctx,
+ (mode & GPGME_KEYLIST_MODE_VALIDATE)?
+ "OPTION with-validation=1":
+ "OPTION with-validation=0" ,
+ NULL, NULL);
+ /* Include the ephemeral keys if requested. We don't check for an error
+ yet because this is a pretty fresh gpgsm features. */
+ gpgsm_assuan_simple_command (gpgsm->assuan_ctx,
+ (mode & GPGME_KEYLIST_MODE_EPHEMERAL)?
+ "OPTION with-ephemeral-keys=1":
+ "OPTION with-ephemeral-keys=0" ,
+ NULL, NULL);
+
+
+ /* Length is "LISTSECRETKEYS " + p + '\0'. */
+ line = malloc (15 + strlen (pattern) + 1);
+ if (!line)
+ return gpg_error_from_syserror ();
+ if (secret_only)
+ {
+ strcpy (line, "LISTSECRETKEYS ");
+ strcpy (&line[15], pattern);
+ }
+ else
+ {
+ strcpy (line, "LISTKEYS ");
+ strcpy (&line[9], pattern);
+ }
+
+ gpgsm_clear_fd (gpgsm, INPUT_FD);
+ gpgsm_clear_fd (gpgsm, OUTPUT_FD);
+ gpgsm_clear_fd (gpgsm, MESSAGE_FD);
+ gpgsm->inline_data = NULL;
+
+ err = start (gpgsm, line);
+ free (line);
+ return err;
+}
+
+
+static gpgme_error_t
+gpgsm_keylist_ext (void *engine, const char *pattern[], int secret_only,
+ int reserved, gpgme_keylist_mode_t mode)
+{
+ engine_gpgsm_t gpgsm = engine;
+ char *line;
+ gpgme_error_t err;
+ /* Length is "LISTSECRETKEYS " + p + '\0'. */
+ int length = 15 + 1;
+ char *linep;
+ int any_pattern = 0;
+ int list_mode = 0;
+
+ if (reserved)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if (mode & GPGME_KEYLIST_MODE_LOCAL)
+ list_mode |= 1;
+ if (mode & GPGME_KEYLIST_MODE_EXTERN)
+ list_mode |= 2;
+
+ /* Always send list-mode option because RESET does not reset it. */
+ if (asprintf (&line, "OPTION list-mode=%d", (list_mode & 3)) < 0)
+ return gpg_error_from_syserror ();
+ err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, line, NULL, NULL);
+ free (line);
+ if (err)
+ return err;
+
+ /* Always send key validation because RESET does not reset it. */
+ /* Use the validation mode if required. We don't check for an error
+ yet because this is a pretty fresh gpgsm features. */
+ gpgsm_assuan_simple_command (gpgsm->assuan_ctx,
+ (mode & GPGME_KEYLIST_MODE_VALIDATE)?
+ "OPTION with-validation=1":
+ "OPTION with-validation=0" ,
+ NULL, NULL);
+
+
+ if (pattern && *pattern)
+ {
+ const char **pat = pattern;
+
+ while (*pat)
+ {
+ const char *patlet = *pat;
+
+ while (*patlet)
+ {
+ length++;
+ if (*patlet == '%' || *patlet == ' ' || *patlet == '+')
+ length += 2;
+ patlet++;
+ }
+ pat++;
+ length++;
+ }
+ }
+ line = malloc (length);
+ if (!line)
+ return gpg_error_from_syserror ();
+ if (secret_only)
+ {
+ strcpy (line, "LISTSECRETKEYS ");
+ linep = &line[15];
+ }
+ else
+ {
+ strcpy (line, "LISTKEYS ");
+ linep = &line[9];
+ }
+
+ if (pattern && *pattern)
+ {
+ while (*pattern)
+ {
+ const char *patlet = *pattern;
+
+ while (*patlet)
+ {
+ switch (*patlet)
+ {
+ case '%':
+ *(linep++) = '%';
+ *(linep++) = '2';
+ *(linep++) = '5';
+ break;
+ case ' ':
+ *(linep++) = '%';
+ *(linep++) = '2';
+ *(linep++) = '0';
+ break;
+ case '+':
+ *(linep++) = '%';
+ *(linep++) = '2';
+ *(linep++) = 'B';
+ break;
+ default:
+ *(linep++) = *patlet;
+ break;
+ }
+ patlet++;
+ }
+ any_pattern = 1;
+ *linep++ = ' ';
+ pattern++;
+ }
+ }
+ if (any_pattern)
+ linep--;
+ *linep = '\0';
+
+ gpgsm_clear_fd (gpgsm, INPUT_FD);
+ gpgsm_clear_fd (gpgsm, OUTPUT_FD);
+ gpgsm_clear_fd (gpgsm, MESSAGE_FD);
+ gpgsm->inline_data = NULL;
+
+ err = start (gpgsm, line);
+ free (line);
+ return err;
+}
+
+
+static gpgme_error_t
+gpgsm_sign (void *engine, gpgme_data_t in, gpgme_data_t out,
+ gpgme_sig_mode_t mode, int use_armor, int use_textmode,
+ int include_certs, gpgme_ctx_t ctx /* FIXME */)
+{
+ engine_gpgsm_t gpgsm = engine;
+ gpgme_error_t err;
+ char *assuan_cmd;
+ int i;
+ gpgme_key_t key;
+
+ if (!gpgsm)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ /* FIXME: This does not work as RESET does not reset it so we can't
+ revert back to default. */
+ if (include_certs != GPGME_INCLUDE_CERTS_DEFAULT)
+ {
+ /* FIXME: Make sure that if we run multiple operations, that we
+ can reset any previously set value in case the default is
+ requested. */
+
+ if (asprintf (&assuan_cmd, "OPTION include-certs %i", include_certs) < 0)
+ return gpg_error_from_syserror ();
+ err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, assuan_cmd,
+ NULL, NULL);
+ free (assuan_cmd);
+ if (err)
+ return err;
+ }
+
+ for (i = 0; (key = gpgme_signers_enum (ctx, i)); i++)
+ {
+ const char *s = key->subkeys ? key->subkeys->fpr : NULL;
+ if (s && strlen (s) < 80)
+ {
+ char buf[100];
+
+ strcpy (stpcpy (buf, "SIGNER "), s);
+ err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, buf,
+ gpgsm->status.fnc,
+ gpgsm->status.fnc_value);
+ }
+ else
+ err = gpg_error (GPG_ERR_INV_VALUE);
+ gpgme_key_unref (key);
+ if (err)
+ return err;
+ }
+
+ gpgsm->input_cb.data = in;
+ err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
+ if (err)
+ return err;
+ gpgsm->output_cb.data = out;
+ err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor"
+ : map_data_enc (gpgsm->output_cb.data));
+ if (err)
+ return err;
+ gpgsm_clear_fd (gpgsm, MESSAGE_FD);
+ gpgsm->inline_data = NULL;
+
+ err = start (gpgsm, mode == GPGME_SIG_MODE_DETACH
+ ? "SIGN --detached" : "SIGN");
+ return err;
+}
+
+
+static gpgme_error_t
+gpgsm_verify (void *engine, gpgme_data_t sig, gpgme_data_t signed_text,
+ gpgme_data_t plaintext)
+{
+ engine_gpgsm_t gpgsm = engine;
+ gpgme_error_t err;
+
+ if (!gpgsm)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ gpgsm->input_cb.data = sig;
+ err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
+ if (err)
+ return err;
+ if (plaintext)
+ {
+ /* Normal or cleartext signature. */
+ gpgsm->output_cb.data = plaintext;
+ err = gpgsm_set_fd (gpgsm, OUTPUT_FD, 0);
+ gpgsm_clear_fd (gpgsm, MESSAGE_FD);
+ }
+ else
+ {
+ /* Detached signature. */
+ gpgsm->message_cb.data = signed_text;
+ err = gpgsm_set_fd (gpgsm, MESSAGE_FD, 0);
+ gpgsm_clear_fd (gpgsm, OUTPUT_FD);
+ }
+ gpgsm->inline_data = NULL;
+
+ if (!err)
+ err = start (gpgsm, "VERIFY");
+
+ return err;
+}
+
+
+/* Send the GETAUDITLOG command. The result is saved to a gpgme data
+ object. */
+static gpgme_error_t
+gpgsm_getauditlog (void *engine, gpgme_data_t output, unsigned int flags)
+{
+ engine_gpgsm_t gpgsm = engine;
+ gpgme_error_t err = 0;
+
+ if (!gpgsm || !output)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+#if USE_DESCRIPTOR_PASSING
+ gpgsm->output_cb.data = output;
+ err = gpgsm_set_fd (gpgsm, OUTPUT_FD, 0);
+ if (err)
+ return err;
+
+ gpgsm_clear_fd (gpgsm, INPUT_FD);
+ gpgsm_clear_fd (gpgsm, MESSAGE_FD);
+ gpgsm->inline_data = NULL;
+# define CMD "GETAUDITLOG"
+#else
+ gpgsm_clear_fd (gpgsm, OUTPUT_FD);
+ gpgsm_clear_fd (gpgsm, INPUT_FD);
+ gpgsm_clear_fd (gpgsm, MESSAGE_FD);
+ gpgsm->inline_data = output;
+# define CMD "GETAUDITLOG --data"
+#endif
+
+ err = start (gpgsm, (flags & GPGME_AUDITLOG_HTML)? CMD " --html" : CMD);
+
+ return err;
+}
+
+
+
+static void
+gpgsm_set_status_handler (void *engine, engine_status_handler_t fnc,
+ void *fnc_value)
+{
+ engine_gpgsm_t gpgsm = engine;
+
+ gpgsm->status.fnc = fnc;
+ gpgsm->status.fnc_value = fnc_value;
+}
+
+
+static gpgme_error_t
+gpgsm_set_colon_line_handler (void *engine, engine_colon_line_handler_t fnc,
+ void *fnc_value)
+{
+ engine_gpgsm_t gpgsm = engine;
+
+ gpgsm->colon.fnc = fnc;
+ gpgsm->colon.fnc_value = fnc_value;
+ gpgsm->colon.any = 0;
+ return 0;
+}
+
+
+static void
+gpgsm_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
+{
+ engine_gpgsm_t gpgsm = engine;
+ gpgsm->io_cbs = *io_cbs;
+}
+
+
+static void
+gpgsm_io_event (void *engine, gpgme_event_io_t type, void *type_data)
+{
+ engine_gpgsm_t gpgsm = engine;
+
+ TRACE3 (DEBUG_ENGINE, "gpgme:gpgsm_io_event", gpgsm,
+ "event %p, type %d, type_data %p",
+ gpgsm->io_cbs.event, type, type_data);
+ if (gpgsm->io_cbs.event)
+ (*gpgsm->io_cbs.event) (gpgsm->io_cbs.event_priv, type, type_data);
+}
+
+
+static gpgme_error_t
+gpgsm_passwd (void *engine, gpgme_key_t key, unsigned int flags)
+{
+ engine_gpgsm_t gpgsm = engine;
+ gpgme_error_t err;
+ char *line;
+
+ if (!key || !key->subkeys || !key->subkeys->fpr)
+ return gpg_error (GPG_ERR_INV_CERT_OBJ);
+
+ if (asprintf (&line, "PASSWD -- %s", key->subkeys->fpr) < 0)
+ return gpg_error_from_syserror ();
+
+ gpgsm_clear_fd (gpgsm, OUTPUT_FD);
+ gpgsm_clear_fd (gpgsm, INPUT_FD);
+ gpgsm_clear_fd (gpgsm, MESSAGE_FD);
+ gpgsm->inline_data = NULL;
+
+ err = start (gpgsm, line);
+ free (line);
+
+ return err;
+}
+
+
+
+struct engine_ops _gpgme_engine_ops_gpgsm =
+ {
+ /* Static functions. */
+ _gpgme_get_gpgsm_path,
+ NULL,
+ gpgsm_get_version,
+ gpgsm_get_req_version,
+ gpgsm_new,
+
+ /* Member functions. */
+ gpgsm_release,
+#if USE_DESCRIPTOR_PASSING
+ gpgsm_reset,
+#else
+ NULL, /* reset */
+#endif
+ gpgsm_set_status_handler,
+ NULL, /* set_command_handler */
+ gpgsm_set_colon_line_handler,
+ gpgsm_set_locale,
+ NULL, /* set_protocol */
+ gpgsm_decrypt,
+ gpgsm_decrypt,
+ gpgsm_delete, /* decrypt_verify */
+ NULL, /* edit */
+ gpgsm_encrypt,
+ NULL, /* encrypt_sign */
+ gpgsm_export,
+ gpgsm_export_ext,
+ gpgsm_genkey,
+ gpgsm_import,
+ gpgsm_keylist,
+ gpgsm_keylist_ext,
+ gpgsm_sign,
+ NULL, /* trustlist */
+ gpgsm_verify,
+ gpgsm_getauditlog,
+ NULL, /* opassuan_transact */
+ NULL, /* conf_load */
+ NULL, /* conf_save */
+ gpgsm_set_io_cbs,
+ gpgsm_io_event,
+ gpgsm_cancel,
+ NULL, /* cancel_op */
+ gpgsm_passwd
+ };
diff --git a/src/engine-uiserver.c b/src/engine-uiserver.c
new file mode 100644
index 0000000..c705a4b
--- /dev/null
+++ b/src/engine-uiserver.c
@@ -0,0 +1,1343 @@
+/* engine-uiserver.c - Uiserver engine.
+ Copyright (C) 2000 Werner Koch (dd9jn)
+ Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2009 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+/* Peculiar: Use special keys from email address for recipient and
+ signer (==sender). Use no data objects with encryption for
+ prep_encrypt. */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#include <assert.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#include <locale.h>
+#include <fcntl.h> /* FIXME */
+#include <errno.h>
+
+#include "gpgme.h"
+#include "util.h"
+#include "ops.h"
+#include "wait.h"
+#include "priv-io.h"
+#include "sema.h"
+#include "data.h"
+
+#include "assuan.h"
+#include "status-table.h"
+#include "debug.h"
+
+#include "engine-backend.h"
+
+
+typedef struct
+{
+ int fd; /* FD we talk about. */
+ int server_fd;/* Server FD for this connection. */
+ int dir; /* Inbound/Outbound, maybe given implicit? */
+ void *data; /* Handler-specific data. */
+ void *tag; /* ID from the user for gpgme_remove_io_callback. */
+ char server_fd_str[15]; /* Same as SERVER_FD but as a string. We
+ need this because _gpgme_io_fd2str can't
+ be used on a closed descriptor. */
+} iocb_data_t;
+
+
+struct engine_uiserver
+{
+ assuan_context_t assuan_ctx;
+
+ int lc_ctype_set;
+ int lc_messages_set;
+ gpgme_protocol_t protocol;
+
+ iocb_data_t status_cb;
+
+ /* Input, output etc are from the servers perspective. */
+ iocb_data_t input_cb;
+ gpgme_data_t input_helper_data; /* Input helper data object. */
+ void *input_helper_memory; /* Input helper memory block. */
+
+ iocb_data_t output_cb;
+
+ iocb_data_t message_cb;
+
+ struct
+ {
+ engine_status_handler_t fnc;
+ void *fnc_value;
+ } status;
+
+ struct
+ {
+ engine_colon_line_handler_t fnc;
+ void *fnc_value;
+ struct
+ {
+ char *line;
+ int linesize;
+ int linelen;
+ } attic;
+ int any; /* any data line seen */
+ } colon;
+
+ gpgme_data_t inline_data; /* Used to collect D lines. */
+
+ struct gpgme_io_cbs io_cbs;
+};
+
+typedef struct engine_uiserver *engine_uiserver_t;
+
+
+static void uiserver_io_event (void *engine,
+ gpgme_event_io_t type, void *type_data);
+
+
+
+static char *
+uiserver_get_version (const char *file_name)
+{
+ return strdup ("1.0");
+}
+
+
+static const char *
+uiserver_get_req_version (void)
+{
+ return "1.0";
+}
+
+
+static void
+close_notify_handler (int fd, void *opaque)
+{
+ engine_uiserver_t uiserver = opaque;
+
+ assert (fd != -1);
+ if (uiserver->status_cb.fd == fd)
+ {
+ if (uiserver->status_cb.tag)
+ (*uiserver->io_cbs.remove) (uiserver->status_cb.tag);
+ uiserver->status_cb.fd = -1;
+ uiserver->status_cb.tag = NULL;
+ }
+ else if (uiserver->input_cb.fd == fd)
+ {
+ if (uiserver->input_cb.tag)
+ (*uiserver->io_cbs.remove) (uiserver->input_cb.tag);
+ uiserver->input_cb.fd = -1;
+ uiserver->input_cb.tag = NULL;
+ if (uiserver->input_helper_data)
+ {
+ gpgme_data_release (uiserver->input_helper_data);
+ uiserver->input_helper_data = NULL;
+ }
+ if (uiserver->input_helper_memory)
+ {
+ free (uiserver->input_helper_memory);
+ uiserver->input_helper_memory = NULL;
+ }
+ }
+ else if (uiserver->output_cb.fd == fd)
+ {
+ if (uiserver->output_cb.tag)
+ (*uiserver->io_cbs.remove) (uiserver->output_cb.tag);
+ uiserver->output_cb.fd = -1;
+ uiserver->output_cb.tag = NULL;
+ }
+ else if (uiserver->message_cb.fd == fd)
+ {
+ if (uiserver->message_cb.tag)
+ (*uiserver->io_cbs.remove) (uiserver->message_cb.tag);
+ uiserver->message_cb.fd = -1;
+ uiserver->message_cb.tag = NULL;
+ }
+}
+
+
+/* This is the default inquiry callback. We use it to handle the
+ Pinentry notifications. */
+static gpgme_error_t
+default_inq_cb (engine_uiserver_t uiserver, const char *line)
+{
+ if (!strncmp (line, "PINENTRY_LAUNCHED", 17) && (line[17]==' '||!line[17]))
+ {
+ _gpgme_allow_set_foreground_window ((pid_t)strtoul (line+17, NULL, 10));
+ }
+
+ return 0;
+}
+
+
+static gpgme_error_t
+uiserver_cancel (void *engine)
+{
+ engine_uiserver_t uiserver = engine;
+
+ if (!uiserver)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if (uiserver->status_cb.fd != -1)
+ _gpgme_io_close (uiserver->status_cb.fd);
+ if (uiserver->input_cb.fd != -1)
+ _gpgme_io_close (uiserver->input_cb.fd);
+ if (uiserver->output_cb.fd != -1)
+ _gpgme_io_close (uiserver->output_cb.fd);
+ if (uiserver->message_cb.fd != -1)
+ _gpgme_io_close (uiserver->message_cb.fd);
+
+ if (uiserver->assuan_ctx)
+ {
+ assuan_release (uiserver->assuan_ctx);
+ uiserver->assuan_ctx = NULL;
+ }
+
+ return 0;
+}
+
+
+static void
+uiserver_release (void *engine)
+{
+ engine_uiserver_t uiserver = engine;
+
+ if (!uiserver)
+ return;
+
+ uiserver_cancel (engine);
+
+ free (uiserver->colon.attic.line);
+ free (uiserver);
+}
+
+
+static gpgme_error_t
+uiserver_new (void **engine, const char *file_name, const char *home_dir)
+{
+ gpgme_error_t err = 0;
+ engine_uiserver_t uiserver;
+ char *dft_display = NULL;
+ char dft_ttyname[64];
+ char *dft_ttytype = NULL;
+ char *optstr;
+
+ uiserver = calloc (1, sizeof *uiserver);
+ if (!uiserver)
+ return gpg_error_from_syserror ();
+
+ uiserver->protocol = GPGME_PROTOCOL_DEFAULT;
+ uiserver->status_cb.fd = -1;
+ uiserver->status_cb.dir = 1;
+ uiserver->status_cb.tag = 0;
+ uiserver->status_cb.data = uiserver;
+
+ uiserver->input_cb.fd = -1;
+ uiserver->input_cb.dir = 0;
+ uiserver->input_cb.tag = 0;
+ uiserver->input_cb.server_fd = -1;
+ *uiserver->input_cb.server_fd_str = 0;
+ uiserver->output_cb.fd = -1;
+ uiserver->output_cb.dir = 1;
+ uiserver->output_cb.tag = 0;
+ uiserver->output_cb.server_fd = -1;
+ *uiserver->output_cb.server_fd_str = 0;
+ uiserver->message_cb.fd = -1;
+ uiserver->message_cb.dir = 0;
+ uiserver->message_cb.tag = 0;
+ uiserver->message_cb.server_fd = -1;
+ *uiserver->message_cb.server_fd_str = 0;
+
+ uiserver->status.fnc = 0;
+ uiserver->colon.fnc = 0;
+ uiserver->colon.attic.line = 0;
+ uiserver->colon.attic.linesize = 0;
+ uiserver->colon.attic.linelen = 0;
+ uiserver->colon.any = 0;
+
+ uiserver->inline_data = NULL;
+
+ uiserver->io_cbs.add = NULL;
+ uiserver->io_cbs.add_priv = NULL;
+ uiserver->io_cbs.remove = NULL;
+ uiserver->io_cbs.event = NULL;
+ uiserver->io_cbs.event_priv = NULL;
+
+ err = assuan_new_ext (&uiserver->assuan_ctx, GPG_ERR_SOURCE_GPGME,
+ &_gpgme_assuan_malloc_hooks, _gpgme_assuan_log_cb,
+ NULL);
+ if (err)
+ goto leave;
+ assuan_ctx_set_system_hooks (uiserver->assuan_ctx,
+ &_gpgme_assuan_system_hooks);
+
+ err = assuan_socket_connect (uiserver->assuan_ctx,
+ file_name ?
+ file_name : _gpgme_get_uiserver_socket_path (),
+ 0, ASSUAN_SOCKET_SERVER_FDPASSING);
+ if (err)
+ goto leave;
+
+ err = _gpgme_getenv ("DISPLAY", &dft_display);
+ if (err)
+ goto leave;
+ if (dft_display)
+ {
+ if (asprintf (&optstr, "OPTION display=%s", dft_display) < 0)
+ {
+ free (dft_display);
+ err = gpg_error_from_errno (errno);
+ goto leave;
+ }
+ free (dft_display);
+
+ err = assuan_transact (uiserver->assuan_ctx, optstr, NULL, NULL, NULL,
+ NULL, NULL, NULL);
+ free (optstr);
+ if (err)
+ goto leave;
+ }
+
+ if (isatty (1))
+ {
+ int rc;
+
+ rc = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname));
+ if (rc)
+ {
+ err = gpg_error_from_errno (rc);
+ goto leave;
+ }
+ else
+ {
+ if (asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0)
+ {
+ err = gpg_error_from_errno (errno);
+ goto leave;
+ }
+ err = assuan_transact (uiserver->assuan_ctx, optstr, NULL, NULL, NULL,
+ NULL, NULL, NULL);
+ free (optstr);
+ if (err)
+ goto leave;
+
+ err = _gpgme_getenv ("TERM", &dft_ttytype);
+ if (err)
+ goto leave;
+ if (dft_ttytype)
+ {
+ if (asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype) < 0)
+ {
+ free (dft_ttytype);
+ err = gpg_error_from_errno (errno);
+ goto leave;
+ }
+ free (dft_ttytype);
+
+ err = assuan_transact (uiserver->assuan_ctx, optstr, NULL, NULL,
+ NULL, NULL, NULL, NULL);
+ free (optstr);
+ if (err)
+ goto leave;
+ }
+ }
+ }
+
+#ifdef HAVE_W32_SYSTEM
+ /* Under Windows we need to use AllowSetForegroundWindow. Tell
+ uiserver to tell us when it needs it. */
+ if (!err)
+ {
+ err = assuan_transact (uiserver->assuan_ctx, "OPTION allow-pinentry-notify",
+ NULL, NULL, NULL, NULL, NULL, NULL);
+ if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
+ err = 0; /* This is a new feature of uiserver. */
+ }
+#endif /*HAVE_W32_SYSTEM*/
+
+ leave:
+ if (err)
+ uiserver_release (uiserver);
+ else
+ *engine = uiserver;
+
+ return err;
+}
+
+
+static gpgme_error_t
+uiserver_set_locale (void *engine, int category, const char *value)
+{
+ engine_uiserver_t uiserver = engine;
+ gpgme_error_t err;
+ char *optstr;
+ char *catstr;
+
+ /* FIXME: If value is NULL, we need to reset the option to default.
+ But we can't do this. So we error out here. UISERVER needs support
+ for this. */
+ if (category == LC_CTYPE)
+ {
+ catstr = "lc-ctype";
+ if (!value && uiserver->lc_ctype_set)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (value)
+ uiserver->lc_ctype_set = 1;
+ }
+#ifdef LC_MESSAGES
+ else if (category == LC_MESSAGES)
+ {
+ catstr = "lc-messages";
+ if (!value && uiserver->lc_messages_set)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (value)
+ uiserver->lc_messages_set = 1;
+ }
+#endif /* LC_MESSAGES */
+ else
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ /* FIXME: Reset value to default. */
+ if (!value)
+ return 0;
+
+ if (asprintf (&optstr, "OPTION %s=%s", catstr, value) < 0)
+ err = gpg_error_from_errno (errno);
+ else
+ {
+ err = assuan_transact (uiserver->assuan_ctx, optstr, NULL, NULL,
+ NULL, NULL, NULL, NULL);
+ free (optstr);
+ }
+
+ return err;
+}
+
+
+static gpgme_error_t
+uiserver_set_protocol (void *engine, gpgme_protocol_t protocol)
+{
+ engine_uiserver_t uiserver = engine;
+
+ if (protocol != GPGME_PROTOCOL_OpenPGP
+ && protocol != GPGME_PROTOCOL_CMS
+ && protocol != GPGME_PROTOCOL_DEFAULT)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ uiserver->protocol = protocol;
+ return 0;
+}
+
+
+static gpgme_error_t
+uiserver_assuan_simple_command (assuan_context_t ctx, char *cmd,
+ engine_status_handler_t status_fnc,
+ void *status_fnc_value)
+{
+ gpg_error_t err;
+ char *line;
+ size_t linelen;
+
+ err = assuan_write_line (ctx, cmd);
+ if (err)
+ return err;
+
+ do
+ {
+ err = assuan_read_line (ctx, &line, &linelen);
+ if (err)
+ return err;
+
+ if (*line == '#' || !linelen)
+ continue;
+
+ if (linelen >= 2
+ && line[0] == 'O' && line[1] == 'K'
+ && (line[2] == '\0' || line[2] == ' '))
+ return 0;
+ else if (linelen >= 4
+ && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
+ && line[3] == ' ')
+ err = atoi (&line[4]);
+ else if (linelen >= 2
+ && line[0] == 'S' && line[1] == ' ')
+ {
+ char *rest;
+ gpgme_status_code_t r;
+
+ rest = strchr (line + 2, ' ');
+ if (!rest)
+ rest = line + linelen; /* set to an empty string */
+ else
+ *(rest++) = 0;
+
+ r = _gpgme_parse_status (line + 2);
+
+ if (r >= 0 && status_fnc)
+ err = status_fnc (status_fnc_value, r, rest);
+ else
+ err = gpg_error (GPG_ERR_GENERAL);
+ }
+ else
+ err = gpg_error (GPG_ERR_GENERAL);
+ }
+ while (!err);
+
+ return err;
+}
+
+
+typedef enum { INPUT_FD, OUTPUT_FD, MESSAGE_FD } fd_type_t;
+
+#define COMMANDLINELEN 40
+static gpgme_error_t
+uiserver_set_fd (engine_uiserver_t uiserver, fd_type_t fd_type, const char *opt)
+{
+ gpg_error_t err = 0;
+ char line[COMMANDLINELEN];
+ char *which;
+ iocb_data_t *iocb_data;
+ int dir;
+
+ switch (fd_type)
+ {
+ case INPUT_FD:
+ which = "INPUT";
+ iocb_data = &uiserver->input_cb;
+ break;
+
+ case OUTPUT_FD:
+ which = "OUTPUT";
+ iocb_data = &uiserver->output_cb;
+ break;
+
+ case MESSAGE_FD:
+ which = "MESSAGE";
+ iocb_data = &uiserver->message_cb;
+ break;
+
+ default:
+ return gpg_error (GPG_ERR_INV_VALUE);
+ }
+
+ dir = iocb_data->dir;
+
+ /* We try to short-cut the communication by giving UISERVER direct
+ access to the file descriptor, rather than using a pipe. */
+ iocb_data->server_fd = _gpgme_data_get_fd (iocb_data->data);
+ if (iocb_data->server_fd < 0)
+ {
+ int fds[2];
+
+ if (_gpgme_io_pipe (fds, 0) < 0)
+ return gpg_error_from_errno (errno);
+
+ iocb_data->fd = dir ? fds[0] : fds[1];
+ iocb_data->server_fd = dir ? fds[1] : fds[0];
+
+ if (_gpgme_io_set_close_notify (iocb_data->fd,
+ close_notify_handler, uiserver))
+ {
+ err = gpg_error (GPG_ERR_GENERAL);
+ goto leave_set_fd;
+ }
+ }
+
+ err = assuan_sendfd (uiserver->assuan_ctx, iocb_data->server_fd);
+ if (err)
+ goto leave_set_fd;
+
+ _gpgme_io_close (iocb_data->server_fd);
+ iocb_data->server_fd = -1;
+
+ if (opt)
+ snprintf (line, COMMANDLINELEN, "%s FD %s", which, opt);
+ else
+ snprintf (line, COMMANDLINELEN, "%s FD", which);
+
+ err = uiserver_assuan_simple_command (uiserver->assuan_ctx, line, NULL, NULL);
+
+ leave_set_fd:
+ if (err)
+ {
+ _gpgme_io_close (iocb_data->fd);
+ iocb_data->fd = -1;
+ if (iocb_data->server_fd != -1)
+ {
+ _gpgme_io_close (iocb_data->server_fd);
+ iocb_data->server_fd = -1;
+ }
+ }
+
+ return err;
+}
+
+
+static const char *
+map_data_enc (gpgme_data_t d)
+{
+ switch (gpgme_data_get_encoding (d))
+ {
+ case GPGME_DATA_ENCODING_NONE:
+ break;
+ case GPGME_DATA_ENCODING_BINARY:
+ return "--binary";
+ case GPGME_DATA_ENCODING_BASE64:
+ return "--base64";
+ case GPGME_DATA_ENCODING_ARMOR:
+ return "--armor";
+ default:
+ break;
+ }
+ return NULL;
+}
+
+
+static gpgme_error_t
+status_handler (void *opaque, int fd)
+{
+ struct io_cb_data *data = (struct io_cb_data *) opaque;
+ engine_uiserver_t uiserver = (engine_uiserver_t) data->handler_value;
+ gpgme_error_t err = 0;
+ char *line;
+ size_t linelen;
+
+ do
+ {
+ err = assuan_read_line (uiserver->assuan_ctx, &line, &linelen);
+ if (err)
+ {
+ /* Try our best to terminate the connection friendly. */
+ /* assuan_write_line (uiserver->assuan_ctx, "BYE"); */
+ TRACE3 (DEBUG_CTX, "gpgme:status_handler", uiserver,
+ "fd 0x%x: error from assuan (%d) getting status line : %s",
+ fd, err, gpg_strerror (err));
+ }
+ else if (linelen >= 3
+ && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
+ && (line[3] == '\0' || line[3] == ' '))
+ {
+ if (line[3] == ' ')
+ err = atoi (&line[4]);
+ if (! err)
+ err = gpg_error (GPG_ERR_GENERAL);
+ TRACE2 (DEBUG_CTX, "gpgme:status_handler", uiserver,
+ "fd 0x%x: ERR line - mapped to: %s",
+ fd, err ? gpg_strerror (err) : "ok");
+ /* Try our best to terminate the connection friendly. */
+ /* assuan_write_line (uiserver->assuan_ctx, "BYE"); */
+ }
+ else if (linelen >= 2
+ && line[0] == 'O' && line[1] == 'K'
+ && (line[2] == '\0' || line[2] == ' '))
+ {
+ if (uiserver->status.fnc)
+ err = uiserver->status.fnc (uiserver->status.fnc_value,
+ GPGME_STATUS_EOF, "");
+
+ if (!err && uiserver->colon.fnc && uiserver->colon.any)
+ {
+ /* We must tell a colon function about the EOF. We do
+ this only when we have seen any data lines. Note
+ that this inlined use of colon data lines will
+ eventually be changed into using a regular data
+ channel. */
+ uiserver->colon.any = 0;
+ err = uiserver->colon.fnc (uiserver->colon.fnc_value, NULL);
+ }
+ TRACE2 (DEBUG_CTX, "gpgme:status_handler", uiserver,
+ "fd 0x%x: OK line - final status: %s",
+ fd, err ? gpg_strerror (err) : "ok");
+ _gpgme_io_close (uiserver->status_cb.fd);
+ return err;
+ }
+ else if (linelen > 2
+ && line[0] == 'D' && line[1] == ' '
+ && uiserver->colon.fnc)
+ {
+ /* We are using the colon handler even for plain inline data
+ - strange name for that function but for historic reasons
+ we keep it. */
+ /* FIXME We can't use this for binary data because we
+ assume this is a string. For the current usage of colon
+ output it is correct. */
+ char *src = line + 2;
+ char *end = line + linelen;
+ char *dst;
+ char **aline = &uiserver->colon.attic.line;
+ int *alinelen = &uiserver->colon.attic.linelen;
+
+ if (uiserver->colon.attic.linesize < *alinelen + linelen + 1)
+ {
+ char *newline = realloc (*aline, *alinelen + linelen + 1);
+ if (!newline)
+ err = gpg_error_from_errno (errno);
+ else
+ {
+ *aline = newline;
+ uiserver->colon.attic.linesize += linelen + 1;
+ }
+ }
+ if (!err)
+ {
+ dst = *aline + *alinelen;
+
+ while (!err && src < end)
+ {
+ if (*src == '%' && src + 2 < end)
+ {
+ /* Handle escaped characters. */
+ ++src;
+ *dst = _gpgme_hextobyte (src);
+ (*alinelen)++;
+ src += 2;
+ }
+ else
+ {
+ *dst = *src++;
+ (*alinelen)++;
+ }
+
+ if (*dst == '\n')
+ {
+ /* Terminate the pending line, pass it to the colon
+ handler and reset it. */
+
+ uiserver->colon.any = 1;
+ if (*alinelen > 1 && *(dst - 1) == '\r')
+ dst--;
+ *dst = '\0';
+
+ /* FIXME How should we handle the return code? */
+ err = uiserver->colon.fnc (uiserver->colon.fnc_value, *aline);
+ if (!err)
+ {
+ dst = *aline;
+ *alinelen = 0;
+ }
+ }
+ else
+ dst++;
+ }
+ }
+ TRACE2 (DEBUG_CTX, "gpgme:status_handler", uiserver,
+ "fd 0x%x: D line; final status: %s",
+ fd, err? gpg_strerror (err):"ok");
+ }
+ else if (linelen > 2
+ && line[0] == 'D' && line[1] == ' '
+ && uiserver->inline_data)
+ {
+ char *src = line + 2;
+ char *end = line + linelen;
+ char *dst = src;
+ ssize_t nwritten;
+
+ linelen = 0;
+ while (src < end)
+ {
+ if (*src == '%' && src + 2 < end)
+ {
+ /* Handle escaped characters. */
+ ++src;
+ *dst++ = _gpgme_hextobyte (src);
+ src += 2;
+ }
+ else
+ *dst++ = *src++;
+
+ linelen++;
+ }
+
+ src = line + 2;
+ while (linelen > 0)
+ {
+ nwritten = gpgme_data_write (uiserver->inline_data, src, linelen);
+ if (!nwritten || (nwritten < 0 && errno != EINTR)
+ || nwritten > linelen)
+ {
+ err = gpg_error_from_errno (errno);
+ break;
+ }
+ src += nwritten;
+ linelen -= nwritten;
+ }
+
+ TRACE2 (DEBUG_CTX, "gpgme:status_handler", uiserver,
+ "fd 0x%x: D inlinedata; final status: %s",
+ fd, err? gpg_strerror (err):"ok");
+ }
+ else if (linelen > 2
+ && line[0] == 'S' && line[1] == ' ')
+ {
+ char *rest;
+ gpgme_status_code_t r;
+
+ rest = strchr (line + 2, ' ');
+ if (!rest)
+ rest = line + linelen; /* set to an empty string */
+ else
+ *(rest++) = 0;
+
+ r = _gpgme_parse_status (line + 2);
+
+ if (r >= 0)
+ {
+ if (uiserver->status.fnc)
+ err = uiserver->status.fnc (uiserver->status.fnc_value, r, rest);
+ }
+ else
+ fprintf (stderr, "[UNKNOWN STATUS]%s %s", line + 2, rest);
+ TRACE3 (DEBUG_CTX, "gpgme:status_handler", uiserver,
+ "fd 0x%x: S line (%s) - final status: %s",
+ fd, line+2, err? gpg_strerror (err):"ok");
+ }
+ else if (linelen >= 7
+ && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
+ && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
+ && line[6] == 'E'
+ && (line[7] == '\0' || line[7] == ' '))
+ {
+ char *keyword = line+7;
+
+ while (*keyword == ' ')
+ keyword++;;
+ default_inq_cb (uiserver, keyword);
+ assuan_write_line (uiserver->assuan_ctx, "END");
+ }
+
+ }
+ while (!err && assuan_pending_line (uiserver->assuan_ctx));
+
+ return err;
+}
+
+
+static gpgme_error_t
+add_io_cb (engine_uiserver_t uiserver, iocb_data_t *iocbd, gpgme_io_cb_t handler)
+{
+ gpgme_error_t err;
+
+ TRACE_BEG2 (DEBUG_ENGINE, "engine-uiserver:add_io_cb", uiserver,
+ "fd %d, dir %d", iocbd->fd, iocbd->dir);
+ err = (*uiserver->io_cbs.add) (uiserver->io_cbs.add_priv,
+ iocbd->fd, iocbd->dir,
+ handler, iocbd->data, &iocbd->tag);
+ if (err)
+ return TRACE_ERR (err);
+ if (!iocbd->dir)
+ /* FIXME Kludge around poll() problem. */
+ err = _gpgme_io_set_nonblocking (iocbd->fd);
+ return TRACE_ERR (err);
+}
+
+
+static gpgme_error_t
+start (engine_uiserver_t uiserver, const char *command)
+{
+ gpgme_error_t err;
+ int fdlist[5];
+ int nfds;
+
+ /* We need to know the fd used by assuan for reads. We do this by
+ using the assumption that the first returned fd from
+ assuan_get_active_fds() is always this one. */
+ nfds = assuan_get_active_fds (uiserver->assuan_ctx, 0 /* read fds */,
+ fdlist, DIM (fdlist));
+ if (nfds < 1)
+ return gpg_error (GPG_ERR_GENERAL); /* FIXME */
+
+ /* We "duplicate" the file descriptor, so we can close it here (we
+ can't close fdlist[0], as that is closed by libassuan, and
+ closing it here might cause libassuan to close some unrelated FD
+ later). Alternatively, we could special case status_fd and
+ register/unregister it manually as needed, but this increases
+ code duplication and is more complicated as we can not use the
+ close notifications etc. A third alternative would be to let
+ Assuan know that we closed the FD, but that complicates the
+ Assuan interface. */
+
+ uiserver->status_cb.fd = _gpgme_io_dup (fdlist[0]);
+ if (uiserver->status_cb.fd < 0)
+ return gpg_error_from_syserror ();
+
+ if (_gpgme_io_set_close_notify (uiserver->status_cb.fd,
+ close_notify_handler, uiserver))
+ {
+ _gpgme_io_close (uiserver->status_cb.fd);
+ uiserver->status_cb.fd = -1;
+ return gpg_error (GPG_ERR_GENERAL);
+ }
+
+ err = add_io_cb (uiserver, &uiserver->status_cb, status_handler);
+ if (!err && uiserver->input_cb.fd != -1)
+ err = add_io_cb (uiserver, &uiserver->input_cb, _gpgme_data_outbound_handler);
+ if (!err && uiserver->output_cb.fd != -1)
+ err = add_io_cb (uiserver, &uiserver->output_cb, _gpgme_data_inbound_handler);
+ if (!err && uiserver->message_cb.fd != -1)
+ err = add_io_cb (uiserver, &uiserver->message_cb, _gpgme_data_outbound_handler);
+
+ if (!err)
+ err = assuan_write_line (uiserver->assuan_ctx, command);
+
+ if (!err)
+ uiserver_io_event (uiserver, GPGME_EVENT_START, NULL);
+
+ return err;
+}
+
+
+static gpgme_error_t
+uiserver_reset (void *engine)
+{
+ engine_uiserver_t uiserver = engine;
+
+ /* We must send a reset because we need to reset the list of
+ signers. Note that RESET does not reset OPTION commands. */
+ return uiserver_assuan_simple_command (uiserver->assuan_ctx, "RESET", NULL, NULL);
+}
+
+
+static gpgme_error_t
+_uiserver_decrypt (void *engine, int verify,
+ gpgme_data_t ciph, gpgme_data_t plain)
+{
+ engine_uiserver_t uiserver = engine;
+ gpgme_error_t err;
+ const char *protocol;
+ char *cmd;
+
+ if (!uiserver)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT)
+ protocol = "";
+ else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP)
+ protocol = " --protocol=OpenPGP";
+ else if (uiserver->protocol == GPGME_PROTOCOL_CMS)
+ protocol = " --protocol=CMS";
+ else
+ return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
+
+ if (asprintf (&cmd, "DECRYPT%s%s", protocol,
+ verify ? "" : " --no-verify") < 0)
+ return gpg_error_from_errno (errno);
+
+ uiserver->input_cb.data = ciph;
+ err = uiserver_set_fd (uiserver, INPUT_FD,
+ map_data_enc (uiserver->input_cb.data));
+ if (err)
+ {
+ free (cmd);
+ return gpg_error (GPG_ERR_GENERAL); /* FIXME */
+ }
+ uiserver->output_cb.data = plain;
+ err = uiserver_set_fd (uiserver, OUTPUT_FD, 0);
+ if (err)
+ {
+ free (cmd);
+ return gpg_error (GPG_ERR_GENERAL); /* FIXME */
+ }
+ uiserver->inline_data = NULL;
+
+ err = start (engine, cmd);
+ free (cmd);
+ return err;
+}
+
+
+static gpgme_error_t
+uiserver_decrypt (void *engine, gpgme_data_t ciph, gpgme_data_t plain)
+{
+ return _uiserver_decrypt (engine, 0, ciph, plain);
+}
+
+
+static gpgme_error_t
+uiserver_decrypt_verify (void *engine, gpgme_data_t ciph, gpgme_data_t plain)
+{
+ return _uiserver_decrypt (engine, 1, ciph, plain);
+}
+
+
+static gpgme_error_t
+set_recipients (engine_uiserver_t uiserver, gpgme_key_t recp[])
+{
+ gpgme_error_t err = 0;
+ assuan_context_t ctx = uiserver->assuan_ctx;
+ char *line;
+ int linelen;
+ int invalid_recipients = 0;
+ int i;
+
+ linelen = 10 + 40 + 1; /* "RECIPIENT " + guess + '\0'. */
+ line = malloc (10 + 40 + 1);
+ if (!line)
+ return gpg_error_from_errno (errno);
+ strcpy (line, "RECIPIENT ");
+ for (i=0; !err && recp[i]; i++)
+ {
+ char *uid;
+ int newlen;
+
+ /* We use only the first user ID of the key. */
+ if (!recp[i]->uids || !(uid=recp[i]->uids->uid) || !*uid)
+ {
+ invalid_recipients++;
+ continue;
+ }
+
+ newlen = 11 + strlen (uid);
+ if (linelen < newlen)
+ {
+ char *newline = realloc (line, newlen);
+ if (! newline)
+ {
+ int saved_errno = errno;
+ free (line);
+ return gpg_error_from_errno (saved_errno);
+ }
+ line = newline;
+ linelen = newlen;
+ }
+ /* FIXME: need to do proper escaping */
+ strcpy (&line[10], uid);
+
+ err = uiserver_assuan_simple_command (ctx, line, uiserver->status.fnc,
+ uiserver->status.fnc_value);
+ /* FIXME: This might requires more work. */
+ if (gpg_err_code (err) == GPG_ERR_NO_PUBKEY)
+ invalid_recipients++;
+ else if (err)
+ {
+ free (line);
+ return err;
+ }
+ }
+ free (line);
+ return gpg_error (invalid_recipients
+ ? GPG_ERR_UNUSABLE_PUBKEY : GPG_ERR_NO_ERROR);
+}
+
+
+static gpgme_error_t
+uiserver_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags,
+ gpgme_data_t plain, gpgme_data_t ciph, int use_armor)
+{
+ engine_uiserver_t uiserver = engine;
+ gpgme_error_t err;
+ const char *protocol;
+ char *cmd;
+
+ if (!uiserver)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT)
+ protocol = "";
+ else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP)
+ protocol = " --protocol=OpenPGP";
+ else if (uiserver->protocol == GPGME_PROTOCOL_CMS)
+ protocol = " --protocol=CMS";
+ else
+ return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
+
+ if (flags & GPGME_ENCRYPT_PREPARE)
+ {
+ if (!recp || plain || ciph)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if (asprintf (&cmd, "PREP_ENCRYPT%s%s", protocol,
+ (flags & GPGME_ENCRYPT_EXPECT_SIGN)
+ ? " --expect-sign" : "") < 0)
+ return gpg_error_from_errno (errno);
+ }
+ else
+ {
+ if (!plain || !ciph)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if (asprintf (&cmd, "ENCRYPT%s", protocol) < 0)
+ return gpg_error_from_errno (errno);
+ }
+
+ if (plain)
+ {
+ uiserver->input_cb.data = plain;
+ err = uiserver_set_fd (uiserver, INPUT_FD,
+ map_data_enc (uiserver->input_cb.data));
+ if (err)
+ {
+ free (cmd);
+ return err;
+ }
+ }
+
+ if (ciph)
+ {
+ uiserver->output_cb.data = ciph;
+ err = uiserver_set_fd (uiserver, OUTPUT_FD, use_armor ? "--armor"
+ : map_data_enc (uiserver->output_cb.data));
+ if (err)
+ {
+ free (cmd);
+ return err;
+ }
+ }
+
+ uiserver->inline_data = NULL;
+
+ if (recp)
+ {
+ err = set_recipients (uiserver, recp);
+ if (err)
+ {
+ free (cmd);
+ return err;
+ }
+ }
+
+ err = start (uiserver, cmd);
+ free (cmd);
+ return err;
+}
+
+
+static gpgme_error_t
+uiserver_sign (void *engine, gpgme_data_t in, gpgme_data_t out,
+ gpgme_sig_mode_t mode, int use_armor, int use_textmode,
+ int include_certs, gpgme_ctx_t ctx /* FIXME */)
+{
+ engine_uiserver_t uiserver = engine;
+ gpgme_error_t err = 0;
+ const char *protocol;
+ char *cmd;
+ gpgme_key_t key;
+
+ if (!uiserver || !in || !out)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT)
+ protocol = "";
+ else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP)
+ protocol = " --protocol=OpenPGP";
+ else if (uiserver->protocol == GPGME_PROTOCOL_CMS)
+ protocol = " --protocol=CMS";
+ else
+ return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
+
+ if (asprintf (&cmd, "SIGN%s%s", protocol,
+ (mode == GPGME_SIG_MODE_DETACH) ? " --detached" : "") < 0)
+ return gpg_error_from_errno (errno);
+
+ key = gpgme_signers_enum (ctx, 0);
+ if (key)
+ {
+ const char *s = NULL;
+
+ if (key && key->uids)
+ s = key->uids->email;
+
+ if (s && strlen (s) < 80)
+ {
+ char buf[100];
+
+ strcpy (stpcpy (buf, "SENDER --info "), s);
+ err = uiserver_assuan_simple_command (uiserver->assuan_ctx, buf,
+ uiserver->status.fnc,
+ uiserver->status.fnc_value);
+ }
+ else
+ err = gpg_error (GPG_ERR_INV_VALUE);
+ gpgme_key_unref (key);
+ if (err)
+ {
+ free (cmd);
+ return err;
+ }
+ }
+
+ uiserver->input_cb.data = in;
+ err = uiserver_set_fd (uiserver, INPUT_FD,
+ map_data_enc (uiserver->input_cb.data));
+ if (err)
+ {
+ free (cmd);
+ return err;
+ }
+ uiserver->output_cb.data = out;
+ err = uiserver_set_fd (uiserver, OUTPUT_FD, use_armor ? "--armor"
+ : map_data_enc (uiserver->output_cb.data));
+ if (err)
+ {
+ free (cmd);
+ return err;
+ }
+ uiserver->inline_data = NULL;
+
+ err = start (uiserver, cmd);
+ free (cmd);
+ return err;
+}
+
+
+/* FIXME: Missing a way to specify --silent. */
+static gpgme_error_t
+uiserver_verify (void *engine, gpgme_data_t sig, gpgme_data_t signed_text,
+ gpgme_data_t plaintext)
+{
+ engine_uiserver_t uiserver = engine;
+ gpgme_error_t err;
+ const char *protocol;
+ char *cmd;
+
+ if (!uiserver)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT)
+ protocol = "";
+ else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP)
+ protocol = " --protocol=OpenPGP";
+ else if (uiserver->protocol == GPGME_PROTOCOL_CMS)
+ protocol = " --protocol=CMS";
+ else
+ return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
+
+ if (asprintf (&cmd, "VERIFY%s", protocol) < 0)
+ return gpg_error_from_errno (errno);
+
+ uiserver->input_cb.data = sig;
+ err = uiserver_set_fd (uiserver, INPUT_FD,
+ map_data_enc (uiserver->input_cb.data));
+ if (err)
+ {
+ free (cmd);
+ return err;
+ }
+ if (plaintext)
+ {
+ /* Normal or cleartext signature. */
+ uiserver->output_cb.data = plaintext;
+ err = uiserver_set_fd (uiserver, OUTPUT_FD, 0);
+ }
+ else
+ {
+ /* Detached signature. */
+ uiserver->message_cb.data = signed_text;
+ err = uiserver_set_fd (uiserver, MESSAGE_FD, 0);
+ }
+ uiserver->inline_data = NULL;
+
+ if (!err)
+ err = start (uiserver, cmd);
+
+ free (cmd);
+ return err;
+}
+
+
+static void
+uiserver_set_status_handler (void *engine, engine_status_handler_t fnc,
+ void *fnc_value)
+{
+ engine_uiserver_t uiserver = engine;
+
+ uiserver->status.fnc = fnc;
+ uiserver->status.fnc_value = fnc_value;
+}
+
+
+static gpgme_error_t
+uiserver_set_colon_line_handler (void *engine, engine_colon_line_handler_t fnc,
+ void *fnc_value)
+{
+ engine_uiserver_t uiserver = engine;
+
+ uiserver->colon.fnc = fnc;
+ uiserver->colon.fnc_value = fnc_value;
+ uiserver->colon.any = 0;
+ return 0;
+}
+
+
+static void
+uiserver_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
+{
+ engine_uiserver_t uiserver = engine;
+ uiserver->io_cbs = *io_cbs;
+}
+
+
+static void
+uiserver_io_event (void *engine, gpgme_event_io_t type, void *type_data)
+{
+ engine_uiserver_t uiserver = engine;
+
+ TRACE3 (DEBUG_ENGINE, "gpgme:uiserver_io_event", uiserver,
+ "event %p, type %d, type_data %p",
+ uiserver->io_cbs.event, type, type_data);
+ if (uiserver->io_cbs.event)
+ (*uiserver->io_cbs.event) (uiserver->io_cbs.event_priv, type, type_data);
+}
+
+
+struct engine_ops _gpgme_engine_ops_uiserver =
+ {
+ /* Static functions. */
+ _gpgme_get_uiserver_socket_path,
+ NULL,
+ uiserver_get_version,
+ uiserver_get_req_version,
+ uiserver_new,
+
+ /* Member functions. */
+ uiserver_release,
+ uiserver_reset,
+ uiserver_set_status_handler,
+ NULL, /* set_command_handler */
+ uiserver_set_colon_line_handler,
+ uiserver_set_locale,
+ uiserver_set_protocol,
+ uiserver_decrypt,
+ uiserver_decrypt_verify,
+ NULL, /* delete */
+ NULL, /* edit */
+ uiserver_encrypt,
+ NULL, /* encrypt_sign */
+ NULL, /* export */
+ NULL, /* export_ext */
+ NULL, /* genkey */
+ NULL, /* import */
+ NULL, /* keylist */
+ NULL, /* keylist_ext */
+ uiserver_sign,
+ NULL, /* trustlist */
+ uiserver_verify,
+ NULL, /* getauditlog */
+ NULL, /* opassuan_transact */
+ NULL, /* conf_load */
+ NULL, /* conf_save */
+ uiserver_set_io_cbs,
+ uiserver_io_event,
+ uiserver_cancel,
+ NULL /* cancel_op */
+ };
diff --git a/src/engine.c b/src/engine.c
new file mode 100644
index 0000000..1244dd3
--- /dev/null
+++ b/src/engine.c
@@ -0,0 +1,924 @@
+/* engine.c - GPGME engine support.
+ Copyright (C) 2000 Werner Koch (dd9jn)
+ Copyright (C) 2001, 2002, 2003, 2004, 2006, 2009, 2010 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "gpgme.h"
+#include "util.h"
+#include "sema.h"
+#include "ops.h"
+
+#include "engine.h"
+#include "engine-backend.h"
+
+
+struct engine
+{
+ struct engine_ops *ops;
+ void *engine;
+};
+
+
+static struct engine_ops *engine_ops[] =
+ {
+ &_gpgme_engine_ops_gpg, /* OpenPGP. */
+#ifdef ENABLE_GPGSM
+ &_gpgme_engine_ops_gpgsm, /* CMS. */
+#else
+ NULL,
+#endif
+#ifdef ENABLE_GPGCONF
+ &_gpgme_engine_ops_gpgconf, /* gpg-conf. */
+#else
+ NULL,
+#endif
+#ifdef ENABLE_ASSUAN
+ &_gpgme_engine_ops_assuan, /* Low-Level Assuan. */
+#else
+ NULL,
+#endif
+#ifdef ENABLE_G13
+ &_gpgme_engine_ops_g13, /* Crypto VFS. */
+#else
+ NULL,
+#endif
+#ifdef ENABLE_UISERVER
+ &_gpgme_engine_ops_uiserver /* UI-Server. */
+#else
+ NULL
+#endif
+ };
+
+
+/* The engine info. */
+static gpgme_engine_info_t engine_info;
+DEFINE_STATIC_LOCK (engine_info_lock);
+
+
+/* Get the file name of the engine for PROTOCOL. */
+static const char *
+engine_get_file_name (gpgme_protocol_t proto)
+{
+ if (proto > DIM (engine_ops))
+ return NULL;
+
+ if (engine_ops[proto] && engine_ops[proto]->get_file_name)
+ return (*engine_ops[proto]->get_file_name) ();
+ else
+ return NULL;
+}
+
+
+/* Get the standard home dir of the engine for PROTOCOL. */
+static const char *
+engine_get_home_dir (gpgme_protocol_t proto)
+{
+ if (proto > DIM (engine_ops))
+ return NULL;
+
+ if (engine_ops[proto] && engine_ops[proto]->get_home_dir)
+ return (*engine_ops[proto]->get_home_dir) ();
+ else
+ return NULL;
+}
+
+
+/* Get a malloced string containing the version number of the engine
+ for PROTOCOL. */
+static char *
+engine_get_version (gpgme_protocol_t proto, const char *file_name)
+{
+ if (proto > DIM (engine_ops))
+ return NULL;
+
+ if (engine_ops[proto] && engine_ops[proto]->get_version)
+ return (*engine_ops[proto]->get_version) (file_name);
+ else
+ return NULL;
+}
+
+
+/* Get the required version number of the engine for PROTOCOL. */
+static const char *
+engine_get_req_version (gpgme_protocol_t proto)
+{
+ if (proto > DIM (engine_ops))
+ return NULL;
+
+ if (engine_ops[proto] && engine_ops[proto]->get_req_version)
+ return (*engine_ops[proto]->get_req_version) ();
+ else
+ return NULL;
+}
+
+
+/* Verify the version requirement for the engine for PROTOCOL. */
+gpgme_error_t
+gpgme_engine_check_version (gpgme_protocol_t proto)
+{
+ gpgme_error_t err;
+ gpgme_engine_info_t info;
+ int result;
+
+ LOCK (engine_info_lock);
+ info = engine_info;
+ if (!info)
+ {
+ /* Make sure it is initialized. */
+ UNLOCK (engine_info_lock);
+ err = gpgme_get_engine_info (&info);
+ if (err)
+ return err;
+
+ LOCK (engine_info_lock);
+ }
+
+ while (info && info->protocol != proto)
+ info = info->next;
+
+ if (!info)
+ result = 0;
+ else
+ result = _gpgme_compare_versions (info->version,
+ info->req_version);
+
+ UNLOCK (engine_info_lock);
+ return result ? 0 : gpg_error (GPG_ERR_INV_ENGINE);
+}
+
+
+/* Release the engine info INFO. */
+void
+_gpgme_engine_info_release (gpgme_engine_info_t info)
+{
+ while (info)
+ {
+ gpgme_engine_info_t next_info = info->next;
+
+ assert (info->file_name);
+ free (info->file_name);
+ if (info->home_dir)
+ free (info->home_dir);
+ if (info->version)
+ free (info->version);
+ free (info);
+ info = next_info;
+ }
+}
+
+
+/* Get the information about the configured and installed engines. A
+ pointer to the first engine in the statically allocated linked list
+ is returned in *INFO. If an error occurs, it is returned. The
+ returned data is valid until the next gpgme_set_engine_info. */
+gpgme_error_t
+gpgme_get_engine_info (gpgme_engine_info_t *info)
+{
+ LOCK (engine_info_lock);
+ if (!engine_info)
+ {
+ gpgme_engine_info_t *lastp = &engine_info;
+ gpgme_protocol_t proto_list[] = { GPGME_PROTOCOL_OpenPGP,
+ GPGME_PROTOCOL_CMS,
+ GPGME_PROTOCOL_GPGCONF,
+ GPGME_PROTOCOL_ASSUAN,
+ GPGME_PROTOCOL_G13,
+ GPGME_PROTOCOL_UISERVER };
+ unsigned int proto;
+
+ for (proto = 0; proto < DIM (proto_list); proto++)
+ {
+ const char *ofile_name = engine_get_file_name (proto_list[proto]);
+ const char *ohome_dir = engine_get_home_dir (proto_list[proto]);
+ char *file_name;
+ char *home_dir;
+
+ if (!ofile_name)
+ continue;
+
+ file_name = strdup (ofile_name);
+ home_dir = ohome_dir? strdup (ohome_dir): NULL;
+
+ *lastp = malloc (sizeof (*engine_info));
+ if (!*lastp || !file_name)
+ {
+ int saved_errno = errno;
+
+ _gpgme_engine_info_release (engine_info);
+ engine_info = NULL;
+
+ if (file_name)
+ free (file_name);
+ if (home_dir)
+ free (home_dir);
+
+ UNLOCK (engine_info_lock);
+ return gpg_error_from_errno (saved_errno);
+ }
+
+ (*lastp)->protocol = proto_list[proto];
+ (*lastp)->file_name = file_name;
+ (*lastp)->home_dir = home_dir;
+ (*lastp)->version = engine_get_version (proto_list[proto], NULL);
+ (*lastp)->req_version = engine_get_req_version (proto_list[proto]);
+ (*lastp)->next = NULL;
+ lastp = &(*lastp)->next;
+ }
+ }
+
+ *info = engine_info;
+ UNLOCK (engine_info_lock);
+ return 0;
+}
+
+
+/* Get a deep copy of the engine info and return it in INFO. */
+gpgme_error_t
+_gpgme_engine_info_copy (gpgme_engine_info_t *r_info)
+{
+ gpgme_error_t err = 0;
+ gpgme_engine_info_t info;
+ gpgme_engine_info_t new_info;
+ gpgme_engine_info_t *lastp;
+
+ LOCK (engine_info_lock);
+ info = engine_info;
+ if (!info)
+ {
+ /* Make sure it is initialized. */
+ UNLOCK (engine_info_lock);
+ err = gpgme_get_engine_info (&info);
+ if (err)
+ return err;
+
+ LOCK (engine_info_lock);
+ }
+
+ new_info = NULL;
+ lastp = &new_info;
+
+ while (info)
+ {
+ char *file_name;
+ char *home_dir;
+ char *version;
+
+ assert (info->file_name);
+ file_name = strdup (info->file_name);
+
+ if (info->home_dir)
+ {
+ home_dir = strdup (info->home_dir);
+ if (!home_dir)
+ err = gpg_error_from_errno (errno);
+ }
+ else
+ home_dir = NULL;
+
+ if (info->version)
+ {
+ version = strdup (info->version);
+ if (!version)
+ err = gpg_error_from_errno (errno);
+ }
+ else
+ version = NULL;
+
+ *lastp = malloc (sizeof (*engine_info));
+ if (!*lastp || !file_name || err)
+ {
+ int saved_errno = errno;
+
+ _gpgme_engine_info_release (new_info);
+
+ if (file_name)
+ free (file_name);
+ if (home_dir)
+ free (home_dir);
+ if (version)
+ free (version);
+
+ UNLOCK (engine_info_lock);
+ return gpg_error_from_errno (saved_errno);
+ }
+
+ (*lastp)->protocol = info->protocol;
+ (*lastp)->file_name = file_name;
+ (*lastp)->home_dir = home_dir;
+ (*lastp)->version = version;
+ (*lastp)->req_version = info->req_version;
+ (*lastp)->next = NULL;
+ lastp = &(*lastp)->next;
+
+ info = info->next;
+ }
+
+ *r_info = new_info;
+ UNLOCK (engine_info_lock);
+ return 0;
+}
+
+
+/* Set the engine info for the info list INFO, protocol PROTO, to the
+ file name FILE_NAME and the home directory HOME_DIR. */
+gpgme_error_t
+_gpgme_set_engine_info (gpgme_engine_info_t info, gpgme_protocol_t proto,
+ const char *file_name, const char *home_dir)
+{
+ char *new_file_name;
+ char *new_home_dir;
+
+ /* FIXME: Use some PROTO_MAX definition. */
+ if (proto > DIM (engine_ops))
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ while (info && info->protocol != proto)
+ info = info->next;
+
+ if (!info)
+ return gpg_error (GPG_ERR_INV_ENGINE);
+
+ /* Prepare new members. */
+ if (file_name)
+ new_file_name = strdup (file_name);
+ else
+ {
+ const char *ofile_name = engine_get_file_name (proto);
+ assert (ofile_name);
+ new_file_name = strdup (ofile_name);
+ }
+ if (!new_file_name)
+ return gpg_error_from_errno (errno);
+
+ if (home_dir)
+ {
+ new_home_dir = strdup (home_dir);
+ if (!new_home_dir)
+ {
+ free (new_file_name);
+ return gpg_error_from_errno (errno);
+ }
+ }
+ else
+ {
+ const char *ohome_dir = engine_get_home_dir (proto);
+ if (ohome_dir)
+ {
+ new_home_dir = strdup (ohome_dir);
+ if (!new_home_dir)
+ {
+ free (new_file_name);
+ return gpg_error_from_errno (errno);
+ }
+ }
+ else
+ new_home_dir = NULL;
+ }
+
+ /* Remove the old members. */
+ assert (info->file_name);
+ free (info->file_name);
+ if (info->home_dir)
+ free (info->home_dir);
+ if (info->version)
+ free (info->version);
+
+ /* Install the new members. */
+ info->file_name = new_file_name;
+ info->home_dir = new_home_dir;
+ info->version = engine_get_version (proto, new_file_name);
+
+ return 0;
+}
+
+
+/* Set the default engine info for the protocol PROTO to the file name
+ FILE_NAME and the home directory HOME_DIR. */
+gpgme_error_t
+gpgme_set_engine_info (gpgme_protocol_t proto,
+ const char *file_name, const char *home_dir)
+{
+ gpgme_error_t err;
+ gpgme_engine_info_t info;
+
+ LOCK (engine_info_lock);
+ info = engine_info;
+ if (!info)
+ {
+ /* Make sure it is initialized. */
+ UNLOCK (engine_info_lock);
+ err = gpgme_get_engine_info (&info);
+ if (err)
+ return err;
+
+ LOCK (engine_info_lock);
+ }
+
+ err = _gpgme_set_engine_info (info, proto, file_name, home_dir);
+ UNLOCK (engine_info_lock);
+ return err;
+}
+
+
+gpgme_error_t
+_gpgme_engine_new (gpgme_engine_info_t info, engine_t *r_engine)
+{
+ engine_t engine;
+
+ if (!info->file_name || !info->version)
+ return gpg_error (GPG_ERR_INV_ENGINE);
+
+ engine = calloc (1, sizeof *engine);
+ if (!engine)
+ return gpg_error_from_errno (errno);
+
+ engine->ops = engine_ops[info->protocol];
+ if (engine->ops->new)
+ {
+ gpgme_error_t err;
+ err = (*engine->ops->new) (&engine->engine,
+ info->file_name, info->home_dir);
+ if (err)
+ {
+ free (engine);
+ return err;
+ }
+ }
+ else
+ engine->engine = NULL;
+
+ *r_engine = engine;
+ return 0;
+}
+
+
+gpgme_error_t
+_gpgme_engine_reset (engine_t engine)
+{
+ if (!engine)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if (!engine->ops->reset)
+ return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+
+ return (*engine->ops->reset) (engine->engine);
+}
+
+
+void
+_gpgme_engine_release (engine_t engine)
+{
+ if (!engine)
+ return;
+
+ if (engine->ops->release)
+ (*engine->ops->release) (engine->engine);
+ free (engine);
+}
+
+
+void
+_gpgme_engine_set_status_handler (engine_t engine,
+ engine_status_handler_t fnc, void *fnc_value)
+{
+ if (!engine)
+ return;
+
+ if (engine->ops->set_status_handler)
+ (*engine->ops->set_status_handler) (engine->engine, fnc, fnc_value);
+}
+
+
+gpgme_error_t
+_gpgme_engine_set_command_handler (engine_t engine,
+ engine_command_handler_t fnc,
+ void *fnc_value,
+ gpgme_data_t linked_data)
+{
+ if (!engine)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if (!engine->ops->set_command_handler)
+ return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+
+ return (*engine->ops->set_command_handler) (engine->engine,
+ fnc, fnc_value, linked_data);
+}
+
+gpgme_error_t
+_gpgme_engine_set_colon_line_handler (engine_t engine,
+ engine_colon_line_handler_t fnc,
+ void *fnc_value)
+{
+ if (!engine)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if (!engine->ops->set_colon_line_handler)
+ return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+
+ return (*engine->ops->set_colon_line_handler) (engine->engine,
+ fnc, fnc_value);
+}
+
+gpgme_error_t
+_gpgme_engine_set_locale (engine_t engine, int category,
+ const char *value)
+{
+ if (!engine)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if (!engine->ops->set_locale)
+ return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+
+ return (*engine->ops->set_locale) (engine->engine, category, value);
+}
+
+
+gpgme_error_t
+_gpgme_engine_set_protocol (engine_t engine, gpgme_protocol_t protocol)
+{
+ if (!engine)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if (!engine->ops->set_protocol)
+ return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+
+ return (*engine->ops->set_protocol) (engine->engine, protocol);
+}
+
+
+gpgme_error_t
+_gpgme_engine_op_decrypt (engine_t engine, gpgme_data_t ciph,
+ gpgme_data_t plain)
+{
+ if (!engine)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if (!engine->ops->decrypt)
+ return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+
+ return (*engine->ops->decrypt) (engine->engine, ciph, plain);
+}
+
+
+gpgme_error_t
+_gpgme_engine_op_decrypt_verify (engine_t engine, gpgme_data_t ciph,
+ gpgme_data_t plain)
+{
+ if (!engine)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if (!engine->ops->decrypt_verify)
+ return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+
+ return (*engine->ops->decrypt_verify) (engine->engine, ciph, plain);
+}
+
+
+gpgme_error_t
+_gpgme_engine_op_delete (engine_t engine, gpgme_key_t key,
+ int allow_secret)
+{
+ if (!engine)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if (!engine->ops->delete)
+ return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+
+ return (*engine->ops->delete) (engine->engine, key, allow_secret);
+}
+
+
+gpgme_error_t
+_gpgme_engine_op_edit (engine_t engine, int type, gpgme_key_t key,
+ gpgme_data_t out, gpgme_ctx_t ctx /* FIXME */)
+{
+ if (!engine)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if (!engine->ops->edit)
+ return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+
+ return (*engine->ops->edit) (engine->engine, type, key, out, ctx);
+}
+
+
+gpgme_error_t
+_gpgme_engine_op_encrypt (engine_t engine, gpgme_key_t recp[],
+ gpgme_encrypt_flags_t flags,
+ gpgme_data_t plain, gpgme_data_t ciph, int use_armor)
+{
+ if (!engine)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if (!engine->ops->encrypt)
+ return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+
+ return (*engine->ops->encrypt) (engine->engine, recp, flags, plain, ciph,
+ use_armor);
+}
+
+
+gpgme_error_t
+_gpgme_engine_op_encrypt_sign (engine_t engine, gpgme_key_t recp[],
+ gpgme_encrypt_flags_t flags,
+ gpgme_data_t plain, gpgme_data_t ciph,
+ int use_armor, gpgme_ctx_t ctx /* FIXME */)
+{
+ if (!engine)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if (!engine->ops->encrypt_sign)
+ return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+
+ return (*engine->ops->encrypt_sign) (engine->engine, recp, flags,
+ plain, ciph, use_armor, ctx);
+}
+
+
+gpgme_error_t
+_gpgme_engine_op_export (engine_t engine, const char *pattern,
+ gpgme_export_mode_t mode, gpgme_data_t keydata,
+ int use_armor)
+{
+ if (!engine)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if (!engine->ops->export)
+ return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+
+ return (*engine->ops->export) (engine->engine, pattern, mode,
+ keydata, use_armor);
+}
+
+
+gpgme_error_t
+_gpgme_engine_op_export_ext (engine_t engine, const char *pattern[],
+ unsigned int reserved, gpgme_data_t keydata,
+ int use_armor)
+{
+ if (!engine)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if (!engine->ops->export_ext)
+ return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+
+ return (*engine->ops->export_ext) (engine->engine, pattern, reserved,
+ keydata, use_armor);
+}
+
+
+gpgme_error_t
+_gpgme_engine_op_genkey (engine_t engine, gpgme_data_t help_data,
+ int use_armor, gpgme_data_t pubkey,
+ gpgme_data_t seckey)
+{
+ if (!engine)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if (!engine->ops->genkey)
+ return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+
+ return (*engine->ops->genkey) (engine->engine, help_data, use_armor,
+ pubkey, seckey);
+}
+
+
+gpgme_error_t
+_gpgme_engine_op_import (engine_t engine, gpgme_data_t keydata,
+ gpgme_key_t *keyarray)
+{
+ if (!engine)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if (!engine->ops->import)
+ return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+
+ return (*engine->ops->import) (engine->engine, keydata, keyarray);
+}
+
+
+gpgme_error_t
+_gpgme_engine_op_keylist (engine_t engine, const char *pattern,
+ int secret_only, gpgme_keylist_mode_t mode)
+{
+ if (!engine)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if (!engine->ops->keylist)
+ return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+
+ return (*engine->ops->keylist) (engine->engine, pattern, secret_only, mode);
+}
+
+
+gpgme_error_t
+_gpgme_engine_op_keylist_ext (engine_t engine, const char *pattern[],
+ int secret_only, int reserved,
+ gpgme_keylist_mode_t mode)
+{
+ if (!engine)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if (!engine->ops->keylist_ext)
+ return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+
+ return (*engine->ops->keylist_ext) (engine->engine, pattern, secret_only,
+ reserved, mode);
+}
+
+
+gpgme_error_t
+_gpgme_engine_op_sign (engine_t engine, gpgme_data_t in, gpgme_data_t out,
+ gpgme_sig_mode_t mode, int use_armor,
+ int use_textmode, int include_certs,
+ gpgme_ctx_t ctx /* FIXME */)
+{
+ if (!engine)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if (!engine->ops->sign)
+ return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+
+ return (*engine->ops->sign) (engine->engine, in, out, mode, use_armor,
+ use_textmode, include_certs, ctx);
+}
+
+
+gpgme_error_t
+_gpgme_engine_op_trustlist (engine_t engine, const char *pattern)
+{
+ if (!engine)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if (!engine->ops->trustlist)
+ return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+
+ return (*engine->ops->trustlist) (engine->engine, pattern);
+}
+
+
+gpgme_error_t
+_gpgme_engine_op_verify (engine_t engine, gpgme_data_t sig,
+ gpgme_data_t signed_text, gpgme_data_t plaintext)
+{
+ if (!engine)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if (!engine->ops->verify)
+ return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+
+ return (*engine->ops->verify) (engine->engine, sig, signed_text, plaintext);
+}
+
+
+gpgme_error_t
+_gpgme_engine_op_getauditlog (engine_t engine, gpgme_data_t output,
+ unsigned int flags)
+{
+ if (!engine)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if (!engine->ops->getauditlog)
+ return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+
+ return (*engine->ops->getauditlog) (engine->engine, output, flags);
+}
+
+
+gpgme_error_t
+_gpgme_engine_op_assuan_transact (engine_t engine,
+ const char *command,
+ gpgme_assuan_data_cb_t data_cb,
+ void *data_cb_value,
+ gpgme_assuan_inquire_cb_t inq_cb,
+ void *inq_cb_value,
+ gpgme_assuan_status_cb_t status_cb,
+ void *status_cb_value)
+{
+ if (!engine)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if (!engine->ops->opassuan_transact)
+ return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+
+ return (*engine->ops->opassuan_transact) (engine->engine,
+ command,
+ data_cb, data_cb_value,
+ inq_cb, inq_cb_value,
+ status_cb, status_cb_value);
+}
+
+
+gpgme_error_t
+_gpgme_engine_op_conf_load (engine_t engine, gpgme_conf_comp_t *conf_p)
+{
+ if (!engine)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if (!engine->ops->conf_load)
+ return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+
+ return (*engine->ops->conf_load) (engine->engine, conf_p);
+}
+
+
+gpgme_error_t
+_gpgme_engine_op_conf_save (engine_t engine, gpgme_conf_comp_t conf)
+{
+ if (!engine)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if (!engine->ops->conf_save)
+ return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+
+ return (*engine->ops->conf_save) (engine->engine, conf);
+}
+
+
+void
+_gpgme_engine_set_io_cbs (engine_t engine, gpgme_io_cbs_t io_cbs)
+{
+ if (!engine)
+ return;
+
+ (*engine->ops->set_io_cbs) (engine->engine, io_cbs);
+}
+
+
+void
+_gpgme_engine_io_event (engine_t engine,
+ gpgme_event_io_t type, void *type_data)
+{
+ if (!engine)
+ return;
+
+ (*engine->ops->io_event) (engine->engine, type, type_data);
+}
+
+
+/* Cancel the session and the pending operation if any. */
+gpgme_error_t
+_gpgme_engine_cancel (engine_t engine)
+{
+ if (!engine)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if (!engine->ops->cancel)
+ return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+
+ return (*engine->ops->cancel) (engine->engine);
+}
+
+
+/* Cancel the pending operation, but not the complete session. */
+gpgme_error_t
+_gpgme_engine_cancel_op (engine_t engine)
+{
+ if (!engine)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if (!engine->ops->cancel_op)
+ return 0;
+
+ return (*engine->ops->cancel_op) (engine->engine);
+}
+
+
+/* Change the passphrase for KEY. */
+gpgme_error_t
+_gpgme_engine_op_passwd (engine_t engine, gpgme_key_t key,
+ unsigned int flags)
+{
+ if (!engine)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if (!engine->ops->passwd)
+ return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+
+ return (*engine->ops->passwd) (engine->engine, key, flags);
+}
+
diff --git a/src/engine.h b/src/engine.h
new file mode 100644
index 0000000..dfc73ac
--- /dev/null
+++ b/src/engine.h
@@ -0,0 +1,164 @@
+/* engine.h - GPGME engine interface.
+ Copyright (C) 2000 Werner Koch (dd9jn)
+ Copyright (C) 2001, 2002, 2003, 2004, 2010 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#ifndef ENGINE_H
+#define ENGINE_H
+
+#include "gpgme.h"
+
+struct engine;
+typedef struct engine *engine_t;
+
+typedef gpgme_error_t (*engine_status_handler_t) (void *priv,
+ gpgme_status_code_t code,
+ char *args);
+typedef gpgme_error_t (*engine_colon_line_handler_t) (void *priv, char *line);
+typedef gpgme_error_t (*engine_command_handler_t) (void *priv,
+ gpgme_status_code_t code,
+ const char *keyword,
+ int fd, int *processed);
+typedef gpgme_error_t (*engine_assuan_result_cb_t) (void *priv,
+ gpgme_error_t result);
+
+
+/* Get a deep copy of the engine info and return it in INFO. */
+gpgme_error_t _gpgme_engine_info_copy (gpgme_engine_info_t *r_info);
+
+/* Release the engine info INFO. */
+void _gpgme_engine_info_release (gpgme_engine_info_t info);
+
+/* Set the engine info for the info list INFO, protocol PROTO, to the
+ file name FILE_NAME and the home directory HOME_DIR. */
+gpgme_error_t _gpgme_set_engine_info (gpgme_engine_info_t info,
+ gpgme_protocol_t praoto,
+ const char *file_name,
+ const char *home_dir);
+
+
+gpgme_error_t _gpgme_engine_new (gpgme_engine_info_t info,
+ engine_t *r_engine);
+gpgme_error_t _gpgme_engine_reset (engine_t engine);
+
+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_release (engine_t engine);
+void _gpgme_engine_set_status_handler (engine_t engine,
+ engine_status_handler_t fnc,
+ void *fnc_value);
+gpgme_error_t _gpgme_engine_set_command_handler (engine_t engine,
+ engine_command_handler_t fnc,
+ void *fnc_value,
+ gpgme_data_t data);
+gpgme_error_t
+_gpgme_engine_set_colon_line_handler (engine_t engine,
+ engine_colon_line_handler_t fnc,
+ void *fnc_value);
+gpgme_error_t _gpgme_engine_op_decrypt (engine_t engine, gpgme_data_t ciph,
+ gpgme_data_t plain);
+gpgme_error_t _gpgme_engine_op_decrypt_verify (engine_t engine,
+ gpgme_data_t ciph,
+ gpgme_data_t plain);
+gpgme_error_t _gpgme_engine_op_delete (engine_t engine, gpgme_key_t key,
+ int allow_secret);
+gpgme_error_t _gpgme_engine_op_edit (engine_t engine, int type,
+ gpgme_key_t key, gpgme_data_t out,
+ gpgme_ctx_t ctx /* FIXME */);
+gpgme_error_t _gpgme_engine_op_encrypt (engine_t engine,
+ gpgme_key_t recp[],
+ gpgme_encrypt_flags_t flags,
+ gpgme_data_t plain, gpgme_data_t ciph,
+ int use_armor);
+gpgme_error_t _gpgme_engine_op_encrypt_sign (engine_t engine,
+ gpgme_key_t recp[],
+ gpgme_encrypt_flags_t flags,
+ gpgme_data_t plain,
+ gpgme_data_t ciph,
+ int use_armor,
+ gpgme_ctx_t ctx /* FIXME */);
+gpgme_error_t _gpgme_engine_op_export (engine_t engine, const char *pattern,
+ gpgme_export_mode_t mode,
+ gpgme_data_t keydata, int use_armor);
+gpgme_error_t _gpgme_engine_op_export_ext (engine_t engine,
+ const char *pattern[],
+ gpgme_export_mode_t mode,
+ gpgme_data_t keydata,
+ int use_armor);
+gpgme_error_t _gpgme_engine_op_genkey (engine_t engine,
+ gpgme_data_t help_data,
+ int use_armor, gpgme_data_t pubkey,
+ gpgme_data_t seckey);
+gpgme_error_t _gpgme_engine_op_import (engine_t engine,
+ gpgme_data_t keydata,
+ gpgme_key_t *keyarray);
+gpgme_error_t _gpgme_engine_op_keylist (engine_t engine,
+ const char *pattern,
+ int secret_only,
+ gpgme_keylist_mode_t mode);
+gpgme_error_t _gpgme_engine_op_keylist_ext (engine_t engine,
+ const char *pattern[],
+ int secret_only,
+ int reserved,
+ gpgme_keylist_mode_t mode);
+gpgme_error_t _gpgme_engine_op_sign (engine_t engine, gpgme_data_t in,
+ gpgme_data_t out, gpgme_sig_mode_t mode,
+ int use_armor, int use_textmode,
+ int include_certs,
+ gpgme_ctx_t ctx /* FIXME */);
+gpgme_error_t _gpgme_engine_op_trustlist (engine_t engine,
+ const char *pattern);
+gpgme_error_t _gpgme_engine_op_verify (engine_t engine, gpgme_data_t sig,
+ gpgme_data_t signed_text,
+ gpgme_data_t plaintext);
+
+gpgme_error_t _gpgme_engine_op_getauditlog (engine_t engine,
+ gpgme_data_t output,
+ unsigned int flags);
+gpgme_error_t _gpgme_engine_op_assuan_transact
+ (engine_t engine,
+ const char *command,
+ gpgme_assuan_data_cb_t data_cb,
+ void *data_cb_value,
+ gpgme_assuan_inquire_cb_t inq_cb,
+ void *inq_cb_value,
+ gpgme_assuan_status_cb_t status_cb,
+ void *status_cb_value);
+
+gpgme_error_t _gpgme_engine_op_conf_load (engine_t engine,
+ gpgme_conf_comp_t *conf_p);
+gpgme_error_t _gpgme_engine_op_conf_save (engine_t engine,
+ gpgme_conf_comp_t conf);
+
+void _gpgme_engine_set_io_cbs (engine_t engine,
+ gpgme_io_cbs_t io_cbs);
+void _gpgme_engine_io_event (engine_t engine,
+ gpgme_event_io_t type, void *type_data);
+
+gpgme_error_t _gpgme_engine_cancel (engine_t engine);
+
+gpgme_error_t _gpgme_engine_cancel_op (engine_t engine);
+
+gpgme_error_t _gpgme_engine_op_passwd (engine_t engine, gpgme_key_t key,
+ unsigned int flags);
+
+
+#endif /* ENGINE_H */
diff --git a/src/error.c b/src/error.c
new file mode 100644
index 0000000..5f82efd
--- /dev/null
+++ b/src/error.c
@@ -0,0 +1,111 @@
+/* error.c - Error handling for GPGME.
+ Copyright (C) 2003, 2004 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gpgme.h>
+
+/* Return a pointer to a string containing a description of the error
+ code in the error value ERR. */
+const char *
+gpgme_strerror (gpgme_error_t err)
+{
+ return gpg_strerror (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. */
+int
+gpgme_strerror_r (gpg_error_t err, char *buf, size_t buflen)
+{
+ return gpg_strerror_r (err, buf, buflen);
+}
+
+
+/* Return a pointer to a string containing a description of the error
+ source in the error value ERR. */
+const char *
+gpgme_strsource (gpgme_error_t err)
+{
+ return gpg_strsource (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). */
+gpgme_err_code_t
+gpgme_err_code_from_errno (int err)
+{
+ return gpg_err_code_from_errno (err);
+}
+
+
+/* Retrieve the system error for the error code CODE. This returns 0
+ if CODE is not a system error code. */
+int
+gpgme_err_code_to_errno (gpgme_err_code_t code)
+{
+ return gpg_err_code_to_errno (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. */
+gpgme_err_code_t
+gpgme_err_code_from_syserror (void)
+{
+ return gpg_err_code_from_syserror ();
+}
+
+
+/* Set the ERRNO variable. This function is the preferred way to set
+ ERRNO due to peculiarities on WindowsCE. */
+void
+gpgme_err_set_errno (int err)
+{
+ gpg_err_set_errno (err);
+}
+
+
+/* Return an error value with the error source SOURCE and the system
+ error ERR. */
+gpgme_error_t
+gpgme_err_make_from_errno (gpg_err_source_t source, int err)
+{
+ return gpg_err_make_from_errno (source, err);
+}
+
+
+/* Return an error value with the system error ERR. */
+gpgme_error_t
+gpgme_error_from_errno (int err)
+{
+ return gpgme_error (gpg_err_code_from_errno (err));
+}
diff --git a/src/export.c b/src/export.c
new file mode 100644
index 0000000..9c1c79c
--- /dev/null
+++ b/src/export.c
@@ -0,0 +1,325 @@
+/* export.c - Export a key.
+ Copyright (C) 2000 Werner Koch (dd9jn)
+ Copyright (C) 2001, 2002, 2003, 2004, 2010 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+
+#include "gpgme.h"
+#include "util.h"
+#include "debug.h"
+#include "context.h"
+#include "ops.h"
+
+
+static gpgme_error_t
+export_status_handler (void *priv, gpgme_status_code_t code, char *args)
+{
+ return 0;
+}
+
+
+static gpgme_error_t
+export_start (gpgme_ctx_t ctx, int synchronous, const char *pattern,
+ gpgme_export_mode_t mode, gpgme_data_t keydata)
+{
+ gpgme_error_t err;
+
+ if ((mode & ~(GPGME_EXPORT_MODE_EXTERN
+ |GPGME_EXPORT_MODE_MINIMAL)))
+ return gpg_error (GPG_ERR_INV_VALUE); /* Invalid flags in MODE. */
+
+
+ if ((mode & GPGME_EXPORT_MODE_EXTERN))
+ {
+ if (keydata)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ }
+ else
+ {
+ if (!keydata)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ }
+
+ err = _gpgme_op_reset (ctx, synchronous);
+ if (err)
+ return err;
+
+ _gpgme_engine_set_status_handler (ctx->engine, export_status_handler, ctx);
+
+ return _gpgme_engine_op_export (ctx->engine, pattern, mode, keydata,
+ ctx->use_armor);
+}
+
+
+/* Export the keys listed in PATTERN into KEYDATA. */
+gpgme_error_t
+gpgme_op_export_start (gpgme_ctx_t ctx, const char *pattern,
+ gpgme_export_mode_t mode, gpgme_data_t keydata)
+{
+ gpgme_error_t err;
+
+ TRACE_BEG3 (DEBUG_CTX, "gpgme_op_export_start", ctx,
+ "pattern=%s, mode=0x%x, keydata=%p", pattern, mode, keydata);
+
+ if (!ctx)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ err = export_start (ctx, 0, pattern, mode, keydata);
+ return TRACE_ERR (err);
+}
+
+
+/* Export the keys listed in PATTERN into KEYDATA. */
+gpgme_error_t
+gpgme_op_export (gpgme_ctx_t ctx, const char *pattern,
+ gpgme_export_mode_t mode, gpgme_data_t keydata)
+{
+ gpgme_error_t err;
+
+ TRACE_BEG3 (DEBUG_CTX, "gpgme_op_export", ctx,
+ "pattern=%s, mode=0x%x, keydata=%p", pattern, mode, keydata);
+
+ if (!ctx)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ err = export_start (ctx, 1, pattern, mode, keydata);
+ if (!err)
+ err = _gpgme_wait_one (ctx);
+ return err;
+}
+
+
+static gpgme_error_t
+export_ext_start (gpgme_ctx_t ctx, int synchronous, const char *pattern[],
+ gpgme_export_mode_t mode, gpgme_data_t keydata)
+{
+ gpgme_error_t err;
+
+ if ((mode & ~(GPGME_EXPORT_MODE_EXTERN
+ |GPGME_EXPORT_MODE_MINIMAL)))
+ return gpg_error (GPG_ERR_INV_VALUE); /* Invalid flags in MODE. */
+
+ if ((mode & GPGME_EXPORT_MODE_EXTERN))
+ {
+ if (keydata)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ }
+ else
+ {
+ if (!keydata)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ }
+
+ err = _gpgme_op_reset (ctx, synchronous);
+ if (err)
+ return err;
+
+ _gpgme_engine_set_status_handler (ctx->engine, export_status_handler, ctx);
+
+ return _gpgme_engine_op_export_ext (ctx->engine, pattern, mode, keydata,
+ ctx->use_armor);
+}
+
+
+/* Export the keys listed in PATTERN into KEYDATA. */
+gpgme_error_t
+gpgme_op_export_ext_start (gpgme_ctx_t ctx, const char *pattern[],
+ gpgme_export_mode_t mode, gpgme_data_t keydata)
+{
+ gpgme_error_t err;
+
+ TRACE_BEG2 (DEBUG_CTX, "gpgme_op_export_ext_start", ctx,
+ "mode=0x%x, keydata=%p", mode, keydata);
+
+ if (!ctx)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ if (_gpgme_debug_trace () && pattern)
+ {
+ int i = 0;
+
+ while (pattern[i])
+ {
+ TRACE_LOG2 ("pattern[%i] = %s", i, pattern[i]);
+ i++;
+ }
+ }
+
+ err = export_ext_start (ctx, 0, pattern, mode, keydata);
+ return TRACE_ERR (err);
+}
+
+
+/* Export the keys listed in PATTERN into KEYDATA. */
+gpgme_error_t
+gpgme_op_export_ext (gpgme_ctx_t ctx, const char *pattern[],
+ gpgme_export_mode_t mode, gpgme_data_t keydata)
+{
+ gpgme_error_t err;
+
+ TRACE_BEG2 (DEBUG_CTX, "gpgme_op_export_ext_start", ctx,
+ "mode=0x%x, keydata=%p", mode, keydata);
+
+ if (!ctx)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ if (_gpgme_debug_trace () && pattern)
+ {
+ int i = 0;
+
+ while (pattern[i])
+ {
+ TRACE_LOG2 ("pattern[%i] = %s", i, pattern[i]);
+ i++;
+ }
+ }
+
+ err = export_ext_start (ctx, 1, pattern, mode, keydata);
+ if (!err)
+ err = _gpgme_wait_one (ctx);
+ return TRACE_ERR (err);
+}
+
+
+
+
+
+static gpgme_error_t
+export_keys_start (gpgme_ctx_t ctx, int synchronous, gpgme_key_t keys[],
+ gpgme_export_mode_t mode, gpgme_data_t keydata)
+{
+ gpgme_error_t err;
+ int nkeys, idx;
+ char **pattern;
+
+ if (!keys)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ /* Create a list of pattern from the keys. */
+ for (idx=nkeys=0; keys[idx]; idx++)
+ if (keys[idx]->protocol == ctx->protocol)
+ nkeys++;
+ if (!nkeys)
+ return gpg_error (GPG_ERR_NO_DATA);
+
+ pattern = calloc (nkeys+1, sizeof *pattern);
+ if (!pattern)
+ return gpg_error_from_syserror ();
+
+ for (idx=nkeys=0; keys[idx]; idx++)
+ if (keys[idx]->protocol == ctx->protocol
+ && keys[idx]->subkeys
+ && keys[idx]->subkeys->fpr
+ && *keys[idx]->subkeys->fpr)
+ {
+ pattern[nkeys] = strdup (keys[idx]->subkeys->fpr);
+ if (!pattern[nkeys])
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ nkeys++;
+ }
+
+
+ /* Pass on to the regular function. */
+ err = export_ext_start (ctx, synchronous, (const char**)pattern,
+ mode, keydata);
+
+ leave:
+ for (idx=0; pattern[idx]; idx++)
+ free (pattern[idx]);
+ free (pattern);
+
+ return err;
+}
+
+
+/* Export the keys from the array KEYS into KEYDATA. Only keys of the
+ current protocol are exported and only those which have a
+ fingerprint set; that is keys received with some external search
+ methods are silently skipped. */
+gpgme_error_t
+gpgme_op_export_keys_start (gpgme_ctx_t ctx,
+ gpgme_key_t keys[],
+ gpgme_export_mode_t mode,
+ gpgme_data_t keydata)
+{
+ gpg_error_t err;
+
+ TRACE_BEG2 (DEBUG_CTX, "gpgme_op_export_keys_start", ctx,
+ "mode=0x%x, keydata=%p", mode, keydata);
+
+ if (!ctx)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ if (_gpgme_debug_trace () && keys)
+ {
+ int i = 0;
+
+ while (keys[i])
+ {
+ TRACE_LOG3 ("keys[%i] = %p (%s)", i, keys[i],
+ (keys[i]->subkeys && keys[i]->subkeys->fpr) ?
+ keys[i]->subkeys->fpr : "invalid");
+ i++;
+ }
+ }
+
+ err = export_keys_start (ctx, 0, keys, mode, keydata);
+ return TRACE_ERR (err);
+}
+
+gpgme_error_t
+gpgme_op_export_keys (gpgme_ctx_t ctx,
+ gpgme_key_t keys[],
+ gpgme_export_mode_t mode,
+ gpgme_data_t keydata)
+{
+ gpgme_error_t err;
+
+ TRACE_BEG2 (DEBUG_CTX, "gpgme_op_export_keys", ctx,
+ "mode=0x%x, keydata=%p", mode, keydata);
+
+ if (!ctx)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ if (_gpgme_debug_trace () && keys)
+ {
+ int i = 0;
+
+ while (keys[i])
+ {
+ TRACE_LOG3 ("keys[%i] = %p (%s)", i, keys[i],
+ (keys[i]->subkeys && keys[i]->subkeys->fpr) ?
+ keys[i]->subkeys->fpr : "invalid");
+ i++;
+ }
+ }
+
+ err = export_keys_start (ctx, 1, keys, mode, keydata);
+ if (!err)
+ err = _gpgme_wait_one (ctx);
+ return TRACE_ERR (err);
+}
+
diff --git a/src/funopen.c b/src/funopen.c
new file mode 100644
index 0000000..a20dd8a
--- /dev/null
+++ b/src/funopen.c
@@ -0,0 +1,63 @@
+/* funopen.c - Replacement for funopen.
+ Copyright (C) 2004 g10 Code GmbH
+
+ This file is part of GPGME
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+
+
+/* Replacement for the *BSD function:
+
+ FILE *funopen (void *cookie,
+ int (*readfn)(void *, char *, int),
+ int (*writefn)(void *, const char *, int),
+ fpos_t (*seekfn)(void *, fpos_t, int),
+ int (*closefn)(void *));
+
+ The functions to provide my either be NULL if not required or
+ similar to the unistd function with the exception of using the
+ cookie instead of the fiel descripor.
+*/
+
+
+#ifdef HAVE_FOPENCOOKIE
+FILE *
+_gpgme_funopen(void *cookie,
+ cookie_read_function_t *readfn,
+ cookie_write_function_t *writefn,
+ cookie_seek_function_t *seekfn,
+ cookie_close_function_t *closefn)
+{
+ cookie_io_functions_t io = { NULL };
+
+ io.read = readfn;
+ io.write = writefn;
+ io.seek = seekfn;
+ io.close = closefn;
+
+ return fopencookie (cookie,
+ readfn ? ( writefn ? "rw" : "r" )
+ : ( writefn ? "w" : ""), io);
+}
+#else
+#error No known way to implement funopen.
+#endif
diff --git a/src/genkey.c b/src/genkey.c
new file mode 100644
index 0000000..bfc7c2a
--- /dev/null
+++ b/src/genkey.c
@@ -0,0 +1,235 @@
+/* genkey.c - Key generation.
+ Copyright (C) 2000 Werner Koch (dd9jn)
+ Copyright (C) 2001, 2002, 2003, 2004 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "gpgme.h"
+#include "debug.h"
+#include "context.h"
+#include "ops.h"
+#include "util.h"
+
+
+typedef struct
+{
+ struct _gpgme_op_genkey_result result;
+
+ /* The key parameters passed to the crypto engine. */
+ gpgme_data_t key_parameter;
+} *op_data_t;
+
+
+static void
+release_op_data (void *hook)
+{
+ op_data_t opd = (op_data_t) hook;
+
+ if (opd->result.fpr)
+ free (opd->result.fpr);
+ if (opd->key_parameter)
+ gpgme_data_release (opd->key_parameter);
+}
+
+
+gpgme_genkey_result_t
+gpgme_op_genkey_result (gpgme_ctx_t ctx)
+{
+ void *hook;
+ op_data_t opd;
+ gpgme_error_t err;
+
+ TRACE_BEG (DEBUG_CTX, "gpgme_op_genkey_result", ctx);
+
+ err = _gpgme_op_data_lookup (ctx, OPDATA_GENKEY, &hook, -1, NULL);
+ opd = hook;
+ if (err || !opd)
+ {
+ TRACE_SUC0 ("result=(null)");
+ return NULL;
+ }
+
+ TRACE_LOG3 ("fpr = %s, %s, %s", opd->result.fpr,
+ opd->result.primary ? "primary" : "no primary",
+ opd->result.sub ? "sub" : "no sub");
+
+ TRACE_SUC1 ("result=%p", &opd->result);
+ return &opd->result;
+}
+
+
+static gpgme_error_t
+genkey_status_handler (void *priv, gpgme_status_code_t code, char *args)
+{
+ gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
+ gpgme_error_t err;
+ void *hook;
+ op_data_t opd;
+
+ /* Pipe the status code through the progress status handler. */
+ err = _gpgme_progress_status_handler (ctx, code, args);
+ if (err)
+ return err;
+
+ err = _gpgme_op_data_lookup (ctx, OPDATA_GENKEY, &hook, -1, NULL);
+ opd = hook;
+ if (err)
+ return err;
+
+ switch (code)
+ {
+ case GPGME_STATUS_KEY_CREATED:
+ if (args && *args)
+ {
+ if (*args == 'B' || *args == 'P')
+ opd->result.primary = 1;
+ if (*args == 'B' || *args == 'S')
+ opd->result.sub = 1;
+ if (args[1] == ' ')
+ {
+ if (opd->result.fpr)
+ free (opd->result.fpr);
+ opd->result.fpr = strdup (&args[2]);
+ if (!opd->result.fpr)
+ return gpg_error_from_errno (errno);
+ }
+ }
+ break;
+
+ case GPGME_STATUS_EOF:
+ /* FIXME: Should return some more useful error value. */
+ if (!opd->result.primary && !opd->result.sub)
+ return gpg_error (GPG_ERR_GENERAL);
+ break;
+
+ default:
+ break;
+ }
+ return 0;
+}
+
+
+static gpgme_error_t
+get_key_parameter (const char *parms, gpgme_data_t *key_parameter)
+{
+ const char *content;
+ const char *attrib;
+ const char *endtag;
+
+ /* Extract the key parameter from the XML structure. */
+ parms = strstr (parms, "<GnupgKeyParms ");
+ if (!parms)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ content = strchr (parms, '>');
+ if (!content)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ content++;
+
+ attrib = strstr (parms, "format=\"internal\"");
+ if (!attrib || attrib >= content)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ endtag = strstr (content, "</GnupgKeyParms>");
+ /* FIXME: Check that there are no control statements inside. */
+ while (content[0] == '\n'
+ || (content[0] == '\r' && content[1] == '\n'))
+ content++;
+
+ return gpgme_data_new_from_mem (key_parameter, content,
+ endtag - content, 1);
+}
+
+
+static gpgme_error_t
+genkey_start (gpgme_ctx_t ctx, int synchronous, const char *parms,
+ gpgme_data_t pubkey, gpgme_data_t seckey)
+{
+ gpgme_error_t err;
+ void *hook;
+ op_data_t opd;
+ err = _gpgme_op_reset (ctx, synchronous);
+ if (err)
+ return err;
+
+ err = _gpgme_op_data_lookup (ctx, OPDATA_GENKEY, &hook,
+ sizeof (*opd), release_op_data);
+ opd = hook;
+ if (err)
+ return err;
+
+ err = get_key_parameter (parms, &opd->key_parameter);
+ if (err)
+ return err;
+
+ _gpgme_engine_set_status_handler (ctx->engine, genkey_status_handler, ctx);
+
+ return _gpgme_engine_op_genkey (ctx->engine, opd->key_parameter,
+ ctx->use_armor, pubkey, seckey);
+}
+
+
+/* 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. */
+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 err;
+
+ TRACE_BEG2 (DEBUG_CTX, "gpgme_op_genkey_start", ctx,
+ "pubkey=%p, seckey=%p", pubkey, seckey);
+ TRACE_LOGBUF (parms, strlen (parms));
+
+ if (!ctx)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ err = genkey_start (ctx, 0, parms, pubkey, seckey);
+ return TRACE_ERR (err);
+}
+
+
+/* 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. */
+gpgme_error_t
+gpgme_op_genkey (gpgme_ctx_t ctx, const char *parms, gpgme_data_t pubkey,
+ gpgme_data_t seckey)
+{
+ gpgme_error_t err;
+
+ TRACE_BEG2 (DEBUG_CTX, "gpgme_op_genkey", ctx,
+ "pubkey=%p, seckey=%p", pubkey, seckey);
+ TRACE_LOGBUF (parms, strlen (parms));
+
+ if (!ctx)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ err = genkey_start (ctx, 1, parms, pubkey, seckey);
+ if (!err)
+ err = _gpgme_wait_one (ctx);
+ return TRACE_ERR (err);
+}
diff --git a/src/get-env.c b/src/get-env.c
new file mode 100644
index 0000000..bea8949
--- /dev/null
+++ b/src/get-env.c
@@ -0,0 +1,59 @@
+/* get_env.c - A getenv() replacement.
+ Copyright (C) 2003, 2004 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+
+#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. */
+gpgme_error_t
+_gpgme_getenv (const char *name, char **value)
+{
+ char *env_value;
+
+ env_value = getenv (name);
+ if (!env_value)
+ *value = NULL;
+ else
+ {
+ *value = strdup (env_value);
+ if (!*value)
+ return gpg_error_from_errno (errno);
+ }
+ return 0;
+}
+
+#else
+
+/* FIXME: Implement this when we have the specification for it. */
+#error Use of getenv_r not implemented.
+
+#endif
diff --git a/src/getauditlog.c b/src/getauditlog.c
new file mode 100644
index 0000000..d8435ac
--- /dev/null
+++ b/src/getauditlog.c
@@ -0,0 +1,99 @@
+
+/* getauditlog.c - Retrieve the audit log.
+ Copyright (C) 2007 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "gpgme.h"
+#include "debug.h"
+#include "context.h"
+#include "ops.h"
+
+
+static gpgme_error_t
+getauditlog_status_handler (void *priv, gpgme_status_code_t code, char *args)
+{
+ return 0;
+}
+
+
+static gpgme_error_t
+getauditlog_start (gpgme_ctx_t ctx, int synchronous,
+ gpgme_data_t output, unsigned int flags)
+{
+ gpgme_error_t err;
+
+ if (!output)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ err = _gpgme_op_reset (ctx, ((synchronous&255) | 256) );
+ if (err)
+ return err;
+
+ _gpgme_engine_set_status_handler (ctx->engine,
+ getauditlog_status_handler, ctx);
+
+ return _gpgme_engine_op_getauditlog (ctx->engine, output, flags);
+}
+
+
+
+/* Return the auditlog for the current session. This may be called
+ after a successful or failed operation. If no audit log is
+ available GPG_ERR_NO_DATA is returned. This is the asynchronous
+ variant. */
+gpgme_error_t
+gpgme_op_getauditlog_start (gpgme_ctx_t ctx,
+ gpgme_data_t output, unsigned int flags)
+{
+ gpg_error_t err;
+ TRACE_BEG2 (DEBUG_CTX, "gpgme_op_getauditlog_start", ctx,
+ "output=%p, flags=0x%x", output, flags);
+
+ if (!ctx)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ err = getauditlog_start (ctx, 0, output, flags);
+ return TRACE_ERR (err);
+}
+
+
+/* Return the auditlog for the current session. This may be called
+ after a successful or failed operation. If no audit log is
+ available GPG_ERR_NO_DATA is returned. This is the synchronous
+ variant. */
+gpgme_error_t
+gpgme_op_getauditlog (gpgme_ctx_t ctx, gpgme_data_t output, unsigned int flags)
+{
+ gpgme_error_t err;
+
+ TRACE_BEG2 (DEBUG_CTX, "gpgme_op_getauditlog", ctx,
+ "output=%p, flags=0x%x", output, flags);
+
+ if (!ctx)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ err = getauditlog_start (ctx, 1, output, flags);
+ if (!err)
+ err = _gpgme_wait_one (ctx);
+ return TRACE_ERR (err);
+}
+
diff --git a/src/gpgconf.c b/src/gpgconf.c
new file mode 100644
index 0000000..f3411d5
--- /dev/null
+++ b/src/gpgconf.c
@@ -0,0 +1,142 @@
+/* gpgconf.c - GnuPG Made Easy.
+ Copyright (C) 2007 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "gpgme.h"
+
+#include "ops.h"
+#include "engine.h"
+
+#ifdef ENABLE_GPGCONF
+/* engine-gpgconf.c. */
+gpgme_error_t _gpgme_conf_arg_new (gpgme_conf_arg_t *arg_p,
+ gpgme_conf_type_t type, const void *value);
+void _gpgme_conf_arg_release (gpgme_conf_arg_t arg, gpgme_conf_type_t type);
+gpgme_error_t _gpgme_conf_opt_change (gpgme_conf_opt_t opt, int reset,
+ gpgme_conf_arg_t arg);
+void _gpgme_conf_release (gpgme_conf_comp_t conf);
+gpgme_error_t _gpgme_conf_load (void *engine, gpgme_conf_comp_t *conf_p);
+gpgme_error_t gpgme_op_conf_save (gpgme_ctx_t ctx, gpgme_conf_comp_t comp);
+
+#endif
+
+
+/* Allocate a new gpgme_conf_arg_t. */
+gpgme_error_t
+gpgme_conf_arg_new (gpgme_conf_arg_t *arg_p,
+ gpgme_conf_type_t type, const void *value)
+{
+#ifdef ENABLE_GPGCONF
+ return _gpgme_conf_arg_new (arg_p, type, value);
+#else
+ return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+#endif
+}
+
+
+/* This also releases all chained argument structures! */
+void
+gpgme_conf_arg_release (gpgme_conf_arg_t arg, gpgme_conf_type_t type)
+{
+#ifdef ENABLE_GPGCONF
+ _gpgme_conf_arg_release (arg, type);
+#endif
+}
+
+
+/* Register a change for the value of OPT to ARG. */
+gpgme_error_t
+gpgme_conf_opt_change (gpgme_conf_opt_t opt, int reset, gpgme_conf_arg_t arg)
+{
+#ifdef ENABLE_GPGCONF
+ return _gpgme_conf_opt_change (opt, reset, arg);
+#else
+ return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+#endif
+}
+
+
+
+/* Public function to release a gpgme_conf_comp list. */
+void
+gpgme_conf_release (gpgme_conf_comp_t conf)
+{
+#ifdef ENABLE_GPGCONF
+ _gpgme_conf_release (conf);
+#endif
+}
+
+
+/* Public function to release load a configuration list. No
+ asynchronous interface for now. */
+gpgme_error_t
+gpgme_op_conf_load (gpgme_ctx_t ctx, gpgme_conf_comp_t *conf_p)
+{
+#ifdef ENABLE_GPGCONF
+ gpgme_error_t err;
+ gpgme_protocol_t proto;
+
+ if (!ctx)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ proto = ctx->protocol;
+ ctx->protocol = GPGME_PROTOCOL_GPGCONF;
+ err = _gpgme_op_reset (ctx, 1);
+ if (err)
+ return err;
+
+ err = _gpgme_engine_op_conf_load (ctx->engine, conf_p);
+ ctx->protocol = proto;
+ return err;
+#else
+ return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+#endif
+}
+
+
+/* This function does not follow chained components! */
+gpgme_error_t
+gpgme_op_conf_save (gpgme_ctx_t ctx, gpgme_conf_comp_t comp)
+{
+#ifdef ENABLE_GPGCONF
+ gpgme_error_t err;
+ gpgme_protocol_t proto;
+
+ if (!ctx)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ proto = ctx->protocol;
+ ctx->protocol = GPGME_PROTOCOL_GPGCONF;
+ err = _gpgme_op_reset (ctx, 1);
+ if (err)
+ return err;
+
+ err = _gpgme_engine_op_conf_save (ctx->engine, comp);
+ ctx->protocol = proto;
+ return err;
+#else
+ return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+#endif
+}
+
+
diff --git a/src/gpgme-config.in b/src/gpgme-config.in
new file mode 100644
index 0000000..4be1e08
--- /dev/null
+++ b/src/gpgme-config.in
@@ -0,0 +1,194 @@
+#!/bin/sh
+# Copyright (C) 1999, 2002, 2003 Free Software Foundation, Inc.
+#
+# This file is free software; as a special exception the author gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+#
+# This file is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+
+# Make sure that no weird locale setting messes up our sed regexps etc.
+LC_COLLATE=C
+LC_ALL=C
+LANG=C
+
+# GPGME's own cflags and libs
+cflags="-I@includedir@"
+libs="-L@libdir@"
+
+# Network libraries.
+assuan_cflags="@LIBASSUAN_CFLAGS@"
+assuan_libs="@LIBASSUAN_LIBS@"
+
+# Configure libgpg-error.
+gpg_error_cflags="@GPG_ERROR_CFLAGS@"
+gpg_error_libs="@GPG_ERROR_LIBS@"
+
+# Configure thread packages.
+thread_modules=""
+
+@HAVE_PTHREAD_TRUE@thread_modules="$thread_modules pthread"
+libs_pthread="-lpthread"
+cflags_pthread=""
+
+# Configure glib.
+libs_glib="@GLIB_LIBS@"
+cflags_glib="@GLIB_CFLAGS@"
+with_glib=
+
+output=""
+
+usage()
+{
+ cat <<EOF
+Usage: gpgme-config [OPTIONS]
+Options:
+ [--thread={${thread_modules}}]
+ [--prefix]
+ [--exec-prefix]
+ [--version]
+ [--api-version]
+ [--host]
+ [--libs]
+ [--cflags]
+ [--get-gpg]
+ [--get-gpgsm]
+EOF
+ exit $1
+}
+
+if test $# -eq 0; then
+ usage 1 1>&2
+fi
+
+while test $# -gt 0; do
+ case "$1" in
+ -*=*)
+ optarg=`echo "$1" | sed 's/[-_a-zA-Z0-9]*=//'`
+ ;;
+ *)
+ optarg=
+ ;;
+ esac
+
+ case $1 in
+ --prefix=*)
+ # For compatibility reasons with old M4 macros, we ignore
+ # setting of prefix.
+ ;;
+ --prefix)
+ output="$output $prefix"
+ ;;
+ --exec-prefix=*)
+ ;;
+ --exec-prefix)
+ output="$output $exec_prefix"
+ ;;
+ --glib)
+ with_glib=yes
+ ;;
+ --version)
+ echo "@VERSION@"
+ exit 0
+ ;;
+ --api-version)
+ echo "@GPGME_CONFIG_API_VERSION@"
+ exit 0
+ ;;
+ --host)
+ echo "@GPGME_CONFIG_HOST@"
+ exit 0
+ ;;
+ --cflags)
+ result=
+ tmp_c=
+ tmp_g=
+ case "$thread_module" in
+ pthread) tmp_c="$cflags_pthread" ;;
+ esac
+ test "x$with_glib" = "xyes" && tmp_g="$cflags_glib"
+ for i in $cflags $tmp_c $assuan_cflags $gpg_error_cflags $tmp_g ; do
+ skip=no
+ case $i in
+ -I/usr/include|-I/include)
+ skip=yes
+ ;;
+ -I*)
+ for j in $result ; do
+ if test x"$j" = x"$i" ; then
+ skip=yes
+ break;
+ fi
+ done
+ ;;
+ esac
+ if test $skip = no ; then
+ result="$result $i"
+ fi
+ done
+ output="$output $result"
+ ;;
+ --libs)
+ result=
+ tmp_x=
+ case "$thread_module" in
+ pthread) tmp_l="-lgpgme-pthread"; tmp_x="$libs_pthread" ;;
+ *)
+ if test "x$with_glib" = "xyes" ; then
+ tmp_l="-lgpgme-glib"
+ tmp_x="$libs_glib"
+ else
+ tmp_l="-lgpgme"
+ fi
+ ;;
+ esac
+ for i in $libs $tmp_l $assuan_libs $gpg_error_libs $tmp_x; do
+ skip=no
+ case $i in
+ -L/usr/lib|-L/lib)
+ skip=yes
+ ;;
+ -L*|-l*)
+ for j in $result ; do
+ if test x"$j" = x"$i" ; then
+ skip=yes
+ break;
+ fi
+ done
+ ;;
+ esac
+ if test $skip = no ; then
+ result="$result $i"
+ fi
+ done
+ output="$output $result"
+ ;;
+ --thread=*)
+ for thread_mod in $thread_modules; do
+ if test "$thread_mod" = "$optarg"; then
+ thread_module="$optarg";
+ fi
+ done
+ if test "x$thread_module" = "x"; then
+ usage 1 1>&2
+ fi
+ ;;
+ --get-gpg)
+ output="$output @GPG@"
+ ;;
+ --get-gpgsm)
+ output="$output @GPGSM@"
+ ;;
+ *)
+ usage 1 1>&2
+ ;;
+ esac
+ shift
+done
+
+echo $output
diff --git a/src/gpgme-tool.c b/src/gpgme-tool.c
new file mode 100644
index 0000000..9591d15
--- /dev/null
+++ b/src/gpgme-tool.c
@@ -0,0 +1,3267 @@
+/* gpgme-tool.c - Assuan server exposing GnuPG Made Easy operations.
+ Copyright (C) 2009, 2010 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <getopt.h>
+#include <ctype.h>
+#include <stdarg.h>
+#ifdef HAVE_LOCALE_H
+#include <locale.h>
+#endif
+#ifdef HAVE_ARGP_H
+#include <argp.h>
+#endif
+
+#include <assuan.h>
+
+#include "gpgme.h"
+
+/* GCC attributes. */
+#if __GNUC__ >= 4
+# define GT_GCC_A_SENTINEL(a) __attribute__ ((sentinel(a)))
+#else
+# define GT_GCC_A_SENTINEL(a)
+#endif
+
+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 5 )
+# define GT_GCC_A_PRINTF(f, a) __attribute__ ((format (printf,f,a)))
+#else
+# define GT_GCC_A_PRINTF(f, a)
+#endif
+
+#define DIM(v) (sizeof(v)/sizeof((v)[0]))
+#define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \
+ *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
+#define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1))
+
+
+
+#ifndef HAVE_ARGP_H
+/* Minimal argp implementation. */
+
+/* Differences to ARGP:
+ argp_program_version: Required.
+ argp_program_bug_address: Required.
+ argp_program_version_hook: Not supported.
+ argp_err_exit_status: Required.
+ struct argp: Children and help_filter not supported.
+ argp_domain: Not supported.
+ struct argp_option: Group not supported. Options are printed in
+ order given. Flags OPTION_ALIAS, OPTION_DOC and OPTION_NO_USAGE
+ are not supported.
+ argp_parse: No flags are supported (ARGP_PARSE_ARGV0, ARGP_NO_ERRS,
+ ARGP_NO_ARGS, ARGP_IN_ORDER, ARGP_NO_HELP, ARGP_NO_EXIT,
+ ARGP_LONG_ONLY, ARGP_SILENT). ARGP must not be NULL.
+ argp_help: Flag ARGP_HELP_LONG_ONLY not supported.
+ argp_state: argc, argv, next may not be modified and should not be used. */
+
+extern const char *argp_program_version;
+extern const char *argp_program_bug_address;
+extern error_t argp_err_exit_status;
+
+struct argp_option
+{
+ const char *name;
+ int key;
+ const char *arg;
+#define OPTION_ARG_OPTIONAL 0x1
+#define OPTION_HIDDEN 0x2
+ int flags;
+ const char *doc;
+ int group;
+};
+
+struct argp;
+struct argp_state
+{
+ const struct argp *const root_argp;
+ int argc;
+ char **argv;
+ int next;
+ unsigned flags;
+ unsigned arg_num;
+ int quoted;
+ void *input;
+ void **child_inputs;
+ void *hook;
+ char *name;
+ FILE *err_stream;
+ FILE *out_stream;
+ void *pstate;
+};
+
+#ifdef EDEADLK
+# define ARGP_ERR_UNKNOWN EDEADLK /* POSIX */
+#else
+# define ARGP_ERR_UNKNOWN EDEADLOCK /* *GNU/kFreebsd does not define this) */
+#endif
+#define ARGP_KEY_ARG 0
+#define ARGP_KEY_ARGS 0x1000006
+#define ARGP_KEY_END 0x1000001
+#define ARGP_KEY_NO_ARGS 0x1000002
+#define ARGP_KEY_INIT 0x1000003
+#define ARGP_KEY_FINI 0x1000007
+#define ARGP_KEY_SUCCESS 0x1000004
+#define ARGP_KEY_ERROR 0x1000005
+typedef error_t (*argp_parser_t) (int key, char *arg, struct argp_state *state);
+
+struct argp
+{
+ const struct argp_option *options;
+ argp_parser_t parser;
+ const char *args_doc;
+ const char *doc;
+
+ const struct argp_child *children;
+ char *(*help_filter) (int key, const char *text, void *input);
+ const char *argp_domain;
+};
+
+#define ARGP_HELP_USAGE ARGP_HELP_SHORT_USAGE
+#define ARGP_HELP_SHORT_USAGE 0x02
+#define ARGP_HELP_SEE 0x04
+#define ARGP_HELP_LONG 0x08
+#define ARGP_HELP_PRE_DOC 0x10
+#define ARGP_HELP_POST_DOC 0x20
+#define ARGP_HELP_DOC (ARGP_HELP_PRE_DOC | ARGP_HELP_POST_DOC)
+#define ARGP_HELP_BUG_ADDR 0x40
+#define ARGP_HELP_EXIT_ERR 0x100
+#define ARGP_HELP_EXIT_OK 0x200
+#define ARGP_HELP_STD_ERR (ARGP_HELP_SEE | ARGP_HELP_EXIT_ERR)
+#define ARGP_HELP_STD_USAGE \
+ (ARGP_HELP_SHORT_USAGE | ARGP_HELP_SEE | ARGP_HELP_EXIT_ERR)
+#define ARGP_HELP_STD_HELP \
+ (ARGP_HELP_SHORT_USAGE | ARGP_HELP_LONG | ARGP_HELP_EXIT_OK \
+ | ARGP_HELP_DOC | ARGP_HELP_BUG_ADDR)
+
+
+void argp_error (const struct argp_state *state,
+ const char *fmt, ...) GT_GCC_A_PRINTF(2, 3);
+
+
+
+char *
+_argp_pname (char *name)
+{
+ char *pname = name;
+ char *bname = strrchr (pname, '/');
+ if (! bname)
+ bname = strrchr (pname, '\\');
+ if (bname)
+ pname = bname + 1;
+ return pname;
+}
+
+
+void
+_argp_state_help (const struct argp *argp, const struct argp_state *state,
+ FILE *stream, unsigned flags, char *name)
+{
+ if (state)
+ name = state->name;
+
+ if (flags & ARGP_HELP_SHORT_USAGE)
+ fprintf (stream, "Usage: %s [OPTIONS...] %s\n", name, argp->args_doc);
+ if (flags & ARGP_HELP_SEE)
+ fprintf (stream, "Try `%s --help' or `%s --usage' for more information.\n",
+ name, name);
+ if (flags & ARGP_HELP_PRE_DOC)
+ {
+ char buf[1024];
+ char *end;
+ strncpy (buf, argp->doc, sizeof (buf));
+ buf[sizeof (buf) - 1] = '\0';
+ end = strchr (buf, '\v');
+ if (end)
+ *end = '\0';
+ fprintf (stream, "%s\n%s", buf, buf[0] ? "\n" : "");
+ }
+ if (flags & ARGP_HELP_LONG)
+ {
+ const struct argp_option *opt = argp->options;
+ while (opt->key)
+ {
+ #define NSPACES 29
+ char spaces[NSPACES + 1] = " ";
+ int len = 0;
+ fprintf (stream, " ");
+ len += 2;
+ if (isascii (opt->key))
+ {
+ fprintf (stream, "-%c", opt->key);
+ len += 2;
+ if (opt->name)
+ {
+ fprintf (stream, ", ");
+ len += 2;
+ }
+ }
+ if (opt->name)
+ {
+ fprintf (stream, "--%s", opt->name);
+ len += 2 + strlen (opt->name);
+ }
+ if (opt->arg && (opt->flags & OPTION_ARG_OPTIONAL))
+ {
+ fprintf (stream, "[=%s]", opt->arg);
+ len += 3 + strlen (opt->arg);
+ }
+ else if (opt->arg)
+ {
+ fprintf (stream, "=%s", opt->arg);
+ len += 1 + strlen (opt->arg);
+ }
+ if (len >= NSPACES)
+ len = NSPACES - 1;
+ spaces[NSPACES - len] = '\0';
+ fprintf (stream, "%s%s\n", spaces, opt->doc);
+ opt++;
+ }
+ fprintf (stream, " -?, --help Give this help list\n");
+ fprintf (stream, " --usage Give a short usage "
+ "message\n");
+ }
+ if (flags & ARGP_HELP_POST_DOC)
+ {
+ char buf[1024];
+ char *end;
+ strncpy (buf, argp->doc, sizeof (buf));
+ buf[sizeof (buf) - 1] = '\0';
+ end = strchr (buf, '\v');
+ if (end)
+ {
+ end++;
+ if (*end)
+ fprintf (stream, "\n%s\n", end);
+ }
+ fprintf (stream, "\nMandatory or optional arguments to long options are also mandatory or optional\n");
+ fprintf (stream, "for any corresponding short options.\n");
+ }
+ if (flags & ARGP_HELP_BUG_ADDR)
+ fprintf (stream, "\nReport bugs to %s.\n", argp_program_bug_address);
+
+ if (flags & ARGP_HELP_EXIT_ERR)
+ exit (argp_err_exit_status);
+ if (flags & ARGP_HELP_EXIT_OK)
+ exit (0);
+}
+
+
+void
+argp_usage (const struct argp_state *state)
+{
+ _argp_state_help (state->root_argp, state, state->err_stream,
+ ARGP_HELP_STD_USAGE, state->name);
+}
+
+
+void
+argp_state_help (const struct argp_state *state, FILE *stream, unsigned flags)
+{
+ _argp_state_help (state->root_argp, state, stream, flags, state->name);
+}
+
+
+void
+argp_error (const struct argp_state *state, const char *fmt, ...)
+{
+ va_list ap;
+
+ fprintf (state->err_stream, "%s: ", state->name);
+ va_start (ap, fmt);
+ vfprintf (state->err_stream, fmt, ap);
+ va_end (ap);
+ fprintf (state->err_stream, "\n");
+ argp_state_help (state, state->err_stream, ARGP_HELP_STD_ERR);
+ exit (argp_err_exit_status);
+}
+
+
+void
+argp_help (const struct argp *argp, FILE *stream, unsigned flags, char *name)
+{
+ _argp_state_help (argp, NULL, stream, flags, name);
+}
+
+
+error_t
+argp_parse (const struct argp *argp, int argc,
+ char **argv, unsigned flags, int *arg_index, void *input)
+{
+ int rc = 0;
+ struct argp_state state = { argp, argc, argv, 1, flags, 0, 0, input,
+ NULL, NULL, _argp_pname (argv[0]),
+ stderr, stdout, NULL };
+ /* All non-option arguments are collected at the beginning of
+ &argv[1] during processing. This is a counter for their number. */
+ int non_opt_args = 0;
+
+ rc = argp->parser (ARGP_KEY_INIT, NULL, &state);
+ if (rc && rc != ARGP_ERR_UNKNOWN)
+ goto argperror;
+
+ while (state.next < state.argc - non_opt_args)
+ {
+ int idx = state.next;
+ state.next++;
+
+ if (! strcasecmp (state.argv[idx], "--"))
+ {
+ state.quoted = idx;
+ continue;
+ }
+
+ if (state.quoted || state.argv[idx][0] != '-')
+ {
+ char *arg_saved = state.argv[idx];
+ non_opt_args++;
+ memmove (&state.argv[idx], &state.argv[idx + 1],
+ (state.argc - 1 - idx) * sizeof (char *));
+ state.argv[argc - 1] = arg_saved;
+ state.next--;
+ }
+ else if (! strcasecmp (state.argv[idx], "--help")
+ || !strcmp (state.argv[idx], "-?"))
+ {
+ argp_state_help (&state, state.out_stream, ARGP_HELP_STD_HELP);
+ }
+ else if (! strcasecmp (state.argv[idx], "--usage"))
+ {
+ argp_state_help (&state, state.out_stream,
+ ARGP_HELP_USAGE | ARGP_HELP_EXIT_OK);
+ }
+ else if (! strcasecmp (state.argv[idx], "--version")
+ || !strcmp (state.argv[idx], "-V"))
+ {
+ fprintf (state.out_stream, "%s\n", argp_program_version);
+ exit (0);
+ }
+ else
+ {
+ /* Search for option and call parser with its KEY. */
+ int key = ARGP_KEY_ARG; /* Just some dummy value. */
+ const struct argp_option *opt = argp->options;
+ char *arg = NULL;
+ int found = 0;
+
+ /* Check for --opt=value syntax. */
+ arg = strchr (state.argv[idx], '=');
+ if (arg)
+ {
+ *arg = '\0';
+ arg++;
+ }
+
+ if (state.argv[idx][1] != '-')
+ key = state.argv[idx][1];
+
+ while (! found && opt->key)
+ {
+ if (key == opt->key
+ || (key == ARGP_KEY_ARG
+ && ! strcasecmp (&state.argv[idx][2], opt->name)))
+ {
+ if (arg && !opt->arg)
+ argp_error (&state, "Option %s does not take an argument",
+ state.argv[idx]);
+ if (opt->arg && state.next < state.argc
+ && state.argv[idx + 1][0] != '-')
+ {
+ arg = state.argv[idx + 1];
+ state.next++;
+ }
+ if (opt->arg && !(opt->flags & OPTION_ARG_OPTIONAL))
+ argp_error (&state, "Option %s requires an argument",
+ state.argv[idx]);
+
+ rc = argp->parser (opt->key, arg, &state);
+ if (rc == ARGP_ERR_UNKNOWN)
+ break;
+ else if (rc)
+ goto argperror;
+ found = 1;
+ }
+ opt++;
+ }
+ if (! found)
+ argp_error (&state, "Unknown option %s", state.argv[idx]);
+ }
+ }
+
+ while (state.next < state.argc)
+ {
+ /* Call parser for all non-option args. */
+ int idx = state.next;
+ state.next++;
+ rc = argp->parser (ARGP_KEY_ARG, state.argv[idx], &state);
+ if (rc && rc != ARGP_ERR_UNKNOWN)
+ goto argperror;
+ if (rc == ARGP_ERR_UNKNOWN)
+ {
+ int old_next = state.next;
+ rc = argp->parser (ARGP_KEY_ARGS, NULL, &state);
+ if (rc == ARGP_ERR_UNKNOWN)
+ {
+ argp_error (&state, "Too many arguments");
+ goto argperror;
+ }
+ if (! rc && state.next == old_next)
+ {
+ state.arg_num += state.argc - state.next;
+ state.next = state.argc;
+ }
+ }
+ else
+ state.arg_num++;
+ }
+
+ if (state.arg_num == 0)
+ {
+ rc = argp->parser (ARGP_KEY_NO_ARGS, NULL, &state);
+ if (rc && rc != ARGP_ERR_UNKNOWN)
+ goto argperror;
+ }
+ if (state.next == state.argc)
+ {
+ rc = argp->parser (ARGP_KEY_END, NULL, &state);
+ if (rc && rc != ARGP_ERR_UNKNOWN)
+ goto argperror;
+ }
+ rc = argp->parser (ARGP_KEY_FINI, NULL, &state);
+ if (rc && rc != ARGP_ERR_UNKNOWN)
+ goto argperror;
+
+ rc = 0;
+ argp->parser (ARGP_KEY_SUCCESS, NULL, &state);
+
+ argperror:
+ if (rc)
+ {
+ argp_error (&state, "unexpected error: %s", strerror (rc));
+ argp->parser (ARGP_KEY_ERROR, NULL, &state);
+ }
+
+ argp->parser (ARGP_KEY_FINI, NULL, &state);
+
+ if (arg_index)
+ *arg_index = state.next - 1;
+
+ return 0;
+}
+#endif
+
+
+/* SUPPORT. */
+FILE *log_stream;
+char *program_name = "gpgme-tool";
+
+#define spacep(p) (*(p) == ' ' || *(p) == '\t')
+
+
+void log_error (int status, gpg_error_t errnum,
+ const char *fmt, ...) GT_GCC_A_PRINTF(3,4);
+
+
+void
+log_init (void)
+{
+ log_stream = stderr;
+}
+
+
+void
+log_error (int status, gpg_error_t errnum, const char *fmt, ...)
+{
+ va_list ap;
+
+ fprintf (log_stream, "%s: ", program_name);
+ va_start (ap, fmt);
+ vfprintf (log_stream, fmt, ap);
+ va_end (ap);
+ if (errnum)
+ fprintf (log_stream, ": %s <%s>", gpg_strerror (errnum),
+ gpg_strsource (errnum));
+ fprintf (log_stream, "\n");
+ if (status)
+ exit (status);
+}
+
+
+/* Note that it is sufficient to allocate the target string D as long
+ as the source string S, i.e.: strlen(s)+1;. D == S is allowed. */
+static void
+strcpy_escaped_plus (char *d, const char *s)
+{
+ while (*s)
+ {
+ if (*s == '%' && s[1] && s[2])
+ {
+ s++;
+ *d++ = xtoi_2 (s);
+ s += 2;
+ }
+ else if (*s == '+')
+ *d++ = ' ', s++;
+ else
+ *d++ = *s++;
+ }
+ *d = 0;
+}
+
+
+/* Check whether the option NAME appears in LINE. */
+static int
+has_option (const char *line, const char *name)
+{
+ const char *s;
+ int n = strlen (name);
+
+ s = strstr (line, name);
+ return (s && (s == line || spacep (s-1)) && (!s[n] || spacep (s+n)));
+}
+
+/* Skip over options. It is assumed that leading spaces have been
+ removed (this is the case for lines passed to a handler from
+ assuan). Blanks after the options are also removed. */
+static char *
+skip_options (char *line)
+{
+ while ( *line == '-' && line[1] == '-' )
+ {
+ while (*line && !spacep (line))
+ line++;
+ while (spacep (line))
+ line++;
+ }
+ return line;
+}
+
+
+
+
+typedef gpg_error_t (*result_xml_write_cb_t) (void *hook, const void *buf,
+ size_t len);
+
+struct result_xml_state
+{
+ int indent;
+ result_xml_write_cb_t cb;
+ void *hook;
+
+#define MAX_TAGS 20
+ int next_tag;
+ char *tag[MAX_TAGS];
+ int had_data[MAX_TAGS];
+};
+
+
+void
+result_init (struct result_xml_state *state, int indent,
+ result_xml_write_cb_t cb, void *hook)
+{
+ memset (state, '\0', sizeof (*state));
+ state->indent = indent;
+ state->cb = cb;
+ state->hook = hook;
+}
+
+
+gpg_error_t
+result_xml_indent (struct result_xml_state *state)
+{
+ char spaces[state->indent + 1];
+ int i;
+ for (i = 0; i < state->indent; i++)
+ spaces[i] = ' ';
+ spaces[i] = '\0';
+ return (*state->cb) (state->hook, spaces, i);
+}
+
+
+gpg_error_t
+result_xml_tag_start (struct result_xml_state *state, char *name, ...)
+{
+ result_xml_write_cb_t cb = state->cb;
+ void *hook = state->hook;
+ va_list ap;
+ char *attr;
+ char *attr_val;
+
+ va_start (ap, name);
+
+ if (state->next_tag > 0)
+ {
+ if (! state->had_data[state->next_tag - 1])
+ {
+ (*cb) (hook, ">\n", 2);
+ (*cb) (hook, NULL, 0);
+ }
+ state->had_data[state->next_tag - 1] = 1;
+ }
+
+ result_xml_indent (state);
+ (*cb) (hook, "<", 1);
+ (*cb) (hook, name, strlen (name));
+
+ state->tag[state->next_tag] = name;
+ state->had_data[state->next_tag] = 0;
+ state->indent += 2;
+ state->next_tag++;
+
+ while (1)
+ {
+ attr = va_arg (ap, char *);
+ if (attr == NULL)
+ break;
+
+ attr_val = va_arg (ap, char *);
+ if (attr_val == NULL)
+ attr_val = "(null)";
+
+ (*cb) (hook, " ", 1);
+ (*cb) (hook, attr, strlen (attr));
+ (*cb) (hook, "=\"", 2);
+ (*cb) (hook, attr_val, strlen (attr_val));
+ (*cb) (hook, "\"", 1);
+ }
+ va_end (ap);
+ return 0;
+}
+
+
+gpg_error_t
+result_xml_tag_data (struct result_xml_state *state, char *data)
+{
+ result_xml_write_cb_t cb = state->cb;
+ void *hook = state->hook;
+
+ if (state->had_data[state->next_tag - 1])
+ {
+ (*cb) (hook, "\n", 2);
+ (*cb) (hook, NULL, 0);
+ result_xml_indent (state);
+ }
+ else
+ (*cb) (hook, ">", 1);
+ state->had_data[state->next_tag - 1] = 2;
+
+ (*cb) (hook, data, strlen (data));
+
+ return 0;
+}
+
+
+gpg_error_t
+result_xml_tag_end (struct result_xml_state *state)
+{
+ result_xml_write_cb_t cb = state->cb;
+ void *hook = state->hook;
+
+ state->next_tag--;
+ state->indent -= 2;
+
+ if (state->had_data[state->next_tag])
+ {
+ if (state->had_data[state->next_tag] == 1)
+ result_xml_indent (state);
+ (*cb) (hook, "</", 2);
+ (*cb) (hook, state->tag[state->next_tag],
+ strlen (state->tag[state->next_tag]));
+ (*cb) (hook, ">\n", 2);
+ (*cb) (hook, NULL, 0);
+ }
+ else
+ {
+ (*cb) (hook, " />\n", 4);
+ (*cb) (hook, NULL, 0);
+ }
+ return 0;
+}
+
+
+gpg_error_t
+result_add_error (struct result_xml_state *state, char *name, gpg_error_t err)
+{
+ char code[20];
+ char msg[1024];
+ snprintf (code, sizeof (code) - 1, "0x%x", err);
+ snprintf (msg, sizeof (msg) - 1, "%s &lt;%s&gt;",
+ gpg_strerror (err), gpg_strsource (err));
+ result_xml_tag_start (state, name, "value", code, NULL);
+ result_xml_tag_data (state, msg);
+ result_xml_tag_end (state);
+ return 0;
+}
+
+
+gpg_error_t
+result_add_pubkey_algo (struct result_xml_state *state,
+ char *name, gpgme_pubkey_algo_t algo)
+{
+ char code[20];
+ char msg[80];
+ snprintf (code, sizeof (code) - 1, "0x%x", algo);
+ snprintf (msg, sizeof (msg) - 1, "%s",
+ gpgme_pubkey_algo_name (algo));
+ result_xml_tag_start (state, name, "value", code, NULL);
+ result_xml_tag_data (state, msg);
+ result_xml_tag_end (state);
+ return 0;
+}
+
+
+gpg_error_t
+result_add_hash_algo (struct result_xml_state *state,
+ char *name, gpgme_hash_algo_t algo)
+{
+ char code[20];
+ char msg[80];
+ snprintf (code, sizeof (code) - 1, "0x%x", algo);
+ snprintf (msg, sizeof (msg) - 1, "%s",
+ gpgme_hash_algo_name (algo));
+ result_xml_tag_start (state, name, "value", code, NULL);
+ result_xml_tag_data (state, msg);
+ result_xml_tag_end (state);
+ return 0;
+}
+
+
+gpg_error_t
+result_add_keyid (struct result_xml_state *state, char *name, char *keyid)
+{
+ result_xml_tag_start (state, name, NULL);
+ result_xml_tag_data (state, keyid);
+ result_xml_tag_end (state);
+ return 0;
+}
+
+
+gpg_error_t
+result_add_fpr (struct result_xml_state *state, char *name, char *fpr)
+{
+ result_xml_tag_start (state, name, NULL);
+ result_xml_tag_data (state, fpr);
+ result_xml_tag_end (state);
+ return 0;
+}
+
+
+gpg_error_t
+result_add_timestamp (struct result_xml_state *state, char *name,
+ unsigned int timestamp)
+{
+ char code[20];
+
+ snprintf (code, sizeof (code) - 1, "%ui", timestamp);
+ result_xml_tag_start (state, name, "unix", code, NULL);
+ result_xml_tag_end (state);
+ return 0;
+}
+
+
+gpg_error_t
+result_add_sig_mode (struct result_xml_state *state, char *name,
+ gpgme_sig_mode_t sig_mode)
+{
+ char *mode;
+ char code[20];
+
+ snprintf (code, sizeof (code) - 1, "%i", sig_mode);
+ switch (sig_mode)
+ {
+ case GPGME_SIG_MODE_NORMAL:
+ mode = "normal";
+ break;
+ case GPGME_SIG_MODE_DETACH:
+ mode = "detach";
+ break;
+ case GPGME_SIG_MODE_CLEAR:
+ mode = "clear";
+ break;
+ default:
+ mode = "unknown";
+ }
+
+ result_xml_tag_start (state, name, "type", mode, "value", code, NULL);
+ result_xml_tag_data (state, mode);
+ result_xml_tag_end (state);
+ return 0;
+}
+
+
+gpg_error_t
+result_add_value (struct result_xml_state *state,
+ char *name, unsigned int val)
+{
+ char code[20];
+
+ snprintf (code, sizeof (code) - 1, "0x%x", val);
+ result_xml_tag_start (state, name, "value", code, NULL);
+ result_xml_tag_end (state);
+ return 0;
+}
+
+
+gpg_error_t
+result_add_string (struct result_xml_state *state,
+ char *name, char *str)
+{
+ result_xml_tag_start (state, name, NULL);
+ result_xml_tag_data (state, str);
+ result_xml_tag_end (state);
+ return 0;
+}
+
+
+gpg_error_t
+result_encrypt_to_xml (gpgme_ctx_t ctx, int indent,
+ result_xml_write_cb_t cb, void *hook)
+{
+ struct result_xml_state state;
+ gpgme_encrypt_result_t res = gpgme_op_encrypt_result (ctx);
+ gpgme_invalid_key_t inv_recp;
+
+ if (! res)
+ return 0;
+
+ result_init (&state, indent, cb, hook);
+ result_xml_tag_start (&state, "encrypt-result", NULL);
+
+ inv_recp = res->invalid_recipients;
+ if (inv_recp)
+ {
+ result_xml_tag_start (&state, "invalid-recipients", NULL);
+
+ while (inv_recp)
+ {
+ result_xml_tag_start (&state, "invalid-key", NULL);
+ if (inv_recp->fpr)
+ result_add_fpr (&state, "fpr", inv_recp->fpr);
+ result_add_error (&state, "reason", inv_recp->reason);
+ result_xml_tag_end (&state);
+ inv_recp = inv_recp->next;
+ }
+ result_xml_tag_end (&state);
+ }
+ result_xml_tag_end (&state);
+
+ return 0;
+}
+
+
+gpg_error_t
+result_decrypt_to_xml (gpgme_ctx_t ctx, int indent,
+ result_xml_write_cb_t cb, void *hook)
+{
+ struct result_xml_state state;
+ gpgme_decrypt_result_t res = gpgme_op_decrypt_result (ctx);
+ gpgme_recipient_t recp;
+
+ if (! res)
+ return 0;
+
+ result_init (&state, indent, cb, hook);
+ result_xml_tag_start (&state, "decrypt-result", NULL);
+
+ if (res->file_name)
+ {
+ result_xml_tag_start (&state, "file-name", NULL);
+ result_xml_tag_data (&state, res->file_name);
+ result_xml_tag_end (&state);
+ }
+ if (res->unsupported_algorithm)
+ {
+ result_xml_tag_start (&state, "unsupported-alogorithm", NULL);
+ result_xml_tag_data (&state, res->unsupported_algorithm);
+ result_xml_tag_end (&state);
+ }
+ if (res->wrong_key_usage)
+ {
+ result_xml_tag_start (&state, "wrong-key-usage", NULL);
+ result_xml_tag_end (&state);
+ }
+
+ recp = res->recipients;
+ if (recp)
+ {
+ result_xml_tag_start (&state, "recipients", NULL);
+ while (recp)
+ {
+ result_xml_tag_start (&state, "recipient", NULL);
+ result_add_keyid (&state, "keyid", recp->keyid);
+ result_add_pubkey_algo (&state, "pubkey-algo", recp->pubkey_algo);
+ result_add_error (&state, "status", recp->status);
+ result_xml_tag_end (&state);
+ recp = recp->next;
+ }
+ result_xml_tag_end (&state);
+ }
+ result_xml_tag_end (&state);
+
+ return 0;
+}
+
+
+gpg_error_t
+result_sign_to_xml (gpgme_ctx_t ctx, int indent,
+ result_xml_write_cb_t cb, void *hook)
+{
+ struct result_xml_state state;
+ gpgme_sign_result_t res = gpgme_op_sign_result (ctx);
+ gpgme_invalid_key_t inv_key;
+ gpgme_new_signature_t new_sig;
+
+ if (! res)
+ return 0;
+
+ result_init (&state, indent, cb, hook);
+ result_xml_tag_start (&state, "sign-result", NULL);
+
+ inv_key = res->invalid_signers;
+ if (inv_key)
+ {
+ result_xml_tag_start (&state, "invalid-signers", NULL);
+
+ while (inv_key)
+ {
+ result_xml_tag_start (&state, "invalid-key", NULL);
+ if (inv_key->fpr)
+ result_add_fpr (&state, "fpr", inv_key->fpr);
+ result_add_error (&state, "reason", inv_key->reason);
+ result_xml_tag_end (&state);
+ inv_key = inv_key->next;
+ }
+ result_xml_tag_end (&state);
+ }
+
+ new_sig = res->signatures;
+ if (new_sig)
+ {
+ result_xml_tag_start (&state, "signatures", NULL);
+
+ while (new_sig)
+ {
+ result_xml_tag_start (&state, "new-signature", NULL);
+ result_add_sig_mode (&state, "type", new_sig->type);
+ result_add_pubkey_algo (&state, "pubkey-algo", new_sig->pubkey_algo);
+ result_add_hash_algo (&state, "hash-algo", new_sig->hash_algo);
+ result_add_timestamp (&state, "timestamp", new_sig->timestamp);
+ if (new_sig->fpr)
+ result_add_fpr (&state, "fpr", new_sig->fpr);
+ result_add_value (&state, "sig-class", new_sig->sig_class);
+
+ result_xml_tag_end (&state);
+ new_sig = new_sig->next;
+ }
+ result_xml_tag_end (&state);
+ }
+
+ result_xml_tag_end (&state);
+
+ return 0;
+}
+
+
+gpg_error_t
+result_verify_to_xml (gpgme_ctx_t ctx, int indent,
+ result_xml_write_cb_t cb, void *hook)
+{
+ struct result_xml_state state;
+ gpgme_verify_result_t res = gpgme_op_verify_result (ctx);
+ gpgme_signature_t sig;
+
+ if (! res)
+ return 0;
+
+ result_init (&state, indent, cb, hook);
+ result_xml_tag_start (&state, "verify-result", NULL);
+
+ if (res->file_name)
+ {
+ result_xml_tag_start (&state, "file-name", NULL);
+ result_xml_tag_data (&state, res->file_name);
+ result_xml_tag_end (&state);
+ }
+
+ sig = res->signatures;
+ if (sig)
+ {
+ result_xml_tag_start (&state, "signatures", NULL);
+
+ while (sig)
+ {
+ result_xml_tag_start (&state, "signature", NULL);
+
+ /* FIXME: Could be done better. */
+ result_add_value (&state, "summary", sig->summary);
+ if (sig->fpr)
+ result_add_fpr (&state, "fpr", sig->fpr);
+ result_add_error (&state, "status", sig->status);
+ /* FIXME: notations */
+ result_add_timestamp (&state, "timestamp", sig->timestamp);
+ result_add_timestamp (&state, "exp-timestamp", sig->exp_timestamp);
+ result_add_value (&state, "wrong-key-usage", sig->wrong_key_usage);
+ result_add_value (&state, "pka-trust", sig->pka_trust);
+ result_add_value (&state, "chain-model", sig->chain_model);
+ result_add_value (&state, "validity", sig->validity);
+ result_add_error (&state, "validity-reason", sig->validity_reason);
+ result_add_pubkey_algo (&state, "pubkey-algo", sig->pubkey_algo);
+ result_add_hash_algo (&state, "hash-algo", sig->hash_algo);
+ if (sig->pka_address)
+ result_add_string (&state, "pka_address", sig->pka_address);
+
+ result_xml_tag_end (&state);
+ sig = sig->next;
+ }
+ result_xml_tag_end (&state);
+ }
+
+ result_xml_tag_end (&state);
+
+ return 0;
+}
+
+
+gpg_error_t
+result_import_to_xml (gpgme_ctx_t ctx, int indent,
+ result_xml_write_cb_t cb, void *hook)
+{
+ struct result_xml_state state;
+ gpgme_import_result_t res = gpgme_op_import_result (ctx);
+ gpgme_import_status_t stat;
+
+ if (! res)
+ return 0;
+
+ result_init (&state, indent, cb, hook);
+ result_xml_tag_start (&state, "import-result", NULL);
+
+ result_add_value (&state, "considered", res->considered);
+ result_add_value (&state, "no-user-id", res->no_user_id);
+ result_add_value (&state, "imported", res->imported);
+ result_add_value (&state, "imported-rsa", res->imported_rsa);
+ result_add_value (&state, "unchanged", res->unchanged);
+ result_add_value (&state, "new-user-ids", res->new_user_ids);
+ result_add_value (&state, "new-sub-keys", res->new_sub_keys);
+ result_add_value (&state, "new-signatures", res->new_signatures);
+ result_add_value (&state, "new-revocations", res->new_revocations);
+ result_add_value (&state, "secret-read", res->secret_read);
+ result_add_value (&state, "secret-imported", res->secret_imported);
+ result_add_value (&state, "secret-unchanged", res->secret_unchanged);
+ result_add_value (&state, "skipped-new-keys", res->skipped_new_keys);
+ result_add_value (&state, "not-imported", res->not_imported);
+
+ stat = res->imports;
+ if (stat)
+ {
+ result_xml_tag_start (&state, "imports", NULL);
+
+ while (stat)
+ {
+ result_xml_tag_start (&state, "import-status", NULL);
+
+ if (stat->fpr)
+ result_add_fpr (&state, "fpr", stat->fpr);
+ result_add_error (&state, "result", stat->result);
+ /* FIXME: Could be done better. */
+ result_add_value (&state, "status", stat->status);
+
+ result_xml_tag_end (&state);
+ stat = stat->next;
+ }
+ result_xml_tag_end (&state);
+ }
+
+ result_xml_tag_end (&state);
+
+ return 0;
+}
+
+
+gpg_error_t
+result_genkey_to_xml (gpgme_ctx_t ctx, int indent,
+ result_xml_write_cb_t cb, void *hook)
+{
+ struct result_xml_state state;
+ gpgme_genkey_result_t res = gpgme_op_genkey_result (ctx);
+
+ if (! res)
+ return 0;
+
+ result_init (&state, indent, cb, hook);
+ result_xml_tag_start (&state, "genkey-result", NULL);
+
+ result_add_value (&state, "primary", res->primary);
+ result_add_value (&state, "sub", res->sub);
+ if (res->fpr)
+ result_add_fpr (&state, "fpr", res->fpr);
+
+ result_xml_tag_end (&state);
+
+ return 0;
+}
+
+
+gpg_error_t
+result_keylist_to_xml (gpgme_ctx_t ctx, int indent,
+ result_xml_write_cb_t cb, void *hook)
+{
+ struct result_xml_state state;
+ gpgme_keylist_result_t res = gpgme_op_keylist_result (ctx);
+
+ if (! res)
+ return 0;
+
+ result_init (&state, indent, cb, hook);
+ result_xml_tag_start (&state, "keylist-result", NULL);
+
+ result_add_value (&state, "truncated", res->truncated);
+
+ result_xml_tag_end (&state);
+
+ return 0;
+}
+
+
+gpg_error_t
+result_vfs_mount_to_xml (gpgme_ctx_t ctx, int indent,
+ result_xml_write_cb_t cb, void *hook)
+{
+ struct result_xml_state state;
+ gpgme_vfs_mount_result_t res = gpgme_op_vfs_mount_result (ctx);
+
+ if (! res)
+ return 0;
+
+ result_init (&state, indent, cb, hook);
+ result_xml_tag_start (&state, "vfs-mount-result", NULL);
+
+ result_add_string (&state, "mount-dir", res->mount_dir);
+
+ result_xml_tag_end (&state);
+
+ return 0;
+}
+
+
+typedef enum status
+ {
+ STATUS_PROTOCOL,
+ STATUS_PROGRESS,
+ STATUS_ENGINE,
+ STATUS_ARMOR,
+ STATUS_TEXTMODE,
+ STATUS_INCLUDE_CERTS,
+ STATUS_KEYLIST_MODE,
+ STATUS_RECIPIENT,
+ STATUS_ENCRYPT_RESULT
+ } status_t;
+
+const char *status_string[] =
+ {
+ "PROTOCOL",
+ "PROGRESS",
+ "ENGINE",
+ "ARMOR",
+ "TEXTMODE",
+ "INCLUDE_CERTS",
+ "KEYLIST_MODE",
+ "RECIPIENT",
+ "ENCRYPT_RESULT"
+ };
+
+struct gpgme_tool
+{
+ gpgme_ctx_t ctx;
+#define MAX_RECIPIENTS 10
+ gpgme_key_t recipients[MAX_RECIPIENTS + 1];
+ int recipients_nr;
+
+ gpg_error_t (*write_status) (void *hook, const char *status, const char *msg);
+ void *write_status_hook;
+ gpg_error_t (*write_data) (void *hook, const void *buf, size_t len);
+ void *write_data_hook;
+};
+typedef struct gpgme_tool *gpgme_tool_t;
+
+
+/* Forward declaration. */
+void gt_write_status (gpgme_tool_t gt,
+ status_t status, ...) GT_GCC_A_SENTINEL(0);
+
+void
+_gt_progress_cb (void *opaque, const char *what,
+ int type, int current, int total)
+{
+ gpgme_tool_t gt = opaque;
+ char buf[100];
+
+ snprintf (buf, sizeof (buf), "0x%02x %i %i", type, current, total);
+ gt_write_status (gt, STATUS_PROGRESS, what, buf, NULL);
+}
+
+
+gpg_error_t
+_gt_gpgme_new (gpgme_tool_t gt, gpgme_ctx_t *ctx)
+{
+ gpg_error_t err;
+
+ err = gpgme_new (ctx);
+ if (err)
+ return err;
+ gpgme_set_progress_cb (*ctx, _gt_progress_cb, gt);
+ return 0;
+}
+
+
+void
+gt_init (gpgme_tool_t gt)
+{
+ memset (gt, '\0', sizeof (*gt));
+ gpg_error_t err;
+
+ err = _gt_gpgme_new (gt, &gt->ctx);
+ if (err)
+ log_error (1, err, "can't create gpgme context");
+}
+
+
+gpg_error_t
+gt_signers_add (gpgme_tool_t gt, const char *fpr)
+{
+ gpg_error_t err;
+ gpgme_key_t key;
+
+ err = gpgme_get_key (gt->ctx, fpr, &key, 0);
+ if (err)
+ return err;
+
+ return gpgme_signers_add (gt->ctx, key);
+}
+
+
+gpg_error_t
+gt_signers_clear (gpgme_tool_t gt)
+{
+ gpgme_signers_clear (gt->ctx);
+ return 0;
+}
+
+
+gpg_error_t
+gt_get_key (gpgme_tool_t gt, const char *pattern, gpgme_key_t *r_key)
+{
+ gpgme_ctx_t ctx;
+ gpgme_ctx_t listctx;
+ gpgme_error_t err;
+ gpgme_key_t key;
+
+ if (!gt || !r_key || !pattern)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ ctx = gt->ctx;
+
+ err = gpgme_new (&listctx);
+ if (err)
+ return err;
+
+ {
+ gpgme_protocol_t proto;
+ gpgme_engine_info_t info;
+
+ /* Clone the relevant state. */
+ proto = gpgme_get_protocol (ctx);
+ /* The g13 protocol does not allow keylisting, we need to choose
+ something else. */
+ if (proto == GPGME_PROTOCOL_G13)
+ proto = GPGME_PROTOCOL_OpenPGP;
+
+ gpgme_set_protocol (listctx, proto);
+ gpgme_set_keylist_mode (listctx, gpgme_get_keylist_mode (ctx));
+ info = gpgme_ctx_get_engine_info (ctx);
+ while (info && info->protocol != proto)
+ info = info->next;
+ if (info)
+ gpgme_ctx_set_engine_info (listctx, proto,
+ info->file_name, info->home_dir);
+ }
+
+ err = gpgme_op_keylist_start (listctx, pattern, 0);
+ if (!err)
+ err = gpgme_op_keylist_next (listctx, r_key);
+ if (!err)
+ {
+ try_next_key:
+ err = gpgme_op_keylist_next (listctx, &key);
+ if (gpgme_err_code (err) == GPG_ERR_EOF)
+ err = 0;
+ else
+ {
+ if (!err
+ && *r_key && (*r_key)->subkeys && (*r_key)->subkeys->fpr
+ && key && key->subkeys && key->subkeys->fpr
+ && !strcmp ((*r_key)->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
+ problem may occur with corrupted keyrings and has
+ been noticed often with gpgsm. In fact gpgsm uses a
+ similar hack to sort out such duplicates but it can't
+ do that while listing keys. */
+ gpgme_key_unref (key);
+ goto try_next_key;
+ }
+ if (!err)
+ {
+ gpgme_key_unref (key);
+ err = gpg_error (GPG_ERR_AMBIGUOUS_NAME);
+ }
+ gpgme_key_unref (*r_key);
+ }
+ }
+ gpgme_release (listctx);
+
+ if (! err)
+ gt_write_status (gt, STATUS_RECIPIENT,
+ ((*r_key)->subkeys && (*r_key)->subkeys->fpr) ?
+ (*r_key)->subkeys->fpr : "invalid", NULL);
+ return err;
+}
+
+
+gpg_error_t
+gt_recipients_add (gpgme_tool_t gt, const char *pattern)
+{
+ gpg_error_t err;
+ gpgme_key_t key;
+
+ if (gt->recipients_nr >= MAX_RECIPIENTS)
+ return gpg_error_from_errno (ENOMEM);
+
+ if (gpgme_get_protocol (gt->ctx) == GPGME_PROTOCOL_UISERVER)
+ err = gpgme_key_from_uid (&key, pattern);
+ else
+ err = gt_get_key (gt, pattern, &key);
+ if (err)
+ return err;
+
+ gt->recipients[gt->recipients_nr++] = key;
+ return 0;
+}
+
+
+void
+gt_recipients_clear (gpgme_tool_t gt)
+{
+ int idx;
+
+ for (idx = 0; idx < gt->recipients_nr; idx++)
+ gpgme_key_unref (gt->recipients[idx]);
+ memset (gt->recipients, '\0', gt->recipients_nr * sizeof (gpgme_key_t));
+ gt->recipients_nr = 0;
+}
+
+
+gpg_error_t
+gt_reset (gpgme_tool_t gt)
+{
+ gpg_error_t err;
+ gpgme_ctx_t ctx;
+
+ err = _gt_gpgme_new (gt, &ctx);
+ if (err)
+ return err;
+
+ gpgme_release (gt->ctx);
+ gt->ctx = ctx;
+ gt_recipients_clear (gt);
+ return 0;
+}
+
+
+void
+gt_write_status (gpgme_tool_t gt, status_t status, ...)
+{
+ va_list ap;
+ const char *text;
+ char buf[950];
+ char *p;
+ size_t n;
+ gpg_error_t err;
+
+ va_start (ap, status);
+ p = buf;
+ n = 0;
+ while ((text = va_arg (ap, const char *)))
+ {
+ if (n)
+ {
+ *p++ = ' ';
+ n++;
+ }
+ while (*text && n < sizeof (buf) - 2)
+ {
+ *p++ = *text++;
+ n++;
+ }
+ }
+ *p = 0;
+ va_end (ap);
+
+ err = gt->write_status (gt->write_status_hook, status_string[status], buf);
+ if (err)
+ log_error (1, err, "can't write status line");
+}
+
+
+gpg_error_t
+gt_write_data (gpgme_tool_t gt, const void *buf, size_t len)
+{
+ return gt->write_data (gt->write_data_hook, buf, len);
+}
+
+
+gpg_error_t
+gt_get_engine_info (gpgme_tool_t gt, gpgme_protocol_t proto)
+{
+ gpgme_engine_info_t info;
+ info = gpgme_ctx_get_engine_info (gt->ctx);
+ while (info)
+ {
+ if (proto == GPGME_PROTOCOL_UNKNOWN || proto == info->protocol)
+ gt_write_status (gt, STATUS_ENGINE,
+ gpgme_get_protocol_name (info->protocol),
+ info->file_name, info->version,
+ info->req_version, info->home_dir, NULL);
+ info = info->next;
+ }
+ return 0;
+}
+
+
+gpgme_protocol_t
+gt_protocol_from_name (const char *name)
+{
+ if (! strcasecmp (name, gpgme_get_protocol_name (GPGME_PROTOCOL_OpenPGP)))
+ return GPGME_PROTOCOL_OpenPGP;
+ if (! strcasecmp (name, gpgme_get_protocol_name (GPGME_PROTOCOL_CMS)))
+ return GPGME_PROTOCOL_CMS;
+ if (! strcasecmp (name,gpgme_get_protocol_name (GPGME_PROTOCOL_GPGCONF)))
+ return GPGME_PROTOCOL_GPGCONF;
+ if (! strcasecmp (name, gpgme_get_protocol_name (GPGME_PROTOCOL_ASSUAN)))
+ return GPGME_PROTOCOL_ASSUAN;
+ if (! strcasecmp (name, gpgme_get_protocol_name (GPGME_PROTOCOL_G13)))
+ return GPGME_PROTOCOL_G13;
+ if (! strcasecmp (name, gpgme_get_protocol_name (GPGME_PROTOCOL_UISERVER)))
+ return GPGME_PROTOCOL_UISERVER;
+ if (! strcasecmp (name, gpgme_get_protocol_name (GPGME_PROTOCOL_DEFAULT)))
+ return GPGME_PROTOCOL_DEFAULT;
+ return GPGME_PROTOCOL_UNKNOWN;
+}
+
+
+gpg_error_t
+gt_set_protocol (gpgme_tool_t gt, gpgme_protocol_t proto)
+{
+ return gpgme_set_protocol (gt->ctx, proto);
+}
+
+
+gpg_error_t
+gt_get_protocol (gpgme_tool_t gt)
+{
+ gpgme_protocol_t proto = gpgme_get_protocol (gt->ctx);
+
+ gt_write_status (gt, STATUS_PROTOCOL, gpgme_get_protocol_name (proto),
+ NULL);
+
+ return 0;
+}
+
+
+gpg_error_t
+gt_set_sub_protocol (gpgme_tool_t gt, gpgme_protocol_t proto)
+{
+ return gpgme_set_sub_protocol (gt->ctx, proto);
+}
+
+
+gpg_error_t
+gt_get_sub_protocol (gpgme_tool_t gt)
+{
+ gpgme_protocol_t proto = gpgme_get_sub_protocol (gt->ctx);
+
+ gt_write_status (gt, STATUS_PROTOCOL, gpgme_get_protocol_name (proto),
+ NULL);
+
+ return 0;
+}
+
+
+gpg_error_t
+gt_set_armor (gpgme_tool_t gt, int armor)
+{
+ gpgme_set_armor (gt->ctx, armor);
+ return 0;
+}
+
+
+gpg_error_t
+gt_get_armor (gpgme_tool_t gt)
+{
+ gt_write_status (gt, STATUS_ARMOR,
+ gpgme_get_armor (gt->ctx) ? "true" : "false", NULL);
+
+ return 0;
+}
+
+
+gpg_error_t
+gt_set_textmode (gpgme_tool_t gt, int textmode)
+{
+ gpgme_set_textmode (gt->ctx, textmode);
+ return 0;
+}
+
+
+gpg_error_t
+gt_get_textmode (gpgme_tool_t gt)
+{
+ gt_write_status (gt, STATUS_TEXTMODE,
+ gpgme_get_textmode (gt->ctx) ? "true" : "false", NULL);
+
+ return 0;
+}
+
+
+gpg_error_t
+gt_set_keylist_mode (gpgme_tool_t gt, gpgme_keylist_mode_t keylist_mode)
+{
+ gpgme_set_keylist_mode (gt->ctx, keylist_mode);
+ return 0;
+}
+
+
+gpg_error_t
+gt_get_keylist_mode (gpgme_tool_t gt)
+{
+#define NR_KEYLIST_MODES 6
+ const char *modes[NR_KEYLIST_MODES + 1];
+ int idx = 0;
+ gpgme_keylist_mode_t mode = gpgme_get_keylist_mode (gt->ctx);
+
+ if (mode & GPGME_KEYLIST_MODE_LOCAL)
+ modes[idx++] = "local";
+ if (mode & GPGME_KEYLIST_MODE_EXTERN)
+ modes[idx++] = "extern";
+ if (mode & GPGME_KEYLIST_MODE_SIGS)
+ modes[idx++] = "sigs";
+ if (mode & GPGME_KEYLIST_MODE_SIG_NOTATIONS)
+ modes[idx++] = "sig_notations";
+ if (mode & GPGME_KEYLIST_MODE_EPHEMERAL)
+ modes[idx++] = "ephemeral";
+ if (mode & GPGME_KEYLIST_MODE_VALIDATE)
+ modes[idx++] = "validate";
+ modes[idx++] = NULL;
+
+ gt_write_status (gt, STATUS_KEYLIST_MODE, modes[0], modes[1], modes[2],
+ modes[3], modes[4], modes[5], modes[6], NULL);
+
+ return 0;
+}
+
+
+gpg_error_t
+gt_set_include_certs (gpgme_tool_t gt, int include_certs)
+{
+ gpgme_set_include_certs (gt->ctx, include_certs);
+ return 0;
+}
+
+
+gpg_error_t
+gt_get_include_certs (gpgme_tool_t gt)
+{
+ int include_certs = gpgme_get_include_certs (gt->ctx);
+ char buf[100];
+
+ if (include_certs == GPGME_INCLUDE_CERTS_DEFAULT)
+ strcpy (buf, "default");
+ else
+ snprintf (buf, sizeof (buf), "%i", include_certs);
+
+ gt_write_status (gt, STATUS_INCLUDE_CERTS, buf, NULL);
+
+ return 0;
+}
+
+
+gpg_error_t
+gt_decrypt_verify (gpgme_tool_t gt, gpgme_data_t cipher, gpgme_data_t plain,
+ int verify)
+{
+ if (verify)
+ return gpgme_op_decrypt_verify (gt->ctx, cipher, plain);
+ else
+ return gpgme_op_decrypt (gt->ctx, cipher, plain);
+}
+
+
+gpg_error_t
+gt_sign_encrypt (gpgme_tool_t gt, gpgme_encrypt_flags_t flags,
+ gpgme_data_t plain, gpgme_data_t cipher, int sign)
+{
+ gpg_error_t err;
+
+ if (sign)
+ err = gpgme_op_encrypt_sign (gt->ctx, gt->recipients, flags, plain, cipher);
+ else
+ err = gpgme_op_encrypt (gt->ctx, gt->recipients, flags, plain, cipher);
+
+ gt_recipients_clear (gt);
+
+ return err;
+}
+
+
+gpg_error_t
+gt_sign (gpgme_tool_t gt, gpgme_data_t plain, gpgme_data_t sig,
+ gpgme_sig_mode_t mode)
+{
+ return gpgme_op_sign (gt->ctx, plain, sig, mode);
+}
+
+
+gpg_error_t
+gt_verify (gpgme_tool_t gt, gpgme_data_t sig, gpgme_data_t sig_text,
+ gpgme_data_t plain)
+{
+ return gpgme_op_verify (gt->ctx, sig, sig_text, plain);
+}
+
+
+gpg_error_t
+gt_import (gpgme_tool_t gt, gpgme_data_t data)
+{
+ return gpgme_op_import (gt->ctx, data);
+}
+
+
+gpg_error_t
+gt_export (gpgme_tool_t gt, const char *pattern[], gpgme_export_mode_t mode,
+ gpgme_data_t data)
+{
+ return gpgme_op_export_ext (gt->ctx, pattern, mode, data);
+}
+
+
+gpg_error_t
+gt_genkey (gpgme_tool_t gt, const char *parms, gpgme_data_t public,
+ gpgme_data_t secret)
+{
+ return gpgme_op_genkey (gt->ctx, parms, public, secret);
+}
+
+
+gpg_error_t
+gt_import_keys (gpgme_tool_t gt, char *fpr[])
+{
+ gpg_error_t err = 0;
+ int cnt;
+ int idx;
+ gpgme_key_t *keys;
+
+ cnt = 0;
+ while (fpr[cnt])
+ cnt++;
+
+ if (! cnt)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ keys = malloc ((cnt + 1) * sizeof (gpgme_key_t));
+ if (! keys)
+ return gpg_error_from_syserror ();
+
+ for (idx = 0; idx < cnt; idx++)
+ {
+ err = gpgme_get_key (gt->ctx, fpr[idx], &keys[idx], 0);
+ if (err)
+ break;
+ }
+ if (! err)
+ {
+ keys[cnt] = NULL;
+ err = gpgme_op_import_keys (gt->ctx, keys);
+ }
+
+ /* Rollback. */
+ while (--idx >= 0)
+ gpgme_key_unref (keys[idx]);
+ free (keys);
+
+ return err;
+}
+
+
+gpg_error_t
+gt_delete (gpgme_tool_t gt, char *fpr, int allow_secret)
+{
+ gpg_error_t err;
+ gpgme_key_t key;
+
+ err = gpgme_get_key (gt->ctx, fpr, &key, 0);
+ if (err)
+ return err;
+
+ err = gpgme_op_delete (gt->ctx, key, allow_secret);
+ gpgme_key_unref (key);
+ return err;
+}
+
+
+gpg_error_t
+gt_keylist_start (gpgme_tool_t gt, const char *pattern[], int secret_only)
+{
+ return gpgme_op_keylist_ext_start (gt->ctx, pattern, secret_only, 0);
+}
+
+
+gpg_error_t
+gt_keylist_next (gpgme_tool_t gt, gpgme_key_t *key)
+{
+ return gpgme_op_keylist_next (gt->ctx, key);
+}
+
+
+gpg_error_t
+gt_getauditlog (gpgme_tool_t gt, gpgme_data_t output, unsigned int flags)
+{
+ return gpgme_op_getauditlog (gt->ctx, output, flags);
+}
+
+
+gpg_error_t
+gt_vfs_mount (gpgme_tool_t gt, const char *container_file,
+ const char *mount_dir, int flags)
+{
+ gpg_error_t err;
+ gpg_error_t op_err;
+ err = gpgme_op_vfs_mount (gt->ctx, container_file, mount_dir, flags, &op_err);
+ return err ? err : op_err;
+}
+
+
+gpg_error_t
+gt_vfs_create (gpgme_tool_t gt, const char *container_file, int flags)
+{
+ gpg_error_t err;
+ gpg_error_t op_err;
+ err = gpgme_op_vfs_create (gt->ctx, gt->recipients, container_file,
+ flags, &op_err);
+ gt_recipients_clear (gt);
+ return err ? err : op_err;
+}
+
+
+static const char hlp_passwd[] =
+ "PASSWD <user-id>\n"
+ "\n"
+ "Ask the backend to change the passphrase for the key\n"
+ "specified by USER-ID.";
+gpg_error_t
+gt_passwd (gpgme_tool_t gt, char *fpr)
+{
+ gpg_error_t err;
+ gpgme_key_t key;
+
+ err = gpgme_get_key (gt->ctx, fpr, &key, 0);
+ if (err)
+ return gpg_err_code (err) == GPG_ERR_EOF? gpg_error (GPG_ERR_NO_PUBKEY):err;
+
+ err = gpgme_op_passwd (gt->ctx, key, 0);
+ gpgme_key_unref (key);
+ return err;
+}
+
+
+#define GT_RESULT_ENCRYPT 0x1
+#define GT_RESULT_DECRYPT 0x2
+#define GT_RESULT_SIGN 0x4
+#define GT_RESULT_VERIFY 0x8
+#define GT_RESULT_IMPORT 0x10
+#define GT_RESULT_GENKEY 0x20
+#define GT_RESULT_KEYLIST 0x40
+#define GT_RESULT_VFS_MOUNT 0x80
+#define GT_RESULT_ALL (~0U)
+
+gpg_error_t
+gt_result (gpgme_tool_t gt, unsigned int flags)
+{
+ static const char xml_preamble1[] = "<?xml version=\"1.0\" "
+ "encoding=\"UTF-8\" standalone=\"yes\"?>\n";
+ static const char xml_preamble2[] = "<gpgme>\n";
+ static const char xml_end[] = "</gpgme>\n";
+ int indent = 2;
+
+ gt_write_data (gt, xml_preamble1, sizeof (xml_preamble1));
+ gt_write_data (gt, NULL, 0);
+ gt_write_data (gt, xml_preamble2, sizeof (xml_preamble2));
+ gt_write_data (gt, NULL, 0);
+ if (flags & GT_RESULT_ENCRYPT)
+ result_encrypt_to_xml (gt->ctx, indent,
+ (result_xml_write_cb_t) gt_write_data, gt);
+ if (flags & GT_RESULT_DECRYPT)
+ result_decrypt_to_xml (gt->ctx, indent,
+ (result_xml_write_cb_t) gt_write_data, gt);
+ if (flags & GT_RESULT_SIGN)
+ result_sign_to_xml (gt->ctx, indent,
+ (result_xml_write_cb_t) gt_write_data, gt);
+ if (flags & GT_RESULT_VERIFY)
+ result_verify_to_xml (gt->ctx, indent,
+ (result_xml_write_cb_t) gt_write_data, gt);
+ if (flags & GT_RESULT_IMPORT)
+ result_import_to_xml (gt->ctx, indent,
+ (result_xml_write_cb_t) gt_write_data, gt);
+ if (flags & GT_RESULT_GENKEY)
+ result_genkey_to_xml (gt->ctx, indent,
+ (result_xml_write_cb_t) gt_write_data, gt);
+ if (flags & GT_RESULT_KEYLIST)
+ result_keylist_to_xml (gt->ctx, indent,
+ (result_xml_write_cb_t) gt_write_data, gt);
+ if (flags & GT_RESULT_VFS_MOUNT)
+ result_vfs_mount_to_xml (gt->ctx, indent,
+ (result_xml_write_cb_t) gt_write_data, gt);
+ gt_write_data (gt, xml_end, sizeof (xml_end));
+
+ return 0;
+}
+
+
+/* GPGME SERVER. */
+
+#include <assuan.h>
+
+struct server
+{
+ gpgme_tool_t gt;
+ assuan_context_t assuan_ctx;
+
+ gpgme_data_encoding_t input_enc;
+ gpgme_data_encoding_t output_enc;
+ assuan_fd_t input_fd;
+ char *input_filename;
+ FILE *input_stream;
+ assuan_fd_t output_fd;
+ char *output_filename;
+ FILE *output_stream;
+ assuan_fd_t message_fd;
+ char *message_filename;
+ FILE *message_stream;
+ gpgme_data_encoding_t message_enc;
+};
+
+
+gpg_error_t
+server_write_status (void *hook, const char *status, const char *msg)
+{
+ struct server *server = hook;
+ return assuan_write_status (server->assuan_ctx, status, msg);
+}
+
+
+gpg_error_t
+server_write_data (void *hook, const void *buf, size_t len)
+{
+ struct server *server = hook;
+ return assuan_send_data (server->assuan_ctx, buf, len);
+}
+
+
+
+static gpg_error_t
+server_parse_fd (assuan_context_t ctx, char *line, assuan_fd_t *rfd,
+ char **filename)
+{
+ *rfd = ASSUAN_INVALID_FD;
+ *filename = NULL;
+
+ if (! strncasecmp (line, "file=", 5))
+ {
+ char *term;
+ *filename = strdup (line + 5);
+ if (!*filename)
+ return gpg_error_from_syserror();
+ term = strchr (*filename, ' ');
+ if (term)
+ *term = '\0';
+ return 0;
+ }
+ else
+ return assuan_command_parse_fd (ctx, line, rfd);
+}
+
+
+static gpgme_data_encoding_t
+server_data_encoding (const char *line)
+{
+ if (strstr (line, "--binary"))
+ return GPGME_DATA_ENCODING_BINARY;
+ if (strstr (line, "--base64"))
+ return GPGME_DATA_ENCODING_BASE64;
+ if (strstr (line, "--armor"))
+ return GPGME_DATA_ENCODING_ARMOR;
+ if (strstr (line, "--url"))
+ return GPGME_DATA_ENCODING_URL;
+ if (strstr (line, "--urlesc"))
+ return GPGME_DATA_ENCODING_URLESC;
+ if (strstr (line, "--url0"))
+ return GPGME_DATA_ENCODING_URL0;
+ return GPGME_DATA_ENCODING_NONE;
+}
+
+
+static gpgme_error_t
+server_data_obj (assuan_fd_t fd, char *fn, int out,
+ gpgme_data_encoding_t encoding,
+ gpgme_data_t *data, FILE **fs)
+{
+ gpgme_error_t err;
+
+ *fs = NULL;
+ if (fn)
+ {
+ *fs = fopen (fn, out ? "wb" : "rb");
+ if (!*fs)
+ return gpg_error_from_syserror ();
+
+ err = gpgme_data_new_from_stream (data, *fs);
+ }
+ else
+ err = gpgme_data_new_from_fd (data, (int) fd);
+
+ if (err)
+ return err;
+ return gpgme_data_set_encoding (*data, encoding);
+}
+
+
+void
+server_reset_fds (struct server *server)
+{
+ /* assuan closes the input and output FDs for us when doing a RESET,
+ but we use this same function after commands, so repeat it
+ here. */
+ assuan_close_input_fd (server->assuan_ctx);
+ assuan_close_output_fd (server->assuan_ctx);
+ if (server->message_fd != ASSUAN_INVALID_FD)
+ {
+ /* FIXME: Assuan should provide a close function. */
+#if HAVE_W32_SYSTEM
+ CloseHandle (server->message_fd);
+#else
+ close (server->message_fd);
+#endif
+ server->message_fd = ASSUAN_INVALID_FD;
+ }
+ if (server->input_filename)
+ {
+ free (server->input_filename);
+ server->input_filename = NULL;
+ }
+ if (server->output_filename)
+ {
+ free (server->output_filename);
+ server->output_filename = NULL;
+ }
+ if (server->message_filename)
+ {
+ free (server->message_filename);
+ server->message_filename = NULL;
+ }
+ if (server->input_stream)
+ {
+ fclose (server->input_stream);
+ server->input_stream = NULL;
+ }
+ if (server->output_stream)
+ {
+ fclose (server->output_stream);
+ server->output_stream = NULL;
+ }
+ if (server->message_stream)
+ {
+ fclose (server->message_stream);
+ server->message_stream = NULL;
+ }
+
+ server->input_enc = GPGME_DATA_ENCODING_NONE;
+ server->output_enc = GPGME_DATA_ENCODING_NONE;
+ server->message_enc = GPGME_DATA_ENCODING_NONE;
+}
+
+
+static gpg_error_t
+reset_notify (assuan_context_t ctx, char *line)
+{
+ struct server *server = assuan_get_pointer (ctx);
+ server_reset_fds (server);
+ gt_reset (server->gt);
+ return 0;
+}
+
+
+static const char hlp_version[] =
+ "VERSION [<string>]\n"
+ "\n"
+ "Call the function gpgme_check_version.";
+static gpg_error_t
+cmd_version (assuan_context_t ctx, char *line)
+{
+ if (line && *line)
+ {
+ const char *version = gpgme_check_version (line);
+ return version ? 0 : gpg_error (GPG_ERR_SELFTEST_FAILED);
+ }
+ else
+ {
+ const char *version = gpgme_check_version (NULL);
+ return assuan_send_data (ctx, version, strlen (version));
+ }
+}
+
+
+static const char hlp_engine[] =
+ "ENGINE [<string>]\n"
+ "\n"
+ "Get information about a GPGME engine (a.k.a. protocol).";
+static gpg_error_t
+cmd_engine (assuan_context_t ctx, char *line)
+{
+ struct server *server = assuan_get_pointer (ctx);
+ return gt_get_engine_info (server->gt, gt_protocol_from_name (line));
+}
+
+
+static const char hlp_protocol[] =
+ "PROTOCOL [<name>]\n"
+ "\n"
+ "With NAME, set the protocol. Without, return the current\n"
+ "protocol.";
+static gpg_error_t
+cmd_protocol (assuan_context_t ctx, char *line)
+{
+ struct server *server = assuan_get_pointer (ctx);
+ if (line && *line)
+ return gt_set_protocol (server->gt, gt_protocol_from_name (line));
+ else
+ return gt_get_protocol (server->gt);
+}
+
+
+static const char hlp_sub_protocol[] =
+ "SUB_PROTOCOL [<name>]\n"
+ "\n"
+ "With NAME, set the sub-protocol. Without, return the\n"
+ "current sub-protocol.";
+static gpg_error_t
+cmd_sub_protocol (assuan_context_t ctx, char *line)
+{
+ struct server *server = assuan_get_pointer (ctx);
+ if (line && *line)
+ return gt_set_sub_protocol (server->gt, gt_protocol_from_name (line));
+ else
+ return gt_get_sub_protocol (server->gt);
+}
+
+
+static const char hlp_armor[] =
+ "ARMOR [true|false]\n"
+ "\n"
+ "With 'true' or 'false', turn output ASCII armoring on or\n"
+ "off. Without, return the current armoring status.";
+static gpg_error_t
+cmd_armor (assuan_context_t ctx, char *line)
+{
+ struct server *server = assuan_get_pointer (ctx);
+ if (line && *line)
+ {
+ int flag = 0;
+
+ if (! strcasecmp (line, "true") || ! strcasecmp (line, "yes")
+ || line[0] == '1')
+ flag = 1;
+
+ return gt_set_armor (server->gt, flag);
+ }
+ else
+ return gt_get_armor (server->gt);
+}
+
+
+static const char hlp_textmode[] =
+ "TEXTMODE [true|false]\n"
+ "\n"
+ "With 'true' or 'false', turn text mode on or off.\n"
+ "Without, return the current text mode status.";
+static gpg_error_t
+cmd_textmode (assuan_context_t ctx, char *line)
+{
+ struct server *server = assuan_get_pointer (ctx);
+ if (line && *line)
+ {
+ int flag = 0;
+
+ if (! strcasecmp (line, "true") || ! strcasecmp (line, "yes")
+ || line[0] == '1')
+ flag = 1;
+
+ return gt_set_textmode (server->gt, flag);
+ }
+ else
+ return gt_get_textmode (server->gt);
+}
+
+
+static const char hlp_include_certs[] =
+ "INCLUDE_CERTS [default|<n>]\n"
+ "\n"
+ "With DEFAULT or N, set how many certificates should be\n"
+ "included in the next S/MIME signed message. See the\n"
+ "GPGME documentation for details on the meaning of"
+ "various N. Without either, return the current setting.";
+static gpg_error_t
+cmd_include_certs (assuan_context_t ctx, char *line)
+{
+ struct server *server = assuan_get_pointer (ctx);
+
+ if (line && *line)
+ {
+ int include_certs = 0;
+
+ if (! strcasecmp (line, "default"))
+ include_certs = GPGME_INCLUDE_CERTS_DEFAULT;
+ else
+ include_certs = atoi (line);
+
+ return gt_set_include_certs (server->gt, include_certs);
+ }
+ else
+ return gt_get_include_certs (server->gt);
+}
+
+
+static const char hlp_keylist_mode[] =
+ "KEYLIST_MODE [local] [extern] [sigs] [sig_notations]\n"
+ " [ephemeral] [validate]\n"
+ "\n"
+ "Set the mode for the next KEYLIST command.";
+static gpg_error_t
+cmd_keylist_mode (assuan_context_t ctx, char *line)
+{
+ struct server *server = assuan_get_pointer (ctx);
+
+ if (line && *line)
+ {
+ gpgme_keylist_mode_t mode = 0;
+
+ if (strstr (line, "local"))
+ mode |= GPGME_KEYLIST_MODE_LOCAL;
+ if (strstr (line, "extern"))
+ mode |= GPGME_KEYLIST_MODE_EXTERN;
+ if (strstr (line, "sigs"))
+ mode |= GPGME_KEYLIST_MODE_SIGS;
+ if (strstr (line, "sig_notations"))
+ mode |= GPGME_KEYLIST_MODE_SIG_NOTATIONS;
+ if (strstr (line, "ephemeral"))
+ mode |= GPGME_KEYLIST_MODE_EPHEMERAL;
+ if (strstr (line, "validate"))
+ mode |= GPGME_KEYLIST_MODE_VALIDATE;
+
+ return gt_set_keylist_mode (server->gt, mode);
+ }
+ else
+ return gt_get_keylist_mode (server->gt);
+}
+
+
+static const char hlp_input[] =
+ "INPUT [<fd>|FILE=<path>]\n"
+ "\n"
+ "Set the input for the next command. Use either the\n"
+ "Assuan file descriptor FD or a filesystem PATH.";
+static gpg_error_t
+cmd_input (assuan_context_t ctx, char *line)
+{
+ struct server *server = assuan_get_pointer (ctx);
+ gpg_error_t err;
+ assuan_fd_t sysfd;
+ char *filename;
+
+ err = server_parse_fd (ctx, line, &sysfd, &filename);
+ if (err)
+ return err;
+ server->input_fd = sysfd;
+ server->input_filename = filename;
+ server->input_enc = server_data_encoding (line);
+ return 0;
+}
+
+
+static const char hlp_output[] =
+ "OUTPUT [<fd>|FILE=<path>]\n"
+ "\n"
+ "Set the output for the next command. Use either the\n"
+ "Assuan file descriptor FD or a filesystem PATH.";
+static gpg_error_t
+cmd_output (assuan_context_t ctx, char *line)
+{
+ struct server *server = assuan_get_pointer (ctx);
+ gpg_error_t err;
+ assuan_fd_t sysfd;
+ char *filename;
+
+ err = server_parse_fd (ctx, line, &sysfd, &filename);
+ if (err)
+ return err;
+ server->output_fd = sysfd;
+ server->output_filename = filename;
+ server->output_enc = server_data_encoding (line);
+ return 0;
+}
+
+
+static const char hlp_message[] =
+ "MESSAGE [<fd>|FILE=<path>]\n"
+ "\n"
+ "Set the plaintext message for the next VERIFY command\n"
+ "with a detached signature. Use either the Assuan file\n"
+ "descriptor FD or a filesystem PATH.";
+static gpg_error_t
+cmd_message (assuan_context_t ctx, char *line)
+{
+ struct server *server = assuan_get_pointer (ctx);
+ gpg_error_t err;
+ assuan_fd_t sysfd;
+ char *filename;
+
+ err = server_parse_fd (ctx, line, &sysfd, &filename);
+ if (err)
+ return err;
+ server->message_fd = sysfd;
+ server->message_filename = filename;
+ server->message_enc = server_data_encoding (line);
+ return 0;
+}
+
+
+static const char hlp_recipient[] =
+ "RECIPIENT <pattern>\n"
+ "\n"
+ "Add the key matching PATTERN to the list of recipients\n"
+ "for the next encryption command.";
+static gpg_error_t
+cmd_recipient (assuan_context_t ctx, char *line)
+{
+ struct server *server = assuan_get_pointer (ctx);
+
+ return gt_recipients_add (server->gt, line);
+}
+
+
+static const char hlp_signer[] =
+ "SIGNER <fingerprint>\n"
+ "\n"
+ "Add the key with FINGERPRINT to the list of signers to\n"
+ "be used for the next signing command.";
+static gpg_error_t
+cmd_signer (assuan_context_t ctx, char *line)
+{
+ struct server *server = assuan_get_pointer (ctx);
+
+ return gt_signers_add (server->gt, line);
+}
+
+
+static const char hlp_signers_clear[] =
+ "SIGNERS_CLEAR\n"
+ "\n"
+ "Clear the list of signers specified by previous SIGNER\n"
+ "commands.";
+static gpg_error_t
+cmd_signers_clear (assuan_context_t ctx, char *line)
+{
+ struct server *server = assuan_get_pointer (ctx);
+
+ return gt_signers_clear (server->gt);
+}
+
+
+static gpg_error_t
+_cmd_decrypt_verify (assuan_context_t ctx, char *line, int verify)
+{
+ struct server *server = assuan_get_pointer (ctx);
+ gpg_error_t err;
+ assuan_fd_t inp_fd;
+ char *inp_fn;
+ assuan_fd_t out_fd;
+ char *out_fn;
+ gpgme_data_t inp_data;
+ gpgme_data_t out_data;
+
+ inp_fd = assuan_get_input_fd (ctx);
+ inp_fn = server->input_filename;
+ if (inp_fd == ASSUAN_INVALID_FD && !inp_fn)
+ return GPG_ERR_ASS_NO_INPUT;
+ out_fd = assuan_get_output_fd (ctx);
+ out_fn = server->output_filename;
+ if (out_fd == ASSUAN_INVALID_FD && !out_fn)
+ return GPG_ERR_ASS_NO_OUTPUT;
+
+ err = server_data_obj (inp_fd, inp_fn, 0, server->input_enc, &inp_data,
+ &server->input_stream);
+ if (err)
+ return err;
+ err = server_data_obj (out_fd, out_fn, 1, server->output_enc, &out_data,
+ &server->output_stream);
+ if (err)
+ {
+ gpgme_data_release (inp_data);
+ return err;
+ }
+
+ err = gt_decrypt_verify (server->gt, inp_data, out_data, verify);
+
+ gpgme_data_release (inp_data);
+ gpgme_data_release (out_data);
+
+ server_reset_fds (server);
+
+ return err;
+}
+
+
+static const char hlp_decrypt[] =
+ "DECRYPT\n"
+ "\n"
+ "Decrypt the object set by the last INPUT command and\n"
+ "write the decrypted message to the object set by the\n"
+ "last OUTPUT command.";
+static gpg_error_t
+cmd_decrypt (assuan_context_t ctx, char *line)
+{
+ return _cmd_decrypt_verify (ctx, line, 0);
+}
+
+
+static const char hlp_decrypt_verify[] =
+ "DECRYPT_VERIFY\n"
+ "\n"
+ "Decrypt the object set by the last INPUT command and\n"
+ "verify any embedded signatures. Write the decrypted\n"
+ "message to the object set by the last OUTPUT command.";
+static gpg_error_t
+cmd_decrypt_verify (assuan_context_t ctx, char *line)
+{
+ return _cmd_decrypt_verify (ctx, line, 1);
+}
+
+
+static gpg_error_t
+_cmd_sign_encrypt (assuan_context_t ctx, char *line, int sign)
+{
+ struct server *server = assuan_get_pointer (ctx);
+ gpg_error_t err;
+ assuan_fd_t inp_fd;
+ char *inp_fn;
+ assuan_fd_t out_fd;
+ char *out_fn;
+ gpgme_data_t inp_data = NULL;
+ gpgme_data_t out_data = NULL;
+ gpgme_encrypt_flags_t flags = 0;
+
+ if (strstr (line, "--always-trust"))
+ flags |= GPGME_ENCRYPT_ALWAYS_TRUST;
+ if (strstr (line, "--no-encrypt-to"))
+ flags |= GPGME_ENCRYPT_NO_ENCRYPT_TO;
+ if (strstr (line, "--prepare"))
+ flags |= GPGME_ENCRYPT_PREPARE;
+ if (strstr (line, "--expect-sign"))
+ flags |= GPGME_ENCRYPT_EXPECT_SIGN;
+
+ inp_fd = assuan_get_input_fd (ctx);
+ inp_fn = server->input_filename;
+ out_fd = assuan_get_output_fd (ctx);
+ out_fn = server->output_filename;
+ if (inp_fd != ASSUAN_INVALID_FD || inp_fn)
+ {
+ err = server_data_obj (inp_fd, inp_fn, 0, server->input_enc, &inp_data,
+ &server->input_stream);
+ if (err)
+ return err;
+ }
+ if (out_fd != ASSUAN_INVALID_FD || out_fn)
+ {
+ err = server_data_obj (out_fd, out_fn, 1, server->output_enc, &out_data,
+ &server->output_stream);
+ if (err)
+ {
+ gpgme_data_release (inp_data);
+ return err;
+ }
+ }
+
+ err = gt_sign_encrypt (server->gt, flags, inp_data, out_data, sign);
+
+ gpgme_data_release (inp_data);
+ gpgme_data_release (out_data);
+
+ server_reset_fds (server);
+
+ return err;
+}
+
+
+static const char hlp_encrypt[] =
+ "ENCRYPT [--always-trust] [--no-encrypt-to]\n"
+ " [--prepare] [--expect-sign]\n"
+ "\n"
+ "Encrypt the object set by the last INPUT command to\n"
+ "the keys specified by previous RECIPIENT commands. \n"
+ "Write the signed and encrypted message to the object\n"
+ "set by the last OUTPUT command.";
+static gpg_error_t
+cmd_encrypt (assuan_context_t ctx, char *line)
+{
+ return _cmd_sign_encrypt (ctx, line, 0);
+}
+
+
+static const char hlp_sign_encrypt[] =
+ "SIGN_ENCRYPT [--always-trust] [--no-encrypt-to]\n"
+ " [--prepare] [--expect-sign]\n"
+ "\n"
+ "Sign the object set by the last INPUT command with the\n"
+ "keys specified by previous SIGNER commands and encrypt\n"
+ "it to the keys specified by previous RECIPIENT\n"
+ "commands. Write the signed and encrypted message to\n"
+ "the object set by the last OUTPUT command.";
+static gpg_error_t
+cmd_sign_encrypt (assuan_context_t ctx, char *line)
+{
+ return _cmd_sign_encrypt (ctx, line, 1);
+}
+
+
+static const char hlp_sign[] =
+ "SIGN [--clear|--detach]\n"
+ "\n"
+ "Sign the object set by the last INPUT command with the\n"
+ "keys specified by previous SIGNER commands. Write the\n"
+ "signed message to the object set by the last OUTPUT\n"
+ "command. With `--clear`, generate a clear text\n"
+ "signature. With `--detach`, generate a detached\n"
+ "signature.";
+static gpg_error_t
+cmd_sign (assuan_context_t ctx, char *line)
+{
+ struct server *server = assuan_get_pointer (ctx);
+ gpg_error_t err;
+ assuan_fd_t inp_fd;
+ char *inp_fn;
+ assuan_fd_t out_fd;
+ char *out_fn;
+ gpgme_data_t inp_data;
+ gpgme_data_t out_data;
+ gpgme_sig_mode_t mode = GPGME_SIG_MODE_NORMAL;
+
+ if (strstr (line, "--clear"))
+ mode = GPGME_SIG_MODE_CLEAR;
+ if (strstr (line, "--detach"))
+ mode = GPGME_SIG_MODE_DETACH;
+
+ inp_fd = assuan_get_input_fd (ctx);
+ inp_fn = server->input_filename;
+ if (inp_fd == ASSUAN_INVALID_FD && !inp_fn)
+ return GPG_ERR_ASS_NO_INPUT;
+ out_fd = assuan_get_output_fd (ctx);
+ out_fn = server->output_filename;
+ if (out_fd == ASSUAN_INVALID_FD && !out_fn)
+ return GPG_ERR_ASS_NO_OUTPUT;
+
+ err = server_data_obj (inp_fd, inp_fn, 0, server->input_enc, &inp_data,
+ &server->input_stream);
+ if (err)
+ return err;
+ err = server_data_obj (out_fd, out_fn, 1, server->output_enc, &out_data,
+ &server->output_stream);
+ if (err)
+ {
+ gpgme_data_release (inp_data);
+ return err;
+ }
+
+ err = gt_sign (server->gt, inp_data, out_data, mode);
+
+ gpgme_data_release (inp_data);
+ gpgme_data_release (out_data);
+ server_reset_fds (server);
+
+ return err;
+}
+
+
+static const char hlp_verify[] =
+ "VERIFY\n"
+ "\n"
+ "Verify signatures on the object set by the last INPUT\n"
+ "and MESSAGE commands. If the message was encrypted,\n"
+ "write the plaintext to the object set by the last\n"
+ "OUTPUT command.";
+static gpg_error_t
+cmd_verify (assuan_context_t ctx, char *line)
+{
+ struct server *server = assuan_get_pointer (ctx);
+ gpg_error_t err;
+ assuan_fd_t inp_fd;
+ assuan_fd_t msg_fd;
+ assuan_fd_t out_fd;
+ char *inp_fn;
+ char *msg_fn;
+ char *out_fn;
+ gpgme_data_t inp_data;
+ gpgme_data_t msg_data = NULL;
+ gpgme_data_t out_data = NULL;
+
+ inp_fd = assuan_get_input_fd (ctx);
+ inp_fn = server->input_filename;
+ if (inp_fd == ASSUAN_INVALID_FD && !inp_fn)
+ return GPG_ERR_ASS_NO_INPUT;
+ msg_fd = server->message_fd;
+ msg_fn = server->message_filename;
+ out_fd = assuan_get_output_fd (ctx);
+ out_fn = server->output_filename;
+
+ err = server_data_obj (inp_fd, inp_fn, 0, server->input_enc, &inp_data,
+ &server->input_stream);
+ if (err)
+ return err;
+ if (msg_fd != ASSUAN_INVALID_FD || msg_fn)
+ {
+ err = server_data_obj (msg_fd, msg_fn, 0, server->message_enc, &msg_data,
+ &server->message_stream);
+ if (err)
+ {
+ gpgme_data_release (inp_data);
+ return err;
+ }
+ }
+ if (out_fd != ASSUAN_INVALID_FD || out_fn)
+ {
+ err = server_data_obj (out_fd, out_fn, 1, server->output_enc, &out_data,
+ &server->output_stream);
+ if (err)
+ {
+ gpgme_data_release (inp_data);
+ gpgme_data_release (msg_data);
+ return err;
+ }
+ }
+
+ err = gt_verify (server->gt, inp_data, msg_data, out_data);
+
+ gpgme_data_release (inp_data);
+ if (msg_data)
+ gpgme_data_release (msg_data);
+ if (out_data)
+ gpgme_data_release (out_data);
+
+ server_reset_fds (server);
+
+ return err;
+}
+
+
+static const char hlp_import[] =
+ "IMPORT [<pattern>]\n"
+ "\n"
+ "With PATTERN, import the keys described by PATTERN.\n"
+ "Without, read a key (or keys) from the object set by the\n"
+ "last INPUT command.";
+static gpg_error_t
+cmd_import (assuan_context_t ctx, char *line)
+{
+ struct server *server = assuan_get_pointer (ctx);
+
+ if (line && *line)
+ {
+ char *fprs[2] = { line, NULL };
+
+ return gt_import_keys (server->gt, fprs);
+ }
+ else
+ {
+ gpg_error_t err;
+ assuan_fd_t inp_fd;
+ char *inp_fn;
+ gpgme_data_t inp_data;
+
+ inp_fd = assuan_get_input_fd (ctx);
+ inp_fn = server->input_filename;
+ if (inp_fd == ASSUAN_INVALID_FD && !inp_fn)
+ return GPG_ERR_ASS_NO_INPUT;
+
+ err = server_data_obj (inp_fd, inp_fn, 0, server->input_enc, &inp_data,
+ &server->input_stream);
+ if (err)
+ return err;
+
+ err = gt_import (server->gt, inp_data);
+
+ gpgme_data_release (inp_data);
+ server_reset_fds (server);
+
+ return err;
+ }
+}
+
+
+static const char hlp_export[] =
+ "EXPORT [--extern] [--minimal] [<pattern>]\n"
+ "\n"
+ "Export the keys described by PATTERN. Write the\n"
+ "the output to the object set by the last OUTPUT command.";
+static gpg_error_t
+cmd_export (assuan_context_t ctx, char *line)
+{
+ struct server *server = assuan_get_pointer (ctx);
+ gpg_error_t err;
+ assuan_fd_t out_fd;
+ char *out_fn;
+ gpgme_data_t out_data;
+ gpgme_export_mode_t mode = 0;
+ const char *pattern[2];
+
+ out_fd = assuan_get_output_fd (ctx);
+ out_fn = server->output_filename;
+ if (out_fd == ASSUAN_INVALID_FD && !out_fn)
+ return GPG_ERR_ASS_NO_OUTPUT;
+ err = server_data_obj (out_fd, out_fn, 1, server->output_enc, &out_data,
+ &server->output_stream);
+ if (err)
+ return err;
+
+ if (has_option (line, "--extern"))
+ mode |= GPGME_EXPORT_MODE_EXTERN;
+ if (has_option (line, "--minimal"))
+ mode |= GPGME_EXPORT_MODE_MINIMAL;
+
+ line = skip_options (line);
+
+ pattern[0] = line;
+ pattern[1] = NULL;
+
+ err = gt_export (server->gt, pattern, mode, out_data);
+
+ gpgme_data_release (out_data);
+ server_reset_fds (server);
+
+ return err;
+}
+
+
+static gpg_error_t
+_cmd_genkey_write (gpgme_data_t data, const void *buf, size_t size)
+{
+ while (size > 0)
+ {
+ ssize_t writen = gpgme_data_write (data, buf, size);
+ if (writen < 0 && errno != EAGAIN)
+ return gpg_error_from_syserror ();
+ else if (writen > 0)
+ {
+ buf = (void *) (((char *) buf) + writen);
+ size -= writen;
+ }
+ }
+ return 0;
+}
+
+
+static gpg_error_t
+cmd_genkey (assuan_context_t ctx, char *line)
+{
+ struct server *server = assuan_get_pointer (ctx);
+ gpg_error_t err;
+ assuan_fd_t inp_fd;
+ char *inp_fn;
+ assuan_fd_t out_fd;
+ char *out_fn;
+ gpgme_data_t inp_data;
+ gpgme_data_t out_data = NULL;
+ gpgme_data_t parms_data = NULL;
+ const char *parms;
+
+ inp_fd = assuan_get_input_fd (ctx);
+ inp_fn = server->input_filename;
+ if (inp_fd == ASSUAN_INVALID_FD && !inp_fn)
+ return GPG_ERR_ASS_NO_INPUT;
+ out_fd = assuan_get_output_fd (ctx);
+ out_fn = server->output_filename;
+
+ err = server_data_obj (inp_fd, inp_fn, 0, server->input_enc, &inp_data,
+ &server->input_stream);
+ if (err)
+ return err;
+ if (out_fd != ASSUAN_INVALID_FD || out_fn)
+ {
+ err = server_data_obj (out_fd, out_fn, 1, server->output_enc, &out_data,
+ &server->output_stream);
+ if (err)
+ {
+ gpgme_data_release (inp_data);
+ return err;
+ }
+ }
+
+ /* Convert input data. */
+ err = gpgme_data_new (&parms_data);
+ if (err)
+ goto out;
+ do
+ {
+ char buf[512];
+ ssize_t readn = gpgme_data_read (inp_data, buf, sizeof (buf));
+ if (readn < 0)
+ {
+ err = gpg_error_from_syserror ();
+ goto out;
+ }
+ else if (readn == 0)
+ break;
+
+ err = _cmd_genkey_write (parms_data, buf, readn);
+ if (err)
+ goto out;
+ }
+ while (1);
+ err = _cmd_genkey_write (parms_data, "", 1);
+ if (err)
+ goto out;
+ parms = gpgme_data_release_and_get_mem (parms_data, NULL);
+ parms_data = NULL;
+ if (! parms)
+ {
+ err = gpg_error (GPG_ERR_GENERAL);
+ goto out;
+ }
+
+ err = gt_genkey (server->gt, parms, out_data, NULL);
+
+ server_reset_fds (server);
+
+ out:
+ gpgme_data_release (inp_data);
+ if (out_data)
+ gpgme_data_release (out_data);
+ if (parms_data)
+ gpgme_data_release (parms_data);
+
+ return err;
+}
+
+
+static gpg_error_t
+cmd_delete (assuan_context_t ctx, char *line)
+{
+ struct server *server = assuan_get_pointer (ctx);
+ int allow_secret = 0;
+ const char optstr[] = "--allow-secret";
+
+ if (!strncasecmp (line, optstr, strlen (optstr)))
+ {
+ allow_secret = 1;
+ line += strlen (optstr);
+ while (*line && !spacep (line))
+ line++;
+ }
+ return gt_delete (server->gt, line, allow_secret);
+}
+
+
+static const char hlp_keylist[] =
+ "KEYLIST [--secret-only] [<patterns>]\n"
+ "\n"
+ "List all certificates or only those specified by PATTERNS. Each\n"
+ "pattern shall be a percent-plus escaped certificate specification.";
+static gpg_error_t
+cmd_keylist (assuan_context_t ctx, char *line)
+{
+#define MAX_CMD_KEYLIST_PATTERN 20
+ struct server *server = assuan_get_pointer (ctx);
+ gpg_error_t err;
+ int secret_only = 0;
+ int idx;
+ const char *pattern[MAX_CMD_KEYLIST_PATTERN+1];
+ const char optstr[] = "--secret-only";
+ char *p;
+
+ if (!strncasecmp (line, optstr, strlen (optstr)))
+ {
+ secret_only = 1;
+ line += strlen (optstr);
+ while (*line && !spacep (line))
+ line++;
+ }
+
+ idx = 0;
+ for (p=line; *p; line = p)
+ {
+ while (*p && *p != ' ')
+ p++;
+ if (*p)
+ *p++ = 0;
+ if (*line)
+ {
+ if (idx+1 == DIM (pattern))
+ return gpg_error (GPG_ERR_TOO_MANY);
+ strcpy_escaped_plus (line, line);
+ pattern[idx++] = line;
+ }
+ }
+ pattern[idx] = NULL;
+
+ err = gt_keylist_start (server->gt, pattern, secret_only);
+ while (! err)
+ {
+ gpgme_key_t key;
+
+ err = gt_keylist_next (server->gt, &key);
+ if (gpg_err_code (err) == GPG_ERR_EOF)
+ {
+ err = 0;
+ break;
+ }
+ else if (! err)
+ {
+ char buf[100];
+ /* FIXME: More data. */
+ snprintf (buf, sizeof (buf), "key:%s\n", key->subkeys->fpr);
+ /* Write data and flush so that we see one D line for each
+ key. This does not change the semantics but is easier to
+ read by organic eyes. */
+ if (!assuan_send_data (ctx, buf, strlen (buf)))
+ assuan_send_data (ctx, NULL, 0);
+ gpgme_key_unref (key);
+ }
+ }
+
+ server_reset_fds (server);
+
+ return err;
+}
+
+
+static const char hlp_getauditlog[] =
+ "GETAUDITLOG [--html] [--with-help]\n"
+ "\n"
+ "Call the function gpgme_op_getauditlog with the given flags. Write\n"
+ "the output to the object set by the last OUTPUT command.";
+static gpg_error_t
+cmd_getauditlog (assuan_context_t ctx, char *line)
+{
+ struct server *server = assuan_get_pointer (ctx);
+ gpg_error_t err;
+ assuan_fd_t out_fd;
+ char *out_fn;
+ gpgme_data_t out_data;
+ unsigned int flags = 0;
+
+ out_fd = assuan_get_output_fd (ctx);
+ out_fn = server->output_filename;
+ if (out_fd == ASSUAN_INVALID_FD && !out_fn)
+ return GPG_ERR_ASS_NO_OUTPUT;
+ err = server_data_obj (out_fd, out_fn, 1, server->output_enc, &out_data,
+ &server->output_stream);
+ if (err)
+ return err;
+
+ if (strstr (line, "--html"))
+ flags |= GPGME_AUDITLOG_HTML;
+ if (strstr (line, "--with-help"))
+ flags |= GPGME_AUDITLOG_WITH_HELP;
+
+ err = gt_getauditlog (server->gt, out_data, flags);
+
+ gpgme_data_release (out_data);
+ server_reset_fds (server);
+
+ return err;
+}
+
+
+static gpg_error_t
+cmd_vfs_mount (assuan_context_t ctx, char *line)
+{
+ struct server *server = assuan_get_pointer (ctx);
+ char *mount_dir;
+ gpg_error_t err;
+
+ mount_dir = strchr (line, ' ');
+ if (mount_dir)
+ {
+ *(mount_dir++) = '\0';
+ while (*mount_dir == ' ')
+ mount_dir++;
+ }
+
+ err = gt_vfs_mount (server->gt, line, mount_dir, 0);
+
+ return err;
+}
+
+
+static gpg_error_t
+cmd_vfs_create (assuan_context_t ctx, char *line)
+{
+ struct server *server = assuan_get_pointer (ctx);
+ gpg_error_t err;
+ char *end;
+
+ end = strchr (line, ' ');
+ if (end)
+ {
+ *(end++) = '\0';
+ while (*end == ' ')
+ end++;
+ }
+
+ err = gt_vfs_create (server->gt, line, 0);
+
+ return err;
+}
+
+
+static gpg_error_t
+cmd_passwd (assuan_context_t ctx, char *line)
+{
+ struct server *server = assuan_get_pointer (ctx);
+
+ return gt_passwd (server->gt, line);
+}
+
+
+
+static gpg_error_t
+cmd_result (assuan_context_t ctx, char *line)
+{
+ struct server *server = assuan_get_pointer (ctx);
+ return gt_result (server->gt, GT_RESULT_ALL);
+}
+
+
+/* STRERROR <err> */
+static gpg_error_t
+cmd_strerror (assuan_context_t ctx, char *line)
+{
+ gpg_error_t err;
+ char buf[100];
+
+ err = atoi (line);
+ snprintf (buf, sizeof (buf), "%s <%s>", gpgme_strerror (err),
+ gpgme_strsource (err));
+ return assuan_send_data (ctx, buf, strlen (buf));
+}
+
+
+static gpg_error_t
+cmd_pubkey_algo_name (assuan_context_t ctx, char *line)
+{
+ gpgme_pubkey_algo_t algo;
+ char buf[100];
+
+ algo = atoi (line);
+ snprintf (buf, sizeof (buf), "%s", gpgme_pubkey_algo_name (algo));
+ return assuan_send_data (ctx, buf, strlen (buf));
+}
+
+
+static gpg_error_t
+cmd_hash_algo_name (assuan_context_t ctx, char *line)
+{
+ gpgme_hash_algo_t algo;
+ char buf[100];
+
+ algo = atoi (line);
+ snprintf (buf, sizeof (buf), "%s", gpgme_hash_algo_name (algo));
+ return assuan_send_data (ctx, buf, strlen (buf));
+}
+
+
+/* Tell the assuan library about our commands. */
+static gpg_error_t
+register_commands (assuan_context_t ctx)
+{
+ gpg_error_t err;
+ static struct {
+ const char *name;
+ assuan_handler_t handler;
+ const char * const help;
+ } table[] = {
+ /* RESET, BYE are implicit. */
+ { "VERSION", cmd_version, hlp_version },
+ /* TODO: Set engine info. */
+ { "ENGINE", cmd_engine, hlp_engine },
+ { "PROTOCOL", cmd_protocol, hlp_protocol },
+ { "SUB_PROTOCOL", cmd_sub_protocol, hlp_sub_protocol },
+ { "ARMOR", cmd_armor, hlp_armor },
+ { "TEXTMODE", cmd_textmode, hlp_textmode },
+ { "INCLUDE_CERTS", cmd_include_certs, hlp_include_certs },
+ { "KEYLIST_MODE", cmd_keylist_mode, hlp_keylist_mode },
+ { "INPUT", cmd_input, hlp_input },
+ { "OUTPUT", cmd_output, hlp_output },
+ { "MESSAGE", cmd_message, hlp_message },
+ { "RECIPIENT", cmd_recipient, hlp_recipient },
+ { "SIGNER", cmd_signer, hlp_signer },
+ { "SIGNERS_CLEAR", cmd_signers_clear, hlp_signers_clear },
+ /* TODO: SIGNOTATION missing. */
+ /* TODO: Could add wait interface if we allow more than one context */
+ /* and add _START variants. */
+ /* TODO: Could add data interfaces if we allow multiple data objects. */
+ { "DECRYPT", cmd_decrypt, hlp_decrypt },
+ { "DECRYPT_VERIFY", cmd_decrypt_verify, hlp_decrypt_verify },
+ { "ENCRYPT", cmd_encrypt, hlp_encrypt },
+ { "ENCRYPT_SIGN", cmd_sign_encrypt, hlp_sign_encrypt },
+ { "SIGN_ENCRYPT", cmd_sign_encrypt, hlp_sign_encrypt },
+ { "SIGN", cmd_sign, hlp_sign },
+ { "VERIFY", cmd_verify, hlp_verify },
+ { "IMPORT", cmd_import, hlp_import },
+ { "EXPORT", cmd_export, hlp_export },
+ { "GENKEY", cmd_genkey },
+ { "DELETE", cmd_delete },
+ /* TODO: EDIT, CARD_EDIT (with INQUIRE) */
+ { "KEYLIST", cmd_keylist, hlp_keylist },
+ { "LISTKEYS", cmd_keylist, hlp_keylist },
+ /* TODO: TRUSTLIST, TRUSTLIST_EXT */
+ { "GETAUDITLOG", cmd_getauditlog, hlp_getauditlog },
+ /* TODO: ASSUAN */
+ { "VFS_MOUNT", cmd_vfs_mount },
+ { "MOUNT", cmd_vfs_mount },
+ { "VFS_CREATE", cmd_vfs_create },
+ { "CREATE", cmd_vfs_create },
+ /* TODO: GPGCONF */
+ { "RESULT", cmd_result },
+ { "STRERROR", cmd_strerror },
+ { "PUBKEY_ALGO_NAME", cmd_pubkey_algo_name },
+ { "HASH_ALGO_NAME", cmd_hash_algo_name },
+ { "PASSWD", cmd_passwd, hlp_passwd },
+ { NULL }
+ };
+ int idx;
+
+ for (idx = 0; table[idx].name; idx++)
+ {
+ err = assuan_register_command (ctx, table[idx].name, table[idx].handler,
+ table[idx].help);
+ if (err)
+ return err;
+ }
+ return 0;
+}
+
+
+/* TODO: password callback can do INQUIRE. */
+void
+gpgme_server (gpgme_tool_t gt)
+{
+ gpg_error_t err;
+ assuan_fd_t filedes[2];
+ struct server server;
+ static const char hello[] = ("GPGME-Tool " VERSION " ready");
+
+ memset (&server, 0, sizeof (server));
+ server.message_fd = ASSUAN_INVALID_FD;
+ server.input_enc = GPGME_DATA_ENCODING_NONE;
+ server.output_enc = GPGME_DATA_ENCODING_NONE;
+ server.message_enc = GPGME_DATA_ENCODING_NONE;
+
+ server.gt = gt;
+ gt->write_status = server_write_status;
+ gt->write_status_hook = &server;
+ gt->write_data = server_write_data;
+ gt->write_data_hook = &server;
+
+ /* We use a pipe based server so that we can work from scripts.
+ assuan_init_pipe_server will automagically detect when we are
+ called with a socketpair and ignore FIELDES in this case. */
+#ifdef HAVE_W32CE_SYSTEM
+ filedes[0] = ASSUAN_STDIN;
+ filedes[1] = ASSUAN_STDOUT;
+#else
+ filedes[0] = assuan_fdopen (0);
+ filedes[1] = assuan_fdopen (1);
+#endif
+ err = assuan_new (&server.assuan_ctx);
+ if (err)
+ log_error (1, err, "can't create assuan context");
+
+ assuan_set_pointer (server.assuan_ctx, &server);
+
+ err = assuan_init_pipe_server (server.assuan_ctx, filedes);
+ if (err)
+ log_error (1, err, "can't initialize assuan server");
+ err = register_commands (server.assuan_ctx);
+ if (err)
+ log_error (1, err, "can't register assuan commands");
+ assuan_set_hello_line (server.assuan_ctx, hello);
+
+ assuan_register_reset_notify (server.assuan_ctx, reset_notify);
+
+#define DBG_ASSUAN 0
+ if (DBG_ASSUAN)
+ assuan_set_log_stream (server.assuan_ctx, log_stream);
+
+ for (;;)
+ {
+ err = assuan_accept (server.assuan_ctx);
+ if (err == -1)
+ break;
+ else if (err)
+ {
+ log_error (0, err, "assuan accept problem");
+ break;
+ }
+
+ err = assuan_process (server.assuan_ctx);
+ if (err)
+ log_error (0, err, "assuan processing failed");
+ }
+
+ assuan_release (server.assuan_ctx);
+}
+
+
+
+/* MAIN PROGRAM STARTS HERE. */
+
+const char *argp_program_version = VERSION;
+const char *argp_program_bug_address = "bug-gpgme@gnupg.org";
+error_t argp_err_exit_status = 1;
+
+static char doc[] = "GPGME Tool -- Assuan server exposing GPGME operations";
+static char args_doc[] = "COMMAND [OPTIONS...]";
+
+static struct argp_option options[] = {
+ { "server", 's', 0, 0, "Server mode" },
+ { 0 }
+};
+
+static error_t parse_options (int key, char *arg, struct argp_state *state);
+static struct argp argp = { options, parse_options, args_doc, doc };
+
+struct args
+{
+ enum { CMD_DEFAULT, CMD_SERVER } cmd;
+};
+
+void
+args_init (struct args *args)
+{
+ memset (args, '\0', sizeof (*args));
+ args->cmd = CMD_DEFAULT;
+}
+
+
+static error_t
+parse_options (int key, char *arg, struct argp_state *state)
+{
+ struct args *args = state->input;
+
+ switch (key)
+ {
+ case 's':
+ args->cmd = CMD_SERVER;
+ break;
+#if 0
+ case ARGP_KEY_ARG:
+ if (state->arg_num >= 2)
+ argp_usage (state);
+ printf ("Arg[%i] = %s\n", state->arg_num, arg);
+ break;
+ case ARGP_KEY_END:
+ if (state->arg_num < 2)
+ argp_usage (state);
+ break;
+#endif
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+
+int
+main (int argc, char *argv[])
+{
+ struct args args;
+ struct gpgme_tool gt;
+
+#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
+
+ args_init (&args);
+
+ argp_parse (&argp, argc, argv, 0, 0, &args);
+ log_init ();
+
+ gt_init (&gt);
+
+ switch (args.cmd)
+ {
+ case CMD_DEFAULT:
+ case CMD_SERVER:
+ gpgme_server (&gt);
+ break;
+ }
+
+ gpgme_release (gt.ctx);
+
+#ifdef HAVE_W32CE_SYSTEM
+ /* Give the buggy ssh server time to flush the output buffers. */
+ Sleep (300);
+#endif
+
+ return 0;
+}
+
diff --git a/src/gpgme-w32spawn.c b/src/gpgme-w32spawn.c
new file mode 100644
index 0000000..ccf88a9
--- /dev/null
+++ b/src/gpgme-w32spawn.c
@@ -0,0 +1,501 @@
+/* gpgme-w32spawn.c - Wrapper to spawn a process under Windows.
+ Copyright (C) 2008 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/>.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <string.h>
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#include <stdint.h>
+#include <process.h>
+#include <windows.h>
+
+/* Flag values as used by gpgme. */
+#define IOSPAWN_FLAG_ALLOW_SET_FG 1
+
+
+/* Name of this program. */
+#define PGM "gpgme-w32spawn"
+
+
+
+struct spawn_fd_item_s
+{
+ int handle;
+ int dup_to;
+ int peer_name;
+ int arg_loc;
+};
+
+
+static char *
+build_commandline (char **argv)
+{
+ int i;
+ int n = 0;
+ char *buf;
+ char *p;
+
+ /* We have to quote some things because under Windows the program
+ parses the commandline and does some unquoting. We enclose the
+ whole argument in double-quotes, and escape literal double-quotes
+ as well as backslashes with a backslash. We end up with a
+ trailing space at the end of the line, but that is harmless. */
+ for (i = 0; argv[i]; i++)
+ {
+ p = argv[i];
+ /* The leading double-quote. */
+ n++;
+ while (*p)
+ {
+ /* An extra one for each literal that must be escaped. */
+ if (*p == '\\' || *p == '"')
+ n++;
+ n++;
+ p++;
+ }
+ /* The trailing double-quote and the delimiter. */
+ n += 2;
+ }
+ /* And a trailing zero. */
+ n++;
+
+ buf = p = malloc (n);
+ if (!buf)
+ return NULL;
+ for (i = 0; argv[i]; i++)
+ {
+ char *argvp = argv[i];
+
+ *(p++) = '"';
+ while (*argvp)
+ {
+ if (*argvp == '\\' || *argvp == '"')
+ *(p++) = '\\';
+ *(p++) = *(argvp++);
+ }
+ *(p++) = '"';
+ *(p++) = ' ';
+ }
+ *(p++) = 0;
+
+ return buf;
+}
+
+
+int
+my_spawn (char **argv, struct spawn_fd_item_s *fd_list, unsigned int flags)
+{
+ SECURITY_ATTRIBUTES sec_attr;
+ PROCESS_INFORMATION pi =
+ {
+ NULL, /* returns process handle */
+ 0, /* returns primary thread handle */
+ 0, /* returns pid */
+ 0 /* returns tid */
+ };
+ STARTUPINFO si;
+ char *envblock = NULL;
+ int cr_flags = CREATE_DEFAULT_ERROR_MODE
+ | GetPriorityClass (GetCurrentProcess ());
+ int i;
+ char *arg_string;
+ int duped_stdin = 0;
+ int duped_stdout = 0;
+ int duped_stderr = 0;
+ HANDLE hnul = INVALID_HANDLE_VALUE;
+ /* FIXME. */
+ int debug_me = 0;
+
+ i = 0;
+ while (argv[i])
+ {
+ fprintf (stderr, PGM": argv[%2i] = %s\n", i, argv[i]);
+ i++;
+ }
+
+ memset (&sec_attr, 0, sizeof sec_attr);
+ sec_attr.nLength = sizeof sec_attr;
+ sec_attr.bInheritHandle = FALSE;
+
+ arg_string = build_commandline (argv);
+ if (!arg_string)
+ return -1;
+
+ memset (&si, 0, sizeof si);
+ si.cb = sizeof (si);
+ si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
+ si.wShowWindow = debug_me ? SW_SHOW : SW_HIDE;
+ si.hStdInput = GetStdHandle (STD_INPUT_HANDLE);
+ si.hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE);
+ si.hStdError = GetStdHandle (STD_ERROR_HANDLE);
+
+ fprintf (stderr, PGM": spawning: %s\n", arg_string);
+
+ for (i = 0; fd_list[i].handle != -1; i++)
+ {
+ /* The handle already is inheritable. */
+ if (fd_list[i].dup_to == 0)
+ {
+ si.hStdInput = (HANDLE) fd_list[i].peer_name;
+ duped_stdin = 1;
+ fprintf (stderr, PGM": dup 0x%x to stdin\n", fd_list[i].peer_name);
+ }
+ else if (fd_list[i].dup_to == 1)
+ {
+ si.hStdOutput = (HANDLE) fd_list[i].peer_name;
+ duped_stdout = 1;
+ fprintf (stderr, PGM": dup 0x%x to stdout\n", fd_list[i].peer_name);
+ }
+ else if (fd_list[i].dup_to == 2)
+ {
+ si.hStdError = (HANDLE) fd_list[i].peer_name;
+ duped_stderr = 1;
+ fprintf (stderr, PGM":dup 0x%x to stderr\n", fd_list[i].peer_name);
+ }
+ }
+
+ if (!duped_stdin || !duped_stdout || !duped_stderr)
+ {
+ SECURITY_ATTRIBUTES sa;
+
+ memset (&sa, 0, sizeof sa);
+ sa.nLength = sizeof sa;
+ sa.bInheritHandle = TRUE;
+ hnul = CreateFile ("nul",
+ GENERIC_READ|GENERIC_WRITE,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ &sa,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+ if (hnul == INVALID_HANDLE_VALUE)
+ {
+ free (arg_string);
+ /* FIXME: Should translate the error code. */
+ errno = EIO;
+ return -1;
+ }
+ /* Make sure that the process has a connected stdin. */
+ if (!duped_stdin)
+ si.hStdInput = hnul;
+ /* Make sure that the process has a connected stdout. */
+ if (!duped_stdout)
+ si.hStdOutput = hnul;
+ /* We normally don't want all the normal output. */
+ if (!duped_stderr)
+ si.hStdError = hnul;
+ }
+
+ cr_flags |= CREATE_SUSPENDED;
+ cr_flags |= DETACHED_PROCESS;
+ if (!CreateProcessA (argv[0],
+ arg_string,
+ &sec_attr, /* process security attributes */
+ &sec_attr, /* thread security attributes */
+ TRUE, /* inherit handles */
+ cr_flags, /* creation flags */
+ envblock, /* environment */
+ NULL, /* use current drive/directory */
+ &si, /* startup information */
+ &pi)) /* returns process information */
+ {
+ free (arg_string);
+ /* FIXME: Should translate the error code. */
+ errno = EIO;
+ return -1;
+ }
+
+ free (arg_string);
+
+ /* Close the /dev/nul handle if used. */
+ if (hnul != INVALID_HANDLE_VALUE)
+ CloseHandle (hnul);
+
+ for (i = 0; fd_list[i].handle != -1; i++)
+ CloseHandle ((HANDLE) fd_list[i].handle);
+
+ if (flags & IOSPAWN_FLAG_ALLOW_SET_FG)
+ {
+ static int initialized;
+ static BOOL (WINAPI * func)(DWORD);
+ void *handle;
+
+ if (!initialized)
+ {
+ /* Available since W2000; thus we dynload it. */
+ initialized = 1;
+ handle = LoadLibrary ("user32.dll");
+ if (handle)
+ {
+ func = GetProcAddress (handle, "AllowSetForegroundWindow");
+ if (!func)
+ FreeLibrary (handle);
+ }
+ }
+
+ if (func)
+ {
+ int rc = func (pi.dwProcessId);
+ fprintf (stderr, PGM": AllowSetForegroundWindow(%d): rc=%d\n",
+ (int)pi.dwProcessId, rc);
+ }
+ }
+
+ ResumeThread (pi.hThread);
+ CloseHandle (pi.hThread);
+ CloseHandle (pi.hProcess);
+
+ return 0;
+}
+
+
+#define MAX_TRANS 10
+
+int
+translate_get_from_file (const char *trans_file,
+ struct spawn_fd_item_s *fd_list,
+ unsigned int *r_flags)
+{
+ /* Hold roughly MAX_TRANS triplets of 64 bit numbers in hex
+ notation: "0xFEDCBA9876543210". 10*19*4 - 1 = 759. This plans
+ ahead for a time when a HANDLE is 64 bit. */
+#define BUFFER_MAX 810
+
+ char line[BUFFER_MAX + 1];
+ char *linep;
+ int idx;
+ int res;
+ int fd;
+
+ *r_flags = 0;
+
+ fd = open (trans_file, O_RDONLY);
+ if (fd < 0)
+ return -1;
+
+ /* We always read one line from stdin. */
+ res = read (fd, line, BUFFER_MAX);
+ close (fd);
+ if (res < 0)
+ return -1;
+
+ line[BUFFER_MAX] = '\0';
+ linep = strchr (line, '\n');
+ if (linep)
+ {
+ if (linep > line && linep[-1] == '\r')
+ linep--;
+ *linep = '\0';
+ }
+ linep = line;
+
+ /* Now start to read mapping pairs. */
+ for (idx = 0; idx < MAX_TRANS; idx++)
+ {
+ unsigned long from;
+ long dup_to;
+ unsigned long to;
+ unsigned long loc;
+ char *tail;
+
+ /* FIXME: Maybe could use scanf. */
+ while (isspace (*((unsigned char *)linep)))
+ linep++;
+ if (*linep == '\0')
+ break;
+ if (!idx && *linep == '~')
+ {
+ /* Spawn flags have been passed. */
+ linep++;
+ *r_flags = strtoul (linep, &tail, 0);
+ if (tail == NULL || ! (*tail == '\0' || isspace (*tail)))
+ break;
+ linep = tail;
+
+ while (isspace (*((unsigned char *)linep)))
+ linep++;
+ if (*linep == '\0')
+ break;
+ }
+
+ from = strtoul (linep, &tail, 0);
+ if (tail == NULL || ! (*tail == '\0' || isspace (*tail)))
+ break;
+ linep = tail;
+
+ while (isspace (*linep))
+ linep++;
+ if (*linep == '\0')
+ break;
+ dup_to = strtol (linep, &tail, 0);
+ if (tail == NULL || ! (*tail == '\0' || isspace (*tail)))
+ break;
+ linep = tail;
+
+ while (isspace (*linep))
+ linep++;
+ if (*linep == '\0')
+ break;
+ to = strtoul (linep, &tail, 0);
+ if (tail == NULL || ! (*tail == '\0' || isspace (*tail)))
+ break;
+ linep = tail;
+
+ while (isspace (*linep))
+ linep++;
+ if (*linep == '\0')
+ break;
+ loc = strtoul (linep, &tail, 0);
+ if (tail == NULL || ! (*tail == '\0' || isspace (*tail)))
+ break;
+ linep = tail;
+
+ fd_list[idx].handle = from;
+ fd_list[idx].dup_to = dup_to;
+ fd_list[idx].peer_name = to;
+ fd_list[idx].arg_loc = loc;
+ }
+ fd_list[idx].handle = -1;
+ fd_list[idx].dup_to = -1;
+ fd_list[idx].peer_name = -1;
+ fd_list[idx].arg_loc = 0;
+ return 0;
+}
+
+
+/* Read the translated handles from TRANS_FILE and do a substitution
+ in ARGV. Returns the new argv and the list of substitutions in
+ FD_LIST (which must be MAX_TRANS+1 large). */
+char **
+translate_handles (const char *trans_file, const char * const *argv,
+ struct spawn_fd_item_s *fd_list, unsigned int *r_flags)
+{
+ int res;
+ int idx;
+ int n_args;
+ char **args;
+
+ res = translate_get_from_file (trans_file, fd_list, r_flags);
+ if (res < 0)
+ return NULL;
+
+ for (idx = 0; argv[idx]; idx++)
+ ;
+ args = malloc (sizeof (*args) * (idx + 1));
+ for (idx = 0; argv[idx]; idx++)
+ {
+ args[idx] = strdup (argv[idx]);
+ if (!args[idx])
+ return NULL;
+ }
+ args[idx] = NULL;
+ n_args = idx;
+
+ for (idx = 0; fd_list[idx].handle != -1; idx++)
+ {
+ char buf[25];
+ int aidx;
+
+ aidx = fd_list[idx].arg_loc;
+ if (aidx == 0)
+ continue;
+
+ if (aidx >= n_args)
+ {
+ fprintf (stderr, PGM": translation file does not match args\n");
+ return NULL;
+ }
+
+ args[aidx] = malloc (sizeof (buf));
+ /* We currently disable translation for stdin/stdout/stderr. We
+ assume that the spawned program handles 0/1/2 specially
+ already. FIXME: Check if this is true. */
+ if (!args[idx] || fd_list[idx].dup_to != -1)
+ return NULL;
+
+ /* NOTE: Here is the part where application specific knowledge
+ comes in. GPGME/GnuPG uses two forms of descriptor
+ specification, a plain number and a "-&" form. */
+ if (argv[aidx][0] == '-' && argv[aidx][1] == '&')
+ snprintf (args[aidx], sizeof (buf), "-&%d", fd_list[idx].peer_name);
+ else
+ snprintf (args[aidx], sizeof (buf), "%d", fd_list[idx].peer_name);
+ }
+ return args;
+}
+
+
+int
+main (int argc, const char * const *argv)
+{
+ int rc = 0;
+ char **argv_spawn;
+ struct spawn_fd_item_s fd_list[MAX_TRANS + 1];
+ unsigned int flags;
+
+ if (argc < 3)
+ {
+ rc = 2;
+ goto leave;
+ }
+
+ argv_spawn = translate_handles (argv[1], &argv[2], fd_list, &flags);
+ if (!argv_spawn)
+ {
+ rc = 2;
+ goto leave;
+ }
+
+ /* Using execv does not replace the existing program image, but
+ spawns a new one and daemonizes it, confusing the command line
+ interpreter. So we have to use spawnv. */
+ rc = my_spawn (argv_spawn, fd_list, flags);
+ if (rc < 0)
+ {
+ fprintf (stderr, PGM": executing `%s' failed: %s\n",
+ argv[0], strerror (errno));
+ rc = 2;
+ goto leave;
+ }
+
+ leave:
+ if (rc)
+ fprintf (stderr, PGM": internal error\n");
+ /* Always try to delete the temporary file. */
+ if (argc >= 2)
+ {
+ if (DeleteFile (argv[1]) == 0)
+ fprintf (stderr, PGM": failed to delete %s: ec=%ld\n",
+ argv[1], GetLastError ());
+ }
+ return rc;
+}
diff --git a/src/gpgme.c b/src/gpgme.c
new file mode 100644
index 0000000..c8e6a8b
--- /dev/null
+++ b/src/gpgme.c
@@ -0,0 +1,916 @@
+/* gpgme.c - GnuPG Made Easy.
+ Copyright (C) 2000 Werner Koch (dd9jn)
+ Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#ifdef HAVE_LOCALE_H
+#include <locale.h>
+#endif
+
+#include "util.h"
+#include "context.h"
+#include "ops.h"
+#include "wait.h"
+#include "debug.h"
+#include "priv-io.h"
+
+
+/* The default locale. */
+DEFINE_STATIC_LOCK (def_lc_lock);
+static char *def_lc_ctype;
+static char *def_lc_messages;
+
+
+gpgme_error_t _gpgme_selftest = GPG_ERR_NOT_OPERATIONAL;
+
+/* Protects all reference counters in result structures. All other
+ accesses to a result structure are read only. */
+DEFINE_STATIC_LOCK (result_ref_lock);
+
+
+/* Create a new context as an environment for GPGME crypto
+ operations. */
+gpgme_error_t
+gpgme_new (gpgme_ctx_t *r_ctx)
+{
+ gpgme_ctx_t ctx;
+ TRACE_BEG (DEBUG_CTX, "gpgme_new", r_ctx);
+
+ if (_gpgme_selftest)
+ return TRACE_ERR (gpgme_error (_gpgme_selftest));
+
+ if (!r_ctx)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ ctx = calloc (1, sizeof *ctx);
+ if (!ctx)
+ return TRACE_ERR (gpg_error_from_errno (errno));
+
+ INIT_LOCK (ctx->lock);
+
+ _gpgme_engine_info_copy (&ctx->engine_info);
+ if (!ctx->engine_info)
+ {
+ free (ctx);
+ return TRACE_ERR (gpg_error_from_errno (errno));
+ }
+
+ ctx->keylist_mode = GPGME_KEYLIST_MODE_LOCAL;
+ ctx->include_certs = GPGME_INCLUDE_CERTS_DEFAULT;
+ ctx->protocol = GPGME_PROTOCOL_OpenPGP;
+ ctx->sub_protocol = GPGME_PROTOCOL_DEFAULT;
+ _gpgme_fd_table_init (&ctx->fdt);
+
+ LOCK (def_lc_lock);
+ if (def_lc_ctype)
+ {
+ ctx->lc_ctype = strdup (def_lc_ctype);
+ if (!ctx->lc_ctype)
+ {
+ UNLOCK (def_lc_lock);
+ _gpgme_engine_info_release (ctx->engine_info);
+ free (ctx);
+ return TRACE_ERR (gpg_error_from_errno (errno));
+ }
+ }
+ else
+ def_lc_ctype = NULL;
+
+ if (def_lc_messages)
+ {
+ ctx->lc_messages = strdup (def_lc_messages);
+ if (!ctx->lc_messages)
+ {
+ UNLOCK (def_lc_lock);
+ if (ctx->lc_ctype)
+ free (ctx->lc_ctype);
+ _gpgme_engine_info_release (ctx->engine_info);
+ free (ctx);
+ return TRACE_ERR (gpg_error_from_errno (errno));
+ }
+ }
+ else
+ def_lc_messages = NULL;
+ UNLOCK (def_lc_lock);
+
+ *r_ctx = ctx;
+
+ return TRACE_SUC1 ("ctx=%p", ctx);
+}
+
+
+gpgme_error_t
+_gpgme_cancel_with_err (gpgme_ctx_t ctx, gpg_error_t ctx_err,
+ gpg_error_t op_err)
+{
+ gpgme_error_t err;
+ struct gpgme_io_event_done_data data;
+
+ TRACE_BEG2 (DEBUG_CTX, "_gpgme_cancel_with_err", ctx, "ctx_err=%i, op_err=%i",
+ ctx_err, op_err);
+
+ if (ctx_err)
+ {
+ err = _gpgme_engine_cancel (ctx->engine);
+ if (err)
+ return TRACE_ERR (err);
+ }
+ else
+ {
+ err = _gpgme_engine_cancel_op (ctx->engine);
+ if (err)
+ return TRACE_ERR (err);
+ }
+
+ data.err = ctx_err;
+ data.op_err = op_err;
+
+ _gpgme_engine_io_event (ctx->engine, GPGME_EVENT_DONE, &data);
+
+ return TRACE_ERR (0);
+}
+
+
+/* Cancel a pending asynchronous operation. */
+gpgme_error_t
+gpgme_cancel (gpgme_ctx_t ctx)
+{
+ gpg_error_t err;
+
+ TRACE_BEG (DEBUG_CTX, "gpgme_cancel", ctx);
+
+ if (!ctx)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ err = _gpgme_cancel_with_err (ctx, gpg_error (GPG_ERR_CANCELED), 0);
+
+ return TRACE_ERR (err);
+}
+
+
+/* Cancel a pending operation asynchronously. */
+gpgme_error_t
+gpgme_cancel_async (gpgme_ctx_t ctx)
+{
+ TRACE_BEG (DEBUG_CTX, "gpgme_cancel_async", ctx);
+
+ if (!ctx)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ LOCK (ctx->lock);
+ ctx->canceled = 1;
+ UNLOCK (ctx->lock);
+
+ return TRACE_ERR (0);
+}
+
+
+/* Release all resources associated with the given context. */
+void
+gpgme_release (gpgme_ctx_t ctx)
+{
+ TRACE (DEBUG_CTX, "gpgme_release", ctx);
+
+ if (!ctx)
+ return;
+
+ _gpgme_engine_release (ctx->engine);
+ _gpgme_fd_table_deinit (&ctx->fdt);
+ _gpgme_release_result (ctx);
+ _gpgme_signers_clear (ctx);
+ _gpgme_sig_notation_clear (ctx);
+ if (ctx->signers)
+ free (ctx->signers);
+ if (ctx->lc_ctype)
+ free (ctx->lc_ctype);
+ if (ctx->lc_messages)
+ free (ctx->lc_messages);
+ _gpgme_engine_info_release (ctx->engine_info);
+ DESTROY_LOCK (ctx->lock);
+ free (ctx);
+}
+
+
+void
+gpgme_result_ref (void *result)
+{
+ struct ctx_op_data *data;
+
+ if (! result)
+ return;
+
+ data = (void*)((char*)result - sizeof (struct ctx_op_data));
+
+ assert (data->magic == CTX_OP_DATA_MAGIC);
+
+ LOCK (result_ref_lock);
+ data->references++;
+ UNLOCK (result_ref_lock);
+}
+
+
+void
+gpgme_result_unref (void *result)
+{
+ struct ctx_op_data *data;
+
+ if (! result)
+ return;
+
+ data = (void*)((char*)result - sizeof (struct ctx_op_data));
+
+ assert (data->magic == CTX_OP_DATA_MAGIC);
+
+ LOCK (result_ref_lock);
+ if (--data->references)
+ {
+ UNLOCK (result_ref_lock);
+ return;
+ }
+ UNLOCK (result_ref_lock);
+
+ if (data->cleanup)
+ (*data->cleanup) (data->hook);
+ free (data);
+}
+
+
+void
+_gpgme_release_result (gpgme_ctx_t ctx)
+{
+ struct ctx_op_data *data = ctx->op_data;
+
+ while (data)
+ {
+ struct ctx_op_data *next_data = data->next;
+ data->next = NULL;
+ gpgme_result_unref (data->hook);
+ data = next_data;
+ }
+ ctx->op_data = NULL;
+}
+
+
+gpgme_error_t
+gpgme_set_protocol (gpgme_ctx_t ctx, gpgme_protocol_t protocol)
+{
+ TRACE_BEG2 (DEBUG_CTX, "gpgme_set_protocol", ctx, "protocol=%i (%s)",
+ protocol, gpgme_get_protocol_name (protocol)
+ ? gpgme_get_protocol_name (protocol) : "invalid");
+
+ if (protocol != GPGME_PROTOCOL_OpenPGP
+ && protocol != GPGME_PROTOCOL_CMS
+ && protocol != GPGME_PROTOCOL_GPGCONF
+ && protocol != GPGME_PROTOCOL_ASSUAN
+ && protocol != GPGME_PROTOCOL_G13
+ && protocol != GPGME_PROTOCOL_UISERVER)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ if (!ctx)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ if (ctx->protocol != protocol)
+ {
+ /* Shut down the engine when switching protocols. */
+ if (ctx->engine)
+ {
+ TRACE_LOG1 ("releasing ctx->engine=%p", ctx->engine);
+ _gpgme_engine_release (ctx->engine);
+ ctx->engine = NULL;
+ }
+
+ ctx->protocol = protocol;
+ }
+ return TRACE_ERR (0);
+}
+
+
+gpgme_protocol_t
+gpgme_get_protocol (gpgme_ctx_t ctx)
+{
+ TRACE2 (DEBUG_CTX, "gpgme_get_protocol", ctx,
+ "ctx->protocol=%i (%s)", ctx->protocol,
+ gpgme_get_protocol_name (ctx->protocol)
+ ? gpgme_get_protocol_name (ctx->protocol) : "invalid");
+
+ return ctx->protocol;
+}
+
+
+gpgme_error_t
+gpgme_set_sub_protocol (gpgme_ctx_t ctx, gpgme_protocol_t protocol)
+{
+ TRACE2 (DEBUG_CTX, "gpgme_set_sub_protocol", ctx, "protocol=%i (%s)",
+ protocol, gpgme_get_protocol_name (protocol)
+ ? gpgme_get_protocol_name (protocol) : "invalid");
+
+ if (!ctx)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ ctx->sub_protocol = protocol;
+ return 0;
+}
+
+
+gpgme_error_t
+gpgme_get_sub_protocol (gpgme_ctx_t ctx)
+{
+ TRACE2 (DEBUG_CTX, "gpgme_get_sub_protocol", ctx,
+ "ctx->sub_protocol=%i (%s)", ctx->sub_protocol,
+ gpgme_get_protocol_name (ctx->sub_protocol)
+ ? gpgme_get_protocol_name (ctx->sub_protocol) : "invalid");
+
+ return ctx->sub_protocol;
+}
+
+
+const char *
+gpgme_get_protocol_name (gpgme_protocol_t protocol)
+{
+ switch (protocol)
+ {
+ case GPGME_PROTOCOL_OpenPGP:
+ return "OpenPGP";
+
+ case GPGME_PROTOCOL_CMS:
+ return "CMS";
+
+ case GPGME_PROTOCOL_GPGCONF:
+ return "GPGCONF";
+
+ case GPGME_PROTOCOL_ASSUAN:
+ return "Assuan";
+
+ case GPGME_PROTOCOL_G13:
+ return "G13";
+
+ case GPGME_PROTOCOL_UISERVER:
+ return "UIServer";
+
+ case GPGME_PROTOCOL_DEFAULT:
+ return "default";
+
+ case GPGME_PROTOCOL_UNKNOWN:
+ return "unknown";
+
+ default:
+ return NULL;
+ }
+}
+
+/* Enable or disable the use of an ascii armor for all output. */
+void
+gpgme_set_armor (gpgme_ctx_t ctx, int use_armor)
+{
+ TRACE2 (DEBUG_CTX, "gpgme_set_armor", ctx, "use_armor=%i (%s)",
+ use_armor, use_armor ? "yes" : "no");
+
+ if (!ctx)
+ return;
+
+ ctx->use_armor = use_armor;
+}
+
+
+/* Return the state of the armor flag. */
+int
+gpgme_get_armor (gpgme_ctx_t ctx)
+{
+ TRACE2 (DEBUG_CTX, "gpgme_get_armor", ctx, "ctx->use_armor=%i (%s)",
+ ctx->use_armor, ctx->use_armor ? "yes" : "no");
+ return ctx->use_armor;
+}
+
+
+/* Enable or disable the use of the special textmode. Textmode is for
+ example used for the RFC2015 signatures; note that the updated RFC
+ 3156 mandates that the MUA does some preparations so that textmode
+ is not needed anymore. */
+void
+gpgme_set_textmode (gpgme_ctx_t ctx, int use_textmode)
+{
+ TRACE2 (DEBUG_CTX, "gpgme_set_textmode", ctx, "use_textmode=%i (%s)",
+ use_textmode, use_textmode ? "yes" : "no");
+
+ if (!ctx)
+ return;
+
+ ctx->use_textmode = use_textmode;
+}
+
+/* Return the state of the textmode flag. */
+int
+gpgme_get_textmode (gpgme_ctx_t ctx)
+{
+ TRACE2 (DEBUG_CTX, "gpgme_get_textmode", ctx, "ctx->use_textmode=%i (%s)",
+ ctx->use_textmode, ctx->use_textmode ? "yes" : "no");
+ return ctx->use_textmode;
+}
+
+
+/* Set the number of certifications to include in an S/MIME message.
+ The default is GPGME_INCLUDE_CERTS_DEFAULT. -1 means all certs,
+ and -2 means all certs except the root cert. */
+void
+gpgme_set_include_certs (gpgme_ctx_t ctx, int nr_of_certs)
+{
+ if (!ctx)
+ return;
+
+ if (nr_of_certs == GPGME_INCLUDE_CERTS_DEFAULT)
+ ctx->include_certs = GPGME_INCLUDE_CERTS_DEFAULT;
+ else if (nr_of_certs < -2)
+ ctx->include_certs = -2;
+ else
+ ctx->include_certs = nr_of_certs;
+
+ TRACE2 (DEBUG_CTX, "gpgme_set_include_certs", ctx, "nr_of_certs=%i%s",
+ nr_of_certs, nr_of_certs == ctx->include_certs ? "" : " (-2)");
+}
+
+
+/* Get the number of certifications to include in an S/MIME
+ message. */
+int
+gpgme_get_include_certs (gpgme_ctx_t ctx)
+{
+ TRACE1 (DEBUG_CTX, "gpgme_get_include_certs", ctx, "ctx->include_certs=%i",
+ ctx->include_certs);
+ return ctx->include_certs;
+}
+
+
+/* This function changes the default behaviour of the keylisting
+ functions. MODE is a bitwise-OR of the GPGME_KEYLIST_* flags. The
+ default mode is GPGME_KEYLIST_MODE_LOCAL. */
+gpgme_error_t
+gpgme_set_keylist_mode (gpgme_ctx_t ctx, gpgme_keylist_mode_t mode)
+{
+ TRACE1 (DEBUG_CTX, "gpgme_set_keylist_mode", ctx, "keylist_mode=0x%x",
+ mode);
+
+ if (!ctx)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ ctx->keylist_mode = mode;
+ return 0;
+}
+
+/* This function returns the default behaviour of the keylisting
+ functions. */
+gpgme_keylist_mode_t
+gpgme_get_keylist_mode (gpgme_ctx_t ctx)
+{
+ TRACE1 (DEBUG_CTX, "gpgme_get_keylist_mode", ctx,
+ "ctx->keylist_mode=0x%x", ctx->keylist_mode);
+ return ctx->keylist_mode;
+}
+
+
+/* This function sets a callback function to be used to pass a
+ passphrase to gpg. */
+void
+gpgme_set_passphrase_cb (gpgme_ctx_t ctx, gpgme_passphrase_cb_t cb,
+ void *cb_value)
+{
+ TRACE2 (DEBUG_CTX, "gpgme_set_passphrase_cb", ctx,
+ "passphrase_cb=%p/%p", cb, cb_value);
+
+ if (!ctx)
+ return;
+
+ ctx->passphrase_cb = cb;
+ ctx->passphrase_cb_value = cb_value;
+}
+
+
+/* This function returns the callback function to be used to pass a
+ passphrase to the crypto engine. */
+void
+gpgme_get_passphrase_cb (gpgme_ctx_t ctx, gpgme_passphrase_cb_t *r_cb,
+ void **r_cb_value)
+{
+ TRACE2 (DEBUG_CTX, "gpgme_get_passphrase_cb", ctx,
+ "ctx->passphrase_cb=%p/%p",
+ ctx->passphrase_cb, ctx->passphrase_cb_value);
+ if (r_cb)
+ *r_cb = ctx->passphrase_cb;
+ if (r_cb_value)
+ *r_cb_value = ctx->passphrase_cb_value;
+}
+
+
+/* This function sets a callback function to be used as a progress
+ indicator. */
+void
+gpgme_set_progress_cb (gpgme_ctx_t ctx, gpgme_progress_cb_t cb, void *cb_value)
+{
+ TRACE2 (DEBUG_CTX, "gpgme_set_progress_cb", ctx, "progress_cb=%p/%p",
+ cb, cb_value);
+
+ if (!ctx)
+ return;
+
+ ctx->progress_cb = cb;
+ ctx->progress_cb_value = cb_value;
+}
+
+
+/* This function returns the callback function to be used as a
+ progress indicator. */
+void
+gpgme_get_progress_cb (gpgme_ctx_t ctx, gpgme_progress_cb_t *r_cb,
+ void **r_cb_value)
+{
+ TRACE2 (DEBUG_CTX, "gpgme_get_progress_cb", ctx, "ctx->progress_cb=%p/%p",
+ ctx->progress_cb, ctx->progress_cb_value);
+ if (r_cb)
+ *r_cb = ctx->progress_cb;
+ if (r_cb_value)
+ *r_cb_value = ctx->progress_cb_value;
+}
+
+
+/* Set the I/O callback functions for CTX to IO_CBS. */
+void
+gpgme_set_io_cbs (gpgme_ctx_t ctx, gpgme_io_cbs_t io_cbs)
+{
+ if (!ctx)
+ return;
+
+ if (io_cbs)
+ {
+ TRACE6 (DEBUG_CTX, "gpgme_set_io_cbs", ctx,
+ "io_cbs=%p (add=%p/%p, remove=%p, event=%p/%p",
+ io_cbs, io_cbs->add, io_cbs->add_priv, io_cbs->remove,
+ io_cbs->event, io_cbs->event_priv);
+ ctx->io_cbs = *io_cbs;
+ }
+ else
+ {
+ TRACE1 (DEBUG_CTX, "gpgme_set_io_cbs", ctx,
+ "io_cbs=%p (default)", io_cbs);
+ ctx->io_cbs.add = NULL;
+ ctx->io_cbs.add_priv = NULL;
+ ctx->io_cbs.remove = NULL;
+ ctx->io_cbs.event = NULL;
+ ctx->io_cbs.event_priv = NULL;
+ }
+}
+
+
+/* This function provides access to the internal read function; it is
+ normally not used. */
+ssize_t
+gpgme_io_read (int fd, void *buffer, size_t count)
+{
+ int ret;
+ TRACE_BEG2 (DEBUG_GLOBAL, "gpgme_io_read", fd,
+ "buffer=%p, count=%u", buffer, count);
+
+ ret = _gpgme_io_read (fd, buffer, count);
+
+ return TRACE_SYSRES (ret);
+}
+
+
+/* This function provides access to the internal write function. It
+ is to be used by user callbacks to return data to gpgme. See
+ gpgme_passphrase_cb_t and gpgme_edit_cb_t. */
+ssize_t
+gpgme_io_write (int fd, const void *buffer, size_t count)
+{
+ int ret;
+ TRACE_BEG2 (DEBUG_GLOBAL, "gpgme_io_write", fd,
+ "buffer=%p, count=%u", buffer, count);
+
+ ret = _gpgme_io_write (fd, buffer, count);
+
+ return TRACE_SYSRES (ret);
+}
+
+
+/* This function returns the callback function for I/O. */
+void
+gpgme_get_io_cbs (gpgme_ctx_t ctx, gpgme_io_cbs_t io_cbs)
+{
+ TRACE6 (DEBUG_CTX, "gpgme_get_io_cbs", ctx,
+ "io_cbs=%p, ctx->io_cbs.add=%p/%p, .remove=%p, .event=%p/%p",
+ io_cbs, io_cbs->add, io_cbs->add_priv, io_cbs->remove,
+ io_cbs->event, io_cbs->event_priv);
+
+ *io_cbs = ctx->io_cbs;
+}
+
+
+/* This function sets the locale for the context CTX, or the default
+ locale if CTX is a null pointer. */
+gpgme_error_t
+gpgme_set_locale (gpgme_ctx_t ctx, int category, const char *value)
+{
+ int failed = 0;
+ char *new_lc_ctype = NULL;
+ char *new_lc_messages = NULL;
+
+ TRACE_BEG2 (DEBUG_CTX, "gpgme_set_locale", ctx,
+ "category=%i, value=%s", category, value ? value : "(null)");
+
+ if (!ctx)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+#define PREPARE_ONE_LOCALE(lcat, ucat) \
+ if (!failed && value \
+ && (category == LC_ALL || category == LC_ ## ucat)) \
+ { \
+ new_lc_ ## lcat = strdup (value); \
+ if (!new_lc_ ## lcat) \
+ failed = 1; \
+ }
+
+#ifdef LC_CTYPE
+ PREPARE_ONE_LOCALE (ctype, CTYPE);
+#endif
+#ifdef LC_MESSAGES
+ PREPARE_ONE_LOCALE (messages, MESSAGES);
+#endif
+
+ if (failed)
+ {
+ int saved_errno = errno;
+
+ if (new_lc_ctype)
+ free (new_lc_ctype);
+ if (new_lc_messages)
+ free (new_lc_messages);
+
+ return TRACE_ERR (gpg_error_from_errno (saved_errno));
+ }
+
+#define SET_ONE_LOCALE(lcat, ucat) \
+ if (category == LC_ALL || category == LC_ ## ucat) \
+ { \
+ if (ctx) \
+ { \
+ if (ctx->lc_ ## lcat) \
+ free (ctx->lc_ ## lcat); \
+ ctx->lc_ ## lcat = new_lc_ ## lcat; \
+ } \
+ else \
+ { \
+ if (def_lc_ ## lcat) \
+ free (def_lc_ ## lcat); \
+ def_lc_ ## lcat = new_lc_ ## lcat; \
+ } \
+ }
+
+ if (!ctx)
+ LOCK (def_lc_lock);
+#ifdef LC_CTYPE
+ SET_ONE_LOCALE (ctype, CTYPE);
+#endif
+#ifdef LC_MESSAGES
+ SET_ONE_LOCALE (messages, MESSAGES);
+#endif
+ if (!ctx)
+ UNLOCK (def_lc_lock);
+
+ return TRACE_ERR (0);
+}
+
+
+/* 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. */
+gpgme_engine_info_t
+gpgme_ctx_get_engine_info (gpgme_ctx_t ctx)
+{
+ TRACE1 (DEBUG_CTX, "gpgme_ctx_get_engine_info", ctx,
+ "ctx->engine_info=%p", ctx->engine_info);
+ return ctx->engine_info;
+}
+
+
+/* Set the engine info for the context CTX, protocol PROTO, to the
+ 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, const char *home_dir)
+{
+ gpgme_error_t err;
+ TRACE_BEG4 (DEBUG_CTX, "gpgme_ctx_set_engine_info", ctx,
+ "protocol=%i (%s), file_name=%s, home_dir=%s",
+ proto, gpgme_get_protocol_name (proto)
+ ? gpgme_get_protocol_name (proto) : "unknown",
+ file_name ? file_name : "(default)",
+ home_dir ? home_dir : "(default)");
+
+ if (!ctx)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ /* Shut down the engine when changing engine info. */
+ if (ctx->engine)
+ {
+ TRACE_LOG1 ("releasing ctx->engine=%p", ctx->engine);
+ _gpgme_engine_release (ctx->engine);
+ ctx->engine = NULL;
+ }
+ err = _gpgme_set_engine_info (ctx->engine_info, proto,
+ file_name, home_dir);
+ return TRACE_ERR (err);
+}
+
+
+/* Clear all notation data from the context. */
+void
+_gpgme_sig_notation_clear (gpgme_ctx_t ctx)
+{
+ gpgme_sig_notation_t notation;
+
+ if (!ctx)
+ return;
+
+ notation = ctx->sig_notations;
+ while (notation)
+ {
+ gpgme_sig_notation_t next_notation = notation->next;
+ _gpgme_sig_notation_free (notation);
+ notation = next_notation;
+ }
+ ctx->sig_notations = NULL;
+}
+
+void
+gpgme_sig_notation_clear (gpgme_ctx_t ctx)
+{
+ TRACE (DEBUG_CTX, "gpgme_sig_notation_clear", ctx);
+
+ if (!ctx)
+ return;
+
+ _gpgme_sig_notation_clear (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. */
+gpgme_error_t
+gpgme_sig_notation_add (gpgme_ctx_t ctx, const char *name,
+ const char *value, gpgme_sig_notation_flags_t flags)
+{
+ gpgme_error_t err;
+ gpgme_sig_notation_t notation;
+ gpgme_sig_notation_t *lastp;
+
+ TRACE_BEG3 (DEBUG_CTX, "gpgme_sig_notation_add", ctx,
+ "name=%s, value=%s, flags=0x%x",
+ name ? name : "(null)", value ? value : "(null)",
+ flags);
+
+ if (!ctx)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ if (name)
+ flags |= GPGME_SIG_NOTATION_HUMAN_READABLE;
+ else
+ flags &= ~GPGME_SIG_NOTATION_HUMAN_READABLE;
+
+ err = _gpgme_sig_notation_create (&notation, name, name ? strlen (name) : 0,
+ value, value ? strlen (value) : 0, flags);
+ if (err)
+ return TRACE_ERR (err);
+
+ lastp = &ctx->sig_notations;
+ while (*lastp)
+ lastp = &(*lastp)->next;
+
+ *lastp = notation;
+ return TRACE_ERR (0);
+}
+
+
+/* Get the sig notations for this context. */
+gpgme_sig_notation_t
+gpgme_sig_notation_get (gpgme_ctx_t ctx)
+{
+ if (!ctx)
+ {
+ TRACE (DEBUG_CTX, "gpgme_sig_notation_get", ctx);
+ return NULL;
+ }
+ TRACE1 (DEBUG_CTX, "gpgme_sig_notation_get", ctx,
+ "ctx->sig_notations=%p", ctx->sig_notations);
+
+ return ctx->sig_notations;
+}
+
+
+const char *
+gpgme_pubkey_algo_name (gpgme_pubkey_algo_t algo)
+{
+ switch (algo)
+ {
+ case GPGME_PK_RSA:
+ return "RSA";
+
+ case GPGME_PK_RSA_E:
+ return "RSA-E";
+
+ case GPGME_PK_RSA_S:
+ return "RSA-S";
+
+ case GPGME_PK_ELG_E:
+ return "ELG-E";
+
+ case GPGME_PK_DSA:
+ return "DSA";
+
+ case GPGME_PK_ELG:
+ return "ELG";
+
+ case GPGME_PK_ECDSA:
+ return "ECDSA";
+
+ case GPGME_PK_ECDH:
+ return "ECDH";
+
+ default:
+ return NULL;
+ }
+}
+
+
+const char *
+gpgme_hash_algo_name (gpgme_hash_algo_t algo)
+{
+ switch (algo)
+ {
+ case GPGME_MD_MD5:
+ return "MD5";
+
+ case GPGME_MD_SHA1:
+ return "SHA1";
+
+ case GPGME_MD_RMD160:
+ return "RIPEMD160";
+
+ case GPGME_MD_MD2:
+ return "MD2";
+
+ case GPGME_MD_TIGER:
+ return "TIGER192";
+
+ case GPGME_MD_HAVAL:
+ return "HAVAL";
+
+ case GPGME_MD_SHA256:
+ return "SHA256";
+
+ case GPGME_MD_SHA384:
+ return "SHA384";
+
+ case GPGME_MD_SHA512:
+ return "SHA512";
+
+ case GPGME_MD_MD4:
+ return "MD4";
+
+ case GPGME_MD_CRC32:
+ return "CRC32";
+
+ case GPGME_MD_CRC32_RFC1510:
+ return "CRC32RFC1510";
+
+ case GPGME_MD_CRC24_RFC2440:
+ return "CRC24RFC2440";
+
+ default:
+ return NULL;
+ }
+}
diff --git a/src/gpgme.def b/src/gpgme.def
new file mode 100644
index 0000000..9990b33
--- /dev/null
+++ b/src/gpgme.def
@@ -0,0 +1,206 @@
+; gpgme.def - List of symbols to export.
+; Copyright (C) 2005 g10 Code GmbH
+;
+; This file is part of GPGME.
+;
+; GPGME is free software; you can redistribute it and/or modify
+; it under the terms of the GNU Lesser general Public License as
+; published by the Free Software Foundation; either version 2.1 of
+; the License, or (at your option) any later version.
+;
+; GPGME is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+; GNU Lesser General Public License for more details.
+;
+; You should have received a copy of the GNU Lesser General Public
+; License along with this program; if not, write to the Free Software
+; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+
+
+EXPORTS
+ gpgme_check_version @1
+ gpgme_get_engine_info @2
+ gpgme_engine_check_version @3
+
+ gpgme_err_code_from_errno @4
+ gpgme_err_code_to_errno @5
+ gpgme_err_make_from_errno @6
+ gpgme_error_from_errno @7
+ gpgme_strerror @8
+ gpgme_strerror_r @9
+ gpgme_strsource @10
+
+ gpgme_data_get_encoding @11
+ gpgme_data_new @12
+ gpgme_data_new_from_cbs @13
+ gpgme_data_new_from_fd @14
+ gpgme_data_new_from_file @15
+ gpgme_data_new_from_filepart @16
+ gpgme_data_new_from_mem @17
+ gpgme_data_new_from_stream @18
+ gpgme_data_read @19
+ gpgme_data_release @20
+ gpgme_data_release_and_get_mem @21
+ gpgme_data_seek @22
+ gpgme_data_set_encoding @23
+ gpgme_data_write @24
+
+ gpgme_get_protocol_name @25
+ gpgme_hash_algo_name @26
+ gpgme_pubkey_algo_name @27
+
+ gpgme_new @28
+ gpgme_get_armor @29
+ gpgme_get_include_certs @30
+ gpgme_get_io_cbs @31
+ gpgme_get_keylist_mode @32
+ gpgme_get_passphrase_cb @33
+ gpgme_get_progress_cb @34
+ gpgme_get_protocol @35
+ gpgme_get_textmode @36
+ gpgme_release @37
+ gpgme_set_armor @38
+ gpgme_set_include_certs @39
+ gpgme_set_io_cbs @40
+ gpgme_set_keylist_mode @41
+ gpgme_set_locale @42
+ gpgme_set_passphrase_cb @43
+ gpgme_set_progress_cb @44
+ gpgme_set_protocol @45
+ gpgme_set_textmode @46
+ gpgme_signers_add @47
+ gpgme_signers_clear @48
+ gpgme_signers_enum @49
+
+ gpgme_key_ref @50
+ gpgme_key_unref @51
+ gpgme_key_release @52
+
+ gpgme_trust_item_ref @53
+ gpgme_trust_item_unref @54
+
+ gpgme_cancel @55
+ gpgme_op_card_edit @56
+ gpgme_op_card_edit_start @57
+ gpgme_op_decrypt @58
+ gpgme_op_decrypt_result @59
+ gpgme_op_decrypt_start @60
+ gpgme_op_decrypt_verify @61
+ gpgme_op_decrypt_verify_start @62
+ gpgme_op_delete @63
+ gpgme_op_delete_start @64
+ gpgme_op_edit @65
+ gpgme_op_edit_start @66
+ gpgme_op_encrypt @67
+ gpgme_op_encrypt_result @68
+ gpgme_op_encrypt_sign @69
+ gpgme_op_encrypt_sign_start @70
+ gpgme_op_encrypt_start @71
+ gpgme_op_export @72
+ gpgme_op_export_ext @73
+ gpgme_op_export_ext_start @74
+ gpgme_op_export_start @75
+ gpgme_op_genkey @76
+ gpgme_op_genkey_result @77
+ gpgme_op_genkey_start @78
+ gpgme_get_key @79
+ gpgme_op_import @80
+ gpgme_op_import_result @81
+ gpgme_op_import_start @82
+ gpgme_op_keylist_end @83
+ gpgme_op_keylist_ext_start @84
+ gpgme_op_keylist_next @85
+ gpgme_op_keylist_result @86
+ gpgme_op_keylist_start @87
+ gpgme_op_sign @88
+ gpgme_op_sign_result @89
+ gpgme_op_sign_start @90
+ gpgme_op_trustlist_end @91
+ gpgme_op_trustlist_next @92
+ gpgme_op_trustlist_start @93
+ gpgme_op_verify @94
+ gpgme_op_verify_result @95
+ gpgme_op_verify_start @96
+ gpgme_wait @97
+
+ gpgme_data_new_with_read_cb @98
+ gpgme_data_rewind @99
+ gpgme_get_sig_status @100
+ gpgme_get_sig_string_attr @101
+ gpgme_get_sig_ulong_attr @102
+ gpgme_get_sig_key @103
+ gpgme_key_get_string_attr @104
+ gpgme_key_get_ulong_attr @105
+ gpgme_key_sig_get_string_attr @106
+ gpgme_key_sig_get_ulong_attr @107
+ gpgme_op_import_ext @108
+ gpgme_trust_item_get_int_attr @109
+ gpgme_trust_item_get_string_attr @110
+ gpgme_trust_item_release @111
+
+ gpgme_set_engine_info @112
+
+ gpgme_ctx_get_engine_info @113
+ gpgme_ctx_set_engine_info @114
+
+ gpgme_data_set_file_name @115
+ gpgme_data_get_file_name @116
+
+ gpgme_sig_notation_clear @117
+ gpgme_sig_notation_add @118
+ gpgme_sig_notation_get @119
+
+ gpgme_free @120
+
+ gpgme_get_giochannel @121
+ gpgme_get_fdptr @122
+
+ gpgme_op_getauditlog_start @123
+ gpgme_op_getauditlog @124
+
+ gpgme_conf_release @125
+ gpgme_conf_arg_new @126
+ gpgme_conf_arg_release @127
+ gpgme_conf_opt_change @128
+ gpgme_op_conf_load @129
+ gpgme_op_conf_save @130
+
+ gpgme_cancel_async @131
+
+ gpgme_op_assuan_result @132
+ gpgme_op_assuan_transact_start @133
+ gpgme_op_assuan_transact @134
+
+ gpgme_check_version_internal @135
+
+ gpgme_io_read @136
+ gpgme_io_write @137
+
+ gpgme_result_ref @138
+ gpgme_result_unref @139
+
+ gpgme_op_import_keys @140
+ gpgme_op_import_keys_start @141
+ gpgme_op_export_keys @142
+ gpgme_op_export_keys_start @143
+
+ gpgme_op_assuan_transact_ext @144
+
+ gpgme_wait_ext @145
+ gpgme_op_vfs_mount_result @146
+ gpgme_op_vfs_mount @147
+ gpgme_op_vfs_create @148
+
+ gpgme_key_from_uid @149
+ gpgme_set_sub_protocol @150
+ gpgme_get_sub_protocol @151
+
+ gpgme_op_passwd_start @152
+ gpgme_op_passwd @153
+
+ gpgme_err_code_from_syserror @154
+ gpgme_err_set_errno @155
+
+; END
+
diff --git a/src/gpgme.h.in b/src/gpgme.h.in
new file mode 100644
index 0000000..7263d98
--- /dev/null
+++ b/src/gpgme.h.in
@@ -0,0 +1,2116 @@
+/* gpgme.h - Public interface to GnuPG Made Easy. -*- c -*-
+ Copyright (C) 2000 Werner Koch (dd9jn)
+ Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2009
+ 2010, 2011, 2012 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/>.
+
+ File: @configure_input@ */
+
+#ifndef GPGME_H
+#define GPGME_H
+
+#ifdef __GNUC__
+#define _GPGME_INLINE __inline__
+#elif __STDC_VERSION__ >= 199901L
+#define _GPGME_INLINE inline
+#else
+#define _GPGME_INLINE
+#endif
+
+/* Include stdio.h for the FILE type definition. */
+#include <stdio.h>
+
+@INSERT__TYPEDEFS_FOR_GPGME_H@
+
+#include <time.h>
+
+#include <gpg-error.h>
+
+#ifdef __cplusplus
+extern "C" {
+#if 0 /* just to make Emacs auto-indent happy */
+}
+#endif
+#endif /* __cplusplus */
+
+
+
+/* Check for compiler features. */
+#if __GNUC__
+#define _GPGME_GCC_VERSION (__GNUC__ * 10000 \
+ + __GNUC_MINOR__ * 100 \
+ + __GNUC_PATCHLEVEL__)
+
+#if _GPGME_GCC_VERSION > 30100
+#define _GPGME_DEPRECATED __attribute__ ((__deprecated__))
+#endif
+#endif
+
+#ifndef _GPGME_DEPRECATED
+#define _GPGME_DEPRECATED
+#endif
+
+/* The macro _GPGME_DEPRECATED_OUTSIDE_GPGME suppresses warnings for
+ fields we must access in GPGME for ABI compatibility. */
+#ifdef _GPGME_IN_GPGME
+#define _GPGME_DEPRECATED_OUTSIDE_GPGME
+#else
+#define _GPGME_DEPRECATED_OUTSIDE_GPGME _GPGME_DEPRECATED
+#endif
+
+
+/* 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. */
+#define GPGME_VERSION "@PACKAGE_VERSION@"
+
+/* Check for a matching _FILE_OFFSET_BITS definition. */
+#if @NEED__FILE_OFFSET_BITS@
+#ifndef _FILE_OFFSET_BITS
+#error GPGME was compiled with _FILE_OFFSET_BITS = @NEED__FILE_OFFSET_BITS@, please see the section "Largefile support (LFS)" in the GPGME manual.
+#else
+#if (_FILE_OFFSET_BITS) != (@NEED__FILE_OFFSET_BITS@)
+#error GPGME was compiled with a different value for _FILE_OFFSET_BITS, namely @NEED__FILE_OFFSET_BITS@, please see the section "Largefile support (LFS)" in the GPGME manual.
+#endif
+#endif
+#endif
+
+
+
+/* Some opaque data types used by GPGME. */
+
+/* The context holds some global state and configration options, as
+ well as the results of a crypto operation. */
+struct gpgme_context;
+typedef struct gpgme_context *gpgme_ctx_t;
+
+/* The data object is used by GPGME to exchange arbitrary data. */
+struct gpgme_data;
+typedef struct gpgme_data *gpgme_data_t;
+
+
+/* Wrappers for the libgpg-error library. */
+
+typedef gpg_error_t gpgme_error_t;
+typedef gpg_err_code_t gpgme_err_code_t;
+typedef gpg_err_source_t gpgme_err_source_t;
+
+
+static _GPGME_INLINE gpgme_error_t
+gpgme_err_make (gpgme_err_source_t source, gpgme_err_code_t code)
+{
+ return gpg_err_make (source, code);
+}
+
+
+/* The user can define GPGME_ERR_SOURCE_DEFAULT before including this
+ 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
+
+static _GPGME_INLINE gpgme_error_t
+gpgme_error (gpgme_err_code_t code)
+{
+ return gpgme_err_make (GPGME_ERR_SOURCE_DEFAULT, code);
+}
+
+
+static _GPGME_INLINE gpgme_err_code_t
+gpgme_err_code (gpgme_error_t err)
+{
+ return gpg_err_code (err);
+}
+
+
+static _GPGME_INLINE gpgme_err_source_t
+gpgme_err_source (gpgme_error_t err)
+{
+ return gpg_err_source (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. */
+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. */
+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. */
+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). */
+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. */
+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. */
+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. */
+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. */
+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. */
+gpgme_error_t gpgme_error_from_errno (int err);
+
+
+static _GPGME_INLINE gpgme_error_t
+gpgme_error_from_syserror (void)
+{
+ return gpgme_error (gpgme_err_code_from_syserror ());
+}
+
+
+/* The possible encoding mode of gpgme_data_t objects. */
+typedef enum
+ {
+ GPGME_DATA_ENCODING_NONE = 0, /* Not specified. */
+ GPGME_DATA_ENCODING_BINARY = 1,
+ GPGME_DATA_ENCODING_BASE64 = 2,
+ GPGME_DATA_ENCODING_ARMOR = 3, /* Either PEM or OpenPGP Armor. */
+ GPGME_DATA_ENCODING_URL = 4, /* LF delimited URL list. */
+ GPGME_DATA_ENCODING_URLESC = 5, /* Ditto, but percent escaped. */
+ GPGME_DATA_ENCODING_URL0 = 6 /* Nul delimited URL list. */
+ }
+gpgme_data_encoding_t;
+
+
+/* Public key algorithms from libgcrypt. */
+typedef enum
+ {
+ GPGME_PK_RSA = 1,
+ GPGME_PK_RSA_E = 2,
+ GPGME_PK_RSA_S = 3,
+ GPGME_PK_ELG_E = 16,
+ GPGME_PK_DSA = 17,
+ GPGME_PK_ELG = 20,
+ GPGME_PK_ECDSA = 301,
+ GPGME_PK_ECDH = 302
+ }
+gpgme_pubkey_algo_t;
+
+
+/* Hash algorithms from libgcrypt. */
+typedef enum
+ {
+ GPGME_MD_NONE = 0,
+ GPGME_MD_MD5 = 1,
+ GPGME_MD_SHA1 = 2,
+ GPGME_MD_RMD160 = 3,
+ GPGME_MD_MD2 = 5,
+ GPGME_MD_TIGER = 6, /* TIGER/192. */
+ GPGME_MD_HAVAL = 7, /* HAVAL, 5 pass, 160 bit. */
+ GPGME_MD_SHA256 = 8,
+ GPGME_MD_SHA384 = 9,
+ GPGME_MD_SHA512 = 10,
+ GPGME_MD_MD4 = 301,
+ GPGME_MD_CRC32 = 302,
+ GPGME_MD_CRC32_RFC1510 = 303,
+ GPGME_MD_CRC24_RFC2440 = 304
+ }
+gpgme_hash_algo_t;
+
+
+/* The possible signature stati. Deprecated, use error value in sig
+ status. */
+typedef enum
+ {
+ GPGME_SIG_STAT_NONE = 0,
+ GPGME_SIG_STAT_GOOD = 1,
+ GPGME_SIG_STAT_BAD = 2,
+ GPGME_SIG_STAT_NOKEY = 3,
+ GPGME_SIG_STAT_NOSIG = 4,
+ GPGME_SIG_STAT_ERROR = 5,
+ GPGME_SIG_STAT_DIFF = 6,
+ GPGME_SIG_STAT_GOOD_EXP = 7,
+ GPGME_SIG_STAT_GOOD_EXPKEY = 8
+ }
+_gpgme_sig_stat_t;
+typedef _gpgme_sig_stat_t gpgme_sig_stat_t _GPGME_DEPRECATED;
+
+
+/* The available signature modes. */
+typedef enum
+ {
+ GPGME_SIG_MODE_NORMAL = 0,
+ GPGME_SIG_MODE_DETACH = 1,
+ GPGME_SIG_MODE_CLEAR = 2
+ }
+gpgme_sig_mode_t;
+
+
+/* The available key and signature attributes. Deprecated, use the
+ individual result structures instead. */
+typedef enum
+ {
+ GPGME_ATTR_KEYID = 1,
+ GPGME_ATTR_FPR = 2,
+ GPGME_ATTR_ALGO = 3,
+ GPGME_ATTR_LEN = 4,
+ GPGME_ATTR_CREATED = 5,
+ GPGME_ATTR_EXPIRE = 6,
+ GPGME_ATTR_OTRUST = 7,
+ GPGME_ATTR_USERID = 8,
+ GPGME_ATTR_NAME = 9,
+ GPGME_ATTR_EMAIL = 10,
+ GPGME_ATTR_COMMENT = 11,
+ GPGME_ATTR_VALIDITY = 12,
+ GPGME_ATTR_LEVEL = 13,
+ GPGME_ATTR_TYPE = 14,
+ GPGME_ATTR_IS_SECRET = 15,
+ GPGME_ATTR_KEY_REVOKED = 16,
+ GPGME_ATTR_KEY_INVALID = 17,
+ GPGME_ATTR_UID_REVOKED = 18,
+ GPGME_ATTR_UID_INVALID = 19,
+ GPGME_ATTR_KEY_CAPS = 20,
+ GPGME_ATTR_CAN_ENCRYPT = 21,
+ GPGME_ATTR_CAN_SIGN = 22,
+ GPGME_ATTR_CAN_CERTIFY = 23,
+ GPGME_ATTR_KEY_EXPIRED = 24,
+ GPGME_ATTR_KEY_DISABLED = 25,
+ GPGME_ATTR_SERIAL = 26,
+ GPGME_ATTR_ISSUER = 27,
+ GPGME_ATTR_CHAINID = 28,
+ GPGME_ATTR_SIG_STATUS = 29,
+ GPGME_ATTR_ERRTOK = 30,
+ GPGME_ATTR_SIG_SUMMARY = 31,
+ GPGME_ATTR_SIG_CLASS = 32
+ }
+_gpgme_attr_t;
+typedef _gpgme_attr_t gpgme_attr_t _GPGME_DEPRECATED;
+
+
+/* The available validities for a trust item or key. */
+typedef enum
+ {
+ GPGME_VALIDITY_UNKNOWN = 0,
+ GPGME_VALIDITY_UNDEFINED = 1,
+ GPGME_VALIDITY_NEVER = 2,
+ GPGME_VALIDITY_MARGINAL = 3,
+ GPGME_VALIDITY_FULL = 4,
+ GPGME_VALIDITY_ULTIMATE = 5
+ }
+gpgme_validity_t;
+
+
+/* The available protocols. */
+typedef enum
+ {
+ GPGME_PROTOCOL_OpenPGP = 0, /* The default mode. */
+ GPGME_PROTOCOL_CMS = 1,
+ GPGME_PROTOCOL_GPGCONF = 2, /* Special code for gpgconf. */
+ GPGME_PROTOCOL_ASSUAN = 3, /* Low-level access to an Assuan server. */
+ GPGME_PROTOCOL_G13 = 4,
+ GPGME_PROTOCOL_UISERVER= 5,
+ GPGME_PROTOCOL_DEFAULT = 254,
+ GPGME_PROTOCOL_UNKNOWN = 255
+ }
+gpgme_protocol_t;
+
+
+/* The available keylist mode flags. */
+#define GPGME_KEYLIST_MODE_LOCAL 1
+#define GPGME_KEYLIST_MODE_EXTERN 2
+#define GPGME_KEYLIST_MODE_SIGS 4
+#define GPGME_KEYLIST_MODE_SIG_NOTATIONS 8
+#define GPGME_KEYLIST_MODE_EPHEMERAL 128
+#define GPGME_KEYLIST_MODE_VALIDATE 256
+
+typedef unsigned int gpgme_keylist_mode_t;
+
+
+/* The available export mode flags. */
+#define GPGME_EXPORT_MODE_EXTERN 2
+#define GPGME_EXPORT_MODE_MINIMAL 4
+
+typedef unsigned int gpgme_export_mode_t;
+
+
+/* Flags for the audit log functions. */
+#define GPGME_AUDITLOG_HTML 1
+#define GPGME_AUDITLOG_WITH_HELP 128
+
+
+/* Signature notations. */
+
+/* The available signature notation flags. */
+#define GPGME_SIG_NOTATION_HUMAN_READABLE 1
+#define GPGME_SIG_NOTATION_CRITICAL 2
+
+typedef unsigned int gpgme_sig_notation_flags_t;
+
+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. */
+ char *name;
+
+ /* The value of the notation data. */
+ char *value;
+
+ /* The length of the name of the notation data. */
+ int name_len;
+
+ /* The length of the value of the notation data. */
+ int value_len;
+
+ /* The accumulated flags. */
+ gpgme_sig_notation_flags_t flags;
+
+ /* Notation data is human-readable. */
+ unsigned int human_readable : 1;
+
+ /* Notation data is critical. */
+ unsigned int critical : 1;
+
+ /* Internal to GPGME, do not use. */
+ int _unused : 30;
+};
+typedef struct _gpgme_sig_notation *gpgme_sig_notation_t;
+
+
+/* The possible stati for the edit operation. */
+typedef enum
+ {
+ GPGME_STATUS_EOF = 0,
+ /* mkstatus processing starts here */
+ GPGME_STATUS_ENTER = 1,
+ GPGME_STATUS_LEAVE = 2,
+ GPGME_STATUS_ABORT = 3,
+
+ GPGME_STATUS_GOODSIG = 4,
+ GPGME_STATUS_BADSIG = 5,
+ GPGME_STATUS_ERRSIG = 6,
+
+ GPGME_STATUS_BADARMOR = 7,
+
+ GPGME_STATUS_RSA_OR_IDEA = 8,
+ GPGME_STATUS_KEYEXPIRED = 9,
+ GPGME_STATUS_KEYREVOKED = 10,
+
+ GPGME_STATUS_TRUST_UNDEFINED = 11,
+ GPGME_STATUS_TRUST_NEVER = 12,
+ GPGME_STATUS_TRUST_MARGINAL = 13,
+ GPGME_STATUS_TRUST_FULLY = 14,
+ GPGME_STATUS_TRUST_ULTIMATE = 15,
+
+ GPGME_STATUS_SHM_INFO = 16,
+ GPGME_STATUS_SHM_GET = 17,
+ GPGME_STATUS_SHM_GET_BOOL = 18,
+ GPGME_STATUS_SHM_GET_HIDDEN = 19,
+
+ GPGME_STATUS_NEED_PASSPHRASE = 20,
+ GPGME_STATUS_VALIDSIG = 21,
+ GPGME_STATUS_SIG_ID = 22,
+ GPGME_STATUS_ENC_TO = 23,
+ GPGME_STATUS_NODATA = 24,
+ GPGME_STATUS_BAD_PASSPHRASE = 25,
+ GPGME_STATUS_NO_PUBKEY = 26,
+ GPGME_STATUS_NO_SECKEY = 27,
+ GPGME_STATUS_NEED_PASSPHRASE_SYM = 28,
+ GPGME_STATUS_DECRYPTION_FAILED = 29,
+ GPGME_STATUS_DECRYPTION_OKAY = 30,
+ GPGME_STATUS_MISSING_PASSPHRASE = 31,
+ GPGME_STATUS_GOOD_PASSPHRASE = 32,
+ GPGME_STATUS_GOODMDC = 33,
+ GPGME_STATUS_BADMDC = 34,
+ GPGME_STATUS_ERRMDC = 35,
+ GPGME_STATUS_IMPORTED = 36,
+ GPGME_STATUS_IMPORT_OK = 37,
+ GPGME_STATUS_IMPORT_PROBLEM = 38,
+ GPGME_STATUS_IMPORT_RES = 39,
+ GPGME_STATUS_FILE_START = 40,
+ GPGME_STATUS_FILE_DONE = 41,
+ GPGME_STATUS_FILE_ERROR = 42,
+
+ GPGME_STATUS_BEGIN_DECRYPTION = 43,
+ GPGME_STATUS_END_DECRYPTION = 44,
+ GPGME_STATUS_BEGIN_ENCRYPTION = 45,
+ GPGME_STATUS_END_ENCRYPTION = 46,
+
+ GPGME_STATUS_DELETE_PROBLEM = 47,
+ GPGME_STATUS_GET_BOOL = 48,
+ GPGME_STATUS_GET_LINE = 49,
+ GPGME_STATUS_GET_HIDDEN = 50,
+ GPGME_STATUS_GOT_IT = 51,
+ GPGME_STATUS_PROGRESS = 52,
+ GPGME_STATUS_SIG_CREATED = 53,
+ GPGME_STATUS_SESSION_KEY = 54,
+ GPGME_STATUS_NOTATION_NAME = 55,
+ GPGME_STATUS_NOTATION_DATA = 56,
+ GPGME_STATUS_POLICY_URL = 57,
+ GPGME_STATUS_BEGIN_STREAM = 58,
+ GPGME_STATUS_END_STREAM = 59,
+ GPGME_STATUS_KEY_CREATED = 60,
+ GPGME_STATUS_USERID_HINT = 61,
+ GPGME_STATUS_UNEXPECTED = 62,
+ GPGME_STATUS_INV_RECP = 63,
+ GPGME_STATUS_NO_RECP = 64,
+ GPGME_STATUS_ALREADY_SIGNED = 65,
+ GPGME_STATUS_SIGEXPIRED = 66,
+ GPGME_STATUS_EXPSIG = 67,
+ GPGME_STATUS_EXPKEYSIG = 68,
+ GPGME_STATUS_TRUNCATED = 69,
+ GPGME_STATUS_ERROR = 70,
+ GPGME_STATUS_NEWSIG = 71,
+ GPGME_STATUS_REVKEYSIG = 72,
+ GPGME_STATUS_SIG_SUBPACKET = 73,
+ GPGME_STATUS_NEED_PASSPHRASE_PIN = 74,
+ GPGME_STATUS_SC_OP_FAILURE = 75,
+ GPGME_STATUS_SC_OP_SUCCESS = 76,
+ GPGME_STATUS_CARDCTRL = 77,
+ GPGME_STATUS_BACKUP_KEY_CREATED = 78,
+ GPGME_STATUS_PKA_TRUST_BAD = 79,
+ GPGME_STATUS_PKA_TRUST_GOOD = 80,
+ GPGME_STATUS_PLAINTEXT = 81,
+ GPGME_STATUS_INV_SGNR = 82,
+ GPGME_STATUS_NO_SGNR = 83,
+ GPGME_STATUS_SUCCESS = 84,
+ GPGME_STATUS_DECRYPTION_INFO = 85
+ }
+gpgme_status_code_t;
+
+
+/* The engine information structure. */
+struct _gpgme_engine_info
+{
+ struct _gpgme_engine_info *next;
+
+ /* The protocol ID. */
+ gpgme_protocol_t protocol;
+
+ /* The file name of the engine binary. */
+ char *file_name;
+
+ /* The version string of the installed engine. */
+ char *version;
+
+ /* The minimum version required for GPGME. */
+ const char *req_version;
+
+ /* The home directory used, or NULL if default. */
+ char *home_dir;
+};
+typedef struct _gpgme_engine_info *gpgme_engine_info_t;
+
+
+/* A subkey from a key. */
+struct _gpgme_subkey
+{
+ struct _gpgme_subkey *next;
+
+ /* True if subkey is revoked. */
+ unsigned int revoked : 1;
+
+ /* True if subkey is expired. */
+ unsigned int expired : 1;
+
+ /* True if subkey is disabled. */
+ unsigned int disabled : 1;
+
+ /* True if subkey is invalid. */
+ unsigned int invalid : 1;
+
+ /* True if subkey can be used for encryption. */
+ unsigned int can_encrypt : 1;
+
+ /* True if subkey can be used for signing. */
+ unsigned int can_sign : 1;
+
+ /* True if subkey can be used for certification. */
+ unsigned int can_certify : 1;
+
+ /* True if subkey is secret. */
+ unsigned int secret : 1;
+
+ /* True if subkey can be used for authentication. */
+ unsigned int can_authenticate : 1;
+
+ /* True if subkey is qualified for signatures according to German law. */
+ unsigned int is_qualified : 1;
+
+ /* True if the secret key is stored on a smart card. */
+ unsigned int is_cardkey : 1;
+
+ /* Internal to GPGME, do not use. */
+ unsigned int _unused : 21;
+
+ /* Public key algorithm supported by this subkey. */
+ gpgme_pubkey_algo_t pubkey_algo;
+
+ /* Length of the subkey. */
+ unsigned int length;
+
+ /* The key ID of the subkey. */
+ char *keyid;
+
+ /* Internal to GPGME, do not use. */
+ char _keyid[16 + 1];
+
+ /* The fingerprint of the subkey in hex digit form. */
+ char *fpr;
+
+ /* The creation timestamp, -1 if invalid, 0 if not available. */
+ long int timestamp;
+
+ /* The expiration timestamp, 0 if the subkey does not expire. */
+ long int expires;
+
+ /* The serial number of a smart card holding this key or NULL. */
+ char *card_number;
+};
+typedef struct _gpgme_subkey *gpgme_subkey_t;
+
+
+/* A signature on a user ID. */
+struct _gpgme_key_sig
+{
+ struct _gpgme_key_sig *next;
+
+ /* True if the signature is a revocation signature. */
+ unsigned int revoked : 1;
+
+ /* True if the signature is expired. */
+ unsigned int expired : 1;
+
+ /* True if the signature is invalid. */
+ unsigned int invalid : 1;
+
+ /* True if the signature should be exported. */
+ unsigned int exportable : 1;
+
+ /* Internal to GPGME, do not use. */
+ unsigned int _unused : 28;
+
+ /* The public key algorithm used to create the signature. */
+ gpgme_pubkey_algo_t pubkey_algo;
+
+ /* The key ID of key used to create the signature. */
+ char *keyid;
+
+ /* Internal to GPGME, do not use. */
+ char _keyid[16 + 1];
+
+ /* The creation timestamp, -1 if invalid, 0 if not available. */
+ long int timestamp;
+
+ /* The expiration timestamp, 0 if the subkey does not expire. */
+ long int expires;
+
+ /* Same as in gpgme_signature_t. */
+ gpgme_error_t status;
+
+#ifdef __cplusplus
+ unsigned int _obsolete_class _GPGME_DEPRECATED;
+#else
+ /* Must be set to SIG_CLASS below. */
+ unsigned int class _GPGME_DEPRECATED_OUTSIDE_GPGME;
+#endif
+
+ /* The user ID string. */
+ char *uid;
+
+ /* The name part of the user ID. */
+ char *name;
+
+ /* The email part of the user ID. */
+ char *email;
+
+ /* The comment part of the user ID. */
+ char *comment;
+
+ /* Crypto backend specific signature class. */
+ unsigned int sig_class;
+
+ /* Notation data and policy URLs. */
+ gpgme_sig_notation_t notations;
+
+ /* Internal to GPGME, do not use. */
+ gpgme_sig_notation_t _last_notation;
+};
+typedef struct _gpgme_key_sig *gpgme_key_sig_t;
+
+
+/* An user ID from a key. */
+struct _gpgme_user_id
+{
+ struct _gpgme_user_id *next;
+
+ /* True if the user ID is revoked. */
+ unsigned int revoked : 1;
+
+ /* True if the user ID is invalid. */
+ unsigned int invalid : 1;
+
+ /* Internal to GPGME, do not use. */
+ unsigned int _unused : 30;
+
+ /* The validity of the user ID. */
+ gpgme_validity_t validity;
+
+ /* The user ID string. */
+ char *uid;
+
+ /* The name part of the user ID. */
+ char *name;
+
+ /* The email part of the user ID. */
+ char *email;
+
+ /* The comment part of the user ID. */
+ char *comment;
+
+ /* The signatures of the user ID. */
+ gpgme_key_sig_t signatures;
+
+ /* Internal to GPGME, do not use. */
+ gpgme_key_sig_t _last_keysig;
+};
+typedef struct _gpgme_user_id *gpgme_user_id_t;
+
+
+/* A key from the keyring. */
+struct _gpgme_key
+{
+ /* Internal to GPGME, do not use. */
+ unsigned int _refs;
+
+ /* True if key is revoked. */
+ unsigned int revoked : 1;
+
+ /* True if key is expired. */
+ unsigned int expired : 1;
+
+ /* True if key is disabled. */
+ unsigned int disabled : 1;
+
+ /* True if key is invalid. */
+ unsigned int invalid : 1;
+
+ /* True if key can be used for encryption. */
+ unsigned int can_encrypt : 1;
+
+ /* True if key can be used for signing. */
+ unsigned int can_sign : 1;
+
+ /* True if key can be used for certification. */
+ unsigned int can_certify : 1;
+
+ /* True if key is secret. */
+ unsigned int secret : 1;
+
+ /* True if key can be used for authentication. */
+ unsigned int can_authenticate : 1;
+
+ /* True if subkey is qualified for signatures according to German law. */
+ unsigned int is_qualified : 1;
+
+ /* Internal to GPGME, do not use. */
+ unsigned int _unused : 22;
+
+ /* This is the protocol supported by this key. */
+ gpgme_protocol_t protocol;
+
+ /* If protocol is GPGME_PROTOCOL_CMS, this string contains the
+ issuer serial. */
+ char *issuer_serial;
+
+ /* If protocol is GPGME_PROTOCOL_CMS, this string contains the
+ issuer name. */
+ char *issuer_name;
+
+ /* If protocol is GPGME_PROTOCOL_CMS, this string contains the chain
+ ID. */
+ char *chain_id;
+
+ /* If protocol is GPGME_PROTOCOL_OpenPGP, this field contains the
+ owner trust. */
+ gpgme_validity_t owner_trust;
+
+ /* The subkeys of the key. */
+ gpgme_subkey_t subkeys;
+
+ /* The user IDs of the key. */
+ gpgme_user_id_t uids;
+
+ /* Internal to GPGME, do not use. */
+ gpgme_subkey_t _last_subkey;
+
+ /* Internal to GPGME, do not use. */
+ gpgme_user_id_t _last_uid;
+
+ /* The keylist mode that was active when listing the key. */
+ gpgme_keylist_mode_t keylist_mode;
+};
+typedef struct _gpgme_key *gpgme_key_t;
+
+
+
+/* Types for callback functions. */
+
+/* Request a passphrase from the user. */
+typedef gpgme_error_t (*gpgme_passphrase_cb_t) (void *hook,
+ const char *uid_hint,
+ const char *passphrase_info,
+ int prev_was_bad, int fd);
+
+/* Inform the user about progress made. */
+typedef void (*gpgme_progress_cb_t) (void *opaque, const char *what,
+ int type, int current, int total);
+
+/* Interact with the user about an edit operation. */
+typedef gpgme_error_t (*gpgme_edit_cb_t) (void *opaque,
+ gpgme_status_code_t status,
+ const char *args, int fd);
+
+
+
+
+/* Context management functions. */
+
+/* Create a new context and return it in CTX. */
+gpgme_error_t gpgme_new (gpgme_ctx_t *ctx);
+
+/* Release the context CTX. */
+void gpgme_release (gpgme_ctx_t ctx);
+
+/* Set the protocol to be used by CTX to PROTO. */
+gpgme_error_t gpgme_set_protocol (gpgme_ctx_t ctx, gpgme_protocol_t proto);
+
+/* Get the protocol used with CTX */
+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_error_t gpgme_set_sub_protocol (gpgme_ctx_t ctx,
+ gpgme_protocol_t proto);
+
+/* Get the sub protocol. */
+gpgme_protocol_t gpgme_get_sub_protocol (gpgme_ctx_t ctx);
+
+/* Get the string describing protocol PROTO, or NULL if invalid. */
+const char *gpgme_get_protocol_name (gpgme_protocol_t proto);
+
+/* If YES is non-zero, enable armor mode in CTX, disable it otherwise. */
+void gpgme_set_armor (gpgme_ctx_t ctx, int yes);
+
+/* Return non-zero if armor mode is set in CTX. */
+int gpgme_get_armor (gpgme_ctx_t ctx);
+
+/* If YES is non-zero, enable text mode in CTX, disable it otherwise. */
+void gpgme_set_textmode (gpgme_ctx_t ctx, int yes);
+
+/* Return non-zero if text mode is set in CTX. */
+int gpgme_get_textmode (gpgme_ctx_t ctx);
+
+/* Use whatever the default of the backend crypto engine is. */
+#define GPGME_INCLUDE_CERTS_DEFAULT -256
+
+/* Include up to NR_OF_CERTS certificates in an S/MIME message. */
+void gpgme_set_include_certs (gpgme_ctx_t ctx, int nr_of_certs);
+
+/* Return the number of certs to include in an S/MIME message. */
+int gpgme_get_include_certs (gpgme_ctx_t ctx);
+
+/* Set keylist mode in CTX to MODE. */
+gpgme_error_t gpgme_set_keylist_mode (gpgme_ctx_t ctx,
+ gpgme_keylist_mode_t mode);
+
+/* Get keylist mode in CTX. */
+gpgme_keylist_mode_t gpgme_get_keylist_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. */
+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. */
+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. */
+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. */
+void gpgme_get_progress_cb (gpgme_ctx_t ctx, gpgme_progress_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. */
+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. */
+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. */
+gpgme_error_t gpgme_ctx_set_engine_info (gpgme_ctx_t ctx,
+ gpgme_protocol_t proto,
+ const char *file_name,
+ const char *home_dir);
+
+
+/* Return a statically allocated string with the name of the public
+ 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. */
+const char *gpgme_hash_algo_name (gpgme_hash_algo_t algo);
+
+
+/* Delete all signers from CTX. */
+void gpgme_signers_clear (gpgme_ctx_t ctx);
+
+/* Add KEY to list of signers in CTX. */
+gpgme_error_t gpgme_signers_add (gpgme_ctx_t ctx, const gpgme_key_t key);
+
+/* Return the SEQth signer's key in CTX. */
+gpgme_key_t gpgme_signers_enum (const gpgme_ctx_t ctx, int seq);
+
+/* 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. */
+const char *gpgme_get_sig_status (gpgme_ctx_t ctx, int idx,
+ _gpgme_sig_stat_t *r_stat,
+ time_t *r_created) _GPGME_DEPRECATED;
+
+/* 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 . */
+unsigned long gpgme_get_sig_ulong_attr (gpgme_ctx_t c, int idx,
+ _gpgme_attr_t what, int whatidx)
+ _GPGME_DEPRECATED;
+const char *gpgme_get_sig_string_attr (gpgme_ctx_t c, int idx,
+ _gpgme_attr_t what, int whatidx)
+ _GPGME_DEPRECATED;
+
+
+/* Get the key used to create signature IDX in CTX and return it in
+ R_KEY. */
+gpgme_error_t gpgme_get_sig_key (gpgme_ctx_t ctx, int idx, gpgme_key_t *r_key)
+ _GPGME_DEPRECATED;
+
+
+/* Clear all notation data from the context. */
+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. */
+gpgme_error_t gpgme_sig_notation_add (gpgme_ctx_t ctx, const char *name,
+ const char *value,
+ gpgme_sig_notation_flags_t flags);
+
+/* Get the sig notations for this context. */
+gpgme_sig_notation_t gpgme_sig_notation_get (gpgme_ctx_t ctx);
+
+
+/* Run control. */
+
+/* The type of an I/O callback function. */
+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. */
+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. */
+typedef void (*gpgme_remove_io_cb_t) (void *tag);
+
+typedef enum
+ {
+ GPGME_EVENT_START,
+ GPGME_EVENT_DONE,
+ GPGME_EVENT_NEXT_KEY,
+ GPGME_EVENT_NEXT_TRUSTITEM
+ }
+gpgme_event_io_t;
+
+struct gpgme_io_event_done_data
+{
+ /* A fatal IPC error or an operational error in state-less
+ protocols. */
+ gpgme_error_t err;
+
+ /* An operational errors in session-based protocols. */
+ gpgme_error_t op_err;
+};
+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. */
+typedef void (*gpgme_event_io_cb_t) (void *data, gpgme_event_io_t type,
+ void *type_data);
+
+struct gpgme_io_cbs
+{
+ gpgme_register_io_cb_t add;
+ void *add_priv;
+ gpgme_remove_io_cb_t remove;
+ gpgme_event_io_cb_t event;
+ void *event_priv;
+};
+typedef struct gpgme_io_cbs *gpgme_io_cbs_t;
+
+/* Set the I/O callback functions in CTX to IO_CBS. */
+void gpgme_set_io_cbs (gpgme_ctx_t ctx, gpgme_io_cbs_t io_cbs);
+
+/* Get the current I/O callback functions. */
+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_edit_cb_t. */
+ssize_t gpgme_io_read (int fd, void *buffer, size_t count);
+ssize_t gpgme_io_write (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. */
+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,
+ gpgme_error_t *op_err, int hang);
+
+
+/* Functions to handle data objects. */
+
+/* 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. */
+typedef 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. */
+typedef 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. */
+typedef off_t (*gpgme_data_seek_cb_t) (void *handle, off_t offset, int whence);
+
+/* Close the data object with the handle DL. */
+typedef void (*gpgme_data_release_cb_t) (void *handle);
+
+struct gpgme_data_cbs
+{
+ gpgme_data_read_cb_t read;
+ gpgme_data_write_cb_t write;
+ gpgme_data_seek_cb_t seek;
+ gpgme_data_release_cb_t release;
+};
+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. */
+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. */
+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. */
+off_t gpgme_data_seek (gpgme_data_t dh, off_t offset, int whence);
+
+/* Create a new data buffer and return it in R_DH. */
+gpgme_error_t gpgme_data_new (gpgme_data_t *r_dh);
+
+/* Destroy the data buffer 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. */
+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. */
+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(). */
+void gpgme_free (void *buffer);
+
+gpgme_error_t gpgme_data_new_from_cbs (gpgme_data_t *dh,
+ gpgme_data_cbs_t cbs,
+ void *handle);
+
+gpgme_error_t gpgme_data_new_from_fd (gpgme_data_t *dh, int fd);
+
+gpgme_error_t gpgme_data_new_from_stream (gpgme_data_t *dh, FILE *stream);
+
+/* Return the encoding attribute of the data buffer DH */
+gpgme_data_encoding_t gpgme_data_get_encoding (gpgme_data_t dh);
+
+/* Set the encoding attribute of data buffer DH to ENC */
+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. */
+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. */
+gpgme_error_t gpgme_data_set_file_name (gpgme_data_t dh,
+ const char *file_name);
+
+
+/* Create a new data buffer which retrieves the data from the callback
+ 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*),
+ void *read_cb_value)
+ _GPGME_DEPRECATED;
+
+/* 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. */
+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). */
+gpgme_error_t gpgme_data_new_from_filepart (gpgme_data_t *r_dh,
+ const char *fname, FILE *fp,
+ off_t offset, size_t length);
+
+/* Reset the read pointer in DH. Deprecated, please use
+ gpgme_data_seek instead. */
+gpgme_error_t gpgme_data_rewind (gpgme_data_t dh) _GPGME_DEPRECATED;
+
+
+/* Key and trust functions. */
+
+/* Get the key with the fingerprint FPR from the crypto backend. If
+ 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);
+
+/* Acquire a reference to KEY. */
+void gpgme_key_ref (gpgme_key_t key);
+
+/* Release a reference to KEY. If this was the last one the key is
+ destroyed. */
+void gpgme_key_unref (gpgme_key_t key);
+void gpgme_key_release (gpgme_key_t key);
+
+/* 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. */
+const char *gpgme_key_get_string_attr (gpgme_key_t key, _gpgme_attr_t what,
+ const void *reserved, int idx)
+ _GPGME_DEPRECATED;
+
+/* 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. */
+unsigned long gpgme_key_get_ulong_attr (gpgme_key_t key, _gpgme_attr_t what,
+ const void *reserved, int idx)
+ _GPGME_DEPRECATED;
+
+/* 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. */
+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;
+
+/* 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. */
+unsigned long gpgme_key_sig_get_ulong_attr (gpgme_key_t key, int uid_idx,
+ _gpgme_attr_t what,
+ const void *reserved, int idx)
+ _GPGME_DEPRECATED;
+
+
+/* Crypto Operations. */
+
+/* Cancel a pending asynchronous operation. */
+gpgme_error_t gpgme_cancel (gpgme_ctx_t ctx);
+
+/* Cancel a pending operation asynchronously. */
+gpgme_error_t gpgme_cancel_async (gpgme_ctx_t ctx);
+
+
+struct _gpgme_invalid_key
+{
+ struct _gpgme_invalid_key *next;
+ char *fpr;
+ gpgme_error_t reason;
+};
+typedef struct _gpgme_invalid_key *gpgme_invalid_key_t;
+
+
+/* Encryption. */
+struct _gpgme_op_encrypt_result
+{
+ /* The list of invalid recipients. */
+ gpgme_invalid_key_t invalid_recipients;
+};
+typedef struct _gpgme_op_encrypt_result *gpgme_encrypt_result_t;
+
+/* Retrieve a pointer to the result of the encrypt operation. */
+gpgme_encrypt_result_t gpgme_op_encrypt_result (gpgme_ctx_t ctx);
+
+/* The valid encryption flags. */
+typedef enum
+ {
+ GPGME_ENCRYPT_ALWAYS_TRUST = 1,
+ GPGME_ENCRYPT_NO_ENCRYPT_TO = 2,
+ GPGME_ENCRYPT_PREPARE = 4,
+ GPGME_ENCRYPT_EXPECT_SIGN = 8
+ }
+gpgme_encrypt_flags_t;
+
+/* Encrypt plaintext PLAIN within CTX for the recipients RECP and
+ 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_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);
+
+/* 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_start (gpgme_ctx_t ctx,
+ gpgme_key_t recp[],
+ gpgme_encrypt_flags_t flags,
+ gpgme_data_t plain,
+ 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);
+
+
+/* Decryption. */
+
+struct _gpgme_recipient
+{
+ struct _gpgme_recipient *next;
+
+ /* The key ID of key for which the text was encrypted. */
+ char *keyid;
+
+ /* Internal to GPGME, do not use. */
+ char _keyid[16 + 1];
+
+ /* The public key algorithm of the recipient key. */
+ gpgme_pubkey_algo_t pubkey_algo;
+
+ /* The status of the recipient. */
+ gpgme_error_t status;
+};
+typedef struct _gpgme_recipient *gpgme_recipient_t;
+
+struct _gpgme_op_decrypt_result
+{
+ char *unsupported_algorithm;
+
+ /* Key should not have been used for encryption. */
+ unsigned int wrong_key_usage : 1;
+
+ /* Internal to GPGME, do not use. */
+ int _unused : 31;
+
+ gpgme_recipient_t recipients;
+
+ /* The original file name of the plaintext message, if
+ available. */
+ char *file_name;
+};
+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);
+
+/* Decrypt ciphertext CIPHER within CTX and store the resulting
+ 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. */
+gpgme_error_t gpgme_op_decrypt_verify_start (gpgme_ctx_t ctx,
+ gpgme_data_t cipher,
+ gpgme_data_t plain);
+gpgme_error_t gpgme_op_decrypt_verify (gpgme_ctx_t ctx, gpgme_data_t cipher,
+ gpgme_data_t plain);
+
+
+/* Signing. */
+struct _gpgme_new_signature
+{
+ struct _gpgme_new_signature *next;
+
+ /* The type of the signature. */
+ gpgme_sig_mode_t type;
+
+ /* The public key algorithm used to create the signature. */
+ gpgme_pubkey_algo_t pubkey_algo;
+
+ /* The hash algorithm used to create the signature. */
+ gpgme_hash_algo_t hash_algo;
+
+ /* Internal to GPGME, do not use. Must be set to the same value as
+ CLASS below. */
+ unsigned long _obsolete_class;
+
+ /* Signature creation time. */
+ long int timestamp;
+
+ /* The fingerprint of the signature. */
+ char *fpr;
+
+#ifdef __cplusplus
+ unsigned int _obsolete_class_2;
+#else
+ /* Must be set to SIG_CLASS below. */
+ unsigned int class _GPGME_DEPRECATED_OUTSIDE_GPGME;
+#endif
+
+ /* Crypto backend specific signature class. */
+ unsigned int sig_class;
+};
+typedef struct _gpgme_new_signature *gpgme_new_signature_t;
+
+struct _gpgme_op_sign_result
+{
+ /* The list of invalid signers. */
+ gpgme_invalid_key_t invalid_signers;
+ gpgme_new_signature_t signatures;
+};
+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);
+
+/* Sign the plaintext PLAIN and store the signature in SIG. */
+gpgme_error_t gpgme_op_sign_start (gpgme_ctx_t ctx,
+ gpgme_data_t plain, gpgme_data_t sig,
+ gpgme_sig_mode_t mode);
+gpgme_error_t gpgme_op_sign (gpgme_ctx_t ctx,
+ gpgme_data_t plain, gpgme_data_t sig,
+ gpgme_sig_mode_t mode);
+
+
+/* Verify. */
+
+/* Flags used for the SUMMARY field in a gpgme_signature_t. */
+typedef enum
+ {
+ GPGME_SIGSUM_VALID = 0x0001, /* The signature is fully valid. */
+ GPGME_SIGSUM_GREEN = 0x0002, /* The signature is good. */
+ GPGME_SIGSUM_RED = 0x0004, /* The signature is bad. */
+ GPGME_SIGSUM_KEY_REVOKED = 0x0010, /* One key has been revoked. */
+ GPGME_SIGSUM_KEY_EXPIRED = 0x0020, /* One key has expired. */
+ GPGME_SIGSUM_SIG_EXPIRED = 0x0040, /* The signature has expired. */
+ GPGME_SIGSUM_KEY_MISSING = 0x0080, /* Can't verify: key missing. */
+ GPGME_SIGSUM_CRL_MISSING = 0x0100, /* CRL not available. */
+ GPGME_SIGSUM_CRL_TOO_OLD = 0x0200, /* Available CRL is too old. */
+ GPGME_SIGSUM_BAD_POLICY = 0x0400, /* A policy was not met. */
+ GPGME_SIGSUM_SYS_ERROR = 0x0800 /* A system error occured. */
+ }
+gpgme_sigsum_t;
+
+struct _gpgme_signature
+{
+ struct _gpgme_signature *next;
+
+ /* A summary of the signature status. */
+ gpgme_sigsum_t summary;
+
+ /* The fingerprint or key ID of the signature. */
+ char *fpr;
+
+ /* The status of the signature. */
+ gpgme_error_t status;
+
+ /* Notation data and policy URLs. */
+ gpgme_sig_notation_t notations;
+
+ /* Signature creation time. */
+ unsigned long timestamp;
+
+ /* Signature exipration time or 0. */
+ unsigned long exp_timestamp;
+
+ /* Key should not have been used for signing. */
+ unsigned int wrong_key_usage : 1;
+
+ /* PKA status: 0 = not available, 1 = bad, 2 = okay, 3 = RFU. */
+ unsigned int pka_trust : 2;
+
+ /* Validity has been verified using the chain model. */
+ unsigned int chain_model : 1;
+
+ /* Internal to GPGME, do not use. */
+ int _unused : 28;
+
+ gpgme_validity_t validity;
+ gpgme_error_t validity_reason;
+
+ /* The public key algorithm used to create the signature. */
+ gpgme_pubkey_algo_t pubkey_algo;
+
+ /* The hash algorithm used to create the signature. */
+ gpgme_hash_algo_t hash_algo;
+
+ /* The mailbox from the PKA information or NULL. */
+ char *pka_address;
+};
+typedef struct _gpgme_signature *gpgme_signature_t;
+
+struct _gpgme_op_verify_result
+{
+ gpgme_signature_t signatures;
+
+ /* The original file name of the plaintext message, if
+ available. */
+ char *file_name;
+};
+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);
+
+/* Verify within CTX that SIG is a valid signature for TEXT. */
+gpgme_error_t gpgme_op_verify_start (gpgme_ctx_t ctx, gpgme_data_t sig,
+ gpgme_data_t signed_text,
+ gpgme_data_t plaintext);
+gpgme_error_t gpgme_op_verify (gpgme_ctx_t ctx, gpgme_data_t sig,
+ gpgme_data_t signed_text,
+ gpgme_data_t plaintext);
+
+
+/* Import. */
+
+/* The key was new. */
+#define GPGME_IMPORT_NEW 1
+
+/* The key contained new user IDs. */
+#define GPGME_IMPORT_UID 2
+
+/* The key contained new signatures. */
+#define GPGME_IMPORT_SIG 4
+
+/* The key contained new sub keys. */
+#define GPGME_IMPORT_SUBKEY 8
+
+/* The key contained a secret key. */
+#define GPGME_IMPORT_SECRET 16
+
+
+struct _gpgme_import_status
+{
+ struct _gpgme_import_status *next;
+
+ /* Fingerprint. */
+ char *fpr;
+
+ /* If a problem occured, the reason why the key could not be
+ imported. Otherwise GPGME_No_Error. */
+ gpgme_error_t result;
+
+ /* The result of the import, the GPGME_IMPORT_* values bit-wise
+ ORed. 0 means the key was already known and no new components
+ have been added. */
+ unsigned int status;
+};
+typedef struct _gpgme_import_status *gpgme_import_status_t;
+
+/* Import. */
+struct _gpgme_op_import_result
+{
+ /* Number of considered keys. */
+ int considered;
+
+ /* Keys without user ID. */
+ int no_user_id;
+
+ /* Imported keys. */
+ int imported;
+
+ /* Imported RSA keys. */
+ int imported_rsa;
+
+ /* Unchanged keys. */
+ int unchanged;
+
+ /* Number of new user ids. */
+ int new_user_ids;
+
+ /* Number of new sub keys. */
+ int new_sub_keys;
+
+ /* Number of new signatures. */
+ int new_signatures;
+
+ /* Number of new revocations. */
+ int new_revocations;
+
+ /* Number of secret keys read. */
+ int secret_read;
+
+ /* Number of secret keys imported. */
+ int secret_imported;
+
+ /* Number of secret keys unchanged. */
+ int secret_unchanged;
+
+ /* Number of new keys skipped. */
+ int skipped_new_keys;
+
+ /* Number of keys not imported. */
+ int not_imported;
+
+ /* List of keys for which an import was attempted. */
+ gpgme_import_status_t imports;
+};
+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);
+
+/* Import the key in KEYDATA into the keyring. */
+gpgme_error_t gpgme_op_import_start (gpgme_ctx_t ctx, gpgme_data_t keydata);
+gpgme_error_t gpgme_op_import (gpgme_ctx_t ctx, gpgme_data_t keydata);
+gpgme_error_t gpgme_op_import_ext (gpgme_ctx_t ctx, gpgme_data_t keydata,
+ int *nr) _GPGME_DEPRECATED;
+
+/* Import the keys from the array KEYS into the keyring. */
+gpgme_error_t gpgme_op_import_keys_start (gpgme_ctx_t ctx, gpgme_key_t keys[]);
+gpgme_error_t gpgme_op_import_keys (gpgme_ctx_t ctx, gpgme_key_t keys[]);
+
+
+
+/* Export the keys found by PATTERN into KEYDATA. */
+gpgme_error_t gpgme_op_export_start (gpgme_ctx_t ctx, const char *pattern,
+ gpgme_export_mode_t mode,
+ gpgme_data_t keydata);
+gpgme_error_t gpgme_op_export (gpgme_ctx_t ctx, const char *pattern,
+ gpgme_export_mode_t mode,
+ gpgme_data_t keydata);
+
+gpgme_error_t gpgme_op_export_ext_start (gpgme_ctx_t ctx,
+ const char *pattern[],
+ gpgme_export_mode_t mode,
+ gpgme_data_t keydata);
+gpgme_error_t gpgme_op_export_ext (gpgme_ctx_t ctx, const char *pattern[],
+ gpgme_export_mode_t mode,
+ gpgme_data_t keydata);
+
+/* Export the keys from the array KEYS into KEYDATA. */
+gpgme_error_t gpgme_op_export_keys_start (gpgme_ctx_t ctx,
+ gpgme_key_t keys[],
+ gpgme_export_mode_t mode,
+ gpgme_data_t keydata);
+gpgme_error_t gpgme_op_export_keys (gpgme_ctx_t ctx,
+ gpgme_key_t keys[],
+ gpgme_export_mode_t mode,
+ gpgme_data_t keydata);
+
+
+
+/* Key generation. */
+struct _gpgme_op_genkey_result
+{
+ /* A primary key was generated. */
+ unsigned int primary : 1;
+
+ /* A sub key was generated. */
+ unsigned int sub : 1;
+
+ /* Internal to GPGME, do not use. */
+ unsigned int _unused : 30;
+
+ /* The fingerprint of the generated key. */
+ char *fpr;
+};
+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. */
+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,
+ gpgme_data_t pubkey, gpgme_data_t seckey);
+
+/* Retrieve a pointer to the result of the genkey operation. */
+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. */
+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,
+ int allow_secret);
+
+
+/* Edit the key KEY. Send status and command requests to FNC and
+ output of edit commands to OUT. */
+gpgme_error_t gpgme_op_edit_start (gpgme_ctx_t ctx, gpgme_key_t key,
+ gpgme_edit_cb_t fnc, void *fnc_value,
+ gpgme_data_t out);
+gpgme_error_t gpgme_op_edit (gpgme_ctx_t ctx, gpgme_key_t key,
+ gpgme_edit_cb_t fnc, void *fnc_value,
+ gpgme_data_t out);
+
+/* Edit the card for the key KEY. Send status and command requests to
+ FNC and output of edit commands to OUT. */
+gpgme_error_t gpgme_op_card_edit_start (gpgme_ctx_t ctx, gpgme_key_t key,
+ gpgme_edit_cb_t fnc, void *fnc_value,
+ gpgme_data_t out);
+gpgme_error_t gpgme_op_card_edit (gpgme_ctx_t ctx, gpgme_key_t key,
+ gpgme_edit_cb_t fnc, void *fnc_value,
+ gpgme_data_t out);
+
+
+/* Key management functions. */
+struct _gpgme_op_keylist_result
+{
+ unsigned int truncated : 1;
+
+ /* Internal to GPGME, do not use. */
+ unsigned int _unused : 31;
+};
+typedef struct _gpgme_op_keylist_result *gpgme_keylist_result_t;
+
+/* Retrieve a pointer to the result of the key listing operation. */
+gpgme_keylist_result_t gpgme_op_keylist_result (gpgme_ctx_t ctx);
+
+/* Start a keylist operation within CTX, searching for keys which
+ match PATTERN. If SECRET_ONLY is true, only secret keys are
+ returned. */
+gpgme_error_t gpgme_op_keylist_start (gpgme_ctx_t ctx, const char *pattern,
+ int secret_only);
+gpgme_error_t gpgme_op_keylist_ext_start (gpgme_ctx_t ctx,
+ const char *pattern[],
+ int secret_only, int reserved);
+
+/* Return the next key from the keylist in R_KEY. */
+gpgme_error_t gpgme_op_keylist_next (gpgme_ctx_t ctx, gpgme_key_t *r_key);
+
+/* Terminate a pending keylist operation within CTX. */
+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. */
+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,
+ unsigned int flags);
+
+
+
+/* Trust items and operations. */
+
+struct _gpgme_trust_item
+{
+ /* Internal to GPGME, do not use. */
+ unsigned int _refs;
+
+ /* The key ID to which the trust item belongs. */
+ char *keyid;
+
+ /* Internal to GPGME, do not use. */
+ char _keyid[16 + 1];
+
+ /* The type of the trust item, 1 refers to a key, 2 to a user ID. */
+ int type;
+
+ /* The trust level. */
+ int level;
+
+ /* The owner trust if TYPE is 1. */
+ char *owner_trust;
+
+ /* Internal to GPGME, do not use. */
+ char _owner_trust[2];
+
+ /* The calculated validity. */
+ char *validity;
+
+ /* Internal to GPGME, do not use. */
+ char _validity[2];
+
+ /* The user name if TYPE is 2. */
+ char *name;
+};
+typedef struct _gpgme_trust_item *gpgme_trust_item_t;
+
+/* Start a trustlist operation within CTX, searching for trust items
+ which match PATTERN. */
+gpgme_error_t gpgme_op_trustlist_start (gpgme_ctx_t ctx,
+ const char *pattern, int max_level);
+
+/* Return the next trust item from the trustlist in R_ITEM. */
+gpgme_error_t gpgme_op_trustlist_next (gpgme_ctx_t ctx,
+ gpgme_trust_item_t *r_item);
+
+/* Terminate a pending trustlist operation within CTX. */
+gpgme_error_t gpgme_op_trustlist_end (gpgme_ctx_t ctx);
+
+/* Acquire a reference to ITEM. */
+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. */
+void gpgme_trust_item_unref (gpgme_trust_item_t item);
+
+/* Release the trust item ITEM. Deprecated, use
+ gpgme_trust_item_unref. */
+void gpgme_trust_item_release (gpgme_trust_item_t item) _GPGME_DEPRECATED;
+
+/* Return the value of the attribute WHAT of ITEM, which has to be
+ 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;
+
+/* 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. */
+int gpgme_trust_item_get_int_attr (gpgme_trust_item_t item, _gpgme_attr_t what,
+ const void *reserved, int idx)
+ _GPGME_DEPRECATED;
+
+
+/* Return the auditlog for the current session. This may be called
+ after a successful or failed operation. If no audit log is
+ available GPG_ERR_NO_DATA is returned. */
+gpgme_error_t gpgme_op_getauditlog_start (gpgme_ctx_t ctx, gpgme_data_t output,
+ unsigned int flags);
+gpgme_error_t gpgme_op_getauditlog (gpgme_ctx_t ctx, gpgme_data_t output,
+ unsigned int flags);
+
+
+
+/* Low-level Assuan protocol access. */
+typedef gpgme_error_t (*gpgme_assuan_data_cb_t)
+ (void *opaque, const void *data, size_t datalen);
+
+typedef gpgme_error_t (*gpgme_assuan_inquire_cb_t)
+ (void *opaque, const char *name, const char *args,
+ gpgme_data_t *r_data);
+
+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. */
+gpgme_error_t gpgme_op_assuan_transact_start (gpgme_ctx_t ctx,
+ const char *command,
+ gpgme_assuan_data_cb_t data_cb,
+ void *data_cb_value,
+ gpgme_assuan_inquire_cb_t inq_cb,
+ void *inq_cb_value,
+ gpgme_assuan_status_cb_t stat_cb,
+ void *stat_cb_value);
+
+/* Send the Assuan COMMAND and return results via the callbacks.
+ Synchronous variant. */
+gpgme_error_t gpgme_op_assuan_transact_ext (gpgme_ctx_t ctx,
+ const char *command,
+ gpgme_assuan_data_cb_t data_cb,
+ void *data_cb_value,
+ gpgme_assuan_inquire_cb_t inq_cb,
+ void *inq_cb_value,
+ gpgme_assuan_status_cb_t stat_cb,
+ void *stat_cb_value,
+ gpgme_error_t *op_err);
+
+/* Compat. */
+struct _gpgme_op_assuan_result
+{
+ /* Deprecated. Use the second value in a DONE event or the
+ synchronous variant gpgme_op_assuan_transact_ext. */
+ gpgme_error_t err _GPGME_DEPRECATED_OUTSIDE_GPGME;
+};
+typedef struct _gpgme_op_assuan_result *gpgme_assuan_result_t;
+
+
+/* Return the result of the last Assuan command. */
+gpgme_assuan_result_t gpgme_op_assuan_result (gpgme_ctx_t ctx)
+ _GPGME_DEPRECATED;
+
+gpgme_error_t
+gpgme_op_assuan_transact (gpgme_ctx_t ctx,
+ const char *command,
+ gpgme_assuan_data_cb_t data_cb,
+ void *data_cb_value,
+ gpgme_assuan_inquire_cb_t inq_cb,
+ void *inq_cb_value,
+ gpgme_assuan_status_cb_t status_cb,
+ void *status_cb_value) _GPGME_DEPRECATED;
+
+
+/* Crypto container support. */
+struct _gpgme_op_vfs_mount_result
+{
+ char *mount_dir;
+};
+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. */
+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);
+
+gpgme_error_t gpgme_op_vfs_create (gpgme_ctx_t ctx, gpgme_key_t recp[],
+ const char *container_file,
+ unsigned int flags, gpgme_error_t *op_err);
+
+
+/* Interface to gpgconf(1). */
+
+/* The expert level at which a configuration option or group of
+ options should be displayed. See the gpgconf(1) documentation for
+ more details. */
+typedef enum
+ {
+ GPGME_CONF_BASIC = 0,
+ GPGME_CONF_ADVANCED = 1,
+ GPGME_CONF_EXPERT = 2,
+ GPGME_CONF_INVISIBLE = 3,
+ GPGME_CONF_INTERNAL = 4
+ }
+gpgme_conf_level_t;
+
+
+/* The data type of a configuration option argument. See the gpgconf(1)
+ documentation for more details. */
+typedef enum
+ {
+ /* Basic types. */
+ GPGME_CONF_NONE = 0,
+ GPGME_CONF_STRING = 1,
+ GPGME_CONF_INT32 = 2,
+ GPGME_CONF_UINT32 = 3,
+
+ /* Complex types. */
+ GPGME_CONF_FILENAME = 32,
+ GPGME_CONF_LDAP_SERVER = 33,
+ GPGME_CONF_KEY_FPR = 34,
+ GPGME_CONF_PUB_KEY = 35,
+ GPGME_CONF_SEC_KEY = 36,
+ GPGME_CONF_ALIAS_LIST = 37
+ }
+gpgme_conf_type_t;
+
+/* For now, compatibility. */
+#define GPGME_CONF_PATHNAME GPGME_CONF_FILENAME
+
+
+/* This represents a single argument for a configuration option.
+ Which of the members of value is used depends on the ALT_TYPE. */
+typedef struct gpgme_conf_arg
+{
+ struct gpgme_conf_arg *next;
+ /* True if the option appears without an (optional) argument. */
+ unsigned int no_arg;
+ union
+ {
+ unsigned int count;
+ unsigned int uint32;
+ int int32;
+ char *string;
+ } value;
+} *gpgme_conf_arg_t;
+
+
+/* The flags of a configuration option. See the gpg-conf
+ documentation for details. */
+#define GPGME_CONF_GROUP (1 << 0)
+#define GPGME_CONF_OPTIONAL (1 << 1)
+#define GPGME_CONF_LIST (1 << 2)
+#define GPGME_CONF_RUNTIME (1 << 3)
+#define GPGME_CONF_DEFAULT (1 << 4)
+#define GPGME_CONF_DEFAULT_DESC (1 << 5)
+#define GPGME_CONF_NO_ARG_DESC (1 << 6)
+#define GPGME_CONF_NO_CHANGE (1 << 7)
+
+
+/* The representation of a single configuration option. See the
+ gpg-conf documentation for details. */
+typedef struct gpgme_conf_opt
+{
+ struct gpgme_conf_opt *next;
+
+ /* The option name. */
+ char *name;
+
+ /* The flags for this option. */
+ unsigned int flags;
+
+ /* The level of this option. */
+ gpgme_conf_level_t level;
+
+ /* The localized description of this option. */
+ char *description;
+
+ /* The type and alternate type of this option. */
+ gpgme_conf_type_t type;
+ gpgme_conf_type_t alt_type;
+
+ /* The localized (short) name of the argument, if any. */
+ char *argname;
+
+ /* The default value. */
+ gpgme_conf_arg_t default_value;
+ char *default_description;
+
+ /* The default value if the option is not set. */
+ gpgme_conf_arg_t no_arg_value;
+ char *no_arg_description;
+
+ /* The current value if the option is set. */
+ gpgme_conf_arg_t value;
+
+ /* The new value, if any. NULL means reset to default. */
+ int change_value;
+ gpgme_conf_arg_t new_value;
+
+ /* Free for application use. */
+ void *user_data;
+} *gpgme_conf_opt_t;
+
+
+/* The representation of a component that can be configured. See the
+ gpg-conf documentation for details. */
+typedef struct gpgme_conf_comp
+{
+ struct gpgme_conf_comp *next;
+
+ /* Internal to GPGME, do not use! */
+ gpgme_conf_opt_t *_last_opt_p;
+
+ /* The component name. */
+ char *name;
+
+ /* A human-readable description for the component. */
+ char *description;
+
+ /* The program name (an absolute path to the program). */
+ char *program_name;
+
+ /* A linked list of options for this component. */
+ struct gpgme_conf_opt *options;
+} *gpgme_conf_comp_t;
+
+
+/* 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. */
+gpgme_error_t gpgme_conf_arg_new (gpgme_conf_arg_t *arg_p,
+ gpgme_conf_type_t type, const void *value);
+
+/* This also releases all chained argument structures! */
+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. */
+gpgme_error_t gpgme_conf_opt_change (gpgme_conf_opt_t opt, int reset,
+ gpgme_conf_arg_t arg);
+
+/* Release a set of configurations. */
+void gpgme_conf_release (gpgme_conf_comp_t conf);
+
+/* Retrieve the current configurations. */
+gpgme_error_t gpgme_op_conf_load (gpgme_ctx_t ctx, gpgme_conf_comp_t *conf_p);
+
+/* Save the configuration of component comp. This function does not
+ follow chained components! */
+gpgme_error_t gpgme_op_conf_save (gpgme_ctx_t ctx, gpgme_conf_comp_t comp);
+
+
+/* UIServer support. */
+
+/* Create a dummy key to specify an email address. */
+gpgme_error_t gpgme_key_from_uid (gpgme_key_t *key, const char *name);
+
+
+
+/* Various functions. */
+
+/* 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. */
+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. */
+const char *gpgme_check_version_internal (const char *req_version,
+ size_t offset_sig_validity);
+
+#define gpgme_check_version(req_version) \
+ gpgme_check_version_internal (req_version, \
+ offsetof (struct _gpgme_signature, validity))
+
+/* Get the information about the configured and installed engines. A
+ pointer to the first engine in the statically allocated linked list
+ is returned in *INFO. If an error occurs, it is returned. The
+ returned data is valid until the next gpgme_set_engine_info. */
+gpgme_error_t gpgme_get_engine_info (gpgme_engine_info_t *engine_info);
+
+/* Set the default engine info for the protocol PROTO to the file name
+ FILE_NAME and the home directory HOME_DIR. */
+gpgme_error_t gpgme_set_engine_info (gpgme_protocol_t proto,
+ const char *file_name,
+ const char *home_dir);
+
+
+/* Engine support functions. */
+
+/* Verify that the engine implementing PROTO is installed and
+ available. */
+gpgme_error_t gpgme_engine_check_version (gpgme_protocol_t proto);
+
+
+void gpgme_result_ref (void *result);
+void gpgme_result_unref (void *result);
+
+
+/* Deprecated types. */
+typedef gpgme_ctx_t GpgmeCtx _GPGME_DEPRECATED;
+typedef gpgme_data_t GpgmeData _GPGME_DEPRECATED;
+typedef gpgme_error_t GpgmeError _GPGME_DEPRECATED;
+typedef gpgme_data_encoding_t GpgmeDataEncoding _GPGME_DEPRECATED;
+typedef gpgme_pubkey_algo_t GpgmePubKeyAlgo _GPGME_DEPRECATED;
+typedef gpgme_hash_algo_t GpgmeHashAlgo _GPGME_DEPRECATED;
+typedef gpgme_sig_stat_t GpgmeSigStat _GPGME_DEPRECATED;
+typedef gpgme_sig_mode_t GpgmeSigMode _GPGME_DEPRECATED;
+typedef gpgme_attr_t GpgmeAttr _GPGME_DEPRECATED;
+typedef gpgme_validity_t GpgmeValidity _GPGME_DEPRECATED;
+typedef gpgme_protocol_t GpgmeProtocol _GPGME_DEPRECATED;
+typedef gpgme_engine_info_t GpgmeEngineInfo _GPGME_DEPRECATED;
+typedef gpgme_subkey_t GpgmeSubkey _GPGME_DEPRECATED;
+typedef gpgme_key_sig_t GpgmeKeySig _GPGME_DEPRECATED;
+typedef gpgme_user_id_t GpgmeUserID _GPGME_DEPRECATED;
+typedef gpgme_key_t GpgmeKey _GPGME_DEPRECATED;
+typedef gpgme_passphrase_cb_t GpgmePassphraseCb _GPGME_DEPRECATED;
+typedef gpgme_progress_cb_t GpgmeProgressCb _GPGME_DEPRECATED;
+typedef gpgme_io_cb_t GpgmeIOCb _GPGME_DEPRECATED;
+typedef gpgme_register_io_cb_t GpgmeRegisterIOCb _GPGME_DEPRECATED;
+typedef gpgme_remove_io_cb_t GpgmeRemoveIOCb _GPGME_DEPRECATED;
+typedef gpgme_event_io_t GpgmeEventIO _GPGME_DEPRECATED;
+typedef gpgme_event_io_cb_t GpgmeEventIOCb _GPGME_DEPRECATED;
+#define GpgmeIOCbs gpgme_io_cbs
+typedef gpgme_data_read_cb_t GpgmeDataReadCb _GPGME_DEPRECATED;
+typedef gpgme_data_write_cb_t GpgmeDataWriteCb _GPGME_DEPRECATED;
+typedef gpgme_data_seek_cb_t GpgmeDataSeekCb _GPGME_DEPRECATED;
+typedef gpgme_data_release_cb_t GpgmeDataReleaseCb _GPGME_DEPRECATED;
+#define GpgmeDataCbs gpgme_data_cbs
+typedef gpgme_encrypt_result_t GpgmeEncryptResult _GPGME_DEPRECATED;
+typedef gpgme_sig_notation_t GpgmeSigNotation _GPGME_DEPRECATED;
+typedef gpgme_signature_t GpgmeSignature _GPGME_DEPRECATED;
+typedef gpgme_verify_result_t GpgmeVerifyResult _GPGME_DEPRECATED;
+typedef gpgme_import_status_t GpgmeImportStatus _GPGME_DEPRECATED;
+typedef gpgme_import_result_t GpgmeImportResult _GPGME_DEPRECATED;
+typedef gpgme_genkey_result_t GpgmeGenKeyResult _GPGME_DEPRECATED;
+typedef gpgme_trust_item_t GpgmeTrustItem _GPGME_DEPRECATED;
+typedef gpgme_status_code_t GpgmeStatusCode _GPGME_DEPRECATED;
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* GPGME_H */
+/*
+@emacs_local_vars_begin@
+@emacs_local_vars_read_only@
+@emacs_local_vars_end@
+*/
diff --git a/src/gpgme.m4 b/src/gpgme.m4
new file mode 100644
index 0000000..fe17f21
--- /dev/null
+++ b/src/gpgme.m4
@@ -0,0 +1,238 @@
+# gpgme.m4 - autoconf macro to detect GPGME.
+# Copyright (C) 2002, 2003, 2004 g10 Code GmbH
+#
+# This file is free software; as a special exception the author gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+#
+# This file is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+
+AC_DEFUN([_AM_PATH_GPGME_CONFIG],
+[ AC_ARG_WITH(gpgme-prefix,
+ AC_HELP_STRING([--with-gpgme-prefix=PFX],
+ [prefix where GPGME is installed (optional)]),
+ gpgme_config_prefix="$withval", gpgme_config_prefix="")
+ if test "x$gpgme_config_prefix" != x ; then
+ GPGME_CONFIG="$gpgme_config_prefix/bin/gpgme-config"
+ fi
+ AC_PATH_PROG(GPGME_CONFIG, gpgme-config, no)
+
+ if test "$GPGME_CONFIG" != "no" ; then
+ gpgme_version=`$GPGME_CONFIG --version`
+ fi
+ gpgme_version_major=`echo $gpgme_version | \
+ sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*/\1/'`
+ gpgme_version_minor=`echo $gpgme_version | \
+ sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*/\2/'`
+ gpgme_version_micro=`echo $gpgme_version | \
+ sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*/\3/'`
+])
+
+dnl AM_PATH_GPGME([MINIMUM-VERSION,
+dnl [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND ]]])
+dnl Test for libgpgme and define GPGME_CFLAGS and GPGME_LIBS.
+dnl
+AC_DEFUN([AM_PATH_GPGME],
+[ AC_REQUIRE([_AM_PATH_GPGME_CONFIG])dnl
+ tmp=ifelse([$1], ,1:0.4.2,$1)
+ if echo "$tmp" | grep ':' >/dev/null 2>/dev/null ; then
+ req_gpgme_api=`echo "$tmp" | sed 's/\(.*\):\(.*\)/\1/'`
+ min_gpgme_version=`echo "$tmp" | sed 's/\(.*\):\(.*\)/\2/'`
+ else
+ req_gpgme_api=0
+ min_gpgme_version="$tmp"
+ fi
+
+ AC_MSG_CHECKING(for GPGME - version >= $min_gpgme_version)
+ ok=no
+ if test "$GPGME_CONFIG" != "no" ; then
+ req_major=`echo $min_gpgme_version | \
+ sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\1/'`
+ req_minor=`echo $min_gpgme_version | \
+ sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\2/'`
+ req_micro=`echo $min_gpgme_version | \
+ sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\3/'`
+ if test "$gpgme_version_major" -gt "$req_major"; then
+ ok=yes
+ else
+ if test "$gpgme_version_major" -eq "$req_major"; then
+ if test "$gpgme_version_minor" -gt "$req_minor"; then
+ ok=yes
+ else
+ if test "$gpgme_version_minor" -eq "$req_minor"; then
+ if test "$gpgme_version_micro" -ge "$req_micro"; then
+ ok=yes
+ fi
+ fi
+ fi
+ fi
+ fi
+ fi
+ if test $ok = yes; then
+ # If we have a recent GPGME, we should also check that the
+ # API is compatible.
+ if test "$req_gpgme_api" -gt 0 ; then
+ tmp=`$GPGME_CONFIG --api-version 2>/dev/null || echo 0`
+ if test "$tmp" -gt 0 ; then
+ if test "$req_gpgme_api" -ne "$tmp" ; then
+ ok=no
+ fi
+ fi
+ fi
+ fi
+ if test $ok = yes; then
+ GPGME_CFLAGS=`$GPGME_CONFIG --cflags`
+ GPGME_LIBS=`$GPGME_CONFIG --libs`
+ AC_MSG_RESULT(yes)
+ ifelse([$2], , :, [$2])
+ else
+ GPGME_CFLAGS=""
+ GPGME_LIBS=""
+ AC_MSG_RESULT(no)
+ ifelse([$3], , :, [$3])
+ fi
+ AC_SUBST(GPGME_CFLAGS)
+ AC_SUBST(GPGME_LIBS)
+])
+
+dnl AM_PATH_GPGME_PTHREAD([MINIMUM-VERSION,
+dnl [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND ]]])
+dnl Test for libgpgme and define GPGME_PTHREAD_CFLAGS
+dnl and GPGME_PTHREAD_LIBS.
+dnl
+AC_DEFUN([AM_PATH_GPGME_PTHREAD],
+[ AC_REQUIRE([_AM_PATH_GPGME_CONFIG])dnl
+ tmp=ifelse([$1], ,1:0.4.2,$1)
+ if echo "$tmp" | grep ':' >/dev/null 2>/dev/null ; then
+ req_gpgme_api=`echo "$tmp" | sed 's/\(.*\):\(.*\)/\1/'`
+ min_gpgme_version=`echo "$tmp" | sed 's/\(.*\):\(.*\)/\2/'`
+ else
+ req_gpgme_api=0
+ min_gpgme_version="$tmp"
+ fi
+
+ AC_MSG_CHECKING(for GPGME pthread - version >= $min_gpgme_version)
+ ok=no
+ if test "$GPGME_CONFIG" != "no" ; then
+ if `$GPGME_CONFIG --thread=pthread 2> /dev/null` ; then
+ req_major=`echo $min_gpgme_version | \
+ sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\1/'`
+ req_minor=`echo $min_gpgme_version | \
+ sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\2/'`
+ req_micro=`echo $min_gpgme_version | \
+ sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\3/'`
+ if test "$gpgme_version_major" -gt "$req_major"; then
+ ok=yes
+ else
+ if test "$gpgme_version_major" -eq "$req_major"; then
+ if test "$gpgme_version_minor" -gt "$req_minor"; then
+ ok=yes
+ else
+ if test "$gpgme_version_minor" -eq "$req_minor"; then
+ if test "$gpgme_version_micro" -ge "$req_micro"; then
+ ok=yes
+ fi
+ fi
+ fi
+ fi
+ fi
+ fi
+ fi
+ if test $ok = yes; then
+ # If we have a recent GPGME, we should also check that the
+ # API is compatible.
+ if test "$req_gpgme_api" -gt 0 ; then
+ tmp=`$GPGME_CONFIG --api-version 2>/dev/null || echo 0`
+ if test "$tmp" -gt 0 ; then
+ if test "$req_gpgme_api" -ne "$tmp" ; then
+ ok=no
+ fi
+ fi
+ fi
+ fi
+ if test $ok = yes; then
+ GPGME_PTHREAD_CFLAGS=`$GPGME_CONFIG --thread=pthread --cflags`
+ GPGME_PTHREAD_LIBS=`$GPGME_CONFIG --thread=pthread --libs`
+ AC_MSG_RESULT(yes)
+ ifelse([$2], , :, [$2])
+ else
+ GPGME_PTHREAD_CFLAGS=""
+ GPGME_PTHREAD_LIBS=""
+ AC_MSG_RESULT(no)
+ ifelse([$3], , :, [$3])
+ fi
+ AC_SUBST(GPGME_PTHREAD_CFLAGS)
+ AC_SUBST(GPGME_PTHREAD_LIBS)
+])
+
+
+dnl AM_PATH_GPGME_GLIB([MINIMUM-VERSION,
+dnl [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND ]]])
+dnl Test for libgpgme-glib and define GPGME_GLIB_CFLAGS and GPGME_GLIB_LIBS.
+dnl
+AC_DEFUN([AM_PATH_GPGME_GLIB],
+[ AC_REQUIRE([_AM_PATH_GPGME_CONFIG])dnl
+ tmp=ifelse([$1], ,1:0.4.2,$1)
+ if echo "$tmp" | grep ':' >/dev/null 2>/dev/null ; then
+ req_gpgme_api=`echo "$tmp" | sed 's/\(.*\):\(.*\)/\1/'`
+ min_gpgme_version=`echo "$tmp" | sed 's/\(.*\):\(.*\)/\2/'`
+ else
+ req_gpgme_api=0
+ min_gpgme_version="$tmp"
+ fi
+
+ AC_MSG_CHECKING(for GPGME - version >= $min_gpgme_version)
+ ok=no
+ if test "$GPGME_CONFIG" != "no" ; then
+ req_major=`echo $min_gpgme_version | \
+ sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\1/'`
+ req_minor=`echo $min_gpgme_version | \
+ sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\2/'`
+ req_micro=`echo $min_gpgme_version | \
+ sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\3/'`
+ if test "$gpgme_version_major" -gt "$req_major"; then
+ ok=yes
+ else
+ if test "$gpgme_version_major" -eq "$req_major"; then
+ if test "$gpgme_version_minor" -gt "$req_minor"; then
+ ok=yes
+ else
+ if test "$gpgme_version_minor" -eq "$req_minor"; then
+ if test "$gpgme_version_micro" -ge "$req_micro"; then
+ ok=yes
+ fi
+ fi
+ fi
+ fi
+ fi
+ fi
+ if test $ok = yes; then
+ # If we have a recent GPGME, we should also check that the
+ # API is compatible.
+ if test "$req_gpgme_api" -gt 0 ; then
+ tmp=`$GPGME_CONFIG --api-version 2>/dev/null || echo 0`
+ if test "$tmp" -gt 0 ; then
+ if test "$req_gpgme_api" -ne "$tmp" ; then
+ ok=no
+ fi
+ fi
+ fi
+ fi
+ if test $ok = yes; then
+ GPGME_GLIB_CFLAGS=`$GPGME_CONFIG --glib --cflags`
+ GPGME_GLIB_LIBS=`$GPGME_CONFIG --glib --libs`
+ AC_MSG_RESULT(yes)
+ ifelse([$2], , :, [$2])
+ else
+ GPGME_GLIB_CFLAGS=""
+ GPGME_GLIB_LIBS=""
+ AC_MSG_RESULT(no)
+ ifelse([$3], , :, [$3])
+ fi
+ AC_SUBST(GPGME_GLIB_CFLAGS)
+ AC_SUBST(GPGME_GLIB_LIBS)
+])
+
diff --git a/src/import.c b/src/import.c
new file mode 100644
index 0000000..3bc7441
--- /dev/null
+++ b/src/import.c
@@ -0,0 +1,448 @@
+/* import.c - Import a key.
+ Copyright (C) 2000 Werner Koch (dd9jn)
+ Copyright (C) 2001, 2002, 2003, 2004 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+
+#include "gpgme.h"
+#include "debug.h"
+#include "context.h"
+#include "ops.h"
+#include "util.h"
+
+
+typedef struct
+{
+ struct _gpgme_op_import_result result;
+
+ /* A pointer to the next pointer of the last import status in the
+ list. This makes appending new imports painless while preserving
+ the order. */
+ gpgme_import_status_t *lastp;
+} *op_data_t;
+
+
+static void
+release_op_data (void *hook)
+{
+ op_data_t opd = (op_data_t) hook;
+ gpgme_import_status_t import = opd->result.imports;
+
+ while (import)
+ {
+ gpgme_import_status_t next = import->next;
+ free (import->fpr);
+ free (import);
+ import = next;
+ }
+}
+
+
+gpgme_import_result_t
+gpgme_op_import_result (gpgme_ctx_t ctx)
+{
+ void *hook;
+ op_data_t opd;
+ gpgme_error_t err;
+
+ TRACE_BEG (DEBUG_CTX, "gpgme_op_import_result", ctx);
+
+ err = _gpgme_op_data_lookup (ctx, OPDATA_IMPORT, &hook, -1, NULL);
+ opd = hook;
+ if (err || !opd)
+ {
+ TRACE_SUC0 ("result=(null)");
+ return NULL;
+ }
+
+
+ if (_gpgme_debug_trace ())
+ {
+ gpgme_import_status_t impstat;
+ int i;
+
+ TRACE_LOG5 ("%i considered, %i no UID, %i imported, %i imported RSA, "
+ "%i unchanged", opd->result.considered,
+ opd->result.no_user_id, opd->result.imported,
+ opd->result.imported_rsa, opd->result.unchanged);
+ TRACE_LOG4 ("%i new UIDs, %i new sub keys, %i new signatures, "
+ "%i new revocations", opd->result.new_user_ids,
+ opd->result.new_sub_keys, opd->result.new_signatures,
+ opd->result.new_revocations);
+ 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);
+
+ impstat = opd->result.imports;
+ i = 0;
+ while (impstat)
+ {
+ TRACE_LOG4 ("import[%i] for %s = 0x%x (%s)",
+ i, impstat->fpr, impstat->status, impstat->result);
+ impstat = impstat->next;
+ i++;
+ }
+ }
+
+ TRACE_SUC1 ("result=%p", &opd->result);
+ return &opd->result;
+}
+
+
+static gpgme_error_t
+parse_import (char *args, gpgme_import_status_t *import_status, int problem)
+{
+ gpgme_import_status_t import;
+ char *tail;
+ long int nr;
+
+ import = malloc (sizeof (*import));
+ if (!import)
+ return gpg_error_from_syserror ();
+ import->next = NULL;
+
+ gpg_err_set_errno (0);
+ nr = strtol (args, &tail, 0);
+ if (errno || args == tail || *tail != ' ')
+ {
+ /* The crypto backend does not behave. */
+ free (import);
+ return gpg_error (GPG_ERR_INV_ENGINE);
+ }
+ args = tail;
+
+ if (problem)
+ {
+ switch (nr)
+ {
+ case 0:
+ case 4:
+ default:
+ import->result = gpg_error (GPG_ERR_GENERAL);
+ break;
+
+ case 1:
+ import->result = gpg_error (GPG_ERR_BAD_CERT);
+ break;
+
+ case 2:
+ import->result = gpg_error (GPG_ERR_MISSING_ISSUER_CERT);
+ break;
+
+ case 3:
+ import->result = gpg_error (GPG_ERR_BAD_CERT_CHAIN);
+ break;
+ }
+ import->status = 0;
+ }
+ else
+ {
+ import->result = gpg_error (GPG_ERR_NO_ERROR);
+ import->status = nr;
+ }
+
+ while (*args == ' ')
+ args++;
+ tail = strchr (args, ' ');
+ if (tail)
+ *tail = '\0';
+
+ import->fpr = strdup (args);
+ if (!import->fpr)
+ {
+ int saved_errno = errno;
+ free (import);
+ return gpg_error_from_errno (saved_errno);
+ }
+
+ *import_status = import;
+ return 0;
+}
+
+
+
+gpgme_error_t
+parse_import_res (char *args, gpgme_import_result_t result)
+{
+ char *tail;
+
+ gpg_err_set_errno (0);
+
+#define PARSE_NEXT(x) \
+ (x) = strtol (args, &tail, 0); \
+ if (errno || args == tail || *tail != ' ') \
+ /* The crypto backend does not behave. */ \
+ return gpg_error (GPG_ERR_INV_ENGINE); \
+ args = tail;
+
+ PARSE_NEXT (result->considered);
+ PARSE_NEXT (result->no_user_id);
+ PARSE_NEXT (result->imported);
+ PARSE_NEXT (result->imported_rsa);
+ PARSE_NEXT (result->unchanged);
+ PARSE_NEXT (result->new_user_ids);
+ PARSE_NEXT (result->new_sub_keys);
+ PARSE_NEXT (result->new_signatures);
+ PARSE_NEXT (result->new_revocations);
+ PARSE_NEXT (result->secret_read);
+ PARSE_NEXT (result->secret_imported);
+ PARSE_NEXT (result->secret_unchanged);
+ PARSE_NEXT (result->skipped_new_keys);
+ PARSE_NEXT (result->not_imported);
+
+ return 0;
+}
+
+
+static gpgme_error_t
+import_status_handler (void *priv, gpgme_status_code_t code, char *args)
+{
+ gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
+ gpgme_error_t err;
+ void *hook;
+ op_data_t opd;
+
+ err = _gpgme_op_data_lookup (ctx, OPDATA_IMPORT, &hook, -1, NULL);
+ opd = hook;
+ if (err)
+ return err;
+
+ switch (code)
+ {
+ case GPGME_STATUS_IMPORT_OK:
+ case GPGME_STATUS_IMPORT_PROBLEM:
+ err = parse_import (args, opd->lastp,
+ code == GPGME_STATUS_IMPORT_OK ? 0 : 1);
+ if (err)
+ return err;
+
+ opd->lastp = &(*opd->lastp)->next;
+ break;
+
+ case GPGME_STATUS_IMPORT_RES:
+ err = parse_import_res (args, &opd->result);
+ break;
+
+ default:
+ break;
+ }
+ return 0;
+}
+
+
+static gpgme_error_t
+_gpgme_op_import_start (gpgme_ctx_t ctx, int synchronous, gpgme_data_t keydata)
+{
+ gpgme_error_t err;
+ void *hook;
+ op_data_t opd;
+
+ err = _gpgme_op_reset (ctx, synchronous);
+ if (err)
+ return err;
+
+ err = _gpgme_op_data_lookup (ctx, OPDATA_IMPORT, &hook,
+ sizeof (*opd), release_op_data);
+ opd = hook;
+ if (err)
+ return err;
+ opd->lastp = &opd->result.imports;
+
+ if (!keydata)
+ return gpg_error (GPG_ERR_NO_DATA);
+
+ _gpgme_engine_set_status_handler (ctx->engine, import_status_handler, ctx);
+
+ return _gpgme_engine_op_import (ctx->engine, keydata, NULL);
+}
+
+
+gpgme_error_t
+gpgme_op_import_start (gpgme_ctx_t ctx, gpgme_data_t keydata)
+{
+ gpg_error_t err;
+
+ TRACE_BEG1 (DEBUG_CTX, "gpgme_op_import_start", ctx,
+ "keydata=%p", keydata);
+
+ if (!ctx)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ err = _gpgme_op_import_start (ctx, 0, keydata);
+ return TRACE_ERR (err);
+}
+
+
+/* Import the key in KEYDATA into the keyring. */
+gpgme_error_t
+gpgme_op_import (gpgme_ctx_t ctx, gpgme_data_t keydata)
+{
+ gpgme_error_t err;
+
+ TRACE_BEG1 (DEBUG_CTX, "gpgme_op_import", ctx,
+ "keydata=%p", keydata);
+
+ if (!ctx)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ err = _gpgme_op_import_start (ctx, 1, keydata);
+ if (!err)
+ err = _gpgme_wait_one (ctx);
+ return TRACE_ERR (err);
+}
+
+
+
+static gpgme_error_t
+_gpgme_op_import_keys_start (gpgme_ctx_t ctx, int synchronous,
+ gpgme_key_t *keys)
+{
+ gpgme_error_t err;
+ void *hook;
+ op_data_t opd;
+ int idx, firstidx, nkeys;
+
+ err = _gpgme_op_reset (ctx, synchronous);
+ if (err)
+ return err;
+
+ err = _gpgme_op_data_lookup (ctx, OPDATA_IMPORT, &hook,
+ sizeof (*opd), release_op_data);
+ opd = hook;
+ if (err)
+ return err;
+ opd->lastp = &opd->result.imports;
+
+ if (!keys)
+ return gpg_error (GPG_ERR_NO_DATA);
+
+ for (idx=nkeys=0, firstidx=-1; keys[idx]; idx++)
+ {
+ /* We only consider keys of the current protocol. */
+ if (keys[idx]->protocol != ctx->protocol)
+ continue;
+ if (firstidx == -1)
+ firstidx = idx;
+ /* If a key has been found using a different key listing mode,
+ we bail out. This makes the processing easier. Fixme: To
+ allow a mix of keys we would need to sort them by key listing
+ mode and start two import operations one after the other. */
+ if (keys[idx]->keylist_mode != keys[firstidx]->keylist_mode)
+ return gpg_error (GPG_ERR_CONFLICT);
+ nkeys++;
+ }
+ if (!nkeys)
+ return gpg_error (GPG_ERR_NO_DATA);
+
+ _gpgme_engine_set_status_handler (ctx->engine, import_status_handler, ctx);
+
+ return _gpgme_engine_op_import (ctx->engine, NULL, keys);
+}
+
+
+/* Asynchronous version of gpgme_op_import_key. */
+gpgme_error_t
+gpgme_op_import_keys_start (gpgme_ctx_t ctx, gpgme_key_t *keys)
+{
+ gpg_error_t err;
+
+ TRACE_BEG (DEBUG_CTX, "gpgme_op_import_keys_start", ctx);
+
+ if (!ctx)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ if (_gpgme_debug_trace () && keys)
+ {
+ int i = 0;
+
+ while (keys[i])
+ {
+ TRACE_LOG3 ("keys[%i] = %p (%s)", i, keys[i],
+ (keys[i]->subkeys && keys[i]->subkeys->fpr) ?
+ keys[i]->subkeys->fpr : "invalid");
+ i++;
+ }
+ }
+
+ err = _gpgme_op_import_keys_start (ctx, 0, keys);
+ return TRACE_ERR (err);
+}
+
+
+/* Import the keys from the array KEYS into the keyring. This
+ function allows to move a key from one engine to another as long as
+ they are compatible. In particular it is used to actually import
+ keys retrieved from an external source (i.e. using
+ GPGME_KEYLIST_MODE_EXTERN). It replaces the old workaround of
+ exporting and then importing a key as used to make an X.509 key
+ permanent. This function automagically does the right thing.
+
+ KEYS is a NULL terminated array of gpgme key objects. The result
+ is the usual import result structure. Only keys matching the
+ current protocol are imported; other keys are ignored. */
+gpgme_error_t
+gpgme_op_import_keys (gpgme_ctx_t ctx, gpgme_key_t *keys)
+{
+ gpgme_error_t err;
+
+ TRACE_BEG (DEBUG_CTX, "gpgme_op_import_keys", ctx);
+
+ if (!ctx)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ if (_gpgme_debug_trace () && keys)
+ {
+ int i = 0;
+
+ while (keys[i])
+ {
+ TRACE_LOG3 ("keys[%i] = %p (%s)", i, keys[i],
+ (keys[i]->subkeys && keys[i]->subkeys->fpr) ?
+ keys[i]->subkeys->fpr : "invalid");
+ i++;
+ }
+ }
+
+ err = _gpgme_op_import_keys_start (ctx, 1, keys);
+ if (!err)
+ err = _gpgme_wait_one (ctx);
+ return TRACE_ERR (err);
+}
+
+
+/* Deprecated interface. */
+gpgme_error_t
+gpgme_op_import_ext (gpgme_ctx_t ctx, gpgme_data_t keydata, int *nr)
+{
+ gpgme_error_t err = gpgme_op_import (ctx, keydata);
+ if (!err && nr)
+ {
+ gpgme_import_result_t result = gpgme_op_import_result (ctx);
+ *nr = result->considered;
+ }
+ return err;
+}
diff --git a/src/kdpipeiodevice.cpp b/src/kdpipeiodevice.cpp
new file mode 100644
index 0000000..5661790
--- /dev/null
+++ b/src/kdpipeiodevice.cpp
@@ -0,0 +1,951 @@
+/*
+ Copyright (C) 2007 Klarälvdalens Datakonsult AB
+
+ KDPipeIODevice is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ KDPipeIODevice 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 Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with KDPipeIODevice; see the file COPYING.LIB. If not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "kdpipeiodevice.h"
+
+#include <QtCore>
+
+#include <cassert>
+#include <memory>
+#include <algorithm>
+
+#ifdef Q_OS_WIN32
+# ifndef NOMINMAX
+# define NOMINMAX
+# endif
+# include <windows.h>
+# include <io.h>
+#else
+# include <unistd.h>
+# include <errno.h>
+#endif
+
+using namespace _gpgme_;
+
+#ifndef KDAB_CHECK_THIS
+# define KDAB_CHECK_CTOR (void)1
+# define KDAB_CHECK_DTOR KDAB_CHECK_CTOR
+# define KDAB_CHECK_THIS KDAB_CHECK_CTOR
+#endif
+
+#define LOCKED( d ) const QMutexLocker locker( &d->mutex )
+#define synchronized( d ) if ( int i = 0 ) {} else for ( const QMutexLocker locker( &d->mutex ) ; !i ; ++i )
+
+const unsigned int BUFFER_SIZE = 4096;
+const bool ALLOW_QIODEVICE_BUFFERING = true;
+
+// comment to get trace output:
+//#define qDebug if(1){}else qDebug
+
+namespace {
+class Reader : public QThread {
+ Q_OBJECT
+public:
+ Reader( int fd, Qt::HANDLE handle );
+ ~Reader();
+
+ qint64 readData( char * data, qint64 maxSize );
+
+ unsigned int bytesInBuffer() const {
+ return ( wptr + sizeof buffer - rptr ) % sizeof buffer ;
+ }
+
+ bool bufferFull() const {
+ return bytesInBuffer() == sizeof buffer - 1;
+ }
+
+ bool bufferEmpty() const {
+ return bytesInBuffer() == 0;
+ }
+
+ bool bufferContains( char ch ) {
+ const unsigned int bib = bytesInBuffer();
+ for ( unsigned int i = rptr ; i < rptr + bib ; ++i )
+ if ( buffer[i%sizeof buffer] == ch )
+ return true;
+ return false;
+ }
+
+ void notifyReadyRead();
+
+Q_SIGNALS:
+ void readyRead();
+
+protected:
+ /* reimp */ void run();
+
+private:
+ int fd;
+ Qt::HANDLE handle;
+public:
+ QMutex mutex;
+ QWaitCondition waitForCancelCondition;
+ QWaitCondition bufferNotFullCondition;
+ QWaitCondition bufferNotEmptyCondition;
+ QWaitCondition hasStarted;
+ QWaitCondition readyReadSentCondition;
+ QWaitCondition blockedConsumerIsDoneCondition;
+ bool cancel;
+ bool eof;
+ bool error;
+ bool eofShortCut;
+ int errorCode;
+ bool isReading;
+ bool consumerBlocksOnUs;
+
+private:
+ unsigned int rptr, wptr;
+ char buffer[BUFFER_SIZE+1]; // need to keep one byte free to detect empty state
+};
+
+
+Reader::Reader( int fd_, Qt::HANDLE handle_ )
+ : QThread(),
+ fd( fd_ ),
+ handle( handle_ ),
+ mutex(),
+ bufferNotFullCondition(),
+ bufferNotEmptyCondition(),
+ hasStarted(),
+ cancel( false ),
+ eof( false ),
+ error( false ),
+ eofShortCut( false ),
+ errorCode( 0 ),
+ isReading( false ),
+ consumerBlocksOnUs( false ),
+ rptr( 0 ), wptr( 0 )
+{
+
+}
+
+Reader::~Reader() {}
+
+
+class Writer : public QThread {
+ Q_OBJECT
+public:
+ Writer( int fd, Qt::HANDLE handle );
+ ~Writer();
+
+ qint64 writeData( const char * data, qint64 size );
+
+ unsigned int bytesInBuffer() const { return numBytesInBuffer; }
+
+ bool bufferFull() const {
+ return numBytesInBuffer == sizeof buffer;
+ }
+
+ bool bufferEmpty() const {
+ return numBytesInBuffer == 0;
+ }
+
+Q_SIGNALS:
+ void bytesWritten( qint64 );
+
+protected:
+ /* reimp */ void run();
+
+private:
+ int fd;
+ Qt::HANDLE handle;
+public:
+ QMutex mutex;
+ QWaitCondition bufferEmptyCondition;
+ QWaitCondition bufferNotEmptyCondition;
+ QWaitCondition hasStarted;
+ bool cancel;
+ bool error;
+ int errorCode;
+private:
+ unsigned int numBytesInBuffer;
+ char buffer[BUFFER_SIZE];
+};
+}
+
+Writer::Writer( int fd_, Qt::HANDLE handle_ )
+ : QThread(),
+ fd( fd_ ),
+ handle( handle_ ),
+ mutex(),
+ bufferEmptyCondition(),
+ bufferNotEmptyCondition(),
+ hasStarted(),
+ cancel( false ),
+ error( false ),
+ errorCode( 0 ),
+ numBytesInBuffer( 0 )
+{
+
+}
+
+Writer::~Writer() {}
+
+
+class KDPipeIODevice::Private : public QObject {
+Q_OBJECT
+ friend class ::KDPipeIODevice;
+ KDPipeIODevice * const q;
+public:
+ explicit Private( KDPipeIODevice * qq );
+ ~Private();
+
+ bool doOpen( int, Qt::HANDLE, OpenMode );
+ bool startReaderThread();
+ bool startWriterThread();
+ void stopThreads();
+
+public Q_SLOTS:
+ void emitReadyRead();
+
+private:
+ int fd;
+ Qt::HANDLE handle;
+ Reader * reader;
+ Writer * writer;
+ bool triedToStartReader;
+ bool triedToStartWriter;
+};
+
+KDPipeIODevice::Private::Private( KDPipeIODevice * qq )
+ : QObject( qq ), q( qq ),
+ fd( -1 ),
+ handle( 0 ),
+ reader( 0 ),
+ writer( 0 ),
+ triedToStartReader( false ), triedToStartWriter( false )
+{
+
+}
+
+KDPipeIODevice::Private::~Private() {
+ qDebug( "KDPipeIODevice::~Private(): Destroying %p", q );
+}
+
+KDPipeIODevice::KDPipeIODevice( QObject * p )
+ : QIODevice( p ), d( new Private( this ) )
+{
+ KDAB_CHECK_CTOR;
+}
+
+KDPipeIODevice::KDPipeIODevice( int fd, OpenMode mode, QObject * p )
+ : QIODevice( p ), d( new Private( this ) )
+{
+ KDAB_CHECK_CTOR;
+ open( fd, mode );
+}
+
+KDPipeIODevice::KDPipeIODevice( Qt::HANDLE handle, OpenMode mode, QObject * p )
+ : QIODevice( p ), d( new Private( this ) )
+{
+ KDAB_CHECK_CTOR;
+ open( handle, mode );
+}
+
+KDPipeIODevice::~KDPipeIODevice() { KDAB_CHECK_DTOR;
+ if ( isOpen() )
+ close();
+ delete d; d = 0;
+}
+
+
+bool KDPipeIODevice::open( int fd, OpenMode mode ) { KDAB_CHECK_THIS;
+
+#ifdef Q_OS_WIN32
+ return d->doOpen( fd, (HANDLE)_get_osfhandle( fd ), mode );
+#else
+ return d->doOpen( fd, 0, mode );
+#endif
+
+}
+
+bool KDPipeIODevice::open( Qt::HANDLE h, OpenMode mode ) { KDAB_CHECK_THIS;
+
+#ifdef Q_OS_WIN32
+ return d->doOpen( -1, h, mode );
+#else
+ Q_UNUSED( h );
+ Q_UNUSED( mode );
+ assert( !"KDPipeIODevice::open( Qt::HANDLE, OpenMode ) should never be called except on Windows." );
+#endif
+
+}
+
+bool KDPipeIODevice::Private::startReaderThread()
+{
+ if ( triedToStartReader )
+ return true;
+ triedToStartReader = true;
+ if ( reader && !reader->isRunning() && !reader->isFinished() ) {
+ qDebug("KDPipeIODevice::Private::startReaderThread(): locking reader (CONSUMER THREAD)" );
+ LOCKED( reader );
+ qDebug("KDPipeIODevice::Private::startReaderThread(): locked reader (CONSUMER THREAD)" );
+ reader->start( QThread::HighestPriority );
+ qDebug("KDPipeIODevice::Private::startReaderThread(): waiting for hasStarted (CONSUMER THREAD)" );
+ const bool hasStarted = reader->hasStarted.wait( &reader->mutex, 1000 );
+ qDebug("KDPipeIODevice::Private::startReaderThread(): returned from hasStarted (CONSUMER THREAD)" );
+
+ return hasStarted;
+ }
+ return true;
+}
+
+bool KDPipeIODevice::Private::startWriterThread()
+{
+ if ( triedToStartWriter )
+ return true;
+ triedToStartWriter = true;
+ if ( writer && !writer->isRunning() && !writer->isFinished() ) {
+ LOCKED( writer );
+
+ writer->start( QThread::HighestPriority );
+ if ( !writer->hasStarted.wait( &writer->mutex, 1000 ) )
+ return false;
+ }
+ return true;
+}
+
+void KDPipeIODevice::Private::emitReadyRead()
+{
+ QPointer<Private> thisPointer( this );
+ qDebug( "KDPipeIODevice::Private::emitReadyRead %p", this );
+
+ emit q->readyRead();
+
+ if ( !thisPointer )
+ return;
+
+ bool mustNotify = false;
+
+ if ( reader ) {
+ qDebug( "KDPipeIODevice::Private::emitReadyRead %p: locking reader (CONSUMER THREAD)", this );
+ synchronized( reader ) {
+ qDebug( "KDPipeIODevice::Private::emitReadyRead %p: locked reader (CONSUMER THREAD)", this );
+ reader->readyReadSentCondition.wakeAll();
+ mustNotify = !reader->bufferEmpty() && reader->isReading;
+ qDebug( "KDPipeIODevice::emitReadyRead %p: bufferEmpty: %d reader in ReadFile: %d", this, reader->bufferEmpty(), reader->isReading );
+ }
+ }
+ if ( mustNotify )
+ QTimer::singleShot( 100, this, SLOT( emitReadyRead() ) );
+ qDebug( "KDPipeIODevice::Private::emitReadyRead %p leaving", this );
+
+}
+
+bool KDPipeIODevice::Private::doOpen( int fd_, Qt::HANDLE handle_, OpenMode mode_ ) {
+
+ if ( q->isOpen() )
+ return false;
+
+#ifdef Q_OS_WIN32
+ if ( !handle_ )
+ return false;
+#else
+ if ( fd_ < 0 )
+ return false;
+#endif
+
+ if ( !(mode_ & ReadWrite) )
+ return false; // need to have at least read -or- write
+
+
+ std::auto_ptr<Reader> reader_;
+ std::auto_ptr<Writer> writer_;
+
+ if ( mode_ & ReadOnly ) {
+ reader_.reset( new Reader( fd_, handle_ ) );
+ qDebug( "KDPipeIODevice::doOpen (%p): created reader (%p) for fd %d", this, reader_.get(), fd_ );
+ connect( reader_.get(), SIGNAL(readyRead()), this, SLOT(emitReadyRead()),
+Qt::QueuedConnection );
+ }
+ if ( mode_ & WriteOnly ) {
+ writer_.reset( new Writer( fd_, handle_ ) );
+ qDebug( "KDPipeIODevice::doOpen (%p): created writer (%p) for fd %d", this, writer_.get(), fd_ );
+ connect( writer_.get(), SIGNAL(bytesWritten(qint64)), q, SIGNAL(bytesWritten(qint64)),
+Qt::QueuedConnection );
+ }
+
+ // commit to *this:
+ fd = fd_;
+ handle = handle_;
+ reader = reader_.release();
+ writer = writer_.release();
+
+ q->setOpenMode( mode_|Unbuffered );
+ return true;
+}
+
+int KDPipeIODevice::descriptor() const { KDAB_CHECK_THIS;
+ return d->fd;
+}
+
+
+Qt::HANDLE KDPipeIODevice::handle() const { KDAB_CHECK_THIS;
+ return d->handle;
+}
+
+qint64 KDPipeIODevice::bytesAvailable() const { KDAB_CHECK_THIS;
+ const qint64 base = QIODevice::bytesAvailable();
+ if ( !d->triedToStartReader ) {
+ d->startReaderThread();
+ return base;
+ }
+ if ( d->reader )
+ synchronized( d->reader ) {
+ const qint64 inBuffer = d->reader->bytesInBuffer();
+ return base + inBuffer;
+ }
+ return base;
+}
+
+qint64 KDPipeIODevice::bytesToWrite() const { KDAB_CHECK_THIS;
+ d->startWriterThread();
+ const qint64 base = QIODevice::bytesToWrite();
+ if ( d->writer )
+ synchronized( d->writer ) return base + d->writer->bytesInBuffer();
+ return base;
+}
+
+bool KDPipeIODevice::canReadLine() const { KDAB_CHECK_THIS;
+ d->startReaderThread();
+ if ( QIODevice::canReadLine() )
+ return true;
+ if ( d->reader )
+ synchronized( d->reader ) return d->reader->bufferContains( '\n' );
+ return true;
+}
+
+bool KDPipeIODevice::isSequential() const {
+ return true;
+}
+
+bool KDPipeIODevice::atEnd() const { KDAB_CHECK_THIS;
+ d->startReaderThread();
+ if ( !QIODevice::atEnd() ) {
+ qDebug( "%p: KDPipeIODevice::atEnd returns false since QIODevice::atEnd does (with bytesAvailable=%ld)", this, static_cast<long>(bytesAvailable()) );
+ return false;
+ }
+ if ( !isOpen() )
+ return true;
+ if ( d->reader->eofShortCut )
+ return true;
+ LOCKED( d->reader );
+ const bool eof = ( d->reader->error || d->reader->eof ) && d->reader->bufferEmpty();
+ if ( !eof ) {
+ if ( !d->reader->error && !d->reader->eof )
+ qDebug( "%p: KDPipeIODevice::atEnd returns false since !reader->error && !reader->eof", this );
+ if ( !d->reader->bufferEmpty() )
+ qDebug( "%p: KDPipeIODevice::atEnd returns false since !reader->bufferEmpty()", this );
+ }
+ return eof;
+}
+
+bool KDPipeIODevice::waitForBytesWritten( int msecs ) { KDAB_CHECK_THIS;
+ d->startWriterThread();
+ Writer * const w = d->writer;
+ if ( !w )
+ return true;
+ LOCKED( w );
+ qDebug( "KDPipeIODevice::waitForBytesWritten (%p,w=%p): entered locked area", this, w
+);
+ return w->bufferEmpty() || w->error || w->bufferEmptyCondition.wait( &w->mutex, msecs ) ;
+}
+
+bool KDPipeIODevice::waitForReadyRead( int msecs ) { KDAB_CHECK_THIS;
+ qDebug( "KDPipeIODEvice::waitForReadyRead()(%p)", this);
+ d->startReaderThread();
+ if ( ALLOW_QIODEVICE_BUFFERING ) {
+ if ( bytesAvailable() > 0 )
+ return true;
+ }
+ Reader * const r = d->reader;
+ if ( !r || r->eofShortCut )
+ return true;
+ LOCKED( r );
+ if ( r->bytesInBuffer() != 0 || r->eof || r->error )
+ return true;
+
+ return msecs >= 0 ? r->bufferNotEmptyCondition.wait( &r->mutex, msecs ) : r->bufferNotEmptyCondition.wait( &r->mutex );
+}
+
+template <typename T>
+class TemporaryValue {
+public:
+ TemporaryValue( T& var_, const T& tv ) : var( var_ ), oldValue( var_ ) { var = tv; }
+ ~TemporaryValue() { var = oldValue; }
+private:
+ T& var;
+ const T oldValue;
+};
+
+
+bool KDPipeIODevice::readWouldBlock() const
+{
+ d->startReaderThread();
+ LOCKED( d->reader );
+ return d->reader->bufferEmpty() && !d->reader->eof && !d->reader->error;
+}
+
+bool KDPipeIODevice::writeWouldBlock() const
+{
+ d->startWriterThread();
+ LOCKED( d->writer );
+ return !d->writer->bufferEmpty() && !d->writer->error;
+}
+
+
+qint64 KDPipeIODevice::readData( char * data, qint64 maxSize ) { KDAB_CHECK_THIS;
+ qDebug( "%p: KDPipeIODevice::readData: data=%p, maxSize=%lld", this, data, maxSize );
+ d->startReaderThread();
+ Reader * const r = d->reader;
+
+ assert( r );
+
+
+ //assert( r->isRunning() ); // wrong (might be eof, error)
+ assert( data || maxSize == 0 );
+ assert( maxSize >= 0 );
+
+ if ( r->eofShortCut ) {
+ qDebug( "%p: KDPipeIODevice::readData: hit eofShortCut, returning 0", this );
+ return 0;
+ }
+
+ if ( maxSize < 0 )
+ maxSize = 0;
+
+ if ( ALLOW_QIODEVICE_BUFFERING ) {
+ if ( bytesAvailable() > 0 )
+ maxSize = std::min( maxSize, bytesAvailable() ); // don't block
+ }
+ qDebug( "%p: KDPipeIODevice::readData: try to lock reader (CONSUMER THREAD)", this );
+ LOCKED( r );
+ qDebug( "%p: KDPipeIODevice::readData: locked reader (CONSUMER THREAD)", this );
+
+ r->readyReadSentCondition.wakeAll();
+ if ( /* maxSize > 0 && */ r->bufferEmpty() && !r->error && !r->eof ) { // ### block on maxSize == 0?
+ qDebug( "%p: KDPipeIODevice::readData: waiting for bufferNotEmptyCondition (CONSUMER THREAD)", this );
+ const TemporaryValue<bool> tmp( d->reader->consumerBlocksOnUs, true );
+ r->bufferNotEmptyCondition.wait( &r->mutex );
+ r->blockedConsumerIsDoneCondition.wakeAll();
+ qDebug( "%p: KDPipeIODevice::readData: woke up from bufferNotEmptyCondition (CONSUMER THREAD)", this );
+ }
+
+ if ( r->bufferEmpty() ) {
+ qDebug( "%p: KDPipeIODevice::readData: got empty buffer, signal eof", this );
+ // woken with an empty buffer must mean either EOF or error:
+ assert( r->eof || r->error );
+ r->eofShortCut = true;
+ return r->eof ? 0 : -1 ;
+ }
+
+ qDebug( "%p: KDPipeIODevice::readData: got bufferNotEmptyCondition, trying to read %lld bytes", this, maxSize );
+ const qint64 bytesRead = r->readData( data, maxSize );
+ qDebug( "%p: KDPipeIODevice::readData: read %lld bytes", this, bytesRead );
+ qDebug( "%p (fd=%d): KDPipeIODevice::readData: %s", this, d->fd, data );
+
+ return bytesRead;
+}
+
+qint64 Reader::readData( char * data, qint64 maxSize ) {
+ qint64 numRead = rptr < wptr ? wptr - rptr : sizeof buffer - rptr ;
+ if ( numRead > maxSize )
+ numRead = maxSize;
+
+ qDebug( "%p: KDPipeIODevice::readData: data=%p, maxSize=%lld; rptr=%u, wptr=%u (bytesInBuffer=%u); -> numRead=%lld", this,
+ data, maxSize, rptr, wptr, bytesInBuffer(), numRead );
+
+ std::memcpy( data, buffer + rptr, numRead );
+
+ rptr = ( rptr + numRead ) % sizeof buffer ;
+
+ if ( !bufferFull() ) {
+ qDebug( "%p: KDPipeIODevice::readData: signal bufferNotFullCondition", this );
+ bufferNotFullCondition.wakeAll();
+ }
+
+ return numRead;
+}
+
+qint64 KDPipeIODevice::writeData( const char * data, qint64 size ) { KDAB_CHECK_THIS;
+ d->startWriterThread();
+ Writer * const w = d->writer;
+
+ assert( w );
+ assert( w->error || w->isRunning() );
+ assert( data || size == 0 );
+ assert( size >= 0 );
+
+ LOCKED( w );
+
+ while ( !w->error && !w->bufferEmpty() ) {
+ qDebug( "%p: KDPipeIODevice::writeData: wait for empty buffer", this );
+ w->bufferEmptyCondition.wait( &w->mutex );
+ qDebug( "%p: KDPipeIODevice::writeData: empty buffer signaled", this );
+
+ }
+ if ( w->error )
+ return -1;
+
+ assert( w->bufferEmpty() );
+
+ return w->writeData( data, size );
+}
+
+qint64 Writer::writeData( const char * data, qint64 size ) {
+ assert( bufferEmpty() );
+
+ if ( size > static_cast<qint64>( sizeof buffer ) )
+ size = sizeof buffer;
+
+ std::memcpy( buffer, data, size );
+
+ numBytesInBuffer = size;
+
+ if ( !bufferEmpty() ) {
+ bufferNotEmptyCondition.wakeAll();
+ }
+ return size;
+}
+
+void KDPipeIODevice::Private::stopThreads()
+{
+ if ( triedToStartWriter )
+ {
+ if ( writer && q->bytesToWrite() > 0 )
+ q->waitForBytesWritten( -1 );
+
+ assert( q->bytesToWrite() == 0 );
+ }
+ if ( Reader * & r = reader ) {
+ disconnect( r, SIGNAL( readyRead() ), this, SLOT( emitReadyRead() ) );
+ synchronized( r ) {
+ // tell thread to cancel:
+ r->cancel = true;
+ // and wake it, so it can terminate:
+ r->waitForCancelCondition.wakeAll();
+ r->bufferNotFullCondition.wakeAll();
+ r->readyReadSentCondition.wakeAll();
+ }
+ }
+ if ( Writer * & w = writer ) {
+ synchronized( w ) {
+ // tell thread to cancel:
+ w->cancel = true;
+ // and wake it, so it can terminate:
+ w->bufferNotEmptyCondition.wakeAll();
+ }
+ }
+}
+
+void KDPipeIODevice::close() { KDAB_CHECK_THIS;
+ qDebug( "KDPipeIODevice::close(%p)", this );
+ if ( !isOpen() )
+ return;
+
+ // tell clients we're about to close:
+ emit aboutToClose();
+ d->stopThreads();
+
+#define waitAndDelete( t ) if ( t ) { t->wait(); QThread* const t2 = t; t = 0; delete t2; }
+ qDebug( "KPipeIODevice::close(%p): wait and closing writer %p", this, d->writer );
+ waitAndDelete( d->writer );
+ qDebug( "KPipeIODevice::close(%p): wait and closing reader %p", this, d->reader );
+ if ( d->reader ) {
+ LOCKED( d->reader );
+ d->reader->readyReadSentCondition.wakeAll();
+ }
+ waitAndDelete( d->reader );
+#undef waitAndDelete
+#ifdef Q_OS_WIN32
+ if ( d->fd != -1 )
+ _close( d->fd );
+ else
+ CloseHandle( d->handle );
+#else
+ ::close( d->fd );
+#endif
+
+ setOpenMode( NotOpen );
+ d->fd = -1;
+ d->handle = 0;
+}
+
+void Reader::run() {
+
+ LOCKED( this );
+
+ // too bad QThread doesn't have that itself; a signal isn't enough
+ hasStarted.wakeAll();
+
+ qDebug( "%p: Reader::run: started", this );
+
+ while ( true ) {
+ if ( !cancel && ( eof || error ) ) {
+ //notify the client until the buffer is empty and then once
+ //again so he receives eof/error. After that, wait for him
+ //to cancel
+ const bool wasEmpty = bufferEmpty();
+ qDebug( "%p: Reader::run: received eof(%d) or error(%d), waking everyone", this, eof, error );
+ notifyReadyRead();
+ if ( !cancel && wasEmpty )
+ waitForCancelCondition.wait( &mutex );
+ } else if ( !cancel && !bufferFull() && !bufferEmpty() ) {
+ qDebug( "%p: Reader::run: buffer no longer empty, waking everyone", this );
+ notifyReadyRead();
+ }
+
+ while ( !cancel && !error && bufferFull() ) {
+ notifyReadyRead();
+ if ( !cancel && bufferFull() ) {
+ qDebug( "%p: Reader::run: buffer is full, going to sleep", this );
+ bufferNotFullCondition.wait( &mutex );
+ }
+ }
+
+ if ( cancel ) {
+ qDebug( "%p: Reader::run: detected cancel", this );
+ goto leave;
+ }
+
+ if ( !eof && !error ) {
+ if ( rptr == wptr ) // optimize for larger chunks in case the buffer is empty
+ rptr = wptr = 0;
+
+ unsigned int numBytes = ( rptr + sizeof buffer - wptr - 1 ) % sizeof buffer;
+ if ( numBytes > sizeof buffer - wptr )
+ numBytes = sizeof buffer - wptr;
+
+ qDebug( "%p: Reader::run: rptr=%d, wptr=%d -> numBytes=%d", this, rptr, wptr, numBytes );
+
+ assert( numBytes > 0 );
+
+ qDebug( "%p: Reader::run: trying to read %d bytes", this, numBytes );
+#ifdef Q_OS_WIN32
+ isReading = true;
+ mutex.unlock();
+ DWORD numRead;
+ const bool ok = ReadFile( handle, buffer + wptr, numBytes, &numRead, 0 );
+ mutex.lock();
+ isReading = false;
+ if ( ok ) {
+ if ( numRead == 0 ) {
+ qDebug( "%p: Reader::run: got eof (numRead==0)", this );
+ eof = true;
+ }
+ } else { // !ok
+ errorCode = static_cast<int>( GetLastError() );
+ if ( errorCode == ERROR_BROKEN_PIPE ) {
+ assert( numRead == 0 );
+ qDebug( "%p: Reader::run: got eof (broken pipe)", this );
+ eof = true;
+ } else {
+ assert( numRead == 0 );
+ qDebug( "%p: Reader::run: got error: %s (%d)", this, strerror( errorCode ), errorCode );
+ error = true;
+ }
+ }
+#else
+ qint64 numRead;
+ mutex.unlock();
+ do {
+ numRead = ::read( fd, buffer + wptr, numBytes );
+ } while ( numRead == -1 && errno == EINTR );
+ mutex.lock();
+
+ if ( numRead < 0 ) {
+ errorCode = errno;
+ error = true;
+ qDebug( "%p: Reader::run: got error: %d", this, errorCode );
+ } else if ( numRead == 0 ) {
+ qDebug( "%p: Reader::run: eof detected", this );
+ eof = true;
+ }
+#endif
+ qDebug( "%p: Reader::run: read %ld bytes", this, static_cast<long>(numRead) );
+ qDebug( "%p: Reader::run(fd=%d): %s", this, fd, buffer );
+
+ if ( numRead > 0 ) {
+ qDebug( "%p: Reader::run: buffer before: rptr=%4d, wptr=%4d", this, rptr, wptr );
+ wptr = ( wptr + numRead ) % sizeof buffer;
+ qDebug( "%p: Reader::run: buffer after: rptr=%4d, wptr=%4d", this, rptr, wptr );
+ }
+ }
+ }
+ leave:
+ qDebug( "%p: Reader::run: terminated", this );
+}
+
+void Reader::notifyReadyRead()
+{
+ qDebug( "notifyReadyRead: %d bytes available", bytesInBuffer() );
+ assert( !cancel );
+
+ if ( consumerBlocksOnUs ) {
+ bufferNotEmptyCondition.wakeAll();
+ blockedConsumerIsDoneCondition.wait( &mutex );
+ return;
+ }
+ qDebug( "notifyReadyRead: emit signal" );
+ emit readyRead();
+ readyReadSentCondition.wait( &mutex );
+ qDebug( "notifyReadyRead: returning from waiting, leave" );
+}
+
+void Writer::run() {
+
+ LOCKED( this );
+
+ // too bad QThread doesn't have that itself; a signal isn't enough
+ hasStarted.wakeAll();
+
+ qDebug( "%p: Writer::run: started", this );
+
+ while ( true ) {
+
+ while ( !cancel && bufferEmpty() ) {
+ qDebug( "%p: Writer::run: buffer is empty, wake bufferEmptyCond listeners", this );
+ bufferEmptyCondition.wakeAll();
+ emit bytesWritten( 0 );
+ qDebug( "%p: Writer::run: buffer is empty, going to sleep", this );
+ bufferNotEmptyCondition.wait( &mutex );
+ qDebug( "%p: Writer::run: woke up", this );
+ }
+
+ if ( cancel ) {
+ qDebug( "%p: Writer::run: detected cancel", this );
+ goto leave;
+ }
+
+ assert( numBytesInBuffer > 0 );
+
+ qDebug( "%p: Writer::run: Trying to write %u bytes", this, numBytesInBuffer );
+ qint64 totalWritten = 0;
+ do {
+ mutex.unlock();
+#ifdef Q_OS_WIN32
+ DWORD numWritten;
+ qDebug( "%p (fd=%d): Writer::run: buffer before WriteFile (numBytes=%lld): %s:", this, fd, numBytesInBuffer, buffer );
+ qDebug( "%p (fd=%d): Writer::run: Going into WriteFile", this, fd );
+ if ( !WriteFile( handle, buffer + totalWritten, numBytesInBuffer - totalWritten, &numWritten, 0 ) ) {
+ mutex.lock();
+ errorCode = static_cast<int>( GetLastError() );
+ qDebug( "%p: Writer::run: got error code: %d", this, errorCode );
+ error = true;
+ goto leave;
+ }
+#else
+ qint64 numWritten;
+ do {
+ numWritten = ::write( fd, buffer + totalWritten, numBytesInBuffer - totalWritten );
+ } while ( numWritten == -1 && errno == EINTR );
+
+ if ( numWritten < 0 ) {
+ mutex.lock();
+ errorCode = errno;
+ qDebug( "%p: Writer::run: got error code: %d", this, errorCode );
+ error = true;
+ goto leave;
+ }
+#endif
+ qDebug( "%p (fd=%d): Writer::run: buffer after WriteFile (numBytes=%u): %s:", this, fd, numBytesInBuffer, buffer );
+ totalWritten += numWritten;
+ mutex.lock();
+ } while ( totalWritten < numBytesInBuffer );
+
+ qDebug( "%p: Writer::run: wrote %lld bytes", this, totalWritten );
+
+ numBytesInBuffer = 0;
+
+ qDebug( "%p: Writer::run: buffer is empty, wake bufferEmptyCond listeners", this );
+ bufferEmptyCondition.wakeAll();
+ emit bytesWritten( totalWritten );
+ }
+ leave:
+ qDebug( "%p: Writer::run: terminating", this );
+ numBytesInBuffer = 0;
+ qDebug( "%p: Writer::run: buffer is empty, wake bufferEmptyCond listeners", this );
+ bufferEmptyCondition.wakeAll();
+ emit bytesWritten( 0 );
+}
+
+// static
+std::pair<KDPipeIODevice*,KDPipeIODevice*> KDPipeIODevice::makePairOfConnectedPipes() {
+ KDPipeIODevice * read = 0;
+ KDPipeIODevice * write = 0;
+#ifdef Q_OS_WIN32
+ HANDLE rh;
+ HANDLE wh;
+ SECURITY_ATTRIBUTES sa;
+ memset( &sa, 0, sizeof(sa) );
+ sa.nLength = sizeof(sa);
+ sa.bInheritHandle = TRUE;
+ if ( CreatePipe( &rh, &wh, &sa, BUFFER_SIZE ) ) {
+ read = new KDPipeIODevice;
+ read->open( rh, ReadOnly );
+ write = new KDPipeIODevice;
+ write->open( wh, WriteOnly );
+ }
+#else
+ int fds[2];
+ if ( pipe( fds ) == 0 ) {
+ read = new KDPipeIODevice;
+ read->open( fds[0], ReadOnly );
+ write = new KDPipeIODevice;
+ write->open( fds[1], WriteOnly );
+ }
+#endif
+ return std::make_pair( read, write );
+}
+
+#ifdef KDAB_DEFINE_CHECKS
+KDAB_DEFINE_CHECKS( KDPipeIODevice ) {
+ if ( !isOpen() ) {
+ assert( openMode() == NotOpen );
+ assert( !d->reader );
+ assert( !d->writer );
+#ifdef Q_OS_WIN32
+ assert( !d->handle );
+#else
+ assert( d->fd < 0 );
+#endif
+ } else {
+ assert( openMode() != NotOpen );
+ assert( openMode() & ReadWrite );
+ if ( openMode() & ReadOnly ) {
+ assert( d->reader );
+ synchronized( d->reader )
+ assert( d->reader->eof || d->reader->error || d->reader->isRunning() );
+ }
+ if ( openMode() & WriteOnly ) {
+ assert( d->writer );
+ synchronized( d->writer )
+ assert( d->writer->error || d->writer->isRunning() );
+ }
+#ifdef Q_OS_WIN32
+ assert( d->handle );
+#else
+ assert( d->fd >= 0 );
+#endif
+ }
+}
+#endif // KDAB_DEFINE_CHECKS
+
+#include "moc_kdpipeiodevice.cpp"
+#include "kdpipeiodevice.moc"
diff --git a/src/kdpipeiodevice.h b/src/kdpipeiodevice.h
new file mode 100644
index 0000000..8da6af6
--- /dev/null
+++ b/src/kdpipeiodevice.h
@@ -0,0 +1,73 @@
+/*
+ Copyright (C) 2007 Klarälvdalens Datakonsult AB
+
+ KDPipeIODevice is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ KDPipeIODevice 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 Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with KDPipeIODevice; see the file COPYING.LIB. If not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __KDTOOLSCORE_KDPIPEIODEVICE_H__
+#define __KDTOOLSCORE_KDPIPEIODEVICE_H__
+
+#include <QIODevice>
+
+#include <utility>
+
+//#include "checker.h"
+
+namespace _gpgme_ {
+
+class KDPipeIODevice : public QIODevice {
+ Q_OBJECT
+ //KDAB_MAKE_CHECKABLE( KDPipeIODevice )
+public:
+ explicit KDPipeIODevice( QObject * parent=0 );
+ explicit KDPipeIODevice( int fd, OpenMode=ReadOnly, QObject * parent=0 );
+ explicit KDPipeIODevice( Qt::HANDLE handle, OpenMode=ReadOnly, QObject * parent=0 );
+ ~KDPipeIODevice();
+
+ static std::pair<KDPipeIODevice*, KDPipeIODevice*> makePairOfConnectedPipes();
+
+ bool open( int fd, OpenMode mode=ReadOnly );
+ bool open( Qt::HANDLE handle, OpenMode mode=ReadOnly );
+
+ Qt::HANDLE handle() const;
+ int descriptor() const;
+
+ bool readWouldBlock() const;
+ bool writeWouldBlock() const;
+
+ /* reimp */ qint64 bytesAvailable() const;
+ /* reimp */ qint64 bytesToWrite() const;
+ /* reimp */ bool canReadLine() const;
+ /* reimp */ void close();
+ /* reimp */ bool isSequential() const;
+ /* reimp */ bool atEnd() const;
+
+ /* reimp */ bool waitForBytesWritten( int msecs );
+ /* reimp */ bool waitForReadyRead( int msecs );
+
+protected:
+ /* reimp */ qint64 readData( char * data, qint64 maxSize );
+ /* reimp */ qint64 writeData( const char * data, qint64 maxSize );
+
+private:
+ class Private;
+ Private * d;
+};
+
+} /* namespace _gpgme_ */
+
+#endif /* __KDTOOLSCORE_KDPIPEIODEVICE_H__ */
+
diff --git a/src/kdpipeiodevice.moc b/src/kdpipeiodevice.moc
new file mode 100644
index 0000000..457f371
--- /dev/null
+++ b/src/kdpipeiodevice.moc
@@ -0,0 +1,183 @@
+/****************************************************************************
+** Meta object code from reading C++ file 'kdpipeiodevice.cpp'
+**
+** Created: Tue Oct 2 19:30:13 2007
+** by: The Qt Meta Object Compiler version 59 (Qt 4.3.1)
+**
+** WARNING! All changes made in this file will be lost!
+*****************************************************************************/
+
+#if !defined(Q_MOC_OUTPUT_REVISION)
+#error "The header file 'kdpipeiodevice.cpp' doesn't include <QObject>."
+#elif Q_MOC_OUTPUT_REVISION != 59
+#error "This file was generated using the moc from 4.3.1. It"
+#error "cannot be used with the include files from this version of Qt."
+#error "(The moc has changed too much.)"
+#endif
+
+static const uint qt_meta_data_Reader[] = {
+
+ // content:
+ 1, // revision
+ 0, // classname
+ 0, 0, // classinfo
+ 1, 10, // methods
+ 0, 0, // properties
+ 0, 0, // enums/sets
+
+ // signals: signature, parameters, type, tag, flags
+ 8, 7, 7, 7, 0x05,
+
+ 0 // eod
+};
+
+static const char qt_meta_stringdata_Reader[] = {
+ "Reader\0\0readyRead()\0"
+};
+
+const QMetaObject Reader::staticMetaObject = {
+ { &QThread::staticMetaObject, qt_meta_stringdata_Reader,
+ qt_meta_data_Reader, 0 }
+};
+
+const QMetaObject *Reader::metaObject() const
+{
+ return &staticMetaObject;
+}
+
+void *Reader::qt_metacast(const char *_clname)
+{
+ if (!_clname) return 0;
+ if (!strcmp(_clname, qt_meta_stringdata_Reader))
+ return static_cast<void*>(const_cast< Reader*>(this));
+ return QThread::qt_metacast(_clname);
+}
+
+int Reader::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
+{
+ _id = QThread::qt_metacall(_c, _id, _a);
+ if (_id < 0)
+ return _id;
+ if (_c == QMetaObject::InvokeMetaMethod) {
+ switch (_id) {
+ case 0: readyRead(); break;
+ }
+ _id -= 1;
+ }
+ return _id;
+}
+
+// SIGNAL 0
+void Reader::readyRead()
+{
+ QMetaObject::activate(this, &staticMetaObject, 0, 0);
+}
+static const uint qt_meta_data_Writer[] = {
+
+ // content:
+ 1, // revision
+ 0, // classname
+ 0, 0, // classinfo
+ 1, 10, // methods
+ 0, 0, // properties
+ 0, 0, // enums/sets
+
+ // signals: signature, parameters, type, tag, flags
+ 8, 7, 7, 7, 0x05,
+
+ 0 // eod
+};
+
+static const char qt_meta_stringdata_Writer[] = {
+ "Writer\0\0bytesWritten(qint64)\0"
+};
+
+const QMetaObject Writer::staticMetaObject = {
+ { &QThread::staticMetaObject, qt_meta_stringdata_Writer,
+ qt_meta_data_Writer, 0 }
+};
+
+const QMetaObject *Writer::metaObject() const
+{
+ return &staticMetaObject;
+}
+
+void *Writer::qt_metacast(const char *_clname)
+{
+ if (!_clname) return 0;
+ if (!strcmp(_clname, qt_meta_stringdata_Writer))
+ return static_cast<void*>(const_cast< Writer*>(this));
+ return QThread::qt_metacast(_clname);
+}
+
+int Writer::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
+{
+ _id = QThread::qt_metacall(_c, _id, _a);
+ if (_id < 0)
+ return _id;
+ if (_c == QMetaObject::InvokeMetaMethod) {
+ switch (_id) {
+ case 0: bytesWritten((*reinterpret_cast< qint64(*)>(_a[1]))); break;
+ }
+ _id -= 1;
+ }
+ return _id;
+}
+
+// SIGNAL 0
+void Writer::bytesWritten(qint64 _t1)
+{
+ void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
+ QMetaObject::activate(this, &staticMetaObject, 0, _a);
+}
+static const uint qt_meta_data_KDPipeIODevice__Private[] = {
+
+ // content:
+ 1, // revision
+ 0, // classname
+ 0, 0, // classinfo
+ 1, 10, // methods
+ 0, 0, // properties
+ 0, 0, // enums/sets
+
+ // slots: signature, parameters, type, tag, flags
+ 25, 24, 24, 24, 0x0a,
+
+ 0 // eod
+};
+
+static const char qt_meta_stringdata_KDPipeIODevice__Private[] = {
+ "KDPipeIODevice::Private\0\0emitReadyRead()\0"
+};
+
+const QMetaObject KDPipeIODevice::Private::staticMetaObject = {
+ { &QObject::staticMetaObject, qt_meta_stringdata_KDPipeIODevice__Private,
+ qt_meta_data_KDPipeIODevice__Private, 0 }
+};
+
+const QMetaObject *KDPipeIODevice::Private::metaObject() const
+{
+ return &staticMetaObject;
+}
+
+void *KDPipeIODevice::Private::qt_metacast(const char *_clname)
+{
+ if (!_clname) return 0;
+ if (!strcmp(_clname, qt_meta_stringdata_KDPipeIODevice__Private))
+ return static_cast<void*>(const_cast< Private*>(this));
+ return QObject::qt_metacast(_clname);
+}
+
+int KDPipeIODevice::Private::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
+{
+ _id = QObject::qt_metacall(_c, _id, _a);
+ if (_id < 0)
+ return _id;
+ if (_c == QMetaObject::InvokeMetaMethod) {
+ switch (_id) {
+ case 0: emitReadyRead(); break;
+ }
+ _id -= 1;
+ }
+ return _id;
+}
diff --git a/src/key.c b/src/key.c
new file mode 100644
index 0000000..37db81d
--- /dev/null
+++ b/src/key.c
@@ -0,0 +1,755 @@
+/* key.c - Key objects.
+ Copyright (C) 2000 Werner Koch (dd9jn)
+ Copyright (C) 2001, 2002, 2003, 2004 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+
+#include "util.h"
+#include "ops.h"
+#include "sema.h"
+
+
+/* Protects all reference counters in keys. All other accesses to a
+ key are read only. */
+DEFINE_STATIC_LOCK (key_ref_lock);
+
+
+/* Create a new key. */
+gpgme_error_t
+_gpgme_key_new (gpgme_key_t *r_key)
+{
+ gpgme_key_t key;
+
+ key = calloc (1, sizeof *key);
+ if (!key)
+ return gpg_error_from_errno (errno);
+ key->_refs = 1;
+
+ *r_key = key;
+ return 0;
+}
+
+
+gpgme_error_t
+_gpgme_key_add_subkey (gpgme_key_t key, gpgme_subkey_t *r_subkey)
+{
+ gpgme_subkey_t subkey;
+
+ subkey = calloc (1, sizeof *subkey);
+ if (!subkey)
+ return gpg_error_from_errno (errno);
+ subkey->keyid = subkey->_keyid;
+ subkey->_keyid[16] = '\0';
+
+ if (!key->subkeys)
+ key->subkeys = subkey;
+ if (key->_last_subkey)
+ key->_last_subkey->next = subkey;
+ key->_last_subkey = subkey;
+
+ *r_subkey = subkey;
+ return 0;
+}
+
+
+static char *
+set_user_id_part (char *tail, const char *buf, size_t len)
+{
+ while (len && (buf[len - 1] == ' ' || buf[len - 1] == '\t'))
+ len--;
+ for (; len; len--)
+ *tail++ = *buf++;
+ *tail++ = 0;
+ return tail;
+}
+
+
+static void
+parse_user_id (char *src, char **name, char **email,
+ char **comment, char *tail)
+{
+ const char *start = NULL;
+ int in_name = 0;
+ int in_email = 0;
+ int in_comment = 0;
+
+ while (*src)
+ {
+ if (in_email)
+ {
+ if (*src == '<')
+ /* Not legal but anyway. */
+ in_email++;
+ else if (*src == '>')
+ {
+ if (!--in_email && !*email)
+ {
+ *email = tail;
+ tail = set_user_id_part (tail, start, src - start);
+ }
+ }
+ }
+ else if (in_comment)
+ {
+ if (*src == '(')
+ in_comment++;
+ else if (*src == ')')
+ {
+ if (!--in_comment && !*comment)
+ {
+ *comment = tail;
+ tail = set_user_id_part (tail, start, src - start);
+ }
+ }
+ }
+ else if (*src == '<')
+ {
+ if (in_name)
+ {
+ if (!*name)
+ {
+ *name = tail;
+ tail = set_user_id_part (tail, start, src - start);
+ }
+ in_name = 0;
+ }
+ in_email = 1;
+ start = src + 1;
+ }
+ else if (*src == '(')
+ {
+ if (in_name)
+ {
+ if (!*name)
+ {
+ *name = tail;
+ tail = set_user_id_part (tail, start, src - start);
+ }
+ in_name = 0;
+ }
+ in_comment = 1;
+ start = src + 1;
+ }
+ else if (!in_name && *src != ' ' && *src != '\t')
+ {
+ in_name = 1;
+ start = src;
+ }
+ src++;
+ }
+
+ if (in_name)
+ {
+ if (!*name)
+ {
+ *name = tail;
+ tail = set_user_id_part (tail, start, src - start);
+ }
+ }
+
+ /* Let unused parts point to an EOS. */
+ tail--;
+ if (!*name)
+ *name = tail;
+ if (!*email)
+ *email = tail;
+ if (!*comment)
+ *comment = tail;
+}
+
+
+static void
+parse_x509_user_id (char *src, char **name, char **email,
+ char **comment, char *tail)
+{
+ if (*src == '<' && src[strlen (src) - 1] == '>')
+ *email = src;
+
+ /* Let unused parts point to an EOS. */
+ tail--;
+ if (!*name)
+ *name = tail;
+ if (!*email)
+ *email = tail;
+ if (!*comment)
+ *comment = tail;
+}
+
+
+/* Take a name from the --with-colon listing, remove certain escape
+ sequences sequences and put it into the list of UIDs. */
+gpgme_error_t
+_gpgme_key_append_name (gpgme_key_t key, const char *src, int convert)
+{
+ gpgme_user_id_t uid;
+ char *dst;
+ int src_len = strlen (src);
+
+ assert (key);
+ /* We can malloc a buffer of the same length, because the converted
+ string will never be larger. Actually we allocate it twice the
+ size, so that we are able to store the parsed stuff there too. */
+ uid = malloc (sizeof (*uid) + 2 * src_len + 3);
+ if (!uid)
+ return gpg_error_from_errno (errno);
+ memset (uid, 0, sizeof *uid);
+
+ uid->uid = ((char *) uid) + sizeof (*uid);
+ dst = uid->uid;
+ if (convert)
+ _gpgme_decode_c_string (src, &dst, src_len + 1);
+ else
+ memcpy (dst, src, src_len + 1);
+
+ dst += strlen (dst) + 1;
+ if (key->protocol == GPGME_PROTOCOL_CMS)
+ parse_x509_user_id (uid->uid, &uid->name, &uid->email,
+ &uid->comment, dst);
+ else
+ parse_user_id (uid->uid, &uid->name, &uid->email,
+ &uid->comment, dst);
+
+ if (!key->uids)
+ key->uids = uid;
+ if (key->_last_uid)
+ key->_last_uid->next = uid;
+ key->_last_uid = uid;
+
+ return 0;
+}
+
+
+gpgme_key_sig_t
+_gpgme_key_add_sig (gpgme_key_t key, char *src)
+{
+ int src_len = src ? strlen (src) : 0;
+ gpgme_user_id_t uid;
+ gpgme_key_sig_t sig;
+
+ assert (key); /* XXX */
+
+ uid = key->_last_uid;
+ assert (uid); /* XXX */
+
+ /* We can malloc a buffer of the same length, because the converted
+ string will never be larger. Actually we allocate it twice the
+ size, so that we are able to store the parsed stuff there too. */
+ sig = malloc (sizeof (*sig) + 2 * src_len + 3);
+ if (!sig)
+ return NULL;
+ memset (sig, 0, sizeof *sig);
+
+ sig->keyid = sig->_keyid;
+ sig->_keyid[16] = '\0';
+ sig->uid = ((char *) sig) + sizeof (*sig);
+
+ if (src)
+ {
+ char *dst = sig->uid;
+ _gpgme_decode_c_string (src, &dst, src_len + 1);
+ dst += strlen (dst) + 1;
+ if (key->protocol == GPGME_PROTOCOL_CMS)
+ parse_x509_user_id (sig->uid, &sig->name, &sig->email,
+ &sig->comment, dst);
+ else
+ parse_user_id (sig->uid, &sig->name, &sig->email,
+ &sig->comment, dst);
+ }
+ else
+ sig->uid = '\0';
+
+ if (!uid->signatures)
+ uid->signatures = sig;
+ if (uid->_last_keysig)
+ uid->_last_keysig->next = sig;
+ uid->_last_keysig = sig;
+
+ return sig;
+}
+
+
+/* Acquire a reference to KEY. */
+void
+gpgme_key_ref (gpgme_key_t key)
+{
+ LOCK (key_ref_lock);
+ key->_refs++;
+ UNLOCK (key_ref_lock);
+}
+
+
+/* gpgme_key_unref releases the key object. Note, that this function
+ may not do an actual release if there are other shallow copies of
+ the objects. You have to call this function for every newly
+ created key object as well as for every gpgme_key_ref() done on the
+ key object. */
+void
+gpgme_key_unref (gpgme_key_t key)
+{
+ gpgme_user_id_t uid;
+ gpgme_subkey_t subkey;
+
+ if (!key)
+ return;
+
+ LOCK (key_ref_lock);
+ assert (key->_refs > 0);
+ if (--key->_refs)
+ {
+ UNLOCK (key_ref_lock);
+ return;
+ }
+ UNLOCK (key_ref_lock);
+
+ subkey = key->subkeys;
+ while (subkey)
+ {
+ gpgme_subkey_t next = subkey->next;
+ if (subkey->fpr)
+ free (subkey->fpr);
+ if (subkey->card_number)
+ free (subkey->card_number);
+ free (subkey);
+ subkey = next;
+ }
+
+ uid = key->uids;
+ while (uid)
+ {
+ gpgme_user_id_t next_uid = uid->next;
+ gpgme_key_sig_t keysig = uid->signatures;
+
+ while (keysig)
+ {
+ gpgme_key_sig_t next_keysig = keysig->next;
+ gpgme_sig_notation_t notation = keysig->notations;
+
+ while (notation)
+ {
+ gpgme_sig_notation_t next_notation = notation->next;
+
+ _gpgme_sig_notation_free (notation);
+ notation = next_notation;
+ }
+
+ free (keysig);
+ keysig = next_keysig;
+ }
+ free (uid);
+ uid = next_uid;
+ }
+
+ if (key->issuer_serial)
+ free (key->issuer_serial);
+ if (key->issuer_name)
+ free (key->issuer_name);
+
+ if (key->chain_id)
+ free (key->chain_id);
+
+ free (key);
+}
+
+
+/* Support functions. */
+
+/* Create a dummy key to specify an email address. */
+gpgme_error_t
+gpgme_key_from_uid (gpgme_key_t *r_key, const char *name)
+{
+ gpgme_error_t err;
+ gpgme_key_t key;
+
+ *r_key = NULL;
+ err = _gpgme_key_new (&key);
+ if (err)
+ return err;
+
+ /* Note: protocol doesn't matter if only email is provided. */
+ err = _gpgme_key_append_name (key, name, 0);
+ if (err)
+ gpgme_key_unref (key);
+ else
+ *r_key = key;
+
+ return err;
+}
+
+
+
+/* Compatibility interfaces. */
+
+void
+gpgme_key_release (gpgme_key_t key)
+{
+ gpgme_key_unref (key);
+}
+
+
+static const char *
+otrust_to_string (int otrust)
+{
+ switch (otrust)
+ {
+ case GPGME_VALIDITY_NEVER:
+ return "n";
+
+ case GPGME_VALIDITY_MARGINAL:
+ return "m";
+
+ case GPGME_VALIDITY_FULL:
+ return "f";
+
+ case GPGME_VALIDITY_ULTIMATE:
+ return "u";
+
+ default:
+ return "?";
+ }
+}
+
+
+static const char *
+validity_to_string (int validity)
+{
+ switch (validity)
+ {
+ case GPGME_VALIDITY_UNDEFINED:
+ return "q";
+
+ case GPGME_VALIDITY_NEVER:
+ return "n";
+
+ case GPGME_VALIDITY_MARGINAL:
+ return "m";
+
+ case GPGME_VALIDITY_FULL:
+ return "f";
+
+ case GPGME_VALIDITY_ULTIMATE:
+ return "u";
+
+ case GPGME_VALIDITY_UNKNOWN:
+ default:
+ return "?";
+ }
+}
+
+
+static const char *
+capabilities_to_string (gpgme_subkey_t subkey)
+{
+ static const char *const strings[8] =
+ {
+ "",
+ "c",
+ "s",
+ "sc",
+ "e",
+ "ec",
+ "es",
+ "esc"
+ };
+ return strings[(!!subkey->can_encrypt << 2)
+ | (!!subkey->can_sign << 1)
+ | (!!subkey->can_certify)];
+}
+
+
+/* Return the value of the attribute WHAT of ITEM, which has to be
+ representable by a string. */
+const char *
+gpgme_key_get_string_attr (gpgme_key_t key, _gpgme_attr_t what,
+ const void *reserved, int idx)
+{
+ gpgme_subkey_t subkey;
+ gpgme_user_id_t uid;
+ int i;
+
+ if (!key || reserved || idx < 0)
+ return NULL;
+
+ /* Select IDXth subkey. */
+ subkey = key->subkeys;
+ for (i = 0; i < idx; i++)
+ {
+ subkey = subkey->next;
+ if (!subkey)
+ break;
+ }
+
+ /* Select the IDXth user ID. */
+ uid = key->uids;
+ for (i = 0; i < idx; i++)
+ {
+ uid = uid->next;
+ if (!uid)
+ break;
+ }
+
+ switch (what)
+ {
+ case GPGME_ATTR_KEYID:
+ return subkey ? subkey->keyid : NULL;
+
+ case GPGME_ATTR_FPR:
+ return subkey ? subkey->fpr : NULL;
+
+ case GPGME_ATTR_ALGO:
+ return subkey ? gpgme_pubkey_algo_name (subkey->pubkey_algo) : NULL;
+
+ case GPGME_ATTR_TYPE:
+ return key->protocol == GPGME_PROTOCOL_CMS ? "X.509" : "PGP";
+
+ case GPGME_ATTR_OTRUST:
+ return otrust_to_string (key->owner_trust);
+
+ case GPGME_ATTR_USERID:
+ return uid ? uid->uid : NULL;
+
+ case GPGME_ATTR_NAME:
+ return uid ? uid->name : NULL;
+
+ case GPGME_ATTR_EMAIL:
+ return uid ? uid->email : NULL;
+
+ case GPGME_ATTR_COMMENT:
+ return uid ? uid->comment : NULL;
+
+ case GPGME_ATTR_VALIDITY:
+ return uid ? validity_to_string (uid->validity) : NULL;
+
+ case GPGME_ATTR_KEY_CAPS:
+ return subkey ? capabilities_to_string (subkey) : NULL;
+
+ case GPGME_ATTR_SERIAL:
+ return key->issuer_serial;
+
+ case GPGME_ATTR_ISSUER:
+ return idx ? NULL : key->issuer_name;
+
+ case GPGME_ATTR_CHAINID:
+ return key->chain_id;
+
+ default:
+ return NULL;
+ }
+}
+
+
+unsigned long
+gpgme_key_get_ulong_attr (gpgme_key_t key, _gpgme_attr_t what,
+ const void *reserved, int idx)
+{
+ gpgme_subkey_t subkey;
+ gpgme_user_id_t uid;
+ int i;
+
+ if (!key || reserved || idx < 0)
+ return 0;
+
+ /* Select IDXth subkey. */
+ subkey = key->subkeys;
+ for (i = 0; i < idx; i++)
+ {
+ subkey = subkey->next;
+ if (!subkey)
+ break;
+ }
+
+ /* Select the IDXth user ID. */
+ uid = key->uids;
+ for (i = 0; i < idx; i++)
+ {
+ uid = uid->next;
+ if (!uid)
+ break;
+ }
+
+ switch (what)
+ {
+ case GPGME_ATTR_ALGO:
+ return subkey ? (unsigned long) subkey->pubkey_algo : 0;
+
+ case GPGME_ATTR_LEN:
+ return subkey ? (unsigned long) subkey->length : 0;
+
+ case GPGME_ATTR_TYPE:
+ return key->protocol == GPGME_PROTOCOL_CMS ? 1 : 0;
+
+ case GPGME_ATTR_CREATED:
+ return (subkey && subkey->timestamp >= 0)
+ ? (unsigned long) subkey->timestamp : 0;
+
+ case GPGME_ATTR_EXPIRE:
+ return (subkey && subkey->expires >= 0)
+ ? (unsigned long) subkey->expires : 0;
+
+ case GPGME_ATTR_VALIDITY:
+ return uid ? uid->validity : 0;
+
+ case GPGME_ATTR_OTRUST:
+ return key->owner_trust;
+
+ case GPGME_ATTR_IS_SECRET:
+ return !!key->secret;
+
+ case GPGME_ATTR_KEY_REVOKED:
+ return subkey ? subkey->revoked : 0;
+
+ case GPGME_ATTR_KEY_INVALID:
+ return subkey ? subkey->invalid : 0;
+
+ case GPGME_ATTR_KEY_EXPIRED:
+ return subkey ? subkey->expired : 0;
+
+ case GPGME_ATTR_KEY_DISABLED:
+ return subkey ? subkey->disabled : 0;
+
+ case GPGME_ATTR_UID_REVOKED:
+ return uid ? uid->revoked : 0;
+
+ case GPGME_ATTR_UID_INVALID:
+ return uid ? uid->invalid : 0;
+
+ case GPGME_ATTR_CAN_ENCRYPT:
+ return key->can_encrypt;
+
+ case GPGME_ATTR_CAN_SIGN:
+ return key->can_sign;
+
+ case GPGME_ATTR_CAN_CERTIFY:
+ return key->can_certify;
+
+ default:
+ return 0;
+ }
+}
+
+
+static gpgme_key_sig_t
+get_keysig (gpgme_key_t key, int uid_idx, int idx)
+{
+ gpgme_user_id_t uid;
+ gpgme_key_sig_t sig;
+
+ if (!key || uid_idx < 0 || idx < 0)
+ return NULL;
+
+ uid = key->uids;
+ while (uid && uid_idx > 0)
+ {
+ uid = uid->next;
+ uid_idx--;
+ }
+ if (!uid)
+ return NULL;
+
+ sig = uid->signatures;
+ while (sig && idx > 0)
+ {
+ sig = sig->next;
+ idx--;
+ }
+ return sig;
+}
+
+
+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_key_sig_t certsig = get_keysig (key, uid_idx, idx);
+
+ if (!certsig || reserved)
+ return NULL;
+
+ switch (what)
+ {
+ case GPGME_ATTR_KEYID:
+ return certsig->keyid;
+
+ case GPGME_ATTR_ALGO:
+ return gpgme_pubkey_algo_name (certsig->pubkey_algo);
+
+ case GPGME_ATTR_USERID:
+ return certsig->uid;
+
+ case GPGME_ATTR_NAME:
+ return certsig->name;
+
+ case GPGME_ATTR_EMAIL:
+ return certsig->email;
+
+ case GPGME_ATTR_COMMENT:
+ return certsig->comment;
+
+ default:
+ return NULL;
+ }
+}
+
+
+unsigned long
+gpgme_key_sig_get_ulong_attr (gpgme_key_t key, int uid_idx, _gpgme_attr_t what,
+ const void *reserved, int idx)
+{
+ gpgme_key_sig_t certsig = get_keysig (key, uid_idx, idx);
+
+ if (!certsig || reserved)
+ return 0;
+
+ switch (what)
+ {
+ case GPGME_ATTR_ALGO:
+ return (unsigned long) certsig->pubkey_algo;
+
+ case GPGME_ATTR_CREATED:
+ return certsig->timestamp < 0 ? 0L : (unsigned long) certsig->timestamp;
+
+ case GPGME_ATTR_EXPIRE:
+ return certsig->expires < 0 ? 0L : (unsigned long) certsig->expires;
+
+ case GPGME_ATTR_KEY_REVOKED:
+ return certsig->revoked;
+
+ case GPGME_ATTR_KEY_INVALID:
+ return certsig->invalid;
+
+ case GPGME_ATTR_KEY_EXPIRED:
+ return certsig->expired;
+
+ case GPGME_ATTR_SIG_CLASS:
+ return certsig->sig_class;
+
+ case GPGME_ATTR_SIG_STATUS:
+ return certsig->status;
+
+ default:
+ return 0;
+ }
+}
diff --git a/src/keylist.c b/src/keylist.c
new file mode 100644
index 0000000..29e30a0
--- /dev/null
+++ b/src/keylist.c
@@ -0,0 +1,1075 @@
+/* keylist.c - Listing keys.
+ Copyright (C) 2000 Werner Koch (dd9jn)
+ Copyright (C) 2001, 2002, 2003, 2004, 2006, 2007,
+ 2008, 2009 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_SYS_TYPES_H
+ /* Solaris 8 needs sys/types.h before time.h. */
+# include <sys/types.h>
+#endif
+#include <time.h>
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+
+/* Suppress warning for accessing deprecated member "class". */
+#define _GPGME_IN_GPGME
+#include "gpgme.h"
+#include "util.h"
+#include "context.h"
+#include "ops.h"
+#include "debug.h"
+
+
+struct key_queue_item_s
+{
+ struct key_queue_item_s *next;
+ gpgme_key_t key;
+};
+
+typedef struct
+{
+ struct _gpgme_op_keylist_result result;
+
+ gpgme_key_t tmp_key;
+
+ /* This points to the last uid in tmp_key. */
+ gpgme_user_id_t tmp_uid;
+
+ /* This points to the last sig in tmp_uid. */
+ gpgme_key_sig_t tmp_keysig;
+
+ /* Something new is available. */
+ int key_cond;
+ struct key_queue_item_s *key_queue;
+} *op_data_t;
+
+
+static void
+release_op_data (void *hook)
+{
+ op_data_t opd = (op_data_t) hook;
+ struct key_queue_item_s *key = opd->key_queue;
+
+ if (opd->tmp_key)
+ gpgme_key_unref (opd->tmp_key);
+
+ /* opd->tmp_uid and opd->tmp_keysig are actually part of opd->tmp_key,
+ so we do not need to release them here. */
+
+ while (key)
+ {
+ struct key_queue_item_s *next = key->next;
+
+ gpgme_key_unref (key->key);
+ key = next;
+ }
+}
+
+
+gpgme_keylist_result_t
+gpgme_op_keylist_result (gpgme_ctx_t ctx)
+{
+ void *hook;
+ op_data_t opd;
+ gpgme_error_t err;
+
+ TRACE_BEG (DEBUG_CTX, "gpgme_op_keylist_result", ctx);
+
+ err = _gpgme_op_data_lookup (ctx, OPDATA_KEYLIST, &hook, -1, NULL);
+ opd = hook;
+ if (err || !opd)
+ {
+ TRACE_SUC0 ("result=(null)");
+ return NULL;
+ }
+
+ TRACE_LOG1 ("truncated = %i", opd->result.truncated);
+
+ TRACE_SUC1 ("result=%p", &opd->result);
+ return &opd->result;
+}
+
+
+static gpgme_error_t
+keylist_status_handler (void *priv, gpgme_status_code_t code, char *args)
+{
+ gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
+ gpgme_error_t err;
+ void *hook;
+ op_data_t opd;
+
+ err = _gpgme_op_data_lookup (ctx, OPDATA_KEYLIST, &hook, -1, NULL);
+ opd = hook;
+ if (err)
+ return err;
+
+ switch (code)
+ {
+ case GPGME_STATUS_TRUNCATED:
+ opd->result.truncated = 1;
+ break;
+
+ default:
+ break;
+ }
+ return 0;
+}
+
+
+static void
+set_subkey_trust_info (gpgme_subkey_t subkey, const char *src)
+{
+ while (*src && !isdigit (*src))
+ {
+ switch (*src)
+ {
+ case 'e':
+ subkey->expired = 1;
+ break;
+
+ case 'r':
+ subkey->revoked = 1;
+ break;
+
+ case 'd':
+ /* Note that gpg 1.3 won't print that anymore but only uses
+ the capabilities field. */
+ subkey->disabled = 1;
+ break;
+
+ case 'i':
+ subkey->invalid = 1;
+ break;
+ }
+ src++;
+ }
+}
+
+
+static void
+set_mainkey_trust_info (gpgme_key_t key, const char *src)
+{
+ /* First set the trust info of the main key (the first subkey). */
+ set_subkey_trust_info (key->subkeys, src);
+
+ /* Now set the summarized trust info. */
+ while (*src && !isdigit (*src))
+ {
+ switch (*src)
+ {
+ case 'e':
+ key->expired = 1;
+ break;
+
+ case 'r':
+ key->revoked = 1;
+ break;
+
+ case 'd':
+ /* Note that gpg 1.3 won't print that anymore but only uses
+ the capabilities field. However, it is still used for
+ external key listings. */
+ key->disabled = 1;
+ break;
+
+ case 'i':
+ key->invalid = 1;
+ break;
+ }
+ src++;
+ }
+}
+
+
+static void
+set_userid_flags (gpgme_key_t key, const char *src)
+{
+ gpgme_user_id_t uid = key->_last_uid;
+
+ assert (uid);
+ /* Look at letters and stop at the first digit. */
+ while (*src && !isdigit (*src))
+ {
+ switch (*src)
+ {
+ case 'r':
+ uid->revoked = 1;
+ break;
+
+ case 'i':
+ uid->invalid = 1;
+ break;
+
+ case 'n':
+ uid->validity = GPGME_VALIDITY_NEVER;
+ break;
+
+ case 'm':
+ uid->validity = GPGME_VALIDITY_MARGINAL;
+ break;
+
+ case 'f':
+ uid->validity = GPGME_VALIDITY_FULL;
+ break;
+
+ case 'u':
+ uid->validity = GPGME_VALIDITY_ULTIMATE;
+ break;
+ }
+ src++;
+ }
+}
+
+
+static void
+set_subkey_capability (gpgme_subkey_t subkey, const char *src)
+{
+ while (*src)
+ {
+ switch (*src)
+ {
+ case 'e':
+ subkey->can_encrypt = 1;
+ break;
+
+ case 's':
+ subkey->can_sign = 1;
+ break;
+
+ case 'c':
+ subkey->can_certify = 1;
+ break;
+
+ case 'a':
+ subkey->can_authenticate = 1;
+ break;
+
+ case 'q':
+ subkey->is_qualified = 1;
+ break;
+
+ case 'd':
+ subkey->disabled = 1;
+ break;
+ }
+ src++;
+ }
+}
+
+
+static void
+set_mainkey_capability (gpgme_key_t key, const char *src)
+{
+ /* First set the capabilities of the main key (the first subkey). */
+ set_subkey_capability (key->subkeys, src);
+
+ while (*src)
+ {
+ switch (*src)
+ {
+ case 'd':
+ case 'D':
+ /* Note, that this flag is also set using the key validity
+ field for backward compatibility with gpg 1.2. We use d
+ and D, so that a future gpg version will be able to
+ disable certain subkeys. Currently it is expected that
+ gpg sets this for the primary key. */
+ key->disabled = 1;
+ break;
+
+ case 'e':
+ case 'E':
+ key->can_encrypt = 1;
+ break;
+
+ case 's':
+ case 'S':
+ key->can_sign = 1;
+ break;
+
+ case 'c':
+ case 'C':
+ key->can_certify = 1;
+ break;
+
+ case 'a':
+ case 'A':
+ key->can_authenticate = 1;
+ break;
+
+ case 'q':
+ case 'Q':
+ key->is_qualified = 1;
+ break;
+ }
+ src++;
+ }
+}
+
+
+static void
+set_ownertrust (gpgme_key_t key, const char *src)
+{
+ /* Look at letters and stop at the first digit. */
+ while (*src && !isdigit (*src))
+ {
+ switch (*src)
+ {
+ case 'n':
+ key->owner_trust = GPGME_VALIDITY_NEVER;
+ break;
+
+ case 'm':
+ key->owner_trust = GPGME_VALIDITY_MARGINAL;
+ break;
+
+ case 'f':
+ key->owner_trust = GPGME_VALIDITY_FULL;
+ break;
+
+ case 'u':
+ key->owner_trust = GPGME_VALIDITY_ULTIMATE;
+ break;
+
+ default:
+ key->owner_trust = GPGME_VALIDITY_UNKNOWN;
+ break;
+ }
+ src++;
+ }
+}
+
+
+/* Parse field 15 of a secret key or subkey. This fields holds a
+ reference to smartcards. FIELD is the content of the field and we
+ are allowed to modify it. */
+static gpg_error_t
+parse_sec_field15 (gpgme_subkey_t subkey, char *field)
+{
+ if (!*field)
+ ; /* Empty. */
+ else if (*field == '#')
+ {
+ /* This is a stub for an offline key. We reset the SECRET flag
+ of the subkey here. Note that the secret flag of the entire
+ key will be true even then. */
+ subkey->secret = 0;
+ }
+ else if (strchr ("01234567890ABCDEFabcdef", *field))
+ {
+ /* Fields starts with a hex digit; thus it is a serial number. */
+ subkey->is_cardkey = 1;
+ subkey->card_number = strdup (field);
+ if (!subkey->card_number)
+ return gpg_error_from_syserror ();
+ }
+ else
+ {
+ /* RFU. */
+ }
+
+ return 0;
+}
+
+
+/* We have read an entire key into tmp_key and should now finish it.
+ It is assumed that this releases tmp_key. */
+static void
+finish_key (gpgme_ctx_t ctx, op_data_t opd)
+{
+ gpgme_key_t key = opd->tmp_key;
+
+ opd->tmp_key = NULL;
+ opd->tmp_uid = NULL;
+ opd->tmp_keysig = NULL;
+
+ if (key)
+ _gpgme_engine_io_event (ctx->engine, GPGME_EVENT_NEXT_KEY, key);
+}
+
+
+/* Note: We are allowed to modify LINE. */
+static gpgme_error_t
+keylist_colon_handler (void *priv, char *line)
+{
+ gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
+ enum
+ {
+ RT_NONE, RT_SIG, RT_UID, RT_SUB, RT_PUB, RT_FPR,
+ RT_SSB, RT_SEC, RT_CRT, RT_CRS, RT_REV, RT_SPK
+ }
+ rectype = RT_NONE;
+#define NR_FIELDS 16
+ char *field[NR_FIELDS];
+ int fields = 0;
+ void *hook;
+ op_data_t opd;
+ gpgme_error_t err;
+ gpgme_key_t key;
+ gpgme_subkey_t subkey = NULL;
+ gpgme_key_sig_t keysig = NULL;
+
+ err = _gpgme_op_data_lookup (ctx, OPDATA_KEYLIST, &hook, -1, NULL);
+ opd = hook;
+ if (err)
+ return err;
+
+ key = opd->tmp_key;
+
+ TRACE2 (DEBUG_CTX, "gpgme:keylist_colon_handler", ctx,
+ "key = %p, line = %s", key, line ? line : "(null)");
+
+ if (!line)
+ {
+ /* End Of File. */
+ finish_key (ctx, opd);
+ return 0;
+ }
+
+ while (line && fields < NR_FIELDS)
+ {
+ field[fields++] = line;
+ line = strchr (line, ':');
+ if (line)
+ *(line++) = '\0';
+ }
+
+ if (!strcmp (field[0], "sig"))
+ rectype = RT_SIG;
+ else if (!strcmp (field[0], "rev"))
+ rectype = RT_REV;
+ else if (!strcmp (field[0], "pub"))
+ rectype = RT_PUB;
+ else if (!strcmp (field[0], "sec"))
+ rectype = RT_SEC;
+ else if (!strcmp (field[0], "crt"))
+ rectype = RT_CRT;
+ else if (!strcmp (field[0], "crs"))
+ rectype = RT_CRS;
+ else if (!strcmp (field[0], "fpr") && key)
+ rectype = RT_FPR;
+ else if (!strcmp (field[0], "uid") && key)
+ rectype = RT_UID;
+ else if (!strcmp (field[0], "sub") && key)
+ rectype = RT_SUB;
+ else if (!strcmp (field[0], "ssb") && key)
+ rectype = RT_SSB;
+ else if (!strcmp (field[0], "spk") && key)
+ rectype = RT_SPK;
+ else
+ rectype = RT_NONE;
+
+ /* Only look at signatures immediately following a user ID. For
+ this, clear the user ID pointer when encountering anything but a
+ signature. */
+ if (rectype != RT_SIG && rectype != RT_REV)
+ opd->tmp_uid = NULL;
+
+ /* Only look at subpackets immediately following a signature. For
+ this, clear the signature pointer when encountering anything but
+ a subpacket. */
+ if (rectype != RT_SPK)
+ opd->tmp_keysig = NULL;
+
+ switch (rectype)
+ {
+ case RT_PUB:
+ case RT_SEC:
+ case RT_CRT:
+ case RT_CRS:
+ /* Start a new keyblock. */
+ err = _gpgme_key_new (&key);
+ if (err)
+ return err;
+ key->keylist_mode = ctx->keylist_mode;
+ err = _gpgme_key_add_subkey (key, &subkey);
+ if (err)
+ {
+ gpgme_key_unref (key);
+ return err;
+ }
+
+ if (rectype == RT_SEC || rectype == RT_CRS)
+ key->secret = subkey->secret = 1;
+ if (rectype == RT_CRT || rectype == RT_CRS)
+ key->protocol = GPGME_PROTOCOL_CMS;
+ finish_key (ctx, opd);
+ opd->tmp_key = key;
+
+ /* Field 2 has the trust info. */
+ if (fields >= 2)
+ set_mainkey_trust_info (key, field[1]);
+
+ /* Field 3 has the key length. */
+ if (fields >= 3)
+ {
+ int i = atoi (field[2]);
+ /* Ignore invalid values. */
+ if (i > 1)
+ subkey->length = i;
+ }
+
+ /* Field 4 has the public key algorithm. */
+ if (fields >= 4)
+ {
+ int i = atoi (field[3]);
+ if (i >= 1 && i < 128)
+ subkey->pubkey_algo = i;
+ }
+
+ /* Field 5 has the long keyid. Allow short key IDs for the
+ output of an external keyserver listing. */
+ if (fields >= 5 && strlen (field[4]) <= DIM(subkey->_keyid) - 1)
+ strcpy (subkey->_keyid, field[4]);
+
+ /* Field 6 has the timestamp (seconds). */
+ if (fields >= 6)
+ subkey->timestamp = _gpgme_parse_timestamp (field[5], NULL);
+
+ /* Field 7 has the expiration time (seconds). */
+ if (fields >= 7)
+ subkey->expires = _gpgme_parse_timestamp (field[6], NULL);
+
+ /* Field 8 has the X.509 serial number. */
+ if (fields >= 8 && (rectype == RT_CRT || rectype == RT_CRS))
+ {
+ key->issuer_serial = strdup (field[7]);
+ if (!key->issuer_serial)
+ return gpg_error_from_errno (errno);
+ }
+
+ /* Field 9 has the ownertrust. */
+ if (fields >= 9)
+ set_ownertrust (key, field[8]);
+
+ /* Field 10 is not used for gpg due to --fixed-list-mode option
+ but GPGSM stores the issuer name. */
+ if (fields >= 10 && (rectype == RT_CRT || rectype == RT_CRS))
+ if (_gpgme_decode_c_string (field[9], &key->issuer_name, 0))
+ return gpg_error (GPG_ERR_ENOMEM); /* FIXME */
+
+ /* Field 11 has the signature class. */
+
+ /* Field 12 has the capabilities. */
+ if (fields >= 12)
+ set_mainkey_capability (key, field[11]);
+
+ /* Field 15 carries special flags of a secret key. */
+ if (fields >= 15 && key->secret)
+ {
+ err = parse_sec_field15 (subkey, field[14]);
+ if (err)
+ return err;
+ }
+ break;
+
+ case RT_SUB:
+ case RT_SSB:
+ /* Start a new subkey. */
+ err = _gpgme_key_add_subkey (key, &subkey);
+ if (err)
+ return err;
+
+ if (rectype == RT_SSB)
+ subkey->secret = 1;
+
+ /* Field 2 has the trust info. */
+ if (fields >= 2)
+ set_subkey_trust_info (subkey, field[1]);
+
+ /* Field 3 has the key length. */
+ if (fields >= 3)
+ {
+ int i = atoi (field[2]);
+ /* Ignore invalid values. */
+ if (i > 1)
+ subkey->length = i;
+ }
+
+ /* Field 4 has the public key algorithm. */
+ if (fields >= 4)
+ {
+ int i = atoi (field[3]);
+ if (i >= 1 && i < 128)
+ subkey->pubkey_algo = i;
+ }
+
+ /* Field 5 has the long keyid. */
+ if (fields >= 5 && strlen (field[4]) == DIM(subkey->_keyid) - 1)
+ strcpy (subkey->_keyid, field[4]);
+
+ /* Field 6 has the timestamp (seconds). */
+ if (fields >= 6)
+ subkey->timestamp = _gpgme_parse_timestamp (field[5], NULL);
+
+ /* Field 7 has the expiration time (seconds). */
+ if (fields >= 7)
+ subkey->expires = _gpgme_parse_timestamp (field[6], NULL);
+
+ /* Field 8 is reserved (LID). */
+ /* Field 9 has the ownertrust. */
+ /* Field 10, the user ID, is n/a for a subkey. */
+
+ /* Field 11 has the signature class. */
+
+ /* Field 12 has the capabilities. */
+ if (fields >= 12)
+ set_subkey_capability (subkey, field[11]);
+
+ /* Field 15 carries special flags of a secret key. */
+ if (fields >= 15 && key->secret)
+ {
+ err = parse_sec_field15 (subkey, field[14]);
+ if (err)
+ return err;
+ }
+ break;
+
+ case RT_UID:
+ /* Field 2 has the trust info, and field 10 has the user ID. */
+ if (fields >= 10)
+ {
+ if (_gpgme_key_append_name (key, field[9], 1))
+ return gpg_error_from_errno (GPG_ERR_ENOMEM); /* FIXME */
+ else
+ {
+ if (field[1])
+ set_userid_flags (key, field[1]);
+ opd->tmp_uid = key->_last_uid;
+ }
+ }
+ break;
+
+ case RT_FPR:
+ /* Field 10 has the fingerprint (take only the first one). */
+ if (fields >= 10 && field[9] && *field[9])
+ {
+ /* Need to apply it to the last subkey because all subkeys
+ do have fingerprints. */
+ subkey = key->_last_subkey;
+ if (!subkey->fpr)
+ {
+ subkey->fpr = strdup (field[9]);
+ if (!subkey->fpr)
+ return gpg_error_from_errno (errno);
+ }
+ }
+
+ /* Field 13 has the gpgsm chain ID (take only the first one). */
+ if (fields >= 13 && !key->chain_id && *field[12])
+ {
+ key->chain_id = strdup (field[12]);
+ if (!key->chain_id)
+ return gpg_error_from_errno (errno);
+ }
+ break;
+
+ case RT_SIG:
+ case RT_REV:
+ if (!opd->tmp_uid)
+ return 0;
+
+ /* Start a new (revoked) signature. */
+ assert (opd->tmp_uid == key->_last_uid);
+ keysig = _gpgme_key_add_sig (key, (fields >= 10) ? field[9] : NULL);
+ if (!keysig)
+ return gpg_error (GPG_ERR_ENOMEM); /* FIXME */
+
+ /* Field 2 has the calculated trust ('!', '-', '?', '%'). */
+ if (fields >= 2)
+ switch (field[1][0])
+ {
+ case '!':
+ keysig->status = gpg_error (GPG_ERR_NO_ERROR);
+ break;
+
+ case '-':
+ keysig->status = gpg_error (GPG_ERR_BAD_SIGNATURE);
+ break;
+
+ case '?':
+ keysig->status = gpg_error (GPG_ERR_NO_PUBKEY);
+ break;
+
+ case '%':
+ keysig->status = gpg_error (GPG_ERR_GENERAL);
+ break;
+
+ default:
+ keysig->status = gpg_error (GPG_ERR_NO_ERROR);
+ break;
+ }
+
+ /* Field 4 has the public key algorithm. */
+ if (fields >= 4)
+ {
+ int i = atoi (field[3]);
+ if (i >= 1 && i < 128)
+ keysig->pubkey_algo = i;
+ }
+
+ /* Field 5 has the long keyid. */
+ if (fields >= 5 && strlen (field[4]) == DIM(keysig->_keyid) - 1)
+ strcpy (keysig->_keyid, field[4]);
+
+ /* Field 6 has the timestamp (seconds). */
+ if (fields >= 6)
+ keysig->timestamp = _gpgme_parse_timestamp (field[5], NULL);
+
+ /* Field 7 has the expiration time (seconds). */
+ if (fields >= 7)
+ keysig->expires = _gpgme_parse_timestamp (field[6], NULL);
+
+ /* Field 11 has the signature class (eg, 0x30 means revoked). */
+ if (fields >= 11)
+ if (field[10][0] && field[10][1])
+ {
+ int sig_class = _gpgme_hextobyte (field[10]);
+ if (sig_class >= 0)
+ {
+ keysig->sig_class = sig_class;
+ keysig->class = keysig->sig_class;
+ if (sig_class == 0x30)
+ keysig->revoked = 1;
+ }
+ if (field[10][2] == 'x')
+ keysig->exportable = 1;
+ }
+
+ opd->tmp_keysig = keysig;
+ break;
+
+ case RT_SPK:
+ if (!opd->tmp_keysig)
+ return 0;
+ assert (opd->tmp_keysig == key->_last_uid->_last_keysig);
+
+ if (fields >= 4)
+ {
+ /* Field 2 has the subpacket type. */
+ int type = atoi (field[1]);
+
+ /* Field 3 has the flags. */
+ int flags = atoi (field[2]);
+
+ /* Field 4 has the length. */
+ int len = atoi (field[3]);
+
+ /* Field 5 has the data. */
+ char *data = field[4];
+
+ /* Type 20: Notation data. */
+ /* Type 26: Policy URL. */
+ if (type == 20 || type == 26)
+ {
+ gpgme_sig_notation_t notation;
+
+ keysig = opd->tmp_keysig;
+
+ /* At this time, any error is serious. */
+ err = _gpgme_parse_notation (&notation, type, flags, len, data);
+ if (err)
+ return err;
+
+ /* Add a new notation. FIXME: Could be factored out. */
+ if (!keysig->notations)
+ keysig->notations = notation;
+ if (keysig->_last_notation)
+ keysig->_last_notation->next = notation;
+ keysig->_last_notation = notation;
+ }
+ }
+
+ case RT_NONE:
+ /* Unknown record. */
+ break;
+ }
+ return 0;
+}
+
+
+void
+_gpgme_op_keylist_event_cb (void *data, gpgme_event_io_t type, void *type_data)
+{
+ gpgme_error_t err;
+ gpgme_ctx_t ctx = (gpgme_ctx_t) data;
+ gpgme_key_t key = (gpgme_key_t) type_data;
+ void *hook;
+ op_data_t opd;
+ struct key_queue_item_s *q, *q2;
+
+ assert (type == GPGME_EVENT_NEXT_KEY);
+
+ err = _gpgme_op_data_lookup (ctx, OPDATA_KEYLIST, &hook, -1, NULL);
+ opd = hook;
+ if (err)
+ return;
+
+ q = malloc (sizeof *q);
+ if (!q)
+ {
+ gpgme_key_unref (key);
+ /* FIXME return GPGME_Out_Of_Core; */
+ return;
+ }
+ q->key = key;
+ q->next = NULL;
+ /* FIXME: Use a tail pointer? */
+ if (!(q2 = opd->key_queue))
+ opd->key_queue = q;
+ else
+ {
+ for (; q2->next; q2 = q2->next)
+ ;
+ q2->next = q;
+ }
+ opd->key_cond = 1;
+}
+
+
+/* Start a keylist operation within CTX, searching for keys which
+ match PATTERN. If SECRET_ONLY is true, only secret keys are
+ returned. */
+gpgme_error_t
+gpgme_op_keylist_start (gpgme_ctx_t ctx, const char *pattern, int secret_only)
+{
+ gpgme_error_t err;
+ void *hook;
+ op_data_t opd;
+
+ TRACE_BEG2 (DEBUG_CTX, "gpgme_op_keylist_start", ctx,
+ "pattern=%s, secret_only=%i", pattern, secret_only);
+
+ if (!ctx)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ err = _gpgme_op_reset (ctx, 2);
+ if (err)
+ return TRACE_ERR (err);
+
+ err = _gpgme_op_data_lookup (ctx, OPDATA_KEYLIST, &hook,
+ sizeof (*opd), release_op_data);
+ opd = hook;
+ if (err)
+ return TRACE_ERR (err);
+
+ _gpgme_engine_set_status_handler (ctx->engine, keylist_status_handler, ctx);
+
+ err = _gpgme_engine_set_colon_line_handler (ctx->engine,
+ keylist_colon_handler, ctx);
+ if (err)
+ return TRACE_ERR (err);
+
+ err = _gpgme_engine_op_keylist (ctx->engine, pattern, secret_only,
+ ctx->keylist_mode);
+ return TRACE_ERR (err);
+}
+
+
+/* Start a keylist operation within CTX, searching for keys which
+ match PATTERN. If SECRET_ONLY is true, only secret keys are
+ returned. */
+gpgme_error_t
+gpgme_op_keylist_ext_start (gpgme_ctx_t ctx, const char *pattern[],
+ int secret_only, int reserved)
+{
+ gpgme_error_t err;
+ void *hook;
+ op_data_t opd;
+
+ TRACE_BEG2 (DEBUG_CTX, "gpgme_op_keylist_ext_start", ctx,
+ "secret_only=%i, reserved=0x%x", secret_only, reserved);
+
+ if (!ctx)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ err = _gpgme_op_reset (ctx, 2);
+ if (err)
+ return TRACE_ERR (err);
+
+ err = _gpgme_op_data_lookup (ctx, OPDATA_KEYLIST, &hook,
+ sizeof (*opd), release_op_data);
+ opd = hook;
+ if (err)
+ return TRACE_ERR (err);
+
+ _gpgme_engine_set_status_handler (ctx->engine, keylist_status_handler, ctx);
+ err = _gpgme_engine_set_colon_line_handler (ctx->engine,
+ keylist_colon_handler, ctx);
+ if (err)
+ return TRACE_ERR (err);
+
+ err = _gpgme_engine_op_keylist_ext (ctx->engine, pattern, secret_only,
+ reserved, ctx->keylist_mode);
+ return TRACE_ERR (err);
+}
+
+
+/* Return the next key from the keylist in R_KEY. */
+gpgme_error_t
+gpgme_op_keylist_next (gpgme_ctx_t ctx, gpgme_key_t *r_key)
+{
+ gpgme_error_t err;
+ struct key_queue_item_s *queue_item;
+ void *hook;
+ op_data_t opd;
+
+ TRACE_BEG (DEBUG_CTX, "gpgme_op_keylist_next", ctx);
+
+ if (!ctx || !r_key)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+ *r_key = NULL;
+ if (!ctx)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ err = _gpgme_op_data_lookup (ctx, OPDATA_KEYLIST, &hook, -1, NULL);
+ opd = hook;
+ if (err)
+ return TRACE_ERR (err);
+ if (opd == NULL)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ if (!opd->key_queue)
+ {
+ err = _gpgme_wait_on_condition (ctx, &opd->key_cond, NULL);
+ if (err)
+ return TRACE_ERR (err);
+
+ if (!opd->key_cond)
+ return TRACE_ERR (gpg_error (GPG_ERR_EOF));
+
+ opd->key_cond = 0;
+ assert (opd->key_queue);
+ }
+ queue_item = opd->key_queue;
+ opd->key_queue = queue_item->next;
+ if (!opd->key_queue)
+ opd->key_cond = 0;
+
+ *r_key = queue_item->key;
+ free (queue_item);
+
+ return TRACE_SUC2 ("key=%p (%s)", *r_key,
+ ((*r_key)->subkeys && (*r_key)->subkeys->fpr) ?
+ (*r_key)->subkeys->fpr : "invalid");
+}
+
+
+/* Terminate a pending keylist operation within CTX. */
+gpgme_error_t
+gpgme_op_keylist_end (gpgme_ctx_t ctx)
+{
+ TRACE (DEBUG_CTX, "gpgme_op_keylist_end", ctx);
+
+ if (!ctx)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ return 0;
+}
+
+
+/* Get the key with the fingerprint FPR from the crypto backend. If
+ 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)
+{
+ gpgme_ctx_t listctx;
+ gpgme_error_t err;
+ gpgme_key_t key;
+
+ TRACE_BEG2 (DEBUG_CTX, "gpgme_get_key", ctx,
+ "fpr=%s, secret=%i", fpr, secret);
+
+ if (!ctx || !r_key || !fpr)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ if (strlen (fpr) < 8) /* We have at least a key ID. */
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ /* FIXME: We use our own context because we have to avoid the user's
+ I/O callback handlers. */
+ err = gpgme_new (&listctx);
+ if (err)
+ return TRACE_ERR (err);
+ {
+ gpgme_protocol_t proto;
+ gpgme_engine_info_t info;
+
+ /* Clone the relevant state. */
+ proto = gpgme_get_protocol (ctx);
+ gpgme_set_protocol (listctx, proto);
+ gpgme_set_keylist_mode (listctx, gpgme_get_keylist_mode (ctx));
+ info = gpgme_ctx_get_engine_info (ctx);
+ while (info && info->protocol != proto)
+ info = info->next;
+ if (info)
+ gpgme_ctx_set_engine_info (listctx, proto,
+ info->file_name, info->home_dir);
+ }
+
+ err = gpgme_op_keylist_start (listctx, fpr, secret);
+ if (!err)
+ err = gpgme_op_keylist_next (listctx, r_key);
+ if (!err)
+ {
+ try_next_key:
+ err = gpgme_op_keylist_next (listctx, &key);
+ if (gpgme_err_code (err) == GPG_ERR_EOF)
+ err = 0;
+ else
+ {
+ if (!err
+ && *r_key && (*r_key)->subkeys && (*r_key)->subkeys->fpr
+ && key && key->subkeys && key->subkeys->fpr
+ && !strcmp ((*r_key)->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
+ problem may occur with corrupted keyrings and has
+ been noticed often with gpgsm. In fact gpgsm uses a
+ similar hack to sort out such duplicates but it can't
+ do that while listing keys. */
+ gpgme_key_unref (key);
+ goto try_next_key;
+ }
+ if (!err)
+ {
+ gpgme_key_unref (key);
+ err = gpg_error (GPG_ERR_AMBIGUOUS_NAME);
+ }
+ gpgme_key_unref (*r_key);
+ }
+ }
+ gpgme_release (listctx);
+ if (! err)
+ {
+ TRACE_LOG2 ("key=%p (%s)", *r_key,
+ ((*r_key)->subkeys && (*r_key)->subkeys->fpr) ?
+ (*r_key)->subkeys->fpr : "invalid");
+ }
+ return TRACE_ERR (err);
+}
diff --git a/src/libgpgme.vers b/src/libgpgme.vers
new file mode 100644
index 0000000..3477d31
--- /dev/null
+++ b/src/libgpgme.vers
@@ -0,0 +1,213 @@
+# libgpgme.vers - List of symbols to export.
+# Copyright (C) 2002, 2004, 2005, 2009 g10 Code GmbH
+#
+# This file is part of GPGME.
+#
+# GPGME is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser general Public License as
+# published by the Free Software Foundation; either version 2.1 of
+# the License, or (at your option) any later version.
+#
+# GPGME is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+#-------------------------------------------------------
+# Please remember to add new functions also to gpgme.def
+#-------------------------------------------------------
+
+GPGME_1.1 {
+ global:
+ gpgme_set_engine_info;
+
+ gpgme_ctx_get_engine_info;
+ gpgme_ctx_set_engine_info;
+
+ gpgme_data_set_file_name;
+ gpgme_data_get_file_name;
+
+ gpgme_sig_notation_clear;
+ gpgme_sig_notation_add;
+ gpgme_sig_notation_get;
+
+ gpgme_free;
+
+ gpgme_op_getauditlog_start;
+ gpgme_op_getauditlog;
+
+ gpgme_conf_release;
+ gpgme_conf_arg_new;
+ gpgme_conf_arg_release;
+ gpgme_conf_opt_change;
+ gpgme_op_conf_load;
+ gpgme_op_conf_save;
+
+ gpgme_cancel_async;
+
+ gpgme_op_assuan_result;
+ gpgme_op_assuan_transact;
+ gpgme_op_assuan_transact_start;
+
+ gpgme_check_version_internal;
+
+ gpgme_io_read;
+ gpgme_io_write;
+
+ gpgme_result_ref;
+ gpgme_result_unref;
+
+ gpgme_op_import_keys;
+ gpgme_op_import_keys_start;
+ gpgme_op_export_keys;
+ gpgme_op_export_keys_start;
+
+ gpgme_op_assuan_transact_ext;
+
+ gpgme_wait_ext;
+
+ gpgme_op_vfs_mount_result;
+ gpgme_op_vfs_mount;
+ gpgme_op_vfs_create;
+
+ gpgme_key_from_uid;
+ gpgme_set_sub_protocol;
+ gpgme_get_sub_protocol;
+
+ gpgme_op_passwd_start;
+ gpgme_op_passwd;
+};
+
+
+GPGME_1.0 {
+ global:
+ gpgme_check_version;
+ gpgme_get_engine_info;
+ gpgme_engine_check_version;
+
+ gpgme_err_code_from_errno;
+ gpgme_err_code_to_errno;
+ gpgme_err_make_from_errno;
+ gpgme_error_from_errno;
+ gpgme_strerror;
+ gpgme_strerror_r;
+ gpgme_strsource;
+
+ gpgme_data_get_encoding;
+ gpgme_data_new;
+ gpgme_data_new_from_cbs;
+ gpgme_data_new_from_fd;
+ gpgme_data_new_from_file;
+ gpgme_data_new_from_filepart;
+ gpgme_data_new_from_mem;
+ gpgme_data_new_from_stream;
+ gpgme_data_read;
+ gpgme_data_release;
+ gpgme_data_release_and_get_mem;
+ gpgme_data_seek;
+ gpgme_data_set_encoding;
+ gpgme_data_write;
+
+ gpgme_get_protocol_name;
+ gpgme_hash_algo_name;
+ gpgme_pubkey_algo_name;
+
+ gpgme_new;
+ gpgme_get_armor;
+ gpgme_get_include_certs;
+ gpgme_get_io_cbs;
+ gpgme_get_keylist_mode;
+ gpgme_get_passphrase_cb;
+ gpgme_get_progress_cb;
+ gpgme_get_protocol;
+ gpgme_get_textmode;
+ gpgme_release;
+ gpgme_set_armor;
+ gpgme_set_include_certs;
+ gpgme_set_io_cbs;
+ gpgme_set_keylist_mode;
+ gpgme_set_locale;
+ gpgme_set_passphrase_cb;
+ gpgme_set_progress_cb;
+ gpgme_set_protocol;
+ gpgme_set_textmode;
+ gpgme_signers_add;
+ gpgme_signers_clear;
+ gpgme_signers_enum;
+
+ gpgme_key_ref;
+ gpgme_key_unref;
+ gpgme_key_release;
+
+ gpgme_trust_item_ref;
+ gpgme_trust_item_unref;
+
+ gpgme_cancel;
+ gpgme_op_card_edit;
+ gpgme_op_card_edit_start;
+ gpgme_op_decrypt;
+ gpgme_op_decrypt_result;
+ gpgme_op_decrypt_start;
+ gpgme_op_decrypt_verify;
+ gpgme_op_decrypt_verify_start;
+ gpgme_op_delete;
+ gpgme_op_delete_start;
+ gpgme_op_edit;
+ gpgme_op_edit_start;
+ gpgme_op_encrypt;
+ gpgme_op_encrypt_result;
+ gpgme_op_encrypt_sign;
+ gpgme_op_encrypt_sign_start;
+ gpgme_op_encrypt_start;
+ gpgme_op_export;
+ gpgme_op_export_ext;
+ gpgme_op_export_ext_start;
+ gpgme_op_export_start;
+ gpgme_op_genkey;
+ gpgme_op_genkey_result;
+ gpgme_op_genkey_start;
+ gpgme_get_key;
+ gpgme_op_import;
+ gpgme_op_import_result;
+ gpgme_op_import_start;
+ gpgme_op_keylist_end;
+ gpgme_op_keylist_ext_start;
+ gpgme_op_keylist_next;
+ gpgme_op_keylist_result;
+ gpgme_op_keylist_start;
+ gpgme_op_sign;
+ gpgme_op_sign_result;
+ gpgme_op_sign_start;
+ gpgme_op_trustlist_end;
+ gpgme_op_trustlist_next;
+ gpgme_op_trustlist_start;
+ gpgme_op_verify;
+ gpgme_op_verify_result;
+ gpgme_op_verify_start;
+ gpgme_wait;
+
+ gpgme_data_new_with_read_cb;
+ gpgme_data_rewind;
+ gpgme_get_sig_status;
+ gpgme_get_sig_string_attr;
+ gpgme_get_sig_ulong_attr;
+ gpgme_get_sig_key;
+ gpgme_key_get_string_attr;
+ gpgme_key_get_ulong_attr;
+ gpgme_key_sig_get_string_attr;
+ gpgme_key_sig_get_ulong_attr;
+ gpgme_op_import_ext;
+ gpgme_trust_item_get_int_attr;
+ gpgme_trust_item_get_string_attr;
+ gpgme_trust_item_release;
+
+ gpgme_err_code_from_syserror;
+ gpgme_err_set_errno;
+
+ local:
+ *;
+
+};
diff --git a/src/moc_kdpipeiodevice.cpp b/src/moc_kdpipeiodevice.cpp
new file mode 100644
index 0000000..eac7b23
--- /dev/null
+++ b/src/moc_kdpipeiodevice.cpp
@@ -0,0 +1,60 @@
+/****************************************************************************
+** Meta object code from reading C++ file 'kdpipeiodevice.h'
+**
+** Created: Mon Aug 27 15:17:18 2007
+** by: The Qt Meta Object Compiler version 59 (Qt 4.3.0)
+**
+** WARNING! All changes made in this file will be lost!
+*****************************************************************************/
+
+#include "kdpipeiodevice.h"
+#if !defined(Q_MOC_OUTPUT_REVISION)
+#error "The header file 'kdpipeiodevice.h' doesn't include <QObject>."
+#elif Q_MOC_OUTPUT_REVISION != 59
+#error "This file was generated using the moc from 4.3.0. It"
+#error "cannot be used with the include files from this version of Qt."
+#error "(The moc has changed too much.)"
+#endif
+
+static const uint qt_meta_data_KDPipeIODevice[] = {
+
+ // content:
+ 1, // revision
+ 0, // classname
+ 0, 0, // classinfo
+ 0, 0, // methods
+ 0, 0, // properties
+ 0, 0, // enums/sets
+
+ 0 // eod
+};
+
+static const char qt_meta_stringdata_KDPipeIODevice[] = {
+ "KDPipeIODevice\0"
+};
+
+const QMetaObject KDPipeIODevice::staticMetaObject = {
+ { &QIODevice::staticMetaObject, qt_meta_stringdata_KDPipeIODevice,
+ qt_meta_data_KDPipeIODevice, 0 }
+};
+
+const QMetaObject *KDPipeIODevice::metaObject() const
+{
+ return &staticMetaObject;
+}
+
+void *KDPipeIODevice::qt_metacast(const char *_clname)
+{
+ if (!_clname) return 0;
+ if (!strcmp(_clname, qt_meta_stringdata_KDPipeIODevice))
+ return static_cast<void*>(const_cast< KDPipeIODevice*>(this));
+ return QIODevice::qt_metacast(_clname);
+}
+
+int KDPipeIODevice::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
+{
+ _id = QIODevice::qt_metacall(_c, _id, _a);
+ if (_id < 0)
+ return _id;
+ return _id;
+}
diff --git a/src/op-support.c b/src/op-support.c
new file mode 100644
index 0000000..000560b
--- /dev/null
+++ b/src/op-support.c
@@ -0,0 +1,322 @@
+/* op-support.c - Supporting functions.
+ Copyright (C) 2002, 2003, 2004, 2007 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#ifdef HAVE_LOCALE_H
+#include <locale.h>
+#endif
+
+#include "gpgme.h"
+#include "context.h"
+#include "ops.h"
+#include "util.h"
+
+
+gpgme_error_t
+_gpgme_op_data_lookup (gpgme_ctx_t ctx, ctx_op_data_id_t type, void **hook,
+ int size, void (*cleanup) (void *))
+{
+ struct ctx_op_data *data;
+
+ if (!ctx)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ data = ctx->op_data;
+ while (data && data->type != type)
+ data = data->next;
+ if (!data)
+ {
+ if (size < 0)
+ {
+ *hook = NULL;
+ return 0;
+ }
+
+ data = calloc (1, sizeof (struct ctx_op_data) + size);
+ if (!data)
+ return gpg_error_from_errno (errno);
+ data->magic = CTX_OP_DATA_MAGIC;
+ data->next = ctx->op_data;
+ data->type = type;
+ data->cleanup = cleanup;
+ data->hook = (void *) (((char *) data) + sizeof (struct ctx_op_data));
+ data->references = 1;
+ ctx->op_data = data;
+ }
+ *hook = data->hook;
+ return 0;
+}
+
+
+/* type is: 0: asynchronous operation (use global or user event loop).
+ 1: synchronous operation (always use private event loop).
+ 2: asynchronous private operation (use private or user
+ event loop).
+ 256: Modification flag to suppress the engine reset.
+*/
+gpgme_error_t
+_gpgme_op_reset (gpgme_ctx_t ctx, int type)
+{
+ gpgme_error_t err = 0;
+ struct gpgme_io_cbs io_cbs;
+ int no_reset = (type & 256);
+ int reuse_engine = 0;
+
+ type &= 255;
+
+ _gpgme_release_result (ctx);
+ LOCK (ctx->lock);
+ ctx->canceled = 0;
+ UNLOCK (ctx->lock);
+
+ if (ctx->engine && no_reset)
+ reuse_engine = 1;
+ else if (ctx->engine)
+ {
+ /* Attempt to reset an existing engine. */
+
+ err = _gpgme_engine_reset (ctx->engine);
+ if (gpg_err_code (err) == GPG_ERR_NOT_IMPLEMENTED)
+ {
+ _gpgme_engine_release (ctx->engine);
+ ctx->engine = NULL;
+ }
+ }
+
+ if (!ctx->engine)
+ {
+ gpgme_engine_info_t info;
+ info = ctx->engine_info;
+ while (info && info->protocol != ctx->protocol)
+ info = info->next;
+
+ if (!info)
+ return gpg_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
+
+ /* Create an engine object. */
+ err = _gpgme_engine_new (info, &ctx->engine);
+ if (err)
+ return err;
+ }
+
+ if (!reuse_engine)
+ {
+ err = 0;
+#ifdef LC_CTYPE
+ err = _gpgme_engine_set_locale (ctx->engine, LC_CTYPE, ctx->lc_ctype);
+#endif
+#ifdef LC_MESSAGES
+ if (!err)
+ err = _gpgme_engine_set_locale (ctx->engine,
+ LC_MESSAGES, ctx->lc_messages);
+#endif
+ if (gpg_err_code (err) == GPG_ERR_NOT_IMPLEMENTED)
+ err = 0;
+ if (err)
+ {
+ _gpgme_engine_release (ctx->engine);
+ ctx->engine = NULL;
+ return err;
+ }
+ }
+
+ if (ctx->sub_protocol != GPGME_PROTOCOL_DEFAULT)
+ {
+ err = _gpgme_engine_set_protocol (ctx->engine, ctx->sub_protocol);
+ if (err)
+ return err;
+ }
+
+ if (type == 1 || (type == 2 && !ctx->io_cbs.add))
+ {
+ /* Use private event loop. */
+ io_cbs.add = _gpgme_add_io_cb;
+ io_cbs.add_priv = ctx;
+ io_cbs.remove = _gpgme_remove_io_cb;
+ io_cbs.event = _gpgme_wait_private_event_cb;
+ io_cbs.event_priv = ctx;
+ }
+ else if (! ctx->io_cbs.add)
+ {
+ /* Use global event loop. */
+ io_cbs.add = _gpgme_add_io_cb;
+ io_cbs.add_priv = ctx;
+ io_cbs.remove = _gpgme_remove_io_cb;
+ io_cbs.event = _gpgme_wait_global_event_cb;
+ io_cbs.event_priv = ctx;
+ }
+ else
+ {
+ /* Use user event loop. */
+ io_cbs.add = _gpgme_wait_user_add_io_cb;
+ io_cbs.add_priv = ctx;
+ io_cbs.remove = _gpgme_wait_user_remove_io_cb;
+ io_cbs.event = _gpgme_wait_user_event_cb;
+ io_cbs.event_priv = ctx;
+ }
+ _gpgme_engine_set_io_cbs (ctx->engine, &io_cbs);
+ return err;
+}
+
+
+/* Parse the INV_RECP or INV-SNDR status line in ARGS and return the
+ result in KEY. */
+gpgme_error_t
+_gpgme_parse_inv_recp (char *args, gpgme_invalid_key_t *key)
+{
+ gpgme_invalid_key_t inv_key;
+ char *tail;
+ long int reason;
+
+ inv_key = malloc (sizeof (*inv_key));
+ if (!inv_key)
+ return gpg_error_from_errno (errno);
+ inv_key->next = NULL;
+ gpg_err_set_errno (0);
+ reason = strtol (args, &tail, 0);
+ if (errno || args == tail || (*tail && *tail != ' '))
+ {
+ /* The crypto backend does not behave. */
+ free (inv_key);
+ return gpg_error (GPG_ERR_INV_ENGINE);
+ }
+
+ switch (reason)
+ {
+ default:
+ case 0:
+ inv_key->reason = gpg_error (GPG_ERR_GENERAL);
+ break;
+
+ case 1:
+ inv_key->reason = gpg_error (GPG_ERR_NO_PUBKEY);
+ break;
+
+ case 2:
+ inv_key->reason = gpg_error (GPG_ERR_AMBIGUOUS_NAME);
+ break;
+
+ case 3:
+ inv_key->reason = gpg_error (GPG_ERR_WRONG_KEY_USAGE);
+ break;
+
+ case 4:
+ inv_key->reason = gpg_error (GPG_ERR_CERT_REVOKED);
+ break;
+
+ case 5:
+ inv_key->reason = gpg_error (GPG_ERR_CERT_EXPIRED);
+ break;
+
+ case 6:
+ inv_key->reason = gpg_error (GPG_ERR_NO_CRL_KNOWN);
+ break;
+
+ case 7:
+ inv_key->reason = gpg_error (GPG_ERR_CRL_TOO_OLD);
+ break;
+
+ case 8:
+ inv_key->reason = gpg_error (GPG_ERR_NO_POLICY_MATCH);
+ break;
+
+ case 9:
+ inv_key->reason = gpg_error (GPG_ERR_NO_SECKEY);
+ break;
+
+ case 10:
+ inv_key->reason = gpg_error (GPG_ERR_PUBKEY_NOT_TRUSTED);
+ break;
+
+ case 11:
+ inv_key->reason = gpg_error (GPG_ERR_MISSING_CERT);
+ break;
+
+ case 12:
+ inv_key->reason = gpg_error (GPG_ERR_MISSING_ISSUER_CERT);
+ break;
+ }
+
+ while (*tail && *tail == ' ')
+ tail++;
+ if (*tail)
+ {
+ inv_key->fpr = strdup (tail);
+ if (!inv_key->fpr)
+ {
+ int saved_errno = errno;
+ free (inv_key);
+ return gpg_error_from_errno (saved_errno);
+ }
+ }
+ else
+ inv_key->fpr = NULL;
+
+ *key = inv_key;
+ return 0;
+}
+
+
+/* Parse the PLAINTEXT status line in ARGS and return the result in
+ FILENAMEP. */
+gpgme_error_t
+_gpgme_parse_plaintext (char *args, char **filenamep)
+{
+ char *tail;
+
+ while (*args == ' ')
+ args++;
+ if (*args == '\0')
+ return 0;
+
+ /* First argument is file type. */
+ while (*args != ' ' && *args != '\0')
+ args++;
+ while (*args == ' ')
+ args++;
+ if (*args == '\0')
+ return 0;
+
+ /* Second argument is the timestamp. */
+ while (*args != ' ' && *args != '\0')
+ args++;
+ while (*args == ' ')
+ args++;
+ if (*args == '\0')
+ return 0;
+
+ tail = args;
+ while (*tail != ' ' && *tail != '\0')
+ tail++;
+ *tail = '\0';
+ if (filenamep && *args != '\0')
+ {
+ char *filename = strdup (args);
+ if (!filename)
+ return gpg_error_from_syserror ();
+
+ *filenamep = filename;
+ }
+ return 0;
+}
diff --git a/src/opassuan.c b/src/opassuan.c
new file mode 100644
index 0000000..21c25ca
--- /dev/null
+++ b/src/opassuan.c
@@ -0,0 +1,230 @@
+/* opassuan.c - Low-level Assuan operations.
+ Copyright (C) 2009 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* Suppress warning for accessing deprecated member "err". */
+#define _GPGME_IN_GPGME 1
+#include "gpgme.h"
+#include "context.h"
+#include "ops.h"
+#include "util.h"
+#include "debug.h"
+
+/* LEGACY: Remove this when removing the deprecated result
+ structure. */
+typedef struct
+{
+ struct _gpgme_op_assuan_result result;
+} *op_data_t;
+
+
+static gpgme_error_t
+opassuan_start (gpgme_ctx_t ctx, int synchronous,
+ const char *command,
+ gpgme_assuan_data_cb_t data_cb,
+ void *data_cb_value,
+ gpgme_assuan_inquire_cb_t inq_cb,
+ void *inq_cb_value,
+ gpgme_assuan_status_cb_t status_cb,
+ void *status_cb_value)
+{
+ gpgme_error_t err;
+
+ if (!command || !*command)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ /* The flag value 256 is used to suppress an engine reset. This is
+ required to keep the connection running. */
+ err = _gpgme_op_reset (ctx, ((synchronous&255) | 256));
+ if (err)
+ return err;
+
+ {
+ /* LEGACY: Remove this when removing the deprecated result
+ structure. */
+ void *hook;
+ op_data_t opd;
+ err = _gpgme_op_data_lookup (ctx, OPDATA_ASSUAN, &hook,
+ sizeof (*opd), NULL);
+ if (err)
+ return err;
+ }
+
+ return _gpgme_engine_op_assuan_transact (ctx->engine, command,
+ data_cb, data_cb_value,
+ inq_cb, inq_cb_value,
+ status_cb, status_cb_value);
+}
+
+
+
+/* XXXX. This is the asynchronous variant. */
+gpgme_error_t
+gpgme_op_assuan_transact_start (gpgme_ctx_t ctx,
+ const char *command,
+ gpgme_assuan_data_cb_t data_cb,
+ void *data_cb_value,
+ gpgme_assuan_inquire_cb_t inq_cb,
+ void *inq_cb_value,
+ gpgme_assuan_status_cb_t status_cb,
+ void *status_cb_value)
+{
+ gpg_error_t err;
+
+ TRACE_BEG7 (DEBUG_CTX, "gpgme_op_assuan_transact_start", ctx,
+ "command=%s, data_cb=%p/%p, inq_cb=%p/%p, status_cb=%p/%p",
+ command, data_cb, data_cb_value, inq_cb, inq_cb_value,
+ status_cb, status_cb_value);
+
+ if (!ctx)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ err = opassuan_start (ctx, 0, command, data_cb, data_cb_value,
+ inq_cb, inq_cb_value, status_cb, status_cb_value);
+ return TRACE_ERR (err);
+}
+
+
+/* XXXX. This is the synchronous variant. */
+gpgme_error_t
+gpgme_op_assuan_transact_ext (gpgme_ctx_t ctx,
+ const char *command,
+ gpgme_assuan_data_cb_t data_cb,
+ void *data_cb_value,
+ gpgme_assuan_inquire_cb_t inq_cb,
+ void *inq_cb_value,
+ gpgme_assuan_status_cb_t status_cb,
+ void *status_cb_value,
+ gpgme_error_t *op_err_p)
+{
+ gpgme_error_t err;
+ gpgme_error_t op_err;
+
+ TRACE_BEG8 (DEBUG_CTX, "gpgme_op_assuan_transact", ctx,
+ "command=%s, data_cb=%p/%p, inq_cb=%p/%p, status_cb=%p/%p, "
+ "op_err=%p",
+ command, data_cb, data_cb_value, inq_cb, inq_cb_value,
+ status_cb, status_cb_value, op_err_p);
+
+ if (!ctx)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ err = opassuan_start (ctx, 1, command,
+ data_cb, data_cb_value,
+ inq_cb, inq_cb_value,
+ status_cb, status_cb_value);
+ if (err)
+ goto out;
+
+ err = _gpgme_wait_one_ext (ctx, &op_err);
+ if (op_err)
+ {
+ TRACE_LOG2 ("op_err = %s <%s>", gpgme_strerror (op_err),
+ gpgme_strsource (op_err));
+ if (! op_err_p)
+ {
+ TRACE_LOG ("warning: operational error ignored by user");
+ }
+ }
+ if (op_err_p)
+ *op_err_p = op_err;
+
+ out:
+ return TRACE_ERR (err);
+}
+
+
+
+
+/* Compatibility code for old interface. */
+
+/* Evil hack breaking abstractions for the purpose of localizing our
+ other hack. This is copied from engine.c. */
+struct engine
+{
+ struct engine_ops *ops;
+ void *engine;
+};
+
+gpg_error_t _gpgme_engine_assuan_last_op_err (void *engine);
+
+gpgme_assuan_result_t
+gpgme_op_assuan_result (gpgme_ctx_t ctx)
+{
+ gpgme_error_t err;
+ void *hook;
+ op_data_t opd;
+
+ TRACE_BEG (DEBUG_CTX, "gpgme_op_assuan_result", ctx);
+
+ err = _gpgme_op_data_lookup (ctx, OPDATA_ASSUAN, &hook, -1, NULL);
+ opd = hook;
+ /* Check in case this function is used without having run a command
+ before. */
+ if (err || !opd)
+ {
+ TRACE_SUC0 ("result=(null)");
+ return NULL;
+ }
+
+ /* All of this is a hack for the old style interface. The new style
+ interface returns op errors directly. */
+ opd->result.err = _gpgme_engine_assuan_last_op_err (ctx->engine->engine);
+ if (opd->result.err)
+ {
+ TRACE_LOG1 ("err = %s", gpg_strerror (0));
+ }
+ else
+ {
+ TRACE_LOG2 ("err = %s <%s>", gpg_strerror (opd->result.err),
+ gpg_strsource (opd->result.err));
+ }
+
+ TRACE_SUC1 ("result=%p", &opd->result);
+ return &opd->result;
+}
+
+
+gpgme_error_t
+gpgme_op_assuan_transact (gpgme_ctx_t ctx,
+ const char *command,
+ gpgme_assuan_data_cb_t data_cb,
+ void *data_cb_value,
+ gpgme_assuan_inquire_cb_t inq_cb,
+ void *inq_cb_value,
+ gpgme_assuan_status_cb_t status_cb,
+ void *status_cb_value)
+{
+ gpgme_error_t err;
+
+ TRACE (DEBUG_CTX, "gpgme_op_assuan_transact", ctx);
+
+ if (!ctx)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ /* Users of the old-style session based interfaces need to look at
+ the result structure. */
+ err = gpgme_op_assuan_transact_ext (ctx, command, data_cb, data_cb_value,
+ inq_cb, inq_cb_value,
+ status_cb, status_cb_value, NULL);
+ return err;
+}
diff --git a/src/ops.h b/src/ops.h
new file mode 100644
index 0000000..7b3025b
--- /dev/null
+++ b/src/ops.h
@@ -0,0 +1,179 @@
+/* ops.h - Internal operation support.
+ Copyright (C) 2000 Werner Koch (dd9jn)
+ Copyright (C) 2001, 2002, 2003, 2004, 2005 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#ifndef OPS_H
+#define OPS_H
+
+#include "gpgme.h"
+#include "context.h"
+
+
+/* From gpgme.c. */
+gpgme_error_t _gpgme_cancel_with_err (gpgme_ctx_t ctx, gpg_error_t ctx_err,
+ gpg_error_t op_err);
+/* Clear all notation data from the context. */
+void _gpgme_sig_notation_clear (gpgme_ctx_t ctx);
+
+void _gpgme_release_result (gpgme_ctx_t ctx);
+
+
+/* From wait.c. */
+gpgme_error_t _gpgme_wait_one (gpgme_ctx_t ctx);
+gpgme_error_t _gpgme_wait_one_ext (gpgme_ctx_t ctx, gpgme_error_t *op_err);
+gpgme_error_t _gpgme_wait_on_condition (gpgme_ctx_t ctx, volatile int *cond,
+ gpgme_error_t *op_err);
+
+
+/* From data.c. */
+gpgme_error_t _gpgme_data_inbound_handler (void *opaque, int fd);
+gpgme_error_t _gpgme_data_outbound_handler (void *opaque, int fd);
+
+
+/* From op-support.c. */
+
+/* Find or create the op data object of type TYPE. */
+gpgme_error_t _gpgme_op_data_lookup (gpgme_ctx_t ctx, ctx_op_data_id_t type,
+ void **hook, int size,
+ void (*cleanup) (void *));
+
+/* Prepare a new operation on CTX. */
+gpgme_error_t _gpgme_op_reset (gpgme_ctx_t ctx, int synchronous);
+
+/* Parse the INV_RECP status line in ARGS and return the result in
+ KEY. */
+gpgme_error_t _gpgme_parse_inv_recp (char *args, 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);
+
+
+
+/* From verify.c. */
+gpgme_error_t _gpgme_op_verify_init_result (gpgme_ctx_t ctx);
+gpgme_error_t _gpgme_verify_status_handler (void *priv,
+ gpgme_status_code_t code,
+ char *args);
+
+
+/* From decrypt.c. */
+gpgme_error_t _gpgme_op_decrypt_init_result (gpgme_ctx_t ctx);
+gpgme_error_t _gpgme_decrypt_status_handler (void *priv,
+ gpgme_status_code_t code,
+ char *args);
+
+
+/* From signers.c. */
+void _gpgme_signers_clear (gpgme_ctx_t ctx);
+
+/* From sign.c. */
+
+/* Create an initial op data object for signing. Needs to be called
+ once before calling _gpgme_sign_status_handler. */
+gpgme_error_t _gpgme_op_sign_init_result (gpgme_ctx_t ctx);
+
+/* Process a status line for signing operations. */
+gpgme_error_t _gpgme_sign_status_handler (void *priv,
+ gpgme_status_code_t code,
+ char *args);
+
+
+/* From encrypt.c. */
+
+/* Create an initial op data object for encrypt. Needs to be called
+ once before calling _gpgme_encrypt_status_handler. */
+gpgme_error_t _gpgme_op_encrypt_init_result (gpgme_ctx_t ctx);
+
+/* Process a status line for encryption operations. */
+gpgme_error_t _gpgme_encrypt_status_handler (void *priv,
+ gpgme_status_code_t code,
+ char *args);
+
+
+/* From passphrase.c. */
+gpgme_error_t _gpgme_passphrase_status_handler (void *priv,
+ gpgme_status_code_t code,
+ char *args);
+gpgme_error_t _gpgme_passphrase_command_handler (void *opaque,
+ gpgme_status_code_t code,
+ const char *key, int fd,
+ int *processed);
+
+
+/* From progress.c. */
+gpgme_error_t _gpgme_progress_status_handler (void *priv,
+ gpgme_status_code_t code,
+ char *args);
+
+
+/* From key.c. */
+gpgme_error_t _gpgme_key_new (gpgme_key_t *r_key);
+gpgme_error_t _gpgme_key_add_subkey (gpgme_key_t key,
+ gpgme_subkey_t *r_subkey);
+gpgme_error_t _gpgme_key_append_name (gpgme_key_t key, const char *src, int convert);
+gpgme_key_sig_t _gpgme_key_add_sig (gpgme_key_t key, char *src);
+
+
+/* From keylist.c. */
+void _gpgme_op_keylist_event_cb (void *data, gpgme_event_io_t type,
+ void *type_data);
+
+
+/* From trust-item.c. */
+
+/* Create a new trust item. */
+gpgme_error_t _gpgme_trust_item_new (gpgme_trust_item_t *r_item);
+
+
+/* From trustlist.c. */
+void _gpgme_op_trustlist_event_cb (void *data, gpgme_event_io_t type,
+ void *type_data);
+
+
+/* From version.c. */
+
+/* Return true if MY_VERSION is at least REQ_VERSION, and false
+ otherwise. */
+int _gpgme_compare_versions (const char *my_version,
+ const char *req_version);
+char *_gpgme_get_program_version (const char *const path);
+
+
+/* From sig-notation.c. */
+
+/* Create a new, empty signature notation data object. */
+gpgme_error_t _gpgme_sig_notation_create (gpgme_sig_notation_t *notationp,
+ const char *name, int name_len,
+ const char *value, int value_len,
+ gpgme_sig_notation_flags_t flags);
+
+/* Free the signature notation object and all associated resources.
+ The object must already be removed from any linked list as the next
+ pointer is ignored. */
+void _gpgme_sig_notation_free (gpgme_sig_notation_t notation);
+
+/* Parse a notation or policy URL subpacket. If the packet type is
+ not known, return no error but NULL in NOTATION. */
+gpgme_error_t _gpgme_parse_notation (gpgme_sig_notation_t *notationp,
+ int type, int pkflags, int len,
+ char *data);
+
+#endif /* OPS_H */
diff --git a/src/passphrase.c b/src/passphrase.c
new file mode 100644
index 0000000..c36b6ae
--- /dev/null
+++ b/src/passphrase.c
@@ -0,0 +1,154 @@
+/* passphrase.c - Passphrase callback.
+ Copyright (C) 2000 Werner Koch (dd9jn)
+ Copyright (C) 2001, 2002, 2003, 2004, 2005 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+
+#include "gpgme.h"
+#include "context.h"
+#include "ops.h"
+#include "util.h"
+
+
+typedef struct
+{
+ int no_passphrase;
+ char *uid_hint;
+ char *passphrase_info;
+ int bad_passphrase;
+} *op_data_t;
+
+
+static void
+release_op_data (void *hook)
+{
+ op_data_t opd = (op_data_t) hook;
+
+ if (opd->passphrase_info)
+ free (opd->passphrase_info);
+ if (opd->uid_hint)
+ free (opd->uid_hint);
+}
+
+
+gpgme_error_t
+_gpgme_passphrase_status_handler (void *priv, gpgme_status_code_t code,
+ char *args)
+{
+ gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
+ gpgme_error_t err;
+ void *hook;
+ op_data_t opd;
+
+ err = _gpgme_op_data_lookup (ctx, OPDATA_PASSPHRASE, &hook,
+ sizeof (*opd), release_op_data);
+ opd = hook;
+ if (err)
+ return err;
+
+ switch (code)
+ {
+ case GPGME_STATUS_USERID_HINT:
+ if (opd->uid_hint)
+ free (opd->uid_hint);
+ if (!(opd->uid_hint = strdup (args)))
+ return gpg_error_from_errno (errno);
+ break;
+
+ case GPGME_STATUS_BAD_PASSPHRASE:
+ opd->bad_passphrase++;
+ opd->no_passphrase = 0;
+ break;
+
+ case GPGME_STATUS_GOOD_PASSPHRASE:
+ opd->bad_passphrase = 0;
+ opd->no_passphrase = 0;
+ break;
+
+ case GPGME_STATUS_NEED_PASSPHRASE:
+ case GPGME_STATUS_NEED_PASSPHRASE_SYM:
+ case GPGME_STATUS_NEED_PASSPHRASE_PIN:
+ if (opd->passphrase_info)
+ free (opd->passphrase_info);
+ opd->passphrase_info = strdup (args);
+ if (!opd->passphrase_info)
+ return gpg_error_from_errno (errno);
+ break;
+
+ case GPGME_STATUS_MISSING_PASSPHRASE:
+ opd->no_passphrase = 1;
+ break;
+
+ case GPGME_STATUS_EOF:
+ if (opd->no_passphrase || opd->bad_passphrase)
+ return gpg_error (GPG_ERR_BAD_PASSPHRASE);
+ break;
+
+ default:
+ /* Ignore all other codes. */
+ break;
+ }
+ return 0;
+}
+
+
+gpgme_error_t
+_gpgme_passphrase_command_handler (void *priv, gpgme_status_code_t code,
+ const char *key, int fd, int *processed)
+{
+ gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
+ gpgme_error_t err;
+ void *hook;
+ op_data_t opd;
+
+ assert (ctx->passphrase_cb);
+
+ err = _gpgme_op_data_lookup (ctx, OPDATA_PASSPHRASE, &hook,
+ sizeof (*opd), release_op_data);
+ opd = hook;
+ if (err)
+ return err;
+
+ if (code == GPGME_STATUS_GET_HIDDEN
+ && (!strcmp (key, "passphrase.enter")
+ || !strcmp (key, "passphrase.pin.ask")))
+ {
+ if (processed)
+ *processed = 1;
+
+ err = ctx->passphrase_cb (ctx->passphrase_cb_value,
+ opd->uid_hint, opd->passphrase_info,
+ opd->bad_passphrase, fd);
+
+ /* Reset bad passphrase flag, in case it is correct now. */
+ opd->bad_passphrase = 0;
+
+ return err;
+ }
+
+ return 0;
+}
diff --git a/src/passwd.c b/src/passwd.c
new file mode 100644
index 0000000..97b6960
--- /dev/null
+++ b/src/passwd.c
@@ -0,0 +1,187 @@
+/* passwd.c - Passphrase changing function
+ Copyright (C) 2010 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdlib.h>
+
+#include "gpgme.h"
+#include "debug.h"
+#include "context.h"
+#include "ops.h"
+
+
+typedef struct
+{
+ int success_seen;
+ int error_seen;
+} *op_data_t;
+
+
+
+/* Parse an error status line and return the error code. */
+static gpgme_error_t
+parse_error (char *args)
+{
+ gpgme_error_t err;
+ char *where = strchr (args, ' ');
+ char *which;
+
+ if (where)
+ {
+ *where = '\0';
+ which = where + 1;
+
+ where = strchr (which, ' ');
+ if (where)
+ *where = '\0';
+
+ where = args;
+ }
+ else
+ return gpg_error (GPG_ERR_INV_ENGINE);
+
+ err = atoi (which);
+
+ if (!strcmp (where, "keyedit.passwd"))
+ return err;
+
+ return 0;
+}
+
+
+static gpgme_error_t
+passwd_status_handler (void *priv, gpgme_status_code_t code, char *args)
+{
+ gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
+ gpgme_error_t err;
+ void *hook;
+ op_data_t opd;
+
+ err = _gpgme_op_data_lookup (ctx, OPDATA_PASSWD, &hook, -1, NULL);
+ opd = hook;
+ if (err)
+ return err;
+
+ switch (code)
+ {
+ case GPGME_STATUS_ERROR:
+ err = parse_error (args);
+ if (err)
+ opd->error_seen = 1;
+ break;
+
+ case GPGME_STATUS_SUCCESS:
+ opd->success_seen = 1;
+ break;
+
+ case GPGME_STATUS_EOF:
+ /* In case the OpenPGP engine does not properly implement the
+ passwd command we won't get a success status back and thus we
+ conclude that this operation is not supported. This is for
+ example the case for GnuPG < 2.0.16. Note that this test is
+ obsolete for assuan based engines because they will properly
+ return an error for an unknown command. */
+ if (ctx->protocol == GPGME_PROTOCOL_OpenPGP
+ && !opd->error_seen && !opd->success_seen)
+ err = gpg_error (GPG_ERR_NOT_SUPPORTED);
+ break;
+
+ default:
+ break;
+ }
+
+ return err;
+}
+
+
+static gpgme_error_t
+passwd_start (gpgme_ctx_t ctx, int synchronous, gpgme_key_t key,
+ unsigned int flags)
+{
+ gpgme_error_t err;
+ void *hook;
+ op_data_t opd;
+
+ if (!key)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (flags)
+ return gpg_error (GPG_ERR_INV_FLAG);
+
+ err = _gpgme_op_reset (ctx, synchronous);
+ if (err)
+ return err;
+
+ err = _gpgme_op_data_lookup (ctx, OPDATA_PASSWD, &hook, sizeof (*opd), NULL);
+ opd = hook;
+ if (err)
+ return err;
+
+ opd->success_seen = 0;
+ opd->error_seen = 0;
+
+ _gpgme_engine_set_status_handler (ctx->engine, passwd_status_handler, ctx);
+
+ return _gpgme_engine_op_passwd (ctx->engine, key, flags);
+}
+
+
+
+/* Change the passphrase for KEY. FLAGS is reserved for future use
+ and must be passed as 0. The engine is expected to present a user
+ interface to enter the old and the new passphrase. This is the
+ asynchronous variant.
+
+ Note that if ever the need arises to supply a passphrase we can do
+ this with a flag value and the passphrase callback feature. */
+gpgme_error_t
+gpgme_op_passwd_start (gpgme_ctx_t ctx, gpgme_key_t key, unsigned int flags)
+{
+ gpg_error_t err;
+ TRACE_BEG2 (DEBUG_CTX, "gpgme_op_passwd_start", ctx,
+ "key=%p, flags=0x%x", key, flags);
+
+ if (!ctx)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ err = passwd_start (ctx, 0, key, flags);
+ return TRACE_ERR (err);
+}
+
+
+/* Change the passphrase for KEY. FLAGS is reserved for future use
+ and must be passed as 0. This is the synchronous variant. */
+gpgme_error_t
+gpgme_op_passwd (gpgme_ctx_t ctx, gpgme_key_t key, unsigned int flags)
+{
+ gpgme_error_t err;
+
+ TRACE_BEG2 (DEBUG_CTX, "gpgme_op_passwd", ctx,
+ "key=%p, flags=0x%x", key, flags);
+
+ if (!ctx)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ err = passwd_start (ctx, 1, key, flags);
+ if (!err)
+ err = _gpgme_wait_one (ctx);
+ return TRACE_ERR (err);
+}
+
diff --git a/src/posix-io.c b/src/posix-io.c
new file mode 100644
index 0000000..0278217
--- /dev/null
+++ b/src/posix-io.c
@@ -0,0 +1,750 @@
+/* posix-io.c - Posix I/O functions
+ Copyright (C) 2000 Werner Koch (dd9jn)
+ Copyright (C) 2001, 2002, 2004, 2005, 2007, 2010 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <signal.h>
+#include <fcntl.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#include <sys/wait.h>
+#ifdef HAVE_SYS_UIO_H
+# include <sys/uio.h>
+#endif
+#include <ctype.h>
+#include <sys/resource.h>
+
+#include "util.h"
+#include "priv-io.h"
+#include "sema.h"
+#include "ath.h"
+#include "debug.h"
+
+
+void
+_gpgme_io_subsystem_init (void)
+{
+ struct sigaction act;
+
+ sigaction (SIGPIPE, NULL, &act);
+ if (act.sa_handler == SIG_DFL)
+ {
+ act.sa_handler = SIG_IGN;
+ sigemptyset (&act.sa_mask);
+ act.sa_flags = 0;
+ sigaction (SIGPIPE, &act, NULL);
+ }
+}
+
+
+/* Write the printable version of FD to the buffer BUF of length
+ BUFLEN. The printable version is the representation on the command
+ line that the child process expects. */
+int
+_gpgme_io_fd2str (char *buf, int buflen, int fd)
+{
+ return snprintf (buf, buflen, "%d", fd);
+}
+
+
+/* The table to hold notification handlers. We use a linear search
+ and extend the table as needed. */
+struct notify_table_item_s
+{
+ int fd; /* -1 indicates an unused entry. */
+ _gpgme_close_notify_handler_t handler;
+ void *value;
+};
+typedef struct notify_table_item_s *notify_table_item_t;
+
+static notify_table_item_t notify_table;
+static size_t notify_table_size;
+DEFINE_STATIC_LOCK (notify_table_lock);
+
+
+
+int
+_gpgme_io_read (int fd, void *buffer, size_t count)
+{
+ int nread;
+ TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_read", fd,
+ "buffer=%p, count=%u", buffer, count);
+
+ do
+ {
+ nread = _gpgme_ath_read (fd, buffer, count);
+ }
+ while (nread == -1 && errno == EINTR);
+
+ TRACE_LOGBUF (buffer, nread);
+ return TRACE_SYSRES (nread);
+}
+
+
+int
+_gpgme_io_write (int fd, const void *buffer, size_t count)
+{
+ int nwritten;
+ TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_write", fd,
+ "buffer=%p, count=%u", buffer, count);
+ TRACE_LOGBUF (buffer, count);
+
+ do
+ {
+ nwritten = _gpgme_ath_write (fd, buffer, count);
+ }
+ while (nwritten == -1 && errno == EINTR);
+
+ return TRACE_SYSRES (nwritten);
+}
+
+
+int
+_gpgme_io_pipe (int filedes[2], int inherit_idx)
+{
+ int saved_errno;
+ int err;
+ TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_pipe", filedes,
+ "inherit_idx=%i (GPGME uses it for %s)",
+ inherit_idx, inherit_idx ? "reading" : "writing");
+
+ err = pipe (filedes);
+ if (err < 0)
+ return TRACE_SYSRES (err);
+
+ /* FIXME: Should get the old flags first. */
+ err = fcntl (filedes[1 - inherit_idx], F_SETFD, FD_CLOEXEC);
+ saved_errno = errno;
+ if (err < 0)
+ {
+ close (filedes[0]);
+ close (filedes[1]);
+ }
+ errno = saved_errno;
+ if (err)
+ return TRACE_SYSRES (err);
+
+ return TRACE_SUC2 ("read=0x%x, write=0x%x", filedes[0], filedes[1]);
+}
+
+
+int
+_gpgme_io_close (int fd)
+{
+ int res;
+ _gpgme_close_notify_handler_t handler = NULL;
+ void *handler_value;
+ int idx;
+
+ TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_close", fd);
+
+ if (fd == -1)
+ {
+ errno = EINVAL;
+ return TRACE_SYSRES (-1);
+ }
+
+ /* First call the notify handler. */
+ LOCK (notify_table_lock);
+ for (idx=0; idx < notify_table_size; idx++)
+ {
+ if (notify_table[idx].fd == fd)
+ {
+ handler = notify_table[idx].handler;
+ handler_value = notify_table[idx].value;
+ notify_table[idx].handler = NULL;
+ notify_table[idx].value = NULL;
+ notify_table[idx].fd = -1; /* Mark slot as free. */
+ break;
+ }
+ }
+ UNLOCK (notify_table_lock);
+ if (handler)
+ {
+ TRACE_LOG2 ("invoking close handler %p/%p", handler, handler_value);
+ handler (fd, handler_value);
+ }
+
+ /* Then do the close. */
+ res = close (fd);
+ return TRACE_SYSRES (res);
+}
+
+
+int
+_gpgme_io_set_close_notify (int fd, _gpgme_close_notify_handler_t handler,
+ void *value)
+{
+ int res = 0;
+ int idx;
+
+ TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_set_close_notify", fd,
+ "close_handler=%p/%p", handler, value);
+
+ assert (fd != -1);
+
+ LOCK (notify_table_lock);
+ for (idx=0; idx < notify_table_size; idx++)
+ if (notify_table[idx].fd == -1)
+ break;
+ if (idx == notify_table_size)
+ {
+ /* We need to increase the size of the table. The approach we
+ take is straightforward to minimize the risk of bugs. */
+ notify_table_item_t newtbl;
+ size_t newsize = notify_table_size + 64;
+
+ newtbl = calloc (newsize, sizeof *newtbl);
+ if (!newtbl)
+ {
+ res = -1;
+ goto leave;
+ }
+ for (idx=0; idx < notify_table_size; idx++)
+ newtbl[idx] = notify_table[idx];
+ for (; idx < newsize; idx++)
+ {
+ newtbl[idx].fd = -1;
+ newtbl[idx].handler = NULL;
+ newtbl[idx].value = NULL;
+ }
+ free (notify_table);
+ notify_table = newtbl;
+ idx = notify_table_size;
+ notify_table_size = newsize;
+ }
+ notify_table[idx].fd = fd;
+ notify_table[idx].handler = handler;
+ notify_table[idx].value = value;
+
+ leave:
+ UNLOCK (notify_table_lock);
+
+ return TRACE_SYSRES (res);
+}
+
+
+int
+_gpgme_io_set_nonblocking (int fd)
+{
+ int flags;
+ int res;
+ TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_set_nonblocking", fd);
+
+ flags = fcntl (fd, F_GETFL, 0);
+ if (flags == -1)
+ return TRACE_SYSRES (-1);
+ flags |= O_NONBLOCK;
+ res = fcntl (fd, F_SETFL, flags);
+ return TRACE_SYSRES (res);
+}
+
+
+static long int
+get_max_fds (void)
+{
+ char *source = NULL;
+ long int fds = -1;
+ int rc;
+
+#ifdef RLIMIT_NOFILE
+ {
+ struct rlimit rl;
+ rc = getrlimit (RLIMIT_NOFILE, &rl);
+ if (rc == 0)
+ {
+ source = "RLIMIT_NOFILE";
+ fds = rl.rlim_max;
+ }
+ }
+#endif
+#ifdef RLIMIT_OFILE
+ if (fds == -1)
+ {
+ struct rlimit rl;
+ rc = getrlimit (RLIMIT_OFILE, &rl);
+ if (rc == 0)
+ {
+ source = "RLIMIT_OFILE";
+ fds = rl.rlim_max;
+ }
+ }
+#endif
+#ifdef _SC_OPEN_MAX
+ if (fds == -1)
+ {
+ long int scres;
+ scres = sysconf (_SC_OPEN_MAX);
+ if (scres >= 0)
+ {
+ source = "_SC_OPEN_MAX";
+ return scres;
+ }
+ }
+#endif
+#ifdef OPEN_MAX
+ if (fds == -1)
+ {
+ source = "OPEN_MAX";
+ fds = OPEN_MAX;
+ }
+#endif
+
+#if !defined(RLIMIT_NOFILE) && !defined(RLIMIT_OFILE) \
+ && !defined(_SC_OPEN_MAX) && !defined(OPEN_MAX)
+#warning "No known way to get the maximum number of file descriptors."
+#endif
+ if (fds == -1)
+ {
+ source = "arbitrary";
+ /* Arbitrary limit. */
+ fds = 1024;
+ }
+
+ TRACE2 (DEBUG_SYSIO, "gpgme:max_fds", 0, "max fds=%i (%s)", fds, source);
+ return fds;
+}
+
+
+int
+_gpgme_io_waitpid (int pid, int hang, int *r_status, int *r_signal)
+{
+ int status;
+
+ *r_status = 0;
+ *r_signal = 0;
+ if (_gpgme_ath_waitpid (pid, &status, hang? 0 : WNOHANG) == pid)
+ {
+ if (WIFSIGNALED (status))
+ {
+ *r_status = 4; /* Need some value here. */
+ *r_signal = WTERMSIG (status);
+ }
+ else if (WIFEXITED (status))
+ *r_status = WEXITSTATUS (status);
+ else
+ *r_status = 4; /* Oops. */
+ return 1;
+ }
+ return 0;
+}
+
+
+/* Returns 0 on success, -1 on error. */
+int
+_gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags,
+ struct spawn_fd_item_s *fd_list,
+ void (*atfork) (void *opaque, int reserved),
+ void *atforkvalue, pid_t *r_pid)
+{
+ pid_t pid;
+ int i;
+ int status;
+ int signo;
+
+ (void)flags;
+
+ TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_spawn", path,
+ "path=%s", path);
+ i = 0;
+ while (argv[i])
+ {
+ TRACE_LOG2 ("argv[%2i] = %s", i, argv[i]);
+ i++;
+ }
+ for (i = 0; fd_list[i].fd != -1; i++)
+ if (fd_list[i].dup_to == -1)
+ TRACE_LOG2 ("fd[%i] = 0x%x", i, fd_list[i].fd);
+ else
+ TRACE_LOG3 ("fd[%i] = 0x%x -> 0x%x", i, fd_list[i].fd, fd_list[i].dup_to);
+
+ pid = fork ();
+ if (pid == -1)
+ return TRACE_SYSRES (-1);
+
+ if (!pid)
+ {
+ /* Intermediate child to prevent zombie processes. */
+ if ((pid = fork ()) == 0)
+ {
+ int max_fds = get_max_fds ();
+ int fd;
+
+ /* Child. */
+ int seen_stdin = 0;
+ int seen_stderr = 0;
+
+ if (atfork)
+ atfork (atforkvalue, 0);
+
+ /* First close all fds which will not be inherited. */
+ for (fd = 0; fd < max_fds; fd++)
+ {
+ for (i = 0; fd_list[i].fd != -1; i++)
+ if (fd_list[i].fd == fd)
+ break;
+ if (fd_list[i].fd == -1)
+ close (fd);
+ }
+
+ /* And now dup and close those to be duplicated. */
+ for (i = 0; fd_list[i].fd != -1; i++)
+ {
+ int child_fd;
+ int res;
+
+ if (fd_list[i].dup_to != -1)
+ child_fd = fd_list[i].dup_to;
+ else
+ child_fd = fd_list[i].fd;
+
+ if (child_fd == 0)
+ seen_stdin = 1;
+ else if (child_fd == 2)
+ seen_stderr = 1;
+
+ if (fd_list[i].dup_to == -1)
+ continue;
+
+ res = dup2 (fd_list[i].fd, fd_list[i].dup_to);
+ if (res < 0)
+ {
+#if 0
+ /* FIXME: The debug file descriptor is not
+ dup'ed anyway, so we can't see this. */
+ TRACE_LOG1 ("dup2 failed in child: %s\n",
+ strerror (errno));
+#endif
+ _exit (8);
+ }
+
+ close (fd_list[i].fd);
+ }
+
+ if (! seen_stdin || ! seen_stderr)
+ {
+ fd = open ("/dev/null", O_RDWR);
+ if (fd == -1)
+ {
+#if 0
+ /* FIXME: The debug file descriptor is not dup'ed
+ anyway, so we can't see this. */
+ TRACE_LOG1 ("can't open `/dev/null': %s\n",
+ strerror (errno));
+#endif
+ _exit (8);
+ }
+ /* Make sure that the process has a connected stdin. */
+ if (! seen_stdin && fd != 0)
+ {
+ if (dup2 (fd, 0) == -1)
+ {
+#if 0
+ /* FIXME: The debug file descriptor is not dup'ed
+ anyway, so we can't see this. */
+ TRACE_LOG1 ("dup2(/dev/null, 0) failed: %s\n",
+ strerror (errno));
+#endif
+ _exit (8);
+ }
+ }
+ if (! seen_stderr && fd != 2)
+ if (dup2 (fd, 2) == -1)
+ {
+#if 0
+ /* FIXME: The debug file descriptor is not dup'ed
+ anyway, so we can't see this. */
+ TRACE_LOG1 ("dup2(dev/null, 2) failed: %s\n",
+ strerror (errno));
+#endif
+ _exit (8);
+ }
+ if (fd != 0 && fd != 2)
+ close (fd);
+ }
+
+ execv (path, (char *const *) argv);
+ /* Hmm: in that case we could write a special status code to the
+ status-pipe. */
+#if 0
+ /* FIXME: The debug file descriptor is not dup'ed anyway, so
+ we can't see this. */
+ TRACE_LOG1 ("exec of `%s' failed\n", path);
+#endif
+ _exit (8);
+ /* End child. */
+ }
+ if (pid == -1)
+ _exit (1);
+ else
+ _exit (0);
+ }
+
+ TRACE_LOG1 ("waiting for child process pid=%i", pid);
+ _gpgme_io_waitpid (pid, 1, &status, &signo);
+ if (status)
+ return TRACE_SYSRES (-1);
+
+ for (i = 0; fd_list[i].fd != -1; i++)
+ {
+ if (! (flags & IOSPAWN_FLAG_NOCLOSE))
+ _gpgme_io_close (fd_list[i].fd);
+ /* No handle translation. */
+ fd_list[i].peer_name = fd_list[i].fd;
+ }
+
+ if (r_pid)
+ *r_pid = pid;
+
+ return TRACE_SYSRES (0);
+}
+
+
+/* Select on the list of fds. Returns: -1 = error, 0 = timeout or
+ nothing to select, > 0 = number of signaled fds. */
+int
+_gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
+{
+ fd_set readfds;
+ fd_set writefds;
+ unsigned int i;
+ int any;
+ int max_fd;
+ int n;
+ int count;
+ /* Use a 1s timeout. */
+ struct timeval timeout = { 1, 0 };
+ void *dbg_help = NULL;
+ TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_select", fds,
+ "nfds=%u, nonblock=%u", nfds, nonblock);
+
+ FD_ZERO (&readfds);
+ FD_ZERO (&writefds);
+ max_fd = 0;
+ if (nonblock)
+ timeout.tv_sec = 0;
+
+ TRACE_SEQ (dbg_help, "select on [ ");
+
+ any = 0;
+ for (i = 0; i < nfds; i++)
+ {
+ if (fds[i].fd == -1)
+ continue;
+ if (fds[i].for_read)
+ {
+ assert (!FD_ISSET (fds[i].fd, &readfds));
+ FD_SET (fds[i].fd, &readfds);
+ if (fds[i].fd > max_fd)
+ max_fd = fds[i].fd;
+ TRACE_ADD1 (dbg_help, "r0x%x ", fds[i].fd);
+ any = 1;
+ }
+ else if (fds[i].for_write)
+ {
+ assert (!FD_ISSET (fds[i].fd, &writefds));
+ FD_SET (fds[i].fd, &writefds);
+ if (fds[i].fd > max_fd)
+ max_fd = fds[i].fd;
+ TRACE_ADD1 (dbg_help, "w0x%x ", fds[i].fd);
+ any = 1;
+ }
+ fds[i].signaled = 0;
+ }
+ TRACE_END (dbg_help, "]");
+ if (!any)
+ return TRACE_SYSRES (0);
+
+ do
+ {
+ count = _gpgme_ath_select (max_fd + 1, &readfds, &writefds, NULL,
+ &timeout);
+ }
+ while (count < 0 && errno == EINTR);
+ if (count < 0)
+ return TRACE_SYSRES (-1);
+
+ TRACE_SEQ (dbg_help, "select OK [ ");
+ if (TRACE_ENABLED (dbg_help))
+ {
+ for (i = 0; i <= max_fd; i++)
+ {
+ if (FD_ISSET (i, &readfds))
+ TRACE_ADD1 (dbg_help, "r0x%x ", i);
+ if (FD_ISSET (i, &writefds))
+ TRACE_ADD1 (dbg_help, "w0x%x ", i);
+ }
+ TRACE_END (dbg_help, "]");
+ }
+
+ /* The variable N is used to optimize it a little bit. */
+ for (n = count, i = 0; i < nfds && n; i++)
+ {
+ if (fds[i].fd == -1)
+ ;
+ else if (fds[i].for_read)
+ {
+ if (FD_ISSET (fds[i].fd, &readfds))
+ {
+ fds[i].signaled = 1;
+ n--;
+ }
+ }
+ else if (fds[i].for_write)
+ {
+ if (FD_ISSET (fds[i].fd, &writefds))
+ {
+ fds[i].signaled = 1;
+ n--;
+ }
+ }
+ }
+ return TRACE_SYSRES (count);
+}
+
+
+int
+_gpgme_io_recvmsg (int fd, struct msghdr *msg, int flags)
+{
+ int nread;
+ int saved_errno;
+ struct iovec *iov;
+ TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_recvmsg", fd,
+ "msg=%p, flags=%i", msg, flags);
+
+ nread = 0;
+ iov = msg->msg_iov;
+ while (iov < msg->msg_iov + msg->msg_iovlen)
+ {
+ nread += iov->iov_len;
+ iov++;
+ }
+
+ TRACE_LOG1 ("about to receive %d bytes", nread);
+
+ do
+ {
+ nread = _gpgme_ath_recvmsg (fd, msg, flags);
+ }
+ while (nread == -1 && errno == EINTR);
+ saved_errno = errno;
+ if (nread > 0)
+ {
+ int nr = nread;
+
+ iov = msg->msg_iov;
+ while (nr > 0)
+ {
+ int len = nr > iov->iov_len ? iov->iov_len : nr;
+ TRACE_LOGBUF (msg->msg_iov->iov_base, len);
+ iov++;
+ nr -= len;
+ }
+ }
+ errno = saved_errno;
+ return TRACE_SYSRES (nread);
+}
+
+
+int
+_gpgme_io_sendmsg (int fd, const struct msghdr *msg, int flags)
+{
+ int nwritten;
+ struct iovec *iov;
+ TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_sendmsg", fd,
+ "msg=%p, flags=%i", msg, flags);
+
+ nwritten = 0;
+ iov = msg->msg_iov;
+ while (iov < msg->msg_iov + msg->msg_iovlen)
+ {
+ nwritten += iov->iov_len;
+ iov++;
+ }
+
+ TRACE_LOG1 ("about to receive %d bytes", nwritten);
+ iov = msg->msg_iov;
+ while (nwritten > 0)
+ {
+ int len = nwritten > iov->iov_len ? iov->iov_len : nwritten;
+ TRACE_LOGBUF (msg->msg_iov->iov_base, len);
+ iov++;
+ nwritten -= len;
+ }
+
+ do
+ {
+ nwritten = _gpgme_ath_sendmsg (fd, msg, flags);
+ }
+ while (nwritten == -1 && errno == EINTR);
+ return TRACE_SYSRES (nwritten);
+}
+
+
+int
+_gpgme_io_dup (int fd)
+{
+ int new_fd = dup (fd);
+
+ TRACE1 (DEBUG_SYSIO, "_gpgme_io_dup", fd, "new fd==%i", new_fd);
+
+ return new_fd;
+}
+
+
+int
+_gpgme_io_socket (int domain, int type, int proto)
+{
+ int res;
+
+ TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_socket", domain,
+ "type=%i, proto=%i", type, proto);
+
+ res = socket (domain, type, proto);
+
+ return TRACE_SYSRES (res);
+}
+
+
+int
+_gpgme_io_connect (int fd, struct sockaddr *addr, int addrlen)
+{
+ int res;
+
+ TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_connect", fd,
+ "addr=%p, addrlen=%i", addr, addrlen);
+
+ res = ath_connect (fd, addr, addrlen);
+
+ return TRACE_SYSRES (res);
+}
diff --git a/src/posix-sema.c b/src/posix-sema.c
new file mode 100644
index 0000000..b1808fd
--- /dev/null
+++ b/src/posix-sema.c
@@ -0,0 +1,68 @@
+/* posix-sema.c
+ Copyright (C) 2001 Werner Koch (dd9jn)
+ Copyright (C) 2001, 2002, 2004, 2007 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <signal.h>
+#include <fcntl.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+#include "util.h"
+#include "sema.h"
+#include "ath.h"
+
+void
+_gpgme_sema_subsystem_init ()
+{
+}
+
+void
+_gpgme_sema_cs_enter (struct critsect_s *s)
+{
+ _gpgme_ath_mutex_lock (&s->priv);
+}
+
+void
+_gpgme_sema_cs_leave (struct critsect_s *s)
+{
+ _gpgme_ath_mutex_unlock (&s->priv);
+}
+
+void
+_gpgme_sema_cs_destroy (struct critsect_s *s)
+{
+ _gpgme_ath_mutex_destroy (&s->priv);
+ s->priv = NULL;
+}
diff --git a/src/posix-util.c b/src/posix-util.c
new file mode 100644
index 0000000..d7ed8f0
--- /dev/null
+++ b/src/posix-util.c
@@ -0,0 +1,108 @@
+/* posix-util.c - Utility functions for Posix
+ Copyright (C) 2001 Werner Koch (dd9jn)
+ Copyright (C) 2001, 2002, 2004 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "util.h"
+
+const char *
+_gpgme_get_gpg_path (void)
+{
+#ifdef GPG_PATH
+ return GPG_PATH;
+#else
+ return NULL;
+#endif
+}
+
+const char *
+_gpgme_get_gpgsm_path (void)
+{
+#ifdef GPGSM_PATH
+ return GPGSM_PATH;
+#else
+ return NULL;
+#endif
+}
+
+const char *
+_gpgme_get_gpgconf_path (void)
+{
+#ifdef GPGCONF_PATH
+ return GPGCONF_PATH;
+#else
+ return NULL;
+#endif
+}
+
+const char *
+_gpgme_get_g13_path (void)
+{
+#ifdef G13_PATH
+ return G13_PATH;
+#else
+ return NULL;
+#endif
+}
+
+
+const char *
+_gpgme_get_uiserver_socket_path (void)
+{
+ static char *socket_path;
+ const char *homedir;
+ const char name[] = "S.uiserver";
+
+ if (socket_path)
+ return socket_path;
+
+ homedir = _gpgme_get_default_homedir ();
+ if (! homedir)
+ return NULL;
+
+ socket_path = malloc (strlen (homedir) + 1 + strlen (name) + 1);
+ if (! socket_path)
+ return NULL;
+
+ strcpy (stpcpy (stpcpy (socket_path, homedir), "/"), name);
+ return socket_path;
+}
+
+
+/* See w32-util.c */
+int
+_gpgme_get_conf_int (const char *key, int *value)
+{
+ return 0;
+}
+
+void
+_gpgme_allow_set_foreground_window (pid_t pid)
+{
+ (void)pid;
+ /* Not needed. */
+}
diff --git a/src/priv-io.h b/src/priv-io.h
new file mode 100644
index 0000000..770c061
--- /dev/null
+++ b/src/priv-io.h
@@ -0,0 +1,115 @@
+/* priv-io.h - Interface to the private I/O functions.
+ Copyright (C) 2000 Werner Koch (dd9jn)
+ Copyright (C) 2001, 2002, 2003, 2004, 2005 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#ifndef IO_H
+#define IO_H
+
+#ifdef HAVE_W32_SYSTEM
+# ifdef HAVE_W32CE_SYSTEM
+# include "w32-ce.h"
+# endif
+# include <windows.h>
+#else
+# include <sys/socket.h>
+#endif
+
+/* For pid_t. */
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+
+/* A single file descriptor passed to spawn. For child fds, dup_to
+ specifies the fd it should become in the child, but only 0, 1 and 2
+ are valid values (due to a limitation in the W32 code). As return
+ value, the PEER_NAME fields specify the name of the file
+ descriptor in the spawned process, or -1 if no change. If ARG_LOC
+ is not 0, it specifies the index in the argument vector of the
+ program which contains a numerical representation of the file
+ descriptor for translation purposes. */
+struct spawn_fd_item_s
+{
+ int fd;
+ int dup_to;
+ int peer_name;
+ int arg_loc;
+};
+
+struct io_select_fd_s
+{
+ int fd;
+ int for_read;
+ int for_write;
+ int signaled;
+ void *opaque;
+};
+
+/* These function are either defined in posix-io.c or w32-io.c. */
+void _gpgme_io_subsystem_init (void);
+int _gpgme_io_socket (int namespace, int style, int protocol);
+int _gpgme_io_connect (int fd, struct sockaddr *addr, int addrlen);
+int _gpgme_io_read (int fd, void *buffer, size_t count);
+int _gpgme_io_write (int fd, const void *buffer, size_t count);
+int _gpgme_io_pipe (int filedes[2], int inherit_idx);
+int _gpgme_io_close (int fd);
+typedef void (*_gpgme_close_notify_handler_t) (int,void*);
+int _gpgme_io_set_close_notify (int fd, _gpgme_close_notify_handler_t handler,
+ void *value);
+int _gpgme_io_set_nonblocking (int fd);
+
+/* A flag to tell the spawn function to allow the child process to set
+ the foreground window. */
+#define IOSPAWN_FLAG_ALLOW_SET_FG 1
+/* Don't close any child FDs. */
+#define IOSPAWN_FLAG_NOCLOSE 2
+
+/* Spawn the executable PATH with ARGV as arguments. After forking
+ close all fds except for those in FD_LIST in the child, then
+ optionally dup() the child fds. Finally, all fds in the list are
+ closed in the parent. */
+int _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags,
+ struct spawn_fd_item_s *fd_list,
+ void (*atfork) (void *opaque, int reserved),
+ void *atforkvalue, pid_t *r_pid);
+
+int _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock);
+
+/* Write the printable version of FD to the buffer BUF of length
+ BUFLEN. The printable version is the representation on the command
+ line that the child process expects. */
+int _gpgme_io_fd2str (char *buf, int buflen, int fd);
+
+/* Duplicate a file descriptor. This is more restrictive than dup():
+ it assumes that the resulting file descriptors are essentially
+ co-equal (for example, no private offset), which is true for pipes
+ and sockets (but not files) under Unix with the standard dup()
+ function. Basically, this function is used to reference count the
+ status output file descriptor shared between GPGME and libassuan
+ (in engine-gpgsm.c). */
+int _gpgme_io_dup (int fd);
+
+#ifndef HAVE_W32_SYSTEM
+int _gpgme_io_recvmsg (int fd, struct msghdr *msg, int flags);
+int _gpgme_io_sendmsg (int fd, const struct msghdr *msg, int flags);
+int _gpgme_io_waitpid (int pid, int hang, int *r_status, int *r_signal);
+#endif
+
+#endif /* IO_H */
diff --git a/src/progress.c b/src/progress.c
new file mode 100644
index 0000000..4fe6b63
--- /dev/null
+++ b/src/progress.c
@@ -0,0 +1,81 @@
+/* progress.c - status handler for progress status
+ Copyright (C) 2000 Werner Koch (dd9jn)
+ Copyright (C) 2001, 2002, 2003, 2004 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "util.h"
+#include "context.h"
+
+
+gpgme_error_t
+_gpgme_progress_status_handler (void *priv, gpgme_status_code_t code,
+ char *args)
+{
+ gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
+ char *p;
+ char *args_cpy;
+ int type = 0;
+ int current = 0;
+ int total = 0;
+
+ if (code != GPGME_STATUS_PROGRESS || !*args || !ctx->progress_cb)
+ return 0;
+
+ args_cpy = strdup (args);
+ if (!args_cpy)
+ return gpg_error_from_errno (errno);
+
+ p = strchr (args_cpy, ' ');
+ if (p)
+ {
+ *p++ = 0;
+ if (*p)
+ {
+ type = *(unsigned char *)p;
+ p = strchr (p+1, ' ');
+ if (p)
+ {
+ *p++ = 0;
+ if (*p)
+ {
+ current = atoi (p);
+ p = strchr (p+1, ' ');
+ if (p)
+ {
+ *p++ = 0;
+ total = atoi (p);
+ }
+ }
+ }
+ }
+ }
+
+ if (type != 'X')
+ ctx->progress_cb (ctx->progress_cb_value, args_cpy, type, current, total);
+
+ free (args_cpy);
+ return 0;
+}
diff --git a/src/sema.h b/src/sema.h
new file mode 100644
index 0000000..7d3870e
--- /dev/null
+++ b/src/sema.h
@@ -0,0 +1,67 @@
+/* sema.h - Definitions for semaphores.
+ Copyright (C) 2000 Werner Koch (dd9jn)
+ Copyright (C) 2001, 2003, 2004, 2007 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#ifndef SEMA_H
+#define SEMA_H
+
+struct critsect_s
+{
+ const char *name;
+ void *priv;
+};
+
+#define DEFINE_GLOBAL_LOCK(name) \
+ struct critsect_s name = { #name, NULL }
+#define DEFINE_STATIC_LOCK(name) \
+ static struct critsect_s name = { #name, NULL }
+
+#define DECLARE_LOCK(name) \
+ struct critsect_s name
+#define INIT_LOCK(a) \
+ do \
+ { \
+ (a).name = #a; \
+ (a).priv = NULL; \
+ } \
+ while (0)
+#define DESTROY_LOCK(name) _gpgme_sema_cs_destroy (&(name))
+
+
+#define LOCK(name) \
+ do \
+ { \
+ _gpgme_sema_cs_enter (&(name)); \
+ } \
+ while (0)
+
+#define UNLOCK(name) \
+ do \
+ { \
+ _gpgme_sema_cs_leave (&(name)); \
+ } \
+ while (0)
+
+void _gpgme_sema_subsystem_init (void);
+void _gpgme_sema_cs_enter (struct critsect_s *s);
+void _gpgme_sema_cs_leave (struct critsect_s *s);
+void _gpgme_sema_cs_destroy (struct critsect_s *s);
+
+#endif /* SEMA_H */
diff --git a/src/setenv.c b/src/setenv.c
new file mode 100644
index 0000000..d85bec9
--- /dev/null
+++ b/src/setenv.c
@@ -0,0 +1,354 @@
+/* Copyright (C) 1992,1995-2001,2004 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library 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 the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02110-1301 USA. */
+
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <string.h>
+#include <gpg-error.h>
+#define __set_errno(ev) (gpg_err_set_errno (ev))
+
+#if HAVE_ASSUAN_H
+/* Fixme: Why do we need to include the assuan header and why the
+ internal ones? */
+#include "assuan-defs.h"
+#endif /*HAVE_ASSUAN_H*/
+
+#define __builtin_expect(cond,val) (cond)
+
+#include <errno.h>
+
+#if _LIBC || HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+#if _LIBC || HAVE_STRING_H
+# include <string.h>
+#endif
+#if _LIBC || HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#if !_LIBC
+# define __environ environ
+# ifndef HAVE_ENVIRON_DECL
+extern char **environ;
+# endif
+#endif
+
+#if _LIBC
+/* This lock protects against simultaneous modifications of `environ'. */
+# include <bits/libc-lock.h>
+__libc_lock_define_initialized (static, envlock)
+# define LOCK __libc_lock_lock (envlock)
+# define UNLOCK __libc_lock_unlock (envlock)
+#else
+# define LOCK
+# define UNLOCK
+#endif
+
+/* In the GNU C library we must keep the namespace clean. */
+#ifdef _LIBC
+# define setenv __setenv
+# define unsetenv __unsetenv
+# define clearenv __clearenv
+# define tfind __tfind
+# define tsearch __tsearch
+#endif
+
+/* In the GNU C library implementation we try to be more clever and
+ allow arbitrarily many changes of the environment given that the used
+ values are from a small set. Outside glibc this will eat up all
+ memory after a while. */
+#if defined _LIBC || (defined HAVE_SEARCH_H && defined HAVE_TSEARCH \
+ && defined __GNUC__)
+# define USE_TSEARCH 1
+# include <search.h>
+
+/* This is a pointer to the root of the search tree with the known
+ values. */
+static void *known_values;
+
+# define KNOWN_VALUE(Str) \
+ ({ \
+ void *value = tfind (Str, &known_values, (__compar_fn_t) strcmp); \
+ value != NULL ? *(char **) value : NULL; \
+ })
+# define STORE_VALUE(Str) \
+ tsearch (Str, &known_values, (__compar_fn_t) strcmp)
+
+#else
+# undef USE_TSEARCH
+
+# define KNOWN_VALUE(Str) NULL
+# define STORE_VALUE(Str) do { } while (0)
+
+#endif
+
+
+/* If this variable is not a null pointer we allocated the current
+ environment. */
+static char **last_environ;
+
+
+/* This function is used by `setenv' and `putenv'. The difference between
+ the two functions is that for the former must create a new string which
+ is then placed in the environment, while the argument of `putenv'
+ must be used directly. This is all complicated by the fact that we try
+ to reuse values once generated for a `setenv' call since we can never
+ free the strings. */
+static int
+__add_to_environ (const char *name, const char *value, const char *combined,
+ int replace)
+{
+ register char **ep;
+ register size_t size;
+ const size_t namelen = strlen (name);
+ const size_t vallen = value != NULL ? strlen (value) + 1 : 0;
+
+ LOCK;
+
+ /* We have to get the pointer now that we have the lock and not earlier
+ since another thread might have created a new environment. */
+ ep = __environ;
+
+ size = 0;
+ if (ep != NULL)
+ {
+ for (; *ep != NULL; ++ep)
+ if (!strncmp (*ep, name, namelen) && (*ep)[namelen] == '=')
+ break;
+ else
+ ++size;
+ }
+
+ if (ep == NULL || __builtin_expect (*ep == NULL, 1))
+ {
+ char **new_environ;
+
+ /* We allocated this space; we can extend it. */
+ new_environ = (char **) realloc (last_environ,
+ (size + 2) * sizeof (char *));
+ if (new_environ == NULL)
+ {
+ UNLOCK;
+ return -1;
+ }
+
+ /* If the whole entry is given add it. */
+ if (combined != NULL)
+ /* We must not add the string to the search tree since it belongs
+ to the user. */
+ new_environ[size] = (char *) combined;
+ else
+ {
+ /* See whether the value is already known. */
+#ifdef USE_TSEARCH
+# ifdef __GNUC__
+ char new_value[namelen + 1 + vallen];
+# else
+ char *new_value = (char *) alloca (namelen + 1 + vallen);
+# endif
+# ifdef _LIBC
+ __mempcpy (__mempcpy (__mempcpy (new_value, name, namelen), "=", 1),
+ value, vallen);
+# else
+ memcpy (new_value, name, namelen);
+ new_value[namelen] = '=';
+ memcpy (&new_value[namelen + 1], value, vallen);
+# endif
+
+ new_environ[size] = KNOWN_VALUE (new_value);
+ if (__builtin_expect (new_environ[size] == NULL, 1))
+#endif
+ {
+ new_environ[size] = (char *) malloc (namelen + 1 + vallen);
+ if (__builtin_expect (new_environ[size] == NULL, 0))
+ {
+ __set_errno (ENOMEM);
+ UNLOCK;
+ return -1;
+ }
+
+#ifdef USE_TSEARCH
+ memcpy (new_environ[size], new_value, namelen + 1 + vallen);
+#else
+ memcpy (new_environ[size], name, namelen);
+ new_environ[size][namelen] = '=';
+ memcpy (&new_environ[size][namelen + 1], value, vallen);
+#endif
+ /* And save the value now. We cannot do this when we remove
+ the string since then we cannot decide whether it is a
+ user string or not. */
+ STORE_VALUE (new_environ[size]);
+ }
+ }
+
+ if (__environ != last_environ)
+ memcpy ((char *) new_environ, (char *) __environ,
+ size * sizeof (char *));
+
+ new_environ[size + 1] = NULL;
+
+ last_environ = __environ = new_environ;
+ }
+ else if (replace)
+ {
+ char *np;
+
+ /* Use the user string if given. */
+ if (combined != NULL)
+ np = (char *) combined;
+ else
+ {
+#ifdef USE_TSEARCH
+# ifdef __GNUC__
+ char new_value[namelen + 1 + vallen];
+# else
+ char *new_value = (char *) alloca (namelen + 1 + vallen);
+# endif
+# ifdef _LIBC
+ __mempcpy (__mempcpy (__mempcpy (new_value, name, namelen), "=", 1),
+ value, vallen);
+# else
+ memcpy (new_value, name, namelen);
+ new_value[namelen] = '=';
+ memcpy (&new_value[namelen + 1], value, vallen);
+# endif
+
+ np = KNOWN_VALUE (new_value);
+ if (__builtin_expect (np == NULL, 1))
+#endif
+ {
+ np = malloc (namelen + 1 + vallen);
+ if (__builtin_expect (np == NULL, 0))
+ {
+ UNLOCK;
+ return -1;
+ }
+
+#ifdef USE_TSEARCH
+ memcpy (np, new_value, namelen + 1 + vallen);
+#else
+ memcpy (np, name, namelen);
+ np[namelen] = '=';
+ memcpy (&np[namelen + 1], value, vallen);
+#endif
+ /* And remember the value. */
+ STORE_VALUE (np);
+ }
+ }
+
+ *ep = np;
+ }
+
+ UNLOCK;
+
+ return 0;
+}
+
+int
+setenv (const char *name, const char *value, int replace)
+{
+ if (name == NULL || *name == '\0' || strchr (name, '=') != NULL)
+ {
+ __set_errno (EINVAL);
+ return -1;
+ }
+
+ return __add_to_environ (name, value, NULL, replace);
+}
+
+int
+unsetenv (const char *name)
+{
+ size_t len;
+ char **ep;
+
+ if (name == NULL || *name == '\0' || strchr (name, '=') != NULL)
+ {
+ __set_errno (EINVAL);
+ return -1;
+ }
+
+ len = strlen (name);
+
+ LOCK;
+
+ ep = __environ;
+ while (*ep != NULL)
+ if (!strncmp (*ep, name, len) && (*ep)[len] == '=')
+ {
+ /* Found it. Remove this pointer by moving later ones back. */
+ char **dp = ep;
+
+ do
+ dp[0] = dp[1];
+ while (*dp++);
+ /* Continue the loop in case NAME appears again. */
+ }
+ else
+ ++ep;
+
+ UNLOCK;
+
+ return 0;
+}
+
+/* The `clearenv' was planned to be added to POSIX.1 but probably
+ never made it. Nevertheless the POSIX.9 standard (POSIX bindings
+ for Fortran 77) requires this function. */
+int
+clearenv (void)
+{
+ LOCK;
+
+ if (__environ == last_environ && __environ != NULL)
+ {
+ /* We allocated this environment so we can free it. */
+ free (__environ);
+ last_environ = NULL;
+ }
+
+ /* Clear the environment pointer removes the whole environment. */
+ __environ = NULL;
+
+ UNLOCK;
+
+ return 0;
+}
+#ifdef _LIBC
+libc_freeres_fn (free_mem)
+{
+ /* Remove all traces. */
+ clearenv ();
+
+ /* Now remove the search tree. */
+ __tdestroy (known_values, free);
+ known_values = NULL;
+}
+
+# undef setenv
+# undef unsetenv
+# undef clearenv
+weak_alias (__setenv, setenv)
+weak_alias (__unsetenv, unsetenv)
+weak_alias (__clearenv, clearenv)
+#endif
+
+
diff --git a/src/sig-notation.c b/src/sig-notation.c
new file mode 100644
index 0000000..5800c37
--- /dev/null
+++ b/src/sig-notation.c
@@ -0,0 +1,260 @@
+/* sig-notation.c - Signature notation data support.
+ Copyright (C) 2005 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "gpgme.h"
+#include "util.h"
+#include "context.h"
+#include "ops.h"
+
+
+/* Free the signature notation object and all associated resources.
+ The object must already be removed from any linked list as the next
+ pointer is ignored. */
+void
+_gpgme_sig_notation_free (gpgme_sig_notation_t notation)
+{
+ if (notation->name)
+ free (notation->name);
+
+ if (notation->value)
+ free (notation->value);
+
+ free (notation);
+}
+
+
+/* Set the flags of NOTATION to FLAGS. */
+static void
+sig_notation_set_flags (gpgme_sig_notation_t notation,
+ gpgme_sig_notation_flags_t flags)
+{
+ /* We copy the flags into individual bits to make them easier
+ accessible individually for the user. */
+ notation->human_readable = flags & GPGME_SIG_NOTATION_HUMAN_READABLE ? 1 : 0;
+ notation->critical = flags & GPGME_SIG_NOTATION_CRITICAL ? 1 : 0;
+
+ notation->flags = flags;
+}
+
+
+/* Create a new, empty signature notation data object. */
+gpgme_error_t
+_gpgme_sig_notation_create (gpgme_sig_notation_t *notationp,
+ const char *name, int name_len,
+ const char *value, int value_len,
+ gpgme_sig_notation_flags_t flags)
+{
+ gpgme_error_t err = 0;
+ gpgme_sig_notation_t notation;
+
+ /* Currently, we require all notations to be human-readable. */
+ if (name && !(flags & GPGME_SIG_NOTATION_HUMAN_READABLE))
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ notation = calloc (1, sizeof (*notation));
+ if (!notation)
+ return gpg_error_from_errno (errno);
+
+ /* This is critical. We want to reliably identify policy URLs by
+ using a NULL pointer for NAME. So all notations must have a NAME
+ string, even if it is empty. */
+ if (name)
+ {
+ /* We add a trailing '\0' for stringification in the good
+ case. */
+ notation->name = malloc (name_len + 1);
+ if (!notation->name)
+ {
+ err = gpg_error_from_errno (errno);
+ goto err;
+ }
+
+ memcpy (notation->name, name, name_len);
+ notation->name[name_len] = '\0';
+ notation->name_len = name_len;
+ }
+
+ if (value)
+ {
+ /* We add a trailing '\0' for stringification in the good
+ case. */
+ notation->value = malloc (value_len + 1);
+ if (!notation->value)
+ {
+ err = gpg_error_from_errno (errno);
+ goto err;
+ }
+
+ memcpy (notation->value, value, value_len);
+ notation->value[value_len] = '\0';
+ notation->value_len = value_len;
+ }
+
+ sig_notation_set_flags (notation, flags);
+
+ *notationp = notation;
+ return 0;
+
+ err:
+ _gpgme_sig_notation_free (notation);
+ return err;
+}
+
+
+/* GnuPG subpacket flags. */
+
+/* This subpacket data is part of the hashed data. */
+#define GNUPG_SPK_HASHED 0x01
+
+/* This subpacket is marked critical. */
+#define GNUPG_SPK_CRITICAL 0x02
+
+/* Parse a notation or policy URL subpacket. If the packet type is
+ not known, return no error but NULL in NOTATION. */
+gpgme_error_t
+_gpgme_parse_notation (gpgme_sig_notation_t *notationp,
+ int type, int pkflags, int len, char *data)
+{
+ gpgme_error_t err;
+ char *name = NULL;
+ int name_len = 0;
+ char *value = NULL;
+ int value_len = 0;
+ gpgme_sig_notation_flags_t flags = 0;
+ char *decoded_data;
+ unsigned char *bdata;
+
+ /* Type 20: Notation data. */
+ /* Type 26: Policy URL. */
+ if (type != 20 && type != 26)
+ {
+ *notationp = NULL;
+ return 0;
+ }
+
+ /* A few simple sanity checks. */
+ if (len > strlen (data))
+ return gpg_error (GPG_ERR_INV_ENGINE);
+
+ /* See below for the format of a notation subpacket. It has at
+ least four octets of flags and two times two octets of length
+ information. */
+ if (type == 20 && len < 4 + 2 + 2)
+ return gpg_error (GPG_ERR_INV_ENGINE);
+
+ err = _gpgme_decode_percent_string (data, &decoded_data, 0, 1);
+ if (err)
+ return err;
+ bdata = (unsigned char *) decoded_data;
+
+ /* Flags common to notation data and policy URL. */
+ if (pkflags & GNUPG_SPK_CRITICAL)
+ flags |= GPGME_SIG_NOTATION_CRITICAL;
+
+ /* This information is relevant in parsing multi-octet numbers below:
+
+ 3.1. Scalar numbers
+
+ Scalar numbers are unsigned, and are always stored in big-endian
+ format. Using n[k] to refer to the kth octet being interpreted,
+ the value of a two-octet scalar is ((n[0] << 8) + n[1]). The
+ value of a four-octet scalar is ((n[0] << 24) + (n[1] << 16) +
+ (n[2] << 8) + n[3]).
+
+ From RFC2440: OpenPGP Message Format. Copyright (C) The Internet
+ Society (1998). All Rights Reserved. */
+#define RFC2440_GET_WORD(chr) ((((int)((unsigned char *)(chr))[0]) << 8) \
+ + ((int)((unsigned char *)(chr))[1]))
+
+ if (type == 20)
+ {
+ /* 5.2.3.15. Notation Data
+
+ (4 octets of flags, 2 octets of name length (M),
+ 2 octets of value length (N), M octets of name data,
+ N octets of value data)
+
+ [...] The "flags" field holds four octets of flags.
+ All undefined flags MUST be zero. Defined flags are:
+
+ First octet: 0x80 = human-readable. [...]
+ Other octets: none.
+
+ From RFC2440: OpenPGP Message Format. Copyright (C) The
+ Internet Society (1998). All Rights Reserved. */
+
+ int chr;
+
+ /* First octet of flags. */
+#define RFC2440_SPK20_FLAG1_HUMAN_READABLE 0x80
+
+ chr = *bdata;
+ bdata++;
+
+ if (chr & RFC2440_SPK20_FLAG1_HUMAN_READABLE)
+ flags |= GPGME_SIG_NOTATION_HUMAN_READABLE;
+
+ /* The second, third and four octet of flags are unused. */
+ bdata++;
+ bdata++;
+ bdata++;
+
+ name_len = RFC2440_GET_WORD (bdata);
+ bdata += 2;
+
+ value_len = RFC2440_GET_WORD (bdata);
+ bdata += 2;
+
+ /* Small sanity check. */
+ if (4 + 2 + 2 + name_len + value_len > len)
+ {
+ free (decoded_data);
+ return gpg_error (GPG_ERR_INV_ENGINE);
+ }
+
+ name = (char *) bdata;
+ bdata += name_len;
+
+ value = (char *) bdata;
+ }
+ else
+ {
+ /* Type is 26. */
+
+ /* NAME is NULL, name_len is 0. */
+
+ value = (char *) bdata;
+ value_len = strlen (value);
+ }
+
+ err = _gpgme_sig_notation_create (notationp, name, name_len,
+ value, value_len, flags);
+
+ free (decoded_data);
+ return err;
+}
diff --git a/src/sign.c b/src/sign.c
new file mode 100644
index 0000000..2f308a0
--- /dev/null
+++ b/src/sign.c
@@ -0,0 +1,422 @@
+/* sign.c - Signing function.
+ Copyright (C) 2000 Werner Koch (dd9jn)
+ Copyright (C) 2001, 2002, 2003, 2004, 2007 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+/* Suppress warning for accessing deprecated member "class". */
+#define _GPGME_IN_GPGME 1
+#include "gpgme.h"
+#include "context.h"
+#include "ops.h"
+#include "util.h"
+#include "debug.h"
+
+
+typedef struct
+{
+ struct _gpgme_op_sign_result result;
+
+ /* A pointer to the next pointer of the last invalid signer in
+ the list. This makes appending new invalid signers painless
+ while preserving the order. */
+ gpgme_invalid_key_t *last_signer_p;
+
+ /* Likewise for signature information. */
+ gpgme_new_signature_t *last_sig_p;
+
+ /* Flags used while processing the status lines. */
+ unsigned int ignore_inv_recp:1;
+ unsigned int inv_sgnr_seen:1;
+ unsigned int sig_created_seen:1;
+} *op_data_t;
+
+
+static void
+release_op_data (void *hook)
+{
+ op_data_t opd = (op_data_t) hook;
+ gpgme_invalid_key_t invalid_signer = opd->result.invalid_signers;
+ gpgme_new_signature_t sig = opd->result.signatures;
+
+ while (invalid_signer)
+ {
+ gpgme_invalid_key_t next = invalid_signer->next;
+ if (invalid_signer->fpr)
+ free (invalid_signer->fpr);
+ free (invalid_signer);
+ invalid_signer = next;
+ }
+
+ while (sig)
+ {
+ gpgme_new_signature_t next = sig->next;
+ free (sig->fpr);
+ free (sig);
+ sig = next;
+ }
+}
+
+
+gpgme_sign_result_t
+gpgme_op_sign_result (gpgme_ctx_t ctx)
+{
+ void *hook;
+ op_data_t opd;
+ gpgme_error_t err;
+
+ TRACE_BEG (DEBUG_CTX, "gpgme_op_sign_result", ctx);
+
+ err = _gpgme_op_data_lookup (ctx, OPDATA_SIGN, &hook, -1, NULL);
+ opd = hook;
+ if (err || !opd)
+ {
+ TRACE_SUC0 ("result=(null)");
+ return NULL;
+ }
+
+ if (_gpgme_debug_trace ())
+ {
+ gpgme_invalid_key_t inv_key = opd->result.invalid_signers;
+ gpgme_new_signature_t sig = opd->result.signatures;
+ int inv_signers = 0;
+ int signatures = 0;
+
+ while (inv_key)
+ {
+ inv_signers++;
+ inv_key = inv_key->next;
+ }
+ while (sig)
+ {
+ signatures++;
+ sig = sig->next;
+ }
+
+ TRACE_LOG2 ("result: invalid signers: %i, signatures: %i",
+ inv_signers, signatures);
+ inv_key = opd->result.invalid_signers;
+ while (inv_key)
+ {
+ TRACE_LOG3 ("result: invalid signer: fpr=%s, reason=%s <%s>",
+ inv_key->fpr, gpgme_strerror (inv_key->reason),
+ gpgme_strsource (inv_key->reason));
+ inv_key = inv_key->next;
+ }
+ sig = opd->result.signatures;
+ while (sig)
+ {
+ TRACE_LOG6 ("result: signature: type=%i, pubkey_algo=%i, "
+ "hash_algo=%i, timestamp=%li, fpr=%s, sig_class=%i",
+ sig->type, sig->pubkey_algo, sig->hash_algo,
+ sig->timestamp, sig->fpr, sig->sig_class);
+ sig = sig->next;
+ }
+ }
+
+ TRACE_SUC1 ("result=%p", &opd->result);
+ return &opd->result;
+}
+
+
+static gpgme_error_t
+parse_sig_created (char *args, gpgme_new_signature_t *sigp)
+{
+ gpgme_new_signature_t sig;
+ char *tail;
+
+ sig = malloc (sizeof (*sig));
+ if (!sig)
+ return gpg_error_from_syserror ();
+
+ sig->next = NULL;
+ switch (*args)
+ {
+ case 'S':
+ sig->type = GPGME_SIG_MODE_NORMAL;
+ break;
+
+ case 'D':
+ sig->type = GPGME_SIG_MODE_DETACH;
+ break;
+
+ case 'C':
+ sig->type = GPGME_SIG_MODE_CLEAR;
+ break;
+
+ default:
+ /* The backend engine is not behaving. */
+ free (sig);
+ return gpg_error (GPG_ERR_INV_ENGINE);
+ }
+
+ args++;
+ if (*args != ' ')
+ {
+ free (sig);
+ return gpg_error (GPG_ERR_INV_ENGINE);
+ }
+
+ gpg_err_set_errno (0);
+ sig->pubkey_algo = strtol (args, &tail, 0);
+ if (errno || args == tail || *tail != ' ')
+ {
+ /* The crypto backend does not behave. */
+ free (sig);
+ return gpg_error (GPG_ERR_INV_ENGINE);
+ }
+ args = tail;
+
+ sig->hash_algo = strtol (args, &tail, 0);
+ if (errno || args == tail || *tail != ' ')
+ {
+ /* The crypto backend does not behave. */
+ free (sig);
+ return gpg_error (GPG_ERR_INV_ENGINE);
+ }
+ args = tail;
+
+ sig->sig_class = strtol (args, &tail, 0);
+ sig->class = sig->sig_class;
+ sig->_obsolete_class = sig->sig_class;
+ if (errno || args == tail || *tail != ' ')
+ {
+ /* The crypto backend does not behave. */
+ free (sig);
+ return gpg_error (GPG_ERR_INV_ENGINE);
+ }
+ args = tail;
+
+ sig->timestamp = _gpgme_parse_timestamp (args, &tail);
+ if (sig->timestamp == -1 || args == tail || *tail != ' ')
+ {
+ /* The crypto backend does not behave. */
+ free (sig);
+ return gpg_error (GPG_ERR_INV_ENGINE);
+ }
+ args = tail;
+ while (*args == ' ')
+ args++;
+
+ if (!*args)
+ {
+ /* The crypto backend does not behave. */
+ free (sig);
+ return gpg_error (GPG_ERR_INV_ENGINE);
+ }
+
+ tail = strchr (args, ' ');
+ if (tail)
+ *tail = '\0';
+
+ sig->fpr = strdup (args);
+ if (!sig->fpr)
+ {
+ int saved_errno = errno;
+ free (sig);
+ return gpg_error_from_errno (saved_errno);
+ }
+ *sigp = sig;
+ return 0;
+}
+
+
+gpgme_error_t
+_gpgme_sign_status_handler (void *priv, gpgme_status_code_t code, char *args)
+{
+ gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
+ gpgme_error_t err;
+ void *hook;
+ op_data_t opd;
+
+ err = _gpgme_passphrase_status_handler (priv, code, args);
+ if (err)
+ return err;
+
+ err = _gpgme_op_data_lookup (ctx, OPDATA_SIGN, &hook, -1, NULL);
+ opd = hook;
+ if (err)
+ return err;
+
+ switch (code)
+ {
+ case GPGME_STATUS_SIG_CREATED:
+ opd->sig_created_seen = 1;
+ err = parse_sig_created (args, opd->last_sig_p);
+ if (err)
+ return err;
+
+ opd->last_sig_p = &(*opd->last_sig_p)->next;
+ break;
+
+ case GPGME_STATUS_INV_RECP:
+ if (opd->inv_sgnr_seen && opd->ignore_inv_recp)
+ break;
+ /* FALLTROUGH */
+ case GPGME_STATUS_INV_SGNR:
+ if (code == GPGME_STATUS_INV_SGNR)
+ opd->inv_sgnr_seen = 1;
+ err = _gpgme_parse_inv_recp (args, opd->last_signer_p);
+ if (err)
+ return err;
+
+ opd->last_signer_p = &(*opd->last_signer_p)->next;
+ break;
+
+ case GPGME_STATUS_EOF:
+ /* The UI server does not send information about the created
+ signature. This is irrelevant for this protocol and thus we
+ should not check for that. */
+ if (opd->result.invalid_signers)
+ err = gpg_error (GPG_ERR_UNUSABLE_SECKEY);
+ else if (!opd->sig_created_seen
+ && ctx->protocol != GPGME_PROTOCOL_UISERVER)
+ err = gpg_error (GPG_ERR_GENERAL);
+ break;
+
+ default:
+ break;
+ }
+ return err;
+}
+
+
+static gpgme_error_t
+sign_status_handler (void *priv, gpgme_status_code_t code, char *args)
+{
+ gpgme_error_t err;
+
+ err = _gpgme_progress_status_handler (priv, code, args);
+ if (!err)
+ err = _gpgme_sign_status_handler (priv, code, args);
+ return err;
+}
+
+
+static gpgme_error_t
+sign_init_result (gpgme_ctx_t ctx, int ignore_inv_recp)
+{
+ gpgme_error_t err;
+ void *hook;
+ op_data_t opd;
+
+ err = _gpgme_op_data_lookup (ctx, OPDATA_SIGN, &hook,
+ sizeof (*opd), release_op_data);
+ opd = hook;
+ if (err)
+ return err;
+ opd->last_signer_p = &opd->result.invalid_signers;
+ opd->last_sig_p = &opd->result.signatures;
+ opd->ignore_inv_recp = !!ignore_inv_recp;
+ opd->inv_sgnr_seen = 0;
+ opd->sig_created_seen = 0;
+ return 0;
+}
+
+gpgme_error_t
+_gpgme_op_sign_init_result (gpgme_ctx_t ctx)
+{
+ return sign_init_result (ctx, 0);
+}
+
+
+static gpgme_error_t
+sign_start (gpgme_ctx_t ctx, int synchronous, gpgme_data_t plain,
+ gpgme_data_t sig, gpgme_sig_mode_t mode)
+{
+ gpgme_error_t err;
+
+ err = _gpgme_op_reset (ctx, synchronous);
+ if (err)
+ return err;
+
+ /* If we are using the CMS protocol, we ignore the INV_RECP status
+ code if a newer GPGSM is in use. GPGMS does not support combined
+ sign+encrypt and thus this can't harm. */
+ err = sign_init_result (ctx, (ctx->protocol == GPGME_PROTOCOL_CMS));
+ if (err)
+ return err;
+
+ if (mode != GPGME_SIG_MODE_NORMAL && mode != GPGME_SIG_MODE_DETACH
+ && mode != GPGME_SIG_MODE_CLEAR)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if (!plain)
+ return gpg_error (GPG_ERR_NO_DATA);
+ if (!sig)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ if (ctx->passphrase_cb)
+ {
+ err = _gpgme_engine_set_command_handler
+ (ctx->engine, _gpgme_passphrase_command_handler, ctx, NULL);
+ if (err)
+ return err;
+ }
+
+ _gpgme_engine_set_status_handler (ctx->engine, sign_status_handler,
+ ctx);
+
+ return _gpgme_engine_op_sign (ctx->engine, plain, sig, mode, ctx->use_armor,
+ ctx->use_textmode, ctx->include_certs,
+ ctx /* FIXME */);
+}
+
+
+/* Sign the plaintext PLAIN and store the signature in SIG. */
+gpgme_error_t
+gpgme_op_sign_start (gpgme_ctx_t ctx, gpgme_data_t plain, gpgme_data_t sig,
+ gpgme_sig_mode_t mode)
+{
+ gpg_error_t err;
+ TRACE_BEG3 (DEBUG_CTX, "gpgme_op_sign_start", ctx,
+ "plain=%p, sig=%p, mode=%i", plain, sig, mode);
+
+ if (!ctx)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ err = sign_start (ctx, 0, plain, sig, mode);
+ return TRACE_ERR (err);
+}
+
+
+/* Sign the plaintext PLAIN and store the signature in SIG. */
+gpgme_error_t
+gpgme_op_sign (gpgme_ctx_t ctx, gpgme_data_t plain, gpgme_data_t sig,
+ gpgme_sig_mode_t mode)
+{
+ gpgme_error_t err;
+
+ TRACE_BEG3 (DEBUG_CTX, "gpgme_op_sign", ctx,
+ "plain=%p, sig=%p, mode=%i", plain, sig, mode);
+
+ if (!ctx)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ err = sign_start (ctx, 1, plain, sig, mode);
+ if (!err)
+ err = _gpgme_wait_one (ctx);
+ return TRACE_ERR (err);
+}
diff --git a/src/signers.c b/src/signers.c
new file mode 100644
index 0000000..ae74e81
--- /dev/null
+++ b/src/signers.c
@@ -0,0 +1,110 @@
+/* signers.c - Maintain signer sets.
+ Copyright (C) 2001 Werner Koch (dd9jn)
+ Copyright (C) 2001, 2002, 2003, 2004 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <errno.h>
+
+#include "gpgme.h"
+#include "util.h"
+#include "context.h"
+#include "debug.h"
+
+
+/* Delete all signers from CTX. */
+void
+_gpgme_signers_clear (gpgme_ctx_t ctx)
+{
+ unsigned int i;
+
+ if (!ctx || !ctx->signers)
+ return;
+
+ for (i = 0; i < ctx->signers_len; i++)
+ {
+ assert (ctx->signers[i]);
+ gpgme_key_unref (ctx->signers[i]);
+ ctx->signers[i] = NULL;
+ }
+ ctx->signers_len = 0;
+}
+
+
+void
+gpgme_signers_clear (gpgme_ctx_t ctx)
+{
+ TRACE (DEBUG_CTX, "gpgme_signers_clear", ctx);
+ _gpgme_signers_clear (ctx);
+}
+
+
+/* Add KEY to list of signers in CTX. */
+gpgme_error_t
+gpgme_signers_add (gpgme_ctx_t ctx, const gpgme_key_t key)
+{
+ TRACE_BEG2 (DEBUG_CTX, "gpgme_signers_add", ctx,
+ "key=%p (%s)", key, (key->subkeys && key->subkeys->fpr) ?
+ key->subkeys->fpr : "invalid");
+
+ if (!ctx || !key)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ if (ctx->signers_len == ctx->signers_size)
+ {
+ gpgme_key_t *newarr;
+ int n = ctx->signers_size + 5;
+ int j;
+
+ newarr = realloc (ctx->signers, n * sizeof (*newarr));
+ if (!newarr)
+ return TRACE_ERR (gpg_error_from_errno (errno));
+ for (j = ctx->signers_size; j < n; j++)
+ newarr[j] = NULL;
+ ctx->signers = newarr;
+ ctx->signers_size = n;
+ }
+
+ gpgme_key_ref (key);
+ ctx->signers[ctx->signers_len++] = key;
+ return TRACE_SUC ();
+}
+
+
+/* Return the SEQth signer's key in CTX with one reference. */
+gpgme_key_t
+gpgme_signers_enum (const gpgme_ctx_t ctx, int seq)
+{
+ unsigned int seqno;
+
+ if (!ctx || seq < 0)
+ return NULL;
+
+ seqno = (unsigned int) seq;
+ if (seqno >= ctx->signers_len)
+ return NULL;
+ gpgme_key_ref (ctx->signers[seqno]);
+ return ctx->signers[seqno];
+}
diff --git a/src/status-table.c b/src/status-table.c
new file mode 100644
index 0000000..8060bdb
--- /dev/null
+++ b/src/status-table.c
@@ -0,0 +1,157 @@
+/* gpgme.c - GnuPG Made Easy.
+ Copyright (C) 2000 Werner Koch (dd9jn)
+ Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2012 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "util.h"
+
+struct status_table_s {
+ const char *name;
+ gpgme_status_code_t code;
+};
+
+
+/* Lexicographically sorted ('_' comes after any letter). You can use
+ the Emacs command M-x sort-lines. But don't sweat it, the table is
+ sorted at start up, too. */
+static struct status_table_s status_table[] =
+{
+ { "ALREADY_SIGNED", GPGME_STATUS_ALREADY_SIGNED },
+ { "BACKUP_KEY_CREATED", GPGME_STATUS_BACKUP_KEY_CREATED },
+ { "BADARMOR", GPGME_STATUS_BADARMOR },
+ { "BADMDC", GPGME_STATUS_BADMDC },
+ { "BADSIG", GPGME_STATUS_BADSIG },
+ { "BAD_PASSPHRASE", GPGME_STATUS_BAD_PASSPHRASE },
+ { "BEGIN_DECRYPTION", GPGME_STATUS_BEGIN_DECRYPTION },
+ { "BEGIN_ENCRYPTION", GPGME_STATUS_BEGIN_ENCRYPTION },
+ { "BEGIN_STREAM", GPGME_STATUS_BEGIN_STREAM },
+ { "CARDCTRL", GPGME_STATUS_CARDCTRL },
+ { "DECRYPTION_FAILED", GPGME_STATUS_DECRYPTION_FAILED },
+ { "DECRYPTION_OKAY", GPGME_STATUS_DECRYPTION_OKAY },
+ { "DELETE_PROBLEM", GPGME_STATUS_DELETE_PROBLEM },
+ { "ENC_TO", GPGME_STATUS_ENC_TO },
+ { "END_DECRYPTION", GPGME_STATUS_END_DECRYPTION },
+ { "END_ENCRYPTION", GPGME_STATUS_END_ENCRYPTION },
+ { "END_STREAM", GPGME_STATUS_END_STREAM },
+ { "ENTER", GPGME_STATUS_ENTER },
+ { "ERRMDC", GPGME_STATUS_ERRMDC },
+ { "ERROR", GPGME_STATUS_ERROR },
+ { "ERRSIG", GPGME_STATUS_ERRSIG },
+ { "EXPKEYSIG", GPGME_STATUS_EXPKEYSIG },
+ { "EXPSIG", GPGME_STATUS_EXPSIG },
+ { "FILE_DONE", GPGME_STATUS_FILE_DONE },
+ { "FILE_ERROR", GPGME_STATUS_FILE_ERROR },
+ { "FILE_START", GPGME_STATUS_FILE_START },
+ { "GET_BOOL", GPGME_STATUS_GET_BOOL },
+ { "GET_HIDDEN", GPGME_STATUS_GET_HIDDEN },
+ { "GET_LINE", GPGME_STATUS_GET_LINE },
+ { "GOODMDC", GPGME_STATUS_GOODMDC },
+ { "GOODSIG", GPGME_STATUS_GOODSIG },
+ { "GOOD_PASSPHRASE", GPGME_STATUS_GOOD_PASSPHRASE },
+ { "GOT_IT", GPGME_STATUS_GOT_IT },
+ { "IMPORTED", GPGME_STATUS_IMPORTED },
+ { "IMPORT_OK", GPGME_STATUS_IMPORT_OK },
+ { "IMPORT_PROBLEM", GPGME_STATUS_IMPORT_PROBLEM },
+ { "IMPORT_RES", GPGME_STATUS_IMPORT_RES },
+ { "INV_RECP", GPGME_STATUS_INV_RECP },
+ { "INV_SGNR", GPGME_STATUS_INV_SGNR },
+ { "KEYEXPIRED", GPGME_STATUS_KEYEXPIRED },
+ { "KEYREVOKED", GPGME_STATUS_KEYREVOKED },
+ { "KEY_CREATED", GPGME_STATUS_KEY_CREATED },
+ { "LEAVE", GPGME_STATUS_LEAVE },
+ { "MISSING_PASSPHRASE", GPGME_STATUS_MISSING_PASSPHRASE },
+ { "NEED_PASSPHRASE", GPGME_STATUS_NEED_PASSPHRASE },
+ { "NEED_PASSPHRASE_PIN", GPGME_STATUS_NEED_PASSPHRASE_PIN },
+ { "NEED_PASSPHRASE_SYM", GPGME_STATUS_NEED_PASSPHRASE_SYM },
+ { "NEWSIG", GPGME_STATUS_NEWSIG },
+ { "NODATA", GPGME_STATUS_NODATA },
+ { "NOTATION_DATA", GPGME_STATUS_NOTATION_DATA },
+ { "NOTATION_NAME", GPGME_STATUS_NOTATION_NAME },
+ { "NO_PUBKEY", GPGME_STATUS_NO_PUBKEY },
+ { "NO_RECP", GPGME_STATUS_NO_RECP },
+ { "NO_SECKEY", GPGME_STATUS_NO_SECKEY },
+ { "NO_SGNR", GPGME_STATUS_NO_SGNR },
+ { "PKA_TRUST_BAD", GPGME_STATUS_PKA_TRUST_BAD },
+ { "PKA_TRUST_GOOD", GPGME_STATUS_PKA_TRUST_GOOD },
+ { "PLAINTEXT", GPGME_STATUS_PLAINTEXT },
+ { "POLICY_URL", GPGME_STATUS_POLICY_URL },
+ { "PROGRESS", GPGME_STATUS_PROGRESS },
+ { "REVKEYSIG", GPGME_STATUS_REVKEYSIG },
+ { "RSA_OR_IDEA", GPGME_STATUS_RSA_OR_IDEA },
+ { "SC_OP_FAILURE", GPGME_STATUS_SC_OP_FAILURE },
+ { "SC_OP_SUCCESS", GPGME_STATUS_SC_OP_SUCCESS },
+ { "SESSION_KEY", GPGME_STATUS_SESSION_KEY },
+ { "SHM_GET", GPGME_STATUS_SHM_GET },
+ { "SHM_GET_BOOL", GPGME_STATUS_SHM_GET_BOOL },
+ { "SHM_GET_HIDDEN", GPGME_STATUS_SHM_GET_HIDDEN },
+ { "SHM_INFO", GPGME_STATUS_SHM_INFO },
+ { "SIGEXPIRED", GPGME_STATUS_SIGEXPIRED },
+ { "SIG_CREATED", GPGME_STATUS_SIG_CREATED },
+ { "SIG_ID", GPGME_STATUS_SIG_ID },
+ { "SIG_SUBPACKET", GPGME_STATUS_SIG_SUBPACKET },
+ { "SUCCESS", GPGME_STATUS_SUCCESS },
+ { "TRUNCATED", GPGME_STATUS_TRUNCATED },
+ { "TRUST_FULLY", GPGME_STATUS_TRUST_FULLY },
+ { "TRUST_MARGINAL", GPGME_STATUS_TRUST_MARGINAL },
+ { "TRUST_NEVER", GPGME_STATUS_TRUST_NEVER },
+ { "TRUST_ULTIMATE", GPGME_STATUS_TRUST_ULTIMATE },
+ { "TRUST_UNDEFINED", GPGME_STATUS_TRUST_UNDEFINED },
+ { "UNEXPECTED", GPGME_STATUS_UNEXPECTED },
+ { "USERID_HINT", GPGME_STATUS_USERID_HINT },
+ { "VALIDSIG", GPGME_STATUS_VALIDSIG },
+ { "ABORT", GPGME_STATUS_ABORT },
+ {NULL, 0}
+};
+
+
+static int
+status_cmp (const void *ap, const void *bp)
+{
+ const struct status_table_s *a = ap;
+ const struct status_table_s *b = bp;
+
+ return strcmp (a->name, b->name);
+}
+
+
+void
+_gpgme_status_init (void)
+{
+ qsort (status_table,
+ DIM(status_table) - 1, sizeof (status_table[0]),
+ status_cmp);
+}
+
+
+gpgme_status_code_t
+_gpgme_parse_status (const char *name)
+{
+ struct status_table_s t, *r;
+ t.name = name;
+ r = bsearch (&t, status_table, DIM(status_table) - 1,
+ sizeof t, status_cmp);
+ return r ? r->code : -1;
+}
diff --git a/src/stpcpy.c b/src/stpcpy.c
new file mode 100644
index 0000000..6e42911
--- /dev/null
+++ b/src/stpcpy.c
@@ -0,0 +1,55 @@
+/* Copyright (C) 1992, 1995, 1997, 2002, 2004 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library 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 the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <string.h>
+
+#undef __stpcpy
+#undef stpcpy
+
+#ifndef weak_alias
+# define __stpcpy stpcpy
+#endif
+
+/* Copy SRC to DEST, returning the address of the terminating '\0' in DEST. */
+char *
+__stpcpy (dest, src)
+ char *dest;
+ const char *src;
+{
+ register char *d = dest;
+ register const char *s = src;
+
+ do
+ *d++ = *s;
+ while (*s++ != '\0');
+
+ return d - 1;
+}
+#ifdef libc_hidden_def
+libc_hidden_def (__stpcpy)
+#endif
+#ifdef weak_alias
+weak_alias (__stpcpy, stpcpy)
+#endif
+#ifdef libc_hidden_builtin_def
+libc_hidden_builtin_def (stpcpy)
+#endif
diff --git a/src/trust-item.c b/src/trust-item.c
new file mode 100644
index 0000000..4dd0c4e
--- /dev/null
+++ b/src/trust-item.c
@@ -0,0 +1,171 @@
+/* trust-item.c - Trust item objects.
+ Copyright (C) 2000 Werner Koch (dd9jn)
+ Copyright (C) 2001, 2002, 2003, 2004 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+
+#include "util.h"
+#include "ops.h"
+#include "sema.h"
+
+
+/* Protects all reference counters in trust items. All other accesses
+ to a trust item are either read only or happen before the trust
+ item is available to the user. */
+DEFINE_STATIC_LOCK (trust_item_ref_lock);
+
+
+/* Create a new trust item. */
+gpgme_error_t
+_gpgme_trust_item_new (gpgme_trust_item_t *r_item)
+{
+ gpgme_trust_item_t item;
+
+ item = calloc (1, sizeof *item);
+ if (!item)
+ return gpg_error_from_errno (errno);
+ item->_refs = 1;
+ item->keyid = item->_keyid;
+ item->_keyid[16] = '\0';
+ item->owner_trust = item->_owner_trust;
+ item->_owner_trust[1] = '\0';
+ item->validity = item->_validity;
+ item->_validity[1] = '\0';
+ *r_item = item;
+ return 0;
+}
+
+
+/* Acquire a reference to ITEM. */
+void
+gpgme_trust_item_ref (gpgme_trust_item_t item)
+{
+ LOCK (trust_item_ref_lock);
+ item->_refs++;
+ UNLOCK (trust_item_ref_lock);
+}
+
+
+/* gpgme_trust_item_unref releases the trust item object. Note that
+ this function may not do an actual release if there are other
+ shallow copies of the object. You have to call this function for
+ every newly created trust item object as well as for every
+ gpgme_trust_item_ref() done on the trust item object. */
+void
+gpgme_trust_item_unref (gpgme_trust_item_t item)
+{
+ LOCK (trust_item_ref_lock);
+ assert (item->_refs > 0);
+ if (--item->_refs)
+ {
+ UNLOCK (trust_item_ref_lock);
+ return;
+ }
+ UNLOCK (trust_item_ref_lock);
+
+ if (item->name)
+ free (item->name);
+ free (item);
+}
+
+
+/* Compatibility interfaces. */
+void
+gpgme_trust_item_release (gpgme_trust_item_t item)
+{
+ gpgme_trust_item_unref (item);
+}
+
+/* Return the value of the attribute WHAT of ITEM, which has to be
+ representable by a string. */
+const char *gpgme_trust_item_get_string_attr (gpgme_trust_item_t item,
+ _gpgme_attr_t what,
+ const void *reserved, int idx)
+{
+ const char *val = NULL;
+
+ if (!item)
+ return NULL;
+ if (reserved)
+ return NULL;
+ if (idx)
+ return NULL;
+
+ switch (what)
+ {
+ case GPGME_ATTR_KEYID:
+ val = item->keyid;
+ break;
+
+ case GPGME_ATTR_OTRUST:
+ val = item->owner_trust;
+ break;
+
+ case GPGME_ATTR_VALIDITY:
+ val = item->validity;
+ break;
+
+ case GPGME_ATTR_USERID:
+ val = item->name;
+ break;
+
+ default:
+ break;
+ }
+ return val;
+}
+
+
+/* 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. */
+int gpgme_trust_item_get_int_attr (gpgme_trust_item_t item, _gpgme_attr_t what,
+ const void *reserved, int idx)
+{
+ int val = 0;
+
+ if (!item)
+ return 0;
+ if (reserved)
+ return 0;
+ if (idx)
+ return 0;
+
+ switch (what)
+ {
+ case GPGME_ATTR_LEVEL:
+ val = item->level;
+ break;
+
+ case GPGME_ATTR_TYPE:
+ val = item->type;
+ break;
+
+ default:
+ break;
+ }
+ return val;
+}
diff --git a/src/trustlist.c b/src/trustlist.c
new file mode 100644
index 0000000..a96b6c7
--- /dev/null
+++ b/src/trustlist.c
@@ -0,0 +1,276 @@
+/* trustlist.c - Trust item listing.
+ Copyright (C) 2000 Werner Koch (dd9jn)
+ Copyright (C) 2001, 2002, 2003, 2004 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+
+#include "gpgme.h"
+#include "debug.h"
+#include "util.h"
+#include "context.h"
+#include "ops.h"
+
+
+struct trust_queue_item_s
+{
+ struct trust_queue_item_s *next;
+ gpgme_trust_item_t item;
+};
+
+typedef struct
+{
+ /* Something new is available. */
+ int trust_cond;
+ struct trust_queue_item_s *trust_queue;
+} *op_data_t;
+
+
+
+static gpgme_error_t
+trustlist_status_handler (void *priv, gpgme_status_code_t code, char *args)
+{
+ return 0;
+}
+
+
+/* This handler is used to parse the output of --list-trust-path:
+ Format:
+ level:keyid:type:recno:ot:val:mc:cc:name:
+ With TYPE = U for a user ID
+ K for a key
+ The RECNO is either the one of the dir record or the one of the uid
+ record. OT is the the usual trust letter and only availabel on K
+ lines. VAL is the calcualted validity MC is the marginal trust
+ counter and only available on U lines CC is the same for the
+ complete count NAME ist the username and only printed on U
+ lines. */
+static gpgme_error_t
+trustlist_colon_handler (void *priv, char *line)
+{
+ gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
+ gpgme_error_t err;
+ char *p, *pend;
+ int field = 0;
+ gpgme_trust_item_t item = NULL;
+
+ if (!line)
+ return 0; /* EOF */
+
+ for (p = line; p; p = pend)
+ {
+ field++;
+ pend = strchr (p, ':');
+ if (pend)
+ *pend++ = 0;
+
+ switch (field)
+ {
+ case 1: /* level */
+ err = _gpgme_trust_item_new (&item);
+ if (err)
+ return err;
+ item->level = atoi (p);
+ break;
+ case 2: /* long keyid */
+ if (strlen (p) == DIM(item->keyid) - 1)
+ strcpy (item->keyid, p);
+ break;
+ case 3: /* type */
+ item->type = *p == 'K'? 1 : *p == 'U'? 2 : 0;
+ break;
+ case 5: /* owner trust */
+ item->_owner_trust[0] = *p;
+ break;
+ case 6: /* validity */
+ item->_validity[0] = *p;
+ break;
+ case 9: /* user ID */
+ item->name = strdup (p);
+ if (!item->name)
+ {
+ int saved_errno = errno;
+ gpgme_trust_item_unref (item);
+ return gpg_error_from_errno (saved_errno);
+ }
+ break;
+ }
+ }
+
+ if (item)
+ _gpgme_engine_io_event (ctx->engine, GPGME_EVENT_NEXT_TRUSTITEM, item);
+ return 0;
+}
+
+
+void
+_gpgme_op_trustlist_event_cb (void *data, gpgme_event_io_t type,
+ void *type_data)
+{
+ gpgme_ctx_t ctx = (gpgme_ctx_t) data;
+ gpgme_error_t err;
+ void *hook;
+ op_data_t opd;
+ gpgme_trust_item_t item = (gpgme_trust_item_t) type_data;
+ struct trust_queue_item_s *q, *q2;
+
+ assert (type == GPGME_EVENT_NEXT_TRUSTITEM);
+
+ err = _gpgme_op_data_lookup (ctx, OPDATA_TRUSTLIST, &hook, -1, NULL);
+ opd = hook;
+ if (err)
+ return;
+
+ q = malloc (sizeof *q);
+ if (!q)
+ {
+ gpgme_trust_item_unref (item);
+ /* FIXME: GPGME_Out_Of_Core; */
+ return;
+ }
+ q->item = item;
+ q->next = NULL;
+ /* FIXME: Use a tail pointer */
+ q2 = opd->trust_queue;
+ if (!q2)
+ opd->trust_queue = q;
+ else
+ {
+ while (q2->next)
+ q2 = q2->next;
+ q2->next = q;
+ }
+ /* FIXME: unlock queue */
+ opd->trust_cond = 1;
+}
+
+
+gpgme_error_t
+gpgme_op_trustlist_start (gpgme_ctx_t ctx, const char *pattern, int max_level)
+{
+ gpgme_error_t err = 0;
+ void *hook;
+ op_data_t opd;
+
+ TRACE_BEG2 (DEBUG_CTX, "gpgme_op_trustlist_start", ctx,
+ "pattern=%s, max_level=%i", pattern, max_level);
+
+ if (!ctx || !pattern || !*pattern)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ err = _gpgme_op_reset (ctx, 2);
+ if (err)
+ return TRACE_ERR (err);
+
+ err = _gpgme_op_data_lookup (ctx, OPDATA_TRUSTLIST, &hook,
+ sizeof (*opd), NULL);
+ opd = hook;
+ if (err)
+ return TRACE_ERR (err);
+
+ _gpgme_engine_set_status_handler (ctx->engine,
+ trustlist_status_handler, ctx);
+ err = _gpgme_engine_set_colon_line_handler (ctx->engine,
+ trustlist_colon_handler, ctx);
+ if (err)
+ return TRACE_ERR (err);
+
+ err = _gpgme_engine_op_trustlist (ctx->engine, pattern);
+ return TRACE_ERR (err);
+}
+
+
+gpgme_error_t
+gpgme_op_trustlist_next (gpgme_ctx_t ctx, gpgme_trust_item_t *r_item)
+{
+ gpgme_error_t err;
+ void *hook;
+ op_data_t opd;
+ struct trust_queue_item_s *q;
+
+ TRACE_BEG (DEBUG_CTX, "gpgme_op_trustlist_next", ctx);
+
+ if (!ctx || !r_item)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+ *r_item = NULL;
+ if (!ctx)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ err = _gpgme_op_data_lookup (ctx, OPDATA_TRUSTLIST, &hook, -1, NULL);
+ opd = hook;
+ if (err)
+ return TRACE_ERR (err);
+ if (opd == NULL)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ if (!opd->trust_queue)
+ {
+ err = _gpgme_wait_on_condition (ctx, &opd->trust_cond, NULL);
+ if (err)
+ return TRACE_ERR (err);
+ if (!opd->trust_cond)
+ return TRACE_ERR (gpg_error (GPG_ERR_EOF));
+ opd->trust_cond = 0;
+ assert (opd->trust_queue);
+ }
+ q = opd->trust_queue;
+ opd->trust_queue = q->next;
+
+ *r_item = q->item;
+ free (q);
+ if ((*r_item)->type == 1)
+ {
+ TRACE_SUC5 ("trust_item=%p: %s: owner trust %s with level %i "
+ "and validity 0x%x", *r_item, (*r_item)->keyid,
+ (*r_item)->owner_trust, (*r_item)->level,
+ (*r_item)->validity);
+ }
+ else if ((*r_item)->type == 2)
+ {
+ TRACE_SUC5 ("trust_item=%p: %s: UID %s with level %i "
+ "and validity 0x%x", *r_item, (*r_item)->keyid,
+ (*r_item)->name, (*r_item)->level, (*r_item)->validity);
+ }
+ else
+ {
+ TRACE_SUC5 ("trust_item=%p: %s: unknown type %i with level %i "
+ "and validity 0x%x", *r_item, (*r_item)->keyid,
+ (*r_item)->type, (*r_item)->level, (*r_item)->validity);
+ }
+ return 0;
+}
+
+
+/* Terminate a pending trustlist operation within CTX. */
+gpgme_error_t
+gpgme_op_trustlist_end (gpgme_ctx_t ctx)
+{
+ TRACE (DEBUG_CTX, "gpgme_op_trustlist_end", ctx);
+
+ if (!ctx)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ return 0;
+}
diff --git a/src/ttyname_r.c b/src/ttyname_r.c
new file mode 100644
index 0000000..105e0af
--- /dev/null
+++ b/src/ttyname_r.c
@@ -0,0 +1,130 @@
+/* ttyname_r.c - A ttyname_r() replacement.
+ Copyright (C) 2003, 2004, 2012 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/>.
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+
+#if !HAVE_TTYNAME_R && defined(__GNUC__)
+# warning ttyname is not thread-safe, and ttyname_r is missing
+#endif
+
+int
+_gpgme_ttyname_r (int fd, char *buf, size_t buflen)
+{
+#if HAVE_TTYNAME_R
+# if HAVE_BROKEN_TTYNAME_R
+ /* Solaris fails if BUFLEN is less than 128. OSF/1 5.1 completely
+ ignores BUFLEN. We use a large buffer to woraround this. */
+ {
+ char largebuf[512];
+ size_t namelen;
+ int rc;
+
+# if HAVE_POSIXDECL_TTYNAME_R
+ if (buflen < sizeof (largebuf))
+ {
+ rc = ttyname_r (fd, largebuf, (int)sizeof (largebuf));
+ if (!rc)
+ {
+ namelen = strlen (largebuf) + 1;
+ if (namelen > buflen)
+ rc = ERANGE;
+ else
+ memcpy (buf, largebuf, namelen);
+ }
+ }
+ else
+ rc = ttyname_r (fd, buf, (int)buflen);
+
+# else /*!HAVE_POSIXDECL_TTYNAME_R*/
+ char *name;
+
+ if (buflen < sizeof (largebuf))
+ name = ttyname_r (fd, largebuf, (int)sizeof (largebuf));
+ else
+ name = ttyname_r (fd, buf, (int)buflen);
+ rc = name? 0 : (errno? errno : -1);
+ if (!rc && buf != name)
+ {
+ namelen = strlen (name) + 1;
+ if (namelen > buflen)
+ rc = ERANGE;
+ else
+ memmove (buf, name, namelen);
+ }
+# endif
+
+ return rc;
+ }
+# else /*!HAVE_BROKEN_TTYNAME_R*/
+ {
+ int rc;
+
+# if HAVE_POSIXDECL_TTYNAME_R
+
+ rc = ttyname_r (fd, buf, buflen);
+
+# else /*!HAVE_POSIXDECL_TTYNAME_R*/
+ char *name;
+ size_t namelen;
+
+ name = ttyname_r (fd, buf, (int)buflen);
+ rc = name? 0 : (errno? errno : -1);
+ if (!rc && buf != name)
+ {
+ namelen = strlen (name) + 1;
+ if (namelen > buflen)
+ rc = ERANGE;
+ else
+ memmove (buf, name, namelen);
+ }
+# endif
+
+ return rc;
+ }
+# endif /*!HAVE_BROKEN_TTYNAME_R*/
+#else /*!HAVE_TTYNAME_R*/
+ char *tty;
+
+# if HAVE_W32_SYSTEM
+ /* We use this default one for now. AFAICS we only need it to be
+ passed to gpg and in turn to pinentry. Providing a replacement
+ is needed because elsewhere we bail out on error. If we
+ eventually implement a pinentry for Windows it is inlikely that
+ we need a real tty at all. */
+ tty = "/dev/tty";
+# else
+ tty = ttyname (fd);
+ if (!tty)
+ return errno? errno : -1;
+# endif
+
+ strncpy (buf, tty, buflen);
+ buf[buflen - 1] = '\0';
+ return (strlen (tty) >= buflen) ? ERANGE : 0;
+#endif /*!HAVE_TTYNAME_R*/
+}
diff --git a/src/util.h b/src/util.h
new file mode 100644
index 0000000..cf18099
--- /dev/null
+++ b/src/util.h
@@ -0,0 +1,175 @@
+/* util.h
+ Copyright (C) 2000 Werner Koch (dd9jn)
+ Copyright (C) 2001, 2002, 2003, 2004, 2005 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#ifndef UTIL_H
+#define UTIL_H
+
+#ifdef HAVE_W32_SYSTEM
+# ifdef HAVE_W32CE_SYSTEM
+# include "w32-ce.h"
+# else
+# include "windows.h"
+# endif
+#endif
+
+/* For pid_t. */
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+/* We must see the symbol ttyname_r before a redefinition. */
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#include "gpgme.h"
+
+
+#define DIM(v) (sizeof(v)/sizeof((v)[0]))
+
+
+/*-- {posix,w32}-util.c --*/
+const char *_gpgme_get_gpg_path (void);
+const char *_gpgme_get_gpgsm_path (void);
+const char *_gpgme_get_gpgconf_path (void);
+const char *_gpgme_get_g13_path (void);
+const char *_gpgme_get_uiserver_socket_path (void);
+
+int _gpgme_get_conf_int (const char *key, int *value);
+void _gpgme_allow_set_foreground_window (pid_t pid);
+
+/*-- dirinfo.c --*/
+const char *_gpgme_get_default_homedir (void);
+const char *_gpgme_get_default_agent_socket (void);
+
+
+
+/*-- replacement functions in <funcname>.c --*/
+#ifdef HAVE_CONFIG_H
+
+#ifndef HAVE_STPCPY
+static _GPGME_INLINE char *
+_gpgme_stpcpy (char *a, const char *b)
+{
+ while (*b)
+ *a++ = *b++;
+ *a = 0;
+ return a;
+}
+#define stpcpy(a,b) _gpgme_stpcpy ((a), (b))
+#endif /*!HAVE_STPCPY*/
+
+#if !HAVE_VASPRINTF
+#include <stdarg.h>
+int vasprintf (char **result, const char *format, va_list args);
+int asprintf (char **result, const char *format, ...);
+#endif
+
+#if REPLACE_TTYNAME_R
+int _gpgme_ttyname_r (int fd, char *buf, size_t buflen);
+#undef ttyname_r
+#define ttyname_r(a,b,c) _gpgme_ttyname_r ((a), (b), (c))
+#endif
+
+#endif /*HAVE_CONFIG_H*/
+
+
+/*-- conversion.c --*/
+/* Convert two hexadecimal digits from STR to the value they
+ represent. Returns -1 if one of the characters is not a
+ hexadecimal digit. */
+int _gpgme_hextobyte (const char *str);
+
+/* Decode the C formatted string SRC and store the result in the
+ buffer *DESTP which is LEN bytes long. If LEN is zero, then a
+ large enough buffer is allocated with malloc and *DESTP is set to
+ the result. Currently, LEN is only used to specify if allocation
+ is desired or not, the caller is expected to make sure that *DESTP
+ is large enough if LEN is not zero. */
+gpgme_error_t _gpgme_decode_c_string (const char *src, char **destp,
+ size_t len);
+
+/* Decode the percent escaped string SRC and store the result in the
+ buffer *DESTP which is LEN bytes long. If LEN is zero, then a
+ large enough buffer is allocated with malloc and *DESTP is set to
+ the result. Currently, LEN is only used to specify if allocation
+ is desired or not, the caller is expected to make sure that *DESTP
+ is large enough if LEN is not zero. If BINARY is 1, then '\0'
+ characters are allowed in the output. */
+gpgme_error_t _gpgme_decode_percent_string (const char *src, char **destp,
+ size_t len, int binary);
+
+gpgme_error_t _gpgme_encode_percent_string (const char *src, char **destp,
+ size_t len);
+
+
+/* Parse the string TIMESTAMP into a time_t. The string may either be
+ seconds since Epoch or in the ISO 8601 format like
+ "20390815T143012". Returns 0 for an empty string or seconds since
+ Epoch. Leading spaces are skipped. If ENDP is not NULL, it will
+ point to the next non-parsed character in TIMESTRING. */
+time_t _gpgme_parse_timestamp (const char *timestamp, char **endp);
+
+
+gpgme_error_t _gpgme_map_gnupg_error (char *err);
+
+
+/* Retrieve the environment variable NAME and return a copy of it in a
+ malloc()'ed buffer in *VALUE. If the environment variable is not
+ set, return NULL in *VALUE. */
+gpgme_error_t _gpgme_getenv (const char *name, char **value);
+
+
+/*-- status-table.c --*/
+/* Convert a status string to a status code. */
+void _gpgme_status_init (void);
+gpgme_status_code_t _gpgme_parse_status (const char *name);
+
+
+#ifdef HAVE_W32_SYSTEM
+int _gpgme_mkstemp (int *fd, char **name);
+const char *_gpgme_get_w32spawn_path (void);
+#endif /*HAVE_W32_SYSTEM*/
+#ifdef HAVE_W32CE_SYSTEM
+char *_gpgme_w32ce_get_debug_envvar (void);
+#endif /*HAVE_W32CE_SYSTEM*/
+
+/*-- Error codes not yet available in current gpg-error.h. --*/
+#ifndef GPG_ERR_UNFINISHED
+#define GPG_ERR_UNFINISHED 199
+#endif
+#ifndef GPG_ERR_NOT_OPERATIONAL
+#define GPG_ERR_NOT_OPERATIONAL 176
+#endif
+#ifndef GPG_ERR_MISSING_ISSUER_CERT
+#define GPG_ERR_MISSING_ISSUER_CERT 185
+#endif
+
+
+#ifdef ENABLE_ASSUAN
+#include <assuan.h>
+/* System hooks for assuan integration. */
+extern struct assuan_system_hooks _gpgme_assuan_system_hooks;
+extern struct assuan_malloc_hooks _gpgme_assuan_malloc_hooks;
+int _gpgme_assuan_log_cb (assuan_context_t ctx, void *hook,
+ unsigned int cat, const char *msg);
+#endif
+
+#endif /* UTIL_H */
diff --git a/src/vasprintf.c b/src/vasprintf.c
new file mode 100644
index 0000000..03d38ff
--- /dev/null
+++ b/src/vasprintf.c
@@ -0,0 +1,206 @@
+/* Like vsprintf but provides a pointer to malloc'd storage, which must
+ be freed by the caller.
+ Copyright (C) 1994, 2002 Free Software Foundation, Inc.
+
+This file is part of the libiberty library.
+Libiberty is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+Libiberty 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with libiberty; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+
+#ifndef va_copy /* According to POSIX, va_copy is a macro. */
+#if defined (__GNUC__) && defined (__PPC__) \
+ && (defined (_CALL_SYSV) || defined (_WIN32))
+#define va_copy(d, s) (*(d) = *(s))
+#elif defined (MUST_COPY_VA_BYVAL)
+#define va_copy(d, s) ((d) = (s))
+#else
+#define va_copy(d, s) memcpy ((d), (s), sizeof (va_list))
+#endif
+#endif
+
+
+#ifdef TEST
+int global_total_width;
+#endif
+
+static int int_vasprintf (char **, const char *, va_list *);
+
+static int
+int_vasprintf (result, format, args)
+ char **result;
+ const char *format;
+ va_list *args;
+{
+#ifdef HAVE_W32CE_SYSTEM
+ /* No va_copy and the replacement above doesn't work. */
+#define MAX_STRLEN 256
+ *result = malloc (MAX_STRLEN);
+ if (*result != NULL)
+ {
+ int res = _vsnprintf (*result, MAX_STRLEN, format, *args);
+ (*result)[MAX_STRLEN - 1] = '\0';
+ return res;
+ }
+ else
+ return 0;
+#else
+ const char *p = format;
+ /* Add one to make sure that it is never zero, which might cause malloc
+ to return NULL. */
+ int total_width = strlen (format) + 1;
+ va_list ap;
+
+ va_copy (ap, *args);
+
+ while (*p != '\0')
+ {
+ if (*p++ == '%')
+ {
+ while (strchr ("-+ #0", *p))
+ ++p;
+ if (*p == '*')
+ {
+ ++p;
+ total_width += abs (va_arg (ap, int));
+ }
+ else
+ total_width += strtoul (p, (char **) &p, 10);
+ if (*p == '.')
+ {
+ ++p;
+ if (*p == '*')
+ {
+ ++p;
+ total_width += abs (va_arg (ap, int));
+ }
+ else
+ total_width += strtoul (p, (char **) &p, 10);
+ }
+ while (strchr ("hlL", *p))
+ ++p;
+ /* Should be big enough for any format specifier except %s and floats. */
+ total_width += 30;
+ switch (*p)
+ {
+ case 'd':
+ case 'i':
+ case 'o':
+ case 'u':
+ case 'x':
+ case 'X':
+ case 'c':
+ (void) va_arg (ap, int);
+ break;
+ case 'f':
+ case 'e':
+ case 'E':
+ case 'g':
+ case 'G':
+ (void) va_arg (ap, double);
+ /* Since an ieee double can have an exponent of 307, we'll
+ make the buffer wide enough to cover the gross case. */
+ total_width += 307;
+ break;
+ case 's':
+ {
+ char *tmp = va_arg (ap, char *);
+ if (tmp)
+ total_width += strlen (tmp);
+ else /* in case the vsprintf does prints a text */
+ total_width += 25; /* e.g. "(null pointer reference)" */
+ }
+ break;
+ case 'p':
+ case 'n':
+ (void) va_arg (ap, char *);
+ break;
+ }
+ p++;
+ }
+ }
+#ifdef TEST
+ global_total_width = total_width;
+#endif
+ *result = malloc (total_width);
+ if (*result != NULL)
+ return vsprintf (*result, format, *args);
+ else
+ return 0;
+#endif
+}
+
+int
+vasprintf (result, format, args)
+ char **result;
+ const char *format;
+#if defined (_BSD_VA_LIST_) && defined (__FreeBSD__)
+ _BSD_VA_LIST_ args;
+#else
+ va_list args;
+#endif
+{
+ return int_vasprintf (result, format, &args);
+}
+
+
+int
+asprintf (char **buf, const char *fmt, ...)
+{
+ int status;
+ va_list ap;
+
+ va_start (ap, fmt);
+ status = vasprintf (buf, fmt, ap);
+ va_end (ap);
+ return status;
+}
+
+
+#ifdef TEST
+void
+checkit (const char* format, ...)
+{
+ va_list args;
+ char *result;
+
+ va_start (args, format);
+ vasprintf (&result, format, args);
+ if (strlen (result) < global_total_width)
+ printf ("PASS: ");
+ else
+ printf ("FAIL: ");
+ printf ("%d %s\n", global_total_width, result);
+}
+
+int
+main (void)
+{
+ checkit ("%d", 0x12345678);
+ checkit ("%200d", 5);
+ checkit ("%.300d", 6);
+ checkit ("%100.150d", 7);
+ checkit ("%s", "jjjjjjjjjiiiiiiiiiiiiiiioooooooooooooooooppppppppppppaa\n\
+777777777777777777333333333333366666666666622222222222777777777777733333");
+ checkit ("%f%s%d%s", 1.0, "foo", 77, "asdjffffffffffffffiiiiiiiiiiixxxxx");
+}
+#endif /* TEST */
diff --git a/src/verify.c b/src/verify.c
new file mode 100644
index 0000000..a61cc95
--- /dev/null
+++ b/src/verify.c
@@ -0,0 +1,1084 @@
+/* verify.c - Signature verification.
+ Copyright (C) 2000 Werner Koch (dd9jn)
+ Copyright (C) 2001, 2002, 2003, 2004, 2005 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "gpgme.h"
+#include "debug.h"
+#include "util.h"
+#include "context.h"
+#include "ops.h"
+
+
+typedef struct
+{
+ struct _gpgme_op_verify_result result;
+
+ gpgme_signature_t current_sig;
+ int did_prepare_new_sig;
+ int only_newsig_seen;
+ int plaintext_seen;
+} *op_data_t;
+
+
+static void
+release_op_data (void *hook)
+{
+ op_data_t opd = (op_data_t) hook;
+ gpgme_signature_t sig = opd->result.signatures;
+
+ while (sig)
+ {
+ gpgme_signature_t next = sig->next;
+ gpgme_sig_notation_t notation = sig->notations;
+
+ while (notation)
+ {
+ gpgme_sig_notation_t next_nota = notation->next;
+
+ _gpgme_sig_notation_free (notation);
+ notation = next_nota;
+ }
+
+ if (sig->fpr)
+ free (sig->fpr);
+ if (sig->pka_address)
+ free (sig->pka_address);
+ free (sig);
+ sig = next;
+ }
+
+ if (opd->result.file_name)
+ free (opd->result.file_name);
+}
+
+
+gpgme_verify_result_t
+gpgme_op_verify_result (gpgme_ctx_t ctx)
+{
+ void *hook;
+ op_data_t opd;
+ gpgme_error_t err;
+ gpgme_signature_t sig;
+
+ TRACE_BEG (DEBUG_CTX, "gpgme_op_verify_result", ctx);
+ err = _gpgme_op_data_lookup (ctx, OPDATA_VERIFY, &hook, -1, NULL);
+ opd = hook;
+ if (err || !opd)
+ {
+ TRACE_SUC0 ("result=(null)");
+ return NULL;
+ }
+
+ /* It is possible that we saw a new signature only followed by an
+ ERROR line for that. In particular a missing X.509 key triggers
+ this. In this case it is surprising that the summary field has
+ not been updated. We fix it here by explicitly looking for this
+ case. The real fix would be to have GPGME emit ERRSIG. */
+ for (sig = opd->result.signatures; sig; sig = sig->next)
+ {
+ if (!sig->summary)
+ {
+ switch (gpg_err_code (sig->status))
+ {
+ case GPG_ERR_KEY_EXPIRED:
+ sig->summary |= GPGME_SIGSUM_KEY_EXPIRED;
+ break;
+
+ case GPG_ERR_NO_PUBKEY:
+ sig->summary |= GPGME_SIGSUM_KEY_MISSING;
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ /* Now for some tracing stuff. */
+ if (_gpgme_debug_trace ())
+ {
+ int i;
+
+ for (sig = opd->result.signatures, i = 0; sig; sig = sig->next, i++)
+ {
+ TRACE_LOG4 ("sig[%i] = fpr %s, summary 0x%x, status %s",
+ i, sig->fpr, sig->summary, gpg_strerror (sig->status));
+ TRACE_LOG6 ("sig[%i] = timestamps 0x%x/0x%x flags:%s%s%s",
+ i, sig->timestamp, sig->exp_timestamp,
+ sig->wrong_key_usage ? "wrong key usage" : "",
+ sig->pka_trust == 1 ? "pka bad"
+ : (sig->pka_trust == 2 ? "pka_okay" : "pka RFU"),
+ sig->chain_model ? "chain model" : "");
+ TRACE_LOG5 ("sig[%i] = validity 0x%x (%s), algos %s/%s",
+ i, sig->validity, gpg_strerror (sig->validity_reason),
+ gpgme_pubkey_algo_name (sig->pubkey_algo),
+ gpgme_hash_algo_name (sig->hash_algo));
+ if (sig->pka_address)
+ {
+ TRACE_LOG2 ("sig[%i] = PKA address %s", i, sig->pka_address);
+ }
+ if (sig->notations)
+ {
+ TRACE_LOG1 ("sig[%i] = has notations (not shown)", i);
+ }
+ }
+ }
+
+ TRACE_SUC1 ("result=%p", &opd->result);
+ return &opd->result;
+}
+
+
+/* Build a summary vector from RESULT. */
+static void
+calc_sig_summary (gpgme_signature_t sig)
+{
+ unsigned long sum = 0;
+
+ /* Calculate the red/green flag. */
+ if (sig->validity == GPGME_VALIDITY_FULL
+ || sig->validity == GPGME_VALIDITY_ULTIMATE)
+ {
+ if (gpg_err_code (sig->status) == GPG_ERR_NO_ERROR
+ || gpg_err_code (sig->status) == GPG_ERR_SIG_EXPIRED
+ || gpg_err_code (sig->status) == GPG_ERR_KEY_EXPIRED)
+ sum |= GPGME_SIGSUM_GREEN;
+ }
+ else if (sig->validity == GPGME_VALIDITY_NEVER)
+ {
+ if (gpg_err_code (sig->status) == GPG_ERR_NO_ERROR
+ || gpg_err_code (sig->status) == GPG_ERR_SIG_EXPIRED
+ || gpg_err_code (sig->status) == GPG_ERR_KEY_EXPIRED)
+ sum |= GPGME_SIGSUM_RED;
+ }
+ else if (gpg_err_code (sig->status) == GPG_ERR_BAD_SIGNATURE)
+ sum |= GPGME_SIGSUM_RED;
+
+
+ /* FIXME: handle the case when key and message are expired. */
+ switch (gpg_err_code (sig->status))
+ {
+ case GPG_ERR_SIG_EXPIRED:
+ sum |= GPGME_SIGSUM_SIG_EXPIRED;
+ break;
+
+ case GPG_ERR_KEY_EXPIRED:
+ sum |= GPGME_SIGSUM_KEY_EXPIRED;
+ break;
+
+ case GPG_ERR_NO_PUBKEY:
+ sum |= GPGME_SIGSUM_KEY_MISSING;
+ break;
+
+ case GPG_ERR_BAD_SIGNATURE:
+ case GPG_ERR_NO_ERROR:
+ break;
+
+ default:
+ sum |= GPGME_SIGSUM_SYS_ERROR;
+ break;
+ }
+
+ /* Now look at the certain reason codes. */
+ switch (gpg_err_code (sig->validity_reason))
+ {
+ case GPG_ERR_CRL_TOO_OLD:
+ if (sig->validity == GPGME_VALIDITY_UNKNOWN)
+ sum |= GPGME_SIGSUM_CRL_TOO_OLD;
+ break;
+
+ case GPG_ERR_CERT_REVOKED:
+ sum |= GPGME_SIGSUM_KEY_REVOKED;
+ break;
+
+ default:
+ break;
+ }
+
+ /* Check other flags. */
+ if (sig->wrong_key_usage)
+ sum |= GPGME_SIGSUM_BAD_POLICY;
+
+ /* Set the valid flag when the signature is unquestionable
+ valid. (The test is identical to if(sum == GPGME_SIGSUM_GREEN)). */
+ if ((sum & GPGME_SIGSUM_GREEN) && !(sum & ~GPGME_SIGSUM_GREEN))
+ sum |= GPGME_SIGSUM_VALID;
+
+ sig->summary = sum;
+}
+
+
+static gpgme_error_t
+prepare_new_sig (op_data_t opd)
+{
+ gpgme_signature_t sig;
+
+ if (opd->only_newsig_seen && opd->current_sig)
+ {
+ /* We have only seen the NEWSIG status and nothing else - we
+ better skip this signature therefore and reuse it for the
+ next possible signature. */
+ sig = opd->current_sig;
+ memset (sig, 0, sizeof *sig);
+ assert (opd->result.signatures == sig);
+ }
+ else
+ {
+ sig = calloc (1, sizeof (*sig));
+ if (!sig)
+ return gpg_error_from_syserror ();
+ if (!opd->result.signatures)
+ opd->result.signatures = sig;
+ if (opd->current_sig)
+ opd->current_sig->next = sig;
+ opd->current_sig = sig;
+ }
+ opd->did_prepare_new_sig = 1;
+ opd->only_newsig_seen = 0;
+ return 0;
+}
+
+static gpgme_error_t
+parse_new_sig (op_data_t opd, gpgme_status_code_t code, char *args)
+{
+ gpgme_signature_t sig;
+ char *end = strchr (args, ' ');
+ char *tail;
+
+ if (end)
+ {
+ *end = '\0';
+ end++;
+ }
+
+ if (!opd->did_prepare_new_sig)
+ {
+ gpg_error_t err;
+
+ err = prepare_new_sig (opd);
+ if (err)
+ return err;
+ }
+ assert (opd->did_prepare_new_sig);
+ opd->did_prepare_new_sig = 0;
+
+ assert (opd->current_sig);
+ sig = opd->current_sig;
+
+ /* FIXME: We should set the source of the state. */
+ switch (code)
+ {
+ case GPGME_STATUS_GOODSIG:
+ sig->status = gpg_error (GPG_ERR_NO_ERROR);
+ break;
+
+ case GPGME_STATUS_EXPSIG:
+ sig->status = gpg_error (GPG_ERR_SIG_EXPIRED);
+ break;
+
+ case GPGME_STATUS_EXPKEYSIG:
+ sig->status = gpg_error (GPG_ERR_KEY_EXPIRED);
+ break;
+
+ case GPGME_STATUS_BADSIG:
+ sig->status = gpg_error (GPG_ERR_BAD_SIGNATURE);
+ break;
+
+ case GPGME_STATUS_REVKEYSIG:
+ sig->status = gpg_error (GPG_ERR_CERT_REVOKED);
+ break;
+
+ case GPGME_STATUS_ERRSIG:
+ /* Parse the pubkey algo. */
+ if (!end)
+ goto parse_err_sig_fail;
+ gpg_err_set_errno (0);
+ sig->pubkey_algo = strtol (end, &tail, 0);
+ if (errno || end == tail || *tail != ' ')
+ goto parse_err_sig_fail;
+ end = tail;
+ while (*end == ' ')
+ end++;
+
+ /* Parse the hash algo. */
+ if (!*end)
+ goto parse_err_sig_fail;
+ gpg_err_set_errno (0);
+ sig->hash_algo = strtol (end, &tail, 0);
+ if (errno || end == tail || *tail != ' ')
+ goto parse_err_sig_fail;
+ end = tail;
+ while (*end == ' ')
+ end++;
+
+ /* Skip the sig class. */
+ end = strchr (end, ' ');
+ if (!end)
+ goto parse_err_sig_fail;
+ while (*end == ' ')
+ end++;
+
+ /* Parse the timestamp. */
+ sig->timestamp = _gpgme_parse_timestamp (end, &tail);
+ if (sig->timestamp == -1 || end == tail || (*tail && *tail != ' '))
+ return gpg_error (GPG_ERR_INV_ENGINE);
+ end = tail;
+ while (*end == ' ')
+ end++;
+
+ /* Parse the return code. */
+ if (end[0] && (!end[1] || end[1] == ' '))
+ {
+ switch (end[0])
+ {
+ case '4':
+ sig->status = gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
+ break;
+
+ case '9':
+ sig->status = gpg_error (GPG_ERR_NO_PUBKEY);
+ break;
+
+ default:
+ sig->status = gpg_error (GPG_ERR_GENERAL);
+ }
+ }
+ else
+ goto parse_err_sig_fail;
+
+ goto parse_err_sig_ok;
+
+ parse_err_sig_fail:
+ sig->status = gpg_error (GPG_ERR_GENERAL);
+ parse_err_sig_ok:
+ break;
+
+ default:
+ return gpg_error (GPG_ERR_GENERAL);
+ }
+
+ if (*args)
+ {
+ sig->fpr = strdup (args);
+ if (!sig->fpr)
+ return gpg_error_from_syserror ();
+ }
+ return 0;
+}
+
+
+static gpgme_error_t
+parse_valid_sig (gpgme_signature_t sig, char *args)
+{
+ char *end = strchr (args, ' ');
+ if (end)
+ {
+ *end = '\0';
+ end++;
+ }
+
+ if (!*args)
+ /* We require at least the fingerprint. */
+ return gpg_error (GPG_ERR_GENERAL);
+
+ if (sig->fpr)
+ free (sig->fpr);
+ sig->fpr = strdup (args);
+ if (!sig->fpr)
+ return gpg_error_from_syserror ();
+
+ /* Skip the creation date. */
+ end = strchr (end, ' ');
+ if (end)
+ {
+ char *tail;
+
+ sig->timestamp = _gpgme_parse_timestamp (end, &tail);
+ if (sig->timestamp == -1 || end == tail || (*tail && *tail != ' '))
+ return gpg_error (GPG_ERR_INV_ENGINE);
+ end = tail;
+
+ sig->exp_timestamp = _gpgme_parse_timestamp (end, &tail);
+ if (sig->exp_timestamp == -1 || end == tail || (*tail && *tail != ' '))
+ return gpg_error (GPG_ERR_INV_ENGINE);
+ end = tail;
+
+ while (*end == ' ')
+ end++;
+ /* Skip the signature version. */
+ end = strchr (end, ' ');
+ if (end)
+ {
+ while (*end == ' ')
+ end++;
+
+ /* Skip the reserved field. */
+ end = strchr (end, ' ');
+ if (end)
+ {
+ /* Parse the pubkey algo. */
+ gpg_err_set_errno (0);
+ sig->pubkey_algo = strtol (end, &tail, 0);
+ if (errno || end == tail || *tail != ' ')
+ return gpg_error (GPG_ERR_INV_ENGINE);
+ end = tail;
+
+ while (*end == ' ')
+ end++;
+
+ if (*end)
+ {
+ /* Parse the hash algo. */
+
+ gpg_err_set_errno (0);
+ sig->hash_algo = strtol (end, &tail, 0);
+ if (errno || end == tail || *tail != ' ')
+ return gpg_error (GPG_ERR_INV_ENGINE);
+ end = tail;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+
+static gpgme_error_t
+parse_notation (gpgme_signature_t sig, gpgme_status_code_t code, char *args)
+{
+ gpgme_error_t err;
+ gpgme_sig_notation_t *lastp = &sig->notations;
+ gpgme_sig_notation_t notation = sig->notations;
+ char *end = strchr (args, ' ');
+
+ if (end)
+ *end = '\0';
+
+ if (code == GPGME_STATUS_NOTATION_NAME || code == GPGME_STATUS_POLICY_URL)
+ {
+ /* FIXME: We could keep a pointer to the last notation in the list. */
+ while (notation && notation->value)
+ {
+ lastp = &notation->next;
+ notation = notation->next;
+ }
+
+ if (notation)
+ /* There is another notation name without data for the
+ previous one. The crypto backend misbehaves. */
+ return gpg_error (GPG_ERR_INV_ENGINE);
+
+ err = _gpgme_sig_notation_create (&notation, NULL, 0, NULL, 0, 0);
+ if (err)
+ return err;
+
+ if (code == GPGME_STATUS_NOTATION_NAME)
+ {
+ err = _gpgme_decode_percent_string (args, &notation->name, 0, 0);
+ if (err)
+ {
+ _gpgme_sig_notation_free (notation);
+ return err;
+ }
+
+ notation->name_len = strlen (notation->name);
+
+ /* FIXME: For now we fake the human-readable flag. The
+ critical flag can not be reported as it is not
+ provided. */
+ notation->flags = GPGME_SIG_NOTATION_HUMAN_READABLE;
+ notation->human_readable = 1;
+ }
+ else
+ {
+ /* This is a policy URL. */
+
+ err = _gpgme_decode_percent_string (args, &notation->value, 0, 0);
+ if (err)
+ {
+ _gpgme_sig_notation_free (notation);
+ return err;
+ }
+
+ notation->value_len = strlen (notation->value);
+ }
+ *lastp = notation;
+ }
+ else if (code == GPGME_STATUS_NOTATION_DATA)
+ {
+ int len = strlen (args) + 1;
+ char *dest;
+
+ /* FIXME: We could keep a pointer to the last notation in the list. */
+ while (notation && notation->next)
+ {
+ lastp = &notation->next;
+ notation = notation->next;
+ }
+
+ if (!notation || !notation->name)
+ /* There is notation data without a previous notation
+ name. The crypto backend misbehaves. */
+ return gpg_error (GPG_ERR_INV_ENGINE);
+
+ if (!notation->value)
+ {
+ dest = notation->value = malloc (len);
+ if (!dest)
+ return gpg_error_from_syserror ();
+ }
+ else
+ {
+ int cur_len = strlen (notation->value);
+ dest = realloc (notation->value, len + strlen (notation->value));
+ if (!dest)
+ return gpg_error_from_syserror ();
+ notation->value = dest;
+ dest += cur_len;
+ }
+
+ err = _gpgme_decode_percent_string (args, &dest, len, 0);
+ if (err)
+ return err;
+
+ notation->value_len += strlen (dest);
+ }
+ else
+ return gpg_error (GPG_ERR_INV_ENGINE);
+ return 0;
+}
+
+
+static gpgme_error_t
+parse_trust (gpgme_signature_t sig, gpgme_status_code_t code, char *args)
+{
+ char *end = strchr (args, ' ');
+
+ if (end)
+ *end = '\0';
+
+ switch (code)
+ {
+ case GPGME_STATUS_TRUST_UNDEFINED:
+ default:
+ sig->validity = GPGME_VALIDITY_UNKNOWN;
+ break;
+
+ case GPGME_STATUS_TRUST_NEVER:
+ sig->validity = GPGME_VALIDITY_NEVER;
+ break;
+
+ case GPGME_STATUS_TRUST_MARGINAL:
+ sig->validity = GPGME_VALIDITY_MARGINAL;
+ break;
+
+ case GPGME_STATUS_TRUST_FULLY:
+ case GPGME_STATUS_TRUST_ULTIMATE:
+ sig->validity = GPGME_VALIDITY_FULL;
+ break;
+ }
+
+ sig->validity_reason = 0;
+ sig->chain_model = 0;
+ if (*args)
+ {
+ sig->validity_reason = atoi (args);
+ while (*args && *args != ' ')
+ args++;
+ if (*args)
+ {
+ while (*args == ' ')
+ args++;
+ if (!strncmp (args, "chain", 2) && (args[2] == ' ' || !args[2]))
+ sig->chain_model = 1;
+ }
+ }
+
+ return 0;
+}
+
+
+/* Parse an error status line and if SET_STATUS is true update the
+ result status as appropriate. With SET_STATUS being false, only
+ check for an error. */
+static gpgme_error_t
+parse_error (gpgme_signature_t sig, char *args, int set_status)
+{
+ gpgme_error_t err;
+ char *where = strchr (args, ' ');
+ char *which;
+
+ if (where)
+ {
+ *where = '\0';
+ which = where + 1;
+
+ where = strchr (which, ' ');
+ if (where)
+ *where = '\0';
+
+ where = args;
+ }
+ else
+ return gpg_error (GPG_ERR_INV_ENGINE);
+
+ err = atoi (which);
+
+ if (!strcmp (where, "proc_pkt.plaintext")
+ && gpg_err_code (err) == GPG_ERR_BAD_DATA)
+ {
+ /* This indicates a double plaintext. The only solid way to
+ handle this is by failing the oepration. */
+ return gpg_error (GPG_ERR_BAD_DATA);
+ }
+ else if (!set_status)
+ ;
+ else if (!strcmp (where, "verify.findkey"))
+ sig->status = err;
+ else if (!strcmp (where, "verify.keyusage")
+ && gpg_err_code (err) == GPG_ERR_WRONG_KEY_USAGE)
+ sig->wrong_key_usage = 1;
+
+ return 0;
+}
+
+
+gpgme_error_t
+_gpgme_verify_status_handler (void *priv, gpgme_status_code_t code, char *args)
+{
+ gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
+ gpgme_error_t err;
+ void *hook;
+ op_data_t opd;
+ gpgme_signature_t sig;
+ char *end;
+
+ err = _gpgme_op_data_lookup (ctx, OPDATA_VERIFY, &hook, -1, NULL);
+ opd = hook;
+ if (err)
+ return err;
+
+ sig = opd->current_sig;
+
+ switch (code)
+ {
+ case GPGME_STATUS_NEWSIG:
+ if (sig)
+ calc_sig_summary (sig);
+ err = prepare_new_sig (opd);
+ opd->only_newsig_seen = 1;
+ return err;
+
+ case GPGME_STATUS_GOODSIG:
+ case GPGME_STATUS_EXPSIG:
+ case GPGME_STATUS_EXPKEYSIG:
+ case GPGME_STATUS_BADSIG:
+ case GPGME_STATUS_ERRSIG:
+ case GPGME_STATUS_REVKEYSIG:
+ if (sig && !opd->did_prepare_new_sig)
+ calc_sig_summary (sig);
+ opd->only_newsig_seen = 0;
+ return parse_new_sig (opd, code, args);
+
+ case GPGME_STATUS_VALIDSIG:
+ opd->only_newsig_seen = 0;
+ return sig ? parse_valid_sig (sig, args)
+ : gpg_error (GPG_ERR_INV_ENGINE);
+
+ case GPGME_STATUS_NODATA:
+ opd->only_newsig_seen = 0;
+ if (!sig)
+ return gpg_error (GPG_ERR_NO_DATA);
+ sig->status = gpg_error (GPG_ERR_NO_DATA);
+ break;
+
+ case GPGME_STATUS_UNEXPECTED:
+ opd->only_newsig_seen = 0;
+ if (!sig)
+ return gpg_error (GPG_ERR_GENERAL);
+ sig->status = gpg_error (GPG_ERR_NO_DATA);
+ break;
+
+ case GPGME_STATUS_NOTATION_NAME:
+ case GPGME_STATUS_NOTATION_DATA:
+ case GPGME_STATUS_POLICY_URL:
+ opd->only_newsig_seen = 0;
+ return sig ? parse_notation (sig, code, args)
+ : gpg_error (GPG_ERR_INV_ENGINE);
+
+ case GPGME_STATUS_TRUST_UNDEFINED:
+ case GPGME_STATUS_TRUST_NEVER:
+ case GPGME_STATUS_TRUST_MARGINAL:
+ case GPGME_STATUS_TRUST_FULLY:
+ case GPGME_STATUS_TRUST_ULTIMATE:
+ opd->only_newsig_seen = 0;
+ return sig ? parse_trust (sig, code, args)
+ : gpg_error (GPG_ERR_INV_ENGINE);
+
+ case GPGME_STATUS_PKA_TRUST_BAD:
+ case GPGME_STATUS_PKA_TRUST_GOOD:
+ opd->only_newsig_seen = 0;
+ /* Check that we only get one of these status codes per
+ signature; if not the crypto backend misbehaves. */
+ if (!sig || sig->pka_trust || sig->pka_address)
+ return gpg_error (GPG_ERR_INV_ENGINE);
+ sig->pka_trust = code == GPGME_STATUS_PKA_TRUST_GOOD? 2 : 1;
+ end = strchr (args, ' ');
+ if (end)
+ *end = 0;
+ sig->pka_address = strdup (args);
+ break;
+
+ case GPGME_STATUS_ERROR:
+ opd->only_newsig_seen = 0;
+ /* Some error stati are informational, so we don't return an
+ error code if we are not ready to process this status. */
+ return parse_error (sig, args, !!sig );
+
+ case GPGME_STATUS_EOF:
+ if (sig && !opd->did_prepare_new_sig)
+ calc_sig_summary (sig);
+ if (opd->only_newsig_seen && sig)
+ {
+ gpgme_signature_t sig2;
+ /* The last signature has no valid information - remove it
+ from the list. */
+ assert (!sig->next);
+ if (sig == opd->result.signatures)
+ opd->result.signatures = NULL;
+ else
+ {
+ for (sig2 = opd->result.signatures; sig2; sig2 = sig2->next)
+ if (sig2->next == sig)
+ {
+ sig2->next = NULL;
+ break;
+ }
+ }
+ /* Note that there is no need to release the members of SIG
+ because we won't be here if they have been set. */
+ free (sig);
+ opd->current_sig = NULL;
+ }
+ opd->only_newsig_seen = 0;
+ break;
+
+ 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;
+
+ default:
+ break;
+ }
+ return 0;
+}
+
+
+static gpgme_error_t
+verify_status_handler (void *priv, gpgme_status_code_t code, char *args)
+{
+ gpgme_error_t err;
+
+ err = _gpgme_progress_status_handler (priv, code, args);
+ if (!err)
+ err = _gpgme_verify_status_handler (priv, code, args);
+ return err;
+}
+
+
+gpgme_error_t
+_gpgme_op_verify_init_result (gpgme_ctx_t ctx)
+{
+ void *hook;
+ op_data_t opd;
+
+ return _gpgme_op_data_lookup (ctx, OPDATA_VERIFY, &hook,
+ sizeof (*opd), release_op_data);
+}
+
+
+static gpgme_error_t
+verify_start (gpgme_ctx_t ctx, int synchronous, gpgme_data_t sig,
+ gpgme_data_t signed_text, gpgme_data_t plaintext)
+{
+ gpgme_error_t err;
+
+ err = _gpgme_op_reset (ctx, synchronous);
+ if (err)
+ return err;
+
+ err = _gpgme_op_verify_init_result (ctx);
+ if (err)
+ return err;
+
+ _gpgme_engine_set_status_handler (ctx->engine, verify_status_handler, ctx);
+
+ if (!sig)
+ return gpg_error (GPG_ERR_NO_DATA);
+ if (!signed_text && !plaintext)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ return _gpgme_engine_op_verify (ctx->engine, sig, signed_text, plaintext);
+}
+
+
+/* Decrypt ciphertext CIPHER and make a signature verification within
+ CTX and store the resulting plaintext in PLAIN. */
+gpgme_error_t
+gpgme_op_verify_start (gpgme_ctx_t ctx, gpgme_data_t sig,
+ gpgme_data_t signed_text, gpgme_data_t plaintext)
+{
+ gpg_error_t err;
+ TRACE_BEG3 (DEBUG_CTX, "gpgme_op_verify_start", ctx,
+ "sig=%p, signed_text=%p, plaintext=%p",
+ sig, signed_text, plaintext);
+
+ if (!ctx)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ err = verify_start (ctx, 0, sig, signed_text, plaintext);
+ return TRACE_ERR (err);
+}
+
+
+/* Decrypt ciphertext CIPHER and make a signature verification within
+ CTX and store the resulting plaintext in PLAIN. */
+gpgme_error_t
+gpgme_op_verify (gpgme_ctx_t ctx, gpgme_data_t sig, gpgme_data_t signed_text,
+ gpgme_data_t plaintext)
+{
+ gpgme_error_t err;
+
+ TRACE_BEG3 (DEBUG_CTX, "gpgme_op_verify", ctx,
+ "sig=%p, signed_text=%p, plaintext=%p",
+ sig, signed_text, plaintext);
+
+ if (!ctx)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ err = verify_start (ctx, 1, sig, signed_text, plaintext);
+ if (!err)
+ err = _gpgme_wait_one (ctx);
+ return TRACE_ERR (err);
+}
+
+
+/* Compatibility interfaces. */
+
+/* Get the key used to create signature IDX in CTX and return it in
+ R_KEY. */
+gpgme_error_t
+gpgme_get_sig_key (gpgme_ctx_t ctx, int idx, gpgme_key_t *r_key)
+{
+ gpgme_verify_result_t result;
+ gpgme_signature_t sig;
+
+ if (!ctx)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ result = gpgme_op_verify_result (ctx);
+ sig = result->signatures;
+
+ while (sig && idx)
+ {
+ sig = sig->next;
+ idx--;
+ }
+ if (!sig || idx)
+ return gpg_error (GPG_ERR_EOF);
+
+ return gpgme_get_key (ctx, sig->fpr, r_key, 0);
+}
+
+
+/* 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. */
+const char *
+gpgme_get_sig_status (gpgme_ctx_t ctx, int idx,
+ _gpgme_sig_stat_t *r_stat, time_t *r_created)
+{
+ gpgme_verify_result_t result;
+ gpgme_signature_t sig;
+
+ result = gpgme_op_verify_result (ctx);
+ sig = result->signatures;
+
+ while (sig && idx)
+ {
+ sig = sig->next;
+ idx--;
+ }
+ if (!sig || idx)
+ return NULL;
+
+ if (r_stat)
+ {
+ switch (gpg_err_code (sig->status))
+ {
+ case GPG_ERR_NO_ERROR:
+ *r_stat = GPGME_SIG_STAT_GOOD;
+ break;
+
+ case GPG_ERR_BAD_SIGNATURE:
+ *r_stat = GPGME_SIG_STAT_BAD;
+ break;
+
+ case GPG_ERR_NO_PUBKEY:
+ *r_stat = GPGME_SIG_STAT_NOKEY;
+ break;
+
+ case GPG_ERR_NO_DATA:
+ *r_stat = GPGME_SIG_STAT_NOSIG;
+ break;
+
+ case GPG_ERR_SIG_EXPIRED:
+ *r_stat = GPGME_SIG_STAT_GOOD_EXP;
+ break;
+
+ case GPG_ERR_KEY_EXPIRED:
+ *r_stat = GPGME_SIG_STAT_GOOD_EXPKEY;
+ break;
+
+ default:
+ *r_stat = GPGME_SIG_STAT_ERROR;
+ break;
+ }
+ }
+ if (r_created)
+ *r_created = sig->timestamp;
+ return sig->fpr;
+}
+
+
+/* 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 . */
+unsigned long
+gpgme_get_sig_ulong_attr (gpgme_ctx_t ctx, int idx,
+ _gpgme_attr_t what, int whatidx)
+{
+ gpgme_verify_result_t result;
+ gpgme_signature_t sig;
+
+ result = gpgme_op_verify_result (ctx);
+ sig = result->signatures;
+
+ while (sig && idx)
+ {
+ sig = sig->next;
+ idx--;
+ }
+ if (!sig || idx)
+ return 0;
+
+ switch (what)
+ {
+ case GPGME_ATTR_CREATED:
+ return sig->timestamp;
+
+ case GPGME_ATTR_EXPIRE:
+ return sig->exp_timestamp;
+
+ case GPGME_ATTR_VALIDITY:
+ return (unsigned long) sig->validity;
+
+ case GPGME_ATTR_SIG_STATUS:
+ switch (gpg_err_code (sig->status))
+ {
+ case GPG_ERR_NO_ERROR:
+ return GPGME_SIG_STAT_GOOD;
+
+ case GPG_ERR_BAD_SIGNATURE:
+ return GPGME_SIG_STAT_BAD;
+
+ case GPG_ERR_NO_PUBKEY:
+ return GPGME_SIG_STAT_NOKEY;
+
+ case GPG_ERR_NO_DATA:
+ return GPGME_SIG_STAT_NOSIG;
+
+ case GPG_ERR_SIG_EXPIRED:
+ return GPGME_SIG_STAT_GOOD_EXP;
+
+ case GPG_ERR_KEY_EXPIRED:
+ return GPGME_SIG_STAT_GOOD_EXPKEY;
+
+ default:
+ return GPGME_SIG_STAT_ERROR;
+ }
+
+ case GPGME_ATTR_SIG_SUMMARY:
+ return sig->summary;
+
+ default:
+ break;
+ }
+ return 0;
+}
+
+
+const char *
+gpgme_get_sig_string_attr (gpgme_ctx_t ctx, int idx,
+ _gpgme_attr_t what, int whatidx)
+{
+ gpgme_verify_result_t result;
+ gpgme_signature_t sig;
+
+ result = gpgme_op_verify_result (ctx);
+ sig = result->signatures;
+
+ while (sig && idx)
+ {
+ sig = sig->next;
+ idx--;
+ }
+ if (!sig || idx)
+ return NULL;
+
+ switch (what)
+ {
+ case GPGME_ATTR_FPR:
+ return sig->fpr;
+
+ case GPGME_ATTR_ERRTOK:
+ if (whatidx == 1)
+ return sig->wrong_key_usage ? "Wrong_Key_Usage" : "";
+ else
+ return "";
+ default:
+ break;
+ }
+
+ return NULL;
+}
diff --git a/src/version.c b/src/version.c
new file mode 100644
index 0000000..3aef404
--- /dev/null
+++ b/src/version.c
@@ -0,0 +1,353 @@
+/* version.c - Version check routines.
+ Copyright (C) 2000 Werner Koch (dd9jn)
+ Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2008 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <ctype.h>
+#ifdef HAVE_W32_SYSTEM
+#include <winsock2.h>
+#endif
+
+#include "gpgme.h"
+#include "priv-io.h"
+#include "debug.h"
+#include "context.h"
+
+/* For _gpgme_sema_subsystem_init and _gpgme_status_init. */
+#include "sema.h"
+#include "util.h"
+
+#ifdef HAVE_ASSUAN_H
+#include "assuan.h"
+#endif
+
+#ifdef HAVE_W32_SYSTEM
+#include "windows.h"
+#endif
+
+/* We implement this function, so we have to disable the overriding
+ macro. */
+#undef gpgme_check_version
+
+
+/* Bootstrap the subsystems needed for concurrent operation. This
+ must be done once at startup. We can not guarantee this using a
+ lock, though, because the semaphore subsystem needs to be
+ initialized itself before it can be used. So we expect that the
+ user performs the necessary synchronization. */
+static void
+do_subsystem_inits (void)
+{
+ static int done = 0;
+
+ if (done)
+ return;
+
+#ifdef HAVE_W32_SYSTEM
+ /* We need to make sure that the sockets are initialized. */
+ {
+ WSADATA wsadat;
+
+ WSAStartup (0x202, &wsadat);
+ }
+#endif
+
+ _gpgme_sema_subsystem_init ();
+ _gpgme_debug_subsystem_init ();
+ _gpgme_io_subsystem_init ();
+ _gpgme_status_init ();
+
+ done = 1;
+}
+
+
+/* Read the next number in the version string STR and return it in
+ *NUMBER. Return a pointer to the tail of STR after parsing, or
+ *NULL if the version string was invalid. */
+static const char *
+parse_version_number (const char *str, int *number)
+{
+#define MAXVAL ((INT_MAX - 10) / 10)
+ int val = 0;
+
+ /* Leading zeros are not allowed. */
+ if (*str == '0' && isdigit(str[1]))
+ return NULL;
+
+ while (isdigit (*str) && val <= MAXVAL)
+ {
+ val *= 10;
+ val += *(str++) - '0';
+ }
+ *number = val;
+ return val > MAXVAL ? NULL : str;
+}
+
+
+/* Parse the version string STR in the format MAJOR.MINOR.MICRO (for
+ example, 9.3.2) and return the components in MAJOR, MINOR and MICRO
+ as integers. The function returns the tail of the string that
+ follows the version number. This might be te empty string if there
+ is nothing following the version number, or a patchlevel. The
+ function returns NULL if the version string is not valid. */
+static const char *
+parse_version_string (const char *str, int *major, int *minor, int *micro)
+{
+ str = parse_version_number (str, major);
+ if (!str || *str != '.')
+ return NULL;
+ str++;
+
+ str = parse_version_number (str, minor);
+ if (!str || *str != '.')
+ return NULL;
+ str++;
+
+ str = parse_version_number (str, micro);
+ if (!str)
+ return NULL;
+
+ /* A patchlevel might follow. */
+ return str;
+}
+
+
+/* Return true if MY_VERSION is at least REQ_VERSION, and false
+ otherwise. */
+int
+_gpgme_compare_versions (const char *my_version,
+ const char *rq_version)
+{
+ int my_major, my_minor, my_micro;
+ int rq_major, rq_minor, rq_micro;
+ const char *my_plvl, *rq_plvl;
+
+ if (!rq_version)
+ return 1;
+ if (!my_version)
+ return 0;
+
+ my_plvl = parse_version_string (my_version, &my_major, &my_minor, &my_micro);
+ if (!my_plvl)
+ return 0;
+
+ rq_plvl = parse_version_string (rq_version, &rq_major, &rq_minor, &rq_micro);
+ if (!rq_plvl)
+ return 0;
+
+ if (my_major > rq_major
+ || (my_major == rq_major && my_minor > rq_minor)
+ || (my_major == rq_major && my_minor == rq_minor
+ && my_micro > rq_micro)
+ || (my_major == rq_major && my_minor == rq_minor
+ && my_micro == rq_micro && strcmp (my_plvl, rq_plvl) >= 0))
+ return 1;
+
+ return 0;
+}
+
+
+/* Check that the the version of the library is at minimum the
+ requested one and return the version string; return NULL if the
+ condition is not met. If a NULL is passed to this function, no
+ check is done and the version string is simply returned.
+
+ This function must be run once at startup, as it also initializes
+ some subsystems. Its invocation must be synchronized against
+ calling any of the other functions in a multi-threaded
+ environments. */
+const char *
+gpgme_check_version (const char *req_version)
+{
+ char *result;
+ do_subsystem_inits ();
+
+ /* Catch-22: We need to get at least the debug subsystem ready
+ before using the trace facility. If we won't the trace would
+ automagically initialize the debug system with out the locks
+ being initialized and missing the assuan log level setting. */
+ TRACE2 (DEBUG_INIT, "gpgme_check_version", 0,
+ "req_version=%s, VERSION=%s",
+ req_version? req_version:"(null)", VERSION);
+
+ result = _gpgme_compare_versions (VERSION, req_version) ? VERSION : NULL;
+ if (result != NULL)
+ _gpgme_selftest = 0;
+
+ return result;
+}
+
+/* Check the version and also at runtime if the struct layout of the
+ library matches the one of the user. This is particular useful for
+ Windows targets (-mms-bitfields). */
+const char *
+gpgme_check_version_internal (const char *req_version,
+ size_t offset_sig_validity)
+{
+ const char *result;
+
+ result = gpgme_check_version (req_version);
+ if (result == NULL)
+ return result;
+
+ /* Catch-22, see above. */
+ TRACE2 (DEBUG_INIT, "gpgme_check_version_internal", 0,
+ "req_version=%s, offset_sig_validity=%i",
+ req_version ? req_version : "(null)", offset_sig_validity);
+
+ if (offset_sig_validity != offsetof (struct _gpgme_signature, validity))
+ {
+ TRACE1 (DEBUG_INIT, "gpgme_check_version_internal", 0,
+ "offset_sig_validity mismatch: expected %i",
+ offsetof (struct _gpgme_signature, validity));
+ _gpgme_selftest = GPG_ERR_SELFTEST_FAILED;
+ }
+
+ return result;
+}
+
+
+#define LINELENGTH 80
+
+/* Extract the version string of a program from STRING. The version
+ number is expected to be in GNU style format:
+
+ foo 1.2.3
+ foo (bar system) 1.2.3
+ foo 1.2.3 cruft
+ foo (bar system) 1.2.3 cruft.
+
+ Spaces and tabs are skipped and used as delimiters, a term in
+ (nested) parenthesis before the version string is skipped, the
+ version string may consist of any non-space and non-tab characters
+ but needs to bstart with a digit.
+*/
+static const char *
+extract_version_string (const char *string, size_t *r_len)
+{
+ const char *s;
+ int count, len;
+
+ for (s=string; *s; s++)
+ if (*s == ' ' || *s == '\t')
+ break;
+ while (*s == ' ' || *s == '\t')
+ s++;
+ if (*s == '(')
+ {
+ for (count=1, s++; count && *s; s++)
+ if (*s == '(')
+ count++;
+ else if (*s == ')')
+ count--;
+ }
+ /* For robustness we look for a digit. */
+ while ( *s && !(*s >= '0' && *s <= '9') )
+ s++;
+ if (*s >= '0' && *s <= '9')
+ {
+ for (len=0; s[len]; len++)
+ if (s[len] == ' ' || s[len] == '\t')
+ break;
+ }
+ else
+ len = 0;
+
+ *r_len = len;
+ return s;
+}
+
+
+/* Retrieve the version number from the --version output of the
+ program FILE_NAME. */
+char *
+_gpgme_get_program_version (const char *const file_name)
+{
+ char line[LINELENGTH] = "";
+ int linelen = 0;
+ char *mark = NULL;
+ int rp[2];
+ int nread;
+ char *argv[] = {NULL /* file_name */, "--version", 0};
+ struct spawn_fd_item_s cfd[] = { {-1, 1 /* STDOUT_FILENO */, -1, 0},
+ {-1, -1} };
+ int status;
+
+ if (!file_name)
+ return NULL;
+ argv[0] = (char *) file_name;
+
+ if (_gpgme_io_pipe (rp, 1) < 0)
+ return NULL;
+
+ cfd[0].fd = rp[1];
+
+ status = _gpgme_io_spawn (file_name, argv, 0, cfd, NULL, NULL, NULL);
+ if (status < 0)
+ {
+ _gpgme_io_close (rp[0]);
+ _gpgme_io_close (rp[1]);
+ return NULL;
+ }
+
+ do
+ {
+ nread = _gpgme_io_read (rp[0], &line[linelen], LINELENGTH - linelen - 1);
+ if (nread > 0)
+ {
+ line[linelen + nread] = '\0';
+ mark = strchr (&line[linelen], '\n');
+ if (mark)
+ {
+ if (mark > &line[0] && *mark == '\r')
+ mark--;
+ *mark = '\0';
+ break;
+ }
+ linelen += nread;
+ }
+ }
+ while (nread > 0 && linelen < LINELENGTH - 1);
+
+ _gpgme_io_close (rp[0]);
+
+ if (mark)
+ {
+ size_t len;
+ const char *s;
+
+ s = extract_version_string (line, &len);
+ if (!len)
+ return NULL;
+ mark = malloc (len + 1);
+ if (!mark)
+ return NULL;
+ memcpy (mark, s, len);
+ mark[len] = 0;
+ return mark;
+ }
+
+ return NULL;
+}
diff --git a/src/versioninfo.rc.in b/src/versioninfo.rc.in
new file mode 100644
index 0000000..9c1b4f5
--- /dev/null
+++ b/src/versioninfo.rc.in
@@ -0,0 +1,52 @@
+/* versioninfo.rc.in - for gpgme
+ * Copyright (C) 2005 g10 Code GmbH
+ *
+ * This file is free software; as a special exception the author gives
+ * unlimited permission to copy and/or distribute it, with or without
+ * modifications, as long as this notice is preserved.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* This file is processed by configure to create versioninfo.rc */
+
+#line __LINE__ "versioninfo.rc.in"
+
+#include <afxres.h>
+
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION @LIBGPGME_LT_CURRENT@,@LIBGPGME_LT_AGE@,@LIBGPGME_LT_REVISION@,@BUILD_NUMBER@
+ PRODUCTVERSION @BUILD_FILEVERSION@
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x21L
+#else
+ FILEFLAGS 0x20L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x1L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "Comments", "Provided under the terms of the GNU Lesser General Public License.\0"
+ VALUE "CompanyName", "g10 Code GmbH\0"
+ VALUE "FileDescription", "GPGME - GnuPG Made Easy\0"
+ VALUE "FileVersion", "@LIBGPGME_LT_CURRENT@.@LIBGPGME_LT_AGE@.@LIBGPGME_LT_REVISION@.@BUILD_NUMBER@\0"
+ VALUE "InternalName", "gpgme\0"
+ VALUE "LegalCopyright", "Copyright © 2005 g10 Code GmbH\0"
+ VALUE "LegalTrademarks", "\0"
+ VALUE "OriginalFilename", "gpgme.dll\0"
+ VALUE "PrivateBuild", "\0"
+ VALUE "ProductName", "GPGME\0"
+ VALUE "ProductVersion", "@VERSION@\0"
+ VALUE "SpecialBuild", "@BUILD_TIMESTAMP@\0"
+ END
+ END
+END
+
diff --git a/src/vfs-create.c b/src/vfs-create.c
new file mode 100644
index 0000000..e8dab47
--- /dev/null
+++ b/src/vfs-create.c
@@ -0,0 +1,203 @@
+/* vfs-create.c - vfs create support in GPGME
+ Copyright (C) 2009 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "gpgme.h"
+#include "debug.h"
+#include "context.h"
+#include "ops.h"
+#include "util.h"
+
+static gpgme_error_t
+vfs_start (gpgme_ctx_t ctx, int synchronous,
+ const char *command,
+ gpgme_assuan_data_cb_t data_cb,
+ void *data_cb_value,
+ gpgme_assuan_inquire_cb_t inq_cb,
+ void *inq_cb_value,
+ gpgme_assuan_status_cb_t status_cb,
+ void *status_cb_value)
+{
+ gpgme_error_t err;
+
+ if (!command || !*command)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ /* The flag value 256 is used to suppress an engine reset. This is
+ required to keep the connection running. */
+ err = _gpgme_op_reset (ctx, ((synchronous & 255) | 256));
+ if (err)
+ return err;
+
+ return _gpgme_engine_op_assuan_transact (ctx->engine, command,
+ data_cb, data_cb_value,
+ inq_cb, inq_cb_value,
+ status_cb, status_cb_value);
+}
+
+
+#if 0
+/* XXXX. This is the asynchronous variant. */
+static gpgme_error_t
+gpgme_op_vfs_transact_start (gpgme_ctx_t ctx,
+ const char *command,
+ gpgme_assuan_data_cb_t data_cb,
+ void *data_cb_value,
+ gpgme_assuan_inquire_cb_t inq_cb,
+ void *inq_cb_value,
+ gpgme_assuan_status_cb_t status_cb,
+ void *status_cb_value)
+{
+ return vfs_start (ctx, 0, command, data_cb, data_cb_value,
+ inq_cb, inq_cb_value, status_cb, status_cb_value);
+}
+#endif
+
+
+/* XXXX. This is the synchronous variant. */
+static gpgme_error_t
+gpgme_op_vfs_transact (gpgme_ctx_t ctx,
+ const char *command,
+ gpgme_assuan_data_cb_t data_cb,
+ void *data_cb_value,
+ gpgme_assuan_inquire_cb_t inq_cb,
+ void *inq_cb_value,
+ gpgme_assuan_status_cb_t status_cb,
+ void *status_cb_value,
+ gpgme_error_t *op_err)
+{
+ gpgme_error_t err;
+
+ if (!ctx)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ err = vfs_start (ctx, 1, command, data_cb, data_cb_value,
+ inq_cb, inq_cb_value, status_cb, status_cb_value);
+ if (!err)
+ err = _gpgme_wait_one_ext (ctx, op_err);
+ return err;
+}
+
+
+/* The actual exported interface follows. */
+
+/* The container is automatically uncreateed when the context is reset
+ or destroyed. This is a synchronous convenience interface, which
+ automatically returns an operation error if there is no
+ transmission error. */
+static gpgme_error_t
+_gpgme_op_vfs_create (gpgme_ctx_t ctx, gpgme_key_t recp[],
+ const char *container_file, unsigned int flags,
+ gpgme_error_t *op_err)
+{
+ gpg_error_t err;
+ char *cmd;
+ char *container_file_esc = NULL;
+ int i;
+
+ /* We want to encourage people to check error values, so not getting
+ them is discouraged here. Also makes our code easier. */
+ if (! op_err)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ err = _gpgme_encode_percent_string (container_file, &container_file_esc, 0);
+ if (err)
+ return err;
+
+ i = 0;
+ while (!err && recp[i])
+ {
+ if (!recp[i]->subkeys || !recp[i]->subkeys->fpr)
+ {
+ free (container_file_esc);
+ return gpg_error (GPG_ERR_UNUSABLE_PUBKEY);
+ }
+
+ if (asprintf (&cmd, "RECIPIENT %s", recp[i]->subkeys->fpr) < 0)
+ {
+ err = gpg_error_from_syserror ();
+ free (container_file_esc);
+ return err;
+ }
+
+ err = gpgme_op_vfs_transact (ctx, cmd, NULL, NULL, NULL, NULL,
+ NULL, NULL, op_err);
+ free (cmd);
+ if (err || *op_err)
+ {
+ free (container_file_esc);
+ return err;
+ }
+ recp++;
+ }
+
+ if (asprintf (&cmd, "CREATE -- %s", container_file_esc) < 0)
+ {
+ err = gpg_error_from_syserror ();
+ free (container_file_esc);
+ return err;
+ }
+ free (container_file_esc);
+
+ err = gpgme_op_vfs_transact (ctx, cmd, NULL, NULL, NULL, NULL,
+ NULL, NULL, op_err);
+ free (cmd);
+
+ return err;
+}
+
+
+gpgme_error_t
+gpgme_op_vfs_create (gpgme_ctx_t ctx, gpgme_key_t recp[],
+ const char *container_file, unsigned int flags,
+ gpgme_error_t *op_err)
+{
+ gpg_error_t err;
+
+ TRACE_BEG3 (DEBUG_CTX, "gpgme_op_vfs_create", ctx,
+ "container_file=%s, flags=0x%x, op_err=%p",
+ container_file, flags, op_err);
+
+ if (!ctx)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ if (_gpgme_debug_trace () && 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++;
+ }
+ }
+
+ err = _gpgme_op_vfs_create (ctx, recp, container_file, flags, op_err);
+ return TRACE_ERR (err);
+}
+
diff --git a/src/vfs-mount.c b/src/vfs-mount.c
new file mode 100644
index 0000000..327e353
--- /dev/null
+++ b/src/vfs-mount.c
@@ -0,0 +1,245 @@
+/* vfs-mount.c - vfs mount support in GPGME
+ Copyright (C) 2009 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+
+#include "gpgme.h"
+#include "debug.h"
+#include "context.h"
+#include "ops.h"
+#include "util.h"
+
+typedef struct
+{
+ struct _gpgme_op_vfs_mount_result result;
+} *op_data_t;
+
+
+
+gpgme_vfs_mount_result_t
+gpgme_op_vfs_mount_result (gpgme_ctx_t ctx)
+{
+ gpgme_error_t err;
+ void *hook;
+ op_data_t opd;
+
+ err = _gpgme_op_data_lookup (ctx, OPDATA_VFS_MOUNT, &hook, -1, NULL);
+ opd = hook;
+ /* Check in case this function is used without having run a command
+ before. */
+ if (err || !opd)
+ return NULL;
+
+ return &opd->result;
+}
+
+
+static gpgme_error_t
+_gpgme_vfs_mount_status_handler (void *priv, const char *code, const char *args)
+{
+ gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
+ gpgme_error_t err;
+ void *hook;
+ op_data_t opd;
+
+ err = _gpgme_op_data_lookup (ctx, OPDATA_VFS_MOUNT, &hook, -1, NULL);
+ opd = hook;
+ if (err)
+ return err;
+
+ if (! strcasecmp ("MOUNTPOINT", code))
+ {
+ if (opd->result.mount_dir)
+ free (opd->result.mount_dir);
+ opd->result.mount_dir = strdup (args);
+ }
+
+ return 0;
+}
+
+
+static gpgme_error_t
+vfs_start (gpgme_ctx_t ctx, int synchronous,
+ const char *command,
+ gpgme_assuan_data_cb_t data_cb,
+ void *data_cb_value,
+ gpgme_assuan_inquire_cb_t inq_cb,
+ void *inq_cb_value,
+ gpgme_assuan_status_cb_t status_cb,
+ void *status_cb_value)
+{
+ gpgme_error_t err;
+ void *hook;
+ op_data_t opd;
+
+ if (!command || !*command)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ /* The flag value 256 is used to suppress an engine reset. This is
+ required to keep the connection running. */
+ err = _gpgme_op_reset (ctx, ((synchronous & 255) | 256));
+ if (err)
+ return err;
+
+ err = _gpgme_op_data_lookup (ctx, OPDATA_VFS_MOUNT, &hook, sizeof (*opd),
+ NULL);
+ opd = hook;
+ if (err)
+ return err;
+
+ return _gpgme_engine_op_assuan_transact (ctx->engine, command,
+ data_cb, data_cb_value,
+ inq_cb, inq_cb_value,
+ status_cb, status_cb_value);
+}
+
+
+
+#if 0
+/* XXXX. This is the asynchronous variant. */
+static gpgme_error_t
+gpgme_op_vfs_transact_start (gpgme_ctx_t ctx,
+ const char *command,
+ gpgme_assuan_data_cb_t data_cb,
+ void *data_cb_value,
+ gpgme_assuan_inquire_cb_t inq_cb,
+ void *inq_cb_value,
+ gpgme_assuan_status_cb_t status_cb,
+ void *status_cb_value)
+{
+ return vfs_start (ctx, 0, command, data_cb, data_cb_value,
+ inq_cb, inq_cb_value, status_cb, status_cb_value);
+}
+#endif
+
+
+/* XXXX. This is the synchronous variant. */
+static gpgme_error_t
+gpgme_op_vfs_transact (gpgme_ctx_t ctx,
+ const char *command,
+ gpgme_assuan_data_cb_t data_cb,
+ void *data_cb_value,
+ gpgme_assuan_inquire_cb_t inq_cb,
+ void *inq_cb_value,
+ gpgme_assuan_status_cb_t status_cb,
+ void *status_cb_value,
+ gpgme_error_t *op_err)
+{
+ gpgme_error_t err;
+
+ if (!ctx)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ err = vfs_start (ctx, 1, command, data_cb, data_cb_value,
+ inq_cb, inq_cb_value, status_cb, status_cb_value);
+ if (!err)
+ err = _gpgme_wait_one_ext (ctx, op_err);
+ return err;
+}
+
+
+/* The actual exported interface follows. */
+
+/* The container is automatically unmounted when the context is reset
+ or destroyed. This is a synchronous convenience interface, which
+ automatically returns an operation error if there is no
+ transmission error. */
+static gpgme_error_t
+_gpgme_op_vfs_mount (gpgme_ctx_t ctx, const char *container_file,
+ const char *mount_dir, int flags, gpgme_error_t *op_err)
+{
+ gpg_error_t err;
+ char *cmd;
+ char *container_file_esc = NULL;
+
+ /* We want to encourage people to check error values, so not getting
+ them is discouraged here. Also makes our code easier. */
+ if (! op_err)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ err = _gpgme_encode_percent_string (container_file, &container_file_esc, 0);
+ if (err)
+ return err;
+
+ if (asprintf (&cmd, "OPEN -- %s", container_file_esc) < 0)
+ {
+ err = gpg_error_from_syserror ();
+ free (container_file_esc);
+ return err;
+ }
+ free (container_file_esc);
+
+ err = gpgme_op_vfs_transact (ctx, cmd, NULL, NULL, NULL, NULL,
+ NULL, NULL, op_err);
+ free (cmd);
+ if (err || *op_err)
+ return err;
+
+ if (mount_dir)
+ {
+ char *mount_dir_esc = NULL;
+
+ err = _gpgme_encode_percent_string (mount_dir, &mount_dir_esc, 0);
+ if (err)
+ return err;
+
+ if (asprintf (&cmd, "MOUNT -- %s", mount_dir_esc) < 0)
+ {
+ err = gpg_error_from_syserror ();
+ free (mount_dir_esc);
+ return err;
+ }
+ free (mount_dir_esc);
+ }
+ else
+ {
+ if (asprintf (&cmd, "MOUNT") < 0)
+ return gpg_error_from_syserror ();
+ }
+
+ err = gpgme_op_vfs_transact (ctx, cmd, NULL, NULL, NULL, NULL,
+ _gpgme_vfs_mount_status_handler, ctx, op_err);
+ free (cmd);
+
+ return 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)
+{
+ gpg_error_t err;
+
+ TRACE_BEG4 (DEBUG_CTX, "gpgme_op_vfs_mount", ctx,
+ "container=%s, mount_dir=%s, flags=0x%x, op_err=%p",
+ container_file, mount_dir, flags, op_err);
+
+ if (!ctx)
+ return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+ err = _gpgme_op_vfs_mount (ctx, container_file, mount_dir, flags, op_err);
+ return TRACE_ERR (err);
+}
+
diff --git a/src/w32-ce.c b/src/w32-ce.c
new file mode 100644
index 0000000..88a8f31
--- /dev/null
+++ b/src/w32-ce.c
@@ -0,0 +1,507 @@
+/* w32-ce.h
+ Copyright (C) 2010 g10 Code GmbH
+ Copyright (C) 1991,92,97,2000,02 Free Software Foundation, Inc.
+
+ 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/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include <gpg-error.h>
+
+#define _WIN32_IE 0x0400 /* Required for SHGetSpecialFolderPathW. */
+
+/* We need to include the windows stuff here prior to shlobj.h so that
+ we get the right winsock version. This is usually done in w32-ce.h
+ but that header also redefines some Windows functions which we need
+ to avoid unless having included shlobj.h. */
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <windows.h>
+#include <shlobj.h>
+
+#include "w32-ce.h"
+
+/* Return a malloced string encoded in UTF-8 from the wide char input
+ string STRING. Caller must free this value. Returns NULL and sets
+ ERRNO on failure. Calling this function with STRING set to NULL is
+ not defined. */
+char *
+wchar_to_utf8 (const wchar_t *string)
+{
+ int n;
+ char *result;
+
+ n = WideCharToMultiByte (CP_UTF8, 0, string, -1, NULL, 0, NULL, NULL);
+ if (n < 0)
+ {
+ gpg_err_set_errno (EINVAL);
+ return NULL;
+ }
+
+ result = malloc (n+1);
+ if (!result)
+ return NULL;
+
+ n = WideCharToMultiByte (CP_UTF8, 0, string, -1, result, n, NULL, NULL);
+ if (n < 0)
+ {
+ free (result);
+ gpg_err_set_errno (EINVAL);
+ result = NULL;
+ }
+ return result;
+}
+
+
+/* Return a malloced wide char string from an UTF-8 encoded input
+ string STRING. Caller must free this value. Returns NULL and sets
+ ERRNO on failure. Calling this function with STRING set to NULL is
+ not defined. */
+wchar_t *
+utf8_to_wchar (const char *string)
+{
+ int n;
+ size_t nbytes;
+ wchar_t *result;
+
+ n = MultiByteToWideChar (CP_UTF8, 0, string, -1, NULL, 0);
+ if (n < 0)
+ {
+ gpg_err_set_errno (EINVAL);
+ return NULL;
+ }
+
+ nbytes = (size_t)(n+1) * sizeof(*result);
+ if (nbytes / sizeof(*result) != (n+1))
+ {
+ gpg_err_set_errno (ENOMEM);
+ return NULL;
+ }
+ result = malloc (nbytes);
+ if (!result)
+ return NULL;
+
+ n = MultiByteToWideChar (CP_UTF8, 0, string, -1, result, n);
+ if (n < 0)
+ {
+ free (result);
+ gpg_err_set_errno (EINVAL);
+ result = NULL;
+ }
+ return result;
+}
+
+
+#define MAX_ENV 30
+
+char *environ[MAX_ENV + 1];
+
+char *
+getenv (const char *name)
+{
+ static char *past_result;
+ char **envp;
+
+ if (past_result)
+ {
+ free (past_result);
+ past_result = NULL;
+ }
+
+#if 0
+ if (! strcmp (name, "DBUS_VERBOSE"))
+ return past_result = get_verbose_setting ();
+ else if (! strcmp (name, "HOMEPATH"))
+ return past_result = find_my_documents_folder ();
+ else if (! strcmp (name, "DBUS_DATADIR"))
+ return past_result = find_inst_subdir ("share");
+#endif
+
+ for (envp = environ; *envp != 0; envp++)
+ {
+ const char *varp = name;
+ char *ep = *envp;
+
+ while (*varp == *ep && *varp != '\0')
+ {
+ ++ep;
+ ++varp;
+ };
+
+ if (*varp == '\0' && *ep == '=')
+ return ep + 1;
+ }
+
+ return NULL;
+}
+
+
+void
+GetSystemTimeAsFileTime (LPFILETIME ftp)
+{
+ SYSTEMTIME st;
+ GetSystemTime (&st);
+ SystemTimeToFileTime (&st, ftp);
+}
+
+
+BOOL
+DeleteFileA (LPCSTR lpFileName)
+{
+ wchar_t *filename;
+ BOOL result;
+ int err;
+
+ filename = utf8_to_wchar (lpFileName);
+ if (!filename)
+ return FALSE;
+
+ result = DeleteFileW (filename);
+
+ err = GetLastError ();
+ free (filename);
+ SetLastError (err);
+ return result;
+}
+
+
+HANDLE
+CreateFileA (LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwSharedMode,
+ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
+ DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes,
+ HANDLE hTemplateFile)
+{
+ wchar_t *filename;
+ HANDLE result;
+ int err;
+
+ filename = utf8_to_wchar (lpFileName);
+ if (!filename)
+ return INVALID_HANDLE_VALUE;
+
+ result = CreateFileW (filename, dwDesiredAccess, dwSharedMode,
+ lpSecurityAttributes, dwCreationDisposition,
+ dwFlagsAndAttributes, hTemplateFile);
+
+ err = GetLastError ();
+ free (filename);
+ SetLastError (err);
+ return result;
+}
+
+
+BOOL
+CreateProcessA (LPCSTR pszImageName, LPSTR pszCmdLine,
+ LPSECURITY_ATTRIBUTES psaProcess,
+ LPSECURITY_ATTRIBUTES psaThread, BOOL fInheritHandles,
+ DWORD fdwCreate, PVOID pvEnvironment, LPCSTR pszCurDir,
+ LPSTARTUPINFOA psiStartInfo,
+ LPPROCESS_INFORMATION pProcInfo)
+{
+ wchar_t *image_name = NULL;
+ wchar_t *cmd_line = NULL;
+ BOOL result;
+ int err;
+
+ assert (psaProcess == NULL);
+ assert (psaThread == NULL);
+ assert (fInheritHandles == FALSE);
+ assert (pvEnvironment == NULL);
+ assert (pszCurDir == NULL);
+ /* psiStartInfo is generally not NULL. */
+
+ if (pszImageName)
+ {
+ image_name = utf8_to_wchar (pszImageName);
+ if (!image_name)
+ return 0;
+ }
+ if (pszCmdLine)
+ {
+ cmd_line = utf8_to_wchar (pszCmdLine);
+ if (!cmd_line)
+ {
+ if (image_name)
+ free (image_name);
+ return 0;
+ }
+ }
+
+ result = CreateProcessW (image_name, cmd_line, NULL, NULL, FALSE,
+ fdwCreate, NULL, NULL, NULL, pProcInfo);
+
+ err = GetLastError ();
+ free (image_name);
+ free (cmd_line);
+ SetLastError (err);
+ return result;
+}
+
+
+LONG
+RegOpenKeyExA (HKEY hKey, LPCSTR lpSubKey, DWORD ulOptions,
+ REGSAM samDesired, PHKEY phkResult)
+{
+ wchar_t *subkey;
+ LONG result;
+ int err;
+
+ if (lpSubKey)
+ {
+ subkey = utf8_to_wchar (lpSubKey);
+ if (!subkey)
+ return 0;
+ }
+ else
+ subkey = NULL;
+
+ result = RegOpenKeyEx (hKey, subkey, ulOptions, samDesired, phkResult);
+
+ err = GetLastError ();
+ free (subkey);
+ SetLastError (err);
+ return result;
+}
+
+
+LONG WINAPI
+RegQueryValueExA (HKEY hKey, LPCSTR lpValueName, LPDWORD lpReserved,
+ LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData)
+{
+ wchar_t *name;
+ LONG err;
+ void *data;
+ DWORD data_len;
+ DWORD type;
+
+ if (lpValueName)
+ {
+ name = utf8_to_wchar (lpValueName);
+ if (!name)
+ return GetLastError ();
+ }
+ else
+ name = NULL;
+
+ data_len = 0;
+ err = RegQueryValueExW (hKey, name, lpReserved, lpType, NULL, &data_len);
+ if (err || !lpcbData)
+ {
+ free (name);
+ return err;
+ }
+
+ data = malloc (data_len + sizeof (wchar_t));
+ if (!data)
+ {
+ free (name);
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ err = RegQueryValueExW (hKey, name, lpReserved, &type, data, &data_len);
+ if (lpType)
+ *lpType = type;
+ free (name);
+ /* If err is ERROR_MORE_DATA, there probably was a race condition.
+ We can punt this to the caller just as well. */
+ if (err)
+ return err;
+
+ /* NOTE: REG_MULTI_SZ and REG_EXPAND_SZ not supported, because they
+ are not needed in this module. */
+ if (type == REG_SZ)
+ {
+ char *data_c;
+ int data_c_len;
+
+ /* This is valid since we allocated one more above. */
+ ((char*)data)[data_len] = '\0';
+ ((char*)data)[data_len + 1] = '\0';
+
+ data_c = wchar_to_utf8 ((wchar_t*) data);
+ if (!data_c)
+ return GetLastError();
+
+ data_c_len = strlen (data_c) + 1;
+ assert (data_c_len <= data_len + sizeof (wchar_t));
+ memcpy (data, data_c, data_c_len);
+ data_len = data_c_len;
+ free (data_c);
+ }
+
+ /* DATA and DATA_LEN now contain the result. */
+ if (lpData)
+ {
+ if (data_len > *lpcbData)
+ err = ERROR_MORE_DATA;
+ else
+ memcpy (lpData, data, data_len);
+ }
+ *lpcbData = data_len;
+ return err;
+}
+
+
+DWORD
+GetTempPathA (DWORD nBufferLength, LPSTR lpBuffer)
+{
+ wchar_t dummy[1];
+ DWORD len;
+
+ len = GetTempPathW (0, dummy);
+ if (len == 0)
+ return 0;
+
+ assert (len <= MAX_PATH);
+
+ /* Better be safe than sorry. MSDN doesn't say if len is with or
+ without terminating 0. */
+ len++;
+
+ {
+ wchar_t *buffer_w;
+ DWORD len_w;
+ char *buffer_c;
+ DWORD len_c;
+
+ buffer_w = malloc (sizeof (wchar_t) * len);
+ if (! buffer_w)
+ return 0;
+
+ len_w = GetTempPathW (len, buffer_w);
+ /* Give up if we still can't get at it. */
+ if (len_w == 0 || len_w >= len)
+ {
+ free (buffer_w);
+ return 0;
+ }
+
+ /* Better be really safe. */
+ buffer_w[len_w] = '\0';
+
+ buffer_c = wchar_to_utf8 (buffer_w);
+ free (buffer_w);
+ if (! buffer_c)
+ return 0;
+
+ /* strlen is correct (not _mbstrlen), because we want storage and
+ not string length. */
+ len_c = strlen (buffer_c) + 1;
+ if (len_c > nBufferLength)
+ return len_c;
+
+ strcpy (lpBuffer, buffer_c);
+ free (buffer_c);
+ return len_c - 1;
+ }
+}
+
+
+/* The symbol is named SHGetSpecialFolderPath and not
+ SHGetSpecialFolderPathW but shlobj.h from cegcc redefines it to *W
+ which is a bug. Work around it. */
+#ifdef __MINGW32CE__
+# undef SHGetSpecialFolderPath
+#endif
+BOOL
+SHGetSpecialFolderPathA (HWND hwndOwner, LPSTR lpszPath, int nFolder,
+ BOOL fCreate)
+{
+ wchar_t path[MAX_PATH];
+ char *path_c;
+ BOOL result;
+
+ path[0] = (wchar_t) 0;
+ result = SHGetSpecialFolderPath (hwndOwner, path, nFolder, fCreate);
+ /* Note: May return false even if succeeds. */
+
+ path[MAX_PATH - 1] = (wchar_t) 0;
+ path_c = wchar_to_utf8 (path);
+ if (! path_c)
+ return 0;
+
+ strncpy (lpszPath, path_c, MAX_PATH);
+ free (path_c);
+ lpszPath[MAX_PATH - 1] = '\0';
+ return result;
+}
+
+/* Replacement for the access function. Note that we can't use fopen
+ here because wince might now allow to have a shared read for an
+ executable; it is better to to read the file attributes.
+
+ Limitation: Only F_OK is supported.
+*/
+int
+_gpgme_wince_access (const char *fname, int mode)
+{
+ DWORD attr;
+ wchar_t *wfname;
+
+ (void)mode;
+
+ wfname = utf8_to_wchar (fname);
+ if (!wfname)
+ return -1;
+
+ attr = GetFileAttributes (wfname);
+ free (wfname);
+ if (attr == (DWORD)(-1))
+ {
+ gpg_err_set_errno (ENOENT);
+ return -1;
+ }
+ return 0;
+}
+
+
+/* Perform a binary search for KEY in BASE which has NMEMB elements
+ of SIZE bytes each. The comparisons are done by (*COMPAR)().
+ Code taken from glibc-2.6. */
+void *
+_gpgme_wince_bsearch (const void *key, const void *base,
+ size_t nmemb, size_t size,
+ int (*compar) (const void *, const void *))
+{
+ size_t l, u, idx;
+ const void *p;
+ int comparison;
+
+ l = 0;
+ u = nmemb;
+ while (l < u)
+ {
+ idx = (l + u) / 2;
+ p = (void *) (((const char *) base) + (idx * size));
+ comparison = (*compar) (key, p);
+ if (comparison < 0)
+ u = idx;
+ else if (comparison > 0)
+ l = idx + 1;
+ else
+ return (void *) p;
+ }
+
+ return NULL;
+}
+
diff --git a/src/w32-ce.h b/src/w32-ce.h
new file mode 100644
index 0000000..36f1e9e
--- /dev/null
+++ b/src/w32-ce.h
@@ -0,0 +1,91 @@
+/* w32-ce.h
+ Copyright (C) 2010 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#ifndef GPGME_W32_CE_H
+#define GPGME_W32_CE_H
+
+#include <time.h>
+#include <stdarg.h>
+
+#ifdef _MSC_VER
+typedef int pid_t;
+#define strdup _strdup
+#define strcasecmp _stricmp
+#endif
+
+#include <winsock2.h>
+#include <ws2tcpip.h> /* For getaddrinfo. */
+#include <windows.h>
+
+#define getenv _gpgme_wince_getenv
+char *getenv (const char *name);
+
+#include <io.h>
+#define isatty(fd) 0
+
+
+/* Windows CE is missing some Windows functions that we want. */
+
+#define GetSystemTimeAsFileTime _gpgme_wince_GetSystemTimeAsFileTime
+void GetSystemTimeAsFileTime (LPFILETIME ftp);
+
+#define DeleteFileA _gpgme_wince_DeleteFileA
+BOOL DeleteFileA(LPCSTR);
+
+#define CreateFileA _gpgme_wince_CreateFileA
+HANDLE CreateFileA (LPCSTR, DWORD, DWORD, LPSECURITY_ATTRIBUTES,
+ DWORD, DWORD, HANDLE);
+
+#define CreateProcessA _gpgme_wince_CreateProcessA
+BOOL CreateProcessA(LPCSTR,LPSTR,LPSECURITY_ATTRIBUTES,LPSECURITY_ATTRIBUTES,BOOL,DWORD,PVOID,LPCSTR,LPSTARTUPINFOA,LPPROCESS_INFORMATION);
+
+#define RegOpenKeyExA _gpgme_wince_RegOpenKeyExA
+LONG RegOpenKeyExA(HKEY,LPCSTR,DWORD,REGSAM,PHKEY);
+
+#define RegQueryValueExA _gpgme_wince_RegQueryValueExA
+LONG WINAPI RegQueryValueExA(HKEY,LPCSTR,LPDWORD,LPDWORD,LPBYTE,LPDWORD);
+
+#define GetTempPathA _gpgme_wince_GetTempPathA
+DWORD GetTempPathA(DWORD,LPSTR);
+
+#define SHGetSpecialFolderPathA _gpgme_wince_SHGetSpecialFolderPathA
+BOOL SHGetSpecialFolderPathA(HWND,LPSTR,int,BOOL);
+
+int _gpgme_wince_access (const char *fname, int mode);
+#define access(a,b) _gpgme_wince_access ((a), (b))
+
+void *_gpgme_wince_bsearch (const void *key, const void *base,
+ size_t nmemb, size_t size,
+ int (*compar) (const void *, const void *));
+#define bsearch(a,b,c,d,e) _gpgme_wince_bsearch ((a),(b),(c),(d),(e))
+
+#if defined(_MSC_VER)
+ /* Remove the redefined __leave keyword. It is defined by MSC for
+ W32 in excpt.h and not in sehmap.h as for the plain windows
+ version. */
+# undef leave
+# define HKEY_PERFORMANCE_DATA ((HKEY)0x80000004)
+# define HKEY_CURRENT_CONFIG ((HKEY)0x80000005)
+ /* Replace the Mingw32CE provided abort function. */
+# define abort() do { TerminateProcess (GetCurrentProcess(), 8); } while (0)
+# define _IOLBF 0x40
+#endif
+
+#endif /* GPGME_W32_CE_H */
diff --git a/src/w32-glib-io.c b/src/w32-glib-io.c
new file mode 100644
index 0000000..5c72f03
--- /dev/null
+++ b/src/w32-glib-io.c
@@ -0,0 +1,1084 @@
+/* w32-glib-io.c - W32 Glib I/O functions
+ Copyright (C) 2000 Werner Koch (dd9jn)
+ Copyright (C) 2001, 2002, 2004, 2005 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#include <glib.h>
+#include <windows.h>
+#include <io.h>
+
+#include "util.h"
+#include "priv-io.h"
+#include "sema.h"
+#include "debug.h"
+
+#ifndef O_BINARY
+#ifdef _O_BINARY
+#define O_BINARY _O_BINARY
+#else
+#define O_BINARY 0
+#endif
+#endif
+
+
+/* This file is an ugly hack to get GPGME working with glib on Windows
+ targets. On Windows, you can not select() on file descriptors.
+ The only way to check if there is something to read is to read
+ something. This means that GPGME can not let glib check for data
+ without letting glib also handle the data on Windows targets.
+
+ The ugly consequence is that we need to work on GIOChannels in
+ GPGME, creating a glib dependency. Also, we need to export an
+ interface for the application to get at GPGME's GIOChannel. There
+ is no good way to abstract all this with callbacks, because the
+ whole thing is also interconnected with the creation of pipes and
+ child processes.
+
+ The following rule applies only to this I/O backend:
+
+ * ALL operations must use the user defined event loop. GPGME can
+ not anymore provide its own event loop. This is mostly a sanity
+ requirement: Although we have in theory all information we need to
+ make the GPGME W32 code for select still work, it would be a big
+ complication and require changes throughout GPGME.
+
+ Eventually, we probably have to bite the bullet and make some
+ really nice callback interfaces to let the user control all this at
+ a per-context level. */
+
+
+#define MAX_SLAFD 256
+
+static struct
+{
+ int used;
+
+ /* If this is not -1, then it's a libc file descriptor. */
+ int fd;
+ /* If fd is -1, this is the Windows socket handle. */
+ int socket;
+
+ GIOChannel *chan;
+ /* The boolean PRIMARY is true if this file descriptor caused the
+ allocation of CHAN. Only then should CHAN be destroyed when this
+ FD is closed. This, together with the fact that dup'ed file
+ descriptors are closed before the file descriptors from which
+ they are dup'ed are closed, ensures that CHAN is always valid,
+ and shared among all file descriptors refering to the same
+ underlying object.
+
+ The logic behind this is that there is only one reason for us to
+ dup file descriptors anyway: to allow simpler book-keeping of
+ file descriptors shared between GPGME and libassuan, which both
+ want to close something. Using the same channel for these
+ duplicates works just fine (and in fact, using different channels
+ does not work because the W32 backend in glib does not support
+ that: One would end up with several competing reader/writer
+ threads. */
+ int primary;
+} giochannel_table[MAX_SLAFD];
+
+
+static GIOChannel *
+find_channel (int fd)
+{
+ if (fd < 0 || fd >= MAX_SLAFD || !giochannel_table[fd].used)
+ return NULL;
+
+ return giochannel_table[fd].chan;
+}
+
+
+/* Returns the FD or -1 on resource limit. */
+int
+new_dummy_channel_from_fd (int cfd)
+{
+ int idx;
+
+ for (idx = 0; idx < MAX_SLAFD; idx++)
+ if (! giochannel_table[idx].used)
+ break;
+
+ if (idx == MAX_SLAFD)
+ {
+ errno = EIO;
+ return -1;
+ }
+
+ giochannel_table[idx].used = 1;
+ giochannel_table[idx].chan = NULL;
+ giochannel_table[idx].fd = cfd;
+ giochannel_table[idx].socket = INVALID_SOCKET;
+ giochannel_table[idx].primary = 1;
+
+ return idx;
+}
+
+
+/* Returns the FD or -1 on resource limit. */
+int
+new_channel_from_fd (int cfd)
+{
+ int idx;
+
+ for (idx = 0; idx < MAX_SLAFD; idx++)
+ if (! giochannel_table[idx].used)
+ break;
+
+ if (idx == MAX_SLAFD)
+ {
+ errno = EIO;
+ return -1;
+ }
+
+ giochannel_table[idx].used = 1;
+ giochannel_table[idx].chan = g_io_channel_win32_new_fd (cfd);
+ giochannel_table[idx].fd = cfd;
+ giochannel_table[idx].socket = INVALID_SOCKET;
+ giochannel_table[idx].primary = 1;
+
+ g_io_channel_set_encoding (giochannel_table[idx].chan, NULL, NULL);
+ g_io_channel_set_buffered (giochannel_table[idx].chan, FALSE);
+
+ return idx;
+}
+
+
+/* Returns the FD or -1 on resource limit. */
+int
+new_channel_from_socket (int sock)
+{
+ int idx;
+
+ for (idx = 0; idx < MAX_SLAFD; idx++)
+ if (! giochannel_table[idx].used)
+ break;
+
+ if (idx == MAX_SLAFD)
+ {
+ errno = EIO;
+ return -1;
+ }
+
+ giochannel_table[idx].used = 1;
+ giochannel_table[idx].chan = g_io_channel_win32_new_socket (sock);
+ giochannel_table[idx].fd = -1;
+ giochannel_table[idx].socket = sock;
+ giochannel_table[idx].primary = 1;
+
+ g_io_channel_set_encoding (giochannel_table[idx].chan, NULL, NULL);
+ g_io_channel_set_buffered (giochannel_table[idx].chan, FALSE);
+
+ return idx;
+}
+
+
+/* Compatibility interface. Obsolete. */
+void *
+gpgme_get_giochannel (int fd)
+{
+ return find_channel (fd);
+}
+
+
+/* Look up the giochannel for "file descriptor" FD. */
+void *
+gpgme_get_fdptr (int fd)
+{
+ return find_channel (fd);
+}
+
+
+/* Write the printable version of FD to the buffer BUF of length
+ BUFLEN. The printable version is the representation on the command
+ line that the child process expects. */
+int
+_gpgme_io_fd2str (char *buf, int buflen, int fd)
+{
+ HANDLE hndl;
+
+ TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_fd2str", fd, "fd=%d", fd);
+ if (giochannel_table[fd].fd != -1)
+ hndl = (HANDLE) _get_osfhandle (giochannel_table[fd].fd);
+ else
+ hndl = (HANDLE) giochannel_table[fd].socket;
+
+ TRACE_SUC1 ("syshd=%p", hndl);
+
+ return snprintf (buf, buflen, "%d", (int) hndl);
+}
+
+
+void
+_gpgme_io_subsystem_init (void)
+{
+}
+
+
+static struct
+{
+ _gpgme_close_notify_handler_t handler;
+ void *value;
+} notify_table[MAX_SLAFD];
+
+
+int
+_gpgme_io_read (int fd, void *buffer, size_t count)
+{
+ int saved_errno = 0;
+ gsize nread;
+ GIOChannel *chan;
+ GIOStatus status;
+ TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_read", fd,
+ "buffer=%p, count=%u", buffer, count);
+
+ chan = find_channel (fd);
+ if (!chan)
+ {
+ TRACE_LOG ("no channel registered");
+ errno = EINVAL;
+ return TRACE_SYSRES (-1);
+ }
+ TRACE_LOG1 ("channel %p", chan);
+
+ {
+ GError *err = NULL;
+ status = g_io_channel_read_chars (chan, (gchar *) buffer,
+ count, &nread, &err);
+ if (err)
+ {
+ TRACE_LOG2 ("status %i, err %s", status, err->message);
+ g_error_free (err);
+ }
+ }
+
+ if (status == G_IO_STATUS_EOF)
+ nread = 0;
+ else if (status == G_IO_STATUS_AGAIN)
+ {
+ nread = -1;
+ saved_errno = EAGAIN;
+ }
+ else if (status != G_IO_STATUS_NORMAL)
+ {
+ TRACE_LOG1 ("status %d", status);
+ nread = -1;
+ saved_errno = EIO;
+ }
+
+ if (nread != 0 && nread != -1)
+ TRACE_LOGBUF (buffer, nread);
+
+ errno = saved_errno;
+ return TRACE_SYSRES (nread);
+}
+
+
+int
+_gpgme_io_write (int fd, const void *buffer, size_t count)
+{
+ int saved_errno = 0;
+ gsize nwritten;
+ GIOChannel *chan;
+ GIOStatus status;
+ GError *err = NULL;
+
+ TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_write", fd,
+ "buffer=%p, count=%u", buffer, count);
+ TRACE_LOGBUF (buffer, count);
+
+ chan = find_channel (fd);
+ if (!chan)
+ {
+ TRACE_LOG ("fd %d: no channel registered");
+ errno = EINVAL;
+ return -1;
+ }
+
+ status = g_io_channel_write_chars (chan, (gchar *) buffer, count,
+ &nwritten, &err);
+ if (err)
+ {
+ TRACE_LOG1 ("write error: %s", err->message);
+ g_error_free (err);
+ }
+
+ if (status == G_IO_STATUS_AGAIN)
+ {
+ nwritten = -1;
+ saved_errno = EAGAIN;
+ }
+ else if (status != G_IO_STATUS_NORMAL)
+ {
+ nwritten = -1;
+ saved_errno = EIO;
+ }
+ errno = saved_errno;
+
+ return TRACE_SYSRES (nwritten);
+}
+
+
+int
+_gpgme_io_pipe (int filedes[2], int inherit_idx)
+{
+ int fds[2];
+
+ TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_pipe", filedes,
+ "inherit_idx=%i (GPGME uses it for %s)",
+ inherit_idx, inherit_idx ? "reading" : "writing");
+
+#define PIPEBUF_SIZE 4096
+ if (_pipe (fds, PIPEBUF_SIZE, O_NOINHERIT | O_BINARY) == -1)
+ return TRACE_SYSRES (-1);
+
+ /* Make one end inheritable. */
+ if (inherit_idx == 0)
+ {
+ int new_read;
+
+ new_read = _dup (fds[0]);
+ _close (fds[0]);
+ fds[0] = new_read;
+
+ if (new_read < 0)
+ {
+ _close (fds[1]);
+ return TRACE_SYSRES (-1);
+ }
+ }
+ else if (inherit_idx == 1)
+ {
+ int new_write;
+
+ new_write = _dup (fds[1]);
+ _close (fds[1]);
+ fds[1] = new_write;
+
+ if (new_write < 0)
+ {
+ _close (fds[0]);
+ return TRACE_SYSRES (-1);
+ }
+ }
+
+ /* For _gpgme_io_close. */
+ filedes[inherit_idx] = new_dummy_channel_from_fd (fds[inherit_idx]);
+ if (filedes[inherit_idx] < 0)
+ {
+ int saved_errno = errno;
+
+ _close (fds[0]);
+ _close (fds[1]);
+ errno = saved_errno;
+ return TRACE_SYSRES (-1);
+ }
+
+ /* Now we have a pipe with the correct end inheritable. The other end
+ should have a giochannel. */
+ filedes[1 - inherit_idx] = new_channel_from_fd (fds[1 - inherit_idx]);
+ if (filedes[1 - inherit_idx] < 0)
+ {
+ int saved_errno = errno;
+
+ _gpgme_io_close (fds[inherit_idx]);
+ _close (fds[1 - inherit_idx]);
+ errno = saved_errno;
+ return TRACE_SYSRES (-1);
+ }
+
+ return TRACE_SUC5 ("read=0x%x/%p, write=0x%x/%p, channel=%p",
+ filedes[0],
+ (HANDLE) _get_osfhandle (giochannel_table[filedes[0]].fd),
+ filedes[1],
+ (HANDLE) _get_osfhandle (giochannel_table[filedes[1]].fd),
+ giochannel_table[1 - inherit_idx].chan);
+}
+
+
+int
+_gpgme_io_close (int fd)
+{
+ TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_close", fd);
+
+ if (fd < 0 || fd >= MAX_SLAFD)
+ {
+ errno = EBADF;
+ return TRACE_SYSRES (-1);
+ }
+
+ assert (giochannel_table[fd].used);
+
+ /* First call the notify handler. */
+ if (notify_table[fd].handler)
+ {
+ notify_table[fd].handler (fd, notify_table[fd].value);
+ notify_table[fd].handler = NULL;
+ notify_table[fd].value = NULL;
+ }
+
+ /* Then do the close. */
+ if (giochannel_table[fd].chan)
+ {
+ if (giochannel_table[fd].primary)
+ g_io_channel_shutdown (giochannel_table[fd].chan, 1, NULL);
+
+ g_io_channel_unref (giochannel_table[fd].chan);
+ }
+ else
+ {
+ /* Dummy entry, just close. */
+ assert (giochannel_table[fd].fd != -1);
+ _close (giochannel_table[fd].fd);
+ }
+
+ giochannel_table[fd].used = 0;
+ giochannel_table[fd].fd = -1;
+ giochannel_table[fd].socket = INVALID_SOCKET;
+ giochannel_table[fd].chan = NULL;
+ giochannel_table[fd].primary = 0;
+
+ TRACE_SUC ();
+ return 0;
+}
+
+
+int
+_gpgme_io_set_close_notify (int fd, _gpgme_close_notify_handler_t handler,
+ void *value)
+{
+ TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_set_close_notify", fd,
+ "close_handler=%p/%p", handler, value);
+
+ assert (fd != -1);
+
+ if (fd < 0 || fd >= (int) DIM (notify_table))
+ {
+ errno = EINVAL;
+ return TRACE_SYSRES (-1);
+ }
+ notify_table[fd].handler = handler;
+ notify_table[fd].value = value;
+ return TRACE_SYSRES (0);
+}
+
+
+int
+_gpgme_io_set_nonblocking (int fd)
+{
+ GIOChannel *chan;
+ GIOStatus status;
+
+ TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_set_nonblocking", fd);
+
+ chan = find_channel (fd);
+ if (!chan)
+ {
+ errno = EIO;
+ return TRACE_SYSRES (-1);
+ }
+
+ status = g_io_channel_set_flags (chan,
+ g_io_channel_get_flags (chan) |
+ G_IO_FLAG_NONBLOCK, NULL);
+
+ if (status != G_IO_STATUS_NORMAL)
+ {
+#if 0
+ /* glib 1.9.2 does not implement set_flags and returns an
+ error. */
+ errno = EIO;
+ return TRACE_SYSRES (-1);
+#else
+ TRACE_LOG1 ("g_io_channel_set_flags failed: status=%d (ignored)",
+ status);
+#endif
+ }
+
+ return TRACE_SYSRES (0);
+}
+
+
+static char *
+build_commandline (char **argv)
+{
+ int i;
+ int n = 0;
+ char *buf;
+ char *p;
+
+ /* We have to quote some things because under Windows the program
+ parses the commandline and does some unquoting. We enclose the
+ whole argument in double-quotes, and escape literal double-quotes
+ as well as backslashes with a backslash. We end up with a
+ trailing space at the end of the line, but that is harmless. */
+ for (i = 0; argv[i]; i++)
+ {
+ p = argv[i];
+ /* The leading double-quote. */
+ n++;
+ while (*p)
+ {
+ /* An extra one for each literal that must be escaped. */
+ if (*p == '\\' || *p == '"')
+ n++;
+ n++;
+ p++;
+ }
+ /* The trailing double-quote and the delimiter. */
+ n += 2;
+ }
+ /* And a trailing zero. */
+ n++;
+
+ buf = p = malloc (n);
+ if (!buf)
+ return NULL;
+ for (i = 0; argv[i]; i++)
+ {
+ char *argvp = argv[i];
+
+ *(p++) = '"';
+ while (*argvp)
+ {
+ if (*argvp == '\\' || *argvp == '"')
+ *(p++) = '\\';
+ *(p++) = *(argvp++);
+ }
+ *(p++) = '"';
+ *(p++) = ' ';
+ }
+ *(p++) = 0;
+
+ return buf;
+}
+
+
+int
+_gpgme_io_spawn (const char *path, char * const argv[], unsigned int flags,
+ struct spawn_fd_item_s *fd_list,
+ void (*atfork) (void *opaque, int reserved),
+ void *atforkvalue, pid_t *r_pid)
+{
+ SECURITY_ATTRIBUTES sec_attr;
+ PROCESS_INFORMATION pi =
+ {
+ NULL, /* returns process handle */
+ 0, /* returns primary thread handle */
+ 0, /* returns pid */
+ 0 /* returns tid */
+ };
+ STARTUPINFO si;
+ int cr_flags = (CREATE_DEFAULT_ERROR_MODE
+ | GetPriorityClass (GetCurrentProcess ()));
+ int i;
+ char **args;
+ char *arg_string;
+ /* FIXME. */
+ int debug_me = 0;
+ int tmp_fd;
+ char *tmp_name;
+
+ TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_spawn", path,
+ "path=%s", path);
+ i = 0;
+ while (argv[i])
+ {
+ TRACE_LOG2 ("argv[%2i] = %s", i, argv[i]);
+ i++;
+ }
+
+ /* We do not inherit any handles by default, and just insert those
+ handles we want the child to have afterwards. But some handle
+ values occur on the command line, and we need to move
+ stdin/out/err to the right location. So we use a wrapper program
+ which gets the information from a temporary file. */
+ if (_gpgme_mkstemp (&tmp_fd, &tmp_name) < 0)
+ {
+ TRACE_LOG1 ("_gpgme_mkstemp failed: %s", strerror (errno));
+ return TRACE_SYSRES (-1);
+ }
+ TRACE_LOG1 ("tmp_name = %s", tmp_name);
+
+ args = calloc (2 + i + 1, sizeof (*args));
+ args[0] = (char *) _gpgme_get_w32spawn_path ();
+ args[1] = tmp_name;
+ args[2] = path;
+ memcpy (&args[3], &argv[1], i * sizeof (*args));
+
+ memset (&sec_attr, 0, sizeof sec_attr);
+ sec_attr.nLength = sizeof sec_attr;
+ sec_attr.bInheritHandle = FALSE;
+
+ arg_string = build_commandline (args);
+ free (args);
+ if (!arg_string)
+ {
+ close (tmp_fd);
+ DeleteFile (tmp_name);
+ return TRACE_SYSRES (-1);
+ }
+
+ memset (&si, 0, sizeof si);
+ si.cb = sizeof (si);
+ si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
+ si.wShowWindow = debug_me ? SW_SHOW : SW_HIDE;
+ si.hStdInput = INVALID_HANDLE_VALUE;
+ si.hStdOutput = INVALID_HANDLE_VALUE;
+ si.hStdError = INVALID_HANDLE_VALUE;
+
+ cr_flags |= CREATE_SUSPENDED;
+ cr_flags |= DETACHED_PROCESS;
+ if (!CreateProcessA (_gpgme_get_w32spawn_path (),
+ arg_string,
+ &sec_attr, /* process security attributes */
+ &sec_attr, /* thread security attributes */
+ FALSE, /* inherit handles */
+ cr_flags, /* creation flags */
+ NULL, /* environment */
+ NULL, /* use current drive/directory */
+ &si, /* startup information */
+ &pi)) /* returns process information */
+ {
+ TRACE_LOG1 ("CreateProcess failed: ec=%d", (int) GetLastError ());
+ free (arg_string);
+ close (tmp_fd);
+ DeleteFile (tmp_name);
+
+ /* FIXME: Should translate the error code. */
+ errno = EIO;
+ return TRACE_SYSRES (-1);
+ }
+
+ free (arg_string);
+
+ if (flags & IOSPAWN_FLAG_ALLOW_SET_FG)
+ _gpgme_allow_set_foreground_window ((pid_t)pi.dwProcessId);
+
+ /* Insert the inherited handles. */
+ for (i = 0; fd_list[i].fd != -1; i++)
+ {
+ HANDLE hd;
+
+ /* Make it inheritable for the wrapper process. */
+ if (!DuplicateHandle (GetCurrentProcess(),
+ _get_osfhandle (giochannel_table[fd_list[i].fd].fd),
+ pi.hProcess, &hd, 0, TRUE, DUPLICATE_SAME_ACCESS))
+ {
+ TRACE_LOG1 ("DuplicateHandle failed: ec=%d", (int) GetLastError ());
+ TerminateProcess (pi.hProcess, 0);
+ /* Just in case TerminateProcess didn't work, let the
+ process fail on its own. */
+ ResumeThread (pi.hThread);
+ CloseHandle (pi.hThread);
+ CloseHandle (pi.hProcess);
+
+ close (tmp_fd);
+ DeleteFile (tmp_name);
+
+ /* FIXME: Should translate the error code. */
+ errno = EIO;
+ return TRACE_SYSRES (-1);
+ }
+ /* Return the child name of this handle. */
+ fd_list[i].peer_name = (int) hd;
+ }
+
+ /* Write the handle translation information to the temporary
+ file. */
+ {
+ /* Hold roughly MAX_TRANS quadruplets of 64 bit numbers in hex
+ notation: "0xFEDCBA9876543210" with an extra white space after
+ every quadruplet. 10*(19*4 + 1) - 1 = 769. This plans ahead
+ for a time when a HANDLE is 64 bit. */
+#define BUFFER_MAX 800
+ char line[BUFFER_MAX + 1];
+ int res;
+ int written;
+ size_t len;
+
+ if ((flags & IOSPAWN_FLAG_ALLOW_SET_FG))
+ strcpy (line, "~1 \n");
+ else
+ strcpy (line, "\n");
+ for (i = 0; fd_list[i].fd != -1; i++)
+ {
+ /* Strip the newline. */
+ len = strlen (line) - 1;
+
+ /* Format is: Local name, stdin/stdout/stderr, peer name, argv idx. */
+ snprintf (&line[len], BUFFER_MAX - len, "0x%x %d 0x%x %d \n",
+ fd_list[i].fd, fd_list[i].dup_to,
+ fd_list[i].peer_name, fd_list[i].arg_loc);
+ /* Rather safe than sorry. */
+ line[BUFFER_MAX - 1] = '\n';
+ line[BUFFER_MAX] = '\0';
+ }
+ len = strlen (line);
+ written = 0;
+ do
+ {
+ res = write (tmp_fd, &line[written], len - written);
+ if (res > 0)
+ written += res;
+ }
+ while (res > 0 || (res < 0 && errno == EAGAIN));
+ }
+ close (tmp_fd);
+ /* The temporary file is deleted by the gpgme-w32spawn process
+ (hopefully). */
+
+ TRACE_LOG4 ("CreateProcess ready: hProcess=%p, hThread=%p, "
+ "dwProcessID=%d, dwThreadId=%d",
+ pi.hProcess, pi.hThread,
+ (int) pi.dwProcessId, (int) pi.dwThreadId);
+
+ if (r_pid)
+ *r_pid = (pid_t)pi.dwProcessId;
+
+ if (ResumeThread (pi.hThread) < 0)
+ TRACE_LOG1 ("ResumeThread failed: ec=%d", (int) GetLastError ());
+
+ if (!CloseHandle (pi.hThread))
+ TRACE_LOG1 ("CloseHandle of thread failed: ec=%d",
+ (int) GetLastError ());
+
+ TRACE_LOG1 ("process=%p", pi.hProcess);
+
+ /* We don't need to wait for the process. */
+ if (!CloseHandle (pi.hProcess))
+ TRACE_LOG1 ("CloseHandle of process failed: ec=%d",
+ (int) GetLastError ());
+
+ if (! (flags & IOSPAWN_FLAG_NOCLOSE))
+ {
+ for (i = 0; fd_list[i].fd != -1; i++)
+ _gpgme_io_close (fd_list[i].fd);
+ }
+
+ for (i = 0; fd_list[i].fd != -1; i++)
+ if (fd_list[i].dup_to == -1)
+ TRACE_LOG3 ("fd[%i] = 0x%x -> 0x%x", i, fd_list[i].fd,
+ fd_list[i].peer_name);
+ else
+ TRACE_LOG4 ("fd[%i] = 0x%x -> 0x%x (std%s)", i, fd_list[i].fd,
+ fd_list[i].peer_name, (fd_list[i].dup_to == 0) ? "in" :
+ ((fd_list[i].dup_to == 1) ? "out" : "err"));
+
+ return TRACE_SYSRES (0);
+}
+
+
+/* Select on the list of fds. Returns: -1 = error, 0 = timeout or
+ nothing to select, > 0 = number of signaled fds. */
+int
+_gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
+{
+ int npollfds;
+ GPollFD *pollfds;
+ int *pollfds_map;
+ int i;
+ int j;
+ int any;
+ int n;
+ int count;
+ /* Use a 1s timeout. */
+ int timeout = 1000;
+ void *dbg_help = NULL;
+ TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_select", fds,
+ "nfds=%u, nonblock=%u", nfds, nonblock);
+
+ if (nonblock)
+ timeout = 0;
+
+ pollfds = calloc (nfds, sizeof *pollfds);
+ if (!pollfds)
+ return -1;
+ pollfds_map = calloc (nfds, sizeof *pollfds_map);
+ if (!pollfds_map)
+ {
+ free (pollfds);
+ return -1;
+ }
+ npollfds = 0;
+
+ TRACE_SEQ (dbg_help, "select on [ ");
+ any = 0;
+ for (i = 0; i < nfds; i++)
+ {
+ GIOChannel *chan = NULL;
+
+ if (fds[i].fd == -1)
+ continue;
+
+ if ((fds[i].for_read || fds[i].for_write)
+ && !(chan = find_channel (fds[i].fd)))
+ {
+ TRACE_ADD1 (dbg_help, "[BAD0x%x ", fds[i].fd);
+ TRACE_END (dbg_help, "]");
+ assert (!"see log file");
+ }
+ else if (fds[i].for_read )
+ {
+ assert(chan);
+ g_io_channel_win32_make_pollfd (chan, G_IO_IN, pollfds + npollfds);
+ pollfds_map[npollfds] = i;
+ TRACE_ADD2 (dbg_help, "r0x%x<%d> ", fds[i].fd, pollfds[npollfds].fd);
+ npollfds++;
+ any = 1;
+ }
+ else if (fds[i].for_write)
+ {
+ assert(chan);
+ g_io_channel_win32_make_pollfd (chan, G_IO_OUT, pollfds + npollfds);
+ pollfds_map[npollfds] = i;
+ TRACE_ADD2 (dbg_help, "w0x%x<%d> ", fds[i].fd, pollfds[npollfds].fd);
+ npollfds++;
+ any = 1;
+ }
+ fds[i].signaled = 0;
+ }
+ TRACE_END (dbg_help, "]");
+ if (!any)
+ {
+ count = 0;
+ goto leave;
+ }
+
+
+ count = g_io_channel_win32_poll (pollfds, npollfds, timeout);
+ if (count < 0)
+ {
+ int saved_errno = errno;
+ errno = saved_errno;
+ goto leave;
+ }
+
+ TRACE_SEQ (dbg_help, "select OK [ ");
+ if (TRACE_ENABLED (dbg_help))
+ {
+ for (i = 0; i < npollfds; i++)
+ {
+ if ((pollfds[i].revents & G_IO_IN))
+ TRACE_ADD1 (dbg_help, "r0x%x ", fds[pollfds_map[i]].fd);
+ if ((pollfds[i].revents & G_IO_OUT))
+ TRACE_ADD1 (dbg_help, "w0x%x ", fds[pollfds_map[i]].fd);
+ }
+ TRACE_END (dbg_help, "]");
+ }
+
+ /* COUNT is used to stop the lop as soon as possible. */
+ for (n = count, i = 0; i < npollfds && n; i++)
+ {
+ j = pollfds_map[i];
+ assert (j >= 0 && j < nfds);
+ if (fds[j].fd == -1)
+ ;
+ else if (fds[j].for_read)
+ {
+ if ((pollfds[i].revents & G_IO_IN))
+ {
+ fds[j].signaled = 1;
+ n--;
+ }
+ }
+ else if (fds[j].for_write)
+ {
+ if ((pollfds[i].revents & G_IO_OUT))
+ {
+ fds[j].signaled = 1;
+ n--;
+ }
+ }
+ }
+
+leave:
+ free (pollfds);
+ free (pollfds_map);
+ return TRACE_SYSRES (count);
+}
+
+
+int
+_gpgme_io_dup (int fd)
+{
+ int newfd;
+ GIOChannel *chan;
+
+ TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_dup", fd);
+
+ if (fd < 0 || fd >= MAX_SLAFD || !giochannel_table[fd].used)
+ {
+ errno = EINVAL;
+ return TRACE_SYSRES (-1);
+ }
+
+ for (newfd = 0; newfd < MAX_SLAFD; newfd++)
+ if (! giochannel_table[newfd].used)
+ break;
+ if (newfd == MAX_SLAFD)
+ {
+ errno = EIO;
+ return TRACE_SYSRES (-1);
+ }
+
+ chan = giochannel_table[fd].chan;
+ g_io_channel_ref (chan);
+ giochannel_table[newfd].used = 1;
+ giochannel_table[newfd].chan = chan;
+ giochannel_table[newfd].fd = -1;
+ giochannel_table[newfd].socket = INVALID_SOCKET;
+ giochannel_table[newfd].primary = 0;
+
+ return TRACE_SYSRES (newfd);
+}
+
+
+
+
+
+static int
+wsa2errno (int err)
+{
+ switch (err)
+ {
+ case WSAENOTSOCK:
+ return EINVAL;
+ case WSAEWOULDBLOCK:
+ return EAGAIN;
+ case ERROR_BROKEN_PIPE:
+ return EPIPE;
+ case WSANOTINITIALISED:
+ return ENOSYS;
+ default:
+ return EIO;
+ }
+}
+
+
+int
+_gpgme_io_socket (int domain, int type, int proto)
+{
+ int res;
+ int fd;
+
+ TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_socket", domain,
+ "type=%i, protp=%i", type, proto);
+
+ res = socket (domain, type, proto);
+ if (res == INVALID_SOCKET)
+ {
+ errno = wsa2errno (WSAGetLastError ());
+ return TRACE_SYSRES (-1);
+ }
+
+ fd = new_channel_from_socket (res);
+ if (fd < 0)
+ {
+ int saved_errno = errno;
+ closesocket (res);
+ errno = saved_errno;
+ return TRACE_SYSRES (-1);
+ }
+
+ TRACE_SUC2 ("fd=%i, socket=0x%x", fd, res);
+
+ return fd;
+}
+
+
+int
+_gpgme_io_connect (int fd, struct sockaddr *addr, int addrlen)
+{
+ GIOChannel *chan;
+ int sockfd;
+ int res;
+ GIOFlags flags;
+ GIOStatus status;
+ GError *err = NULL;
+
+ TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_connect", fd,
+ "addr=%p, addrlen=%i", addr, addrlen);
+
+ chan = find_channel (fd);
+ if (! chan)
+ {
+ errno = EINVAL;
+ return TRACE_SYSRES (-1);
+ }
+
+ flags = g_io_channel_get_flags (chan);
+ if (flags & G_IO_FLAG_NONBLOCK)
+ {
+ status = g_io_channel_set_flags (chan, flags & ~G_IO_FLAG_NONBLOCK, &err);
+ if (err)
+ {
+ TRACE_LOG1 ("setting flags error: %s", err->message);
+ g_error_free (err);
+ err = NULL;
+ }
+ if (status != G_IO_STATUS_NORMAL)
+ {
+ errno = EIO;
+ return TRACE_SYSRES (-1);
+ }
+ }
+
+ sockfd = giochannel_table[fd].socket;
+ if (sockfd == INVALID_SOCKET)
+ {
+ errno = EINVAL;
+ return TRACE_SYSRES (-1);
+ }
+
+ TRACE_LOG1 ("connect sockfd=0x%x", sockfd);
+ res = connect (sockfd, addr, addrlen);
+
+ /* FIXME: Error ignored here. */
+ if (! (flags & G_IO_FLAG_NONBLOCK))
+ g_io_channel_set_flags (chan, flags, NULL);
+
+ if (res)
+ {
+ TRACE_LOG2 ("connect failed: %i %i", res, WSAGetLastError ());
+
+ errno = wsa2errno (WSAGetLastError ());
+ return TRACE_SYSRES (-1);
+ }
+
+ return TRACE_SUC ();
+}
diff --git a/src/w32-io.c b/src/w32-io.c
new file mode 100644
index 0000000..56a05c4
--- /dev/null
+++ b/src/w32-io.c
@@ -0,0 +1,2064 @@
+/* w32-io.c - W32 API I/O functions.
+ Copyright (C) 2000 Werner Koch (dd9jn)
+ Copyright (C) 2001, 2002, 2003, 2004, 2007, 2010 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#include <io.h>
+
+#include "util.h"
+
+#ifdef HAVE_W32CE_SYSTEM
+#include <assuan.h>
+#include <winioctl.h>
+#define GPGCEDEV_IOCTL_UNBLOCK \
+ CTL_CODE (FILE_DEVICE_STREAMS, 2050, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define GPGCEDEV_IOCTL_ASSIGN_RVID \
+ CTL_CODE (FILE_DEVICE_STREAMS, 2051, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#endif
+
+#include "sema.h"
+#include "priv-io.h"
+#include "debug.h"
+
+
+/* FIXME: Optimize. */
+#define MAX_SLAFD 512
+
+static struct
+{
+ int used;
+
+ /* If this is not INVALID_HANDLE_VALUE, then it's a handle. */
+ HANDLE handle;
+
+ /* If this is not INVALID_SOCKET, then it's a Windows socket. */
+ int socket;
+
+ /* If this is not 0, then it's a rendezvous ID for the pipe server. */
+ int rvid;
+
+ /* DUP_FROM is -1 if this file descriptor was allocated by pipe or
+ socket functions. Only then should the handle or socket be
+ destroyed when this FD is closed. This, together with the fact
+ that dup'ed file descriptors are closed before the file
+ descriptors from which they are dup'ed are closed, ensures that
+ the handle or socket is always valid, and shared among all file
+ descriptors refering to the same underlying object.
+
+ The logic behind this is that there is only one reason for us to
+ dup file descriptors anyway: to allow simpler book-keeping of
+ file descriptors shared between GPGME and libassuan, which both
+ want to close something. Using the same handle for these
+ duplicates works just fine. */
+ int dup_from;
+} fd_table[MAX_SLAFD];
+
+
+/* Returns the FD or -1 on resource limit. */
+int
+new_fd (void)
+{
+ int idx;
+
+ for (idx = 0; idx < MAX_SLAFD; idx++)
+ if (! fd_table[idx].used)
+ break;
+
+ if (idx == MAX_SLAFD)
+ {
+ gpg_err_set_errno (EIO);
+ return -1;
+ }
+
+ fd_table[idx].used = 1;
+ fd_table[idx].handle = INVALID_HANDLE_VALUE;
+ fd_table[idx].socket = INVALID_SOCKET;
+ fd_table[idx].rvid = 0;
+ fd_table[idx].dup_from = -1;
+
+ return idx;
+}
+
+
+void
+release_fd (int fd)
+{
+ if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used)
+ return;
+
+ fd_table[fd].used = 0;
+ fd_table[fd].handle = INVALID_HANDLE_VALUE;
+ fd_table[fd].socket = INVALID_SOCKET;
+ fd_table[fd].rvid = 0;
+ fd_table[fd].dup_from = -1;
+}
+
+
+#define handle_to_fd(a) ((int)(a))
+
+#define READBUF_SIZE 4096
+#define WRITEBUF_SIZE 4096
+#define PIPEBUF_SIZE 4096
+#define MAX_READERS 64
+#define MAX_WRITERS 64
+
+static struct
+{
+ int inuse;
+ int fd;
+ _gpgme_close_notify_handler_t handler;
+ void *value;
+} notify_table[MAX_SLAFD];
+DEFINE_STATIC_LOCK (notify_table_lock);
+
+
+struct reader_context_s
+{
+ HANDLE file_hd;
+ int file_sock;
+ HANDLE thread_hd;
+ int refcount;
+
+ DECLARE_LOCK (mutex);
+
+ int stop_me;
+ int eof;
+ int eof_shortcut;
+ int error;
+ int error_code;
+
+ /* This is manually reset. */
+ HANDLE have_data_ev;
+ /* This is automatically reset. */
+ HANDLE have_space_ev;
+ HANDLE stopped;
+ size_t readpos, writepos;
+ char buffer[READBUF_SIZE];
+};
+
+
+static struct
+{
+ volatile int used;
+ int fd;
+ struct reader_context_s *context;
+} reader_table[MAX_READERS];
+static int reader_table_size= MAX_READERS;
+DEFINE_STATIC_LOCK (reader_table_lock);
+
+
+struct writer_context_s
+{
+ HANDLE file_hd;
+ int file_sock;
+ HANDLE thread_hd;
+ int refcount;
+
+ DECLARE_LOCK (mutex);
+
+ int stop_me;
+ int error;
+ int error_code;
+
+ /* This is manually reset. */
+ HANDLE have_data;
+ HANDLE is_empty;
+ HANDLE stopped;
+ size_t nbytes;
+ char buffer[WRITEBUF_SIZE];
+};
+
+
+static struct
+{
+ volatile int used;
+ int fd;
+ struct writer_context_s *context;
+} writer_table[MAX_WRITERS];
+static int writer_table_size= MAX_WRITERS;
+DEFINE_STATIC_LOCK (writer_table_lock);
+
+
+static int
+get_desired_thread_priority (void)
+{
+ int value;
+
+ if (!_gpgme_get_conf_int ("IOThreadPriority", &value))
+ {
+ value = THREAD_PRIORITY_HIGHEST;
+ TRACE1 (DEBUG_SYSIO, "gpgme:get_desired_thread_priority", 0,
+ "%d (default)", value);
+ }
+ else
+ {
+ TRACE1 (DEBUG_SYSIO, "gpgme:get_desired_thread_priority", 0,
+ "%d (configured)", value);
+ }
+ return value;
+}
+
+
+static HANDLE
+set_synchronize (HANDLE hd)
+{
+#ifdef HAVE_W32CE_SYSTEM
+ return hd;
+#else
+ HANDLE new_hd;
+
+ /* For NT we have to set the sync flag. It seems that the only way
+ to do it is by duplicating the handle. Tsss... */
+ if (!DuplicateHandle (GetCurrentProcess (), hd,
+ GetCurrentProcess (), &new_hd,
+ EVENT_MODIFY_STATE | SYNCHRONIZE, FALSE, 0))
+ {
+ TRACE1 (DEBUG_SYSIO, "gpgme:set_synchronize", hd,
+ "DuplicateHandle failed: ec=%d", (int) GetLastError ());
+ /* FIXME: Should translate the error code. */
+ gpg_err_set_errno (EIO);
+ return INVALID_HANDLE_VALUE;
+ }
+
+ CloseHandle (hd);
+ return new_hd;
+#endif
+}
+
+
+static DWORD CALLBACK
+reader (void *arg)
+{
+ struct reader_context_s *ctx = arg;
+ int nbytes;
+ DWORD nread;
+ int sock;
+ TRACE_BEG1 (DEBUG_SYSIO, "gpgme:reader", ctx->file_hd,
+ "thread=%p", ctx->thread_hd);
+
+ if (ctx->file_hd != INVALID_HANDLE_VALUE)
+ sock = 0;
+ else
+ sock = 1;
+
+ for (;;)
+ {
+ LOCK (ctx->mutex);
+ /* Leave a 1 byte gap so that we can see whether it is empty or
+ full. */
+ if ((ctx->writepos + 1) % READBUF_SIZE == ctx->readpos)
+ {
+ /* Wait for space. */
+ if (!ResetEvent (ctx->have_space_ev))
+ TRACE_LOG1 ("ResetEvent failed: ec=%d", (int) GetLastError ());
+ UNLOCK (ctx->mutex);
+ TRACE_LOG ("waiting for space");
+ WaitForSingleObject (ctx->have_space_ev, INFINITE);
+ TRACE_LOG ("got space");
+ LOCK (ctx->mutex);
+ }
+ if (ctx->stop_me)
+ {
+ UNLOCK (ctx->mutex);
+ break;
+ }
+ nbytes = (ctx->readpos + READBUF_SIZE
+ - ctx->writepos - 1) % READBUF_SIZE;
+ if (nbytes > READBUF_SIZE - ctx->writepos)
+ nbytes = READBUF_SIZE - ctx->writepos;
+ UNLOCK (ctx->mutex);
+
+ TRACE_LOG2 ("%s %d bytes", sock? "receiving":"reading", nbytes);
+
+ if (sock)
+ {
+ int n;
+
+ n = recv (ctx->file_sock, ctx->buffer + ctx->writepos, nbytes, 0);
+ if (n < 0)
+ {
+ ctx->error_code = (int) WSAGetLastError ();
+ if (ctx->error_code == ERROR_BROKEN_PIPE)
+ {
+ ctx->eof = 1;
+ TRACE_LOG ("got EOF (broken connection)");
+ }
+ else
+ {
+ ctx->error = 1;
+ TRACE_LOG1 ("recv error: ec=%d", ctx->error_code);
+ }
+ break;
+ }
+ nread = n;
+ }
+ else
+ {
+ if (!ReadFile (ctx->file_hd,
+ ctx->buffer + ctx->writepos, nbytes, &nread, NULL))
+ {
+ ctx->error_code = (int) GetLastError ();
+ /* NOTE (W32CE): Do not ignore ERROR_BUSY! Check at
+ least stop_me if that happens. */
+ if (ctx->error_code == ERROR_BROKEN_PIPE)
+ {
+ ctx->eof = 1;
+ TRACE_LOG ("got EOF (broken pipe)");
+ }
+ else
+ {
+ ctx->error = 1;
+ TRACE_LOG1 ("read error: ec=%d", ctx->error_code);
+ }
+ break;
+ }
+ }
+ LOCK (ctx->mutex);
+ if (ctx->stop_me)
+ {
+ UNLOCK (ctx->mutex);
+ break;
+ }
+ if (!nread)
+ {
+ ctx->eof = 1;
+ TRACE_LOG ("got eof");
+ UNLOCK (ctx->mutex);
+ break;
+ }
+ TRACE_LOG1 ("got %u bytes", nread);
+
+ ctx->writepos = (ctx->writepos + nread) % READBUF_SIZE;
+ if (!SetEvent (ctx->have_data_ev))
+ TRACE_LOG2 ("SetEvent (0x%x) failed: ec=%d", ctx->have_data_ev,
+ (int) GetLastError ());
+ UNLOCK (ctx->mutex);
+ }
+ /* Indicate that we have an error or EOF. */
+ if (!SetEvent (ctx->have_data_ev))
+ TRACE_LOG2 ("SetEvent (0x%x) failed: ec=%d", ctx->have_data_ev,
+ (int) GetLastError ());
+ SetEvent (ctx->stopped);
+
+ return TRACE_SUC ();
+}
+
+
+static struct reader_context_s *
+create_reader (int fd)
+{
+ struct reader_context_s *ctx;
+ SECURITY_ATTRIBUTES sec_attr;
+ DWORD tid;
+
+ TRACE_BEG (DEBUG_SYSIO, "gpgme:create_reader", fd);
+
+ memset (&sec_attr, 0, sizeof sec_attr);
+ sec_attr.nLength = sizeof sec_attr;
+ sec_attr.bInheritHandle = FALSE;
+
+ ctx = calloc (1, sizeof *ctx);
+ if (!ctx)
+ {
+ TRACE_SYSERR (errno);
+ return NULL;
+ }
+
+ if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used)
+ {
+ TRACE_SYSERR (EIO);
+ return NULL;
+ }
+ ctx->file_hd = fd_table[fd].handle;
+ ctx->file_sock = fd_table[fd].socket;
+
+ ctx->refcount = 1;
+ ctx->have_data_ev = CreateEvent (&sec_attr, TRUE, FALSE, NULL);
+ if (ctx->have_data_ev)
+ ctx->have_space_ev = CreateEvent (&sec_attr, FALSE, TRUE, NULL);
+ if (ctx->have_space_ev)
+ ctx->stopped = CreateEvent (&sec_attr, TRUE, FALSE, NULL);
+ if (!ctx->have_data_ev || !ctx->have_space_ev || !ctx->stopped)
+ {
+ TRACE_LOG1 ("CreateEvent failed: ec=%d", (int) GetLastError ());
+ if (ctx->have_data_ev)
+ CloseHandle (ctx->have_data_ev);
+ if (ctx->have_space_ev)
+ CloseHandle (ctx->have_space_ev);
+ if (ctx->stopped)
+ CloseHandle (ctx->stopped);
+ free (ctx);
+ /* FIXME: Translate the error code. */
+ TRACE_SYSERR (EIO);
+ return NULL;
+ }
+
+ ctx->have_data_ev = set_synchronize (ctx->have_data_ev);
+ INIT_LOCK (ctx->mutex);
+
+#ifdef HAVE_W32CE_SYSTEM
+ ctx->thread_hd = CreateThread (&sec_attr, 64 * 1024, reader, ctx,
+ STACK_SIZE_PARAM_IS_A_RESERVATION, &tid);
+#else
+ ctx->thread_hd = CreateThread (&sec_attr, 0, reader, ctx, 0, &tid);
+#endif
+
+ if (!ctx->thread_hd)
+ {
+ TRACE_LOG1 ("CreateThread failed: ec=%d", (int) GetLastError ());
+ DESTROY_LOCK (ctx->mutex);
+ if (ctx->have_data_ev)
+ CloseHandle (ctx->have_data_ev);
+ if (ctx->have_space_ev)
+ CloseHandle (ctx->have_space_ev);
+ if (ctx->stopped)
+ CloseHandle (ctx->stopped);
+ free (ctx);
+ TRACE_SYSERR (EIO);
+ return NULL;
+ }
+ else
+ {
+ /* We set the priority of the thread higher because we know that
+ it only runs for a short time. This greatly helps to
+ increase the performance of the I/O. */
+ SetThreadPriority (ctx->thread_hd, get_desired_thread_priority ());
+ }
+
+ TRACE_SUC ();
+ return ctx;
+}
+
+
+static void
+destroy_reader (struct reader_context_s *ctx)
+{
+ LOCK (ctx->mutex);
+ ctx->refcount--;
+ if (ctx->refcount != 0)
+ {
+ UNLOCK (ctx->mutex);
+ return;
+ }
+ ctx->stop_me = 1;
+ if (ctx->have_space_ev)
+ SetEvent (ctx->have_space_ev);
+ UNLOCK (ctx->mutex);
+
+#ifdef HAVE_W32CE_SYSTEM
+ /* Scenario: We never create a full pipe, but already started
+ reading. Then we need to unblock the reader in the pipe driver
+ to make our reader thread notice that we want it to go away. */
+
+ if (ctx->file_hd != INVALID_HANDLE_VALUE)
+ {
+ if (!DeviceIoControl (ctx->file_hd, GPGCEDEV_IOCTL_UNBLOCK,
+ NULL, 0, NULL, 0, NULL, NULL))
+ {
+ TRACE1 (DEBUG_SYSIO, "gpgme:destroy_reader", ctx->file_hd,
+ "unblock control call failed for thread %p", ctx->thread_hd);
+ }
+ }
+#endif
+
+ TRACE1 (DEBUG_SYSIO, "gpgme:destroy_reader", ctx->file_hd,
+ "waiting for termination of thread %p", ctx->thread_hd);
+ WaitForSingleObject (ctx->stopped, INFINITE);
+ TRACE1 (DEBUG_SYSIO, "gpgme:destroy_reader", ctx->file_hd,
+ "thread %p has terminated", ctx->thread_hd);
+
+ if (ctx->stopped)
+ CloseHandle (ctx->stopped);
+ if (ctx->have_data_ev)
+ CloseHandle (ctx->have_data_ev);
+ if (ctx->have_space_ev)
+ CloseHandle (ctx->have_space_ev);
+ CloseHandle (ctx->thread_hd);
+ DESTROY_LOCK (ctx->mutex);
+ free (ctx);
+}
+
+
+/* Find a reader context or create a new one. Note that the reader
+ context will last until a _gpgme_io_close. */
+static struct reader_context_s *
+find_reader (int fd, int start_it)
+{
+ struct reader_context_s *rd = NULL;
+ int i;
+
+ LOCK (reader_table_lock);
+ for (i = 0; i < reader_table_size; i++)
+ if (reader_table[i].used && reader_table[i].fd == fd)
+ rd = reader_table[i].context;
+
+ if (rd || !start_it)
+ {
+ UNLOCK (reader_table_lock);
+ return rd;
+ }
+
+ for (i = 0; i < reader_table_size; i++)
+ if (!reader_table[i].used)
+ break;
+
+ if (i != reader_table_size)
+ {
+ rd = create_reader (fd);
+ reader_table[i].fd = fd;
+ reader_table[i].context = rd;
+ reader_table[i].used = 1;
+ }
+
+ UNLOCK (reader_table_lock);
+ return rd;
+}
+
+
+static void
+kill_reader (int fd)
+{
+ int i;
+
+ LOCK (reader_table_lock);
+ for (i = 0; i < reader_table_size; i++)
+ {
+ if (reader_table[i].used && reader_table[i].fd == fd)
+ {
+ destroy_reader (reader_table[i].context);
+ reader_table[i].context = NULL;
+ reader_table[i].used = 0;
+ break;
+ }
+ }
+ UNLOCK (reader_table_lock);
+}
+
+
+int
+_gpgme_io_read (int fd, void *buffer, size_t count)
+{
+ int nread;
+ struct reader_context_s *ctx;
+ TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_read", fd,
+ "buffer=%p, count=%u", buffer, count);
+
+ ctx = find_reader (fd, 1);
+ if (!ctx)
+ {
+ gpg_err_set_errno (EBADF);
+ return TRACE_SYSRES (-1);
+ }
+ if (ctx->eof_shortcut)
+ return TRACE_SYSRES (0);
+
+ LOCK (ctx->mutex);
+ if (ctx->readpos == ctx->writepos && !ctx->error)
+ {
+ /* No data available. */
+ UNLOCK (ctx->mutex);
+ TRACE_LOG1 ("waiting for data from thread %p", ctx->thread_hd);
+ WaitForSingleObject (ctx->have_data_ev, INFINITE);
+ TRACE_LOG1 ("data from thread %p available", ctx->thread_hd);
+ LOCK (ctx->mutex);
+ }
+
+ if (ctx->readpos == ctx->writepos || ctx->error)
+ {
+ UNLOCK (ctx->mutex);
+ ctx->eof_shortcut = 1;
+ if (ctx->eof)
+ return TRACE_SYSRES (0);
+ if (!ctx->error)
+ {
+ TRACE_LOG ("EOF but ctx->eof flag not set");
+ return 0;
+ }
+ gpg_err_set_errno (ctx->error_code);
+ return TRACE_SYSRES (-1);
+ }
+
+ nread = ctx->readpos < ctx->writepos
+ ? ctx->writepos - ctx->readpos
+ : READBUF_SIZE - ctx->readpos;
+ if (nread > count)
+ nread = count;
+ memcpy (buffer, ctx->buffer + ctx->readpos, nread);
+ ctx->readpos = (ctx->readpos + nread) % READBUF_SIZE;
+ if (ctx->readpos == ctx->writepos && !ctx->eof)
+ {
+ if (!ResetEvent (ctx->have_data_ev))
+ {
+ TRACE_LOG1 ("ResetEvent failed: ec=%d", (int) GetLastError ());
+ UNLOCK (ctx->mutex);
+ /* FIXME: Should translate the error code. */
+ gpg_err_set_errno (EIO);
+ return TRACE_SYSRES (-1);
+ }
+ }
+ if (!SetEvent (ctx->have_space_ev))
+ {
+ TRACE_LOG2 ("SetEvent (0x%x) failed: ec=%d",
+ ctx->have_space_ev, (int) GetLastError ());
+ UNLOCK (ctx->mutex);
+ /* FIXME: Should translate the error code. */
+ gpg_err_set_errno (EIO);
+ return TRACE_SYSRES (-1);
+ }
+ UNLOCK (ctx->mutex);
+
+ TRACE_LOGBUF (buffer, nread);
+ return TRACE_SYSRES (nread);
+}
+
+
+/* The writer does use a simple buffering strategy so that we are
+ informed about write errors as soon as possible (i. e. with the the
+ next call to the write function. */
+static DWORD CALLBACK
+writer (void *arg)
+{
+ struct writer_context_s *ctx = arg;
+ DWORD nwritten;
+ int sock;
+ TRACE_BEG1 (DEBUG_SYSIO, "gpgme:writer", ctx->file_hd,
+ "thread=%p", ctx->thread_hd);
+
+ if (ctx->file_hd != INVALID_HANDLE_VALUE)
+ sock = 0;
+ else
+ sock = 1;
+
+ for (;;)
+ {
+ LOCK (ctx->mutex);
+ if (ctx->stop_me)
+ {
+ UNLOCK (ctx->mutex);
+ break;
+ }
+ if (!ctx->nbytes)
+ {
+ if (!SetEvent (ctx->is_empty))
+ TRACE_LOG1 ("SetEvent failed: ec=%d", (int) GetLastError ());
+ if (!ResetEvent (ctx->have_data))
+ TRACE_LOG1 ("ResetEvent failed: ec=%d", (int) GetLastError ());
+ UNLOCK (ctx->mutex);
+ TRACE_LOG ("idle");
+ WaitForSingleObject (ctx->have_data, INFINITE);
+ TRACE_LOG ("got data to send");
+ LOCK (ctx->mutex);
+ }
+ if (ctx->stop_me)
+ {
+ UNLOCK (ctx->mutex);
+ break;
+ }
+ UNLOCK (ctx->mutex);
+
+ TRACE_LOG2 ("%s %d bytes", sock?"sending":"writing", ctx->nbytes);
+
+ /* Note that CTX->nbytes is not zero at this point, because
+ _gpgme_io_write always writes at least 1 byte before waking
+ us up, unless CTX->stop_me is true, which we catch above. */
+ if (sock)
+ {
+ /* We need to try send first because a socket handle can't
+ be used with WriteFile. */
+ int n;
+
+ n = send (ctx->file_sock, ctx->buffer, ctx->nbytes, 0);
+ if (n < 0)
+ {
+ ctx->error_code = (int) WSAGetLastError ();
+ ctx->error = 1;
+ TRACE_LOG1 ("send error: ec=%d", ctx->error_code);
+ break;
+ }
+ nwritten = n;
+ }
+ else
+ {
+ if (!WriteFile (ctx->file_hd, ctx->buffer,
+ ctx->nbytes, &nwritten, NULL))
+ {
+ if (GetLastError () == ERROR_BUSY)
+ {
+ /* Probably stop_me is set now. */
+ TRACE_LOG ("pipe busy (unblocked?)");
+ continue;
+ }
+
+ ctx->error_code = (int) GetLastError ();
+ ctx->error = 1;
+ TRACE_LOG1 ("write error: ec=%d", ctx->error_code);
+ break;
+ }
+ }
+ TRACE_LOG1 ("wrote %d bytes", (int) nwritten);
+
+ LOCK (ctx->mutex);
+ ctx->nbytes -= nwritten;
+ UNLOCK (ctx->mutex);
+ }
+ /* Indicate that we have an error. */
+ if (!SetEvent (ctx->is_empty))
+ TRACE_LOG1 ("SetEvent failed: ec=%d", (int) GetLastError ());
+ SetEvent (ctx->stopped);
+
+ return TRACE_SUC ();
+}
+
+
+static struct writer_context_s *
+create_writer (int fd)
+{
+ struct writer_context_s *ctx;
+ SECURITY_ATTRIBUTES sec_attr;
+ DWORD tid;
+
+ TRACE_BEG (DEBUG_SYSIO, "gpgme:create_writer", fd);
+
+ memset (&sec_attr, 0, sizeof sec_attr);
+ sec_attr.nLength = sizeof sec_attr;
+ sec_attr.bInheritHandle = FALSE;
+
+ ctx = calloc (1, sizeof *ctx);
+ if (!ctx)
+ {
+ TRACE_SYSERR (errno);
+ return NULL;
+ }
+
+ if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used)
+ {
+ TRACE_SYSERR (EIO);
+ return NULL;
+ }
+ ctx->file_hd = fd_table[fd].handle;
+ ctx->file_sock = fd_table[fd].socket;
+
+ ctx->refcount = 1;
+ ctx->have_data = CreateEvent (&sec_attr, TRUE, FALSE, NULL);
+ if (ctx->have_data)
+ ctx->is_empty = CreateEvent (&sec_attr, TRUE, TRUE, NULL);
+ if (ctx->is_empty)
+ ctx->stopped = CreateEvent (&sec_attr, TRUE, FALSE, NULL);
+ if (!ctx->have_data || !ctx->is_empty || !ctx->stopped)
+ {
+ TRACE_LOG1 ("CreateEvent failed: ec=%d", (int) GetLastError ());
+ if (ctx->have_data)
+ CloseHandle (ctx->have_data);
+ if (ctx->is_empty)
+ CloseHandle (ctx->is_empty);
+ if (ctx->stopped)
+ CloseHandle (ctx->stopped);
+ free (ctx);
+ /* FIXME: Translate the error code. */
+ TRACE_SYSERR (EIO);
+ return NULL;
+ }
+
+ ctx->is_empty = set_synchronize (ctx->is_empty);
+ INIT_LOCK (ctx->mutex);
+
+#ifdef HAVE_W32CE_SYSTEM
+ ctx->thread_hd = CreateThread (&sec_attr, 64 * 1024, writer, ctx,
+ STACK_SIZE_PARAM_IS_A_RESERVATION, &tid);
+#else
+ ctx->thread_hd = CreateThread (&sec_attr, 0, writer, ctx, 0, &tid );
+#endif
+
+ if (!ctx->thread_hd)
+ {
+ TRACE_LOG1 ("CreateThread failed: ec=%d", (int) GetLastError ());
+ DESTROY_LOCK (ctx->mutex);
+ if (ctx->have_data)
+ CloseHandle (ctx->have_data);
+ if (ctx->is_empty)
+ CloseHandle (ctx->is_empty);
+ if (ctx->stopped)
+ CloseHandle (ctx->stopped);
+ free (ctx);
+ TRACE_SYSERR (EIO);
+ return NULL;
+ }
+ else
+ {
+ /* We set the priority of the thread higher because we know
+ that it only runs for a short time. This greatly helps to
+ increase the performance of the I/O. */
+ SetThreadPriority (ctx->thread_hd, get_desired_thread_priority ());
+ }
+
+ TRACE_SUC ();
+ return ctx;
+}
+
+static void
+destroy_writer (struct writer_context_s *ctx)
+{
+ LOCK (ctx->mutex);
+ ctx->refcount--;
+ if (ctx->refcount != 0)
+ {
+ UNLOCK (ctx->mutex);
+ return;
+ }
+ ctx->stop_me = 1;
+ if (ctx->have_data)
+ SetEvent (ctx->have_data);
+ UNLOCK (ctx->mutex);
+
+#ifdef HAVE_W32CE_SYSTEM
+ /* Scenario: We never create a full pipe, but already started
+ writing more than the pipe buffer. Then we need to unblock the
+ writer in the pipe driver to make our writer thread notice that
+ we want it to go away. */
+
+ if (!DeviceIoControl (ctx->file_hd, GPGCEDEV_IOCTL_UNBLOCK,
+ NULL, 0, NULL, 0, NULL, NULL))
+ {
+ TRACE1 (DEBUG_SYSIO, "gpgme:destroy_writer", ctx->file_hd,
+ "unblock control call failed for thread %p", ctx->thread_hd);
+ }
+#endif
+
+ TRACE1 (DEBUG_SYSIO, "gpgme:destroy_writer", ctx->file_hd,
+ "waiting for termination of thread %p", ctx->thread_hd);
+ WaitForSingleObject (ctx->stopped, INFINITE);
+ TRACE1 (DEBUG_SYSIO, "gpgme:destroy_writer", ctx->file_hd,
+ "thread %p has terminated", ctx->thread_hd);
+
+ if (ctx->stopped)
+ CloseHandle (ctx->stopped);
+ if (ctx->have_data)
+ CloseHandle (ctx->have_data);
+ if (ctx->is_empty)
+ CloseHandle (ctx->is_empty);
+ CloseHandle (ctx->thread_hd);
+ DESTROY_LOCK (ctx->mutex);
+ free (ctx);
+}
+
+
+/* Find a writer context or create a new one. Note that the writer
+ context will last until a _gpgme_io_close. */
+static struct writer_context_s *
+find_writer (int fd, int start_it)
+{
+ struct writer_context_s *wt = NULL;
+ int i;
+
+ LOCK (writer_table_lock);
+ for (i = 0; i < writer_table_size; i++)
+ if (writer_table[i].used && writer_table[i].fd == fd)
+ wt = writer_table[i].context;
+
+ if (wt || !start_it)
+ {
+ UNLOCK (writer_table_lock);
+ return wt;
+ }
+
+ for (i = 0; i < writer_table_size; i++)
+ if (!writer_table[i].used)
+ break;
+
+ if (i != writer_table_size)
+ {
+ wt = create_writer (fd);
+ writer_table[i].fd = fd;
+ writer_table[i].context = wt;
+ writer_table[i].used = 1;
+ }
+
+ UNLOCK (writer_table_lock);
+ return wt;
+}
+
+
+static void
+kill_writer (int fd)
+{
+ int i;
+
+ LOCK (writer_table_lock);
+ for (i = 0; i < writer_table_size; i++)
+ {
+ if (writer_table[i].used && writer_table[i].fd == fd)
+ {
+ destroy_writer (writer_table[i].context);
+ writer_table[i].context = NULL;
+ writer_table[i].used = 0;
+ break;
+ }
+ }
+ UNLOCK (writer_table_lock);
+}
+
+
+int
+_gpgme_io_write (int fd, const void *buffer, size_t count)
+{
+ struct writer_context_s *ctx;
+ TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_write", fd,
+ "buffer=%p, count=%u", buffer, count);
+ TRACE_LOGBUF (buffer, count);
+
+ if (count == 0)
+ return TRACE_SYSRES (0);
+
+ ctx = find_writer (fd, 1);
+ if (!ctx)
+ return TRACE_SYSRES (-1);
+
+ LOCK (ctx->mutex);
+ if (!ctx->error && ctx->nbytes)
+ {
+ /* Bytes are pending for send. */
+
+ /* Reset the is_empty event. Better safe than sorry. */
+ if (!ResetEvent (ctx->is_empty))
+ {
+ TRACE_LOG1 ("ResetEvent failed: ec=%d", (int) GetLastError ());
+ UNLOCK (ctx->mutex);
+ /* FIXME: Should translate the error code. */
+ gpg_err_set_errno (EIO);
+ return TRACE_SYSRES (-1);
+ }
+ UNLOCK (ctx->mutex);
+ TRACE_LOG1 ("waiting for empty buffer in thread %p", ctx->thread_hd);
+ WaitForSingleObject (ctx->is_empty, INFINITE);
+ TRACE_LOG1 ("thread %p buffer is empty", ctx->thread_hd);
+ LOCK (ctx->mutex);
+ }
+
+ if (ctx->error)
+ {
+ UNLOCK (ctx->mutex);
+ if (ctx->error_code == ERROR_NO_DATA)
+ gpg_err_set_errno (EPIPE);
+ else
+ gpg_err_set_errno (EIO);
+ return TRACE_SYSRES (-1);
+ }
+
+ /* If no error occured, the number of bytes in the buffer must be
+ zero. */
+ assert (!ctx->nbytes);
+
+ if (count > WRITEBUF_SIZE)
+ count = WRITEBUF_SIZE;
+ memcpy (ctx->buffer, buffer, count);
+ ctx->nbytes = count;
+
+ /* We have to reset the is_empty event early, because it is also
+ used by the select() implementation to probe the channel. */
+ if (!ResetEvent (ctx->is_empty))
+ {
+ TRACE_LOG1 ("ResetEvent failed: ec=%d", (int) GetLastError ());
+ UNLOCK (ctx->mutex);
+ /* FIXME: Should translate the error code. */
+ gpg_err_set_errno (EIO);
+ return TRACE_SYSRES (-1);
+ }
+ if (!SetEvent (ctx->have_data))
+ {
+ TRACE_LOG1 ("SetEvent failed: ec=%d", (int) GetLastError ());
+ UNLOCK (ctx->mutex);
+ /* FIXME: Should translate the error code. */
+ gpg_err_set_errno (EIO);
+ return TRACE_SYSRES (-1);
+ }
+ UNLOCK (ctx->mutex);
+
+ return TRACE_SYSRES ((int) count);
+}
+
+
+int
+_gpgme_io_pipe (int filedes[2], int inherit_idx)
+{
+ int rfd;
+ int wfd;
+#ifdef HAVE_W32CE_SYSTEM
+ HANDLE hd;
+ int rvid;
+#else
+ HANDLE rh;
+ HANDLE wh;
+ SECURITY_ATTRIBUTES sec_attr;
+#endif
+
+ TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_pipe", filedes,
+ "inherit_idx=%i (GPGME uses it for %s)",
+ inherit_idx, inherit_idx ? "reading" : "writing");
+
+ rfd = new_fd ();
+ if (rfd == -1)
+ return TRACE_SYSRES (-1);
+ wfd = new_fd ();
+ if (wfd == -1)
+ {
+ release_fd (rfd);
+ return TRACE_SYSRES (-1);
+ }
+
+#ifdef HAVE_W32CE_SYSTEM
+ hd = _assuan_w32ce_prepare_pipe (&rvid, !inherit_idx);
+ if (hd == INVALID_HANDLE_VALUE)
+ {
+ TRACE_LOG1 ("_assuan_w32ce_prepare_pipe failed: ec=%d",
+ (int) GetLastError ());
+ release_fd (rfd);
+ release_fd (wfd);
+ /* FIXME: Should translate the error code. */
+ gpg_err_set_errno (EIO);
+ return TRACE_SYSRES (-1);
+ }
+
+ if (inherit_idx == 0)
+ {
+ fd_table[rfd].rvid = rvid;
+ fd_table[wfd].handle = hd;
+ }
+ else
+ {
+ fd_table[rfd].handle = hd;
+ fd_table[wfd].rvid = rvid;
+ }
+
+#else
+
+ memset (&sec_attr, 0, sizeof (sec_attr));
+ sec_attr.nLength = sizeof (sec_attr);
+ sec_attr.bInheritHandle = FALSE;
+
+ if (!CreatePipe (&rh, &wh, &sec_attr, PIPEBUF_SIZE))
+ {
+ TRACE_LOG1 ("CreatePipe failed: ec=%d", (int) GetLastError ());
+ release_fd (rfd);
+ release_fd (wfd);
+ /* FIXME: Should translate the error code. */
+ gpg_err_set_errno (EIO);
+ return TRACE_SYSRES (-1);
+ }
+
+ /* Make one end inheritable. */
+ if (inherit_idx == 0)
+ {
+ HANDLE hd;
+ if (!DuplicateHandle (GetCurrentProcess(), rh,
+ GetCurrentProcess(), &hd, 0,
+ TRUE, DUPLICATE_SAME_ACCESS))
+ {
+ TRACE_LOG1 ("DuplicateHandle failed: ec=%d",
+ (int) GetLastError ());
+ release_fd (rfd);
+ release_fd (wfd);
+ CloseHandle (rh);
+ CloseHandle (wh);
+ /* FIXME: Should translate the error code. */
+ gpg_err_set_errno (EIO);
+ return TRACE_SYSRES (-1);
+ }
+ CloseHandle (rh);
+ rh = hd;
+ }
+ else if (inherit_idx == 1)
+ {
+ HANDLE hd;
+ if (!DuplicateHandle( GetCurrentProcess(), wh,
+ GetCurrentProcess(), &hd, 0,
+ TRUE, DUPLICATE_SAME_ACCESS))
+ {
+ TRACE_LOG1 ("DuplicateHandle failed: ec=%d",
+ (int) GetLastError ());
+ release_fd (rfd);
+ release_fd (wfd);
+ CloseHandle (rh);
+ CloseHandle (wh);
+ /* FIXME: Should translate the error code. */
+ gpg_err_set_errno (EIO);
+ return TRACE_SYSRES (-1);
+ }
+ CloseHandle (wh);
+ wh = hd;
+ }
+ fd_table[rfd].handle = rh;
+ fd_table[wfd].handle = wh;
+#endif
+
+ filedes[0] = rfd;
+ filedes[1] = wfd;
+ return TRACE_SUC6 ("read=0x%x (%p/0x%x), write=0x%x (%p/0x%x)",
+ rfd, fd_table[rfd].handle, fd_table[rfd].rvid,
+ wfd, fd_table[wfd].handle, fd_table[wfd].rvid);
+}
+
+
+int
+_gpgme_io_close (int fd)
+{
+ int i;
+ _gpgme_close_notify_handler_t handler = NULL;
+ void *value = NULL;
+
+ TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_close", fd);
+
+ if (fd == -1)
+ {
+ gpg_err_set_errno (EBADF);
+ return TRACE_SYSRES (-1);
+ }
+ if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used)
+ {
+ gpg_err_set_errno (EBADF);
+ return TRACE_SYSRES (-1);
+ }
+
+ kill_reader (fd);
+ kill_writer (fd);
+ LOCK (notify_table_lock);
+ for (i = 0; i < DIM (notify_table); i++)
+ {
+ if (notify_table[i].inuse && notify_table[i].fd == fd)
+ {
+ handler = notify_table[i].handler;
+ value = notify_table[i].value;
+ notify_table[i].handler = NULL;
+ notify_table[i].value = NULL;
+ notify_table[i].inuse = 0;
+ break;
+ }
+ }
+ UNLOCK (notify_table_lock);
+ if (handler)
+ handler (fd, value);
+
+ if (fd_table[fd].dup_from == -1)
+ {
+ if (fd_table[fd].handle != INVALID_HANDLE_VALUE)
+ {
+ if (!CloseHandle (fd_table[fd].handle))
+ {
+ TRACE_LOG1 ("CloseHandle failed: ec=%d", (int) GetLastError ());
+ /* FIXME: Should translate the error code. */
+ gpg_err_set_errno (EIO);
+ return TRACE_SYSRES (-1);
+ }
+ }
+ else if (fd_table[fd].socket != INVALID_SOCKET)
+ {
+ if (closesocket (fd_table[fd].socket))
+ {
+ TRACE_LOG1 ("closesocket failed: ec=%d", (int) WSAGetLastError ());
+ /* FIXME: Should translate the error code. */
+ gpg_err_set_errno (EIO);
+ return TRACE_SYSRES (-1);
+ }
+ }
+ /* Nothing to do for RVIDs. */
+ }
+
+ release_fd (fd);
+
+ return TRACE_SYSRES (0);
+}
+
+
+int
+_gpgme_io_set_close_notify (int fd, _gpgme_close_notify_handler_t handler,
+ void *value)
+{
+ int i;
+ TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_set_close_notify", fd,
+ "close_handler=%p/%p", handler, value);
+
+ assert (fd != -1);
+
+ LOCK (notify_table_lock);
+ for (i=0; i < DIM (notify_table); i++)
+ if (notify_table[i].inuse && notify_table[i].fd == fd)
+ break;
+ if (i == DIM (notify_table))
+ for (i = 0; i < DIM (notify_table); i++)
+ if (!notify_table[i].inuse)
+ break;
+ if (i == DIM (notify_table))
+ {
+ UNLOCK (notify_table_lock);
+ gpg_err_set_errno (EINVAL);
+ return TRACE_SYSRES (-1);
+ }
+ notify_table[i].fd = fd;
+ notify_table[i].handler = handler;
+ notify_table[i].value = value;
+ notify_table[i].inuse = 1;
+ UNLOCK (notify_table_lock);
+ return TRACE_SYSRES (0);
+}
+
+
+int
+_gpgme_io_set_nonblocking (int fd)
+{
+ TRACE (DEBUG_SYSIO, "_gpgme_io_set_nonblocking", fd);
+ return 0;
+}
+
+
+#ifdef HAVE_W32CE_SYSTEM
+static char *
+build_commandline (char **argv, int fd0, int fd0_isnull,
+ int fd1, int fd1_isnull,
+ int fd2, int fd2_isnull)
+{
+ int i, n;
+ const char *s;
+ char *buf, *p;
+ char fdbuf[3*30];
+
+ p = fdbuf;
+ *p = 0;
+
+ if (fd0 != -1)
+ {
+ if (fd0_isnull)
+ strcpy (p, "-&S0=null ");
+ else
+ snprintf (p, 25, "-&S0=%d ", fd_table[fd0].rvid);
+ p += strlen (p);
+ }
+ if (fd1 != -1)
+ {
+ if (fd1_isnull)
+ strcpy (p, "-&S1=null ");
+ else
+ snprintf (p, 25, "-&S1=%d ", fd_table[fd1].rvid);
+ p += strlen (p);
+ }
+ if (fd2 != -1)
+ {
+ if (fd2_isnull)
+ strcpy (p, "-&S2=null ");
+ else
+ snprintf (p, 25, "-&S2=%d ", fd_table[fd2].rvid);
+ p += strlen (p);
+ }
+ strcpy (p, "-&S2=null ");
+ p += strlen (p);
+
+ n = strlen (fdbuf);
+ for (i=0; (s = argv[i]); i++)
+ {
+ if (!i)
+ continue; /* Ignore argv[0]. */
+ n += strlen (s) + 1 + 2; /* (1 space, 2 quoting) */
+ for (; *s; s++)
+ if (*s == '\"')
+ n++; /* Need to double inner quotes. */
+ }
+ n++;
+ buf = p = malloc (n);
+ if (! buf)
+ return NULL;
+
+ p = stpcpy (p, fdbuf);
+ for (i = 0; argv[i]; i++)
+ {
+ if (!i)
+ continue; /* Ignore argv[0]. */
+ if (i > 1)
+ p = stpcpy (p, " ");
+
+ if (! *argv[i]) /* Empty string. */
+ p = stpcpy (p, "\"\"");
+ else if (strpbrk (argv[i], " \t\n\v\f\""))
+ {
+ p = stpcpy (p, "\"");
+ for (s = argv[i]; *s; s++)
+ {
+ *p++ = *s;
+ if (*s == '\"')
+ *p++ = *s;
+ }
+ *p++ = '\"';
+ *p = 0;
+ }
+ else
+ p = stpcpy (p, argv[i]);
+ }
+
+ return buf;
+}
+#else
+static char *
+build_commandline (char **argv)
+{
+ int i;
+ int n = 0;
+ char *buf;
+ char *p;
+
+ /* We have to quote some things because under Windows the program
+ parses the commandline and does some unquoting. We enclose the
+ whole argument in double-quotes, and escape literal double-quotes
+ as well as backslashes with a backslash. We end up with a
+ trailing space at the end of the line, but that is harmless. */
+ for (i = 0; argv[i]; i++)
+ {
+ p = argv[i];
+ /* The leading double-quote. */
+ n++;
+ while (*p)
+ {
+ /* An extra one for each literal that must be escaped. */
+ if (*p == '\\' || *p == '"')
+ n++;
+ n++;
+ p++;
+ }
+ /* The trailing double-quote and the delimiter. */
+ n += 2;
+ }
+ /* And a trailing zero. */
+ n++;
+
+ buf = p = malloc (n);
+ if (!buf)
+ return NULL;
+ for (i = 0; argv[i]; i++)
+ {
+ char *argvp = argv[i];
+
+ *(p++) = '"';
+ while (*argvp)
+ {
+ if (*argvp == '\\' || *argvp == '"')
+ *(p++) = '\\';
+ *(p++) = *(argvp++);
+ }
+ *(p++) = '"';
+ *(p++) = ' ';
+ }
+ *(p++) = 0;
+
+ return buf;
+}
+#endif
+
+
+int
+_gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags,
+ struct spawn_fd_item_s *fd_list,
+ void (*atfork) (void *opaque, int reserved),
+ void *atforkvalue, pid_t *r_pid)
+{
+ PROCESS_INFORMATION pi =
+ {
+ NULL, /* returns process handle */
+ 0, /* returns primary thread handle */
+ 0, /* returns pid */
+ 0 /* returns tid */
+ };
+ int i;
+
+#ifdef HAVE_W32CE_SYSTEM
+ int fd_in = -1;
+ int fd_out = -1;
+ int fd_err = -1;
+ int fd_in_isnull = 1;
+ int fd_out_isnull = 1;
+ int fd_err_isnull = 1;
+ char *cmdline;
+ HANDLE hd = INVALID_HANDLE_VALUE;
+
+ TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_spawn", path,
+ "path=%s", path);
+ i = 0;
+ while (argv[i])
+ {
+ TRACE_LOG2 ("argv[%2i] = %s", i, argv[i]);
+ i++;
+ }
+
+ for (i = 0; fd_list[i].fd != -1; i++)
+ {
+ int fd = fd_list[i].fd;
+
+ TRACE_LOG3 ("fd_list[%2i] = fd %i, dup_to %i", i, fd, fd_list[i].dup_to);
+ if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used)
+ {
+ TRACE_LOG1 ("invalid fd 0x%x", fd);
+ gpg_err_set_errno (EBADF);
+ return TRACE_SYSRES (-1);
+ }
+ if (fd_table[fd].rvid == 0)
+ {
+ TRACE_LOG1 ("fd 0x%x not inheritable (not an RVID)", fd);
+ gpg_err_set_errno (EBADF);
+ return TRACE_SYSRES (-1);
+ }
+
+ if (fd_list[i].dup_to == 0)
+ {
+ fd_in = fd_list[i].fd;
+ fd_in_isnull = 0;
+ }
+ else if (fd_list[i].dup_to == 1)
+ {
+ fd_out = fd_list[i].fd;
+ fd_out_isnull = 0;
+ }
+ else if (fd_list[i].dup_to == 2)
+ {
+ fd_err = fd_list[i].fd;
+ fd_err_isnull = 0;
+ }
+ }
+
+ cmdline = build_commandline (argv, fd_in, fd_in_isnull,
+ fd_out, fd_out_isnull, fd_err, fd_err_isnull);
+ if (!cmdline)
+ {
+ TRACE_LOG1 ("build_commandline failed: %s", strerror (errno));
+ return TRACE_SYSRES (-1);
+ }
+
+ if (!CreateProcessA (path, /* Program to start. */
+ cmdline, /* Command line arguments. */
+ NULL, /* (not supported) */
+ NULL, /* (not supported) */
+ FALSE, /* (not supported) */
+ (CREATE_SUSPENDED), /* Creation flags. */
+ NULL, /* (not supported) */
+ NULL, /* (not supported) */
+ NULL, /* (not supported) */
+ &pi /* Returns process information.*/
+ ))
+ {
+ TRACE_LOG1 ("CreateProcess failed: ec=%d", (int) GetLastError ());
+ free (cmdline);
+ gpg_err_set_errno (EIO);
+ return TRACE_SYSRES (-1);
+ }
+
+ /* Create arbitrary pipe descriptor to send in ASSIGN_RVID
+ commands. Errors are ignored. We don't need read or write access,
+ as ASSIGN_RVID works without any permissions, yay! */
+ hd = CreateFile (L"GPG1:", 0, 0,
+ NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hd == INVALID_HANDLE_VALUE)
+ {
+ TRACE_LOG1 ("CreateFile failed (ignored): ec=%d",
+ (int) GetLastError ());
+ }
+
+ /* Insert the inherited handles. */
+ for (i = 0; fd_list[i].fd != -1; i++)
+ {
+ /* Return the child name of this handle. */
+ fd_list[i].peer_name = fd_table[fd_list[i].fd].rvid;
+
+ if (hd != INVALID_HANDLE_VALUE)
+ {
+ DWORD data[2];
+ data[0] = (DWORD) fd_table[fd_list[i].fd].rvid;
+ data[1] = pi.dwProcessId;
+ if (!DeviceIoControl (hd, GPGCEDEV_IOCTL_ASSIGN_RVID,
+ data, sizeof (data), NULL, 0, NULL, NULL))
+ {
+ TRACE_LOG3 ("ASSIGN_RVID(%i, %i) failed (ignored): %i",
+ data[0], data[1], (int) GetLastError ());
+ }
+ }
+ }
+ if (hd != INVALID_HANDLE_VALUE)
+ CloseHandle (hd);
+
+#else
+ SECURITY_ATTRIBUTES sec_attr;
+ STARTUPINFOA si;
+ int cr_flags = CREATE_DEFAULT_ERROR_MODE;
+ char **args;
+ char *arg_string;
+ /* FIXME. */
+ int debug_me = 0;
+ int tmp_fd;
+ char *tmp_name;
+
+ TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_spawn", path,
+ "path=%s", path);
+ i = 0;
+ while (argv[i])
+ {
+ TRACE_LOG2 ("argv[%2i] = %s", i, argv[i]);
+ i++;
+ }
+
+ /* We do not inherit any handles by default, and just insert those
+ handles we want the child to have afterwards. But some handle
+ values occur on the command line, and we need to move
+ stdin/out/err to the right location. So we use a wrapper program
+ which gets the information from a temporary file. */
+ if (_gpgme_mkstemp (&tmp_fd, &tmp_name) < 0)
+ {
+ TRACE_LOG1 ("_gpgme_mkstemp failed: %s", strerror (errno));
+ return TRACE_SYSRES (-1);
+ }
+ TRACE_LOG1 ("tmp_name = %s", tmp_name);
+
+ args = calloc (2 + i + 1, sizeof (*args));
+ args[0] = (char *) _gpgme_get_w32spawn_path ();
+ args[1] = tmp_name;
+ args[2] = path;
+ memcpy (&args[3], &argv[1], i * sizeof (*args));
+
+ memset (&sec_attr, 0, sizeof sec_attr);
+ sec_attr.nLength = sizeof sec_attr;
+ sec_attr.bInheritHandle = FALSE;
+
+ arg_string = build_commandline (args);
+ free (args);
+ if (!arg_string)
+ {
+ close (tmp_fd);
+ DeleteFileA (tmp_name);
+ return TRACE_SYSRES (-1);
+ }
+
+ memset (&si, 0, sizeof si);
+ si.cb = sizeof (si);
+ si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
+ si.wShowWindow = debug_me ? SW_SHOW : SW_HIDE;
+ si.hStdInput = INVALID_HANDLE_VALUE;
+ si.hStdOutput = INVALID_HANDLE_VALUE;
+ si.hStdError = INVALID_HANDLE_VALUE;
+
+ cr_flags |= CREATE_SUSPENDED;
+ cr_flags |= DETACHED_PROCESS;
+ cr_flags |= GetPriorityClass (GetCurrentProcess ());
+ if (!CreateProcessA (_gpgme_get_w32spawn_path (),
+ arg_string,
+ &sec_attr, /* process security attributes */
+ &sec_attr, /* thread security attributes */
+ FALSE, /* inherit handles */
+ cr_flags, /* creation flags */
+ NULL, /* environment */
+ NULL, /* use current drive/directory */
+ &si, /* startup information */
+ &pi)) /* returns process information */
+ {
+ TRACE_LOG1 ("CreateProcess failed: ec=%d", (int) GetLastError ());
+ free (arg_string);
+ close (tmp_fd);
+ DeleteFileA (tmp_name);
+
+ /* FIXME: Should translate the error code. */
+ gpg_err_set_errno (EIO);
+ return TRACE_SYSRES (-1);
+ }
+
+ free (arg_string);
+
+ if (flags & IOSPAWN_FLAG_ALLOW_SET_FG)
+ _gpgme_allow_set_foreground_window ((pid_t)pi.dwProcessId);
+
+ /* Insert the inherited handles. */
+ for (i = 0; fd_list[i].fd != -1; i++)
+ {
+ int fd = fd_list[i].fd;
+ HANDLE ohd = INVALID_HANDLE_VALUE;
+ HANDLE hd = INVALID_HANDLE_VALUE;
+
+ /* Make it inheritable for the wrapper process. */
+ if (fd >= 0 && fd < MAX_SLAFD && fd_table[fd].used)
+ ohd = fd_table[fd].handle;
+
+ if (!DuplicateHandle (GetCurrentProcess(), ohd,
+ pi.hProcess, &hd, 0, TRUE, DUPLICATE_SAME_ACCESS))
+ {
+ TRACE_LOG1 ("DuplicateHandle failed: ec=%d", (int) GetLastError ());
+ TerminateProcess (pi.hProcess, 0);
+ /* Just in case TerminateProcess didn't work, let the
+ process fail on its own. */
+ ResumeThread (pi.hThread);
+ CloseHandle (pi.hThread);
+ CloseHandle (pi.hProcess);
+
+ close (tmp_fd);
+ DeleteFileA (tmp_name);
+
+ /* FIXME: Should translate the error code. */
+ gpg_err_set_errno (EIO);
+ return TRACE_SYSRES (-1);
+ }
+ /* Return the child name of this handle. */
+ fd_list[i].peer_name = handle_to_fd (hd);
+ }
+
+ /* Write the handle translation information to the temporary
+ file. */
+ {
+ /* Hold roughly MAX_TRANS quadruplets of 64 bit numbers in hex
+ notation: "0xFEDCBA9876543210" with an extra white space after
+ every quadruplet. 10*(19*4 + 1) - 1 = 769. This plans ahead
+ for a time when a HANDLE is 64 bit. */
+#define BUFFER_MAX 810
+ char line[BUFFER_MAX + 1];
+ int res;
+ int written;
+ size_t len;
+
+ if ((flags & IOSPAWN_FLAG_ALLOW_SET_FG))
+ strcpy (line, "~1 \n");
+ else
+ strcpy (line, "\n");
+ for (i = 0; fd_list[i].fd != -1; i++)
+ {
+ /* Strip the newline. */
+ len = strlen (line) - 1;
+
+ /* Format is: Local name, stdin/stdout/stderr, peer name, argv idx. */
+ snprintf (&line[len], BUFFER_MAX - len, "0x%x %d 0x%x %d \n",
+ fd_list[i].fd, fd_list[i].dup_to,
+ fd_list[i].peer_name, fd_list[i].arg_loc);
+ /* Rather safe than sorry. */
+ line[BUFFER_MAX - 1] = '\n';
+ line[BUFFER_MAX] = '\0';
+ }
+ len = strlen (line);
+ written = 0;
+ do
+ {
+ res = write (tmp_fd, &line[written], len - written);
+ if (res > 0)
+ written += res;
+ }
+ while (res > 0 || (res < 0 && errno == EAGAIN));
+ }
+ close (tmp_fd);
+ /* The temporary file is deleted by the gpgme-w32spawn process
+ (hopefully). */
+#endif
+
+
+ TRACE_LOG4 ("CreateProcess ready: hProcess=%p, hThread=%p, "
+ "dwProcessID=%d, dwThreadId=%d",
+ pi.hProcess, pi.hThread,
+ (int) pi.dwProcessId, (int) pi.dwThreadId);
+
+ if (r_pid)
+ *r_pid = (pid_t)pi.dwProcessId;
+
+
+ if (ResumeThread (pi.hThread) < 0)
+ TRACE_LOG1 ("ResumeThread failed: ec=%d", (int) GetLastError ());
+
+ if (!CloseHandle (pi.hThread))
+ TRACE_LOG1 ("CloseHandle of thread failed: ec=%d",
+ (int) GetLastError ());
+
+ TRACE_LOG1 ("process=%p", pi.hProcess);
+
+ /* We don't need to wait for the process. */
+ if (!CloseHandle (pi.hProcess))
+ TRACE_LOG1 ("CloseHandle of process failed: ec=%d",
+ (int) GetLastError ());
+
+ if (! (flags & IOSPAWN_FLAG_NOCLOSE))
+ {
+ for (i = 0; fd_list[i].fd != -1; i++)
+ _gpgme_io_close (fd_list[i].fd);
+ }
+
+ for (i = 0; fd_list[i].fd != -1; i++)
+ if (fd_list[i].dup_to == -1)
+ TRACE_LOG3 ("fd[%i] = 0x%x -> 0x%x", i, fd_list[i].fd,
+ fd_list[i].peer_name);
+ else
+ TRACE_LOG4 ("fd[%i] = 0x%x -> 0x%x (std%s)", i, fd_list[i].fd,
+ fd_list[i].peer_name, (fd_list[i].dup_to == 0) ? "in" :
+ ((fd_list[i].dup_to == 1) ? "out" : "err"));
+
+ return TRACE_SYSRES (0);
+}
+
+
+/* Select on the list of fds. Returns: -1 = error, 0 = timeout or
+ nothing to select, > 0 = number of signaled fds. */
+int
+_gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
+{
+ HANDLE waitbuf[MAXIMUM_WAIT_OBJECTS];
+ int waitidx[MAXIMUM_WAIT_OBJECTS];
+ int code;
+ int nwait;
+ int i;
+ int any;
+ int count;
+ void *dbg_help;
+ TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_select", fds,
+ "nfds=%u, nonblock=%u", nfds, nonblock);
+
+ restart:
+ TRACE_SEQ (dbg_help, "select on [ ");
+ any = 0;
+ nwait = 0;
+ count = 0;
+ for (i=0; i < nfds; i++)
+ {
+ if (fds[i].fd == -1)
+ continue;
+ fds[i].signaled = 0;
+ if (fds[i].for_read || fds[i].for_write)
+ {
+ if (fds[i].for_read)
+ {
+ struct reader_context_s *ctx = find_reader (fds[i].fd,1);
+
+ if (!ctx)
+ TRACE_LOG1 ("error: no reader for FD 0x%x (ignored)",
+ fds[i].fd);
+ else
+ {
+ if (nwait >= DIM (waitbuf))
+ {
+ TRACE_END (dbg_help, "oops ]");
+ TRACE_LOG ("Too many objects for WFMO!");
+ /* FIXME: Should translate the error code. */
+ gpg_err_set_errno (EIO);
+ return TRACE_SYSRES (-1);
+ }
+ waitidx[nwait] = i;
+ waitbuf[nwait++] = ctx->have_data_ev;
+ }
+ TRACE_ADD1 (dbg_help, "r0x%x ", fds[i].fd);
+ any = 1;
+ }
+ else if (fds[i].for_write)
+ {
+ struct writer_context_s *ctx = find_writer (fds[i].fd,1);
+
+ if (!ctx)
+ TRACE_LOG1 ("error: no writer for FD 0x%x (ignored)",
+ fds[i].fd);
+ else
+ {
+ if (nwait >= DIM (waitbuf))
+ {
+ TRACE_END (dbg_help, "oops ]");
+ TRACE_LOG ("Too many objects for WFMO!");
+ /* FIXME: Should translate the error code. */
+ gpg_err_set_errno (EIO);
+ return TRACE_SYSRES (-1);
+ }
+ waitidx[nwait] = i;
+ waitbuf[nwait++] = ctx->is_empty;
+ }
+ TRACE_ADD1 (dbg_help, "w0x%x ", fds[i].fd);
+ any = 1;
+ }
+ }
+ }
+ TRACE_END (dbg_help, "]");
+ if (!any)
+ return TRACE_SYSRES (0);
+
+ code = WaitForMultipleObjects (nwait, waitbuf, 0, nonblock ? 0 : 1000);
+ if (code >= WAIT_OBJECT_0 && code < WAIT_OBJECT_0 + nwait)
+ {
+ /* This WFMO is a really silly function: It does return either
+ the index of the signaled object or if 2 objects have been
+ signalled at the same time, the index of the object with the
+ lowest object is returned - so and how do we find out how
+ many objects have been signaled???. The only solution I can
+ imagine is to test each object starting with the returned
+ index individually - how dull. */
+ any = 0;
+ for (i = code - WAIT_OBJECT_0; i < nwait; i++)
+ {
+ if (WaitForSingleObject (waitbuf[i], 0) == WAIT_OBJECT_0)
+ {
+ assert (waitidx[i] >=0 && waitidx[i] < nfds);
+ fds[waitidx[i]].signaled = 1;
+ any = 1;
+ count++;
+ }
+ }
+ if (!any)
+ {
+ TRACE_LOG ("no signaled objects found after WFMO");
+ count = -1;
+ }
+ }
+ else if (code == WAIT_TIMEOUT)
+ TRACE_LOG ("WFMO timed out");
+ else if (code == WAIT_FAILED)
+ {
+ int le = (int) GetLastError ();
+#if 0
+ if (le == ERROR_INVALID_HANDLE)
+ {
+ int k;
+ int j = handle_to_fd (waitbuf[i]);
+
+ TRACE_LOG1 ("WFMO invalid handle %d removed", j);
+ for (k = 0 ; k < nfds; k++)
+ {
+ if (fds[k].fd == j)
+ {
+ fds[k].for_read = fds[k].for_write = 0;
+ goto restart;
+ }
+ }
+ TRACE_LOG (" oops, or not???");
+ }
+#endif
+ TRACE_LOG1 ("WFMO failed: %d", le);
+ count = -1;
+ }
+ else
+ {
+ TRACE_LOG1 ("WFMO returned %d", code);
+ count = -1;
+ }
+
+ if (count > 0)
+ {
+ TRACE_SEQ (dbg_help, "select OK [ ");
+ for (i = 0; i < nfds; i++)
+ {
+ if (fds[i].fd == -1)
+ continue;
+ if ((fds[i].for_read || fds[i].for_write) && fds[i].signaled)
+ TRACE_ADD2 (dbg_help, "%c0x%x ",
+ fds[i].for_read ? 'r' : 'w', fds[i].fd);
+ }
+ TRACE_END (dbg_help, "]");
+ }
+
+ if (count < 0)
+ {
+ /* FIXME: Should determine a proper error code. */
+ gpg_err_set_errno (EIO);
+ }
+
+ return TRACE_SYSRES (count);
+}
+
+
+void
+_gpgme_io_subsystem_init (void)
+{
+ /* Nothing to do. */
+}
+
+
+/* Write the printable version of FD to the buffer BUF of length
+ BUFLEN. The printable version is the representation on the command
+ line that the child process expects. */
+int
+_gpgme_io_fd2str (char *buf, int buflen, int fd)
+{
+#ifdef HAVE_W32CE_SYSTEM
+ /* FIXME: For now. See above. */
+ if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used
+ || fd_table[fd].rvid == 0)
+ fd = -1;
+ else
+ fd = fd_table[fd].rvid;
+#endif
+
+ return snprintf (buf, buflen, "%d", fd);
+}
+
+
+int
+_gpgme_io_dup (int fd)
+{
+ int newfd;
+ struct reader_context_s *rd_ctx;
+ struct writer_context_s *wt_ctx;
+ int i;
+
+ TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_dup", fd);
+
+ if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used)
+ {
+ gpg_err_set_errno (EINVAL);
+ return TRACE_SYSRES (-1);
+ }
+
+ newfd = new_fd();
+ if (newfd == -1)
+ return TRACE_SYSRES (-1);
+
+ fd_table[newfd].handle = fd_table[fd].handle;
+ fd_table[newfd].socket = fd_table[fd].socket;
+ fd_table[newfd].rvid = fd_table[fd].rvid;
+ fd_table[newfd].dup_from = fd;
+
+ rd_ctx = find_reader (fd, 1);
+ if (rd_ctx)
+ {
+ /* No need for locking, as the only races are against the reader
+ thread itself, which doesn't touch refcount. */
+ rd_ctx->refcount++;
+
+ LOCK (reader_table_lock);
+ for (i = 0; i < reader_table_size; i++)
+ if (!reader_table[i].used)
+ break;
+ /* FIXME. */
+ assert (i != reader_table_size);
+ reader_table[i].fd = newfd;
+ reader_table[i].context = rd_ctx;
+ reader_table[i].used = 1;
+ UNLOCK (reader_table_lock);
+ }
+
+ wt_ctx = find_writer (fd, 1);
+ if (wt_ctx)
+ {
+ /* No need for locking, as the only races are against the writer
+ thread itself, which doesn't touch refcount. */
+ wt_ctx->refcount++;
+
+ LOCK (writer_table_lock);
+ for (i = 0; i < writer_table_size; i++)
+ if (!writer_table[i].used)
+ break;
+ /* FIXME. */
+ assert (i != writer_table_size);
+ writer_table[i].fd = newfd;
+ writer_table[i].context = wt_ctx;
+ writer_table[i].used = 1;
+ UNLOCK (writer_table_lock);
+ }
+
+ return TRACE_SYSRES (newfd);
+}
+
+
+/* The following interface is only useful for GPGME Glib and Qt. */
+
+/* Compatibility interface, obsolete. */
+void *
+gpgme_get_giochannel (int fd)
+{
+ return NULL;
+}
+
+
+/* Look up the giochannel or qiodevice for file descriptor FD. */
+void *
+gpgme_get_fdptr (int fd)
+{
+ return NULL;
+}
+
+
+static int
+wsa2errno (int err)
+{
+ switch (err)
+ {
+ case WSAENOTSOCK:
+ return EINVAL;
+ case WSAEWOULDBLOCK:
+ return EAGAIN;
+ case ERROR_BROKEN_PIPE:
+ return EPIPE;
+ case WSANOTINITIALISED:
+ return ENOSYS;
+ default:
+ return EIO;
+ }
+}
+
+
+int
+_gpgme_io_socket (int domain, int type, int proto)
+{
+ int res;
+ int fd;
+
+ TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_socket", domain,
+ "type=%i, protp=%i", type, proto);
+
+ fd = new_fd();
+ if (fd == -1)
+ return TRACE_SYSRES (-1);
+
+ res = socket (domain, type, proto);
+ if (res == INVALID_SOCKET)
+ {
+ release_fd (fd);
+ gpg_err_set_errno (wsa2errno (WSAGetLastError ()));
+ return TRACE_SYSRES (-1);
+ }
+ fd_table[fd].socket = res;
+
+ TRACE_SUC2 ("socket=0x%x (0x%x)", fd, fd_table[fd].socket);
+
+ return fd;
+}
+
+
+int
+_gpgme_io_connect (int fd, struct sockaddr *addr, int addrlen)
+{
+ int res;
+
+ TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_connect", fd,
+ "addr=%p, addrlen=%i", addr, addrlen);
+
+ if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used)
+ {
+ gpg_err_set_errno (EBADF);
+ return TRACE_SYSRES (-1);
+ }
+
+ res = connect (fd_table[fd].socket, addr, addrlen);
+ if (res)
+ {
+ gpg_err_set_errno (wsa2errno (WSAGetLastError ()));
+ return TRACE_SYSRES (-1);
+ }
+
+ return TRACE_SUC ();
+}
diff --git a/src/w32-qt-io.cpp b/src/w32-qt-io.cpp
new file mode 100644
index 0000000..358ff75
--- /dev/null
+++ b/src/w32-qt-io.cpp
@@ -0,0 +1,699 @@
+/* w32-qt-io.c - W32 Glib I/O functions
+ Copyright (C) 2000 Werner Koch (dd9jn)
+ Copyright (C) 2001, 2002, 2004, 2005, 2007 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <windows.h>
+#include <io.h>
+
+#include "kdpipeiodevice.h"
+
+extern "C"
+{
+#include "util.h"
+#include "priv-io.h"
+#include "sema.h"
+#include "debug.h"
+}
+
+#ifndef O_BINARY
+#ifdef _O_BINARY
+#define O_BINARY _O_BINARY
+#else
+#define O_BINARY 0
+#endif
+#endif
+
+using _gpgme_::KDPipeIODevice;
+
+
+/* This file is an ugly hack to get GPGME working with Qt on Windows
+ targets. On Windows, you can not select() on file descriptors.
+
+ The only way to check if there is something to read is to read
+ something. This means that GPGME can not let Qt check for data
+ without letting Qt also handle the data on Windows targets.
+
+ The ugly consequence is that we need to work on QIODevices in
+ GPGME, creating a Qt dependency. Also, we need to export an
+ interface for the application to get at GPGME's QIODevices. There
+ is no good way to abstract all this with callbacks, because the
+ whole thing is also interconnected with the creation of pipes and
+ child processes.
+
+ The following rule applies only to this I/O backend:
+
+ * ALL operations must use the user defined event loop. GPGME can
+ not anymore provide its own event loop. This is mostly a sanity
+ requirement: Although we have in theory all information we need to
+ make the GPGME W32 code for select still work, it would be a big
+ complication and require changes throughout GPGME.
+
+ Eventually, we probably have to bite the bullet and make some
+ really nice callback interfaces to let the user control all this at
+ a per-context level. */
+
+#define MAX_SLAFD 1024
+
+struct DeviceEntry {
+ DeviceEntry() : iodev( 0 ), refCount( 1 ), blocking( true ) {}
+ KDPipeIODevice* iodev;
+ bool blocking;
+ mutable int refCount;
+ void ref() const { ++refCount; }
+ int unref() const { assert( refCount > 0 ); return --refCount; }
+};
+
+DeviceEntry* iodevice_table[MAX_SLAFD];
+
+
+static KDPipeIODevice *
+find_channel (int fd, int create)
+{
+ assert( fd < MAX_SLAFD );
+ if (fd < 0 || fd >= MAX_SLAFD)
+ return NULL;
+
+ if (create && !iodevice_table[fd])
+ {
+ DeviceEntry* entry = new DeviceEntry;
+ entry->iodev = new KDPipeIODevice
+ (fd, QIODevice::ReadWrite|QIODevice::Unbuffered);
+ iodevice_table[fd] = entry;
+ }
+ return iodevice_table[fd] ? iodevice_table[fd]->iodev : 0;
+}
+
+/* Write the printable version of FD to the buffer BUF of length
+ BUFLEN. The printable version is the representation on the command
+ line that the child process expects. */
+int
+_gpgme_io_fd2str (char *buf, int buflen, int fd)
+{
+ return snprintf (buf, buflen, "%d", (long)_get_osfhandle( fd ) );
+}
+
+
+void
+_gpgme_io_subsystem_init (void)
+{
+}
+
+
+static struct
+{
+ _gpgme_close_notify_handler_t handler;
+ void *value;
+} notify_table[MAX_SLAFD];
+
+
+int
+_gpgme_io_read (int fd, void *buffer, size_t count)
+{
+ int saved_errno = 0;
+ qint64 nread;
+ KDPipeIODevice *chan;
+ TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_read", fd,
+ "buffer=%p, count=%u", buffer, count);
+
+ chan = find_channel (fd, 0);
+ if (!chan)
+ {
+ TRACE_LOG ("no channel registered");
+ errno = EINVAL;
+ return TRACE_SYSRES (-1);
+ }
+ TRACE_LOG1 ("channel %p", chan);
+ if ( iodevice_table[fd] && !iodevice_table[fd]->blocking && chan->readWouldBlock() ) {
+ errno = EAGAIN;
+ return TRACE_SYSRES( -1 );
+ }
+
+ nread = chan->read ((char *) buffer, count);
+ if (nread < 0)
+ {
+ TRACE_LOG1 ("err %s", qPrintable (chan->errorString ()));
+ saved_errno = EIO;
+ nread = -1;
+ }
+
+ TRACE_LOGBUF ((char *) buffer, nread);
+
+ errno = saved_errno;
+ return TRACE_SYSRES (nread);
+}
+
+
+int
+_gpgme_io_write (int fd, const void *buffer, size_t count)
+{
+ qint64 nwritten;
+ KDPipeIODevice *chan;
+ TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_write", fd,
+ "buffer=%p, count=%u", buffer, count);
+ TRACE_LOGBUF ((char *) buffer, count);
+
+ chan = find_channel (fd, 0);
+ if (!chan)
+ {
+ TRACE_LOG ("fd %d: no channel registered");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if ( iodevice_table[fd] && !iodevice_table[fd]->blocking && chan->writeWouldBlock() )
+ {
+ errno = EAGAIN;
+ return TRACE_SYSRES( -1 );
+ }
+ nwritten = chan->write ((char *) buffer, count);
+
+ if (nwritten < 0)
+ {
+ nwritten = -1;
+ errno = EIO;
+ return TRACE_SYSRES(-1);
+ }
+ errno = 0;
+ return TRACE_SYSRES (nwritten);
+}
+
+
+int
+_gpgme_io_pipe (int filedes[2], int inherit_idx)
+{
+ KDPipeIODevice *chan;
+ TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_pipe", filedes,
+ "inherit_idx=%i (GPGME uses it for %s)",
+ inherit_idx, inherit_idx ? "reading" : "writing");
+
+#define PIPEBUF_SIZE 4096
+ if (_pipe (filedes, PIPEBUF_SIZE, O_NOINHERIT | O_BINARY) == -1)
+ return TRACE_SYSRES (-1);
+
+ /* Make one end inheritable. */
+ if (inherit_idx == 0)
+ {
+ int new_read;
+
+ new_read = _dup (filedes[0]);
+ _close (filedes[0]);
+ filedes[0] = new_read;
+
+ if (new_read < 0)
+ {
+ _close (filedes[1]);
+ return TRACE_SYSRES (-1);
+ }
+ }
+ else if (inherit_idx == 1)
+ {
+ int new_write;
+
+ new_write = _dup (filedes[1]);
+ _close (filedes[1]);
+ filedes[1] = new_write;
+
+ if (new_write < 0)
+ {
+ _close (filedes[0]);
+ return TRACE_SYSRES (-1);
+ }
+ }
+
+ /* Now we have a pipe with the right end inheritable. The other end
+ should have a giochannel. */
+
+ chan = find_channel (filedes[1 - inherit_idx], 1);
+
+ if (!chan)
+ {
+ int saved_errno = errno;
+ _close (filedes[0]);
+ _close (filedes[1]);
+ errno = saved_errno;
+ return TRACE_SYSRES (-1);
+ }
+
+ return TRACE_SUC5 ("read=0x%x/%p, write=0x%x/%p, channel=%p",
+ filedes[0], (HANDLE) _get_osfhandle (filedes[0]),
+ filedes[1], (HANDLE) _get_osfhandle (filedes[1]),
+ chan);
+}
+
+int
+_gpgme_io_close (int fd)
+{
+ KDPipeIODevice *chan;
+ TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_close", fd);
+
+ if (fd < 0 || fd >= MAX_SLAFD)
+ {
+ errno = EBADF;
+ return TRACE_SYSRES (-1);
+ }
+
+ /* First call the notify handler. */
+ if (notify_table[fd].handler)
+ {
+ notify_table[fd].handler (fd, notify_table[fd].value);
+ notify_table[fd].handler = NULL;
+ notify_table[fd].value = NULL;
+ }
+
+ /* Then do the close. */
+
+ DeviceEntry* const entry = iodevice_table[fd];
+ if ( entry ) {
+ if ( entry->unref() == 0 ) {
+ entry->iodev->close();
+ delete entry->iodev;
+ delete entry;
+ iodevice_table[fd] = 0;
+ }
+ } else {
+ _close( fd );
+ }
+
+
+
+ return 0;
+}
+
+
+int
+_gpgme_io_set_close_notify (int fd, _gpgme_close_notify_handler_t handler,
+ void *value)
+{
+ TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_set_close_notify", fd,
+ "close_handler=%p/%p", handler, value);
+
+ assert (fd != -1);
+
+ if (fd < 0 || fd >= (int) DIM (notify_table))
+ {
+ errno = EINVAL;
+ return TRACE_SYSRES (-1);
+ }
+ notify_table[fd].handler = handler;
+ notify_table[fd].value = value;
+ return TRACE_SYSRES (0);
+}
+
+
+int
+_gpgme_io_set_nonblocking (int fd)
+{
+ DeviceEntry* const entry = iodevice_table[fd];
+ assert( entry );
+ entry->blocking = false;
+ TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_set_nonblocking", fd);
+ return TRACE_SYSRES (0);
+}
+
+
+static char *
+build_commandline (char **argv)
+{
+ int i;
+ int n = 0;
+ char *buf;
+ char *p;
+
+ /* We have to quote some things because under Windows the program
+ parses the commandline and does some unquoting. We enclose the
+ whole argument in double-quotes, and escape literal double-quotes
+ as well as backslashes with a backslash. We end up with a
+ trailing space at the end of the line, but that is harmless. */
+ for (i = 0; argv[i]; i++)
+ {
+ p = argv[i];
+ /* The leading double-quote. */
+ n++;
+ while (*p)
+ {
+ /* An extra one for each literal that must be escaped. */
+ if (*p == '\\' || *p == '"')
+ n++;
+ n++;
+ p++;
+ }
+ /* The trailing double-quote and the delimiter. */
+ n += 2;
+ }
+ /* And a trailing zero. */
+ n++;
+
+ buf = p = (char *) malloc (n);
+ if (!buf)
+ return NULL;
+ for (i = 0; argv[i]; i++)
+ {
+ char *argvp = argv[i];
+
+ *(p++) = '"';
+ while (*argvp)
+ {
+ if (*argvp == '\\' || *argvp == '"')
+ *(p++) = '\\';
+ *(p++) = *(argvp++);
+ }
+ *(p++) = '"';
+ *(p++) = ' ';
+ }
+ *(p++) = 0;
+
+ return buf;
+}
+
+
+int
+_gpgme_io_spawn (const char *path, char * const argv[], unsigned int flags,
+ struct spawn_fd_item_s *fd_list,
+ void (*atfork) (void *opaque, int reserved),
+ void *atforkvalue, pid_t *r_pid)
+{
+ SECURITY_ATTRIBUTES sec_attr;
+ PROCESS_INFORMATION pi =
+ {
+ NULL, /* returns process handle */
+ 0, /* returns primary thread handle */
+ 0, /* returns pid */
+ 0 /* returns tid */
+ };
+ STARTUPINFO si;
+ int cr_flags = CREATE_DEFAULT_ERROR_MODE
+ | GetPriorityClass (GetCurrentProcess ());
+ int i;
+ char **args;
+ char *arg_string;
+ /* FIXME. */
+ int debug_me = 0;
+ int tmp_fd;
+ char *tmp_name;
+
+ TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_spawn", path,
+ "path=%s", path);
+ i = 0;
+ while (argv[i])
+ {
+ TRACE_LOG2 ("argv[%2i] = %s", i, argv[i]);
+ i++;
+ }
+
+ /* We do not inherit any handles by default, and just insert those
+ handles we want the child to have afterwards. But some handle
+ values occur on the command line, and we need to move
+ stdin/out/err to the right location. So we use a wrapper program
+ which gets the information from a temporary file. */
+ if (_gpgme_mkstemp (&tmp_fd, &tmp_name) < 0)
+ {
+ TRACE_LOG1 ("_gpgme_mkstemp failed: %s", strerror (errno));
+ return TRACE_SYSRES (-1);
+ }
+ TRACE_LOG1 ("tmp_name = %s", tmp_name);
+
+ args = (char **) calloc (2 + i + 1, sizeof (*args));
+ args[0] = (char *) _gpgme_get_w32spawn_path ();
+ args[1] = tmp_name;
+ args[2] = const_cast<char *>(path);
+ memcpy (&args[3], &argv[1], i * sizeof (*args));
+
+ memset (&sec_attr, 0, sizeof sec_attr);
+ sec_attr.nLength = sizeof sec_attr;
+ sec_attr.bInheritHandle = FALSE;
+
+ arg_string = build_commandline (args);
+ free (args);
+ if (!arg_string)
+ {
+ close (tmp_fd);
+ DeleteFile (tmp_name);
+ return TRACE_SYSRES (-1);
+ }
+
+ memset (&si, 0, sizeof si);
+ si.cb = sizeof (si);
+ si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
+ si.wShowWindow = debug_me ? SW_SHOW : SW_HIDE;
+ si.hStdInput = INVALID_HANDLE_VALUE;
+ si.hStdOutput = INVALID_HANDLE_VALUE;
+ si.hStdError = INVALID_HANDLE_VALUE;
+
+ cr_flags |= CREATE_SUSPENDED;
+ cr_flags |= DETACHED_PROCESS;
+ if (!CreateProcessA (_gpgme_get_w32spawn_path (),
+ arg_string,
+ &sec_attr, /* process security attributes */
+ &sec_attr, /* thread security attributes */
+ FALSE, /* inherit handles */
+ cr_flags, /* creation flags */
+ NULL, /* environment */
+ NULL, /* use current drive/directory */
+ &si, /* startup information */
+ &pi)) /* returns process information */
+ {
+ TRACE_LOG1 ("CreateProcess failed: ec=%d", (int) GetLastError ());
+ free (arg_string);
+ close (tmp_fd);
+ DeleteFile (tmp_name);
+
+ /* FIXME: Should translate the error code. */
+ errno = EIO;
+ return TRACE_SYSRES (-1);
+ }
+
+ free (arg_string);
+
+ if (flags & IOSPAWN_FLAG_ALLOW_SET_FG)
+ _gpgme_allow_set_foreground_window ((pid_t)pi.dwProcessId);
+
+ /* Insert the inherited handles. */
+ for (i = 0; fd_list[i].fd != -1; i++)
+ {
+ HANDLE hd;
+
+ if (!DuplicateHandle (GetCurrentProcess(),
+ (HANDLE) _get_osfhandle (fd_list[i].fd),
+ pi.hProcess, &hd, 0, TRUE, DUPLICATE_SAME_ACCESS))
+ {
+ TRACE_LOG1 ("DuplicateHandle failed: ec=%d", (int) GetLastError ());
+ TerminateProcess (pi.hProcess, 0);
+ /* Just in case TerminateProcess didn't work, let the
+ process fail on its own. */
+ ResumeThread (pi.hThread);
+ CloseHandle (pi.hThread);
+ CloseHandle (pi.hProcess);
+
+ close (tmp_fd);
+ DeleteFile (tmp_name);
+
+ /* FIXME: Should translate the error code. */
+ errno = EIO;
+ return TRACE_SYSRES (-1);
+ }
+ /* Return the child name of this handle. */
+ fd_list[i].peer_name = (int) hd;
+ }
+
+ /* Write the handle translation information to the temporary
+ file. */
+ {
+ /* Hold roughly MAX_TRANS quadruplets of 64 bit numbers in hex
+ notation: "0xFEDCBA9876543210" with an extra white space after
+ every quadruplet. 10*(19*4 + 1) - 1 = 769. This plans ahead
+ for a time when a HANDLE is 64 bit. */
+#define BUFFER_MAX 800
+ char line[BUFFER_MAX + 1];
+ int res;
+ int written;
+ size_t len;
+
+ if ((flags & IOSPAWN_FLAG_ALLOW_SET_FG))
+ strcpy (line, "~1 \n");
+ else
+ strcpy (line, "\n");
+ for (i = 0; fd_list[i].fd != -1; i++)
+ {
+ /* Strip the newline. */
+ len = strlen (line) - 1;
+
+ /* Format is: Local name, stdin/stdout/stderr, peer name, argv idx. */
+ snprintf (&line[len], BUFFER_MAX - len, "0x%x %d 0x%x %d \n",
+ fd_list[i].fd, fd_list[i].dup_to,
+ fd_list[i].peer_name, fd_list[i].arg_loc);
+ /* Rather safe than sorry. */
+ line[BUFFER_MAX - 1] = '\n';
+ line[BUFFER_MAX] = '\0';
+ }
+ len = strlen (line);
+ written = 0;
+ do
+ {
+ res = write (tmp_fd, &line[written], len - written);
+ if (res > 0)
+ written += res;
+ }
+ while (res > 0 || (res < 0 && errno == EAGAIN));
+ }
+ close (tmp_fd);
+ /* The temporary file is deleted by the gpgme-w32spawn process
+ (hopefully). */
+
+ TRACE_LOG4 ("CreateProcess ready: hProcess=%p, hThread=%p, "
+ "dwProcessID=%d, dwThreadId=%d",
+ pi.hProcess, pi.hThread,
+ (int) pi.dwProcessId, (int) pi.dwThreadId);
+
+ if (r_pid)
+ *r_pid = (pid_t)pi.dwProcessId;
+
+ if (ResumeThread (pi.hThread) < 0)
+ TRACE_LOG1 ("ResumeThread failed: ec=%d", (int) GetLastError ());
+
+ if (!CloseHandle (pi.hThread))
+ TRACE_LOG1 ("CloseHandle of thread failed: ec=%d",
+ (int) GetLastError ());
+
+ TRACE_LOG1 ("process=%p", pi.hProcess);
+
+ /* We don't need to wait for the process. */
+ if (!CloseHandle (pi.hProcess))
+ TRACE_LOG1 ("CloseHandle of process failed: ec=%d",
+ (int) GetLastError ());
+
+ for (i = 0; fd_list[i].fd != -1; i++)
+ _gpgme_io_close (fd_list[i].fd);
+
+ for (i = 0; fd_list[i].fd != -1; i++)
+ if (fd_list[i].dup_to == -1)
+ TRACE_LOG3 ("fd[%i] = 0x%x -> 0x%x", i, fd_list[i].fd,
+ fd_list[i].peer_name);
+ else
+ TRACE_LOG4 ("fd[%i] = 0x%x -> 0x%x (std%s)", i, fd_list[i].fd,
+ fd_list[i].peer_name, (fd_list[i].dup_to == 0) ? "in" :
+ ((fd_list[i].dup_to == 1) ? "out" : "err"));
+
+ return TRACE_SYSRES (0);
+}
+
+
+/* Select on the list of fds. Returns: -1 = error, 0 = timeout or
+ nothing to select, > 0 = number of signaled fds. */
+int
+_gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
+{
+ /* Use a 1s timeout. */
+
+ void *dbg_help = NULL;
+ TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_select", fds,
+ "nfds=%u, nonblock=%u", nfds, nonblock);
+
+ int count = 0;
+
+ TRACE_SEQ (dbg_help, "select on [ ");
+ for (int i = 0; i < nfds; i++)
+ {
+ if (fds[i].fd == -1)
+ {
+ fds[i].signaled = 0;
+ }
+ else if (fds[i].for_read )
+ {
+ KDPipeIODevice * const chan = find_channel (fds[i].fd, 0);
+ assert (chan);
+ if ( nonblock )
+ fds[i].signaled = chan->readWouldBlock() ? 0 : 1;
+ else
+ fds[i].signaled = chan->waitForReadyRead( 1000 ) ? 1 : 0;
+ TRACE_ADD1 (dbg_help, "w0x%x ", fds[i].fd);
+ if ( fds[i].signaled )
+ count++;
+ }
+ else if (fds[i].for_write)
+ {
+ const KDPipeIODevice * const chan = find_channel (fds[i].fd, 0);
+ assert (chan);
+ fds[i].signaled = nonblock ? ( chan->writeWouldBlock() ? 0 : 1 ) : 1;
+ TRACE_ADD1 (dbg_help, "w0x%x ", fds[i].fd);
+ if ( fds[i].signaled )
+ count++;
+ }
+ }
+ TRACE_END (dbg_help, "]");
+
+ return TRACE_SYSRES (count);
+}
+
+
+/* Look up the qiodevice for file descriptor FD. */
+extern "C"
+void *
+gpgme_get_fdptr (int fd)
+{
+ return find_channel (fd, 0);
+}
+
+
+/* Obsolete compatibility interface. */
+extern "C"
+void *
+gpgme_get_giochannel (int fd)
+{
+ return NULL;
+}
+
+
+int
+_gpgme_io_dup (int fd)
+{
+ assert( iodevice_table[fd] );
+ iodevice_table[fd]->ref();
+ return fd;
+}
+
+
+extern "C"
+int
+_gpgme_io_socket (int domain, int type, int proto)
+{
+ errno = EIO;
+ return -1;
+}
+
+
+extern "C"
+int
+_gpgme_io_connect (int fd, struct sockaddr *addr, int addrlen)
+{
+ errno = EIO;
+ return -1;
+}
diff --git a/src/w32-sema.c b/src/w32-sema.c
new file mode 100644
index 0000000..504e682
--- /dev/null
+++ b/src/w32-sema.c
@@ -0,0 +1,117 @@
+/* w32-sema.c
+ Copyright (C) 2001 Werner Koch (dd9jn)
+ Copyright (C) 2001, 2002, 2004, 2007 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#include <io.h>
+
+#include "util.h"
+#include "sema.h"
+#include "debug.h"
+
+static void
+sema_fatal (const char *text)
+{
+ fprintf (stderr, "sema.c: %s\n", text);
+ abort ();
+}
+
+
+static void
+critsect_init (struct critsect_s *s)
+{
+ CRITICAL_SECTION *mp;
+ static CRITICAL_SECTION init_lock;
+ static int initialized;
+
+ if (!initialized) {
+ /* The very first time we call this function, we assume that
+ only one thread is running, so that we can bootstrap the
+ semaphore code. */
+ InitializeCriticalSection (&init_lock);
+ initialized = 1;
+ }
+ if (!s)
+ return; /* we just want to initialize ourself */
+
+ /* first test whether it is really not initialized */
+ EnterCriticalSection (&init_lock);
+ if ( s->priv ) {
+ LeaveCriticalSection (&init_lock);
+ return;
+ }
+ /* now init it */
+ mp = malloc ( sizeof *mp );
+ if (!mp) {
+ LeaveCriticalSection (&init_lock);
+ sema_fatal ("out of core while creating critical section lock");
+ }
+ InitializeCriticalSection (mp);
+ s->priv = mp;
+ LeaveCriticalSection (&init_lock);
+}
+
+void
+_gpgme_sema_subsystem_init ()
+{
+ /* fixme: we should check that there is only one thread running */
+ critsect_init (NULL);
+}
+
+
+void
+_gpgme_sema_cs_enter ( struct critsect_s *s )
+{
+ if (!s->priv)
+ critsect_init (s);
+ EnterCriticalSection ( (CRITICAL_SECTION*)s->priv );
+}
+
+void
+_gpgme_sema_cs_leave (struct critsect_s *s)
+{
+ if (!s->priv)
+ critsect_init (s);
+ LeaveCriticalSection ((CRITICAL_SECTION*)s->priv);
+}
+
+void
+_gpgme_sema_cs_destroy ( struct critsect_s *s )
+{
+ if (s && s->priv) {
+ DeleteCriticalSection ((CRITICAL_SECTION*)s->priv);
+ free (s->priv);
+ s->priv = NULL;
+ }
+}
diff --git a/src/w32-util.c b/src/w32-util.c
new file mode 100644
index 0000000..b33aa2c
--- /dev/null
+++ b/src/w32-util.c
@@ -0,0 +1,639 @@
+/* w32-util.c - Utility functions for the W32 API
+ Copyright (C) 1999 Free Software Foundation, Inc
+ Copyright (C) 2001 Werner Koch (dd9jn)
+ Copyright (C) 2001, 2002, 2003, 2004, 2007 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdint.h>
+#ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <io.h>
+
+#define _WIN32_IE 0x0400 /* Required for SHGetSpecialFolderPathA. */
+
+/* We need to include the windows stuff here prior to shlobj.h so that
+ we get the right winsock version. This is usually done in util.h
+ but that header also redefines some Windows functions which we need
+ to avoid unless having included shlobj.h. */
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <windows.h>
+#include <shlobj.h>
+
+#include "util.h"
+#include "ath.h"
+#include "sema.h"
+#include "debug.h"
+
+
+#ifndef HAVE_W32CE_SYSTEM
+#define HAVE_ALLOW_SET_FOREGROUND_WINDOW 1
+#endif
+#ifndef F_OK
+# define F_OK 0
+#endif
+
+
+DEFINE_STATIC_LOCK (get_path_lock);
+
+
+#ifdef HAVE_ALLOW_SET_FOREGROUND_WINDOW
+
+#define RTLD_LAZY 0
+
+static GPG_ERR_INLINE void *
+dlopen (const char * name, int flag)
+{
+ void * hd = LoadLibrary (name);
+ return hd;
+}
+
+static GPG_ERR_INLINE void *
+dlsym (void * hd, const char * sym)
+{
+ if (hd && sym)
+ {
+ void * fnc = GetProcAddress (hd, sym);
+ if (!fnc)
+ return NULL;
+ return fnc;
+ }
+ return NULL;
+}
+
+static GPG_ERR_INLINE int
+dlclose (void * hd)
+{
+ if (hd)
+ {
+ FreeLibrary (hd);
+ return 0;
+ }
+ return -1;
+}
+#endif /* HAVE_ALLOW_SET_FOREGROUND_WINDOW */
+
+void
+_gpgme_allow_set_foreground_window (pid_t pid)
+{
+#ifdef HAVE_ALLOW_SET_FOREGROUND_WINDOW
+ static int initialized;
+ static BOOL (WINAPI * func)(DWORD);
+ void *handle;
+
+ if (!initialized)
+ {
+ /* Available since W2000; thus we dynload it. */
+ initialized = 1;
+ handle = dlopen ("user32.dll", RTLD_LAZY);
+ if (handle)
+ {
+ func = dlsym (handle, "AllowSetForegroundWindow");
+ if (!func)
+ {
+ dlclose (handle);
+ handle = NULL;
+ }
+ }
+ }
+
+ if (!pid || pid == (pid_t)(-1))
+ {
+ TRACE1 (DEBUG_ENGINE, "gpgme:AllowSetForegroundWindow", 0,
+ "no action for pid %d", (int)pid);
+ }
+ else if (func)
+ {
+ int rc = func (pid);
+ TRACE2 (DEBUG_ENGINE, "gpgme:AllowSetForegroundWindow", 0,
+ "called for pid %d; result=%d", (int)pid, rc);
+
+ }
+ else
+ {
+ TRACE0 (DEBUG_ENGINE, "gpgme:AllowSetForegroundWindow", 0,
+ "function not available");
+ }
+#endif /* HAVE_ALLOW_SET_FOREGROUND_WINDOW */
+}
+
+
+/* Return a string from the W32 Registry or NULL in case of error.
+ Caller must release the return value. A NULL for root is an alias
+ for HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE in turn. */
+static char *
+read_w32_registry_string (const char *root, const char *dir, const char *name)
+{
+ HKEY root_key, key_handle;
+ DWORD n1, nbytes, type;
+ char *result = NULL;
+
+ if (!root)
+ root_key = HKEY_CURRENT_USER;
+ else if (!strcmp( root, "HKEY_CLASSES_ROOT"))
+ root_key = HKEY_CLASSES_ROOT;
+ else if (!strcmp( root, "HKEY_CURRENT_USER"))
+ root_key = HKEY_CURRENT_USER;
+ else if (!strcmp( root, "HKEY_LOCAL_MACHINE"))
+ root_key = HKEY_LOCAL_MACHINE;
+ else if (!strcmp( root, "HKEY_USERS"))
+ root_key = HKEY_USERS;
+ else if (!strcmp( root, "HKEY_PERFORMANCE_DATA"))
+ root_key = HKEY_PERFORMANCE_DATA;
+ else if (!strcmp( root, "HKEY_CURRENT_CONFIG"))
+ root_key = HKEY_CURRENT_CONFIG;
+ else
+ return NULL;
+
+ if (RegOpenKeyExA (root_key, dir, 0, KEY_READ, &key_handle))
+ {
+ if (root)
+ return NULL; /* no need for a RegClose, so return direct */
+ /* It seems to be common practise to fall back to HKLM. */
+ if (RegOpenKeyExA (HKEY_LOCAL_MACHINE, dir, 0, KEY_READ, &key_handle))
+ return NULL; /* still no need for a RegClose, so return direct */
+ }
+
+ nbytes = 1;
+ if (RegQueryValueExA (key_handle, name, 0, NULL, NULL, &nbytes))
+ {
+ if (root)
+ goto leave;
+ /* Try to fallback to HKLM also vor a missing value. */
+ RegCloseKey (key_handle);
+ if (RegOpenKeyExA (HKEY_LOCAL_MACHINE, dir, 0, KEY_READ, &key_handle))
+ return NULL; /* Nope. */
+ if (RegQueryValueExA (key_handle, name, 0, NULL, NULL, &nbytes))
+ goto leave;
+ }
+ n1 = nbytes + 1;
+ result = malloc (n1);
+ if (!result)
+ goto leave;
+ if (RegQueryValueExA (key_handle, name, 0, &type, (LPBYTE) result, &n1))
+ {
+ free (result);
+ result = NULL;
+ goto leave;
+ }
+ result[nbytes] = 0; /* Make sure it is really a string. */
+
+#ifndef HAVE_W32CE_SYSTEM
+ /* Windows CE does not have an environment. */
+ if (type == REG_EXPAND_SZ && strchr (result, '%'))
+ {
+ char *tmp;
+
+ n1 += 1000;
+ tmp = malloc (n1 + 1);
+ if (!tmp)
+ goto leave;
+ nbytes = ExpandEnvironmentStrings (result, tmp, n1);
+ if (nbytes && nbytes > n1)
+ {
+ free (tmp);
+ n1 = nbytes;
+ tmp = malloc (n1 + 1);
+ if (!tmp)
+ goto leave;
+ nbytes = ExpandEnvironmentStrings (result, tmp, n1);
+ if (nbytes && nbytes > n1) {
+ free (tmp); /* Oops - truncated, better don't expand at all. */
+ goto leave;
+ }
+ tmp[nbytes] = 0;
+ free (result);
+ result = tmp;
+ }
+ else if (nbytes) /* Okay, reduce the length. */
+ {
+ tmp[nbytes] = 0;
+ free (result);
+ result = malloc (strlen (tmp)+1);
+ if (!result)
+ result = tmp;
+ else
+ {
+ strcpy (result, tmp);
+ free (tmp);
+ }
+ }
+ else /* Error - don't expand. */
+ {
+ free (tmp);
+ }
+ }
+#endif
+
+ leave:
+ RegCloseKey (key_handle);
+ return result;
+}
+
+
+#if 0
+static char *
+find_program_in_registry (const char *name)
+{
+ char *program = NULL;
+
+ program = read_w32_registry_string (NULL, "Software\\GNU\\GnuPG", name);
+ if (program)
+ {
+ int i;
+
+ TRACE2 (DEBUG_CTX, "gpgme:find_program_in_registry", 0,
+ "found %s in registry: `%s'", name, program);
+ for (i = 0; program[i]; i++)
+ {
+ if (program[i] == '/')
+ program[i] = '\\';
+ }
+ }
+ return program;
+}
+#endif
+
+
+static char *
+find_program_in_inst_dir (const char *name)
+{
+ char *result = NULL;
+ char *tmp;
+
+ tmp = read_w32_registry_string ("HKEY_LOCAL_MACHINE",
+ "Software\\GNU\\GnuPG",
+ "Install Directory");
+ if (!tmp)
+ return NULL;
+
+ result = malloc (strlen (tmp) + 1 + strlen (name) + 1);
+ if (!result)
+ {
+ free (tmp);
+ return NULL;
+ }
+
+ strcpy (stpcpy (stpcpy (result, tmp), "\\"), name);
+ free (tmp);
+ if (access (result, F_OK))
+ {
+ free (result);
+ return NULL;
+ }
+
+ return result;
+}
+
+
+static char *
+find_program_at_standard_place (const char *name)
+{
+ char path[MAX_PATH];
+ char *result = NULL;
+
+ /* See http://wiki.tcl.tk/17492 for details on compatibility. */
+ if (SHGetSpecialFolderPathA (NULL, path, CSIDL_PROGRAM_FILES, 0))
+ {
+ result = malloc (strlen (path) + 1 + strlen (name) + 1);
+ if (result)
+ {
+ strcpy (stpcpy (stpcpy (result, path), "\\"), name);
+ if (access (result, F_OK))
+ {
+ free (result);
+ result = NULL;
+ }
+ }
+ }
+ return result;
+}
+
+
+const char *
+_gpgme_get_gpg_path (void)
+{
+ static char *gpg_program;
+
+ LOCK (get_path_lock);
+#if 0
+ if (!gpg_program)
+ gpg_program = find_program_in_registry ("gpgProgram");
+#endif
+ if (!gpg_program)
+ gpg_program = find_program_in_inst_dir ("gpg.exe");
+ if (!gpg_program)
+ gpg_program = find_program_at_standard_place ("GNU\\GnuPG\\gpg.exe");
+ UNLOCK (get_path_lock);
+ return gpg_program;
+}
+
+
+const char *
+_gpgme_get_gpgsm_path (void)
+{
+ static char *gpgsm_program;
+
+ LOCK (get_path_lock);
+#if 0
+ if (!gpgsm_program)
+ gpgsm_program = find_program_in_registry ("gpgsmProgram");
+#endif
+ if (!gpgsm_program)
+ gpgsm_program = find_program_in_inst_dir ("gpgsm.exe");
+ if (!gpgsm_program)
+ gpgsm_program = find_program_at_standard_place ("GNU\\GnuPG\\gpgsm.exe");
+ UNLOCK (get_path_lock);
+ return gpgsm_program;
+}
+
+
+const char *
+_gpgme_get_gpgconf_path (void)
+{
+ static char *gpgconf_program;
+
+ LOCK (get_path_lock);
+#if 0
+ if (!gpgconf_program)
+ gpgconf_program = find_program_in_registry ("gpgconfProgram");
+#endif
+ if (!gpgconf_program)
+ gpgconf_program = find_program_in_inst_dir ("gpgconf.exe");
+ if (!gpgconf_program)
+ gpgconf_program
+ = find_program_at_standard_place ("GNU\\GnuPG\\gpgconf.exe");
+ UNLOCK (get_path_lock);
+ return gpgconf_program;
+}
+
+
+const char *
+_gpgme_get_g13_path (void)
+{
+ static char *g13_program;
+
+ LOCK (get_path_lock);
+#if 0
+ if (!g13_program)
+ g13_program = find_program_in_registry ("g13Program");
+#endif
+ if (!g13_program)
+ g13_program = find_program_in_inst_dir ("g13.exe");
+ if (!g13_program)
+ g13_program = find_program_at_standard_place ("GNU\\GnuPG\\g13.exe");
+ UNLOCK (get_path_lock);
+ return g13_program;
+}
+
+
+const char *
+_gpgme_get_uiserver_socket_path (void)
+{
+ static char *socket_path;
+ const char *homedir;
+ const char name[] = "S.uiserver";
+
+ if (socket_path)
+ return socket_path;
+
+ homedir = _gpgme_get_default_homedir ();
+ if (! homedir)
+ return NULL;
+
+ socket_path = malloc (strlen (homedir) + 1 + strlen (name) + 1);
+ if (! socket_path)
+ return NULL;
+
+ strcpy (stpcpy (stpcpy (socket_path, homedir), "\\"), name);
+ return socket_path;
+}
+
+
+const char *
+_gpgme_get_w32spawn_path (void)
+{
+ static char *w32spawn_program;
+
+ LOCK (get_path_lock);
+ if (!w32spawn_program)
+ w32spawn_program = find_program_in_inst_dir ("gpgme-w32spawn.exe");
+ if (!w32spawn_program)
+ w32spawn_program
+ = find_program_at_standard_place ("GNU\\GnuPG\\gpgme-w32spawn.exe");
+ UNLOCK (get_path_lock);
+ return w32spawn_program;
+}
+
+
+/* Return an integer value from gpgme specific configuration
+ entries. VALUE receives that value; function returns true if a value
+ has been configured and false if not. */
+int
+_gpgme_get_conf_int (const char *key, int *value)
+{
+ char *tmp = read_w32_registry_string (NULL, "Software\\GNU\\gpgme", key);
+ if (!tmp)
+ return 0;
+ *value = atoi (tmp);
+ free (tmp);
+ return 1;
+}
+
+
+#ifdef HAVE_W32CE_SYSTEM
+int
+_gpgme_mkstemp (int *fd, char **name)
+{
+ return -1;
+}
+#else
+
+/* mkstemp extracted from libc/sysdeps/posix/tempname.c. Copyright
+ (C) 1991-1999, 2000, 2001, 2006 Free Software Foundation, Inc.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version. */
+
+static const char letters[] =
+"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+
+/* Generate a temporary file name based on TMPL. TMPL must match the
+ rules for mk[s]temp (i.e. end in "XXXXXX"). The name constructed
+ does not exist at the time of the call to mkstemp. TMPL is
+ overwritten with the result. */
+static int
+mkstemp (char *tmpl)
+{
+ int len;
+ char *XXXXXX;
+ static uint64_t value;
+ uint64_t random_time_bits;
+ unsigned int count;
+ int fd = -1;
+ int save_errno = errno;
+
+ /* A lower bound on the number of temporary files to attempt to
+ generate. The maximum total number of temporary file names that
+ can exist for a given template is 62**6. It should never be
+ necessary to try all these combinations. Instead if a reasonable
+ number of names is tried (we define reasonable as 62**3) fail to
+ give the system administrator the chance to remove the problems. */
+#define ATTEMPTS_MIN (62 * 62 * 62)
+
+ /* The number of times to attempt to generate a temporary file. To
+ conform to POSIX, this must be no smaller than TMP_MAX. */
+#if ATTEMPTS_MIN < TMP_MAX
+ unsigned int attempts = TMP_MAX;
+#else
+ unsigned int attempts = ATTEMPTS_MIN;
+#endif
+
+ len = strlen (tmpl);
+ if (len < 6 || strcmp (&tmpl[len - 6], "XXXXXX"))
+ {
+ gpg_err_set_errno (EINVAL);
+ return -1;
+ }
+
+ /* This is where the Xs start. */
+ XXXXXX = &tmpl[len - 6];
+
+ /* Get some more or less random data. */
+ {
+ FILETIME ft;
+
+ GetSystemTimeAsFileTime (&ft);
+ random_time_bits = (((uint64_t)ft.dwHighDateTime << 32)
+ | (uint64_t)ft.dwLowDateTime);
+ }
+ value += random_time_bits ^ ath_self ();
+
+ for (count = 0; count < attempts; value += 7777, ++count)
+ {
+ uint64_t v = value;
+
+ /* Fill in the random bits. */
+ XXXXXX[0] = letters[v % 62];
+ v /= 62;
+ XXXXXX[1] = letters[v % 62];
+ v /= 62;
+ XXXXXX[2] = letters[v % 62];
+ v /= 62;
+ XXXXXX[3] = letters[v % 62];
+ v /= 62;
+ XXXXXX[4] = letters[v % 62];
+ v /= 62;
+ XXXXXX[5] = letters[v % 62];
+
+ fd = open (tmpl, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
+ if (fd >= 0)
+ {
+ gpg_err_set_errno (save_errno);
+ return fd;
+ }
+ else if (errno != EEXIST)
+ return -1;
+ }
+
+ /* We got out of the loop because we ran out of combinations to try. */
+ gpg_err_set_errno (EEXIST);
+ return -1;
+}
+
+
+int
+_gpgme_mkstemp (int *fd, char **name)
+{
+ char tmp[MAX_PATH + 2];
+ char *tmpname;
+ int err;
+
+ *fd = -1;
+ *name = NULL;
+
+ err = GetTempPathA (MAX_PATH + 1, tmp);
+ if (err == 0 || err > MAX_PATH + 1)
+ strcpy (tmp,"c:\\windows\\temp");
+ else
+ {
+ int len = strlen(tmp);
+
+ /* GetTempPath may return with \ on the end */
+ while(len > 0 && tmp[len - 1] == '\\')
+ {
+ tmp[len-1] = '\0';
+ len--;
+ }
+ }
+
+ tmpname = malloc (strlen (tmp) + 13 + 1);
+ if (!tmpname)
+ return -1;
+ strcpy (stpcpy (tmpname, tmp), "\\gpgme-XXXXXX");
+ *fd = mkstemp (tmpname);
+ if (fd < 0)
+ return -1;
+
+ *name = tmpname;
+ return 0;
+}
+#endif
+
+
+
+#ifdef HAVE_W32CE_SYSTEM
+/* Return a malloced string with the replacement value for the
+ GPGME_DEBUG envvar. Caller must release. Returns NULL if not
+ set. */
+char *
+_gpgme_w32ce_get_debug_envvar (void)
+{
+ char *tmp;
+
+ tmp = read_w32_registry_string (NULL, "\\Software\\GNU\\gpgme", "debug");
+ if (tmp && !*tmp)
+ {
+ free (tmp);
+ tmp = NULL;
+ }
+ return tmp;
+}
+#endif /*HAVE_W32CE_SYSTEM*/
diff --git a/src/wait-global.c b/src/wait-global.c
new file mode 100644
index 0000000..77fd47f
--- /dev/null
+++ b/src/wait-global.c
@@ -0,0 +1,400 @@
+/* wait-global.c
+ Copyright (C) 2000 Werner Koch (dd9jn)
+ Copyright (C) 2001, 2002, 2003, 2004, 2005 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+
+#include "gpgme.h"
+#include "sema.h"
+#include "util.h"
+#include "context.h"
+#include "wait.h"
+#include "priv-io.h"
+#include "ops.h"
+
+/* The global event loop is used for all asynchronous operations
+ (except key listing) for which no user I/O callbacks are specified.
+
+ A context sets up its initial I/O callbacks and then sends the
+ GPGME_EVENT_START event. After that, it is added to the global
+ list of active contexts.
+
+ The gpgme_wait function contains a select() loop over all file
+ descriptors in all active contexts. If an error occurs, it closes
+ all fds in that context and moves the context to the global done
+ list. Likewise, if a context has removed all I/O callbacks, it is
+ moved to the global done list.
+
+ All contexts in the global done list are eligible for being
+ returned by gpgme_wait if requested by the caller. */
+
+/* The ctx_list_lock protects the list of active and done contexts.
+ Insertion into any of these lists is only allowed when the lock is
+ held. This allows a muli-threaded program to loop over gpgme_wait
+ and in parallel start asynchronous gpgme operations.
+
+ However, the fd tables in the contexts are not protected by this
+ lock. They are only allowed to change either before the context is
+ added to the active list (ie, before the start event is signalled)
+ or in a callback handler. */
+DEFINE_STATIC_LOCK (ctx_list_lock);
+
+/* A ctx_list_item is an item in the global list of active or done
+ contexts. */
+struct ctx_list_item
+{
+ /* Every ctx_list_item is an element in a doubly linked list. The
+ list pointers are protected by the ctx_list_lock. */
+ struct ctx_list_item *next;
+ struct ctx_list_item *prev;
+
+ gpgme_ctx_t ctx;
+ /* The status is set when the ctx is moved to the done list. */
+ gpgme_error_t status;
+ gpgme_error_t op_err;
+};
+
+/* The active list contains all contexts that are in the global event
+ loop, have active I/O callbacks, and have already seen the start
+ event. */
+static struct ctx_list_item *ctx_active_list;
+
+/* The done list contains all contexts that have previously been
+ active but now are not active any longer, either because they
+ finished successfully or an I/O callback returned an error. The
+ status field in the list item contains the error value (or 0 if
+ successful). */
+static struct ctx_list_item *ctx_done_list;
+
+
+/* Enter the context CTX into the active list. */
+static gpgme_error_t
+ctx_active (gpgme_ctx_t ctx)
+{
+ struct ctx_list_item *li = malloc (sizeof (struct ctx_list_item));
+ if (!li)
+ return gpg_error_from_errno (errno);
+ li->ctx = ctx;
+
+ LOCK (ctx_list_lock);
+ /* Add LI to active list. */
+ li->next = ctx_active_list;
+ li->prev = NULL;
+ if (ctx_active_list)
+ ctx_active_list->prev = li;
+ ctx_active_list = li;
+ UNLOCK (ctx_list_lock);
+ return 0;
+}
+
+
+/* Enter the context CTX into the done list with status STATUS. */
+static void
+ctx_done (gpgme_ctx_t ctx, gpgme_error_t status, gpgme_error_t op_err)
+{
+ struct ctx_list_item *li;
+
+ LOCK (ctx_list_lock);
+ li = ctx_active_list;
+ while (li && li->ctx != ctx)
+ li = li->next;
+ assert (li);
+
+ /* Remove LI from active list. */
+ if (li->next)
+ li->next->prev = li->prev;
+ if (li->prev)
+ li->prev->next = li->next;
+ else
+ ctx_active_list = li->next;
+
+ li->status = status;
+ li->op_err = op_err;
+
+ /* Add LI to done list. */
+ li->next = ctx_done_list;
+ li->prev = NULL;
+ if (ctx_done_list)
+ ctx_done_list->prev = li;
+ ctx_done_list = li;
+ UNLOCK (ctx_list_lock);
+}
+
+
+/* Find finished context CTX (or any context if CTX is NULL) and
+ return its status in STATUS after removing it from the done list.
+ If a matching context could be found, return it. Return NULL if no
+ context could be found. */
+static gpgme_ctx_t
+ctx_wait (gpgme_ctx_t ctx, gpgme_error_t *status, gpgme_error_t *op_err)
+{
+ struct ctx_list_item *li;
+
+ LOCK (ctx_list_lock);
+ li = ctx_done_list;
+ if (ctx)
+ {
+ /* A specific context is requested. */
+ while (li && li->ctx != ctx)
+ li = li->next;
+ }
+ if (li)
+ {
+ ctx = li->ctx;
+ if (status)
+ *status = li->status;
+ if (op_err)
+ *op_err = li->op_err;
+
+ /* Remove LI from done list. */
+ if (li->next)
+ li->next->prev = li->prev;
+ if (li->prev)
+ li->prev->next = li->next;
+ else
+ ctx_done_list = li->next;
+ free (li);
+ }
+ else
+ ctx = NULL;
+ UNLOCK (ctx_list_lock);
+ return ctx;
+}
+
+
+/* Internal I/O callback functions. */
+
+/* The add_io_cb and remove_io_cb handlers are shared with the private
+ event loops. */
+
+void
+_gpgme_wait_global_event_cb (void *data, gpgme_event_io_t type,
+ void *type_data)
+{
+ gpgme_ctx_t ctx = (gpgme_ctx_t) data;
+
+ assert (ctx);
+
+ switch (type)
+ {
+ case GPGME_EVENT_START:
+ {
+ gpgme_error_t err = ctx_active (ctx);
+
+ if (err)
+ /* An error occured. Close all fds in this context, and
+ send the error in a done event. */
+ _gpgme_cancel_with_err (ctx, err, 0);
+ }
+ break;
+
+ case GPGME_EVENT_DONE:
+ {
+ gpgme_io_event_done_data_t done_data =
+ (gpgme_io_event_done_data_t) type_data;
+
+ ctx_done (ctx, done_data->err, done_data->op_err);
+ }
+ break;
+
+ case GPGME_EVENT_NEXT_KEY:
+ assert (!"Unexpected event GPGME_EVENT_NEXT_KEY");
+ break;
+
+ case GPGME_EVENT_NEXT_TRUSTITEM:
+ assert (!"Unexpected event GPGME_EVENT_NEXT_TRUSTITEM");
+ break;
+
+ default:
+ assert (!"Unexpected event");
+ break;
+ }
+}
+
+
+
+/* Perform asynchronous operations in the global event loop (ie, any
+ asynchronous operation except key listing and trustitem listing
+ operations). If CTX is not a null pointer, the function will
+ return if the asynchronous operation in the context CTX finished.
+ Otherwise the function will return if any asynchronous operation
+ finished. If HANG is zero, the function will not block for a long
+ time. Otherwise the function does not return until an operation
+ matching CTX finished.
+
+ If a matching context finished, it is returned, and *STATUS is set
+ to the error value of the operation in that context. Otherwise, if
+ the timeout expires, NULL is returned and *STATUS is 0. If an
+ error occurs, NULL is returned and *STATUS is set to the error
+ value. */
+gpgme_ctx_t
+gpgme_wait_ext (gpgme_ctx_t ctx, gpgme_error_t *status,
+ gpgme_error_t *op_err, int hang)
+{
+ do
+ {
+ unsigned int i = 0;
+ struct ctx_list_item *li;
+ struct fd_table fdt;
+ int nr;
+
+ /* Collect the active file descriptors. */
+ LOCK (ctx_list_lock);
+ for (li = ctx_active_list; li; li = li->next)
+ i += li->ctx->fdt.size;
+ fdt.fds = malloc (i * sizeof (struct io_select_fd_s));
+ if (!fdt.fds)
+ {
+ int saved_errno = errno;
+ UNLOCK (ctx_list_lock);
+ if (status)
+ *status = gpg_error_from_errno (saved_errno);
+ if (op_err)
+ *op_err = 0;
+ return NULL;
+ }
+ fdt.size = i;
+ i = 0;
+ for (li = ctx_active_list; li; li = li->next)
+ {
+ memcpy (&fdt.fds[i], li->ctx->fdt.fds,
+ li->ctx->fdt.size * sizeof (struct io_select_fd_s));
+ i += li->ctx->fdt.size;
+ }
+ UNLOCK (ctx_list_lock);
+
+ nr = _gpgme_io_select (fdt.fds, fdt.size, 0);
+ if (nr < 0)
+ {
+ int saved_errno = errno;
+ free (fdt.fds);
+ if (status)
+ *status = gpg_error_from_errno (saved_errno);
+ if (op_err)
+ *op_err = 0;
+ return NULL;
+ }
+
+ for (i = 0; i < fdt.size && nr; i++)
+ {
+ if (fdt.fds[i].fd != -1 && fdt.fds[i].signaled)
+ {
+ gpgme_ctx_t ictx;
+ gpgme_error_t err = 0;
+ gpgme_error_t local_op_err = 0;
+ struct wait_item_s *item;
+
+ assert (nr);
+ nr--;
+
+ item = (struct wait_item_s *) fdt.fds[i].opaque;
+ assert (item);
+ ictx = item->ctx;
+ assert (ictx);
+
+ LOCK (ctx->lock);
+ if (ctx->canceled)
+ err = gpg_error (GPG_ERR_CANCELED);
+ UNLOCK (ctx->lock);
+
+ if (!err)
+ err = _gpgme_run_io_cb (&fdt.fds[i], 0, &local_op_err);
+ if (err || local_op_err)
+ {
+ /* An error occured. Close all fds in this context,
+ and signal it. */
+ _gpgme_cancel_with_err (ictx, err, local_op_err);
+
+ /* Break out of the loop, and retry the select()
+ from scratch, because now all fds should be
+ gone. */
+ break;
+ }
+ }
+ }
+ free (fdt.fds);
+
+ /* Now some contexts might have finished successfully. */
+ LOCK (ctx_list_lock);
+ retry:
+ for (li = ctx_active_list; li; li = li->next)
+ {
+ gpgme_ctx_t actx = li->ctx;
+
+ for (i = 0; i < actx->fdt.size; i++)
+ if (actx->fdt.fds[i].fd != -1)
+ break;
+ if (i == actx->fdt.size)
+ {
+ struct gpgme_io_event_done_data data;
+ data.err = 0;
+ data.op_err = 0;
+
+ /* FIXME: This does not perform too well. We have to
+ release the lock because the I/O event handler
+ acquires it to remove the context from the active
+ list. Two alternative strategies are worth
+ considering: Either implement the DONE event handler
+ here in a lock-free manner, or save a list of all
+ contexts to be released and call the DONE events
+ afterwards. */
+ UNLOCK (ctx_list_lock);
+ _gpgme_engine_io_event (actx->engine, GPGME_EVENT_DONE, &data);
+ LOCK (ctx_list_lock);
+ goto retry;
+ }
+ }
+ UNLOCK (ctx_list_lock);
+
+ {
+ gpgme_ctx_t dctx = ctx_wait (ctx, status, op_err);
+
+ if (dctx)
+ {
+ ctx = dctx;
+ hang = 0;
+ }
+ else if (!hang)
+ {
+ ctx = NULL;
+ if (status)
+ *status = 0;
+ if (op_err)
+ *op_err = 0;
+ }
+ }
+ }
+ while (hang);
+
+ return ctx;
+}
+
+
+gpgme_ctx_t
+gpgme_wait (gpgme_ctx_t ctx, gpgme_error_t *status, int hang)
+{
+ return gpgme_wait_ext (ctx, status, NULL, hang);
+}
diff --git a/src/wait-private.c b/src/wait-private.c
new file mode 100644
index 0000000..c5cbd3c
--- /dev/null
+++ b/src/wait-private.c
@@ -0,0 +1,179 @@
+/* wait-private.c
+ Copyright (C) 2000 Werner Koch (dd9jn)
+ Copyright (C) 2001, 2002, 2003, 2004, 2005 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <assert.h>
+#include <errno.h>
+
+#include "gpgme.h"
+#include "context.h"
+#include "wait.h"
+#include "ops.h"
+#include "priv-io.h"
+#include "util.h"
+
+
+/* The private event loops are used for all blocking operations, and
+ for the key and trust item listing operations. They are completely
+ separated from each other. */
+
+
+/* Internal I/O callback functions. */
+
+/* The add_io_cb and remove_io_cb handlers are shared with the global
+ event loops. */
+
+void
+_gpgme_wait_private_event_cb (void *data, gpgme_event_io_t type,
+ void *type_data)
+{
+ switch (type)
+ {
+ case GPGME_EVENT_START:
+ /* Nothing to do here, as the wait routine is called after the
+ initialization is finished. */
+ break;
+
+ case GPGME_EVENT_DONE:
+ break;
+
+ case GPGME_EVENT_NEXT_KEY:
+ _gpgme_op_keylist_event_cb (data, type, type_data);
+ break;
+
+ case GPGME_EVENT_NEXT_TRUSTITEM:
+ _gpgme_op_trustlist_event_cb (data, type, type_data);
+ break;
+ }
+}
+
+
+/* If COND is a null pointer, wait until the blocking operation in CTX
+ finished and return its error value. Otherwise, wait until COND is
+ satisfied or the operation finished. */
+gpgme_error_t
+_gpgme_wait_on_condition (gpgme_ctx_t ctx, volatile int *cond,
+ gpgme_error_t *op_err_p)
+{
+ gpgme_error_t err = 0;
+ int hang = 1;
+
+ if (op_err_p)
+ *op_err_p = 0;
+
+ do
+ {
+ int nr = _gpgme_io_select (ctx->fdt.fds, ctx->fdt.size, 0);
+ unsigned int i;
+
+ if (nr < 0)
+ {
+ /* An error occured. Close all fds in this context, and
+ signal it. */
+ err = gpg_error_from_errno (errno);
+ _gpgme_cancel_with_err (ctx, err, 0);
+
+ return err;
+ }
+
+ for (i = 0; i < ctx->fdt.size && nr; i++)
+ {
+ if (ctx->fdt.fds[i].fd != -1 && ctx->fdt.fds[i].signaled)
+ {
+ gpgme_error_t op_err = 0;
+
+ ctx->fdt.fds[i].signaled = 0;
+ assert (nr);
+ nr--;
+
+ LOCK (ctx->lock);
+ if (ctx->canceled)
+ err = gpg_error (GPG_ERR_CANCELED);
+ UNLOCK (ctx->lock);
+
+ if (!err)
+ err = _gpgme_run_io_cb (&ctx->fdt.fds[i], 0, &op_err);
+ if (err)
+ {
+ /* An error occured. Close all fds in this context,
+ and signal it. */
+ _gpgme_cancel_with_err (ctx, err, 0);
+
+ return err;
+ }
+ else if (op_err)
+ {
+ /* An operational error occured. Cancel the current
+ operation but not the session, and signal it. */
+ _gpgme_cancel_with_err (ctx, 0, op_err);
+
+ /* NOTE: This relies on the operational error being
+ generated after the operation really has
+ completed, for example after no further status
+ line output is generated. Otherwise the
+ following I/O will spill over into the next
+ operation. */
+ if (op_err_p)
+ *op_err_p = op_err;
+ return 0;
+ }
+ }
+ }
+
+ for (i = 0; i < ctx->fdt.size; i++)
+ if (ctx->fdt.fds[i].fd != -1)
+ break;
+ if (i == ctx->fdt.size)
+ {
+ struct gpgme_io_event_done_data data;
+ data.err = 0;
+ data.op_err = 0;
+ _gpgme_engine_io_event (ctx->engine, GPGME_EVENT_DONE, &data);
+ hang = 0;
+ }
+ if (cond && *cond)
+ hang = 0;
+ }
+ while (hang);
+
+ return 0;
+}
+
+
+/* Wait until the blocking operation in context CTX has finished and
+ return the error value. This variant can not be used for
+ session-based protocols. */
+gpgme_error_t
+_gpgme_wait_one (gpgme_ctx_t ctx)
+{
+ return _gpgme_wait_on_condition (ctx, NULL, NULL);
+}
+
+/* Wait until the blocking operation in context CTX has finished and
+ return the error value. This is the right variant to use for
+ sesion-based protocols. */
+gpgme_error_t
+_gpgme_wait_one_ext (gpgme_ctx_t ctx, gpgme_error_t *op_err)
+{
+ return _gpgme_wait_on_condition (ctx, NULL, op_err);
+}
diff --git a/src/wait-user.c b/src/wait-user.c
new file mode 100644
index 0000000..63dccfa
--- /dev/null
+++ b/src/wait-user.c
@@ -0,0 +1,130 @@
+/* wait-user.c
+ Copyright (C) 2000 Werner Koch (dd9jn)
+ Copyright (C) 2001, 2002, 2003, 2004, 2005 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <assert.h>
+
+#include "gpgme.h"
+#include "context.h"
+#include "priv-io.h"
+#include "wait.h"
+#include "ops.h"
+
+
+/* The user event loops are used for all asynchronous operations for
+ which a user callback is defined. */
+
+
+/* Internal I/O Callbacks. */
+
+gpgme_error_t
+_gpgme_user_io_cb_handler (void *data, int fd)
+{
+ gpgme_error_t err = 0;
+ gpgme_error_t op_err = 0;
+ struct tag *tag = (struct tag *) data;
+ gpgme_ctx_t ctx;
+
+ assert (data);
+ ctx = tag->ctx;
+ assert (ctx);
+
+ LOCK (ctx->lock);
+ if (ctx->canceled)
+ err = gpg_error (GPG_ERR_CANCELED);
+ UNLOCK (ctx->lock);
+
+ if (! err)
+ err = _gpgme_run_io_cb (&ctx->fdt.fds[tag->idx], 0, &op_err);
+ if (err || op_err)
+ _gpgme_cancel_with_err (ctx, err, op_err);
+ else
+ {
+ unsigned int i;
+
+ for (i = 0; i < ctx->fdt.size; i++)
+ if (ctx->fdt.fds[i].fd != -1)
+ break;
+
+ if (i == ctx->fdt.size)
+ {
+ struct gpgme_io_event_done_data done_data;
+
+ done_data.err = 0;
+ done_data.op_err = 0;
+ _gpgme_engine_io_event (ctx->engine, GPGME_EVENT_DONE, &done_data);
+ }
+ }
+ return 0;
+}
+
+
+/* Register the file descriptor FD with the handler FNC (which gets
+ FNC_DATA as its first argument) for the direction DIR. DATA should
+ be the context for which the fd is added. R_TAG will hold the tag
+ that can be used to remove the fd. */
+gpgme_error_t
+_gpgme_wait_user_add_io_cb (void *data, int fd, int dir, gpgme_io_cb_t fnc,
+ void *fnc_data, void **r_tag)
+{
+ gpgme_ctx_t ctx = (gpgme_ctx_t) data;
+ struct tag *tag;
+ gpgme_error_t err;
+
+ assert (ctx);
+ err = _gpgme_add_io_cb (data, fd, dir, fnc, fnc_data, r_tag);
+ if (err)
+ return err;
+ tag = *r_tag;
+ assert (tag);
+ err = (*ctx->io_cbs.add) (ctx->io_cbs.add_priv, fd, dir,
+ _gpgme_user_io_cb_handler, *r_tag,
+ &tag->user_tag);
+ if (err)
+ _gpgme_remove_io_cb (*r_tag);
+ return err;
+}
+
+
+void
+_gpgme_wait_user_remove_io_cb (void *data)
+{
+ struct tag *tag = (struct tag *) data;
+ gpgme_ctx_t ctx;
+
+ assert (tag);
+ ctx = tag->ctx;
+
+ (*ctx->io_cbs.remove) (tag->user_tag);
+ _gpgme_remove_io_cb (data);
+}
+
+
+void
+_gpgme_wait_user_event_cb (void *data, gpgme_event_io_t type, void *type_data)
+{
+ gpgme_ctx_t ctx = data;
+
+ if (ctx->io_cbs.event)
+ (*ctx->io_cbs.event) (ctx->io_cbs.event_priv, type, type_data);
+}
diff --git a/src/wait.c b/src/wait.c
new file mode 100644
index 0000000..a552b54
--- /dev/null
+++ b/src/wait.c
@@ -0,0 +1,223 @@
+/* wait.c
+ Copyright (C) 2000 Werner Koch (dd9jn)
+ Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+#include "util.h"
+#include "context.h"
+#include "ops.h"
+#include "wait.h"
+#include "sema.h"
+#include "priv-io.h"
+#include "engine.h"
+#include "debug.h"
+
+
+void
+_gpgme_fd_table_init (fd_table_t fdt)
+{
+ fdt->fds = NULL;
+ fdt->size = 0;
+}
+
+void
+_gpgme_fd_table_deinit (fd_table_t fdt)
+{
+ if (fdt->fds)
+ free (fdt->fds);
+}
+
+
+/* XXX We should keep a marker and roll over for speed. */
+static gpgme_error_t
+fd_table_put (fd_table_t fdt, int fd, int dir, void *opaque, int *idx)
+{
+ unsigned int i, j;
+ struct io_select_fd_s *new_fds;
+
+ for (i = 0; i < fdt->size; i++)
+ {
+ if (fdt->fds[i].fd == -1)
+ break;
+ }
+ if (i == fdt->size)
+ {
+#define FDT_ALLOCSIZE 10
+ new_fds = realloc (fdt->fds, (fdt->size + FDT_ALLOCSIZE)
+ * sizeof (*new_fds));
+ if (!new_fds)
+ return gpg_error_from_errno (errno);
+
+ fdt->fds = new_fds;
+ fdt->size += FDT_ALLOCSIZE;
+ for (j = 0; j < FDT_ALLOCSIZE; j++)
+ fdt->fds[i + j].fd = -1;
+ }
+
+ fdt->fds[i].fd = fd;
+ fdt->fds[i].for_read = (dir == 1);
+ fdt->fds[i].for_write = (dir == 0);
+ fdt->fds[i].signaled = 0;
+ fdt->fds[i].opaque = opaque;
+ *idx = i;
+ return 0;
+}
+
+
+/* Register the file descriptor FD with the handler FNC (which gets
+ FNC_DATA as its first argument) for the direction DIR. DATA should
+ be the context for which the fd is added. R_TAG will hold the tag
+ that can be used to remove the fd. */
+gpgme_error_t
+_gpgme_add_io_cb (void *data, int fd, int dir, gpgme_io_cb_t fnc,
+ void *fnc_data, void **r_tag)
+{
+ gpgme_error_t err;
+ gpgme_ctx_t ctx = (gpgme_ctx_t) data;
+ fd_table_t fdt;
+ struct wait_item_s *item;
+ struct tag *tag;
+
+ assert (fnc);
+ assert (ctx);
+
+ fdt = &ctx->fdt;
+ assert (fdt);
+
+ tag = malloc (sizeof *tag);
+ if (!tag)
+ return gpg_error_from_errno (errno);
+ tag->ctx = ctx;
+
+ /* Allocate a structure to hold information about the handler. */
+ item = calloc (1, sizeof *item);
+ if (!item)
+ {
+ int saved_errno = errno;
+ free (tag);
+ return gpg_error_from_errno (saved_errno);
+ }
+ item->ctx = ctx;
+ item->dir = dir;
+ item->handler = fnc;
+ item->handler_value = fnc_data;
+
+ err = fd_table_put (fdt, fd, dir, item, &tag->idx);
+ if (err)
+ {
+ free (tag);
+ free (item);
+ return err;
+ }
+
+ TRACE3 (DEBUG_CTX, "_gpgme_add_io_cb", ctx,
+ "fd %d, dir=%d -> tag=%p", fd, dir, tag);
+
+ *r_tag = tag;
+ return 0;
+}
+
+
+void
+_gpgme_remove_io_cb (void *data)
+{
+ struct tag *tag = data;
+ gpgme_ctx_t ctx;
+ fd_table_t fdt;
+ int idx;
+
+ assert (tag);
+ ctx = tag->ctx;
+ assert (ctx);
+ fdt = &ctx->fdt;
+ assert (fdt);
+ idx = tag->idx;
+
+ TRACE2 (DEBUG_CTX, "_gpgme_remove_io_cb", data,
+ "setting fd 0x%x (item=%p) done", fdt->fds[idx].fd,
+ fdt->fds[idx].opaque);
+
+ free (fdt->fds[idx].opaque);
+ free (tag);
+
+ /* Free the table entry. */
+ fdt->fds[idx].fd = -1;
+ fdt->fds[idx].for_read = 0;
+ fdt->fds[idx].for_write = 0;
+ fdt->fds[idx].opaque = NULL;
+}
+
+
+/* This is slightly embarrassing. The problem is that running an I/O
+ callback _may_ influence the status of other file descriptors. Our
+ own event loops could compensate for that, but the external event
+ loops cannot. FIXME: We may still want to optimize this a bit when
+ we are called from our own event loops. So if CHECKED is 1, the
+ check is skipped. */
+gpgme_error_t
+_gpgme_run_io_cb (struct io_select_fd_s *an_fds, int checked,
+ gpgme_error_t *op_err)
+{
+ struct wait_item_s *item;
+ struct io_cb_data iocb_data;
+ gpgme_error_t err;
+
+ item = (struct wait_item_s *) an_fds->opaque;
+ assert (item);
+
+ if (!checked)
+ {
+ int nr;
+ struct io_select_fd_s fds;
+
+ TRACE0 (DEBUG_CTX, "_gpgme_run_io_cb", item, "need to check");
+ fds = *an_fds;
+ fds.signaled = 0;
+ /* Just give it a quick poll. */
+ nr = _gpgme_io_select (&fds, 1, 1);
+ assert (nr <= 1);
+ if (nr < 0)
+ return errno;
+ else if (nr == 0)
+ /* The status changed in the meantime, there is nothing left
+ to do. */
+ return 0;
+ }
+
+ TRACE2 (DEBUG_CTX, "_gpgme_run_io_cb", item, "handler (%p, %d)",
+ item->handler_value, an_fds->fd);
+
+ iocb_data.handler_value = item->handler_value;
+ iocb_data.op_err = 0;
+ err = item->handler (&iocb_data, an_fds->fd);
+
+ *op_err = iocb_data.op_err;
+ return err;
+}
diff --git a/src/wait.h b/src/wait.h
new file mode 100644
index 0000000..6ea51e2
--- /dev/null
+++ b/src/wait.h
@@ -0,0 +1,96 @@
+/* wait.h - Definitions for the wait queue interface.
+ Copyright (C) 2000 Werner Koch (dd9jn)
+ Copyright (C) 2001, 2002, 2003, 2004 g10 Code GmbH
+
+ This file is part of GPGME.
+
+ GPGME is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ GPGME is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
+
+#ifndef WAIT_H
+#define WAIT_H
+
+#include "gpgme.h"
+#include "sema.h"
+
+struct fd_table
+{
+ struct io_select_fd_s *fds;
+ size_t size;
+};
+typedef struct fd_table *fd_table_t;
+
+/* Wait items are hooked into the io_select_fd_s to connect an fd with
+ a callback handler. */
+struct wait_item_s
+{
+ gpgme_ctx_t ctx;
+ gpgme_io_cb_t handler;
+ void *handler_value;
+ int dir;
+};
+
+/* A registered fd handler is removed later using the tag that
+ identifies it. */
+struct tag
+{
+ /* The context for which the fd was registered. */
+ gpgme_ctx_t ctx;
+
+ /* The index into the fd table for this context. */
+ int idx;
+
+ /* This is used by the wrappers for the user event loop. */
+ void *user_tag;
+};
+
+
+void _gpgme_fd_table_init (fd_table_t fdt);
+void _gpgme_fd_table_deinit (fd_table_t fdt);
+
+gpgme_error_t _gpgme_add_io_cb (void *data, int fd, int dir,
+ gpgme_io_cb_t fnc, void *fnc_data, void **r_tag);
+void _gpgme_remove_io_cb (void *tag);
+void _gpgme_wait_private_event_cb (void *data, gpgme_event_io_t type,
+ void *type_data);
+void _gpgme_wait_global_event_cb (void *data, gpgme_event_io_t type,
+ void *type_data);
+
+gpgme_error_t _gpgme_wait_user_add_io_cb (void *data, int fd, int dir,
+ gpgme_io_cb_t fnc, void *fnc_data,
+ void **r_tag);
+void _gpgme_wait_user_remove_io_cb (void *tag);
+void _gpgme_wait_user_event_cb (void *data, gpgme_event_io_t type,
+ void *type_data);
+
+gpgme_error_t _gpgme_run_io_cb (struct io_select_fd_s *an_fds, int checked,
+ gpgme_error_t *err);
+
+
+/* Session based interfaces require to make a distinction between IPC
+ errors and operational errors. To glue this into the old
+ interface, I/O handlers (esp. the status handler) are called with a
+ struct as the opaque value that contains the handlers opaque value
+ but also a field for the operational error to be returned. */
+struct io_cb_data
+{
+ /* If this is the first field, the old internal code will still work. */
+ void *handler_value;
+
+ /* The I/O callback can pass an operational error here. */
+ gpgme_error_t op_err;
+};
+
+#endif /* WAIT_H */