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

Generated by: LCOV version 1.11