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

Generated by: LCOV version 1.11