LCOV - code coverage report
Current view: top level - src - engine-gpgsm.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 511 985 51.9 %
Date: 2018-11-15 08:49:49 Functions: 29 37 78.4 %

          Line data    Source code
       1             : /* engine-gpgsm.c - GpgSM engine.
       2             :    Copyright (C) 2000 Werner Koch (dd9jn)
       3             :    Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2009,
       4             :                  2010 g10 Code GmbH
       5             : 
       6             :    This file is part of GPGME.
       7             : 
       8             :    GPGME is free software; you can redistribute it and/or modify it
       9             :    under the terms of the GNU Lesser General Public License as
      10             :    published by the Free Software Foundation; either version 2.1 of
      11             :    the License, or (at your option) any later version.
      12             : 
      13             :    GPGME is distributed in the hope that it will be useful, but
      14             :    WITHOUT ANY WARRANTY; without even the implied warranty of
      15             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      16             :    Lesser General Public License for more details.
      17             : 
      18             :    You should have received a copy of the GNU Lesser General Public
      19             :    License along with this program; if not, write to the Free Software
      20             :    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
      21             :    02111-1307, USA.  */
      22             : 
      23             : #if HAVE_CONFIG_H
      24             : #include <config.h>
      25             : #endif
      26             : 
      27             : #include <stdlib.h>
      28             : #include <string.h>
      29             : #ifdef HAVE_SYS_TYPES_H
      30             : # include <sys/types.h>
      31             : #endif
      32             : #include <assert.h>
      33             : #ifdef HAVE_UNISTD_H
      34             : # include <unistd.h>
      35             : #endif
      36             : #ifdef HAVE_LOCALE_H
      37             : #include <locale.h>
      38             : #endif
      39             : #include <fcntl.h> /* FIXME */
      40             : 
      41             : #include "gpgme.h"
      42             : #include "util.h"
      43             : #include "ops.h"
      44             : #include "wait.h"
      45             : #include "priv-io.h"
      46             : #include "sema.h"
      47             : #include "data.h"
      48             : 
      49             : #include "assuan.h"
      50             : #include "debug.h"
      51             : 
      52             : #include "engine-backend.h"
      53             : 
      54             : 
      55             : typedef struct
      56             : {
      57             :   int fd;       /* FD we talk about.  */
      58             :   int server_fd;/* Server FD for this connection.  */
      59             :   int dir;      /* Inbound/Outbound, maybe given implicit?  */
      60             :   void *data;   /* Handler-specific data.  */
      61             :   void *tag;    /* ID from the user for gpgme_remove_io_callback.  */
      62             :   char server_fd_str[15]; /* Same as SERVER_FD but as a string.  We
      63             :                              need this because _gpgme_io_fd2str can't
      64             :                              be used on a closed descriptor.  */
      65             : } iocb_data_t;
      66             : 
      67             : 
      68             : struct engine_gpgsm
      69             : {
      70             :   assuan_context_t assuan_ctx;
      71             : 
      72             :   int lc_ctype_set;
      73             :   int lc_messages_set;
      74             : 
      75             :   iocb_data_t status_cb;
      76             : 
      77             :   /* Input, output etc are from the servers perspective.  */
      78             :   iocb_data_t input_cb;
      79             :   gpgme_data_t input_helper_data;  /* Input helper data object.  */
      80             :   void *input_helper_memory;       /* Input helper memory block.  */
      81             : 
      82             :   iocb_data_t output_cb;
      83             : 
      84             :   iocb_data_t message_cb;
      85             : 
      86             :   struct
      87             :   {
      88             :     engine_status_handler_t fnc;
      89             :     void *fnc_value;
      90             :     gpgme_status_cb_t mon_cb;
      91             :     void *mon_cb_value;
      92             :   } status;
      93             : 
      94             :   struct
      95             :   {
      96             :     engine_colon_line_handler_t fnc;
      97             :     void *fnc_value;
      98             :     struct
      99             :     {
     100             :       char *line;
     101             :       int linesize;
     102             :       int linelen;
     103             :     } attic;
     104             :     int any; /* any data line seen */
     105             :   } colon;
     106             : 
     107             :   gpgme_data_t inline_data;  /* Used to collect D lines.  */
     108             : 
     109             :   char request_origin[10];
     110             : 
     111             :   struct gpgme_io_cbs io_cbs;
     112             : };
     113             : 
     114             : typedef struct engine_gpgsm *engine_gpgsm_t;
     115             : 
     116             : 
     117             : static void gpgsm_io_event (void *engine,
     118             :                             gpgme_event_io_t type, void *type_data);
     119             : 
     120             : 
     121             : 
     122             : static char *
     123         118 : gpgsm_get_version (const char *file_name)
     124             : {
     125         118 :   return _gpgme_get_program_version (file_name ? file_name
     126             :                                      : _gpgme_get_default_gpgsm_name ());
     127             : }
     128             : 
     129             : 
     130             : static const char *
     131         117 : gpgsm_get_req_version (void)
     132             : {
     133         117 :   return "2.0.4";
     134             : }
     135             : 
     136             : 
     137             : static void
     138          32 : close_notify_handler (int fd, void *opaque)
     139             : {
     140          32 :   engine_gpgsm_t gpgsm = opaque;
     141             : 
     142          32 :   assert (fd != -1);
     143          32 :   if (gpgsm->status_cb.fd == fd)
     144             :     {
     145          14 :       if (gpgsm->status_cb.tag)
     146          14 :         (*gpgsm->io_cbs.remove) (gpgsm->status_cb.tag);
     147          14 :       gpgsm->status_cb.fd = -1;
     148          14 :       gpgsm->status_cb.tag = NULL;
     149             :     }
     150          18 :   else if (gpgsm->input_cb.fd == fd)
     151             :     {
     152           8 :       if (gpgsm->input_cb.tag)
     153           8 :         (*gpgsm->io_cbs.remove) (gpgsm->input_cb.tag);
     154           8 :       gpgsm->input_cb.fd = -1;
     155           8 :       gpgsm->input_cb.tag = NULL;
     156           8 :       if (gpgsm->input_helper_data)
     157             :         {
     158           0 :           gpgme_data_release (gpgsm->input_helper_data);
     159           0 :           gpgsm->input_helper_data = NULL;
     160             :         }
     161           8 :       if (gpgsm->input_helper_memory)
     162             :         {
     163           0 :           free (gpgsm->input_helper_memory);
     164           0 :           gpgsm->input_helper_memory = NULL;
     165             :         }
     166             :     }
     167          10 :   else if (gpgsm->output_cb.fd == fd)
     168             :     {
     169           8 :       if (gpgsm->output_cb.tag)
     170           8 :         (*gpgsm->io_cbs.remove) (gpgsm->output_cb.tag);
     171           8 :       gpgsm->output_cb.fd = -1;
     172           8 :       gpgsm->output_cb.tag = NULL;
     173             :     }
     174           2 :   else if (gpgsm->message_cb.fd == fd)
     175             :     {
     176           2 :       if (gpgsm->message_cb.tag)
     177           2 :         (*gpgsm->io_cbs.remove) (gpgsm->message_cb.tag);
     178           2 :       gpgsm->message_cb.fd = -1;
     179           2 :       gpgsm->message_cb.tag = NULL;
     180             :     }
     181          32 : }
     182             : 
     183             : 
     184             : /* This is the default inquiry callback.  We use it to handle the
     185             :    Pinentry notifications.  */
     186             : static gpgme_error_t
     187           0 : default_inq_cb (engine_gpgsm_t gpgsm, const char *line)
     188             : {
     189             :   (void)gpgsm;
     190             : 
     191           0 :   if (!strncmp (line, "PINENTRY_LAUNCHED", 17) && (line[17]==' '||!line[17]))
     192             :     {
     193           0 :       _gpgme_allow_set_foreground_window ((pid_t)strtoul (line+17, NULL, 10));
     194             :     }
     195             : 
     196           0 :   return 0;
     197             : }
     198             : 
     199             : 
     200             : static gpgme_error_t
     201           8 : gpgsm_cancel (void *engine)
     202             : {
     203           8 :   engine_gpgsm_t gpgsm = engine;
     204             : 
     205           8 :   if (!gpgsm)
     206           0 :     return gpg_error (GPG_ERR_INV_VALUE);
     207             : 
     208           8 :   if (gpgsm->status_cb.fd != -1)
     209           0 :     _gpgme_io_close (gpgsm->status_cb.fd);
     210           8 :   if (gpgsm->input_cb.fd != -1)
     211           0 :     _gpgme_io_close (gpgsm->input_cb.fd);
     212           8 :   if (gpgsm->output_cb.fd != -1)
     213           0 :     _gpgme_io_close (gpgsm->output_cb.fd);
     214           8 :   if (gpgsm->message_cb.fd != -1)
     215           0 :     _gpgme_io_close (gpgsm->message_cb.fd);
     216             : 
     217           8 :   if (gpgsm->assuan_ctx)
     218             :     {
     219           8 :       assuan_release (gpgsm->assuan_ctx);
     220           8 :       gpgsm->assuan_ctx = NULL;
     221             :     }
     222             : 
     223           8 :   return 0;
     224             : }
     225             : 
     226             : 
     227             : static void
     228           8 : gpgsm_release (void *engine)
     229             : {
     230           8 :   engine_gpgsm_t gpgsm = engine;
     231             : 
     232           8 :   if (!gpgsm)
     233           0 :     return;
     234             : 
     235           8 :   gpgsm_cancel (engine);
     236             : 
     237           8 :   free (gpgsm->colon.attic.line);
     238           8 :   free (gpgsm);
     239             : }
     240             : 
     241             : 
     242             : static gpgme_error_t
     243           8 : gpgsm_new (void **engine, const char *file_name, const char *home_dir,
     244             :            const char *version)
     245             : {
     246           8 :   gpgme_error_t err = 0;
     247             :   engine_gpgsm_t gpgsm;
     248             :   const char *pgmname;
     249             :   const char *argv[5];
     250             :   int argc;
     251             : #if !USE_DESCRIPTOR_PASSING
     252             :   int fds[2];
     253             :   int child_fds[4];
     254             : #endif
     255           8 :   char *dft_display = NULL;
     256             :   char dft_ttyname[64];
     257           8 :   char *env_tty = NULL;
     258           8 :   char *dft_ttytype = NULL;
     259             :   char *optstr;
     260             : 
     261             :   (void)version; /* Not yet used.  */
     262             : 
     263           8 :   gpgsm = calloc (1, sizeof *gpgsm);
     264           8 :   if (!gpgsm)
     265           0 :     return gpg_error_from_syserror ();
     266             : 
     267           8 :   gpgsm->status_cb.fd = -1;
     268           8 :   gpgsm->status_cb.dir = 1;
     269           8 :   gpgsm->status_cb.tag = 0;
     270           8 :   gpgsm->status_cb.data = gpgsm;
     271             : 
     272           8 :   gpgsm->input_cb.fd = -1;
     273           8 :   gpgsm->input_cb.dir = 0;
     274           8 :   gpgsm->input_cb.tag = 0;
     275           8 :   gpgsm->input_cb.server_fd = -1;
     276           8 :   *gpgsm->input_cb.server_fd_str = 0;
     277           8 :   gpgsm->output_cb.fd = -1;
     278           8 :   gpgsm->output_cb.dir = 1;
     279           8 :   gpgsm->output_cb.tag = 0;
     280           8 :   gpgsm->output_cb.server_fd = -1;
     281           8 :   *gpgsm->output_cb.server_fd_str = 0;
     282           8 :   gpgsm->message_cb.fd = -1;
     283           8 :   gpgsm->message_cb.dir = 0;
     284           8 :   gpgsm->message_cb.tag = 0;
     285           8 :   gpgsm->message_cb.server_fd = -1;
     286           8 :   *gpgsm->message_cb.server_fd_str = 0;
     287             : 
     288           8 :   gpgsm->status.fnc = 0;
     289           8 :   gpgsm->colon.fnc = 0;
     290           8 :   gpgsm->colon.attic.line = 0;
     291           8 :   gpgsm->colon.attic.linesize = 0;
     292           8 :   gpgsm->colon.attic.linelen = 0;
     293           8 :   gpgsm->colon.any = 0;
     294             : 
     295           8 :   gpgsm->inline_data = NULL;
     296             : 
     297           8 :   gpgsm->io_cbs.add = NULL;
     298           8 :   gpgsm->io_cbs.add_priv = NULL;
     299           8 :   gpgsm->io_cbs.remove = NULL;
     300           8 :   gpgsm->io_cbs.event = NULL;
     301           8 :   gpgsm->io_cbs.event_priv = NULL;
     302             : 
     303             : #if !USE_DESCRIPTOR_PASSING
     304             :   if (_gpgme_io_pipe (fds, 0) < 0)
     305             :     {
     306             :       err = gpg_error_from_syserror ();
     307             :       goto leave;
     308             :     }
     309             :   gpgsm->input_cb.fd = fds[1];
     310             :   gpgsm->input_cb.server_fd = fds[0];
     311             : 
     312             :   if (_gpgme_io_pipe (fds, 1) < 0)
     313             :     {
     314             :       err = gpg_error_from_syserror ();
     315             :       goto leave;
     316             :     }
     317             :   gpgsm->output_cb.fd = fds[0];
     318             :   gpgsm->output_cb.server_fd = fds[1];
     319             : 
     320             :   if (_gpgme_io_pipe (fds, 0) < 0)
     321             :     {
     322             :       err = gpg_error_from_syserror ();
     323             :       goto leave;
     324             :     }
     325             :   gpgsm->message_cb.fd = fds[1];
     326             :   gpgsm->message_cb.server_fd = fds[0];
     327             : 
     328             :   child_fds[0] = gpgsm->input_cb.server_fd;
     329             :   child_fds[1] = gpgsm->output_cb.server_fd;
     330             :   child_fds[2] = gpgsm->message_cb.server_fd;
     331             :   child_fds[3] = -1;
     332             : #endif
     333             : 
     334           8 :   pgmname = file_name ? file_name : _gpgme_get_default_gpgsm_name ();
     335             : 
     336           8 :   argc = 0;
     337           8 :   argv[argc++] = _gpgme_get_basename (pgmname);
     338           8 :   if (home_dir)
     339             :     {
     340           0 :       argv[argc++] = "--homedir";
     341           0 :       argv[argc++] = home_dir;
     342             :     }
     343           8 :   argv[argc++] = "--server";
     344           8 :   argv[argc++] = NULL;
     345             : 
     346           8 :   err = assuan_new_ext (&gpgsm->assuan_ctx, GPG_ERR_SOURCE_GPGME,
     347             :                         &_gpgme_assuan_malloc_hooks, _gpgme_assuan_log_cb,
     348             :                         NULL);
     349           8 :   if (err)
     350           0 :     goto leave;
     351           8 :   assuan_ctx_set_system_hooks (gpgsm->assuan_ctx, &_gpgme_assuan_system_hooks);
     352             : 
     353             : #if USE_DESCRIPTOR_PASSING
     354           8 :   err = assuan_pipe_connect (gpgsm->assuan_ctx, pgmname, argv,
     355             :                              NULL, NULL, NULL, ASSUAN_PIPE_CONNECT_FDPASSING);
     356             : #else
     357             :   {
     358             :     assuan_fd_t achild_fds[4];
     359             :     int i;
     360             : 
     361             :     /* For now... */
     362             :     for (i = 0; i < 4; i++)
     363             :       achild_fds[i] = (assuan_fd_t) child_fds[i];
     364             : 
     365             :     err = assuan_pipe_connect (gpgsm->assuan_ctx, pgmname, argv,
     366             :                                achild_fds, NULL, NULL, 0);
     367             : 
     368             :     /* For now... */
     369             :     for (i = 0; i < 4; i++)
     370             :       child_fds[i] = (int) achild_fds[i];
     371             :   }
     372             : 
     373             :   /* On Windows, handles are inserted in the spawned process with
     374             :      DuplicateHandle, and child_fds contains the server-local names
     375             :      for the inserted handles when assuan_pipe_connect returns.  */
     376             :   if (!err)
     377             :     {
     378             :       /* Note: We don't use _gpgme_io_fd2str here.  On W32 the
     379             :          returned handles are real W32 system handles, not whatever
     380             :          GPGME uses internally (which may be a system handle, a C
     381             :          library handle or a GLib/Qt channel.  Confusing, yes, but
     382             :          remember these are server-local names, so they are not part
     383             :          of GPGME at all.  */
     384             :       snprintf (gpgsm->input_cb.server_fd_str,
     385             :                 sizeof gpgsm->input_cb.server_fd_str, "%d", child_fds[0]);
     386             :       snprintf (gpgsm->output_cb.server_fd_str,
     387             :                 sizeof gpgsm->output_cb.server_fd_str, "%d", child_fds[1]);
     388             :       snprintf (gpgsm->message_cb.server_fd_str,
     389             :                 sizeof gpgsm->message_cb.server_fd_str, "%d", child_fds[2]);
     390             :     }
     391             : #endif
     392           8 :   if (err)
     393           0 :     goto leave;
     394             : 
     395           8 :   err = _gpgme_getenv ("DISPLAY", &dft_display);
     396           8 :   if (err)
     397           0 :     goto leave;
     398           8 :   if (dft_display)
     399             :     {
     400           8 :       if (gpgrt_asprintf (&optstr, "OPTION display=%s", dft_display) < 0)
     401             :         {
     402           0 :           free (dft_display);
     403           0 :           err = gpg_error_from_syserror ();
     404           0 :           goto leave;
     405             :         }
     406           8 :       free (dft_display);
     407             : 
     408           8 :       err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL,
     409             :                              NULL, NULL, NULL);
     410           8 :       gpgrt_free (optstr);
     411           8 :       if (err)
     412           0 :         goto leave;
     413             :     }
     414             : 
     415           8 :   err = _gpgme_getenv ("GPG_TTY", &env_tty);
     416           8 :   if (isatty (1) || env_tty || err)
     417             :     {
     418           8 :       int rc = 0;
     419             : 
     420           8 :       if (err)
     421           0 :         goto leave;
     422           8 :       else if (env_tty)
     423             :         {
     424           0 :           snprintf (dft_ttyname, sizeof (dft_ttyname), "%s", env_tty);
     425           0 :           free (env_tty);
     426             :         }
     427             :       else
     428           8 :         rc = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname));
     429             : 
     430             :       /* Even though isatty() returns 1, ttyname_r() may fail in many
     431             :          ways, e.g., when /dev/pts is not accessible under chroot.  */
     432           8 :       if (!rc)
     433             :         {
     434           8 :           if (gpgrt_asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0)
     435             :             {
     436           0 :               err = gpg_error_from_syserror ();
     437           0 :               goto leave;
     438             :             }
     439           8 :           err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL,
     440             :                                  NULL, NULL, NULL);
     441           8 :           gpgrt_free (optstr);
     442           8 :           if (err)
     443           0 :             goto leave;
     444             : 
     445           8 :           err = _gpgme_getenv ("TERM", &dft_ttytype);
     446           8 :           if (err)
     447           0 :             goto leave;
     448           8 :           if (dft_ttytype)
     449             :             {
     450           8 :               if (gpgrt_asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype)< 0)
     451             :                 {
     452           0 :                   free (dft_ttytype);
     453           0 :                   err = gpg_error_from_syserror ();
     454           0 :                   goto leave;
     455             :                 }
     456           8 :               free (dft_ttytype);
     457             : 
     458           8 :               err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL,
     459             :                                      NULL, NULL, NULL, NULL);
     460           8 :               gpgrt_free (optstr);
     461           8 :               if (err)
     462           0 :                 goto leave;
     463             :             }
     464             :         }
     465             :     }
     466             : 
     467             :   /* Ask gpgsm to enable the audit log support.  */
     468           8 :   if (!err)
     469             :     {
     470           8 :       err = assuan_transact (gpgsm->assuan_ctx, "OPTION enable-audit-log=1",
     471             :                              NULL, NULL, NULL, NULL, NULL, NULL);
     472           8 :       if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
     473           0 :         err = 0; /* This is an optional feature of gpgsm.  */
     474             :     }
     475             : 
     476             : 
     477             : #ifdef HAVE_W32_SYSTEM
     478             :   /* Under Windows we need to use AllowSetForegroundWindow.  Tell
     479             :      gpgsm to tell us when it needs it.  */
     480             :   if (!err)
     481             :     {
     482             :       err = assuan_transact (gpgsm->assuan_ctx, "OPTION allow-pinentry-notify",
     483             :                              NULL, NULL, NULL, NULL, NULL, NULL);
     484             :       if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
     485             :         err = 0; /* This is a new feature of gpgsm.  */
     486             :     }
     487             : #endif /*HAVE_W32_SYSTEM*/
     488             : 
     489             : #if !USE_DESCRIPTOR_PASSING
     490             :   if (!err
     491             :       && (_gpgme_io_set_close_notify (gpgsm->input_cb.fd,
     492             :                                       close_notify_handler, gpgsm)
     493             :           || _gpgme_io_set_close_notify (gpgsm->output_cb.fd,
     494             :                                          close_notify_handler, gpgsm)
     495             :           || _gpgme_io_set_close_notify (gpgsm->message_cb.fd,
     496             :                                          close_notify_handler, gpgsm)))
     497             :     {
     498             :       err = gpg_error (GPG_ERR_GENERAL);
     499             :       goto leave;
     500             :     }
     501             : #endif
     502             : 
     503             :  leave:
     504             :   /* Close the server ends of the pipes (because of this, we must use
     505             :      the stored server_fd_str in the function start).  Our ends are
     506             :      closed in gpgsm_release().  */
     507             : #if !USE_DESCRIPTOR_PASSING
     508             :   if (gpgsm->input_cb.server_fd != -1)
     509             :     _gpgme_io_close (gpgsm->input_cb.server_fd);
     510             :   if (gpgsm->output_cb.server_fd != -1)
     511             :     _gpgme_io_close (gpgsm->output_cb.server_fd);
     512             :   if (gpgsm->message_cb.server_fd != -1)
     513             :     _gpgme_io_close (gpgsm->message_cb.server_fd);
     514             : #endif
     515             : 
     516           8 :   if (err)
     517           0 :     gpgsm_release (gpgsm);
     518             :   else
     519           8 :     *engine = gpgsm;
     520             : 
     521           8 :   return err;
     522             : }
     523             : 
     524             : 
     525             : /* Copy flags from CTX into the engine object.  */
     526             : static void
     527          12 : gpgsm_set_engine_flags (void *engine, const gpgme_ctx_t ctx)
     528             : {
     529          12 :   engine_gpgsm_t gpgsm = engine;
     530             : 
     531          12 :   if (ctx->request_origin)
     532             :     {
     533           0 :       if (strlen (ctx->request_origin) + 1 > sizeof gpgsm->request_origin)
     534           0 :         strcpy (gpgsm->request_origin, "xxx"); /* Too long  - force error */
     535             :       else
     536           0 :         strcpy (gpgsm->request_origin, ctx->request_origin);
     537             :     }
     538             :   else
     539          12 :     *gpgsm->request_origin = 0;
     540          12 : }
     541             : 
     542             : 
     543             : static gpgme_error_t
     544          24 : gpgsm_set_locale (void *engine, int category, const char *value)
     545             : {
     546          24 :   engine_gpgsm_t gpgsm = engine;
     547             :   gpgme_error_t err;
     548             :   char *optstr;
     549             :   const char *catstr;
     550             : 
     551             :   /* FIXME: If value is NULL, we need to reset the option to default.
     552             :      But we can't do this.  So we error out here.  GPGSM needs support
     553             :      for this.  */
     554             :   if (0)
     555             :     ;
     556             : #ifdef LC_CTYPE
     557          24 :   else if (category == LC_CTYPE)
     558             :     {
     559          12 :       catstr = "lc-ctype";
     560          12 :       if (!value && gpgsm->lc_ctype_set)
     561           0 :         return gpg_error (GPG_ERR_INV_VALUE);
     562          12 :       if (value)
     563          12 :         gpgsm->lc_ctype_set = 1;
     564             :     }
     565             : #endif
     566             : #ifdef LC_MESSAGES
     567          12 :   else if (category == LC_MESSAGES)
     568             :     {
     569          12 :       catstr = "lc-messages";
     570          12 :       if (!value && gpgsm->lc_messages_set)
     571           0 :         return gpg_error (GPG_ERR_INV_VALUE);
     572          12 :       if (value)
     573          12 :         gpgsm->lc_messages_set = 1;
     574             :     }
     575             : #endif /* LC_MESSAGES */
     576             :   else
     577           0 :     return gpg_error (GPG_ERR_INV_VALUE);
     578             : 
     579             :   /* FIXME: Reset value to default.  */
     580          24 :   if (!value)
     581           0 :     return 0;
     582             : 
     583          24 :   if (gpgrt_asprintf (&optstr, "OPTION %s=%s", catstr, value) < 0)
     584           0 :     err = gpg_error_from_syserror ();
     585             :   else
     586             :     {
     587          24 :       err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL,
     588             :                              NULL, NULL, NULL, NULL);
     589          24 :       gpgrt_free (optstr);
     590             :     }
     591             : 
     592          24 :   return err;
     593             : }
     594             : 
     595             : 
     596             : static gpgme_error_t
     597          33 : gpgsm_assuan_simple_command (engine_gpgsm_t gpgsm, const char *cmd,
     598             :                              engine_status_handler_t status_fnc,
     599             :                              void *status_fnc_value)
     600             : {
     601          33 :   assuan_context_t ctx = gpgsm->assuan_ctx;
     602             :   gpg_error_t err, cb_err;
     603             :   char *line;
     604             :   size_t linelen;
     605             : 
     606          33 :   err = assuan_write_line (ctx, cmd);
     607          33 :   if (err)
     608           0 :     return err;
     609             : 
     610          33 :   cb_err = 0;
     611             :   do
     612             :     {
     613          33 :       err = assuan_read_line (ctx, &line, &linelen);
     614          33 :       if (err)
     615           0 :         break;
     616             : 
     617          33 :       if (*line == '#' || !linelen)
     618           0 :         continue;
     619             : 
     620          33 :       if (linelen >= 2
     621          33 :           && line[0] == 'O' && line[1] == 'K'
     622          33 :           && (line[2] == '\0' || line[2] == ' '))
     623             :         break;
     624           0 :       else if (linelen >= 4
     625           0 :           && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
     626           0 :           && line[3] == ' ')
     627             :         {
     628             :           /* We prefer a callback generated error because that one is
     629             :              more related to gpgme and thus probably more important
     630             :              than the error returned by the engine.  */
     631           0 :           err = cb_err? cb_err : atoi (&line[4]);
     632           0 :           cb_err = 0;
     633             :         }
     634           0 :       else if (linelen >= 2
     635           0 :                && line[0] == 'S' && line[1] == ' ')
     636             :         {
     637             :           /* After an error from a status callback we skip all further
     638             :              status lines.  */
     639           0 :           if (!cb_err)
     640             :             {
     641             :               char *rest;
     642             :               gpgme_status_code_t r;
     643             : 
     644           0 :               rest = strchr (line + 2, ' ');
     645           0 :               if (!rest)
     646           0 :                 rest = line + linelen; /* set to an empty string */
     647             :               else
     648           0 :                 *(rest++) = 0;
     649             : 
     650           0 :               r = _gpgme_parse_status (line + 2);
     651           0 :               if (gpgsm->status.mon_cb && r != GPGME_STATUS_PROGRESS)
     652             :                 {
     653             :                   /* Note that we call the monitor even if we do
     654             :                    * not know the status code (r < 0).  */
     655           0 :                   cb_err = gpgsm->status.mon_cb (gpgsm->status.mon_cb_value,
     656           0 :                                                  line + 2, rest);
     657             :                 }
     658             : 
     659           0 :               if (r >= 0 && status_fnc && !cb_err)
     660           0 :                 cb_err = status_fnc (status_fnc_value, r, rest);
     661             :             }
     662             :         }
     663             :       else
     664             :         {
     665             :           /* Invalid line or INQUIRY.  We can't do anything else than
     666             :              to stop.  As with ERR we prefer a status callback
     667             :              generated error code, though.  */
     668           0 :           err = cb_err ? cb_err : gpg_error (GPG_ERR_GENERAL);
     669           0 :           cb_err = 0;
     670             :         }
     671             :     }
     672           0 :   while (!err);
     673             : 
     674             :   /* We only want the first error from the status handler, thus we
     675             :    * take the one saved in CB_ERR. */
     676          33 :   if (!err && cb_err)
     677           0 :     err = cb_err;
     678             : 
     679          33 :   return err;
     680             : }
     681             : 
     682             : 
     683             : typedef enum { INPUT_FD, OUTPUT_FD, MESSAGE_FD } fd_type_t;
     684             : 
     685             : static void
     686          24 : gpgsm_clear_fd (engine_gpgsm_t gpgsm, fd_type_t fd_type)
     687             : {
     688             : #if !USE_DESCRIPTOR_PASSING
     689             :   switch (fd_type)
     690             :     {
     691             :     case INPUT_FD:
     692             :       _gpgme_io_close (gpgsm->input_cb.fd);
     693             :       break;
     694             :     case OUTPUT_FD:
     695             :       _gpgme_io_close (gpgsm->output_cb.fd);
     696             :       break;
     697             :     case MESSAGE_FD:
     698             :       _gpgme_io_close (gpgsm->message_cb.fd);
     699             :       break;
     700             :     }
     701             : #else
     702             :   (void)gpgsm;
     703             :   (void)fd_type;
     704             : #endif
     705          24 : }
     706             : 
     707             : #define COMMANDLINELEN 40
     708             : static gpgme_error_t
     709          18 : gpgsm_set_fd (engine_gpgsm_t gpgsm, fd_type_t fd_type, const char *opt)
     710             : {
     711          18 :   gpg_error_t err = 0;
     712             :   char line[COMMANDLINELEN];
     713             :   const char *which;
     714             :   iocb_data_t *iocb_data;
     715             : #if USE_DESCRIPTOR_PASSING
     716             :   int dir;
     717             : #endif
     718             : 
     719          18 :   switch (fd_type)
     720             :     {
     721             :     case INPUT_FD:
     722           8 :       which = "INPUT";
     723           8 :       iocb_data = &gpgsm->input_cb;
     724           8 :       break;
     725             : 
     726             :     case OUTPUT_FD:
     727           8 :       which = "OUTPUT";
     728           8 :       iocb_data = &gpgsm->output_cb;
     729           8 :       break;
     730             : 
     731             :     case MESSAGE_FD:
     732           2 :       which = "MESSAGE";
     733           2 :       iocb_data = &gpgsm->message_cb;
     734           2 :       break;
     735             : 
     736             :     default:
     737           0 :       return gpg_error (GPG_ERR_INV_VALUE);
     738             :     }
     739             : 
     740             : #if USE_DESCRIPTOR_PASSING
     741          18 :   dir = iocb_data->dir;
     742             :   /* We try to short-cut the communication by giving GPGSM direct
     743             :      access to the file descriptor, rather than using a pipe.  */
     744          18 :   iocb_data->server_fd = _gpgme_data_get_fd (iocb_data->data);
     745          18 :   if (iocb_data->server_fd < 0)
     746             :     {
     747             :       int fds[2];
     748             : 
     749          18 :       if (_gpgme_io_pipe (fds, dir) < 0)
     750           0 :         return gpg_error_from_syserror ();
     751             : 
     752          18 :       iocb_data->fd = dir ? fds[0] : fds[1];
     753          18 :       iocb_data->server_fd = dir ? fds[1] : fds[0];
     754             : 
     755          18 :       if (_gpgme_io_set_close_notify (iocb_data->fd,
     756             :                                       close_notify_handler, gpgsm))
     757             :         {
     758           0 :           err = gpg_error (GPG_ERR_GENERAL);
     759           0 :           goto leave_set_fd;
     760             :         }
     761             :     }
     762             : 
     763          18 :   err = assuan_sendfd (gpgsm->assuan_ctx, iocb_data->server_fd);
     764          18 :   if (err)
     765           0 :     goto leave_set_fd;
     766             : 
     767          18 :   _gpgme_io_close (iocb_data->server_fd);
     768          18 :   iocb_data->server_fd = -1;
     769             : 
     770          18 :   if (opt)
     771           5 :     snprintf (line, COMMANDLINELEN, "%s FD %s", which, opt);
     772             :   else
     773          13 :     snprintf (line, COMMANDLINELEN, "%s FD", which);
     774             : #else
     775             :   if (opt)
     776             :     snprintf (line, COMMANDLINELEN, "%s FD=%s %s",
     777             :               which, iocb_data->server_fd_str, opt);
     778             :   else
     779             :     snprintf (line, COMMANDLINELEN, "%s FD=%s",
     780             :               which, iocb_data->server_fd_str);
     781             : #endif
     782             : 
     783          18 :   err = gpgsm_assuan_simple_command (gpgsm, line, NULL, NULL);
     784             : 
     785             : #if USE_DESCRIPTOR_PASSING
     786             :  leave_set_fd:
     787          18 :   if (err)
     788             :     {
     789           0 :       _gpgme_io_close (iocb_data->fd);
     790           0 :       iocb_data->fd = -1;
     791           0 :       if (iocb_data->server_fd != -1)
     792             :         {
     793           0 :           _gpgme_io_close (iocb_data->server_fd);
     794           0 :           iocb_data->server_fd = -1;
     795             :         }
     796             :     }
     797             : #endif
     798             : 
     799          18 :   return err;
     800             : }
     801             : 
     802             : 
     803             : static const char *
     804           8 : map_data_enc (gpgme_data_t d)
     805             : {
     806           8 :   switch (gpgme_data_get_encoding (d))
     807             :     {
     808             :     case GPGME_DATA_ENCODING_NONE:
     809           8 :       break;
     810             :     case GPGME_DATA_ENCODING_BINARY:
     811           0 :       return "--binary";
     812             :     case GPGME_DATA_ENCODING_BASE64:
     813           0 :       return "--base64";
     814             :     case GPGME_DATA_ENCODING_ARMOR:
     815           0 :       return "--armor";
     816             :     default:
     817           0 :       break;
     818             :     }
     819           8 :   return NULL;
     820             : }
     821             : 
     822             : 
     823             : static gpgme_error_t
     824          25 : status_handler (void *opaque, int fd)
     825             : {
     826          25 :   struct io_cb_data *data = (struct io_cb_data *) opaque;
     827          25 :   engine_gpgsm_t gpgsm = (engine_gpgsm_t) data->handler_value;
     828          25 :   gpgme_error_t err = 0;
     829             :   char *line;
     830             :   size_t linelen;
     831             : 
     832             :   do
     833             :     {
     834          34 :       err = assuan_read_line (gpgsm->assuan_ctx, &line, &linelen);
     835          34 :       if (err)
     836             :         {
     837             :           /* Try our best to terminate the connection friendly.  */
     838             :           /*      assuan_write_line (gpgsm->assuan_ctx, "BYE"); */
     839           0 :           TRACE3 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
     840             :                   "fd 0x%x: error from assuan (%d) getting status line : %s",
     841             :                   fd, err, gpg_strerror (err));
     842             :         }
     843          34 :       else if (linelen >= 3
     844          20 :                && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
     845           0 :                && (line[3] == '\0' || line[3] == ' '))
     846             :         {
     847           0 :           if (line[3] == ' ')
     848           0 :             err = atoi (&line[4]);
     849           0 :           if (! err)
     850           0 :             err = gpg_error (GPG_ERR_GENERAL);
     851           0 :           TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
     852             :                   "fd 0x%x: ERR line - mapped to: %s",
     853             :                   fd, err ? gpg_strerror (err) : "ok");
     854             :           /* Try our best to terminate the connection friendly.  */
     855             :           /*      assuan_write_line (gpgsm->assuan_ctx, "BYE"); */
     856             :         }
     857          34 :       else if (linelen >= 2
     858          34 :                && line[0] == 'O' && line[1] == 'K'
     859          14 :                && (line[2] == '\0' || line[2] == ' '))
     860             :         {
     861          14 :           if (gpgsm->status.fnc)
     862             :             {
     863          14 :               char emptystring[1] = {0};
     864          14 :               err = gpgsm->status.fnc (gpgsm->status.fnc_value,
     865             :                                        GPGME_STATUS_EOF, emptystring);
     866          14 :               if (gpg_err_code (err) == GPG_ERR_FALSE)
     867           0 :                 err = 0; /* Drop special error code.  */
     868             :             }
     869             : 
     870          14 :           if (!err && gpgsm->colon.fnc && gpgsm->colon.any)
     871             :             {
     872             :               /* We must tell a colon function about the EOF. We do
     873             :                  this only when we have seen any data lines.  Note
     874             :                  that this inlined use of colon data lines will
     875             :                  eventually be changed into using a regular data
     876             :                  channel. */
     877           2 :               gpgsm->colon.any = 0;
     878           2 :               err = gpgsm->colon.fnc (gpgsm->colon.fnc_value, NULL);
     879             :             }
     880          14 :           TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
     881             :                   "fd 0x%x: OK line - final status: %s",
     882             :                   fd, err ? gpg_strerror (err) : "ok");
     883          14 :           _gpgme_io_close (gpgsm->status_cb.fd);
     884          14 :           return err;
     885             :         }
     886          20 :       else if (linelen > 2
     887          20 :                && line[0] == 'D' && line[1] == ' '
     888           3 :                && gpgsm->colon.fnc)
     889           3 :         {
     890             :           /* We are using the colon handler even for plain inline data
     891             :              - strange name for that function but for historic reasons
     892             :              we keep it.  */
     893             :           /* FIXME We can't use this for binary data because we
     894             :              assume this is a string.  For the current usage of colon
     895             :              output it is correct.  */
     896           3 :           char *src = line + 2;
     897           3 :           char *end = line + linelen;
     898             :           char *dst;
     899           3 :           char **aline = &gpgsm->colon.attic.line;
     900           3 :           int *alinelen = &gpgsm->colon.attic.linelen;
     901             : 
     902           3 :           if (gpgsm->colon.attic.linesize < *alinelen + linelen + 1)
     903             :             {
     904           2 :               char *newline = realloc (*aline, *alinelen + linelen + 1);
     905           2 :               if (!newline)
     906           0 :                 err = gpg_error_from_syserror ();
     907             :               else
     908             :                 {
     909           2 :                   *aline = newline;
     910           2 :                   gpgsm->colon.attic.linesize = *alinelen + linelen + 1;
     911             :                 }
     912             :             }
     913           3 :           if (!err)
     914             :             {
     915           3 :               dst = *aline + *alinelen;
     916             : 
     917        1908 :               while (!err && src < end)
     918             :                 {
     919        1902 :                   if (*src == '%' && src + 2 < end)
     920             :                     {
     921             :                       /* Handle escaped characters.  */
     922          18 :                       ++src;
     923          18 :                       *dst = _gpgme_hextobyte (src);
     924          18 :                       (*alinelen)++;
     925          18 :                       src += 2;
     926             :                     }
     927             :                   else
     928             :                     {
     929        1884 :                       *dst = *src++;
     930        1884 :                       (*alinelen)++;
     931             :                     }
     932             : 
     933        1902 :                   if (*dst == '\n')
     934             :                     {
     935             :                       /* Terminate the pending line, pass it to the colon
     936             :                          handler and reset it.  */
     937             : 
     938          18 :                       gpgsm->colon.any = 1;
     939          18 :                       if (*alinelen > 1 && *(dst - 1) == '\r')
     940           0 :                         dst--;
     941          18 :                       *dst = '\0';
     942             : 
     943             :                       /* FIXME How should we handle the return code?  */
     944          18 :                       err = gpgsm->colon.fnc (gpgsm->colon.fnc_value, *aline);
     945          18 :                       if (!err)
     946             :                         {
     947          18 :                           dst = *aline;
     948          18 :                           *alinelen = 0;
     949             :                         }
     950             :                     }
     951             :                   else
     952        1884 :                     dst++;
     953             :                 }
     954             :             }
     955           3 :           TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
     956             :                   "fd 0x%x: D line; final status: %s",
     957             :                   fd, err? gpg_strerror (err):"ok");
     958             :         }
     959          17 :       else if (linelen > 2
     960          17 :                && line[0] == 'D' && line[1] == ' '
     961           0 :                && gpgsm->inline_data)
     962           0 :         {
     963           0 :           char *src = line + 2;
     964           0 :           char *end = line + linelen;
     965           0 :           char *dst = src;
     966             :           gpgme_ssize_t nwritten;
     967             : 
     968           0 :           linelen = 0;
     969           0 :           while (src < end)
     970             :             {
     971           0 :               if (*src == '%' && src + 2 < end)
     972             :                 {
     973             :                   /* Handle escaped characters.  */
     974           0 :                   ++src;
     975           0 :                   *dst++ = _gpgme_hextobyte (src);
     976           0 :                   src += 2;
     977             :                 }
     978             :               else
     979           0 :                 *dst++ = *src++;
     980             : 
     981           0 :               linelen++;
     982             :             }
     983             : 
     984           0 :           src = line + 2;
     985           0 :           while (linelen > 0)
     986             :             {
     987           0 :               nwritten = gpgme_data_write (gpgsm->inline_data, src, linelen);
     988           0 :               if (nwritten <= 0 || nwritten > linelen)
     989             :                 {
     990           0 :                   err = gpg_error_from_syserror ();
     991           0 :                   break;
     992             :                 }
     993           0 :               src += nwritten;
     994           0 :               linelen -= nwritten;
     995             :             }
     996             : 
     997           0 :           TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
     998             :                   "fd 0x%x: D inlinedata; final status: %s",
     999             :                   fd, err? gpg_strerror (err):"ok");
    1000             :         }
    1001          17 :       else if (linelen > 2
    1002          17 :                && line[0] == 'S' && line[1] == ' ')
    1003          17 :         {
    1004             :           char *rest;
    1005             :           gpgme_status_code_t r;
    1006             : 
    1007          17 :           rest = strchr (line + 2, ' ');
    1008          17 :           if (!rest)
    1009           3 :             rest = line + linelen; /* set to an empty string */
    1010             :           else
    1011          14 :             *(rest++) = 0;
    1012             : 
    1013          17 :           r = _gpgme_parse_status (line + 2);
    1014          17 :           if (gpgsm->status.mon_cb && r != GPGME_STATUS_PROGRESS)
    1015             :             {
    1016             :               /* Note that we call the monitor even if we do
    1017             :                * not know the status code (r < 0).  */
    1018           0 :               err = gpgsm->status.mon_cb (gpgsm->status.mon_cb_value,
    1019           0 :                                           line + 2, rest);
    1020             :             }
    1021             :           else
    1022          17 :             err = 0;
    1023             : 
    1024          17 :           if (r >= 0 && !err)
    1025             :             {
    1026          17 :               if (gpgsm->status.fnc)
    1027             :                 {
    1028          17 :                   err = gpgsm->status.fnc (gpgsm->status.fnc_value, r, rest);
    1029          17 :                   if (gpg_err_code (err) == GPG_ERR_FALSE)
    1030           0 :                     err = 0; /* Drop special error code.  */
    1031             :                 }
    1032             :             }
    1033             :           else
    1034           0 :             fprintf (stderr, "[UNKNOWN STATUS]%s %s", line + 2, rest);
    1035          17 :           TRACE3 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
    1036             :                   "fd 0x%x: S line (%s) - final status: %s",
    1037             :                   fd, line+2, err? gpg_strerror (err):"ok");
    1038             :         }
    1039           0 :       else if (linelen >= 7
    1040           0 :                && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
    1041           0 :                && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
    1042           0 :                && line[6] == 'E'
    1043           0 :                && (line[7] == '\0' || line[7] == ' '))
    1044             :         {
    1045           0 :           char *keyword = line+7;
    1046             : 
    1047           0 :           while (*keyword == ' ')
    1048           0 :             keyword++;;
    1049           0 :           default_inq_cb (gpgsm, keyword);
    1050           0 :           assuan_write_line (gpgsm->assuan_ctx, "END");
    1051             :         }
    1052             : 
    1053             :     }
    1054          20 :   while (!err && assuan_pending_line (gpgsm->assuan_ctx));
    1055             : 
    1056          11 :   return err;
    1057             : }
    1058             : 
    1059             : 
    1060             : static gpgme_error_t
    1061          32 : add_io_cb (engine_gpgsm_t gpgsm, iocb_data_t *iocbd, gpgme_io_cb_t handler)
    1062             : {
    1063             :   gpgme_error_t err;
    1064             : 
    1065          32 :   TRACE_BEG2 (DEBUG_ENGINE, "engine-gpgsm:add_io_cb", gpgsm,
    1066             :               "fd %d, dir %d", iocbd->fd, iocbd->dir);
    1067          32 :   err = (*gpgsm->io_cbs.add) (gpgsm->io_cbs.add_priv,
    1068             :                               iocbd->fd, iocbd->dir,
    1069             :                               handler, iocbd->data, &iocbd->tag);
    1070          32 :   if (err)
    1071           0 :     return TRACE_ERR (err);
    1072          32 :   if (!iocbd->dir)
    1073             :     /* FIXME Kludge around poll() problem.  */
    1074          10 :     err = _gpgme_io_set_nonblocking (iocbd->fd);
    1075          32 :   return TRACE_ERR (err);
    1076             : }
    1077             : 
    1078             : 
    1079             : static gpgme_error_t
    1080          14 : start (engine_gpgsm_t gpgsm, const char *command)
    1081             : {
    1082             :   gpgme_error_t err;
    1083             :   assuan_fd_t afdlist[5];
    1084             :   int fdlist[5];
    1085             :   int nfds;
    1086             :   int i;
    1087             : 
    1088          14 :   if (*gpgsm->request_origin)
    1089             :     {
    1090             :       char *cmd;
    1091             : 
    1092           0 :       cmd = _gpgme_strconcat ("OPTION request-origin=",
    1093           0 :                               gpgsm->request_origin, NULL);
    1094           0 :       if (!cmd)
    1095           0 :         return gpg_error_from_syserror ();
    1096           0 :       err = gpgsm_assuan_simple_command (gpgsm, cmd, NULL, NULL);
    1097           0 :       free (cmd);
    1098           0 :       if (err && gpg_err_code (err) != GPG_ERR_UNKNOWN_OPTION)
    1099           0 :         return err;
    1100             :     }
    1101             : 
    1102             :   /* We need to know the fd used by assuan for reads.  We do this by
    1103             :      using the assumption that the first returned fd from
    1104             :      assuan_get_active_fds() is always this one.  */
    1105          14 :   nfds = assuan_get_active_fds (gpgsm->assuan_ctx, 0 /* read fds */,
    1106             :                                 afdlist, DIM (afdlist));
    1107          14 :   if (nfds < 1)
    1108           0 :     return gpg_error (GPG_ERR_GENERAL); /* FIXME */
    1109             :   /* For now... */
    1110          28 :   for (i = 0; i < nfds; i++)
    1111          14 :     fdlist[i] = (int) afdlist[i];
    1112             : 
    1113             :   /* We "duplicate" the file descriptor, so we can close it here (we
    1114             :      can't close fdlist[0], as that is closed by libassuan, and
    1115             :      closing it here might cause libassuan to close some unrelated FD
    1116             :      later).  Alternatively, we could special case status_fd and
    1117             :      register/unregister it manually as needed, but this increases
    1118             :      code duplication and is more complicated as we can not use the
    1119             :      close notifications etc.  A third alternative would be to let
    1120             :      Assuan know that we closed the FD, but that complicates the
    1121             :      Assuan interface.  */
    1122             : 
    1123          14 :   gpgsm->status_cb.fd = _gpgme_io_dup (fdlist[0]);
    1124          14 :   if (gpgsm->status_cb.fd < 0)
    1125           0 :     return gpg_error_from_syserror ();
    1126             : 
    1127          14 :   if (_gpgme_io_set_close_notify (gpgsm->status_cb.fd,
    1128             :                                   close_notify_handler, gpgsm))
    1129             :     {
    1130           0 :       _gpgme_io_close (gpgsm->status_cb.fd);
    1131           0 :       gpgsm->status_cb.fd = -1;
    1132           0 :       return gpg_error (GPG_ERR_GENERAL);
    1133             :     }
    1134             : 
    1135          14 :   err = add_io_cb (gpgsm, &gpgsm->status_cb, status_handler);
    1136          14 :   if (!err && gpgsm->input_cb.fd != -1)
    1137           8 :     err = add_io_cb (gpgsm, &gpgsm->input_cb, _gpgme_data_outbound_handler);
    1138          14 :   if (!err && gpgsm->output_cb.fd != -1)
    1139           8 :     err = add_io_cb (gpgsm, &gpgsm->output_cb, _gpgme_data_inbound_handler);
    1140          14 :   if (!err && gpgsm->message_cb.fd != -1)
    1141           2 :     err = add_io_cb (gpgsm, &gpgsm->message_cb, _gpgme_data_outbound_handler);
    1142             : 
    1143          14 :   if (!err)
    1144          14 :     err = assuan_write_line (gpgsm->assuan_ctx, command);
    1145             : 
    1146          14 :   if (!err)
    1147          14 :     gpgsm_io_event (gpgsm, GPGME_EVENT_START, NULL);
    1148             : 
    1149          14 :   return err;
    1150             : }
    1151             : 
    1152             : 
    1153             : #if USE_DESCRIPTOR_PASSING
    1154             : static gpgme_error_t
    1155           4 : gpgsm_reset (void *engine)
    1156             : {
    1157           4 :   engine_gpgsm_t gpgsm = engine;
    1158             : 
    1159             :   /* IF we have an active connection we must send a reset because we
    1160             :      need to reset the list of signers.  Note that RESET does not
    1161             :      reset OPTION commands. */
    1162           4 :   return (gpgsm->assuan_ctx
    1163             :           ? gpgsm_assuan_simple_command (gpgsm, "RESET", NULL, NULL)
    1164           4 :           : 0);
    1165             : }
    1166             : #endif
    1167             : 
    1168             : 
    1169             : 
    1170             : static gpgme_error_t
    1171           1 : gpgsm_decrypt (void *engine,
    1172             :                gpgme_decrypt_flags_t flags,
    1173             :                gpgme_data_t ciph, gpgme_data_t plain,
    1174             :                int export_session_key, const char *override_session_key,
    1175             :                int auto_key_retrieve)
    1176             : {
    1177           1 :   engine_gpgsm_t gpgsm = engine;
    1178             :   gpgme_error_t err;
    1179             : 
    1180             :   (void)flags;
    1181             : 
    1182             :   /* gpgsm is not capable of exporting session keys right now, so we
    1183             :    * will ignore this if requested. */
    1184             :   (void)export_session_key;
    1185             :   (void)override_session_key;
    1186             : 
    1187             :   /* --auto-key-retrieve is also not supported.  */
    1188             :   (void)auto_key_retrieve;
    1189             : 
    1190           1 :   if (!gpgsm)
    1191           0 :     return gpg_error (GPG_ERR_INV_VALUE);
    1192             : 
    1193           1 :   gpgsm->input_cb.data = ciph;
    1194           1 :   err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
    1195           1 :   if (err)
    1196           0 :     return gpg_error (GPG_ERR_GENERAL); /* FIXME */
    1197           1 :   gpgsm->output_cb.data = plain;
    1198           1 :   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, 0);
    1199           1 :   if (err)
    1200           0 :     return gpg_error (GPG_ERR_GENERAL); /* FIXME */
    1201           1 :   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
    1202           1 :   gpgsm->inline_data = NULL;
    1203             : 
    1204           1 :   err = start (engine, "DECRYPT");
    1205           1 :   return err;
    1206             : }
    1207             : 
    1208             : 
    1209             : static gpgme_error_t
    1210           0 : gpgsm_delete (void *engine, gpgme_key_t key, unsigned int flags)
    1211             : {
    1212           0 :   engine_gpgsm_t gpgsm = engine;
    1213             :   gpgme_error_t err;
    1214           0 :   char *fpr = key->subkeys ? key->subkeys->fpr : NULL;
    1215           0 :   char *linep = fpr;
    1216             :   char *line;
    1217           0 :   int length = 8;       /* "DELKEYS " */
    1218             : 
    1219             :   (void)flags;
    1220             : 
    1221           0 :   if (!fpr)
    1222           0 :     return gpg_error (GPG_ERR_INV_VALUE);
    1223             : 
    1224           0 :   while (*linep)
    1225             :     {
    1226           0 :       length++;
    1227           0 :       if (*linep == '%' || *linep == ' ' || *linep == '+')
    1228           0 :         length += 2;
    1229           0 :       linep++;
    1230             :     }
    1231           0 :   length++;
    1232             : 
    1233           0 :   line = malloc (length);
    1234           0 :   if (!line)
    1235           0 :     return gpg_error_from_syserror ();
    1236             : 
    1237           0 :   strcpy (line, "DELKEYS ");
    1238           0 :   linep = &line[8];
    1239             : 
    1240           0 :   while (*fpr)
    1241             :     {
    1242           0 :       switch (*fpr)
    1243             :         {
    1244             :         case '%':
    1245           0 :           *(linep++) = '%';
    1246           0 :           *(linep++) = '2';
    1247           0 :           *(linep++) = '5';
    1248           0 :           break;
    1249             :         case ' ':
    1250           0 :           *(linep++) = '%';
    1251           0 :           *(linep++) = '2';
    1252           0 :           *(linep++) = '0';
    1253           0 :           break;
    1254             :         case '+':
    1255           0 :           *(linep++) = '%';
    1256           0 :           *(linep++) = '2';
    1257           0 :           *(linep++) = 'B';
    1258           0 :           break;
    1259             :         default:
    1260           0 :           *(linep++) = *fpr;
    1261           0 :           break;
    1262             :         }
    1263           0 :       fpr++;
    1264             :     }
    1265           0 :   *linep = '\0';
    1266             : 
    1267           0 :   gpgsm_clear_fd (gpgsm, OUTPUT_FD);
    1268           0 :   gpgsm_clear_fd (gpgsm, INPUT_FD);
    1269           0 :   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
    1270           0 :   gpgsm->inline_data = NULL;
    1271             : 
    1272           0 :   err = start (gpgsm, line);
    1273           0 :   free (line);
    1274             : 
    1275           0 :   return err;
    1276             : }
    1277             : 
    1278             : 
    1279             : static gpgme_error_t
    1280           1 : set_recipients (engine_gpgsm_t gpgsm, gpgme_key_t recp[])
    1281             : {
    1282           1 :   gpgme_error_t err = 0;
    1283             :   char *line;
    1284             :   int linelen;
    1285           1 :   int invalid_recipients = 0;
    1286             :   int i;
    1287             : 
    1288           1 :   linelen = 10 + 40 + 1;        /* "RECIPIENT " + guess + '\0'.  */
    1289           1 :   line = malloc (10 + 40 + 1);
    1290           1 :   if (!line)
    1291           0 :     return gpg_error_from_syserror ();
    1292           1 :   strcpy (line, "RECIPIENT ");
    1293           2 :   for (i =0; !err && recp[i]; i++)
    1294             :     {
    1295             :       char *fpr;
    1296             :       int newlen;
    1297             : 
    1298           1 :       if (!recp[i]->subkeys || !recp[i]->subkeys->fpr)
    1299             :         {
    1300           0 :           invalid_recipients++;
    1301           0 :           continue;
    1302             :         }
    1303           1 :       fpr = recp[i]->subkeys->fpr;
    1304             : 
    1305           1 :       newlen = 11 + strlen (fpr);
    1306           1 :       if (linelen < newlen)
    1307             :         {
    1308           0 :           char *newline = realloc (line, newlen);
    1309           0 :           if (! newline)
    1310             :             {
    1311           0 :               int saved_err = gpg_error_from_syserror ();
    1312           0 :               free (line);
    1313           0 :               return saved_err;
    1314             :             }
    1315           0 :           line = newline;
    1316           0 :           linelen = newlen;
    1317             :         }
    1318           1 :       strcpy (&line[10], fpr);
    1319             : 
    1320           1 :       err = gpgsm_assuan_simple_command (gpgsm, line, gpgsm->status.fnc,
    1321             :                                          gpgsm->status.fnc_value);
    1322             :       /* FIXME: This requires more work.  */
    1323           1 :       if (gpg_err_code (err) == GPG_ERR_NO_PUBKEY)
    1324           0 :         invalid_recipients++;
    1325           1 :       else if (err)
    1326             :         {
    1327           0 :           free (line);
    1328           0 :           return err;
    1329             :         }
    1330             :     }
    1331           1 :   free (line);
    1332           1 :   return gpg_error (invalid_recipients
    1333             :                     ? GPG_ERR_UNUSABLE_PUBKEY : GPG_ERR_NO_ERROR);
    1334             : }
    1335             : 
    1336             : 
    1337             : /* Take recipients from the LF delimited STRING and send RECIPIENT
    1338             :  * commands to gpgsm.  */
    1339             : static gpgme_error_t
    1340           0 : set_recipients_from_string (engine_gpgsm_t gpgsm, const char *string)
    1341             : {
    1342           0 :   gpg_error_t err = 0;
    1343           0 :   char *line = NULL;
    1344           0 :   int no_pubkey = 0;
    1345             :   const char *s;
    1346             :   int n;
    1347             : 
    1348             :   for (;;)
    1349             :     {
    1350           0 :       while (*string == ' ' || *string == '\t')
    1351           0 :         string++;
    1352           0 :       if (!*string)
    1353           0 :         break;
    1354             : 
    1355           0 :       s = strchr (string, '\n');
    1356           0 :       if (s)
    1357           0 :         n = s - string;
    1358             :       else
    1359           0 :         n = strlen (string);
    1360           0 :       while (n && (string[n-1] == ' ' || string[n-1] == '\t'))
    1361           0 :         n--;
    1362             : 
    1363           0 :       gpgrt_free (line);
    1364           0 :       if (gpgrt_asprintf (&line, "RECIPIENT %.*s", n, string) < 0)
    1365             :         {
    1366           0 :           err = gpg_error_from_syserror ();
    1367           0 :           break;
    1368             :         }
    1369           0 :       string += n + !!s;
    1370             : 
    1371           0 :       err = gpgsm_assuan_simple_command (gpgsm, line, gpgsm->status.fnc,
    1372             :                                          gpgsm->status.fnc_value);
    1373             : 
    1374             :       /* Fixme: Improve error reporting.  */
    1375           0 :       if (gpg_err_code (err) == GPG_ERR_NO_PUBKEY)
    1376           0 :         no_pubkey++;
    1377           0 :       else if (err)
    1378           0 :         break;
    1379             :     }
    1380           0 :   gpgrt_free (line);
    1381           0 :   return err? err : no_pubkey? gpg_error (GPG_ERR_NO_PUBKEY) : 0;
    1382             : }
    1383             : 
    1384             : 
    1385             : static gpgme_error_t
    1386           1 : gpgsm_encrypt (void *engine, gpgme_key_t recp[], const char *recpstring,
    1387             :                gpgme_encrypt_flags_t flags,
    1388             :                gpgme_data_t plain, gpgme_data_t ciph, int use_armor)
    1389             : {
    1390           1 :   engine_gpgsm_t gpgsm = engine;
    1391             :   gpgme_error_t err;
    1392             : 
    1393           1 :   if (!gpgsm)
    1394           0 :     return gpg_error (GPG_ERR_INV_VALUE);
    1395           1 :   if (!recp)
    1396           0 :     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
    1397             : 
    1398           1 :   if ((flags & GPGME_ENCRYPT_NO_ENCRYPT_TO))
    1399             :     {
    1400           0 :       err = gpgsm_assuan_simple_command (gpgsm,
    1401             :                                          "OPTION no-encrypt-to", NULL, NULL);
    1402           0 :       if (err)
    1403           0 :         return err;
    1404             :     }
    1405             : 
    1406           1 :   gpgsm->input_cb.data = plain;
    1407           1 :   err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
    1408           1 :   if (err)
    1409           0 :     return err;
    1410           1 :   gpgsm->output_cb.data = ciph;
    1411           1 :   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor"
    1412           0 :                       : map_data_enc (gpgsm->output_cb.data));
    1413           1 :   if (err)
    1414           0 :     return err;
    1415           1 :   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
    1416           1 :   gpgsm->inline_data = NULL;
    1417             : 
    1418           1 :   if (!recp && recpstring)
    1419           0 :     err = set_recipients_from_string (gpgsm, recpstring);
    1420             :   else
    1421           1 :     err = set_recipients (gpgsm, recp);
    1422             : 
    1423           1 :   if (!err)
    1424           1 :     err = start (gpgsm, "ENCRYPT");
    1425             : 
    1426           1 :   return err;
    1427             : }
    1428             : 
    1429             : 
    1430             : static gpgme_error_t
    1431           0 : gpgsm_export (void *engine, const char *pattern, gpgme_export_mode_t mode,
    1432             :               gpgme_data_t keydata, int use_armor)
    1433             : {
    1434           0 :   engine_gpgsm_t gpgsm = engine;
    1435           0 :   gpgme_error_t err = 0;
    1436             :   char *cmd;
    1437             : 
    1438           0 :   if (!gpgsm)
    1439           0 :     return gpg_error (GPG_ERR_INV_VALUE);
    1440             : 
    1441           0 :   if (!pattern)
    1442           0 :     pattern = "";
    1443             : 
    1444           0 :   cmd = malloc (7 + 9 + 9 + strlen (pattern) + 1);
    1445           0 :   if (!cmd)
    1446           0 :     return gpg_error_from_syserror ();
    1447             : 
    1448           0 :   strcpy (cmd, "EXPORT ");
    1449           0 :   if ((mode & GPGME_EXPORT_MODE_SECRET))
    1450             :     {
    1451           0 :       strcat (cmd, "--secret ");
    1452           0 :       if ((mode & GPGME_EXPORT_MODE_RAW))
    1453           0 :         strcat (cmd, "--raw ");
    1454           0 :       else if ((mode & GPGME_EXPORT_MODE_PKCS12))
    1455           0 :         strcat (cmd, "--pkcs12 ");
    1456             :     }
    1457           0 :   strcat (cmd, pattern);
    1458             : 
    1459           0 :   gpgsm->output_cb.data = keydata;
    1460           0 :   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor"
    1461           0 :                       : map_data_enc (gpgsm->output_cb.data));
    1462           0 :   if (err)
    1463           0 :     return err;
    1464           0 :   gpgsm_clear_fd (gpgsm, INPUT_FD);
    1465           0 :   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
    1466           0 :   gpgsm->inline_data = NULL;
    1467             : 
    1468           0 :   err = start (gpgsm, cmd);
    1469           0 :   free (cmd);
    1470           0 :   return err;
    1471             : }
    1472             : 
    1473             : 
    1474             : static gpgme_error_t
    1475           2 : gpgsm_export_ext (void *engine, const char *pattern[], gpgme_export_mode_t mode,
    1476             :                   gpgme_data_t keydata, int use_armor)
    1477             : {
    1478           2 :   engine_gpgsm_t gpgsm = engine;
    1479           2 :   gpgme_error_t err = 0;
    1480             :   char *line;
    1481             :   /* Length is "EXPORT " + "--secret " + "--pkcs12 " + p + '\0'.  */
    1482           2 :   int length = 7 + 9 + 9 + 1;
    1483             :   char *linep;
    1484             : 
    1485           2 :   if (!gpgsm)
    1486           0 :     return gpg_error (GPG_ERR_INV_VALUE);
    1487             : 
    1488           2 :   if (pattern && *pattern)
    1489             :     {
    1490           2 :       const char **pat = pattern;
    1491             : 
    1492           7 :       while (*pat)
    1493             :         {
    1494           3 :           const char *patlet = *pat;
    1495             : 
    1496         117 :           while (*patlet)
    1497             :             {
    1498         111 :               length++;
    1499         111 :               if (*patlet == '%' || *patlet == ' ' || *patlet == '+')
    1500           7 :                 length += 2;
    1501         111 :               patlet++;
    1502             :             }
    1503           3 :           pat++;
    1504           3 :           length++;
    1505             :         }
    1506             :     }
    1507           2 :   line = malloc (length);
    1508           2 :   if (!line)
    1509           0 :     return gpg_error_from_syserror ();
    1510             : 
    1511           2 :   strcpy (line, "EXPORT ");
    1512           2 :   if ((mode & GPGME_EXPORT_MODE_SECRET))
    1513             :     {
    1514           0 :       strcat (line, "--secret ");
    1515           0 :       if ((mode & GPGME_EXPORT_MODE_RAW))
    1516           0 :         strcat (line, "--raw ");
    1517           0 :       else if ((mode & GPGME_EXPORT_MODE_PKCS12))
    1518           0 :         strcat (line, "--pkcs12 ");
    1519             :     }
    1520           2 :   linep = &line[strlen (line)];
    1521             : 
    1522           2 :   if (pattern && *pattern)
    1523             :     {
    1524           7 :       while (*pattern)
    1525             :         {
    1526           3 :           const char *patlet = *pattern;
    1527             : 
    1528         117 :           while (*patlet)
    1529             :             {
    1530         111 :               switch (*patlet)
    1531             :                 {
    1532             :                 case '%':
    1533           0 :                   *(linep++) = '%';
    1534           0 :                   *(linep++) = '2';
    1535           0 :                   *(linep++) = '5';
    1536           0 :                   break;
    1537             :                 case ' ':
    1538           7 :                   *(linep++) = '%';
    1539           7 :                   *(linep++) = '2';
    1540           7 :                   *(linep++) = '0';
    1541           7 :                   break;
    1542             :                 case '+':
    1543           0 :                   *(linep++) = '%';
    1544           0 :                   *(linep++) = '2';
    1545           0 :                   *(linep++) = 'B';
    1546           0 :                   break;
    1547             :                 default:
    1548         104 :                   *(linep++) = *patlet;
    1549         104 :                   break;
    1550             :                 }
    1551         111 :               patlet++;
    1552             :             }
    1553           3 :           pattern++;
    1554           3 :           if (*pattern)
    1555           1 :             *linep++ = ' ';
    1556             :         }
    1557             :     }
    1558           2 :   *linep = '\0';
    1559             : 
    1560           2 :   gpgsm->output_cb.data = keydata;
    1561           2 :   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor"
    1562           0 :                       : map_data_enc (gpgsm->output_cb.data));
    1563           2 :   if (err)
    1564           0 :     return err;
    1565           2 :   gpgsm_clear_fd (gpgsm, INPUT_FD);
    1566           2 :   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
    1567           2 :   gpgsm->inline_data = NULL;
    1568             : 
    1569           2 :   err = start (gpgsm, line);
    1570           2 :   free (line);
    1571           2 :   return err;
    1572             : }
    1573             : 
    1574             : 
    1575             : static gpgme_error_t
    1576           0 : gpgsm_genkey (void *engine,
    1577             :               const char *userid, const char *algo,
    1578             :               unsigned long reserved, unsigned long expires,
    1579             :               gpgme_key_t key, unsigned int flags,
    1580             :               gpgme_data_t help_data, unsigned int extraflags,
    1581             :               gpgme_data_t pubkey, gpgme_data_t seckey)
    1582             : {
    1583           0 :   engine_gpgsm_t gpgsm = engine;
    1584             :   gpgme_error_t err;
    1585             : 
    1586             :   (void)reserved;
    1587             : 
    1588           0 :   if (!gpgsm)
    1589           0 :     return gpg_error (GPG_ERR_INV_VALUE);
    1590             : 
    1591           0 :   if (help_data)
    1592             :     {
    1593           0 :       if (!pubkey || seckey)
    1594           0 :         return gpg_error (GPG_ERR_INV_VALUE);
    1595             : 
    1596           0 :       gpgsm->input_cb.data = help_data;
    1597           0 :       err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
    1598           0 :       if (err)
    1599           0 :         return err;
    1600           0 :       gpgsm->output_cb.data = pubkey;
    1601           0 :       err = gpgsm_set_fd (gpgsm, OUTPUT_FD,
    1602           0 :                           (extraflags & GENKEY_EXTRAFLAG_ARMOR)? "--armor"
    1603           0 :                           : map_data_enc (gpgsm->output_cb.data));
    1604           0 :       if (err)
    1605           0 :         return err;
    1606           0 :       gpgsm_clear_fd (gpgsm, MESSAGE_FD);
    1607           0 :       gpgsm->inline_data = NULL;
    1608             : 
    1609           0 :       err = start (gpgsm, "GENKEY");
    1610           0 :       return err;
    1611             :     }
    1612             : 
    1613             :   (void)userid;
    1614             :   (void)algo;
    1615             :   (void)expires;
    1616             :   (void)key;
    1617             :   (void)flags;
    1618             : 
    1619             :   /* The new interface has not yet been implemented,  */
    1620           0 :   return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
    1621             : }
    1622             : 
    1623             : 
    1624             : static gpgme_error_t
    1625           2 : gpgsm_import (void *engine, gpgme_data_t keydata, gpgme_key_t *keyarray)
    1626             : {
    1627           2 :   engine_gpgsm_t gpgsm = engine;
    1628             :   gpgme_error_t err;
    1629             :   gpgme_data_encoding_t dataenc;
    1630             :   int idx;
    1631             : 
    1632           2 :   if (!gpgsm)
    1633           0 :     return gpg_error (GPG_ERR_INV_VALUE);
    1634             : 
    1635           2 :   if (keydata && keyarray)
    1636           0 :     return gpg_error (GPG_ERR_INV_VALUE); /* Only one is allowed.  */
    1637             : 
    1638           2 :   dataenc = gpgme_data_get_encoding (keydata);
    1639             : 
    1640           2 :   if (keyarray)
    1641             :     {
    1642             :       size_t buflen;
    1643             :       char *buffer, *p;
    1644             : 
    1645             :       /* Fist check whether the engine already features the
    1646             :          --re-import option.  */
    1647           0 :       err = gpgsm_assuan_simple_command
    1648             :         (gpgsm, "GETINFO cmd_has_option IMPORT re-import", NULL, NULL);
    1649           0 :       if (err)
    1650           0 :         return gpg_error (GPG_ERR_NOT_SUPPORTED);
    1651             : 
    1652             :       /* Create an internal data object with a list of all
    1653             :          fingerprints.  The data object and its memory (to avoid an
    1654             :          extra copy by gpgme_data_new_from_mem) are stored in two
    1655             :          variables which are released by the close_notify_handler.  */
    1656           0 :       for (idx=0, buflen=0; keyarray[idx]; idx++)
    1657             :         {
    1658           0 :           if (keyarray[idx]->protocol == GPGME_PROTOCOL_CMS
    1659           0 :               && keyarray[idx]->subkeys
    1660           0 :               && keyarray[idx]->subkeys->fpr
    1661           0 :               && *keyarray[idx]->subkeys->fpr)
    1662           0 :             buflen += strlen (keyarray[idx]->subkeys->fpr) + 1;
    1663             :         }
    1664             :       /* Allocate a buffer with extra space for the trailing Nul
    1665             :          introduced by the use of stpcpy.  */
    1666           0 :       buffer = malloc (buflen+1);
    1667           0 :       if (!buffer)
    1668           0 :         return gpg_error_from_syserror ();
    1669           0 :       for (idx=0, p = buffer; keyarray[idx]; idx++)
    1670             :         {
    1671           0 :           if (keyarray[idx]->protocol == GPGME_PROTOCOL_CMS
    1672           0 :               && keyarray[idx]->subkeys
    1673           0 :               && keyarray[idx]->subkeys->fpr
    1674           0 :               && *keyarray[idx]->subkeys->fpr)
    1675           0 :             p = stpcpy (stpcpy (p, keyarray[idx]->subkeys->fpr), "\n");
    1676             :         }
    1677             : 
    1678           0 :       err = gpgme_data_new_from_mem (&gpgsm->input_helper_data,
    1679             :                                      buffer, buflen, 0);
    1680           0 :       if (err)
    1681             :         {
    1682           0 :           free (buffer);
    1683           0 :           return err;
    1684             :         }
    1685           0 :       gpgsm->input_helper_memory = buffer;
    1686             : 
    1687           0 :       gpgsm->input_cb.data = gpgsm->input_helper_data;
    1688           0 :       err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
    1689           0 :       if (err)
    1690             :         {
    1691           0 :           gpgme_data_release (gpgsm->input_helper_data);
    1692           0 :           gpgsm->input_helper_data = NULL;
    1693           0 :           free (gpgsm->input_helper_memory);
    1694           0 :           gpgsm->input_helper_memory = NULL;
    1695           0 :           return err;
    1696             :         }
    1697           0 :       gpgsm_clear_fd (gpgsm, OUTPUT_FD);
    1698           0 :       gpgsm_clear_fd (gpgsm, MESSAGE_FD);
    1699           0 :       gpgsm->inline_data = NULL;
    1700             : 
    1701           0 :       return start (gpgsm, "IMPORT --re-import");
    1702             :     }
    1703           2 :   else if (dataenc == GPGME_DATA_ENCODING_URL
    1704           2 :            || dataenc == GPGME_DATA_ENCODING_URL0
    1705           2 :            || dataenc == GPGME_DATA_ENCODING_URLESC)
    1706             :     {
    1707           0 :       return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
    1708             :     }
    1709             :   else
    1710             :     {
    1711           2 :       gpgsm->input_cb.data = keydata;
    1712           2 :       err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
    1713           2 :       if (err)
    1714           0 :         return err;
    1715           2 :       gpgsm_clear_fd (gpgsm, OUTPUT_FD);
    1716           2 :       gpgsm_clear_fd (gpgsm, MESSAGE_FD);
    1717           2 :       gpgsm->inline_data = NULL;
    1718             : 
    1719           2 :       return start (gpgsm, "IMPORT");
    1720             :     }
    1721             : }
    1722             : 
    1723             : 
    1724             : static gpgme_error_t
    1725           2 : gpgsm_keylist (void *engine, const char *pattern, int secret_only,
    1726             :                gpgme_keylist_mode_t mode, int engine_flags)
    1727             : {
    1728           2 :   engine_gpgsm_t gpgsm = engine;
    1729             :   char *line;
    1730             :   gpgme_error_t err;
    1731           2 :   int list_mode = 0;
    1732             : 
    1733           2 :   if (mode & GPGME_KEYLIST_MODE_LOCAL)
    1734           2 :     list_mode |= 1;
    1735           2 :   if (mode & GPGME_KEYLIST_MODE_EXTERN)
    1736           0 :     list_mode |= 2;
    1737             : 
    1738           2 :   if (!pattern)
    1739           1 :     pattern = "";
    1740             : 
    1741             :   /* Hack to make sure that the agent is started.  Only if the agent
    1742             :      has been started an application may connect to the agent via
    1743             :      GPGME_PROTOCOL_ASSUAN - for example to look for smartcards.  We
    1744             :      do this only if a secret key listing has been requested.  In
    1745             :      general this is not needed because a secret key listing starts
    1746             :      the agent.  However on a fresh installation no public keys are
    1747             :      available and thus there is no need for gpgsm to ask the agent
    1748             :      whether a secret key exists for the public key.  */
    1749           2 :   if (secret_only || (mode & GPGME_KEYLIST_MODE_WITH_SECRET))
    1750           0 :     gpgsm_assuan_simple_command (gpgsm, "GETINFO agent-check", NULL, NULL);
    1751             : 
    1752             :   /* Always send list-mode option because RESET does not reset it.  */
    1753           2 :   if (gpgrt_asprintf (&line, "OPTION list-mode=%d", (list_mode & 3)) < 0)
    1754           0 :     return gpg_error_from_syserror ();
    1755           2 :   err = gpgsm_assuan_simple_command (gpgsm, line, NULL, NULL);
    1756           2 :   gpgrt_free (line);
    1757           2 :   if (err)
    1758           0 :     return err;
    1759             : 
    1760             : 
    1761             :   /* Always send key validation because RESET does not reset it.  */
    1762             : 
    1763             :   /* Use the validation mode if requested.  We don't check for an error
    1764             :      yet because this is a pretty fresh gpgsm features. */
    1765           2 :   gpgsm_assuan_simple_command (gpgsm,
    1766           2 :                                (mode & GPGME_KEYLIST_MODE_VALIDATE)?
    1767             :                                "OPTION with-validation=1":
    1768             :                                "OPTION with-validation=0" ,
    1769             :                                NULL, NULL);
    1770             :   /* Include the ephemeral keys if requested.  We don't check for an error
    1771             :      yet because this is a pretty fresh gpgsm features. */
    1772           2 :   gpgsm_assuan_simple_command (gpgsm,
    1773           2 :                                (mode & GPGME_KEYLIST_MODE_EPHEMERAL)?
    1774             :                                "OPTION with-ephemeral-keys=1":
    1775             :                                "OPTION with-ephemeral-keys=0" ,
    1776             :                                NULL, NULL);
    1777           2 :   gpgsm_assuan_simple_command (gpgsm,
    1778           2 :                                (mode & GPGME_KEYLIST_MODE_WITH_SECRET)?
    1779             :                                "OPTION with-secret=1":
    1780             :                                "OPTION with-secret=0" ,
    1781             :                                NULL, NULL);
    1782           2 :   gpgsm_assuan_simple_command (gpgsm,
    1783           2 :                                (engine_flags & GPGME_ENGINE_FLAG_OFFLINE)?
    1784             :                                "OPTION offline=1":
    1785             :                                "OPTION offline=0" ,
    1786             :                                NULL, NULL);
    1787             : 
    1788             : 
    1789             :   /* Length is "LISTSECRETKEYS " + p + '\0'.  */
    1790           2 :   line = malloc (15 + strlen (pattern) + 1);
    1791           2 :   if (!line)
    1792           0 :     return gpg_error_from_syserror ();
    1793           2 :   if (secret_only)
    1794             :     {
    1795           0 :       strcpy (line, "LISTSECRETKEYS ");
    1796           0 :       strcpy (&line[15], pattern);
    1797             :     }
    1798             :   else
    1799             :     {
    1800           2 :       strcpy (line, "LISTKEYS ");
    1801           2 :       strcpy (&line[9], pattern);
    1802             :     }
    1803             : 
    1804           2 :   gpgsm_clear_fd (gpgsm, INPUT_FD);
    1805           2 :   gpgsm_clear_fd (gpgsm, OUTPUT_FD);
    1806           2 :   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
    1807           2 :   gpgsm->inline_data = NULL;
    1808             : 
    1809           2 :   err = start (gpgsm, line);
    1810           2 :   free (line);
    1811           2 :   return err;
    1812             : }
    1813             : 
    1814             : 
    1815             : static gpgme_error_t
    1816           0 : gpgsm_keylist_ext (void *engine, const char *pattern[], int secret_only,
    1817             :                    int reserved, gpgme_keylist_mode_t mode, int engine_flags)
    1818             : {
    1819           0 :   engine_gpgsm_t gpgsm = engine;
    1820             :   char *line;
    1821             :   gpgme_error_t err;
    1822             :   /* Length is "LISTSECRETKEYS " + p + '\0'.  */
    1823           0 :   int length = 15 + 1;
    1824             :   char *linep;
    1825           0 :   int any_pattern = 0;
    1826           0 :   int list_mode = 0;
    1827             : 
    1828           0 :   if (reserved)
    1829           0 :     return gpg_error (GPG_ERR_INV_VALUE);
    1830             : 
    1831           0 :   if (mode & GPGME_KEYLIST_MODE_LOCAL)
    1832           0 :     list_mode |= 1;
    1833           0 :   if (mode & GPGME_KEYLIST_MODE_EXTERN)
    1834           0 :     list_mode |= 2;
    1835             : 
    1836             :   /* Always send list-mode option because RESET does not reset it.  */
    1837           0 :   if (gpgrt_asprintf (&line, "OPTION list-mode=%d", (list_mode & 3)) < 0)
    1838           0 :     return gpg_error_from_syserror ();
    1839           0 :   err = gpgsm_assuan_simple_command (gpgsm, line, NULL, NULL);
    1840           0 :   gpgrt_free (line);
    1841           0 :   if (err)
    1842           0 :     return err;
    1843             : 
    1844             :   /* Always send key validation because RESET does not reset it.  */
    1845             :   /* Use the validation mode if required.  We don't check for an error
    1846             :      yet because this is a pretty fresh gpgsm features. */
    1847           0 :   gpgsm_assuan_simple_command (gpgsm,
    1848           0 :                                (mode & GPGME_KEYLIST_MODE_VALIDATE)?
    1849             :                                "OPTION with-validation=1":
    1850             :                                "OPTION with-validation=0" ,
    1851             :                                NULL, NULL);
    1852           0 :   gpgsm_assuan_simple_command (gpgsm,
    1853           0 :                                (mode & GPGME_KEYLIST_MODE_WITH_SECRET)?
    1854             :                                "OPTION with-secret=1":
    1855             :                                "OPTION with-secret=0" ,
    1856             :                                NULL, NULL);
    1857           0 :   gpgsm_assuan_simple_command (gpgsm,
    1858           0 :                                (engine_flags & GPGME_ENGINE_FLAG_OFFLINE)?
    1859             :                                "OPTION offline=1":
    1860             :                                "OPTION offline=0" ,
    1861             :                                NULL, NULL);
    1862             : 
    1863           0 :   if (pattern && *pattern)
    1864             :     {
    1865           0 :       const char **pat = pattern;
    1866             : 
    1867           0 :       while (*pat)
    1868             :         {
    1869           0 :           const char *patlet = *pat;
    1870             : 
    1871           0 :           while (*patlet)
    1872             :             {
    1873           0 :               length++;
    1874           0 :               if (*patlet == '%' || *patlet == ' ' || *patlet == '+')
    1875           0 :                 length += 2;
    1876           0 :               patlet++;
    1877             :             }
    1878           0 :           pat++;
    1879           0 :           length++;
    1880             :         }
    1881             :     }
    1882           0 :   line = malloc (length);
    1883           0 :   if (!line)
    1884           0 :     return gpg_error_from_syserror ();
    1885           0 :   if (secret_only)
    1886             :     {
    1887           0 :       strcpy (line, "LISTSECRETKEYS ");
    1888           0 :       linep = &line[15];
    1889             :     }
    1890             :   else
    1891             :     {
    1892           0 :       strcpy (line, "LISTKEYS ");
    1893           0 :       linep = &line[9];
    1894             :     }
    1895             : 
    1896           0 :   if (pattern && *pattern)
    1897             :     {
    1898           0 :       while (*pattern)
    1899             :         {
    1900           0 :           const char *patlet = *pattern;
    1901             : 
    1902           0 :           while (*patlet)
    1903             :             {
    1904           0 :               switch (*patlet)
    1905             :                 {
    1906             :                 case '%':
    1907           0 :                   *(linep++) = '%';
    1908           0 :                   *(linep++) = '2';
    1909           0 :                   *(linep++) = '5';
    1910           0 :                   break;
    1911             :                 case ' ':
    1912           0 :                   *(linep++) = '%';
    1913           0 :                   *(linep++) = '2';
    1914           0 :                   *(linep++) = '0';
    1915           0 :                   break;
    1916             :                 case '+':
    1917           0 :                   *(linep++) = '%';
    1918           0 :                   *(linep++) = '2';
    1919           0 :                   *(linep++) = 'B';
    1920           0 :                   break;
    1921             :                 default:
    1922           0 :                   *(linep++) = *patlet;
    1923           0 :                   break;
    1924             :                 }
    1925           0 :               patlet++;
    1926             :             }
    1927           0 :           any_pattern = 1;
    1928           0 :           *linep++ = ' ';
    1929           0 :           pattern++;
    1930             :         }
    1931             :     }
    1932           0 :   if (any_pattern)
    1933           0 :     linep--;
    1934           0 :   *linep = '\0';
    1935             : 
    1936           0 :   gpgsm_clear_fd (gpgsm, INPUT_FD);
    1937           0 :   gpgsm_clear_fd (gpgsm, OUTPUT_FD);
    1938           0 :   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
    1939           0 :   gpgsm->inline_data = NULL;
    1940             : 
    1941           0 :   err = start (gpgsm, line);
    1942           0 :   free (line);
    1943           0 :   return err;
    1944             : }
    1945             : 
    1946             : 
    1947             : static gpgme_error_t
    1948           2 : gpgsm_sign (void *engine, gpgme_data_t in, gpgme_data_t out,
    1949             :             gpgme_sig_mode_t mode, int use_armor, int use_textmode,
    1950             :             int include_certs, gpgme_ctx_t ctx /* FIXME */)
    1951             : {
    1952           2 :   engine_gpgsm_t gpgsm = engine;
    1953             :   gpgme_error_t err;
    1954             :   char *assuan_cmd;
    1955             :   int i;
    1956             :   gpgme_key_t key;
    1957             : 
    1958             :   (void)use_textmode;
    1959             : 
    1960           2 :   if (!gpgsm)
    1961           0 :     return gpg_error (GPG_ERR_INV_VALUE);
    1962             : 
    1963             :   /* FIXME: This does not work as RESET does not reset it so we can't
    1964             :      revert back to default.  */
    1965           2 :   if (include_certs != GPGME_INCLUDE_CERTS_DEFAULT)
    1966             :     {
    1967             :       /* FIXME: Make sure that if we run multiple operations, that we
    1968             :          can reset any previously set value in case the default is
    1969             :          requested.  */
    1970             : 
    1971           0 :       if (gpgrt_asprintf (&assuan_cmd,
    1972             :                           "OPTION include-certs %i", include_certs) < 0)
    1973           0 :         return gpg_error_from_syserror ();
    1974           0 :       err = gpgsm_assuan_simple_command (gpgsm, assuan_cmd, NULL, NULL);
    1975           0 :       gpgrt_free (assuan_cmd);
    1976           0 :       if (err)
    1977           0 :         return err;
    1978             :     }
    1979             : 
    1980           2 :   for (i = 0; (key = gpgme_signers_enum (ctx, i)); i++)
    1981             :     {
    1982           0 :       const char *s = key->subkeys ? key->subkeys->fpr : NULL;
    1983           0 :       if (s && strlen (s) < 80)
    1984           0 :         {
    1985             :           char buf[100];
    1986             : 
    1987           0 :           strcpy (stpcpy (buf, "SIGNER "), s);
    1988           0 :           err = gpgsm_assuan_simple_command (gpgsm, buf,
    1989             :                                              gpgsm->status.fnc,
    1990             :                                              gpgsm->status.fnc_value);
    1991             :         }
    1992             :       else
    1993           0 :         err = gpg_error (GPG_ERR_INV_VALUE);
    1994           0 :       gpgme_key_unref (key);
    1995           0 :       if (err)
    1996           0 :         return err;
    1997             :     }
    1998             : 
    1999           2 :   gpgsm->input_cb.data = in;
    2000           2 :   err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
    2001           2 :   if (err)
    2002           0 :     return err;
    2003           2 :   gpgsm->output_cb.data = out;
    2004           2 :   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor"
    2005           0 :                       : map_data_enc (gpgsm->output_cb.data));
    2006           2 :   if (err)
    2007           0 :     return err;
    2008           2 :   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
    2009           2 :   gpgsm->inline_data = NULL;
    2010             : 
    2011           2 :   err = start (gpgsm, mode == GPGME_SIG_MODE_DETACH
    2012             :                ? "SIGN --detached" : "SIGN");
    2013           2 :   return err;
    2014             : }
    2015             : 
    2016             : 
    2017             : static gpgme_error_t
    2018           2 : gpgsm_verify (void *engine, gpgme_data_t sig, gpgme_data_t signed_text,
    2019             :               gpgme_data_t plaintext, gpgme_ctx_t ctx)
    2020             : {
    2021           2 :   engine_gpgsm_t gpgsm = engine;
    2022             :   gpgme_error_t err;
    2023             : 
    2024             :   (void)ctx;
    2025             : 
    2026           2 :   if (!gpgsm)
    2027           0 :     return gpg_error (GPG_ERR_INV_VALUE);
    2028             : 
    2029           2 :   gpgsm->input_cb.data = sig;
    2030           2 :   err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
    2031           2 :   if (err)
    2032           0 :     return err;
    2033           2 :   if (!signed_text)
    2034             :     {
    2035             :       /* Normal or cleartext signature.  */
    2036           0 :       if (plaintext)
    2037             :         {
    2038           0 :           gpgsm->output_cb.data = plaintext;
    2039           0 :           err = gpgsm_set_fd (gpgsm, OUTPUT_FD, 0);
    2040             :         }
    2041             :       else
    2042             :         {
    2043             :           /* No output requested.  */
    2044           0 :           gpgsm_clear_fd (gpgsm, OUTPUT_FD);
    2045             :         }
    2046           0 :       gpgsm_clear_fd (gpgsm, MESSAGE_FD);
    2047             :     }
    2048             :   else
    2049             :     {
    2050             :       /* Detached signature.  */
    2051           2 :       gpgsm->message_cb.data = signed_text;
    2052           2 :       err = gpgsm_set_fd (gpgsm, MESSAGE_FD, 0);
    2053           2 :       gpgsm_clear_fd (gpgsm, OUTPUT_FD);
    2054             :     }
    2055           2 :   gpgsm->inline_data = NULL;
    2056             : 
    2057           2 :   if (!err)
    2058           2 :     err = start (gpgsm, "VERIFY");
    2059             : 
    2060           2 :   return err;
    2061             : }
    2062             : 
    2063             : 
    2064             : /* Send the GETAUDITLOG command.  The result is saved to a gpgme data
    2065             :    object.  */
    2066             : static gpgme_error_t
    2067           2 : gpgsm_getauditlog (void *engine, gpgme_data_t output, unsigned int flags)
    2068             : {
    2069           2 :   engine_gpgsm_t gpgsm = engine;
    2070           2 :   gpgme_error_t err = 0;
    2071             : 
    2072           2 :   if (!gpgsm || !output)
    2073           0 :     return gpg_error (GPG_ERR_INV_VALUE);
    2074             : 
    2075           2 :   if ((flags & GPGME_AUDITLOG_DIAG))
    2076           0 :     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
    2077             : 
    2078             : #if USE_DESCRIPTOR_PASSING
    2079           2 :   gpgsm->output_cb.data = output;
    2080           2 :   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, 0);
    2081           2 :   if (err)
    2082           0 :     return err;
    2083             : 
    2084           2 :   gpgsm_clear_fd (gpgsm, INPUT_FD);
    2085           2 :   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
    2086           2 :   gpgsm->inline_data = NULL;
    2087             : # define CMD  "GETAUDITLOG"
    2088             : #else
    2089             :   gpgsm_clear_fd (gpgsm, OUTPUT_FD);
    2090             :   gpgsm_clear_fd (gpgsm, INPUT_FD);
    2091             :   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
    2092             :   gpgsm->inline_data = output;
    2093             : # define CMD  "GETAUDITLOG --data"
    2094             : #endif
    2095             : 
    2096           2 :   err = start (gpgsm, (flags & GPGME_AUDITLOG_HTML)? CMD " --html" : CMD);
    2097             : 
    2098           2 :   return err;
    2099             : }
    2100             : 
    2101             : 
    2102             : /* This sets a status callback for monitoring status lines before they
    2103             :  * are passed to a caller set handler.  */
    2104             : static void
    2105           0 : gpgsm_set_status_cb (void *engine, gpgme_status_cb_t cb, void *cb_value)
    2106             : {
    2107           0 :   engine_gpgsm_t gpgsm = engine;
    2108             : 
    2109           0 :   gpgsm->status.mon_cb = cb;
    2110           0 :   gpgsm->status.mon_cb_value = cb_value;
    2111           0 : }
    2112             : 
    2113             : 
    2114             : static void
    2115          14 : gpgsm_set_status_handler (void *engine, engine_status_handler_t fnc,
    2116             :                           void *fnc_value)
    2117             : {
    2118          14 :   engine_gpgsm_t gpgsm = engine;
    2119             : 
    2120          14 :   gpgsm->status.fnc = fnc;
    2121          14 :   gpgsm->status.fnc_value = fnc_value;
    2122          14 : }
    2123             : 
    2124             : 
    2125             : static gpgme_error_t
    2126           2 : gpgsm_set_colon_line_handler (void *engine, engine_colon_line_handler_t fnc,
    2127             :                               void *fnc_value)
    2128             : {
    2129           2 :   engine_gpgsm_t gpgsm = engine;
    2130             : 
    2131           2 :   gpgsm->colon.fnc = fnc;
    2132           2 :   gpgsm->colon.fnc_value = fnc_value;
    2133           2 :   gpgsm->colon.any = 0;
    2134           2 :   return 0;
    2135             : }
    2136             : 
    2137             : 
    2138             : static void
    2139          14 : gpgsm_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
    2140             : {
    2141          14 :   engine_gpgsm_t gpgsm = engine;
    2142          14 :   gpgsm->io_cbs = *io_cbs;
    2143          14 : }
    2144             : 
    2145             : 
    2146             : static void
    2147          34 : gpgsm_io_event (void *engine, gpgme_event_io_t type, void *type_data)
    2148             : {
    2149          34 :   engine_gpgsm_t gpgsm = engine;
    2150             : 
    2151          34 :   TRACE3 (DEBUG_ENGINE, "gpgme:gpgsm_io_event", gpgsm,
    2152             :           "event %p, type %d, type_data %p",
    2153             :           gpgsm->io_cbs.event, type, type_data);
    2154          34 :   if (gpgsm->io_cbs.event)
    2155          34 :     (*gpgsm->io_cbs.event) (gpgsm->io_cbs.event_priv, type, type_data);
    2156          34 : }
    2157             : 
    2158             : 
    2159             : static gpgme_error_t
    2160           0 : gpgsm_passwd (void *engine, gpgme_key_t key, unsigned int flags)
    2161             : {
    2162           0 :   engine_gpgsm_t gpgsm = engine;
    2163             :   gpgme_error_t err;
    2164             :   char *line;
    2165             : 
    2166             :   (void)flags;
    2167             : 
    2168           0 :   if (!key || !key->subkeys || !key->subkeys->fpr)
    2169           0 :     return gpg_error (GPG_ERR_INV_CERT_OBJ);
    2170             : 
    2171           0 :   if (gpgrt_asprintf (&line, "PASSWD -- %s", key->subkeys->fpr) < 0)
    2172           0 :     return gpg_error_from_syserror ();
    2173             : 
    2174           0 :   gpgsm_clear_fd (gpgsm, OUTPUT_FD);
    2175           0 :   gpgsm_clear_fd (gpgsm, INPUT_FD);
    2176           0 :   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
    2177           0 :   gpgsm->inline_data = NULL;
    2178             : 
    2179           0 :   err = start (gpgsm, line);
    2180           0 :   gpgrt_free (line);
    2181             : 
    2182           0 :   return err;
    2183             : }
    2184             : 
    2185             : 
    2186             : 
    2187             : struct engine_ops _gpgme_engine_ops_gpgsm =
    2188             :   {
    2189             :     /* Static functions.  */
    2190             :     _gpgme_get_default_gpgsm_name,
    2191             :     NULL,
    2192             :     gpgsm_get_version,
    2193             :     gpgsm_get_req_version,
    2194             :     gpgsm_new,
    2195             : 
    2196             :     /* Member functions.  */
    2197             :     gpgsm_release,
    2198             : #if USE_DESCRIPTOR_PASSING
    2199             :     gpgsm_reset,
    2200             : #else
    2201             :     NULL,                       /* reset */
    2202             : #endif
    2203             :     gpgsm_set_status_cb,
    2204             :     gpgsm_set_status_handler,
    2205             :     NULL,               /* set_command_handler */
    2206             :     gpgsm_set_colon_line_handler,
    2207             :     gpgsm_set_locale,
    2208             :     NULL,               /* set_protocol */
    2209             :     gpgsm_set_engine_flags,
    2210             :     gpgsm_decrypt,
    2211             :     gpgsm_delete,       /* decrypt_verify */
    2212             :     NULL,               /* edit */
    2213             :     gpgsm_encrypt,
    2214             :     NULL,               /* encrypt_sign */
    2215             :     gpgsm_export,
    2216             :     gpgsm_export_ext,
    2217             :     gpgsm_genkey,
    2218             :     gpgsm_import,
    2219             :     gpgsm_keylist,
    2220             :     gpgsm_keylist_ext,
    2221             :     NULL,               /* keylist_data */
    2222             :     NULL,               /* keysign */
    2223             :     NULL,               /* tofu_policy */
    2224             :     gpgsm_sign,
    2225             :     NULL,               /* trustlist */
    2226             :     gpgsm_verify,
    2227             :     gpgsm_getauditlog,
    2228             :     NULL,               /* opassuan_transact */
    2229             :     NULL,               /* conf_load */
    2230             :     NULL,               /* conf_save */
    2231             :     NULL,               /* conf_dir */
    2232             :     NULL,               /* query_swdb */
    2233             :     gpgsm_set_io_cbs,
    2234             :     gpgsm_io_event,
    2235             :     gpgsm_cancel,
    2236             :     NULL,               /* cancel_op */
    2237             :     gpgsm_passwd,
    2238             :     NULL,               /* set_pinentry_mode */
    2239             :     NULL                /* opspawn */
    2240             :   };

Generated by: LCOV version 1.13