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

Generated by: LCOV version 1.11