LCOV - code coverage report
Current view: top level - src - engine-assuan.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 201 298 67.4 %
Date: 2018-11-15 08:49:49 Functions: 16 18 88.9 %

          Line data    Source code
       1             : /* engine-assuan.c - Low-level Assuan protocol engine
       2             :  * Copyright (C) 2009 g10 Code GmbH
       3             :  *
       4             :  * This file is part of GPGME.
       5             :  *
       6             :  * GPGME is free software; you can redistribute it and/or modify it
       7             :  * under the terms of the GNU Lesser General Public License as
       8             :  * published by the Free Software Foundation; either version 2.1 of
       9             :  * the License, or (at your option) any later version.
      10             :  *
      11             :  * GPGME is distributed in the hope that it will be useful, but
      12             :  * WITHOUT ANY WARRANTY; without even the implied warranty of
      13             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      14             :  * Lesser General Public License for more details.
      15             :  *
      16             :  * You should have received a copy of the GNU Lesser General Public
      17             :  * License along with this program; if not, see <https://www.gnu.org/licenses/>.
      18             :  */
      19             : 
      20             : /*
      21             :    Note: This engine requires a modern Assuan server which uses
      22             :    gpg-error codes.  In particular there is no backward compatible
      23             :    mapping of old Assuan error codes implemented.
      24             : */
      25             : 
      26             : 
      27             : #if HAVE_CONFIG_H
      28             : #include <config.h>
      29             : #endif
      30             : 
      31             : #include <stdlib.h>
      32             : #include <string.h>
      33             : #ifdef HAVE_SYS_TYPES_H
      34             : # include <sys/types.h>
      35             : #endif
      36             : #include <assert.h>
      37             : #ifdef HAVE_UNISTD_H
      38             : # include <unistd.h>
      39             : #endif
      40             : #ifdef HAVE_LOCALE_H
      41             : #include <locale.h>
      42             : #endif
      43             : #include <errno.h>
      44             : 
      45             : #include "gpgme.h"
      46             : #include "util.h"
      47             : #include "ops.h"
      48             : #include "wait.h"
      49             : #include "priv-io.h"
      50             : #include "sema.h"
      51             : 
      52             : #include "assuan.h"
      53             : #include "debug.h"
      54             : 
      55             : #include "engine-backend.h"
      56             : 
      57             : 
      58             : typedef struct
      59             : {
      60             :   int fd;       /* FD we talk about.  */
      61             :   int server_fd;/* Server FD for this connection.  */
      62             :   int dir;      /* Inbound/Outbound, maybe given implicit?  */
      63             :   void *data;   /* Handler-specific data.  */
      64             :   void *tag;    /* ID from the user for gpgme_remove_io_callback.  */
      65             : } iocb_data_t;
      66             : 
      67             : /* Engine instance data.  */
      68             : struct engine_llass
      69             : {
      70             :   assuan_context_t assuan_ctx;
      71             : 
      72             :   int lc_ctype_set;
      73             :   int lc_messages_set;
      74             : 
      75             :   iocb_data_t status_cb;
      76             : 
      77             :   struct gpgme_io_cbs io_cbs;
      78             : 
      79             :   /* Hack for old opassuan.c interface, see there the result struct.  */
      80             :   gpg_error_t last_op_err;
      81             : 
      82             :   /* User provided callbacks.  */
      83             :   struct {
      84             :     gpgme_assuan_data_cb_t data_cb;
      85             :     void *data_cb_value;
      86             : 
      87             :     gpgme_assuan_inquire_cb_t inq_cb;
      88             :     void *inq_cb_value;
      89             : 
      90             :     gpgme_assuan_status_cb_t status_cb;
      91             :     void *status_cb_value;
      92             :   } user;
      93             : 
      94             :   /* Option flags.  */
      95             :   struct {
      96             :     int gpg_agent:1;  /* Assume this is a gpg-agent connection.  */
      97             :   } opt;
      98             : 
      99             :   char request_origin[10];  /* Copy from the CTX.  */
     100             : };
     101             : typedef struct engine_llass *engine_llass_t;
     102             : 
     103             : 
     104           0 : gpg_error_t _gpgme_engine_assuan_last_op_err (void *engine)
     105             : {
     106           0 :   engine_llass_t llass = engine;
     107           0 :   return llass->last_op_err;
     108             : }
     109             : 
     110             : 
     111             : /* Prototypes.  */
     112             : static void llass_io_event (void *engine,
     113             :                             gpgme_event_io_t type, void *type_data);
     114             : 
     115             : 
     116             : 
     117             : 
     118             : 
     119             : /* return the default home directory.  */
     120             : static const char *
     121         131 : llass_get_home_dir (void)
     122             : {
     123             :   /* For this engine the home directory is not a filename but a string
     124             :      used to convey options.  The exclamation mark is a marker to show
     125             :      that this is not a directory name. Options are strings delimited
     126             :      by a space.  The only option defined for now is GPG_AGENT to
     127             :      enable GPG_AGENT specific commands to send to the server at
     128             :      connection startup.  */
     129         131 :   return "!GPG_AGENT";
     130             : }
     131             : 
     132             : static char *
     133         131 : llass_get_version (const char *file_name)
     134             : {
     135             :   (void)file_name;
     136         131 :   return NULL;
     137             : }
     138             : 
     139             : 
     140             : static const char *
     141         117 : llass_get_req_version (void)
     142             : {
     143         117 :   return NULL;
     144             : }
     145             : 
     146             : 
     147             : static void
     148          16 : close_notify_handler (int fd, void *opaque)
     149             : {
     150          16 :   engine_llass_t llass = opaque;
     151             : 
     152          16 :   assert (fd != -1);
     153          16 :   if (llass->status_cb.fd == fd)
     154             :     {
     155          16 :       if (llass->status_cb.tag)
     156          16 :         llass->io_cbs.remove (llass->status_cb.tag);
     157          16 :       llass->status_cb.fd = -1;
     158          16 :       llass->status_cb.tag = NULL;
     159             :     }
     160          16 : }
     161             : 
     162             : 
     163             : 
     164             : static gpgme_error_t
     165          14 : llass_cancel (void *engine)
     166             : {
     167          14 :   engine_llass_t llass = engine;
     168             : 
     169          14 :   if (!llass)
     170           0 :     return gpg_error (GPG_ERR_INV_VALUE);
     171             : 
     172          14 :   if (llass->status_cb.fd != -1)
     173           0 :     _gpgme_io_close (llass->status_cb.fd);
     174             : 
     175          14 :   if (llass->assuan_ctx)
     176             :     {
     177          14 :       assuan_release (llass->assuan_ctx);
     178          14 :       llass->assuan_ctx = NULL;
     179             :     }
     180             : 
     181          14 :   return 0;
     182             : }
     183             : 
     184             : 
     185             : static gpgme_error_t
     186           2 : llass_cancel_op (void *engine)
     187             : {
     188           2 :   engine_llass_t llass = engine;
     189             : 
     190           2 :   if (!llass)
     191           0 :     return gpg_error (GPG_ERR_INV_VALUE);
     192             : 
     193           2 :   if (llass->status_cb.fd != -1)
     194           2 :     _gpgme_io_close (llass->status_cb.fd);
     195             : 
     196           2 :   return 0;
     197             : }
     198             : 
     199             : 
     200             : static void
     201          14 : llass_release (void *engine)
     202             : {
     203          14 :   engine_llass_t llass = engine;
     204             : 
     205          14 :   if (!llass)
     206           0 :     return;
     207             : 
     208          14 :   llass_cancel (engine);
     209             : 
     210          14 :   free (llass);
     211             : }
     212             : 
     213             : 
     214             : /* Create a new instance. If HOME_DIR is NULL standard options for use
     215             :    with gpg-agent are issued.  */
     216             : static gpgme_error_t
     217          14 : llass_new (void **engine, const char *file_name, const char *home_dir,
     218             :            const char *version)
     219             : {
     220          14 :   gpgme_error_t err = 0;
     221             :   engine_llass_t llass;
     222             :   char *optstr;
     223          14 :   char *env_tty = NULL;
     224             : 
     225             :   (void)version; /* Not yet used.  */
     226             : 
     227          14 :   llass = calloc (1, sizeof *llass);
     228          14 :   if (!llass)
     229           0 :     return gpg_error_from_syserror ();
     230             : 
     231          14 :   llass->status_cb.fd = -1;
     232          14 :   llass->status_cb.dir = 1;
     233          14 :   llass->status_cb.tag = 0;
     234          14 :   llass->status_cb.data = llass;
     235             : 
     236             :   /* Parse_options.  */
     237          14 :   if (home_dir && *home_dir == '!')
     238             :     {
     239          14 :       home_dir++;
     240             :       /* Very simple parser only working for the one option we support.  */
     241             :       /* Note that wk promised to write a regression test if this
     242             :          parser will be extended.  */
     243          14 :       if (!strncmp (home_dir, "GPG_AGENT", 9)
     244          14 :           && (!home_dir[9] || home_dir[9] == ' '))
     245          14 :         llass->opt.gpg_agent = 1;
     246             :     }
     247             : 
     248          14 :   err = assuan_new_ext (&llass->assuan_ctx, GPG_ERR_SOURCE_GPGME,
     249             :                         &_gpgme_assuan_malloc_hooks, _gpgme_assuan_log_cb,
     250             :                         NULL);
     251          14 :   if (err)
     252           0 :     goto leave;
     253          14 :   assuan_ctx_set_system_hooks (llass->assuan_ctx, &_gpgme_assuan_system_hooks);
     254          14 :   assuan_set_flag (llass->assuan_ctx, ASSUAN_CONVEY_COMMENTS, 1);
     255             : 
     256          14 :   err = assuan_socket_connect (llass->assuan_ctx, file_name, 0, 0);
     257          14 :   if (err)
     258          12 :     goto leave;
     259             : 
     260           2 :   if (llass->opt.gpg_agent)
     261             :     {
     262           2 :       char *dft_display = NULL;
     263             : 
     264           2 :       err = _gpgme_getenv ("DISPLAY", &dft_display);
     265           2 :       if (err)
     266           0 :         goto leave;
     267           2 :       if (dft_display)
     268             :         {
     269           2 :           if (gpgrt_asprintf (&optstr, "OPTION display=%s", dft_display) < 0)
     270             :             {
     271           0 :               err = gpg_error_from_syserror ();
     272           0 :               free (dft_display);
     273           0 :               goto leave;
     274             :             }
     275           2 :           free (dft_display);
     276             : 
     277           2 :           err = assuan_transact (llass->assuan_ctx, optstr, NULL, NULL, NULL,
     278             :                                  NULL, NULL, NULL);
     279           2 :           gpgrt_free (optstr);
     280           2 :           if (err)
     281           0 :             goto leave;
     282             :         }
     283             :     }
     284             : 
     285           2 :   if (llass->opt.gpg_agent)
     286           2 :     err = _gpgme_getenv ("GPG_TTY", &env_tty);
     287             : 
     288           2 :   if (llass->opt.gpg_agent && (isatty (1) || env_tty || err))
     289             :     {
     290           2 :       int rc = 0;
     291             :       char dft_ttyname[64];
     292           2 :       char *dft_ttytype = NULL;
     293             : 
     294           2 :       if (err)
     295           0 :         goto leave;
     296           2 :       else if (env_tty)
     297             :         {
     298           0 :           snprintf (dft_ttyname, sizeof (dft_ttyname), "%s", env_tty);
     299           0 :           free (env_tty);
     300             :         }
     301             :       else
     302           2 :         rc = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname));
     303             : 
     304             :       /* Even though isatty() returns 1, ttyname_r() may fail in many
     305             :          ways, e.g., when /dev/pts is not accessible under chroot.  */
     306           2 :       if (!rc)
     307             :         {
     308           2 :           if (gpgrt_asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0)
     309             :             {
     310           0 :               err = gpg_error_from_syserror ();
     311           0 :               goto leave;
     312             :             }
     313           2 :           err = assuan_transact (llass->assuan_ctx, optstr, NULL, NULL, NULL,
     314             :                                  NULL, NULL, NULL);
     315           2 :           gpgrt_free (optstr);
     316           2 :           if (err)
     317           0 :             goto leave;
     318             : 
     319           2 :           err = _gpgme_getenv ("TERM", &dft_ttytype);
     320           2 :           if (err)
     321           0 :             goto leave;
     322           2 :           if (dft_ttytype)
     323             :             {
     324           2 :               if (gpgrt_asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype)< 0)
     325             :                 {
     326           0 :                   err = gpg_error_from_syserror ();
     327           0 :                   free (dft_ttytype);
     328           0 :                   goto leave;
     329             :                 }
     330           2 :               free (dft_ttytype);
     331             : 
     332           2 :               err = assuan_transact (llass->assuan_ctx, optstr, NULL, NULL,
     333             :                                      NULL, NULL, NULL, NULL);
     334           2 :               gpgrt_free (optstr);
     335           2 :               if (err)
     336           0 :                 goto leave;
     337             :             }
     338             :         }
     339             :     }
     340             : 
     341             : 
     342             : #ifdef HAVE_W32_SYSTEM
     343             :   /* Under Windows we need to use AllowSetForegroundWindow.  Tell
     344             :      llass to tell us when it needs it.  */
     345             :   if (!err && llass->opt.gpg_agent)
     346             :     {
     347             :       err = assuan_transact (llass->assuan_ctx, "OPTION allow-pinentry-notify",
     348             :                              NULL, NULL, NULL, NULL, NULL, NULL);
     349             :       if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
     350             :         err = 0; /* This work only with recent gpg-agents.  */
     351             :     }
     352             : #endif /*HAVE_W32_SYSTEM*/
     353             : 
     354             : 
     355             :  leave:
     356             :   /* Close the server ends of the pipes (because of this, we must use
     357             :      the stored server_fd_str in the function start).  Our ends are
     358             :      closed in llass_release().  */
     359             : 
     360          14 :   if (err)
     361          12 :     llass_release (llass);
     362             :   else
     363           2 :     *engine = llass;
     364             : 
     365          14 :   return err;
     366             : }
     367             : 
     368             : 
     369             : /* Copy flags from CTX into the engine object.  */
     370             : static void
     371           2 : llass_set_engine_flags (void *engine, const gpgme_ctx_t ctx)
     372             : {
     373           2 :   engine_llass_t llass = engine;
     374             : 
     375           2 :   if (ctx->request_origin)
     376             :     {
     377           0 :       if (strlen (ctx->request_origin) + 1 > sizeof llass->request_origin)
     378           0 :         strcpy (llass->request_origin, "xxx"); /* Too long  - force error */
     379             :       else
     380           0 :         strcpy (llass->request_origin, ctx->request_origin);
     381             :     }
     382             :   else
     383           2 :     *llass->request_origin = 0;
     384           2 : }
     385             : 
     386             : 
     387             : static gpgme_error_t
     388           4 : llass_set_locale (void *engine, int category, const char *value)
     389             : {
     390             :   gpgme_error_t err;
     391           4 :   engine_llass_t llass = engine;
     392             :   char *optstr;
     393             :   const char *catstr;
     394             : 
     395           4 :   if (!llass->opt.gpg_agent)
     396           0 :     return 0;
     397             : 
     398             :   /* FIXME: If value is NULL, we need to reset the option to default.
     399             :      But we can't do this.  So we error out here.  gpg-agent needs
     400             :      support for this.  */
     401             :   if (0)
     402             :     ;
     403             : #ifdef LC_CTYPE
     404           4 :   else if (category == LC_CTYPE)
     405             :     {
     406           2 :       catstr = "lc-ctype";
     407           2 :       if (!value && llass->lc_ctype_set)
     408           0 :         return gpg_error (GPG_ERR_INV_VALUE);
     409           2 :       if (value)
     410           0 :         llass->lc_ctype_set = 1;
     411             :     }
     412             : #endif
     413             : #ifdef LC_MESSAGES
     414           2 :   else if (category == LC_MESSAGES)
     415             :     {
     416           2 :       catstr = "lc-messages";
     417           2 :       if (!value && llass->lc_messages_set)
     418           0 :         return gpg_error (GPG_ERR_INV_VALUE);
     419           2 :       if (value)
     420           0 :         llass->lc_messages_set = 1;
     421             :     }
     422             : #endif /* LC_MESSAGES */
     423             :   else
     424           0 :     return gpg_error (GPG_ERR_INV_VALUE);
     425             : 
     426             :   /* FIXME: Reset value to default.  */
     427           4 :   if (!value)
     428           4 :     return 0;
     429             : 
     430           0 :   if (gpgrt_asprintf (&optstr, "OPTION %s=%s", catstr, value) < 0)
     431           0 :     err = gpg_error_from_syserror ();
     432             :   else
     433             :     {
     434           0 :       err = assuan_transact (llass->assuan_ctx, optstr, NULL, NULL,
     435             :                              NULL, NULL, NULL, NULL);
     436           0 :       gpgrt_free (optstr);
     437             :     }
     438           0 :   return err;
     439             : }
     440             : 
     441             : 
     442             : /* This is the inquiry callback.  It handles stuff which ee need to
     443             :    handle here and passes everything on to the user callback.  */
     444             : static gpgme_error_t
     445           0 : inquire_cb (engine_llass_t llass, const char *keyword, const char *args)
     446             : {
     447             :   gpg_error_t err;
     448             : 
     449           0 :   if (llass->opt.gpg_agent && !strcmp (keyword, "PINENTRY_LAUNCHED"))
     450             :     {
     451           0 :       _gpgme_allow_set_foreground_window ((pid_t)strtoul (args, NULL, 10));
     452             :     }
     453             : 
     454           0 :   if (llass->user.inq_cb)
     455             :     {
     456           0 :       gpgme_data_t data = NULL;
     457             : 
     458           0 :       err = llass->user.inq_cb (llass->user.inq_cb_value,
     459             :                                 keyword, args, &data);
     460           0 :       if (!err && data)
     461             :         {
     462             :           /* FIXME: Returning data is not yet implemented.  However we
     463             :              need to allow the caller to cleanup his data object.
     464             :              Thus we run the callback in finish mode immediately.  */
     465           0 :           err = llass->user.inq_cb (llass->user.inq_cb_value,
     466             :                                     NULL, NULL, &data);
     467             :         }
     468             :     }
     469             :   else
     470           0 :     err = 0;
     471             : 
     472           0 :   return err;
     473             : }
     474             : 
     475             : 
     476             : static gpgme_error_t
     477          16 : llass_status_handler (void *opaque, int fd)
     478             : {
     479          16 :   struct io_cb_data *data = (struct io_cb_data *) opaque;
     480          16 :   engine_llass_t llass = (engine_llass_t) data->handler_value;
     481          16 :   gpgme_error_t err = 0;
     482             :   char *line;
     483             :   size_t linelen;
     484             : 
     485             :   do
     486             :     {
     487          22 :       err = assuan_read_line (llass->assuan_ctx, &line, &linelen);
     488          22 :       if (err)
     489             :         {
     490             :           /* Reading a full line may not be possible when
     491             :              communicating over a socket in nonblocking mode.  In this
     492             :              case, we are done for now.  */
     493           0 :           if (gpg_err_code (err) == GPG_ERR_EAGAIN)
     494             :             {
     495           0 :               TRACE1 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
     496             :                       "fd 0x%x: EAGAIN reading assuan line (ignored)", fd);
     497           0 :               err = 0;
     498           0 :               continue;
     499             :             }
     500             : 
     501           0 :           TRACE2 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
     502             :                   "fd 0x%x: error reading assuan line: %s",
     503             :                   fd, gpg_strerror (err));
     504             :         }
     505          22 :       else if (linelen >= 2 && line[0] == 'D' && line[1] == ' ')
     506           4 :         {
     507           4 :           char *src = line + 2;
     508           4 :           char *end = line + linelen;
     509           4 :           char *dst = src;
     510             : 
     511           4 :           linelen = 0;
     512          48 :           while (src < end)
     513             :             {
     514          40 :               if (*src == '%' && src + 2 < end)
     515             :                 {
     516             :                   /* Handle escaped characters.  */
     517           0 :                   ++src;
     518           0 :                   *dst++ = _gpgme_hextobyte (src);
     519           0 :                   src += 2;
     520             :                 }
     521             :               else
     522          40 :                 *dst++ = *src++;
     523             : 
     524          40 :               linelen++;
     525             :             }
     526             : 
     527           4 :           src = line + 2;
     528           4 :           if (linelen && llass->user.data_cb)
     529           4 :             err = llass->user.data_cb (llass->user.data_cb_value,
     530             :                                        src, linelen);
     531             : 
     532           4 :           TRACE2 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
     533             :                   "fd 0x%x: D inlinedata; status from cb: %s",
     534             :                   fd, (llass->user.data_cb ?
     535             :                        (err? gpg_strerror (err):"ok"):"no callback"));
     536             :         }
     537          18 :       else if (linelen >= 3
     538           4 :                && line[0] == 'E' && line[1] == 'N' && line[2] == 'D'
     539           0 :                && (line[3] == '\0' || line[3] == ' '))
     540             :         {
     541             :           /* END received.  Tell the data callback.  */
     542           0 :           if (llass->user.data_cb)
     543           0 :             err = llass->user.data_cb (llass->user.data_cb_value, NULL, 0);
     544             : 
     545           0 :           TRACE2 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
     546             :                   "fd 0x%x: END line; status from cb: %s",
     547             :                   fd, (llass->user.data_cb ?
     548             :                        (err? gpg_strerror (err):"ok"):"no callback"));
     549             :         }
     550          18 :       else if (linelen > 2 && line[0] == 'S' && line[1] == ' ')
     551           2 :         {
     552             :           char *args;
     553             :           char *src;
     554             : 
     555           2 :           for (src=line+2; *src == ' '; src++)
     556             :             ;
     557             : 
     558           2 :           args = strchr (src, ' ');
     559           2 :           if (!args)
     560           0 :             args = line + linelen; /* Let it point to an empty string.  */
     561             :           else
     562           2 :             *(args++) = 0;
     563             : 
     564           4 :           while (*args == ' ')
     565           0 :             args++;
     566             : 
     567           2 :           if (llass->user.status_cb)
     568           2 :             err = llass->user.status_cb (llass->user.status_cb_value,
     569             :                                          src, args);
     570             : 
     571           2 :           TRACE3 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
     572             :                   "fd 0x%x: S line (%s) - status from cb: %s",
     573             :                   fd, line+2, (llass->user.status_cb ?
     574             :                                (err? gpg_strerror (err):"ok"):"no callback"));
     575             :         }
     576          16 :       else if (linelen >= 7
     577           2 :                && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
     578           0 :                && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
     579           0 :                && line[6] == 'E'
     580           0 :                && (line[7] == '\0' || line[7] == ' '))
     581           0 :         {
     582             :           char *src;
     583             :           char *args;
     584             : 
     585           0 :           for (src=line+7; *src == ' '; src++)
     586             :             ;
     587             : 
     588           0 :           args = strchr (src, ' ');
     589           0 :           if (!args)
     590           0 :             args = line + linelen; /* Let it point to an empty string.  */
     591             :           else
     592           0 :             *(args++) = 0;
     593             : 
     594           0 :           while (*args == ' ')
     595           0 :             args++;
     596             : 
     597           0 :           err = inquire_cb (llass, src, args);
     598           0 :           if (!err)
     599             :             {
     600             :               /* Flush and send END.  */
     601           0 :               err = assuan_send_data (llass->assuan_ctx, NULL, 0);
     602             :             }
     603           0 :           else if (gpg_err_code (err) == GPG_ERR_ASS_CANCELED)
     604             :             {
     605             :               /* Flush and send CANcel.  */
     606           0 :               err = assuan_send_data (llass->assuan_ctx, NULL, 1);
     607             :             }
     608             :         }
     609          16 :       else if (linelen >= 3
     610           2 :                && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
     611           2 :                && (line[3] == '\0' || line[3] == ' '))
     612             :         {
     613           2 :           if (line[3] == ' ')
     614           2 :             err = atoi (line+4);
     615             :           else
     616           0 :             err = gpg_error (GPG_ERR_GENERAL);
     617           2 :           TRACE2 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
     618             :                   "fd 0x%x: ERR line: %s",
     619             :                   fd, err ? gpg_strerror (err) : "ok");
     620             : 
     621             :           /* Command execution errors are not fatal, as we use
     622             :              a session based protocol.  */
     623           2 :           data->op_err = err;
     624           2 :           llass->last_op_err = err;
     625             : 
     626             :           /* The caller will do the rest (namely, call cancel_op,
     627             :              which closes status_fd).  */
     628           2 :           return 0;
     629             :         }
     630          14 :       else if (linelen >= 2
     631          14 :                && line[0] == 'O' && line[1] == 'K'
     632          14 :                && (line[2] == '\0' || line[2] == ' '))
     633             :         {
     634          14 :           TRACE1 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
     635             :                   "fd 0x%x: OK line", fd);
     636             : 
     637          14 :           llass->last_op_err = 0;
     638             : 
     639          14 :           _gpgme_io_close (llass->status_cb.fd);
     640          14 :           return 0;
     641             :         }
     642             :       else
     643             :         {
     644             :           /* Comment line or invalid line.  */
     645             :         }
     646             : 
     647             :     }
     648           6 :   while (!err && assuan_pending_line (llass->assuan_ctx));
     649             : 
     650           0 :   return err;
     651             : }
     652             : 
     653             : 
     654             : static gpgme_error_t
     655          16 : add_io_cb (engine_llass_t llass, iocb_data_t *iocbd, gpgme_io_cb_t handler)
     656             : {
     657             :   gpgme_error_t err;
     658             : 
     659          16 :   TRACE_BEG2 (DEBUG_ENGINE, "engine-assuan:add_io_cb", llass,
     660             :               "fd %d, dir %d", iocbd->fd, iocbd->dir);
     661          16 :   err = (*llass->io_cbs.add) (llass->io_cbs.add_priv,
     662             :                               iocbd->fd, iocbd->dir,
     663             :                               handler, iocbd->data, &iocbd->tag);
     664          16 :   if (err)
     665           0 :     return TRACE_ERR (err);
     666          16 :   if (!iocbd->dir)
     667             :     /* FIXME Kludge around poll() problem.  */
     668           0 :     err = _gpgme_io_set_nonblocking (iocbd->fd);
     669          16 :   return TRACE_ERR (err);
     670             : }
     671             : 
     672             : 
     673             : static gpgme_error_t
     674          16 : start (engine_llass_t llass, const char *command)
     675             : {
     676             :   gpgme_error_t err;
     677             :   assuan_fd_t afdlist[5];
     678             :   int fdlist[5];
     679             :   int nfds;
     680             :   int i;
     681             : 
     682          16 :   if (*llass->request_origin && llass->opt.gpg_agent)
     683             :     {
     684             :       char *cmd;
     685             : 
     686           0 :       cmd = _gpgme_strconcat ("OPTION pretend-request-origin=",
     687           0 :                               llass->request_origin, NULL);
     688           0 :       if (!cmd)
     689           0 :         return gpg_error_from_syserror ();
     690           0 :       err = assuan_transact (llass->assuan_ctx, cmd, NULL, NULL, NULL,
     691             :                              NULL, NULL, NULL);
     692           0 :       free (cmd);
     693           0 :       if (err && gpg_err_code (err) != GPG_ERR_UNKNOWN_OPTION)
     694           0 :         return err;
     695             :     }
     696             : 
     697             :   /* We need to know the fd used by assuan for reads.  We do this by
     698             :      using the assumption that the first returned fd from
     699             :      assuan_get_active_fds() is always this one.  */
     700          16 :   nfds = assuan_get_active_fds (llass->assuan_ctx, 0 /* read fds */,
     701             :                                 afdlist, DIM (afdlist));
     702          16 :   if (nfds < 1)
     703           0 :     return gpg_error (GPG_ERR_GENERAL); /* FIXME */
     704             :   /* For now... */
     705          32 :   for (i = 0; i < nfds; i++)
     706          16 :     fdlist[i] = (int) afdlist[i];
     707             : 
     708             :   /* We "duplicate" the file descriptor, so we can close it here (we
     709             :      can't close fdlist[0], as that is closed by libassuan, and
     710             :      closing it here might cause libassuan to close some unrelated FD
     711             :      later).  Alternatively, we could special case status_fd and
     712             :      register/unregister it manually as needed, but this increases
     713             :      code duplication and is more complicated as we can not use the
     714             :      close notifications etc.  A third alternative would be to let
     715             :      Assuan know that we closed the FD, but that complicates the
     716             :      Assuan interface.  */
     717             : 
     718          16 :   llass->status_cb.fd = _gpgme_io_dup (fdlist[0]);
     719          16 :   if (llass->status_cb.fd < 0)
     720           0 :     return gpg_error_from_syserror ();
     721             : 
     722          16 :   if (_gpgme_io_set_close_notify (llass->status_cb.fd,
     723             :                                   close_notify_handler, llass))
     724             :     {
     725           0 :       _gpgme_io_close (llass->status_cb.fd);
     726           0 :       llass->status_cb.fd = -1;
     727           0 :       return gpg_error (GPG_ERR_GENERAL);
     728             :     }
     729             : 
     730          16 :   err = add_io_cb (llass, &llass->status_cb, llass_status_handler);
     731          16 :   if (!err)
     732          16 :     err = assuan_write_line (llass->assuan_ctx, command);
     733             : 
     734             :   /* FIXME: If *command == '#' no answer is expected.  */
     735             : 
     736          16 :   if (!err)
     737          16 :     llass_io_event (llass, GPGME_EVENT_START, NULL);
     738             : 
     739          16 :   return err;
     740             : }
     741             : 
     742             : 
     743             : 
     744             : static gpgme_error_t
     745          16 : llass_transact (void *engine,
     746             :                 const char *command,
     747             :                 gpgme_assuan_data_cb_t data_cb,
     748             :                 void *data_cb_value,
     749             :                 gpgme_assuan_inquire_cb_t inq_cb,
     750             :                 void *inq_cb_value,
     751             :                 gpgme_assuan_status_cb_t status_cb,
     752             :                 void *status_cb_value)
     753             : {
     754          16 :   engine_llass_t llass = engine;
     755             :   gpgme_error_t err;
     756             : 
     757          16 :   if (!llass || !command || !*command)
     758           0 :     return gpg_error (GPG_ERR_INV_VALUE);
     759             : 
     760          16 :   llass->user.data_cb = data_cb;
     761          16 :   llass->user.data_cb_value = data_cb_value;
     762          16 :   llass->user.inq_cb = inq_cb;
     763          16 :   llass->user.inq_cb_value = inq_cb_value;
     764          16 :   llass->user.status_cb = status_cb;
     765          16 :   llass->user.status_cb_value = status_cb_value;
     766             : 
     767          16 :   err = start (llass, command);
     768          16 :   return err;
     769             : }
     770             : 
     771             : 
     772             : 
     773             : static void
     774          16 : llass_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
     775             : {
     776          16 :   engine_llass_t llass = engine;
     777          16 :   llass->io_cbs = *io_cbs;
     778          16 : }
     779             : 
     780             : 
     781             : static void
     782          32 : llass_io_event (void *engine, gpgme_event_io_t type, void *type_data)
     783             : {
     784          32 :   engine_llass_t llass = engine;
     785             : 
     786          32 :   TRACE3 (DEBUG_ENGINE, "gpgme:llass_io_event", llass,
     787             :           "event %p, type %d, type_data %p",
     788             :           llass->io_cbs.event, type, type_data);
     789          32 :   if (llass->io_cbs.event)
     790          32 :     (*llass->io_cbs.event) (llass->io_cbs.event_priv, type, type_data);
     791          32 : }
     792             : 
     793             : 
     794             : struct engine_ops _gpgme_engine_ops_assuan =
     795             :   {
     796             :     /* Static functions.  */
     797             :     _gpgme_get_default_agent_socket,
     798             :     llass_get_home_dir,
     799             :     llass_get_version,
     800             :     llass_get_req_version,
     801             :     llass_new,
     802             : 
     803             :     /* Member functions.  */
     804             :     llass_release,
     805             :     NULL,               /* reset */
     806             :     NULL,               /* set_status_cb */
     807             :     NULL,               /* set_status_handler */
     808             :     NULL,               /* set_command_handler */
     809             :     NULL,               /* set_colon_line_handler */
     810             :     llass_set_locale,
     811             :     NULL,               /* set_protocol */
     812             :     llass_set_engine_flags,
     813             :     NULL,               /* decrypt */
     814             :     NULL,               /* delete */
     815             :     NULL,               /* edit */
     816             :     NULL,               /* encrypt */
     817             :     NULL,               /* encrypt_sign */
     818             :     NULL,               /* export */
     819             :     NULL,               /* export_ext */
     820             :     NULL,               /* genkey */
     821             :     NULL,               /* import */
     822             :     NULL,               /* keylist */
     823             :     NULL,               /* keylist_ext */
     824             :     NULL,               /* keylist_data */
     825             :     NULL,               /* keysign */
     826             :     NULL,               /* tofu_policy */
     827             :     NULL,               /* sign */
     828             :     NULL,               /* trustlist */
     829             :     NULL,               /* verify */
     830             :     NULL,               /* getauditlog */
     831             :     llass_transact,     /* opassuan_transact */
     832             :     NULL,               /* conf_load */
     833             :     NULL,               /* conf_save */
     834             :     NULL,               /* conf_dir */
     835             :     NULL,               /* query_swdb */
     836             :     llass_set_io_cbs,
     837             :     llass_io_event,
     838             :     llass_cancel,
     839             :     llass_cancel_op,
     840             :     NULL,               /* passwd */
     841             :     NULL,               /* set_pinentry_mode */
     842             :     NULL                /* opspawn */
     843             :   };

Generated by: LCOV version 1.13