2009-10-30 Marcus Brinkmann <marcus@g10code.de>
authorMarcus Brinkmann <mb@g10code.com>
Fri, 30 Oct 2009 14:21:08 +0000 (14:21 +0000)
committerMarcus Brinkmann <mb@g10code.com>
Fri, 30 Oct 2009 14:21:08 +0000 (14:21 +0000)
* configure.ac: Check for argp.h and error_t.

src/
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.

15 files changed:
ChangeLog
configure.ac
src/ChangeLog
src/Makefile.am
src/assuan-support.c
src/conversion.c
src/debug.h
src/engine-assuan.c
src/engine-g13.c
src/g13.c
src/gpgme-tool.c [new file with mode: 0644]
src/gpgme.c
src/ops.h
src/signers.c
src/version.c

index 735eb1a..c9c6792 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2009-10-30  Marcus Brinkmann  <marcus@g10code.de>
+
+       * configure.ac: Check for argp.h and error_t.
+
 2009-10-26  Marcus Brinkmann  <marcus@g10code.de>
 
        * configure.ac (NEED_GPG_VERSION_DEFAULT): Bump to 1.4.0 as 1.3.0
index 1d0be30..3cd7c0b 100644 (file)
@@ -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 <errno.h>])
+
+
 # Last check.
 die=no
 if test "$require_libassuan" = "no"; then
index 196be97..4015910 100644 (file)
@@ -1,3 +1,25 @@
+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,
index cf88ab1..677a896 100644 (file)
@@ -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
 
index cd9cbc4..b5d2548 100644 (file)
@@ -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);
index 45513f6..538a5f8 100644 (file)
@@ -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++;
index c4455b2..ed93531 100644 (file)
@@ -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) \
index 92e1e05..38fb1ad 100644 (file)
@@ -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;
index 358efc1..34c6ac1 100644 (file)
@@ -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)
index 029a914..a2ab675 100644 (file)
--- a/src/g13.c
+++ b/src/g13.c
@@ -25,6 +25,7 @@
 #include <stdlib.h>
 
 #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 (file)
index 0000000..08cd82a
--- /dev/null
@@ -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 <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <getopt.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <locale.h>
+#ifdef HAVE_ARGP_H
+#include <argp.h>
+#endif
+
+#include "gpgme.h"
+
+\f
+#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
+
+\f
+/* 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);
+}
+
+
+\f
+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, &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;
+}
+
+\f
+/* 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 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 <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;
+    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);
+}
+
+
+\f
+/* 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;
+}
+
+\f
+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 (&gt);
+
+  switch (args.cmd)
+    {
+    case CMD_DEFAULT:
+    case CMD_SERVER:
+      gpgme_server (&gt);
+      break;
+    }
+
+  gpgme_release (gt.ctx);
+
+  return 0;
+}
+
index 52e14d7..70f93f1 100644 (file)
@@ -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,
 \f
 /* 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
index 7bc7695..5426bb8 100644 (file)
--- 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);
 
 \f
+/* 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
index 9eb4e4e..021a878 100644 (file)
 \f
 /* 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)
index b2d795a..e5d7c3f 100644 (file)
@@ -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,