LCOV - code coverage report
Current view: top level - src - engine-uiserver.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 4 599 0.7 %
Date: 2015-11-05 17:14:26 Functions: 2 27 7.4 %

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

Generated by: LCOV version 1.11