LCOV - code coverage report
Current view: top level - src - engine-uiserver.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 4 646 0.6 %
Date: 2018-11-15 08:49:49 Functions: 2 27 7.4 %

          Line data    Source code
       1             : /* engine-uiserver.c - Uiserver engine.
       2             :    Copyright (C) 2000 Werner Koch (dd9jn)
       3             :    Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2009 g10 Code GmbH
       4             : 
       5             :    This file is part of GPGME.
       6             : 
       7             :    GPGME is free software; you can redistribute it and/or modify it
       8             :    under the terms of the GNU Lesser General Public License as
       9             :    published by the Free Software Foundation; either version 2.1 of
      10             :    the License, or (at your option) any later version.
      11             : 
      12             :    GPGME is distributed in the hope that it will be useful, but
      13             :    WITHOUT ANY WARRANTY; without even the implied warranty of
      14             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      15             :    Lesser General Public License for more details.
      16             : 
      17             :    You should have received a copy of the GNU Lesser General Public
      18             :    License along with this program; if not, write to the Free Software
      19             :    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
      20             :    02111-1307, USA.  */
      21             : 
      22             : /* Peculiar: Use special keys from email address for recipient and
      23             :    signer (==sender).  Use no data objects with encryption for
      24             :    prep_encrypt.  */
      25             : 
      26             : #if HAVE_CONFIG_H
      27             : #include <config.h>
      28             : #endif
      29             : 
      30             : #include <stdlib.h>
      31             : #include <string.h>
      32             : #ifdef HAVE_SYS_TYPES_H
      33             : # include <sys/types.h>
      34             : #endif
      35             : #include <assert.h>
      36             : #ifdef HAVE_UNISTD_H
      37             : # include <unistd.h>
      38             : #endif
      39             : #include <locale.h>
      40             : #include <fcntl.h> /* FIXME */
      41             : #include <errno.h>
      42             : 
      43             : #include "gpgme.h"
      44             : #include "util.h"
      45             : #include "ops.h"
      46             : #include "wait.h"
      47             : #include "priv-io.h"
      48             : #include "sema.h"
      49             : #include "data.h"
      50             : 
      51             : #include "assuan.h"
      52             : #include "debug.h"
      53             : 
      54             : #include "engine-backend.h"
      55             : 
      56             : 
      57             : typedef struct
      58             : {
      59             :   int fd;       /* FD we talk about.  */
      60             :   int server_fd;/* Server FD for this connection.  */
      61             :   int dir;      /* Inbound/Outbound, maybe given implicit?  */
      62             :   void *data;   /* Handler-specific data.  */
      63             :   void *tag;    /* ID from the user for gpgme_remove_io_callback.  */
      64             :   char server_fd_str[15]; /* Same as SERVER_FD but as a string.  We
      65             :                              need this because _gpgme_io_fd2str can't
      66             :                              be used on a closed descriptor.  */
      67             : } iocb_data_t;
      68             : 
      69             : 
      70             : struct engine_uiserver
      71             : {
      72             :   assuan_context_t assuan_ctx;
      73             : 
      74             :   int lc_ctype_set;
      75             :   int lc_messages_set;
      76             :   gpgme_protocol_t protocol;
      77             : 
      78             :   iocb_data_t status_cb;
      79             : 
      80             :   /* Input, output etc are from the servers perspective.  */
      81             :   iocb_data_t input_cb;
      82             :   gpgme_data_t input_helper_data;  /* Input helper data object.  */
      83             :   void *input_helper_memory;       /* Input helper memory block.  */
      84             : 
      85             :   iocb_data_t output_cb;
      86             : 
      87             :   iocb_data_t message_cb;
      88             : 
      89             :   struct
      90             :   {
      91             :     engine_status_handler_t fnc;
      92             :     void *fnc_value;
      93             :     gpgme_status_cb_t mon_cb;
      94             :     void *mon_cb_value;
      95             :   } status;
      96             : 
      97             :   struct
      98             :   {
      99             :     engine_colon_line_handler_t fnc;
     100             :     void *fnc_value;
     101             :     struct
     102             :     {
     103             :       char *line;
     104             :       int linesize;
     105             :       int linelen;
     106             :     } attic;
     107             :     int any; /* any data line seen */
     108             :   } colon;
     109             : 
     110             :   gpgme_data_t inline_data;  /* Used to collect D lines.  */
     111             : 
     112             :   struct gpgme_io_cbs io_cbs;
     113             : };
     114             : 
     115             : typedef struct engine_uiserver *engine_uiserver_t;
     116             : 
     117             : 
     118             : static void uiserver_io_event (void *engine,
     119             :                             gpgme_event_io_t type, void *type_data);
     120             : 
     121             : 
     122             : 
     123             : static char *
     124         117 : uiserver_get_version (const char *file_name)
     125             : {
     126             :   (void)file_name;
     127         117 :   return NULL;
     128             : }
     129             : 
     130             : 
     131             : static const char *
     132         117 : uiserver_get_req_version (void)
     133             : {
     134         117 :   return NULL;
     135             : }
     136             : 
     137             : 
     138             : static void
     139           0 : close_notify_handler (int fd, void *opaque)
     140             : {
     141           0 :   engine_uiserver_t uiserver = opaque;
     142             : 
     143           0 :   assert (fd != -1);
     144           0 :   if (uiserver->status_cb.fd == fd)
     145             :     {
     146           0 :       if (uiserver->status_cb.tag)
     147           0 :         (*uiserver->io_cbs.remove) (uiserver->status_cb.tag);
     148           0 :       uiserver->status_cb.fd = -1;
     149           0 :       uiserver->status_cb.tag = NULL;
     150             :     }
     151           0 :   else if (uiserver->input_cb.fd == fd)
     152             :     {
     153           0 :       if (uiserver->input_cb.tag)
     154           0 :         (*uiserver->io_cbs.remove) (uiserver->input_cb.tag);
     155           0 :       uiserver->input_cb.fd = -1;
     156           0 :       uiserver->input_cb.tag = NULL;
     157           0 :       if (uiserver->input_helper_data)
     158             :         {
     159           0 :           gpgme_data_release (uiserver->input_helper_data);
     160           0 :           uiserver->input_helper_data = NULL;
     161             :         }
     162           0 :       if (uiserver->input_helper_memory)
     163             :         {
     164           0 :           free (uiserver->input_helper_memory);
     165           0 :           uiserver->input_helper_memory = NULL;
     166             :         }
     167             :     }
     168           0 :   else if (uiserver->output_cb.fd == fd)
     169             :     {
     170           0 :       if (uiserver->output_cb.tag)
     171           0 :         (*uiserver->io_cbs.remove) (uiserver->output_cb.tag);
     172           0 :       uiserver->output_cb.fd = -1;
     173           0 :       uiserver->output_cb.tag = NULL;
     174             :     }
     175           0 :   else if (uiserver->message_cb.fd == fd)
     176             :     {
     177           0 :       if (uiserver->message_cb.tag)
     178           0 :         (*uiserver->io_cbs.remove) (uiserver->message_cb.tag);
     179           0 :       uiserver->message_cb.fd = -1;
     180           0 :       uiserver->message_cb.tag = NULL;
     181             :     }
     182           0 : }
     183             : 
     184             : 
     185             : /* This is the default inquiry callback.  We use it to handle the
     186             :    Pinentry notifications.  */
     187             : static gpgme_error_t
     188           0 : default_inq_cb (engine_uiserver_t uiserver, const char *line)
     189             : {
     190             :   (void)uiserver;
     191             : 
     192           0 :   if (!strncmp (line, "PINENTRY_LAUNCHED", 17) && (line[17]==' '||!line[17]))
     193             :     {
     194           0 :       _gpgme_allow_set_foreground_window ((pid_t)strtoul (line+17, NULL, 10));
     195             :     }
     196             : 
     197           0 :   return 0;
     198             : }
     199             : 
     200             : 
     201             : static gpgme_error_t
     202           0 : uiserver_cancel (void *engine)
     203             : {
     204           0 :   engine_uiserver_t uiserver = engine;
     205             : 
     206           0 :   if (!uiserver)
     207           0 :     return gpg_error (GPG_ERR_INV_VALUE);
     208             : 
     209           0 :   if (uiserver->status_cb.fd != -1)
     210           0 :     _gpgme_io_close (uiserver->status_cb.fd);
     211           0 :   if (uiserver->input_cb.fd != -1)
     212           0 :     _gpgme_io_close (uiserver->input_cb.fd);
     213           0 :   if (uiserver->output_cb.fd != -1)
     214           0 :     _gpgme_io_close (uiserver->output_cb.fd);
     215           0 :   if (uiserver->message_cb.fd != -1)
     216           0 :     _gpgme_io_close (uiserver->message_cb.fd);
     217             : 
     218           0 :   if (uiserver->assuan_ctx)
     219             :     {
     220           0 :       assuan_release (uiserver->assuan_ctx);
     221           0 :       uiserver->assuan_ctx = NULL;
     222             :     }
     223             : 
     224           0 :   return 0;
     225             : }
     226             : 
     227             : 
     228             : static void
     229           0 : uiserver_release (void *engine)
     230             : {
     231           0 :   engine_uiserver_t uiserver = engine;
     232             : 
     233           0 :   if (!uiserver)
     234           0 :     return;
     235             : 
     236           0 :   uiserver_cancel (engine);
     237             : 
     238           0 :   free (uiserver->colon.attic.line);
     239           0 :   free (uiserver);
     240             : }
     241             : 
     242             : 
     243             : static gpgme_error_t
     244           0 : uiserver_new (void **engine, const char *file_name, const char *home_dir,
     245             :               const char *version)
     246             : {
     247           0 :   gpgme_error_t err = 0;
     248             :   engine_uiserver_t uiserver;
     249           0 :   char *dft_display = NULL;
     250             :   char dft_ttyname[64];
     251           0 :   char *env_tty = NULL;
     252           0 :   char *dft_ttytype = NULL;
     253             :   char *optstr;
     254             : 
     255             :   (void)home_dir;
     256             :   (void)version; /* Not yet used.  */
     257             : 
     258           0 :   uiserver = calloc (1, sizeof *uiserver);
     259           0 :   if (!uiserver)
     260           0 :     return gpg_error_from_syserror ();
     261             : 
     262           0 :   uiserver->protocol = GPGME_PROTOCOL_DEFAULT;
     263           0 :   uiserver->status_cb.fd = -1;
     264           0 :   uiserver->status_cb.dir = 1;
     265           0 :   uiserver->status_cb.tag = 0;
     266           0 :   uiserver->status_cb.data = uiserver;
     267             : 
     268           0 :   uiserver->input_cb.fd = -1;
     269           0 :   uiserver->input_cb.dir = 0;
     270           0 :   uiserver->input_cb.tag = 0;
     271           0 :   uiserver->input_cb.server_fd = -1;
     272           0 :   *uiserver->input_cb.server_fd_str = 0;
     273           0 :   uiserver->output_cb.fd = -1;
     274           0 :   uiserver->output_cb.dir = 1;
     275           0 :   uiserver->output_cb.tag = 0;
     276           0 :   uiserver->output_cb.server_fd = -1;
     277           0 :   *uiserver->output_cb.server_fd_str = 0;
     278           0 :   uiserver->message_cb.fd = -1;
     279           0 :   uiserver->message_cb.dir = 0;
     280           0 :   uiserver->message_cb.tag = 0;
     281           0 :   uiserver->message_cb.server_fd = -1;
     282           0 :   *uiserver->message_cb.server_fd_str = 0;
     283             : 
     284           0 :   uiserver->status.fnc = 0;
     285           0 :   uiserver->colon.fnc = 0;
     286           0 :   uiserver->colon.attic.line = 0;
     287           0 :   uiserver->colon.attic.linesize = 0;
     288           0 :   uiserver->colon.attic.linelen = 0;
     289           0 :   uiserver->colon.any = 0;
     290             : 
     291           0 :   uiserver->inline_data = NULL;
     292             : 
     293           0 :   uiserver->io_cbs.add = NULL;
     294           0 :   uiserver->io_cbs.add_priv = NULL;
     295           0 :   uiserver->io_cbs.remove = NULL;
     296           0 :   uiserver->io_cbs.event = NULL;
     297           0 :   uiserver->io_cbs.event_priv = NULL;
     298             : 
     299           0 :   err = assuan_new_ext (&uiserver->assuan_ctx, GPG_ERR_SOURCE_GPGME,
     300             :                         &_gpgme_assuan_malloc_hooks, _gpgme_assuan_log_cb,
     301             :                         NULL);
     302           0 :   if (err)
     303           0 :     goto leave;
     304           0 :   assuan_ctx_set_system_hooks (uiserver->assuan_ctx,
     305             :                                &_gpgme_assuan_system_hooks);
     306             : 
     307           0 :   err = assuan_socket_connect (uiserver->assuan_ctx,
     308             :                                file_name ?
     309             :                                file_name : _gpgme_get_default_uisrv_socket (),
     310             :                                0, ASSUAN_SOCKET_SERVER_FDPASSING);
     311           0 :   if (err)
     312           0 :     goto leave;
     313             : 
     314           0 :   err = _gpgme_getenv ("DISPLAY", &dft_display);
     315           0 :   if (err)
     316           0 :     goto leave;
     317           0 :   if (dft_display)
     318             :     {
     319           0 :       if (gpgrt_asprintf (&optstr, "OPTION display=%s", dft_display) < 0)
     320             :         {
     321           0 :           err = gpg_error_from_syserror ();
     322           0 :           free (dft_display);
     323           0 :           goto leave;
     324             :         }
     325           0 :       free (dft_display);
     326             : 
     327           0 :       err = assuan_transact (uiserver->assuan_ctx, optstr, NULL, NULL, NULL,
     328             :                              NULL, NULL, NULL);
     329           0 :       gpgrt_free (optstr);
     330           0 :       if (err)
     331           0 :         goto leave;
     332             :     }
     333             : 
     334           0 :   err = _gpgme_getenv ("GPG_TTY", &env_tty);
     335           0 :   if (isatty (1) || env_tty || err)
     336             :     {
     337           0 :       int rc = 0;
     338             : 
     339           0 :       if (err)
     340           0 :         goto leave;
     341           0 :       else if (env_tty)
     342             :         {
     343           0 :           snprintf (dft_ttyname, sizeof (dft_ttyname), "%s", env_tty);
     344           0 :           free (env_tty);
     345             :         }
     346             :       else
     347           0 :         rc = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname));
     348             : 
     349             :       /* Even though isatty() returns 1, ttyname_r() may fail in many
     350             :          ways, e.g., when /dev/pts is not accessible under chroot.  */
     351           0 :       if (!rc)
     352             :         {
     353           0 :           if (gpgrt_asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0)
     354             :             {
     355           0 :               err = gpg_error_from_syserror ();
     356           0 :               goto leave;
     357             :             }
     358           0 :           err = assuan_transact (uiserver->assuan_ctx, optstr, NULL, NULL, NULL,
     359             :                                  NULL, NULL, NULL);
     360           0 :           gpgrt_free (optstr);
     361           0 :           if (err)
     362           0 :             goto leave;
     363             : 
     364           0 :           err = _gpgme_getenv ("TERM", &dft_ttytype);
     365           0 :           if (err)
     366           0 :             goto leave;
     367           0 :           if (dft_ttytype)
     368             :             {
     369           0 :               if (gpgrt_asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype)< 0)
     370             :                 {
     371           0 :                   err = gpg_error_from_syserror ();
     372           0 :                   free (dft_ttytype);
     373           0 :                   goto leave;
     374             :                 }
     375           0 :               free (dft_ttytype);
     376             : 
     377           0 :               err = assuan_transact (uiserver->assuan_ctx, optstr, NULL, NULL,
     378             :                                      NULL, NULL, NULL, NULL);
     379           0 :               gpgrt_free (optstr);
     380           0 :               if (err)
     381           0 :                 goto leave;
     382             :             }
     383             :         }
     384             :     }
     385             : 
     386             : #ifdef HAVE_W32_SYSTEM
     387             :   /* Under Windows we need to use AllowSetForegroundWindow.  Tell
     388             :      uiserver to tell us when it needs it.  */
     389             :   if (!err)
     390             :     {
     391             :       err = assuan_transact (uiserver->assuan_ctx, "OPTION allow-pinentry-notify",
     392             :                              NULL, NULL, NULL, NULL, NULL, NULL);
     393             :       if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
     394             :         err = 0; /* This is a new feature of uiserver.  */
     395             :     }
     396             : #endif /*HAVE_W32_SYSTEM*/
     397             : 
     398             :  leave:
     399           0 :   if (err)
     400           0 :     uiserver_release (uiserver);
     401             :   else
     402           0 :     *engine = uiserver;
     403             : 
     404           0 :   return err;
     405             : }
     406             : 
     407             : 
     408             : static gpgme_error_t
     409           0 : uiserver_set_locale (void *engine, int category, const char *value)
     410             : {
     411           0 :   engine_uiserver_t uiserver = engine;
     412             :   gpgme_error_t err;
     413             :   char *optstr;
     414             :   const char *catstr;
     415             : 
     416             :   /* FIXME: If value is NULL, we need to reset the option to default.
     417             :      But we can't do this.  So we error out here.  UISERVER needs support
     418             :      for this.  */
     419           0 :   if (category == LC_CTYPE)
     420             :     {
     421           0 :       catstr = "lc-ctype";
     422           0 :       if (!value && uiserver->lc_ctype_set)
     423           0 :         return gpg_error (GPG_ERR_INV_VALUE);
     424           0 :       if (value)
     425           0 :         uiserver->lc_ctype_set = 1;
     426             :     }
     427             : #ifdef LC_MESSAGES
     428           0 :   else if (category == LC_MESSAGES)
     429             :     {
     430           0 :       catstr = "lc-messages";
     431           0 :       if (!value && uiserver->lc_messages_set)
     432           0 :         return gpg_error (GPG_ERR_INV_VALUE);
     433           0 :       if (value)
     434           0 :         uiserver->lc_messages_set = 1;
     435             :     }
     436             : #endif /* LC_MESSAGES */
     437             :   else
     438           0 :     return gpg_error (GPG_ERR_INV_VALUE);
     439             : 
     440             :   /* FIXME: Reset value to default.  */
     441           0 :   if (!value)
     442           0 :     return 0;
     443             : 
     444           0 :   if (gpgrt_asprintf (&optstr, "OPTION %s=%s", catstr, value) < 0)
     445           0 :     err = gpg_error_from_syserror ();
     446             :   else
     447             :     {
     448           0 :       err = assuan_transact (uiserver->assuan_ctx, optstr, NULL, NULL,
     449             :                              NULL, NULL, NULL, NULL);
     450           0 :       gpgrt_free (optstr);
     451             :     }
     452             : 
     453           0 :   return err;
     454             : }
     455             : 
     456             : 
     457             : static gpgme_error_t
     458           0 : uiserver_set_protocol (void *engine, gpgme_protocol_t protocol)
     459             : {
     460           0 :   engine_uiserver_t uiserver = engine;
     461             : 
     462           0 :   if (protocol != GPGME_PROTOCOL_OpenPGP
     463           0 :       && protocol != GPGME_PROTOCOL_CMS
     464           0 :       && protocol != GPGME_PROTOCOL_DEFAULT)
     465           0 :     return gpg_error (GPG_ERR_INV_VALUE);
     466             : 
     467           0 :   uiserver->protocol = protocol;
     468           0 :   return 0;
     469             : }
     470             : 
     471             : 
     472             : static gpgme_error_t
     473           0 : uiserver_assuan_simple_command (engine_uiserver_t uiserver, const char *cmd,
     474             :                                 engine_status_handler_t status_fnc,
     475             :                                 void *status_fnc_value)
     476             : {
     477           0 :   assuan_context_t ctx = uiserver->assuan_ctx;
     478             :   gpg_error_t err;
     479             :   char *line;
     480             :   size_t linelen;
     481             : 
     482           0 :   err = assuan_write_line (ctx, cmd);
     483           0 :   if (err)
     484           0 :     return err;
     485             : 
     486             :   do
     487             :     {
     488           0 :       err = assuan_read_line (ctx, &line, &linelen);
     489           0 :       if (err)
     490           0 :         return err;
     491             : 
     492           0 :       if (*line == '#' || !linelen)
     493           0 :         continue;
     494             : 
     495           0 :       if (linelen >= 2
     496           0 :           && line[0] == 'O' && line[1] == 'K'
     497           0 :           && (line[2] == '\0' || line[2] == ' '))
     498           0 :         return 0;
     499           0 :       else if (linelen >= 4
     500           0 :           && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
     501           0 :           && line[3] == ' ')
     502           0 :         err = atoi (&line[4]);
     503           0 :       else if (linelen >= 2
     504           0 :                && line[0] == 'S' && line[1] == ' ')
     505           0 :         {
     506             :           char *rest;
     507             :           gpgme_status_code_t r;
     508             : 
     509           0 :           rest = strchr (line + 2, ' ');
     510           0 :           if (!rest)
     511           0 :             rest = line + linelen; /* set to an empty string */
     512             :           else
     513           0 :             *(rest++) = 0;
     514             : 
     515           0 :           r = _gpgme_parse_status (line + 2);
     516           0 :           if (uiserver->status.mon_cb && r != GPGME_STATUS_PROGRESS)
     517             :             {
     518             :               /* Note that we call the monitor even if we do
     519             :                * not know the status code (r < 0).  */
     520           0 :               err = uiserver->status.mon_cb (uiserver->status.mon_cb_value,
     521           0 :                                              line + 2, rest);
     522             :             }
     523             : 
     524           0 :           if (err)
     525             :             ;
     526           0 :           else if (r >= 0 && status_fnc)
     527           0 :             err = status_fnc (status_fnc_value, r, rest);
     528             :           else
     529           0 :             err = gpg_error (GPG_ERR_GENERAL);
     530             :         }
     531             :       else
     532           0 :         err = gpg_error (GPG_ERR_GENERAL);
     533             :     }
     534           0 :   while (!err);
     535             : 
     536           0 :   return err;
     537             : }
     538             : 
     539             : 
     540             : typedef enum { INPUT_FD, OUTPUT_FD, MESSAGE_FD } fd_type_t;
     541             : 
     542             : #define COMMANDLINELEN 40
     543             : static gpgme_error_t
     544           0 : uiserver_set_fd (engine_uiserver_t uiserver, fd_type_t fd_type, const char *opt)
     545             : {
     546           0 :   gpg_error_t err = 0;
     547             :   char line[COMMANDLINELEN];
     548             :   const char *which;
     549             :   iocb_data_t *iocb_data;
     550             :   int dir;
     551             : 
     552           0 :   switch (fd_type)
     553             :     {
     554             :     case INPUT_FD:
     555           0 :       which = "INPUT";
     556           0 :       iocb_data = &uiserver->input_cb;
     557           0 :       break;
     558             : 
     559             :     case OUTPUT_FD:
     560           0 :       which = "OUTPUT";
     561           0 :       iocb_data = &uiserver->output_cb;
     562           0 :       break;
     563             : 
     564             :     case MESSAGE_FD:
     565           0 :       which = "MESSAGE";
     566           0 :       iocb_data = &uiserver->message_cb;
     567           0 :       break;
     568             : 
     569             :     default:
     570           0 :       return gpg_error (GPG_ERR_INV_VALUE);
     571             :     }
     572             : 
     573           0 :   dir = iocb_data->dir;
     574             : 
     575             :   /* We try to short-cut the communication by giving UISERVER direct
     576             :      access to the file descriptor, rather than using a pipe.  */
     577           0 :   iocb_data->server_fd = _gpgme_data_get_fd (iocb_data->data);
     578           0 :   if (iocb_data->server_fd < 0)
     579             :     {
     580             :       int fds[2];
     581             : 
     582           0 :       if (_gpgme_io_pipe (fds, 0) < 0)
     583           0 :         return gpg_error_from_syserror ();
     584             : 
     585           0 :       iocb_data->fd = dir ? fds[0] : fds[1];
     586           0 :       iocb_data->server_fd = dir ? fds[1] : fds[0];
     587             : 
     588           0 :       if (_gpgme_io_set_close_notify (iocb_data->fd,
     589             :                                       close_notify_handler, uiserver))
     590             :         {
     591           0 :           err = gpg_error (GPG_ERR_GENERAL);
     592           0 :           goto leave_set_fd;
     593             :         }
     594             :     }
     595             : 
     596           0 :   err = assuan_sendfd (uiserver->assuan_ctx, iocb_data->server_fd);
     597           0 :   if (err)
     598           0 :     goto leave_set_fd;
     599             : 
     600           0 :   _gpgme_io_close (iocb_data->server_fd);
     601           0 :   iocb_data->server_fd = -1;
     602             : 
     603           0 :   if (opt)
     604           0 :     snprintf (line, COMMANDLINELEN, "%s FD %s", which, opt);
     605             :   else
     606           0 :     snprintf (line, COMMANDLINELEN, "%s FD", which);
     607             : 
     608           0 :   err = uiserver_assuan_simple_command (uiserver, line, NULL, NULL);
     609             : 
     610             :  leave_set_fd:
     611           0 :   if (err)
     612             :     {
     613           0 :       _gpgme_io_close (iocb_data->fd);
     614           0 :       iocb_data->fd = -1;
     615           0 :       if (iocb_data->server_fd != -1)
     616             :         {
     617           0 :           _gpgme_io_close (iocb_data->server_fd);
     618           0 :           iocb_data->server_fd = -1;
     619             :         }
     620             :     }
     621             : 
     622           0 :   return err;
     623             : }
     624             : 
     625             : 
     626             : static const char *
     627           0 : map_data_enc (gpgme_data_t d)
     628             : {
     629           0 :   switch (gpgme_data_get_encoding (d))
     630             :     {
     631             :     case GPGME_DATA_ENCODING_NONE:
     632           0 :       break;
     633             :     case GPGME_DATA_ENCODING_BINARY:
     634           0 :       return "--binary";
     635             :     case GPGME_DATA_ENCODING_BASE64:
     636           0 :       return "--base64";
     637             :     case GPGME_DATA_ENCODING_ARMOR:
     638           0 :       return "--armor";
     639             :     default:
     640           0 :       break;
     641             :     }
     642           0 :   return NULL;
     643             : }
     644             : 
     645             : 
     646             : static gpgme_error_t
     647           0 : status_handler (void *opaque, int fd)
     648             : {
     649           0 :   struct io_cb_data *data = (struct io_cb_data *) opaque;
     650           0 :   engine_uiserver_t uiserver = (engine_uiserver_t) data->handler_value;
     651           0 :   gpgme_error_t err = 0;
     652             :   char *line;
     653             :   size_t linelen;
     654             : 
     655             :   do
     656             :     {
     657           0 :       err = assuan_read_line (uiserver->assuan_ctx, &line, &linelen);
     658           0 :       if (err)
     659             :         {
     660             :           /* Try our best to terminate the connection friendly.  */
     661             :           /*      assuan_write_line (uiserver->assuan_ctx, "BYE"); */
     662           0 :           TRACE3 (DEBUG_CTX, "gpgme:status_handler", uiserver,
     663             :                   "fd 0x%x: error from assuan (%d) getting status line : %s",
     664             :                   fd, err, gpg_strerror (err));
     665             :         }
     666           0 :       else if (linelen >= 3
     667           0 :                && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
     668           0 :                && (line[3] == '\0' || line[3] == ' '))
     669             :         {
     670           0 :           if (line[3] == ' ')
     671           0 :             err = atoi (&line[4]);
     672           0 :           if (! err)
     673           0 :             err = gpg_error (GPG_ERR_GENERAL);
     674           0 :           TRACE2 (DEBUG_CTX, "gpgme:status_handler", uiserver,
     675             :                   "fd 0x%x: ERR line - mapped to: %s",
     676             :                   fd, err ? gpg_strerror (err) : "ok");
     677             :           /* Try our best to terminate the connection friendly.  */
     678             :           /*      assuan_write_line (uiserver->assuan_ctx, "BYE"); */
     679             :         }
     680           0 :       else if (linelen >= 2
     681           0 :                && line[0] == 'O' && line[1] == 'K'
     682           0 :                && (line[2] == '\0' || line[2] == ' '))
     683             :         {
     684           0 :           if (uiserver->status.fnc)
     685             :             {
     686           0 :               char emptystring[1] = {0};
     687           0 :               err = uiserver->status.fnc (uiserver->status.fnc_value,
     688             :                                           GPGME_STATUS_EOF, emptystring);
     689           0 :               if (gpg_err_code (err) == GPG_ERR_FALSE)
     690           0 :                 err = 0; /* Drop special error code.  */
     691             :             }
     692             : 
     693           0 :           if (!err && uiserver->colon.fnc && uiserver->colon.any)
     694             :             {
     695             :               /* We must tell a colon function about the EOF. We do
     696             :                  this only when we have seen any data lines.  Note
     697             :                  that this inlined use of colon data lines will
     698             :                  eventually be changed into using a regular data
     699             :                  channel. */
     700           0 :               uiserver->colon.any = 0;
     701           0 :               err = uiserver->colon.fnc (uiserver->colon.fnc_value, NULL);
     702             :             }
     703           0 :           TRACE2 (DEBUG_CTX, "gpgme:status_handler", uiserver,
     704             :                   "fd 0x%x: OK line - final status: %s",
     705             :                   fd, err ? gpg_strerror (err) : "ok");
     706           0 :           _gpgme_io_close (uiserver->status_cb.fd);
     707           0 :           return err;
     708             :         }
     709           0 :       else if (linelen > 2
     710           0 :                && line[0] == 'D' && line[1] == ' '
     711           0 :                && uiserver->colon.fnc)
     712           0 :         {
     713             :           /* We are using the colon handler even for plain inline data
     714             :              - strange name for that function but for historic reasons
     715             :              we keep it.  */
     716             :           /* FIXME We can't use this for binary data because we
     717             :              assume this is a string.  For the current usage of colon
     718             :              output it is correct.  */
     719           0 :           char *src = line + 2;
     720           0 :           char *end = line + linelen;
     721             :           char *dst;
     722           0 :           char **aline = &uiserver->colon.attic.line;
     723           0 :           int *alinelen = &uiserver->colon.attic.linelen;
     724             : 
     725           0 :           if (uiserver->colon.attic.linesize < *alinelen + linelen + 1)
     726             :             {
     727           0 :               char *newline = realloc (*aline, *alinelen + linelen + 1);
     728           0 :               if (!newline)
     729           0 :                 err = gpg_error_from_syserror ();
     730             :               else
     731             :                 {
     732           0 :                   *aline = newline;
     733           0 :                   uiserver->colon.attic.linesize = *alinelen + linelen + 1;
     734             :                 }
     735             :             }
     736           0 :           if (!err)
     737             :             {
     738           0 :               dst = *aline + *alinelen;
     739             : 
     740           0 :               while (!err && src < end)
     741             :                 {
     742           0 :                   if (*src == '%' && src + 2 < end)
     743             :                     {
     744             :                       /* Handle escaped characters.  */
     745           0 :                       ++src;
     746           0 :                       *dst = _gpgme_hextobyte (src);
     747           0 :                       (*alinelen)++;
     748           0 :                       src += 2;
     749             :                     }
     750             :                   else
     751             :                     {
     752           0 :                       *dst = *src++;
     753           0 :                       (*alinelen)++;
     754             :                     }
     755             : 
     756           0 :                   if (*dst == '\n')
     757             :                     {
     758             :                       /* Terminate the pending line, pass it to the colon
     759             :                          handler and reset it.  */
     760             : 
     761           0 :                       uiserver->colon.any = 1;
     762           0 :                       if (*alinelen > 1 && *(dst - 1) == '\r')
     763           0 :                         dst--;
     764           0 :                       *dst = '\0';
     765             : 
     766             :                       /* FIXME How should we handle the return code?  */
     767           0 :                       err = uiserver->colon.fnc (uiserver->colon.fnc_value, *aline);
     768           0 :                       if (!err)
     769             :                         {
     770           0 :                           dst = *aline;
     771           0 :                           *alinelen = 0;
     772             :                         }
     773             :                     }
     774             :                   else
     775           0 :                     dst++;
     776             :                 }
     777             :             }
     778           0 :           TRACE2 (DEBUG_CTX, "gpgme:status_handler", uiserver,
     779             :                   "fd 0x%x: D line; final status: %s",
     780             :                   fd, err? gpg_strerror (err):"ok");
     781             :         }
     782           0 :       else if (linelen > 2
     783           0 :                && line[0] == 'D' && line[1] == ' '
     784           0 :                && uiserver->inline_data)
     785           0 :         {
     786           0 :           char *src = line + 2;
     787           0 :           char *end = line + linelen;
     788           0 :           char *dst = src;
     789             :           gpgme_ssize_t nwritten;
     790             : 
     791           0 :           linelen = 0;
     792           0 :           while (src < end)
     793             :             {
     794           0 :               if (*src == '%' && src + 2 < end)
     795             :                 {
     796             :                   /* Handle escaped characters.  */
     797           0 :                   ++src;
     798           0 :                   *dst++ = _gpgme_hextobyte (src);
     799           0 :                   src += 2;
     800             :                 }
     801             :               else
     802           0 :                 *dst++ = *src++;
     803             : 
     804           0 :               linelen++;
     805             :             }
     806             : 
     807           0 :           src = line + 2;
     808           0 :           while (linelen > 0)
     809             :             {
     810           0 :               nwritten = gpgme_data_write (uiserver->inline_data, src, linelen);
     811           0 :               if (!nwritten || (nwritten < 0 && errno != EINTR)
     812           0 :                   || nwritten > linelen)
     813             :                 {
     814           0 :                   err = gpg_error_from_syserror ();
     815           0 :                   break;
     816             :                 }
     817           0 :               src += nwritten;
     818           0 :               linelen -= nwritten;
     819             :             }
     820             : 
     821           0 :           TRACE2 (DEBUG_CTX, "gpgme:status_handler", uiserver,
     822             :                   "fd 0x%x: D inlinedata; final status: %s",
     823             :                   fd, err? gpg_strerror (err):"ok");
     824             :         }
     825           0 :       else if (linelen > 2
     826           0 :                && line[0] == 'S' && line[1] == ' ')
     827           0 :         {
     828             :           char *rest;
     829             :           gpgme_status_code_t r;
     830             : 
     831           0 :           rest = strchr (line + 2, ' ');
     832           0 :           if (!rest)
     833           0 :             rest = line + linelen; /* set to an empty string */
     834             :           else
     835           0 :             *(rest++) = 0;
     836             : 
     837           0 :           r = _gpgme_parse_status (line + 2);
     838             : 
     839             :           if (r >= 0)
     840             :             {
     841           0 :               if (uiserver->status.fnc)
     842             :                 {
     843           0 :                   err = uiserver->status.fnc (uiserver->status.fnc_value,
     844             :                                               r, rest);
     845           0 :                   if (gpg_err_code (err) == GPG_ERR_FALSE)
     846           0 :                     err = 0; /* Drop special error code.  */
     847             :                 }
     848             :             }
     849             :           else
     850             :             fprintf (stderr, "[UNKNOWN STATUS]%s %s", line + 2, rest);
     851           0 :           TRACE3 (DEBUG_CTX, "gpgme:status_handler", uiserver,
     852             :                   "fd 0x%x: S line (%s) - final status: %s",
     853             :                   fd, line+2, err? gpg_strerror (err):"ok");
     854             :         }
     855           0 :       else if (linelen >= 7
     856           0 :                && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
     857           0 :                && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
     858           0 :                && line[6] == 'E'
     859           0 :                && (line[7] == '\0' || line[7] == ' '))
     860             :         {
     861           0 :           char *keyword = line+7;
     862             : 
     863           0 :           while (*keyword == ' ')
     864           0 :             keyword++;;
     865           0 :           default_inq_cb (uiserver, keyword);
     866           0 :           assuan_write_line (uiserver->assuan_ctx, "END");
     867             :         }
     868             : 
     869             :     }
     870           0 :   while (!err && assuan_pending_line (uiserver->assuan_ctx));
     871             : 
     872           0 :   return err;
     873             : }
     874             : 
     875             : 
     876             : static gpgme_error_t
     877           0 : add_io_cb (engine_uiserver_t uiserver, iocb_data_t *iocbd, gpgme_io_cb_t handler)
     878             : {
     879             :   gpgme_error_t err;
     880             : 
     881           0 :   TRACE_BEG2 (DEBUG_ENGINE, "engine-uiserver:add_io_cb", uiserver,
     882             :               "fd %d, dir %d", iocbd->fd, iocbd->dir);
     883           0 :   err = (*uiserver->io_cbs.add) (uiserver->io_cbs.add_priv,
     884             :                               iocbd->fd, iocbd->dir,
     885             :                               handler, iocbd->data, &iocbd->tag);
     886           0 :   if (err)
     887           0 :     return TRACE_ERR (err);
     888           0 :   if (!iocbd->dir)
     889             :     /* FIXME Kludge around poll() problem.  */
     890           0 :     err = _gpgme_io_set_nonblocking (iocbd->fd);
     891           0 :   return TRACE_ERR (err);
     892             : }
     893             : 
     894             : 
     895             : static gpgme_error_t
     896           0 : start (engine_uiserver_t uiserver, const char *command)
     897             : {
     898             :   gpgme_error_t err;
     899             :   int fdlist[5];
     900             :   int nfds;
     901             : 
     902             :   /* We need to know the fd used by assuan for reads.  We do this by
     903             :      using the assumption that the first returned fd from
     904             :      assuan_get_active_fds() is always this one.  */
     905           0 :   nfds = assuan_get_active_fds (uiserver->assuan_ctx, 0 /* read fds */,
     906             :                                 fdlist, DIM (fdlist));
     907           0 :   if (nfds < 1)
     908           0 :     return gpg_error (GPG_ERR_GENERAL); /* FIXME */
     909             : 
     910             :   /* We "duplicate" the file descriptor, so we can close it here (we
     911             :      can't close fdlist[0], as that is closed by libassuan, and
     912             :      closing it here might cause libassuan to close some unrelated FD
     913             :      later).  Alternatively, we could special case status_fd and
     914             :      register/unregister it manually as needed, but this increases
     915             :      code duplication and is more complicated as we can not use the
     916             :      close notifications etc.  A third alternative would be to let
     917             :      Assuan know that we closed the FD, but that complicates the
     918             :      Assuan interface.  */
     919             : 
     920           0 :   uiserver->status_cb.fd = _gpgme_io_dup (fdlist[0]);
     921           0 :   if (uiserver->status_cb.fd < 0)
     922           0 :     return gpg_error_from_syserror ();
     923             : 
     924           0 :   if (_gpgme_io_set_close_notify (uiserver->status_cb.fd,
     925             :                                   close_notify_handler, uiserver))
     926             :     {
     927           0 :       _gpgme_io_close (uiserver->status_cb.fd);
     928           0 :       uiserver->status_cb.fd = -1;
     929           0 :       return gpg_error (GPG_ERR_GENERAL);
     930             :     }
     931             : 
     932           0 :   err = add_io_cb (uiserver, &uiserver->status_cb, status_handler);
     933           0 :   if (!err && uiserver->input_cb.fd != -1)
     934           0 :     err = add_io_cb (uiserver, &uiserver->input_cb, _gpgme_data_outbound_handler);
     935           0 :   if (!err && uiserver->output_cb.fd != -1)
     936           0 :     err = add_io_cb (uiserver, &uiserver->output_cb, _gpgme_data_inbound_handler);
     937           0 :   if (!err && uiserver->message_cb.fd != -1)
     938           0 :     err = add_io_cb (uiserver, &uiserver->message_cb, _gpgme_data_outbound_handler);
     939             : 
     940           0 :   if (!err)
     941           0 :     err = assuan_write_line (uiserver->assuan_ctx, command);
     942             : 
     943           0 :   if (!err)
     944           0 :     uiserver_io_event (uiserver, GPGME_EVENT_START, NULL);
     945             : 
     946           0 :   return err;
     947             : }
     948             : 
     949             : 
     950             : static gpgme_error_t
     951           0 : uiserver_reset (void *engine)
     952             : {
     953           0 :   engine_uiserver_t uiserver = engine;
     954             : 
     955             :   /* We must send a reset because we need to reset the list of
     956             :      signers.  Note that RESET does not reset OPTION commands. */
     957           0 :   return uiserver_assuan_simple_command (uiserver, "RESET", NULL, NULL);
     958             : }
     959             : 
     960             : 
     961             : static gpgme_error_t
     962           0 : uiserver_decrypt (void *engine,
     963             :                   gpgme_decrypt_flags_t flags,
     964             :                   gpgme_data_t ciph, gpgme_data_t plain,
     965             :                   int export_session_key, const char *override_session_key,
     966             :                   int auto_key_retrieve)
     967             : {
     968           0 :   engine_uiserver_t uiserver = engine;
     969             :   gpgme_error_t err;
     970             :   const char *protocol;
     971             :   char *cmd;
     972           0 :   int verify = !!(flags & GPGME_DECRYPT_VERIFY);
     973             : 
     974             :   (void)override_session_key; /* Fixme: We need to see now to add this
     975             :                                * to the UI server protocol  */
     976             :   (void)auto_key_retrieve;    /* Not yet supported.  */
     977             : 
     978             : 
     979           0 :   if (!uiserver)
     980           0 :     return gpg_error (GPG_ERR_INV_VALUE);
     981           0 :   if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT)
     982           0 :     protocol = "";
     983           0 :   else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP)
     984           0 :     protocol = " --protocol=OpenPGP";
     985           0 :   else if (uiserver->protocol == GPGME_PROTOCOL_CMS)
     986           0 :     protocol = " --protocol=CMS";
     987             :   else
     988           0 :     return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
     989             : 
     990           0 :   if (gpgrt_asprintf (&cmd, "DECRYPT%s%s%s", protocol,
     991             :                 verify ? "" : " --no-verify",
     992             :                 export_session_key ? " --export-session-key" : "") < 0)
     993           0 :     return gpg_error_from_syserror ();
     994             : 
     995           0 :   uiserver->input_cb.data = ciph;
     996           0 :   err = uiserver_set_fd (uiserver, INPUT_FD,
     997           0 :                          map_data_enc (uiserver->input_cb.data));
     998           0 :   if (err)
     999             :     {
    1000           0 :       gpgrt_free (cmd);
    1001           0 :       return gpg_error (GPG_ERR_GENERAL);       /* FIXME */
    1002             :     }
    1003           0 :   uiserver->output_cb.data = plain;
    1004           0 :   err = uiserver_set_fd (uiserver, OUTPUT_FD, 0);
    1005           0 :   if (err)
    1006             :     {
    1007           0 :       gpgrt_free (cmd);
    1008           0 :       return gpg_error (GPG_ERR_GENERAL);       /* FIXME */
    1009             :     }
    1010           0 :   uiserver->inline_data = NULL;
    1011             : 
    1012           0 :   err = start (engine, cmd);
    1013           0 :   gpgrt_free (cmd);
    1014           0 :   return err;
    1015             : }
    1016             : 
    1017             : 
    1018             : static gpgme_error_t
    1019           0 : set_recipients (engine_uiserver_t uiserver, gpgme_key_t recp[])
    1020             : {
    1021           0 :   gpgme_error_t err = 0;
    1022             :   char *line;
    1023             :   int linelen;
    1024           0 :   int invalid_recipients = 0;
    1025             :   int i;
    1026             : 
    1027           0 :   linelen = 10 + 40 + 1;        /* "RECIPIENT " + guess + '\0'.  */
    1028           0 :   line = malloc (10 + 40 + 1);
    1029           0 :   if (!line)
    1030           0 :     return gpg_error_from_syserror ();
    1031           0 :   strcpy (line, "RECIPIENT ");
    1032           0 :   for (i=0; !err && recp[i]; i++)
    1033             :     {
    1034             :       char *uid;
    1035             :       int newlen;
    1036             : 
    1037             :       /* We use only the first user ID of the key.  */
    1038           0 :       if (!recp[i]->uids || !(uid=recp[i]->uids->uid) || !*uid)
    1039             :         {
    1040           0 :           invalid_recipients++;
    1041           0 :           continue;
    1042             :         }
    1043             : 
    1044           0 :       newlen = 11 + strlen (uid);
    1045           0 :       if (linelen < newlen)
    1046             :         {
    1047           0 :           char *newline = realloc (line, newlen);
    1048           0 :           if (! newline)
    1049             :             {
    1050           0 :               int saved_err = gpg_error_from_syserror ();
    1051           0 :               free (line);
    1052           0 :               return saved_err;
    1053             :             }
    1054           0 :           line = newline;
    1055           0 :           linelen = newlen;
    1056             :         }
    1057             :       /* FIXME: need to do proper escaping  */
    1058           0 :       strcpy (&line[10], uid);
    1059             : 
    1060           0 :       err = uiserver_assuan_simple_command (uiserver, line,
    1061             :                                             uiserver->status.fnc,
    1062             :                                             uiserver->status.fnc_value);
    1063             :       /* FIXME: This might requires more work.  */
    1064           0 :       if (gpg_err_code (err) == GPG_ERR_NO_PUBKEY)
    1065           0 :         invalid_recipients++;
    1066           0 :       else if (err)
    1067             :         {
    1068           0 :           free (line);
    1069           0 :           return err;
    1070             :         }
    1071             :     }
    1072           0 :   free (line);
    1073           0 :   return gpg_error (invalid_recipients
    1074             :                     ? GPG_ERR_UNUSABLE_PUBKEY : GPG_ERR_NO_ERROR);
    1075             : }
    1076             : 
    1077             : 
    1078             : /* Take recipients from the LF delimited STRING and send RECIPIENT
    1079             :  * commands to gpgsm.  */
    1080             : static gpgme_error_t
    1081           0 : set_recipients_from_string (engine_uiserver_t uiserver, const char *string)
    1082             : {
    1083           0 :   gpg_error_t err = 0;
    1084           0 :   char *line = NULL;
    1085           0 :   int no_pubkey = 0;
    1086             :   const char *s;
    1087             :   int n;
    1088             : 
    1089             :   for (;;)
    1090             :     {
    1091           0 :       while (*string == ' ' || *string == '\t')
    1092           0 :         string++;
    1093           0 :       if (!*string)
    1094           0 :         break;
    1095             : 
    1096           0 :       s = strchr (string, '\n');
    1097           0 :       if (s)
    1098           0 :         n = s - string;
    1099             :       else
    1100           0 :         n = strlen (string);
    1101           0 :       while (n && (string[n-1] == ' ' || string[n-1] == '\t'))
    1102           0 :         n--;
    1103             : 
    1104           0 :       gpgrt_free (line);
    1105           0 :       if (gpgrt_asprintf (&line, "RECIPIENT %.*s", n, string) < 0)
    1106             :         {
    1107           0 :           err = gpg_error_from_syserror ();
    1108           0 :           break;
    1109             :         }
    1110           0 :       string += n + !!s;
    1111             : 
    1112           0 :       err = uiserver_assuan_simple_command (uiserver, line,
    1113             :                                             uiserver->status.fnc,
    1114             :                                             uiserver->status.fnc_value);
    1115             : 
    1116             :       /* Fixme: Improve error reporting.  */
    1117           0 :       if (gpg_err_code (err) == GPG_ERR_NO_PUBKEY)
    1118           0 :         no_pubkey++;
    1119           0 :       else if (err)
    1120           0 :         break;
    1121             :     }
    1122           0 :   gpgrt_free (line);
    1123           0 :   return err? err : no_pubkey? gpg_error (GPG_ERR_NO_PUBKEY) : 0;
    1124             : }
    1125             : 
    1126             : 
    1127             : static gpgme_error_t
    1128           0 : uiserver_encrypt (void *engine, gpgme_key_t recp[], const char *recpstring,
    1129             :                   gpgme_encrypt_flags_t flags,
    1130             :                   gpgme_data_t plain, gpgme_data_t ciph, int use_armor)
    1131             : {
    1132           0 :   engine_uiserver_t uiserver = engine;
    1133             :   gpgme_error_t err;
    1134             :   const char *protocol;
    1135             :   char *cmd;
    1136             : 
    1137           0 :   if (!uiserver)
    1138           0 :     return gpg_error (GPG_ERR_INV_VALUE);
    1139           0 :   if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT)
    1140           0 :     protocol = "";
    1141           0 :   else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP)
    1142           0 :     protocol = " --protocol=OpenPGP";
    1143           0 :   else if (uiserver->protocol == GPGME_PROTOCOL_CMS)
    1144           0 :     protocol = " --protocol=CMS";
    1145             :   else
    1146           0 :     return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
    1147             : 
    1148           0 :   if (flags & GPGME_ENCRYPT_PREPARE)
    1149             :     {
    1150           0 :       if (!recp || plain || ciph)
    1151           0 :         return gpg_error (GPG_ERR_INV_VALUE);
    1152             : 
    1153           0 :       if (gpgrt_asprintf (&cmd, "PREP_ENCRYPT%s%s", protocol,
    1154           0 :                     (flags & GPGME_ENCRYPT_EXPECT_SIGN)
    1155             :                     ? " --expect-sign" : "") < 0)
    1156           0 :         return gpg_error_from_syserror ();
    1157             :     }
    1158             :   else
    1159             :     {
    1160           0 :       if (!plain || !ciph)
    1161           0 :         return gpg_error (GPG_ERR_INV_VALUE);
    1162             : 
    1163           0 :       if (gpgrt_asprintf (&cmd, "ENCRYPT%s", protocol) < 0)
    1164           0 :         return gpg_error_from_syserror ();
    1165             :     }
    1166             : 
    1167           0 :   if (plain)
    1168             :     {
    1169           0 :       uiserver->input_cb.data = plain;
    1170           0 :       err = uiserver_set_fd (uiserver, INPUT_FD,
    1171           0 :                              map_data_enc (uiserver->input_cb.data));
    1172           0 :       if (err)
    1173             :         {
    1174           0 :           gpgrt_free (cmd);
    1175           0 :           return err;
    1176             :         }
    1177             :     }
    1178             : 
    1179           0 :   if (ciph)
    1180             :     {
    1181           0 :       uiserver->output_cb.data = ciph;
    1182           0 :       err = uiserver_set_fd (uiserver, OUTPUT_FD, use_armor ? "--armor"
    1183           0 :                              : map_data_enc (uiserver->output_cb.data));
    1184           0 :       if (err)
    1185             :         {
    1186           0 :           gpgrt_free (cmd);
    1187           0 :           return err;
    1188             :         }
    1189             :     }
    1190             : 
    1191           0 :   uiserver->inline_data = NULL;
    1192             : 
    1193           0 :   if (recp || recpstring)
    1194             :     {
    1195           0 :       if (recp)
    1196           0 :         err = set_recipients (uiserver, recp);
    1197             :       else
    1198           0 :         err = set_recipients_from_string (uiserver, recpstring);
    1199           0 :       if (err)
    1200             :         {
    1201           0 :           gpgrt_free (cmd);
    1202           0 :           return err;
    1203             :         }
    1204             :     }
    1205             : 
    1206           0 :   err = start (uiserver, cmd);
    1207           0 :   gpgrt_free (cmd);
    1208           0 :   return err;
    1209             : }
    1210             : 
    1211             : 
    1212             : static gpgme_error_t
    1213           0 : uiserver_sign (void *engine, gpgme_data_t in, gpgme_data_t out,
    1214             :                gpgme_sig_mode_t mode, int use_armor, int use_textmode,
    1215             :                int include_certs, gpgme_ctx_t ctx /* FIXME */)
    1216             : {
    1217           0 :   engine_uiserver_t uiserver = engine;
    1218           0 :   gpgme_error_t err = 0;
    1219             :   const char *protocol;
    1220             :   char *cmd;
    1221             :   gpgme_key_t key;
    1222             : 
    1223             :   (void)use_textmode;
    1224             :   (void)include_certs;
    1225             : 
    1226           0 :   if (!uiserver || !in || !out)
    1227           0 :     return gpg_error (GPG_ERR_INV_VALUE);
    1228           0 :   if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT)
    1229           0 :     protocol = "";
    1230           0 :   else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP)
    1231           0 :     protocol = " --protocol=OpenPGP";
    1232           0 :   else if (uiserver->protocol == GPGME_PROTOCOL_CMS)
    1233           0 :     protocol = " --protocol=CMS";
    1234             :   else
    1235           0 :     return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
    1236             : 
    1237           0 :   if (gpgrt_asprintf (&cmd, "SIGN%s%s", protocol,
    1238             :                 (mode == GPGME_SIG_MODE_DETACH) ? " --detached" : "") < 0)
    1239           0 :     return gpg_error_from_syserror ();
    1240             : 
    1241           0 :   key = gpgme_signers_enum (ctx, 0);
    1242           0 :   if (key)
    1243             :     {
    1244           0 :       const char *s = NULL;
    1245             : 
    1246           0 :       if (key && key->uids)
    1247           0 :         s = key->uids->email;
    1248             : 
    1249           0 :       if (s && strlen (s) < 80)
    1250           0 :         {
    1251             :           char buf[100];
    1252             : 
    1253           0 :           strcpy (stpcpy (buf, "SENDER --info "), s);
    1254           0 :           err = uiserver_assuan_simple_command (uiserver, buf,
    1255             :                                                 uiserver->status.fnc,
    1256             :                                                 uiserver->status.fnc_value);
    1257             :         }
    1258             :       else
    1259           0 :         err = gpg_error (GPG_ERR_INV_VALUE);
    1260           0 :       gpgme_key_unref (key);
    1261           0 :       if (err)
    1262             :         {
    1263           0 :           gpgrt_free (cmd);
    1264           0 :           return err;
    1265             :         }
    1266             :   }
    1267             : 
    1268           0 :   uiserver->input_cb.data = in;
    1269           0 :   err = uiserver_set_fd (uiserver, INPUT_FD,
    1270           0 :                          map_data_enc (uiserver->input_cb.data));
    1271           0 :   if (err)
    1272             :     {
    1273           0 :       gpgrt_free (cmd);
    1274           0 :       return err;
    1275             :     }
    1276           0 :   uiserver->output_cb.data = out;
    1277           0 :   err = uiserver_set_fd (uiserver, OUTPUT_FD, use_armor ? "--armor"
    1278           0 :                          : map_data_enc (uiserver->output_cb.data));
    1279           0 :   if (err)
    1280             :     {
    1281           0 :       gpgrt_free (cmd);
    1282           0 :       return err;
    1283             :     }
    1284           0 :   uiserver->inline_data = NULL;
    1285             : 
    1286           0 :   err = start (uiserver, cmd);
    1287           0 :   gpgrt_free (cmd);
    1288           0 :   return err;
    1289             : }
    1290             : 
    1291             : 
    1292             : /* FIXME: Missing a way to specify --silent.  */
    1293             : static gpgme_error_t
    1294           0 : uiserver_verify (void *engine, gpgme_data_t sig, gpgme_data_t signed_text,
    1295             :                  gpgme_data_t plaintext, gpgme_ctx_t ctx)
    1296             : {
    1297           0 :   engine_uiserver_t uiserver = engine;
    1298             :   gpgme_error_t err;
    1299             :   const char *protocol;
    1300             :   char *cmd;
    1301             : 
    1302             :   (void)ctx; /* FIXME: We should to add a --sender option to the
    1303             :               * UISever protocol.  */
    1304             : 
    1305           0 :   if (!uiserver)
    1306           0 :     return gpg_error (GPG_ERR_INV_VALUE);
    1307           0 :   if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT)
    1308           0 :     protocol = "";
    1309           0 :   else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP)
    1310           0 :     protocol = " --protocol=OpenPGP";
    1311           0 :   else if (uiserver->protocol == GPGME_PROTOCOL_CMS)
    1312           0 :     protocol = " --protocol=CMS";
    1313             :   else
    1314           0 :     return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
    1315             : 
    1316           0 :   if (gpgrt_asprintf (&cmd, "VERIFY%s", protocol) < 0)
    1317           0 :     return gpg_error_from_syserror ();
    1318             : 
    1319           0 :   uiserver->input_cb.data = sig;
    1320           0 :   err = uiserver_set_fd (uiserver, INPUT_FD,
    1321           0 :                          map_data_enc (uiserver->input_cb.data));
    1322           0 :   if (err)
    1323             :     {
    1324           0 :       gpgrt_free (cmd);
    1325           0 :       return err;
    1326             :     }
    1327           0 :   if (plaintext)
    1328             :     {
    1329             :       /* Normal or cleartext signature.  */
    1330           0 :       uiserver->output_cb.data = plaintext;
    1331           0 :       err = uiserver_set_fd (uiserver, OUTPUT_FD, 0);
    1332             :     }
    1333             :   else
    1334             :     {
    1335             :       /* Detached signature.  */
    1336           0 :       uiserver->message_cb.data = signed_text;
    1337           0 :       err = uiserver_set_fd (uiserver, MESSAGE_FD, 0);
    1338             :     }
    1339           0 :   uiserver->inline_data = NULL;
    1340             : 
    1341           0 :   if (!err)
    1342           0 :     err = start (uiserver, cmd);
    1343             : 
    1344           0 :   gpgrt_free (cmd);
    1345           0 :   return err;
    1346             : }
    1347             : 
    1348             : 
    1349             : /* This sets a status callback for monitoring status lines before they
    1350             :  * are passed to a caller set handler.  */
    1351             : static void
    1352           0 : uiserver_set_status_cb (void *engine, gpgme_status_cb_t cb, void *cb_value)
    1353             : {
    1354           0 :   engine_uiserver_t uiserver = engine;
    1355             : 
    1356           0 :   uiserver->status.mon_cb = cb;
    1357           0 :   uiserver->status.mon_cb_value = cb_value;
    1358           0 : }
    1359             : 
    1360             : 
    1361             : static void
    1362           0 : uiserver_set_status_handler (void *engine, engine_status_handler_t fnc,
    1363             :                           void *fnc_value)
    1364             : {
    1365           0 :   engine_uiserver_t uiserver = engine;
    1366             : 
    1367           0 :   uiserver->status.fnc = fnc;
    1368           0 :   uiserver->status.fnc_value = fnc_value;
    1369           0 : }
    1370             : 
    1371             : 
    1372             : static gpgme_error_t
    1373           0 : uiserver_set_colon_line_handler (void *engine, engine_colon_line_handler_t fnc,
    1374             :                               void *fnc_value)
    1375             : {
    1376           0 :   engine_uiserver_t uiserver = engine;
    1377             : 
    1378           0 :   uiserver->colon.fnc = fnc;
    1379           0 :   uiserver->colon.fnc_value = fnc_value;
    1380           0 :   uiserver->colon.any = 0;
    1381           0 :   return 0;
    1382             : }
    1383             : 
    1384             : 
    1385             : static void
    1386           0 : uiserver_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
    1387             : {
    1388           0 :   engine_uiserver_t uiserver = engine;
    1389           0 :   uiserver->io_cbs = *io_cbs;
    1390           0 : }
    1391             : 
    1392             : 
    1393             : static void
    1394           0 : uiserver_io_event (void *engine, gpgme_event_io_t type, void *type_data)
    1395             : {
    1396           0 :   engine_uiserver_t uiserver = engine;
    1397             : 
    1398           0 :   TRACE3 (DEBUG_ENGINE, "gpgme:uiserver_io_event", uiserver,
    1399             :           "event %p, type %d, type_data %p",
    1400             :           uiserver->io_cbs.event, type, type_data);
    1401           0 :   if (uiserver->io_cbs.event)
    1402           0 :     (*uiserver->io_cbs.event) (uiserver->io_cbs.event_priv, type, type_data);
    1403           0 : }
    1404             : 
    1405             : 
    1406             : struct engine_ops _gpgme_engine_ops_uiserver =
    1407             :   {
    1408             :     /* Static functions.  */
    1409             :     _gpgme_get_default_uisrv_socket,
    1410             :     NULL,
    1411             :     uiserver_get_version,
    1412             :     uiserver_get_req_version,
    1413             :     uiserver_new,
    1414             : 
    1415             :     /* Member functions.  */
    1416             :     uiserver_release,
    1417             :     uiserver_reset,
    1418             :     uiserver_set_status_cb,
    1419             :     uiserver_set_status_handler,
    1420             :     NULL,               /* set_command_handler */
    1421             :     uiserver_set_colon_line_handler,
    1422             :     uiserver_set_locale,
    1423             :     uiserver_set_protocol,
    1424             :     NULL,               /* set_engine_flags */
    1425             :     uiserver_decrypt,
    1426             :     NULL,               /* delete */
    1427             :     NULL,               /* edit */
    1428             :     uiserver_encrypt,
    1429             :     NULL,               /* encrypt_sign */
    1430             :     NULL,               /* export */
    1431             :     NULL,               /* export_ext */
    1432             :     NULL,               /* genkey */
    1433             :     NULL,               /* import */
    1434             :     NULL,               /* keylist */
    1435             :     NULL,               /* keylist_ext */
    1436             :     NULL,               /* keylist_data */
    1437             :     NULL,               /* keysign */
    1438             :     NULL,               /* tofu_policy */
    1439             :     uiserver_sign,
    1440             :     NULL,               /* trustlist */
    1441             :     uiserver_verify,
    1442             :     NULL,               /* getauditlog */
    1443             :     NULL,               /* opassuan_transact */
    1444             :     NULL,               /* conf_load */
    1445             :     NULL,               /* conf_save */
    1446             :     NULL,               /* conf_dir */
    1447             :     NULL,               /* query_swdb */
    1448             :     uiserver_set_io_cbs,
    1449             :     uiserver_io_event,
    1450             :     uiserver_cancel,
    1451             :     NULL,               /* cancel_op */
    1452             :     NULL,               /* passwd */
    1453             :     NULL,               /* set_pinentry_mode */
    1454             :     NULL                /* opspawn */
    1455             :   };

Generated by: LCOV version 1.13