LCOV - code coverage report
Current view: top level - src - engine-gpgsm.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 500 928 53.9 %
Date: 2016-11-29 15:07:43 Functions: 28 35 80.0 %

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

Generated by: LCOV version 1.11