LCOV - code coverage report
Current view: top level - src - engine-assuan.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 6 271 2.2 %
Date: 2015-11-05 17:14:26 Functions: 3 17 17.6 %

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

Generated by: LCOV version 1.11