LCOV - code coverage report
Current view: top level - src - engine-uiserver.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 4 621 0.6 %
Date: 2016-11-29 15:07:43 Functions: 2 28 7.1 %

          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          79 : uiserver_get_version (const char *file_name)
     125             : {
     126             :   (void)file_name;
     127          79 :   return NULL;
     128             : }
     129             : 
     130             : 
     131             : static const char *
     132          79 : uiserver_get_req_version (void)
     133             : {
     134          79 :   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 (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 :       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 (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 :           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 (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 :               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 (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 :       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, int verify,
     963             :                    gpgme_data_t ciph, gpgme_data_t plain,
     964             :                    int export_session_key, const char *override_session_key)
     965             : {
     966           0 :   engine_uiserver_t uiserver = engine;
     967             :   gpgme_error_t err;
     968             :   const char *protocol;
     969             :   char *cmd;
     970             : 
     971             :   (void)override_session_key; /* Fixme: We need to see now to add this
     972             :                                * to the UI server protocol  */
     973             : 
     974           0 :   if (!uiserver)
     975           0 :     return gpg_error (GPG_ERR_INV_VALUE);
     976           0 :   if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT)
     977           0 :     protocol = "";
     978           0 :   else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP)
     979           0 :     protocol = " --protocol=OpenPGP";
     980           0 :   else if (uiserver->protocol == GPGME_PROTOCOL_CMS)
     981           0 :     protocol = " --protocol=CMS";
     982             :   else
     983           0 :     return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
     984             : 
     985           0 :   if (asprintf (&cmd, "DECRYPT%s%s%s", protocol,
     986             :                 verify ? "" : " --no-verify",
     987             :                 export_session_key ? " --export-session-key" : "") < 0)
     988           0 :     return gpg_error_from_syserror ();
     989             : 
     990           0 :   uiserver->input_cb.data = ciph;
     991           0 :   err = uiserver_set_fd (uiserver, INPUT_FD,
     992           0 :                          map_data_enc (uiserver->input_cb.data));
     993           0 :   if (err)
     994             :     {
     995           0 :       free (cmd);
     996           0 :       return gpg_error (GPG_ERR_GENERAL);       /* FIXME */
     997             :     }
     998           0 :   uiserver->output_cb.data = plain;
     999           0 :   err = uiserver_set_fd (uiserver, OUTPUT_FD, 0);
    1000           0 :   if (err)
    1001             :     {
    1002           0 :       free (cmd);
    1003           0 :       return gpg_error (GPG_ERR_GENERAL);       /* FIXME */
    1004             :     }
    1005           0 :   uiserver->inline_data = NULL;
    1006             : 
    1007           0 :   err = start (engine, cmd);
    1008           0 :   free (cmd);
    1009           0 :   return err;
    1010             : }
    1011             : 
    1012             : 
    1013             : static gpgme_error_t
    1014           0 : uiserver_decrypt (void *engine, gpgme_data_t ciph, gpgme_data_t plain,
    1015             :                   int export_session_key, const char *override_session_key)
    1016             : {
    1017           0 :   return _uiserver_decrypt (engine, 0, ciph, plain,
    1018             :                             export_session_key, override_session_key);
    1019             : }
    1020             : 
    1021             : 
    1022             : static gpgme_error_t
    1023           0 : uiserver_decrypt_verify (void *engine, gpgme_data_t ciph, gpgme_data_t plain,
    1024             :                          int export_session_key,
    1025             :                          const char *override_session_key)
    1026             : {
    1027           0 :   return _uiserver_decrypt (engine, 1, ciph, plain,
    1028             :                             export_session_key, override_session_key);
    1029             : }
    1030             : 
    1031             : 
    1032             : static gpgme_error_t
    1033           0 : set_recipients (engine_uiserver_t uiserver, gpgme_key_t recp[])
    1034             : {
    1035           0 :   gpgme_error_t err = 0;
    1036             :   char *line;
    1037             :   int linelen;
    1038           0 :   int invalid_recipients = 0;
    1039             :   int i;
    1040             : 
    1041           0 :   linelen = 10 + 40 + 1;        /* "RECIPIENT " + guess + '\0'.  */
    1042           0 :   line = malloc (10 + 40 + 1);
    1043           0 :   if (!line)
    1044           0 :     return gpg_error_from_syserror ();
    1045           0 :   strcpy (line, "RECIPIENT ");
    1046           0 :   for (i=0; !err && recp[i]; i++)
    1047             :     {
    1048             :       char *uid;
    1049             :       int newlen;
    1050             : 
    1051             :       /* We use only the first user ID of the key.  */
    1052           0 :       if (!recp[i]->uids || !(uid=recp[i]->uids->uid) || !*uid)
    1053             :         {
    1054           0 :           invalid_recipients++;
    1055           0 :           continue;
    1056             :         }
    1057             : 
    1058           0 :       newlen = 11 + strlen (uid);
    1059           0 :       if (linelen < newlen)
    1060             :         {
    1061           0 :           char *newline = realloc (line, newlen);
    1062           0 :           if (! newline)
    1063             :             {
    1064           0 :               int saved_err = gpg_error_from_syserror ();
    1065           0 :               free (line);
    1066           0 :               return saved_err;
    1067             :             }
    1068           0 :           line = newline;
    1069           0 :           linelen = newlen;
    1070             :         }
    1071             :       /* FIXME: need to do proper escaping  */
    1072           0 :       strcpy (&line[10], uid);
    1073             : 
    1074           0 :       err = uiserver_assuan_simple_command (uiserver, line,
    1075             :                                             uiserver->status.fnc,
    1076             :                                             uiserver->status.fnc_value);
    1077             :       /* FIXME: This might requires more work.  */
    1078           0 :       if (gpg_err_code (err) == GPG_ERR_NO_PUBKEY)
    1079           0 :         invalid_recipients++;
    1080           0 :       else if (err)
    1081             :         {
    1082           0 :           free (line);
    1083           0 :           return err;
    1084             :         }
    1085             :     }
    1086           0 :   free (line);
    1087           0 :   return gpg_error (invalid_recipients
    1088             :                     ? GPG_ERR_UNUSABLE_PUBKEY : GPG_ERR_NO_ERROR);
    1089             : }
    1090             : 
    1091             : 
    1092             : static gpgme_error_t
    1093           0 : uiserver_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags,
    1094             :                   gpgme_data_t plain, gpgme_data_t ciph, int use_armor)
    1095             : {
    1096           0 :   engine_uiserver_t uiserver = engine;
    1097             :   gpgme_error_t err;
    1098             :   const char *protocol;
    1099             :   char *cmd;
    1100             : 
    1101           0 :   if (!uiserver)
    1102           0 :     return gpg_error (GPG_ERR_INV_VALUE);
    1103           0 :   if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT)
    1104           0 :     protocol = "";
    1105           0 :   else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP)
    1106           0 :     protocol = " --protocol=OpenPGP";
    1107           0 :   else if (uiserver->protocol == GPGME_PROTOCOL_CMS)
    1108           0 :     protocol = " --protocol=CMS";
    1109             :   else
    1110           0 :     return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
    1111             : 
    1112           0 :   if (flags & GPGME_ENCRYPT_PREPARE)
    1113             :     {
    1114           0 :       if (!recp || plain || ciph)
    1115           0 :         return gpg_error (GPG_ERR_INV_VALUE);
    1116             : 
    1117           0 :       if (asprintf (&cmd, "PREP_ENCRYPT%s%s", protocol,
    1118           0 :                     (flags & GPGME_ENCRYPT_EXPECT_SIGN)
    1119             :                     ? " --expect-sign" : "") < 0)
    1120           0 :         return gpg_error_from_syserror ();
    1121             :     }
    1122             :   else
    1123             :     {
    1124           0 :       if (!plain || !ciph)
    1125           0 :         return gpg_error (GPG_ERR_INV_VALUE);
    1126             : 
    1127           0 :       if (asprintf (&cmd, "ENCRYPT%s", protocol) < 0)
    1128           0 :         return gpg_error_from_syserror ();
    1129             :     }
    1130             : 
    1131           0 :   if (plain)
    1132             :     {
    1133           0 :       uiserver->input_cb.data = plain;
    1134           0 :       err = uiserver_set_fd (uiserver, INPUT_FD,
    1135           0 :                              map_data_enc (uiserver->input_cb.data));
    1136           0 :       if (err)
    1137             :         {
    1138           0 :           free (cmd);
    1139           0 :           return err;
    1140             :         }
    1141             :     }
    1142             : 
    1143           0 :   if (ciph)
    1144             :     {
    1145           0 :       uiserver->output_cb.data = ciph;
    1146           0 :       err = uiserver_set_fd (uiserver, OUTPUT_FD, use_armor ? "--armor"
    1147           0 :                              : map_data_enc (uiserver->output_cb.data));
    1148           0 :       if (err)
    1149             :         {
    1150           0 :           free (cmd);
    1151           0 :           return err;
    1152             :         }
    1153             :     }
    1154             : 
    1155           0 :   uiserver->inline_data = NULL;
    1156             : 
    1157           0 :   if (recp)
    1158             :     {
    1159           0 :       err = set_recipients (uiserver, recp);
    1160           0 :       if (err)
    1161             :         {
    1162           0 :           free (cmd);
    1163           0 :           return err;
    1164             :         }
    1165             :     }
    1166             : 
    1167           0 :   err = start (uiserver, cmd);
    1168           0 :   free (cmd);
    1169           0 :   return err;
    1170             : }
    1171             : 
    1172             : 
    1173             : static gpgme_error_t
    1174           0 : uiserver_sign (void *engine, gpgme_data_t in, gpgme_data_t out,
    1175             :                gpgme_sig_mode_t mode, int use_armor, int use_textmode,
    1176             :                int include_certs, gpgme_ctx_t ctx /* FIXME */)
    1177             : {
    1178           0 :   engine_uiserver_t uiserver = engine;
    1179           0 :   gpgme_error_t err = 0;
    1180             :   const char *protocol;
    1181             :   char *cmd;
    1182             :   gpgme_key_t key;
    1183             : 
    1184             :   (void)use_textmode;
    1185             :   (void)include_certs;
    1186             : 
    1187           0 :   if (!uiserver || !in || !out)
    1188           0 :     return gpg_error (GPG_ERR_INV_VALUE);
    1189           0 :   if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT)
    1190           0 :     protocol = "";
    1191           0 :   else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP)
    1192           0 :     protocol = " --protocol=OpenPGP";
    1193           0 :   else if (uiserver->protocol == GPGME_PROTOCOL_CMS)
    1194           0 :     protocol = " --protocol=CMS";
    1195             :   else
    1196           0 :     return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
    1197             : 
    1198           0 :   if (asprintf (&cmd, "SIGN%s%s", protocol,
    1199             :                 (mode == GPGME_SIG_MODE_DETACH) ? " --detached" : "") < 0)
    1200           0 :     return gpg_error_from_syserror ();
    1201             : 
    1202           0 :   key = gpgme_signers_enum (ctx, 0);
    1203           0 :   if (key)
    1204             :     {
    1205           0 :       const char *s = NULL;
    1206             : 
    1207           0 :       if (key && key->uids)
    1208           0 :         s = key->uids->email;
    1209             : 
    1210           0 :       if (s && strlen (s) < 80)
    1211           0 :         {
    1212             :           char buf[100];
    1213             : 
    1214           0 :           strcpy (stpcpy (buf, "SENDER --info "), s);
    1215           0 :           err = uiserver_assuan_simple_command (uiserver, buf,
    1216             :                                                 uiserver->status.fnc,
    1217             :                                                 uiserver->status.fnc_value);
    1218             :         }
    1219             :       else
    1220           0 :         err = gpg_error (GPG_ERR_INV_VALUE);
    1221           0 :       gpgme_key_unref (key);
    1222           0 :       if (err)
    1223             :       {
    1224           0 :         free (cmd);
    1225           0 :         return err;
    1226             :       }
    1227             :   }
    1228             : 
    1229           0 :   uiserver->input_cb.data = in;
    1230           0 :   err = uiserver_set_fd (uiserver, INPUT_FD,
    1231           0 :                          map_data_enc (uiserver->input_cb.data));
    1232           0 :   if (err)
    1233             :     {
    1234           0 :       free (cmd);
    1235           0 :       return err;
    1236             :     }
    1237           0 :   uiserver->output_cb.data = out;
    1238           0 :   err = uiserver_set_fd (uiserver, OUTPUT_FD, use_armor ? "--armor"
    1239           0 :                          : map_data_enc (uiserver->output_cb.data));
    1240           0 :   if (err)
    1241             :     {
    1242           0 :       free (cmd);
    1243           0 :       return err;
    1244             :     }
    1245           0 :   uiserver->inline_data = NULL;
    1246             : 
    1247           0 :   err = start (uiserver, cmd);
    1248           0 :   free (cmd);
    1249           0 :   return err;
    1250             : }
    1251             : 
    1252             : 
    1253             : /* FIXME: Missing a way to specify --silent.  */
    1254             : static gpgme_error_t
    1255           0 : uiserver_verify (void *engine, gpgme_data_t sig, gpgme_data_t signed_text,
    1256             :                  gpgme_data_t plaintext, gpgme_ctx_t ctx)
    1257             : {
    1258           0 :   engine_uiserver_t uiserver = engine;
    1259             :   gpgme_error_t err;
    1260             :   const char *protocol;
    1261             :   char *cmd;
    1262             : 
    1263             :   (void)ctx; /* FIXME: We should to add a --sender option to the
    1264             :               * UISever protocol.  */
    1265             : 
    1266           0 :   if (!uiserver)
    1267           0 :     return gpg_error (GPG_ERR_INV_VALUE);
    1268           0 :   if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT)
    1269           0 :     protocol = "";
    1270           0 :   else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP)
    1271           0 :     protocol = " --protocol=OpenPGP";
    1272           0 :   else if (uiserver->protocol == GPGME_PROTOCOL_CMS)
    1273           0 :     protocol = " --protocol=CMS";
    1274             :   else
    1275           0 :     return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
    1276             : 
    1277           0 :   if (asprintf (&cmd, "VERIFY%s", protocol) < 0)
    1278           0 :     return gpg_error_from_syserror ();
    1279             : 
    1280           0 :   uiserver->input_cb.data = sig;
    1281           0 :   err = uiserver_set_fd (uiserver, INPUT_FD,
    1282           0 :                          map_data_enc (uiserver->input_cb.data));
    1283           0 :   if (err)
    1284             :     {
    1285           0 :       free (cmd);
    1286           0 :       return err;
    1287             :     }
    1288           0 :   if (plaintext)
    1289             :     {
    1290             :       /* Normal or cleartext signature.  */
    1291           0 :       uiserver->output_cb.data = plaintext;
    1292           0 :       err = uiserver_set_fd (uiserver, OUTPUT_FD, 0);
    1293             :     }
    1294             :   else
    1295             :     {
    1296             :       /* Detached signature.  */
    1297           0 :       uiserver->message_cb.data = signed_text;
    1298           0 :       err = uiserver_set_fd (uiserver, MESSAGE_FD, 0);
    1299             :     }
    1300           0 :   uiserver->inline_data = NULL;
    1301             : 
    1302           0 :   if (!err)
    1303           0 :     err = start (uiserver, cmd);
    1304             : 
    1305           0 :   free (cmd);
    1306           0 :   return err;
    1307             : }
    1308             : 
    1309             : 
    1310             : /* This sets a status callback for monitoring status lines before they
    1311             :  * are passed to a caller set handler.  */
    1312             : static void
    1313           0 : uiserver_set_status_cb (void *engine, gpgme_status_cb_t cb, void *cb_value)
    1314             : {
    1315           0 :   engine_uiserver_t uiserver = engine;
    1316             : 
    1317           0 :   uiserver->status.mon_cb = cb;
    1318           0 :   uiserver->status.mon_cb_value = cb_value;
    1319           0 : }
    1320             : 
    1321             : 
    1322             : static void
    1323           0 : uiserver_set_status_handler (void *engine, engine_status_handler_t fnc,
    1324             :                           void *fnc_value)
    1325             : {
    1326           0 :   engine_uiserver_t uiserver = engine;
    1327             : 
    1328           0 :   uiserver->status.fnc = fnc;
    1329           0 :   uiserver->status.fnc_value = fnc_value;
    1330           0 : }
    1331             : 
    1332             : 
    1333             : static gpgme_error_t
    1334           0 : uiserver_set_colon_line_handler (void *engine, engine_colon_line_handler_t fnc,
    1335             :                               void *fnc_value)
    1336             : {
    1337           0 :   engine_uiserver_t uiserver = engine;
    1338             : 
    1339           0 :   uiserver->colon.fnc = fnc;
    1340           0 :   uiserver->colon.fnc_value = fnc_value;
    1341           0 :   uiserver->colon.any = 0;
    1342           0 :   return 0;
    1343             : }
    1344             : 
    1345             : 
    1346             : static void
    1347           0 : uiserver_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
    1348             : {
    1349           0 :   engine_uiserver_t uiserver = engine;
    1350           0 :   uiserver->io_cbs = *io_cbs;
    1351           0 : }
    1352             : 
    1353             : 
    1354             : static void
    1355           0 : uiserver_io_event (void *engine, gpgme_event_io_t type, void *type_data)
    1356             : {
    1357           0 :   engine_uiserver_t uiserver = engine;
    1358             : 
    1359           0 :   TRACE3 (DEBUG_ENGINE, "gpgme:uiserver_io_event", uiserver,
    1360             :           "event %p, type %d, type_data %p",
    1361             :           uiserver->io_cbs.event, type, type_data);
    1362           0 :   if (uiserver->io_cbs.event)
    1363           0 :     (*uiserver->io_cbs.event) (uiserver->io_cbs.event_priv, type, type_data);
    1364           0 : }
    1365             : 
    1366             : 
    1367             : struct engine_ops _gpgme_engine_ops_uiserver =
    1368             :   {
    1369             :     /* Static functions.  */
    1370             :     _gpgme_get_default_uisrv_socket,
    1371             :     NULL,
    1372             :     uiserver_get_version,
    1373             :     uiserver_get_req_version,
    1374             :     uiserver_new,
    1375             : 
    1376             :     /* Member functions.  */
    1377             :     uiserver_release,
    1378             :     uiserver_reset,
    1379             :     uiserver_set_status_cb,
    1380             :     uiserver_set_status_handler,
    1381             :     NULL,               /* set_command_handler */
    1382             :     uiserver_set_colon_line_handler,
    1383             :     uiserver_set_locale,
    1384             :     uiserver_set_protocol,
    1385             :     uiserver_decrypt,
    1386             :     uiserver_decrypt_verify,
    1387             :     NULL,               /* delete */
    1388             :     NULL,               /* edit */
    1389             :     uiserver_encrypt,
    1390             :     NULL,               /* encrypt_sign */
    1391             :     NULL,               /* export */
    1392             :     NULL,               /* export_ext */
    1393             :     NULL,               /* genkey */
    1394             :     NULL,               /* import */
    1395             :     NULL,               /* keylist */
    1396             :     NULL,               /* keylist_ext */
    1397             :     NULL,               /* keysign */
    1398             :     NULL,               /* tofu_policy */
    1399             :     uiserver_sign,
    1400             :     NULL,               /* trustlist */
    1401             :     uiserver_verify,
    1402             :     NULL,               /* getauditlog */
    1403             :     NULL,               /* opassuan_transact */
    1404             :     NULL,               /* conf_load */
    1405             :     NULL,               /* conf_save */
    1406             :     NULL,               /* query_swdb */
    1407             :     uiserver_set_io_cbs,
    1408             :     uiserver_io_event,
    1409             :     uiserver_cancel,
    1410             :     NULL,               /* cancel_op */
    1411             :     NULL,               /* passwd */
    1412             :     NULL,               /* set_pinentry_mode */
    1413             :     NULL                /* opspawn */
    1414             :   };

Generated by: LCOV version 1.11