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

Generated by: LCOV version 1.11