From 9d3fdd8c96b79ecbd56cf6b1a424bb2e2cd5236d Mon Sep 17 00:00:00 2001 From: Marcus Brinkmann Date: Fri, 30 Oct 2009 14:21:08 +0000 Subject: [PATCH] 2009-10-30 Marcus Brinkmann * configure.ac: Check for argp.h and error_t. src/ 2009-10-30 Marcus Brinkmann * 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. --- ChangeLog | 4 + configure.ac | 8 + src/ChangeLog | 22 + src/Makefile.am | 4 +- src/assuan-support.c | 27 +- src/conversion.c | 1 + src/debug.h | 180 ++-- src/engine-assuan.c | 6 +- src/engine-g13.c | 4 - src/g13.c | 19 +- src/gpgme-tool.c | 2012 ++++++++++++++++++++++++++++++++++++++++++ src/gpgme.c | 18 +- src/ops.h | 5 + src/signers.c | 13 +- src/version.c | 11 +- 15 files changed, 2214 insertions(+), 120 deletions(-) create mode 100644 src/gpgme-tool.c diff --git a/ChangeLog b/ChangeLog index 735eb1a..c9c6792 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2009-10-30 Marcus Brinkmann + + * configure.ac: Check for argp.h and error_t. + 2009-10-26 Marcus Brinkmann * configure.ac (NEED_GPG_VERSION_DEFAULT): Bump to 1.4.0 as 1.3.0 diff --git a/configure.ac b/configure.ac index 1d0be30..3cd7c0b 100644 --- a/configure.ac +++ b/configure.ac @@ -850,6 +850,14 @@ LTLIBOBJS=`echo "$LIB@&t@OBJS" | sed 's,\.[[^.]]* ,.lo ,g;s,\.[[^.]]*$,.lo,'` AC_SUBST(LTLIBOBJS) +# Some checks for gpgme-tool +AC_CHECK_HEADER([argp.h]) +AC_CHECK_TYPES([error_t], [], + [AC_DEFINE([error_t], [int], + [Define to a type to use for `error_t' if it is not otherwise available.])], + [#include ]) + + # Last check. die=no if test "$require_libassuan" = "no"; then diff --git a/src/ChangeLog b/src/ChangeLog index 196be97..4015910 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,25 @@ +2009-10-30 Marcus Brinkmann + + * 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 * signers.c, encrypt-sign.c, encrypt.c, delete.c, keylist.c, diff --git a/src/Makefile.am b/src/Makefile.am index cf88ab1..677a896 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -153,7 +153,6 @@ if HAVE_W32_SYSTEM # wrapper process. libexec_PROGRAMS = gpgme-w32spawn - LTRCCOMPILE = $(LIBTOOL) --mode=compile $(RC) \ `echo $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) | \ sed -e 's/-I/--include-dir /g;s/-D/--define /g'` @@ -226,6 +225,9 @@ libgpgme_qt_la_LIBADD = @LIBASSUAN_LIBS@ @LTLIBOBJS@ \ @GPG_ERROR_LIBS@ @QT4_CORE_LIBS@ endif +noinst_PROGRAMS = gpgme-tool +gpgme_tool_LDADD = libgpgme.la + status-table.h : gpgme.h $(srcdir)/mkstatus < $(builddir)/gpgme.h > status-table.h diff --git a/src/assuan-support.c b/src/assuan-support.c index cd9cbc4..b5d2548 100644 --- a/src/assuan-support.c +++ b/src/assuan-support.c @@ -117,19 +117,25 @@ my_spawn (assuan_context_t ctx, pid_t *r_pid, const char *name, return gpg_error (GPG_ERR_NOT_IMPLEMENTED); i = 0; - while (fd_child_list[i] != ASSUAN_INVALID_FD) - i++; + if (fd_child_list) + { + while (fd_child_list[i] != ASSUAN_INVALID_FD) + i++; + } /* fd_in, fd_out, terminator */ i += 3; fd_items = malloc (sizeof (struct spawn_fd_item_s) * i); if (! fd_items) return gpg_error_from_syserror (); i = 0; - while (fd_child_list[i] != ASSUAN_INVALID_FD) + if (fd_child_list) { - fd_items[i].fd = fd_child_list[i]; - fd_items[i].dup_to = -1; - i++; + while (fd_child_list[i] != ASSUAN_INVALID_FD) + { + fd_items[i].fd = fd_child_list[i]; + fd_items[i].dup_to = -1; + i++; + } } if (fd_in != ASSUAN_INVALID_FD) { @@ -151,10 +157,13 @@ my_spawn (assuan_context_t ctx, pid_t *r_pid, const char *name, { i = 0; - while (fd_child_list[i] != ASSUAN_INVALID_FD) + if (fd_child_list) { - fd_child_list[i] = fd_items[i].peer_name; - i++; + while (fd_child_list[i] != ASSUAN_INVALID_FD) + { + fd_child_list[i] = fd_items[i].peer_name; + i++; + } } } free (fd_items); diff --git a/src/conversion.c b/src/conversion.c index 45513f6..538a5f8 100644 --- a/src/conversion.c +++ b/src/conversion.c @@ -272,6 +272,7 @@ _gpgme_encode_percent_string (const char *src, char **destp, size_t len) destlen += 3; else destlen++; + str++; } /* Terminating nul byte. */ destlen++; diff --git a/src/debug.h b/src/debug.h index c4455b2..ed93531 100644 --- a/src/debug.h +++ b/src/debug.h @@ -89,155 +89,155 @@ void _gpgme_debug_buffer (int lvl, const char *const fmt, #define TRACE_BEG(lvl, name, tag) \ _TRACE (lvl, name, tag); \ - _gpgme_debug (_gpgme_trace_level, "%s (%s=%p): enter\n", \ - _gpgme_trace_func, _gpgme_trace_tagname, \ - _gpgme_trace_tag), 0 + _gpgme_debug (_gpgme_trace_level, "[%s=%p] %s: enter\n", \ + _gpgme_trace_tagname, _gpgme_trace_tag, \ + _gpgme_trace_func), 0 #define TRACE_BEG0(lvl, name, tag, fmt) \ _TRACE (lvl, name, tag); \ - _gpgme_debug (_gpgme_trace_level, "%s (%s=%p): enter: " fmt "\n", \ - _gpgme_trace_func, _gpgme_trace_tagname, \ - _gpgme_trace_tag), 0 + _gpgme_debug (_gpgme_trace_level, "[%s=%p] %s: enter: " fmt "\n", \ + _gpgme_trace_tagname, _gpgme_trace_tag, \ + _gpgme_trace_func), 0 #define TRACE_BEG1(lvl, name, tag, fmt, arg1) \ _TRACE (lvl, name, tag); \ - _gpgme_debug (_gpgme_trace_level, "%s (%s=%p): enter: " fmt "\n", \ - _gpgme_trace_func, _gpgme_trace_tagname, \ - _gpgme_trace_tag, arg1), 0 + _gpgme_debug (_gpgme_trace_level, "[%s=%p] %s: enter: " fmt "\n", \ + _gpgme_trace_tagname, _gpgme_trace_tag, \ + _gpgme_trace_func, arg1), 0 #define TRACE_BEG2(lvl, name, tag, fmt, arg1, arg2) \ _TRACE (lvl, name, tag); \ - _gpgme_debug (_gpgme_trace_level, "%s (%s=%p): enter: " fmt "\n", \ - _gpgme_trace_func, _gpgme_trace_tagname, \ - _gpgme_trace_tag, arg1, arg2), 0 + _gpgme_debug (_gpgme_trace_level, "[%s=%p] %s: enter: " fmt "\n", \ + _gpgme_trace_tagname, _gpgme_trace_tag, \ + _gpgme_trace_func, arg1, arg2), 0 #define TRACE_BEG3(lvl, name, tag, fmt, arg1, arg2, arg3) \ _TRACE (lvl, name, tag); \ - _gpgme_debug (_gpgme_trace_level, "%s (%s=%p): enter: " fmt "\n", \ - _gpgme_trace_func, _gpgme_trace_tagname, \ - _gpgme_trace_tag, arg1, arg2, arg3), 0 + _gpgme_debug (_gpgme_trace_level, "[%s=%p] %s: enter: " fmt "\n", \ + _gpgme_trace_tagname, _gpgme_trace_tag, \ + _gpgme_trace_func, 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 (%s=%p): enter: " fmt "\n", \ - _gpgme_trace_func, _gpgme_trace_tagname, \ - _gpgme_trace_tag, arg1, arg2, arg3, arg4), 0 + _gpgme_debug (_gpgme_trace_level, "[%s=%p] %s: enter: " fmt "\n", \ + _gpgme_trace_tagname, _gpgme_trace_tag, \ + _gpgme_trace_func, 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 (%s=%p): enter: " fmt "\n", \ - _gpgme_trace_func, _gpgme_trace_tagname, \ - _gpgme_trace_tag, arg1, arg2, arg3, arg4, arg5), 0 + _gpgme_debug (_gpgme_trace_level, "[%s=%p] %s: enter: " fmt "\n", \ + _gpgme_trace_tagname, _gpgme_trace_tag, \ + _gpgme_trace_func, 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 (%s=%p): enter: " fmt "\n", \ - _gpgme_trace_func, _gpgme_trace_tagname, \ - _gpgme_trace_tag, arg1, arg2, arg3, arg4, arg5, \ + _gpgme_debug (_gpgme_trace_level, "[%s=%p] %s: enter: " fmt "\n", \ + _gpgme_trace_tagname, _gpgme_trace_tag, \ + _gpgme_trace_func, 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 (%s=%p): enter: " fmt "\n", \ - _gpgme_trace_func, _gpgme_trace_tagname, \ - _gpgme_trace_tag, arg1, arg2, arg3, arg4, arg5, \ + _gpgme_debug (_gpgme_trace_level, "[%s=%p] %s: enter: " fmt "\n", \ + _gpgme_trace_tagname, _gpgme_trace_tag, \ + _gpgme_trace_func, arg1, arg2, arg3, arg4, arg5, \ arg6, arg7, arg8), 0 #define TRACE(lvl, name, tag) \ - _gpgme_debug (lvl, "%s (%s=%p): call\n", \ - name, STRINGIFY (tag), (void *) (uintptr_t) tag), 0 + _gpgme_debug (lvl, "[%s=%p] %s: call\n", \ + STRINGIFY (tag), (void *) (uintptr_t) tag, name), 0 #define TRACE0(lvl, name, tag, fmt) \ - _gpgme_debug (lvl, "%s (%s=%p): call: " fmt "\n", \ - name, STRINGIFY (tag), (void *) (uintptr_t) tag), 0 + _gpgme_debug (lvl, "[%s=%p] %s: call: " fmt "\n", \ + STRINGIFY (tag), (void *) (uintptr_t) tag, name), 0 #define TRACE1(lvl, name, tag, fmt, arg1) \ - _gpgme_debug (lvl, "%s (%s=%p): call: " fmt "\n", \ - name, STRINGIFY (tag), (void *) (uintptr_t) tag, arg1), 0 + _gpgme_debug (lvl, "[%s=%p] %s: call: " fmt "\n", \ + STRINGIFY (tag), (void *) (uintptr_t) tag, name, arg1), 0 #define TRACE2(lvl, name, tag, fmt, arg1, arg2) \ - _gpgme_debug (lvl, "%s (%s=%p): call: " fmt "\n", \ - name, STRINGIFY (tag), (void *) (uintptr_t) tag, arg1, \ + _gpgme_debug (lvl, "[%s=%p] %s: call: " fmt "\n", \ + STRINGIFY (tag), (void *) (uintptr_t) tag, name, arg1, \ arg2), 0 #define TRACE3(lvl, name, tag, fmt, arg1, arg2, arg3) \ - _gpgme_debug (lvl, "%s (%s=%p): call: " fmt "\n", \ - name, STRINGIFY (tag), (void *) (uintptr_t) tag, arg1, \ + _gpgme_debug (lvl, "[%s=%p] %s: call: " fmt "\n", \ + STRINGIFY (tag), (void *) (uintptr_t) tag, name, arg1, \ arg2, arg3), 0 #define TRACE6(lvl, name, tag, fmt, arg1, arg2, arg3, arg4, arg5, arg6) \ - _gpgme_debug (lvl, "%s (%s=%p): call: " fmt "\n", \ - name, STRINGIFY (tag), (void *) (uintptr_t) tag, arg1, \ + _gpgme_debug (lvl, "[%s=%p] %s: call: " fmt "\n", \ + STRINGIFY (tag), (void *) (uintptr_t) tag, name, arg1, \ arg2, arg3, arg4, arg5, arg6), 0 #define TRACE_ERR(err) \ err == 0 ? (TRACE_SUC ()) : \ - (_gpgme_debug (_gpgme_trace_level, "%s (%s=%p): error: %s <%s>\n", \ - _gpgme_trace_func, _gpgme_trace_tagname, \ - _gpgme_trace_tag, gpgme_strerror (err), \ + (_gpgme_debug (_gpgme_trace_level, "[%s=%p] %s: error: %s <%s>\n", \ + _gpgme_trace_tagname, _gpgme_trace_tag, \ + _gpgme_trace_func, gpgme_strerror (err), \ gpgme_strsource (err)), (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 (%s=%p): error: %s\n", \ - _gpgme_trace_func, _gpgme_trace_tagname, \ - _gpgme_trace_tag, strerror (errno)), (res)) + (_gpgme_debug (_gpgme_trace_level, "[%s=%p] %s: error: %s\n", \ + _gpgme_trace_tagname, _gpgme_trace_tag, \ + _gpgme_trace_func, strerror (errno)), (res)) #define TRACE_SYSERR(res) \ res == 0 ? ((void) (TRACE_SUC1 ("result=%i", res)), (res)) : \ - (_gpgme_debug (_gpgme_trace_level, "%s (%s=%p): error: %s\n", \ - _gpgme_trace_func, _gpgme_trace_tagname, \ - _gpgme_trace_tag, strerror (res)), (res)) + (_gpgme_debug (_gpgme_trace_level, "[%s=%p] %s: error: %s\n", \ + _gpgme_trace_tagname, _gpgme_trace_tag, \ + _gpgme_trace_func, strerror (res)), (res)) #define TRACE_SUC() \ - _gpgme_debug (_gpgme_trace_level, "%s (%s=%p): leave\n", \ - _gpgme_trace_func, _gpgme_trace_tagname, \ - _gpgme_trace_tag), 0 + _gpgme_debug (_gpgme_trace_level, "[%s=%p] %s: leave\n", \ + _gpgme_trace_tagname, _gpgme_trace_tag, \ + _gpgme_trace_func), 0 #define TRACE_SUC0(fmt) \ - _gpgme_debug (_gpgme_trace_level, "%s (%s=%p): leave: " fmt "\n", \ - _gpgme_trace_func, _gpgme_trace_tagname, \ - _gpgme_trace_tag), 0 + _gpgme_debug (_gpgme_trace_level, "[%s=%p] %s: leave: " fmt "\n", \ + _gpgme_trace_tagname, _gpgme_trace_tag, \ + _gpgme_trace_func), 0 #define TRACE_SUC1(fmt, arg1) \ - _gpgme_debug (_gpgme_trace_level, "%s (%s=%p): leave: " fmt "\n", \ - _gpgme_trace_func, _gpgme_trace_tagname, \ - _gpgme_trace_tag, arg1), 0 + _gpgme_debug (_gpgme_trace_level, "[%s=%p] %s: leave: " fmt "\n", \ + _gpgme_trace_tagname, _gpgme_trace_tag, \ + _gpgme_trace_func, arg1), 0 #define TRACE_SUC2(fmt, arg1, arg2) \ - _gpgme_debug (_gpgme_trace_level, "%s (%s=%p): leave: " fmt "\n", \ - _gpgme_trace_func, _gpgme_trace_tagname, \ - _gpgme_trace_tag, arg1, arg2), 0 + _gpgme_debug (_gpgme_trace_level, "[%s=%p] %s: leave: " fmt "\n", \ + _gpgme_trace_tagname, _gpgme_trace_tag, \ + _gpgme_trace_func, arg1, arg2), 0 #define TRACE_SUC5(fmt, arg1, arg2, arg3, arg4, arg5) \ - _gpgme_debug (_gpgme_trace_level, "%s (%s=%p): leave: " fmt "\n", \ - _gpgme_trace_func, _gpgme_trace_tagname, \ - _gpgme_trace_tag, arg1, arg2, arg3, arg4, arg5), 0 + _gpgme_debug (_gpgme_trace_level, "[%s=%p] %s: leave: " fmt "\n", \ + _gpgme_trace_tagname, _gpgme_trace_tag, \ + _gpgme_trace_func, arg1, arg2, arg3, arg4, arg5), 0 #define TRACE_LOG(fmt) \ - _gpgme_debug (_gpgme_trace_level, "%s (%s=%p): check: " fmt "\n", \ - _gpgme_trace_func, _gpgme_trace_tagname, \ - _gpgme_trace_tag), 0 + _gpgme_debug (_gpgme_trace_level, "[%s=%p] %s: check: " fmt "\n", \ + _gpgme_trace_tagname, _gpgme_trace_tag, \ + _gpgme_trace_func), 0 #define TRACE_LOG1(fmt, arg1) \ - _gpgme_debug (_gpgme_trace_level, "%s (%s=%p): check: " fmt "\n", \ - _gpgme_trace_func, _gpgme_trace_tagname, \ - _gpgme_trace_tag, arg1), 0 + _gpgme_debug (_gpgme_trace_level, "[%s=%p] %s: check: " fmt "\n", \ + _gpgme_trace_tagname, _gpgme_trace_tag, \ + _gpgme_trace_func, arg1), 0 #define TRACE_LOG2(fmt, arg1, arg2) \ - _gpgme_debug (_gpgme_trace_level, "%s (%s=%p): check: " fmt "\n", \ - _gpgme_trace_func, _gpgme_trace_tagname, \ - _gpgme_trace_tag, arg1, arg2), 0 + _gpgme_debug (_gpgme_trace_level, "[%s=%p] %s: check: " fmt "\n", \ + _gpgme_trace_tagname, _gpgme_trace_tag, \ + _gpgme_trace_func, arg1, arg2), 0 #define TRACE_LOG3(fmt, arg1, arg2, arg3) \ - _gpgme_debug (_gpgme_trace_level, "%s (%s=%p): check: " fmt "\n", \ - _gpgme_trace_func, _gpgme_trace_tagname, \ - _gpgme_trace_tag, arg1, arg2, arg3), 0 + _gpgme_debug (_gpgme_trace_level, "[%s=%p] %s: check: " fmt "\n", \ + _gpgme_trace_tagname, _gpgme_trace_tag, \ + _gpgme_trace_func, arg1, arg2, arg3), 0 #define TRACE_LOG4(fmt, arg1, arg2, arg3, arg4) \ - _gpgme_debug (_gpgme_trace_level, "%s (%s=%p): check: " fmt "\n", \ - _gpgme_trace_func, _gpgme_trace_tagname, \ - _gpgme_trace_tag, arg1, arg2, arg3, arg4), 0 + _gpgme_debug (_gpgme_trace_level, "[%s=%p] %s: check: " fmt "\n", \ + _gpgme_trace_tagname, _gpgme_trace_tag, \ + _gpgme_trace_func, arg1, arg2, arg3, arg4), 0 #define TRACE_LOG5(fmt, arg1, arg2, arg3, arg4, arg5) \ - _gpgme_debug (_gpgme_trace_level, "%s (%s=%p): check: " fmt "\n", \ - _gpgme_trace_func, _gpgme_trace_tagname, \ - _gpgme_trace_tag, arg1, arg2, arg3, arg4, arg5), 0 + _gpgme_debug (_gpgme_trace_level, "[%s=%p] %s: check: " fmt "\n", \ + _gpgme_trace_tagname, _gpgme_trace_tag, \ + _gpgme_trace_func, arg1, arg2, arg3, arg4, arg5), 0 #define TRACE_LOG6(fmt, arg1, arg2, arg3, arg4, arg5, arg6) \ - _gpgme_debug (_gpgme_trace_level, "%s (%s=%p): check: " fmt "\n", \ - _gpgme_trace_func, _gpgme_trace_tagname, \ - _gpgme_trace_tag, arg1, arg2, arg3, arg4, arg5, \ + _gpgme_debug (_gpgme_trace_level, "[%s=%p] %s: check: " fmt "\n", \ + _gpgme_trace_tagname, _gpgme_trace_tag, \ + _gpgme_trace_func, arg1, arg2, arg3, arg4, arg5, \ arg6), 0 #define TRACE_LOGBUF(buf, len) \ - _gpgme_debug_buffer (_gpgme_trace_level, "%s (%s=%p): check: %s", \ - _gpgme_trace_func, _gpgme_trace_tagname, \ - _gpgme_trace_tag, buf, len) + _gpgme_debug_buffer (_gpgme_trace_level, "[%s=%p] %s: check: %s", \ + _gpgme_trace_tagname, _gpgme_trace_tag, \ + _gpgme_trace_func, buf, len) #define TRACE_SEQ(hlp,fmt) \ _gpgme_debug_begin (&(hlp), _gpgme_trace_level, \ - "%s (%s=%p): check: " fmt, \ - _gpgme_trace_func, _gpgme_trace_tagname, \ - _gpgme_trace_tag) + "[%s=%p] %s: check: " fmt, \ + _gpgme_trace_tagname, _gpgme_trace_tag, \ + _gpgme_trace_func) #define TRACE_ADD0(hlp,fmt) \ _gpgme_debug_add (&(hlp), fmt) #define TRACE_ADD1(hlp,fmt,a) \ diff --git a/src/engine-assuan.c b/src/engine-assuan.c index 92e1e05..38fb1ad 100644 --- a/src/engine-assuan.c +++ b/src/engine-assuan.c @@ -233,9 +233,13 @@ llass_new (void **engine, const char *file_name, const char *home_dir) llass->opt.gpg_agent = 1; } - err = assuan_new (&llass->assuan_ctx); + 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); if (err) goto leave; diff --git a/src/engine-g13.c b/src/engine-g13.c index 358efc1..34c6ac1 100644 --- a/src/engine-g13.c +++ b/src/engine-g13.c @@ -230,10 +230,6 @@ g13_new (void **engine, const char *file_name, const char *home_dir) g13->status_cb.tag = 0; g13->status_cb.data = g13; - err = assuan_new (&g13->assuan_ctx); - if (err) - goto leave; - argc = 0; argv[argc++] = "g13"; if (home_dir) diff --git a/src/g13.c b/src/g13.c index 029a914..a2ab675 100644 --- a/src/g13.c +++ b/src/g13.c @@ -25,6 +25,7 @@ #include #include "gpgme.h" +#include "debug.h" #include "context.h" #include "ops.h" #include "util.h" @@ -135,9 +136,9 @@ gpgme_op_vfs_transact (gpgme_ctx_t ctx, or destroyed. This is a synchronous convenience interface, which automatically returns an operation error if there is no transmission error. */ -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) +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; @@ -194,3 +195,15 @@ gpgme_op_vfs_mount (gpgme_ctx_t ctx, const char *container_file, return err; } + +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) +{ + 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); + return TRACE_ERR (_gpgme_op_vfs_mount (ctx, container_file, mount_dir, + flags, op_err)); +} + diff --git a/src/gpgme-tool.c b/src/gpgme-tool.c new file mode 100644 index 0000000..08cd82a --- /dev/null +++ b/src/gpgme-tool.c @@ -0,0 +1,2012 @@ +/* gpgme-tool.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 +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_ARGP_H +#include +#endif + +#include "gpgme.h" + + +#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; +}; + +#define ARGP_ERR_UNKNOWN E2BIG +#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) + + +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", state.argv[idx]); + 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"; + +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); +} + + + +typedef enum status + { + STATUS_PROTOCOL, + STATUS_PROGRESS, + STATUS_ENGINE, + STATUS_ARMOR, + STATUS_TEXTMODE, + STATUS_INCLUDE_CERTS, + STATUS_KEYLIST_MODE, + STATUS_ENCRYPT_RESULT + } status_t; + +const char *status_string[] = + { + "PROTOCOL", + "PROGRESS", + "ENGINE", + "ARMOR", + "TEXTMODE", + "INCLUDE_CERTS", + "KEYLIST_MODE", + "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; +}; +typedef struct gpgme_tool *gpgme_tool_t; + + +/* Forward declaration. */ +void gt_write_status (gpgme_tool_t gt, status_t status, ...); + +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); +} + + +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, >->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_recipients_add (gpgme_tool_t gt, const char *fpr) +{ + gpg_error_t err; + gpgme_key_t key; + + if (gt->recipients_nr >= MAX_RECIPIENTS) + return gpg_error_from_errno (ENOMEM); + + err = gpgme_get_key (gt->ctx, fpr, &key, 0); + 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_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); + 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; + 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_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]); + + 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 (gt->ctx, gt->recipients, flags, plain, cipher); + else + err = gpgme_op_encrypt_sign (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; + 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 || op_err; +} + + +// TODO +#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) +{ + if (flags & GT_RESULT_ENCRYPT) + { + gpgme_encrypt_result_t res = gpgme_op_encrypt_result (gt->ctx); + if (res) + { + gpgme_invalid_key_t invrec = res->invalid_recipients; + while (invrec) + { + gt_write_status (gt, STATUS_ENCRYPT_RESULT, "invalid_recipient", + invrec->fpr, invrec->reason); + invrec = invrec->next; + } + } + } + return 0; +} + + +/* GPGME SERVER. */ + +#include + +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 message_fd; + 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); +} + + +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, gpgme_data_encoding_t encoding, + gpgme_data_t *data) +{ + gpgme_error_t err; + + err = gpgme_data_new_from_fd (data, 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 != -1) + { + /* FIXME: Assuan should provide a close function. */ + close (server->message_fd); + server->message_fd = -1; + } + 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 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 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 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 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 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 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 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 void +input_notify (assuan_context_t ctx, const char *line) +{ + struct server *server = assuan_get_pointer (ctx); + server->input_enc = server_data_encoding (line); +} + + +static void +output_notify (assuan_context_t ctx, const char *line) +{ + struct server *server = assuan_get_pointer (ctx); + server->output_enc = server_data_encoding (line); +} + + +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; + + err = assuan_command_parse_fd (ctx, line, &sysfd); + if (err) + return err; + server->message_fd = sysfd; + server->message_enc = server_data_encoding (line); + return 0; +} + + +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 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 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; + assuan_fd_t out_fd; + gpgme_data_t inp_data; + gpgme_data_t out_data; + + inp_fd = assuan_get_input_fd (ctx); + if (inp_fd == ASSUAN_INVALID_FD) + return GPG_ERR_ASS_NO_INPUT; + out_fd = assuan_get_output_fd (ctx); + if (out_fd == ASSUAN_INVALID_FD) + return GPG_ERR_ASS_NO_OUTPUT; + + err = server_data_obj (inp_fd, server->input_enc, &inp_data); + if (err) + return err; + err = server_data_obj (out_fd, server->output_enc, &out_data); + 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 gpg_error_t +cmd_decrypt (assuan_context_t ctx, char *line) +{ + return _cmd_decrypt_verify (ctx, line, 0); +} + + +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; + assuan_fd_t out_fd; + gpgme_data_t inp_data; + gpgme_data_t out_data; + 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; + + inp_fd = assuan_get_input_fd (ctx); + if (inp_fd == ASSUAN_INVALID_FD) + return GPG_ERR_ASS_NO_INPUT; + out_fd = assuan_get_output_fd (ctx); + if (out_fd == ASSUAN_INVALID_FD) + return GPG_ERR_ASS_NO_OUTPUT; + + err = server_data_obj (inp_fd, server->input_enc, &inp_data); + if (err) + return err; + err = server_data_obj (out_fd, server->output_enc, &out_data); + 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 gpg_error_t +cmd_encrypt (assuan_context_t ctx, char *line) +{ + return _cmd_sign_encrypt (ctx, line, 0); +} + + +static gpg_error_t +cmd_sign_encrypt (assuan_context_t ctx, char *line) +{ + return _cmd_sign_encrypt (ctx, line, 1); +} + + +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; + assuan_fd_t out_fd; + 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); + if (inp_fd == ASSUAN_INVALID_FD) + return GPG_ERR_ASS_NO_INPUT; + out_fd = assuan_get_output_fd (ctx); + if (out_fd == ASSUAN_INVALID_FD) + return GPG_ERR_ASS_NO_OUTPUT; + + err = server_data_obj (inp_fd, server->input_enc, &inp_data); + if (err) + return err; + err = server_data_obj (out_fd, server->output_enc, &out_data); + 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 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; + gpgme_data_t inp_data; + gpgme_data_t msg_data = NULL; + gpgme_data_t out_data = NULL; + + inp_fd = assuan_get_input_fd (ctx); + if (inp_fd == ASSUAN_INVALID_FD) + return GPG_ERR_ASS_NO_INPUT; + msg_fd = server->message_fd; + out_fd = assuan_get_output_fd (ctx); + + err = server_data_obj (inp_fd, server->input_enc, &inp_data); + if (err) + return err; + if (msg_fd != ASSUAN_INVALID_FD) + { + err = server_data_obj (msg_fd, server->message_enc, &msg_data); + if (err) + { + gpgme_data_release (inp_data); + return err; + } + } + if (out_fd != ASSUAN_INVALID_FD) + { + err = server_data_obj (out_fd, server->output_enc, &out_data); + 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 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; + gpgme_data_t inp_data; + + inp_fd = assuan_get_input_fd (ctx); + if (inp_fd == ASSUAN_INVALID_FD) + return GPG_ERR_ASS_NO_INPUT; + + err = server_data_obj (inp_fd, server->input_enc, &inp_data); + if (err) + return err; + + err = gt_import (server->gt, inp_data); + + gpgme_data_release (inp_data); + server_reset_fds (server); + + return err; + } +} + + +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; + gpgme_data_t out_data; + gpgme_export_mode_t mode = 0; + const char *pattern[2]; + const char optstr[] = "--extern "; + + out_fd = assuan_get_output_fd (ctx); + if (out_fd == ASSUAN_INVALID_FD) + return GPG_ERR_ASS_NO_OUTPUT; + err = server_data_obj (out_fd, server->output_enc, &out_data); + if (err) + return err; + + if (strncasecmp (line, optstr, strlen (optstr))) + { + mode |= GPGME_EXPORT_MODE_EXTERN; + line += strlen (optstr); + } + 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; + assuan_fd_t out_fd; + 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); + if (inp_fd == ASSUAN_INVALID_FD) + return GPG_ERR_ASS_NO_INPUT; + out_fd = assuan_get_output_fd (ctx); + + err = server_data_obj (inp_fd, server->input_enc, &inp_data); + if (err) + return err; + if (out_fd != ASSUAN_INVALID_FD) + { + err = server_data_obj (out_fd, server->output_enc, &out_data); + 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); + } + return gt_delete (server->gt, line, allow_secret); +} + + +static gpg_error_t +cmd_keylist (assuan_context_t ctx, char *line) +{ + struct server *server = assuan_get_pointer (ctx); + gpg_error_t err; + int secret_only = 0; + const char *pattern[2]; + const char optstr[] = "--secret-only "; + + if (strncasecmp (line, optstr, strlen (optstr))) + { + secret_only = 1; + line += strlen (optstr); + } + pattern[0] = line; + pattern[1] = 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); + assuan_send_data (ctx, buf, strlen (buf)); + gpgme_key_unref (key); + } + } + + server_reset_fds (server); + + return err; +} + + +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; + gpgme_data_t out_data; + + out_fd = assuan_get_output_fd (ctx); + if (out_fd == ASSUAN_INVALID_FD) + return GPG_ERR_ASS_NO_OUTPUT; + err = server_data_obj (out_fd, server->output_enc, &out_data); + if (err) + return err; + + err = gt_getauditlog (server->gt, out_data, 0); + + 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_result (assuan_context_t ctx, char *line) +{ + struct server *server = assuan_get_pointer (ctx); + return gt_result (server->gt, GT_RESULT_ALL); +} + + +/* STRERROR */ +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; + gpg_error_t (*handler)(assuan_context_t, char *line); + } table[] = { + // RESET, BYE are implicit. + { "VERSION", cmd_version }, + // TODO: Set engine info. + { "ENGINE", cmd_engine }, + { "PROTOCOL", cmd_protocol }, + { "ARMOR", cmd_armor }, + { "TEXTMODE", cmd_textmode }, + { "INCLUDE_CERTS", cmd_include_certs }, + { "KEYLIST_MODE", cmd_keylist_mode }, + { "INPUT", NULL }, + { "OUTPUT", NULL }, + { "MESSAGE", cmd_message }, + { "RECIPIENT", cmd_recipient }, + { "SIGNER", cmd_signer }, + { "SIGNERS_CLEAR", cmd_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 }, + { "DECRYPT_VERIFY", cmd_decrypt_verify }, + { "ENCRYPT", cmd_encrypt }, + { "ENCRYPT_SIGN", cmd_sign_encrypt }, + { "SIGN_ENCRYPT", cmd_sign_encrypt }, + { "SIGN", cmd_sign }, + { "VERIFY", cmd_verify }, + { "IMPORT", cmd_import }, + { "EXPORT", cmd_export }, + { "GENKEY", cmd_genkey }, + { "DELETE", cmd_delete }, + // TODO: EDIT, CARD_EDIT (with INQUIRE) + { "KEYLIST", cmd_keylist }, + { "LISTKEYS", cmd_keylist }, + // TODO: TRUSTLIST, TRUSTLIST_EXT + { "GETAUDITLOG", cmd_getauditlog }, + // TODO: ASSUAN + { "VFS_MOUNT", cmd_vfs_mount }, + { "MOUNT", cmd_vfs_mount }, + // TODO: GPGCONF + { "RESULT", cmd_result }, + { "STRERROR", cmd_strerror }, + { "PUBKEY_ALGO_NAME", cmd_pubkey_algo_name }, + { "HASH_ALGO_NAME", cmd_hash_algo_name }, + { NULL } + }; + int idx; + + for (idx = 0; table[idx].name; idx++) + { + err = assuan_register_command (ctx, table[idx].name, table[idx].handler); + if (err) + return err; + } + return 0; +} + + +/* TODO: password callback can do INQUIRE. */ +void +gpgme_server (gpgme_tool_t gt) +{ + gpg_error_t err; + int filedes[2]; + struct server server; + static const char hello[] = ("GPGME-Tool " VERSION " ready"); + + memset (&server, 0, sizeof (server)); + server.message_fd = -1; + 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; + + /* 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. */ + filedes[0] = 0; + filedes[1] = 1; + 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); + assuan_register_input_notify (server.assuan_ctx, input_notify); + assuan_register_output_notify (server.assuan_ctx, output_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 -- invoke 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; + + setlocale (LC_ALL, ""); + gpgme_check_version (NULL); + gpgme_set_locale (NULL, LC_CTYPE, setlocale (LC_CTYPE, NULL)); +#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 (>); + + switch (args.cmd) + { + case CMD_DEFAULT: + case CMD_SERVER: + gpgme_server (>); + break; + } + + gpgme_release (gt.ctx); + + return 0; +} + diff --git a/src/gpgme.c b/src/gpgme.c index 52e14d7..70f93f1 100644 --- a/src/gpgme.c +++ b/src/gpgme.c @@ -186,8 +186,8 @@ gpgme_release (gpgme_ctx_t ctx) _gpgme_engine_release (ctx->engine); _gpgme_fd_table_deinit (&ctx->fdt); _gpgme_release_result (ctx); - gpgme_signers_clear (ctx); - gpgme_sig_notation_clear (ctx); + _gpgme_signers_clear (ctx); + _gpgme_sig_notation_clear (ctx); if (ctx->signers) free (ctx->signers); if (ctx->lc_ctype) @@ -269,6 +269,7 @@ gpgme_set_protocol (gpgme_ctx_t ctx, gpgme_protocol_t protocol) if (protocol != GPGME_PROTOCOL_OpenPGP && protocol != GPGME_PROTOCOL_CMS + && protocol != GPGME_PROTOCOL_GPGCONF && protocol != GPGME_PROTOCOL_ASSUAN && protocol != GPGME_PROTOCOL_G13) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); @@ -311,6 +312,9 @@ gpgme_get_protocol_name (gpgme_protocol_t protocol) case GPGME_PROTOCOL_CMS: return "CMS"; + case GPGME_PROTOCOL_GPGCONF: + return "GPGCONF"; + case GPGME_PROTOCOL_ASSUAN: return "Assuan"; @@ -654,10 +658,9 @@ gpgme_ctx_set_engine_info (gpgme_ctx_t ctx, gpgme_protocol_t proto, /* Clear all notation data from the context. */ void -gpgme_sig_notation_clear (gpgme_ctx_t ctx) +_gpgme_sig_notation_clear (gpgme_ctx_t ctx) { gpgme_sig_notation_t notation; - TRACE (DEBUG_CTX, "gpgme_sig_notation_clear", ctx); if (!ctx) return; @@ -672,6 +675,13 @@ gpgme_sig_notation_clear (gpgme_ctx_t ctx) ctx->sig_notations = NULL; } +void +gpgme_sig_notation_clear (gpgme_ctx_t ctx) +{ + TRACE (DEBUG_CTX, "gpgme_sig_notation_clear", ctx); + _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 diff --git a/src/ops.h b/src/ops.h index 7bc7695..5426bb8 100644 --- a/src/ops.h +++ b/src/ops.h @@ -29,6 +29,8 @@ /* 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); @@ -79,6 +81,9 @@ gpgme_error_t _gpgme_decrypt_status_handler (void *priv, 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 diff --git a/src/signers.c b/src/signers.c index 9eb4e4e..021a878 100644 --- a/src/signers.c +++ b/src/signers.c @@ -36,12 +36,10 @@ /* Delete all signers from CTX. */ void -gpgme_signers_clear (gpgme_ctx_t ctx) +_gpgme_signers_clear (gpgme_ctx_t ctx) { unsigned int i; - TRACE (DEBUG_CTX, "gpgme_signers_clear", ctx); - if (!ctx || !ctx->signers) return; @@ -54,6 +52,15 @@ gpgme_signers_clear (gpgme_ctx_t ctx) ctx->signers_len = 0; } + +void +gpgme_signers_clear (gpgme_ctx_t ctx) +{ + TRACE (DEBUG_CTX, "gpgme_signers_clear", ctx); + return _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) diff --git a/src/version.c b/src/version.c index b2d795a..e5d7c3f 100644 --- a/src/version.c +++ b/src/version.c @@ -193,7 +193,7 @@ gpgme_check_version (const char *req_version) do_subsystem_inits (); /* Catch-22: We need to get at least the debug subsystem ready - before using the tarce facility. If we won't the tarce would + 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, @@ -216,14 +216,15 @@ gpgme_check_version_internal (const char *req_version, { const char *result; - TRACE2 (DEBUG_INIT, "gpgme_check_version_internal: ", 0, - "req_version=%s, offset_sig_validity=%i", - req_version ? req_version : "(null)", offset_sig_validity); - 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, -- 2.26.2