LCOV - code coverage report
Current view: top level - src - engine-assuan.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 193 281 68.7 %
Date: 2017-03-02 17:11:10 Functions: 15 17 88.2 %

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

Generated by: LCOV version 1.13