LCOV - code coverage report
Current view: top level - src - engine-gpgconf.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 343 567 60.5 %
Date: 2017-03-02 17:11:10 Functions: 21 24 87.5 %

          Line data    Source code
       1             : /* engine-gpgconf.c - gpg-conf engine.
       2             :    Copyright (C) 2000 Werner Koch (dd9jn)
       3             :    Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2008,
       4             :                  2013 g10 Code GmbH
       5             : 
       6             :    This file is part of GPGME.
       7             : 
       8             :    GPGME is free software; you can redistribute it and/or modify it
       9             :    under the terms of the GNU Lesser General Public License as
      10             :    published by the Free Software Foundation; either version 2.1 of
      11             :    the License, or (at your option) any later version.
      12             : 
      13             :    GPGME is distributed in the hope that it will be useful, but
      14             :    WITHOUT ANY WARRANTY; without even the implied warranty of
      15             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      16             :    Lesser General Public License for more details.
      17             : 
      18             :    You should have received a copy of the GNU Lesser General Public
      19             :    License along with this program; if not, see <https://www.gnu.org/licenses/>.
      20             :  */
      21             : 
      22             : #if HAVE_CONFIG_H
      23             : #include <config.h>
      24             : #endif
      25             : 
      26             : #include <stdlib.h>
      27             : #include <string.h>
      28             : #ifdef HAVE_SYS_TYPES_H
      29             : # include <sys/types.h>
      30             : #endif
      31             : #include <assert.h>
      32             : #ifdef HAVE_UNISTD_H
      33             : # include <unistd.h>
      34             : #endif
      35             : #include <fcntl.h> /* FIXME */
      36             : #include <errno.h>
      37             : 
      38             : #include "gpgme.h"
      39             : #include "util.h"
      40             : #include "ops.h"
      41             : #include "wait.h"
      42             : #include "priv-io.h"
      43             : #include "sema.h"
      44             : 
      45             : #include "assuan.h"
      46             : #include "debug.h"
      47             : 
      48             : #include "engine-backend.h"
      49             : 
      50             : 
      51             : 
      52             : struct engine_gpgconf
      53             : {
      54             :   char *file_name;
      55             :   char *home_dir;
      56             :   char *version;
      57             : };
      58             : 
      59             : typedef struct engine_gpgconf *engine_gpgconf_t;
      60             : 
      61             : 
      62             : /* Return true if the engine's version is at least VERSION.  */
      63             : static int
      64           0 : have_gpgconf_version (engine_gpgconf_t gpgconf, const char *version)
      65             : {
      66           0 :   return _gpgme_compare_versions (gpgconf->version, version);
      67             : }
      68             : 
      69             : 
      70             : static char *
      71          90 : gpgconf_get_version (const char *file_name)
      72             : {
      73          90 :   return _gpgme_get_program_version (file_name ? file_name
      74             :                                      : _gpgme_get_default_gpgconf_name ());
      75             : }
      76             : 
      77             : 
      78             : static const char *
      79          90 : gpgconf_get_req_version (void)
      80             : {
      81          90 :   return "2.0.4";
      82             : }
      83             : 
      84             : 
      85             : static void
      86         182 : gpgconf_release (void *engine)
      87             : {
      88         182 :   engine_gpgconf_t gpgconf = engine;
      89             : 
      90         182 :   if (!gpgconf)
      91           0 :     return;
      92             : 
      93         182 :   if (gpgconf->file_name)
      94         182 :     free (gpgconf->file_name);
      95         182 :   if (gpgconf->home_dir)
      96           0 :     free (gpgconf->home_dir);
      97         182 :   if (gpgconf->version)
      98         182 :     free (gpgconf->version);
      99             : 
     100         182 :   free (gpgconf);
     101             : }
     102             : 
     103             : 
     104             : static gpgme_error_t
     105         182 : gpgconf_new (void **engine, const char *file_name, const char *home_dir,
     106             :              const char *version)
     107             : {
     108         182 :   gpgme_error_t err = 0;
     109             :   engine_gpgconf_t gpgconf;
     110             : 
     111         182 :   gpgconf = calloc (1, sizeof *gpgconf);
     112         182 :   if (!gpgconf)
     113           0 :     return gpg_error_from_syserror ();
     114             : 
     115         182 :   gpgconf->file_name = strdup (file_name ? file_name
     116             :                                : _gpgme_get_default_gpgconf_name ());
     117         182 :   if (!gpgconf->file_name)
     118           0 :     err = gpg_error_from_syserror ();
     119             : 
     120         182 :   if (!err && home_dir)
     121             :     {
     122           0 :       gpgconf->home_dir = strdup (home_dir);
     123           0 :       if (!gpgconf->home_dir)
     124           0 :         err = gpg_error_from_syserror ();
     125             :     }
     126             : 
     127         182 :   if (!err && version)
     128             :     {
     129         182 :       gpgconf->version = strdup (version);
     130         182 :       if (!gpgconf->version)
     131           0 :         err = gpg_error_from_syserror ();
     132             :     }
     133             : 
     134         182 :   if (err)
     135           0 :     gpgconf_release (gpgconf);
     136             :   else
     137         182 :     *engine = gpgconf;
     138             : 
     139         182 :   return err;
     140             : }
     141             : 
     142             : 
     143             : static void
     144       32041 : release_arg (gpgme_conf_arg_t arg, gpgme_conf_type_t alt_type)
     145             : {
     146       66184 :   while (arg)
     147             :     {
     148        2102 :       gpgme_conf_arg_t next = arg->next;
     149             : 
     150        2102 :       if (alt_type == GPGME_CONF_STRING)
     151         993 :         free (arg->value.string);
     152        2102 :       free (arg);
     153        2102 :       arg = next;
     154             :     }
     155       32041 : }
     156             : 
     157             : 
     158             : static void
     159        4879 : release_opt (gpgme_conf_opt_t opt)
     160             : {
     161        4879 :   if (opt->name)
     162        4879 :     free (opt->name);
     163        4879 :   if (opt->description)
     164        4510 :     free (opt->description);
     165        4879 :   if (opt->argname)
     166        1681 :     free (opt->argname);
     167             : 
     168        4879 :   release_arg (opt->default_value, opt->alt_type);
     169        4879 :   if (opt->default_description)
     170           0 :     free (opt->default_description);
     171             : 
     172        4879 :   release_arg (opt->no_arg_value, opt->alt_type);
     173        4879 :   release_arg (opt->value, opt->alt_type);
     174        4879 :   release_arg (opt->new_value, opt->alt_type);
     175             : 
     176        4879 :   free (opt);
     177        4879 : }
     178             : 
     179             : 
     180             : static void
     181         246 : release_comp (gpgme_conf_comp_t comp)
     182             : {
     183             :   gpgme_conf_opt_t opt;
     184             : 
     185         246 :   if (comp->name)
     186         246 :     free (comp->name);
     187         246 :   if (comp->description)
     188         246 :     free (comp->description);
     189         246 :   if (comp->program_name)
     190         246 :     free (comp->program_name);
     191             : 
     192         246 :   opt = comp->options;
     193        5371 :   while (opt)
     194             :     {
     195        4879 :       gpgme_conf_opt_t next = opt->next;
     196        4879 :       release_opt (opt);
     197        4879 :       opt = next;
     198             :     }
     199             : 
     200         246 :   free (comp);
     201         246 : }
     202             : 
     203             : 
     204             : static void
     205         141 : gpgconf_config_release (gpgme_conf_comp_t conf)
     206             : {
     207         528 :   while (conf)
     208             :     {
     209         246 :       gpgme_conf_comp_t next = conf->next;
     210         246 :       release_comp (conf);
     211         246 :       conf = next;
     212             :     }
     213         141 : }
     214             : 
     215             : /* Read from gpgconf and pass line after line to the hook function.
     216             :    We put a limit of 64 k on the maximum size for a line.  This should
     217             :    allow for quite a long "group" line, which is usually the longest
     218             :    line (mine is currently ~3k).  */
     219             : static gpgme_error_t
     220         294 : gpgconf_read (void *engine, const char *arg1, char *arg2,
     221             :               gpgme_error_t (*cb) (void *hook, char *line),
     222             :               void *hook)
     223             : {
     224         294 :   struct engine_gpgconf *gpgconf = engine;
     225         294 :   gpgme_error_t err = 0;
     226             :   char *linebuf;
     227             :   size_t linebufsize;
     228             :   int linelen;
     229             :   char *argv[6];
     230         294 :   int argc = 0;
     231             :   int rp[2];
     232         294 :   struct spawn_fd_item_s cfd[] = { {-1, 1 /* STDOUT_FILENO */, -1, 0},
     233             :                                    {-1, -1} };
     234             :   int status;
     235             :   int nread;
     236         294 :   char *mark = NULL;
     237             : 
     238             :   /* _gpgme_engine_new guarantees that this is not NULL.  */
     239         294 :   argv[argc++] = gpgconf->file_name;
     240             : 
     241         294 :   if (gpgconf->home_dir && have_gpgconf_version (gpgconf, "2.1.13"))
     242             :     {
     243           0 :       argv[argc++] = (char*)"--homedir";
     244           0 :       argv[argc++] = gpgconf->home_dir;
     245             :     }
     246             : 
     247         294 :   argv[argc++] = (char*)arg1;
     248         294 :   argv[argc++] = arg2;
     249         294 :   argv[argc] = NULL;
     250         294 :   assert (argc < DIM (argv));
     251             : 
     252         294 :   if (_gpgme_io_pipe (rp, 1) < 0)
     253           0 :     return gpg_error_from_syserror ();
     254             : 
     255         294 :   cfd[0].fd = rp[1];
     256             : 
     257         294 :   status = _gpgme_io_spawn (gpgconf->file_name, argv,
     258             :                             IOSPAWN_FLAG_DETACHED, cfd, NULL, NULL, NULL);
     259         294 :   if (status < 0)
     260             :     {
     261           0 :       _gpgme_io_close (rp[0]);
     262           0 :       _gpgme_io_close (rp[1]);
     263           0 :       return gpg_error_from_syserror ();
     264             :     }
     265             : 
     266         294 :   linebufsize = 1024; /* Usually enough for conf lines.  */
     267         294 :   linebuf = malloc (linebufsize);
     268         294 :   if (!linebuf)
     269             :     {
     270           0 :       err = gpg_error_from_syserror ();
     271           0 :       goto leave;
     272             :     }
     273         294 :   linelen = 0;
     274             : 
     275         966 :   while ((nread = _gpgme_io_read (rp[0], linebuf + linelen,
     276         672 :                                   linebufsize - linelen - 1)))
     277             :     {
     278             :       char *line;
     279         378 :       const char *lastmark = NULL;
     280             :       size_t nused;
     281             : 
     282         378 :       if (nread < 0)
     283             :         {
     284           0 :           err = gpg_error_from_syserror ();
     285           0 :           goto leave;
     286             :         }
     287             : 
     288         378 :       linelen += nread;
     289         378 :       linebuf[linelen] = '\0';
     290             : 
     291        5628 :       for (line=linebuf; (mark = strchr (line, '\n')); line = mark+1 )
     292             :         {
     293        5250 :           lastmark = mark;
     294        5250 :           if (mark > line && mark[-1] == '\r')
     295           0 :             mark[-1] = '\0';
     296             :           else
     297        5250 :             mark[0] = '\0';
     298             : 
     299             :           /* Got a full line.  Due to the CR removal code (which
     300             :              occurs only on Windows) we might be one-off and thus
     301             :              would see empty lines.  Don't pass them to the
     302             :              callback. */
     303        5250 :           err = *line? (*cb) (hook, line) : 0;
     304        5250 :           if (err)
     305           0 :             goto leave;
     306             :         }
     307             : 
     308         378 :       nused = lastmark? (lastmark + 1 - linebuf) : 0;
     309         378 :       memmove (linebuf, linebuf + nused, linelen - nused);
     310         378 :       linelen -= nused;
     311             : 
     312         378 :       if (!(linelen < linebufsize - 1))
     313             :         {
     314             :           char *newlinebuf;
     315             : 
     316           0 :           if (linelen <  8 * 1024 - 1)
     317           0 :             linebufsize = 8 * 1024;
     318           0 :           else if (linelen < 64 * 1024 - 1)
     319           0 :             linebufsize = 64 * 1024;
     320             :           else
     321             :             {
     322             :               /* We reached our limit - give up.  */
     323           0 :               err = gpg_error (GPG_ERR_LINE_TOO_LONG);
     324           0 :               goto leave;
     325             :             }
     326             : 
     327           0 :           newlinebuf = realloc (linebuf, linebufsize);
     328           0 :           if (!newlinebuf)
     329             :             {
     330           0 :               err = gpg_error_from_syserror ();
     331           0 :               goto leave;
     332             :             }
     333           0 :           linebuf = newlinebuf;
     334             :         }
     335             :     }
     336             : 
     337             :  leave:
     338         294 :   free (linebuf);
     339         294 :   _gpgme_io_close (rp[0]);
     340         294 :   return err;
     341             : }
     342             : 
     343             : 
     344             : static gpgme_error_t
     345         252 : gpgconf_config_load_cb (void *hook, char *line)
     346             : {
     347         252 :   gpgme_conf_comp_t *comp_p = hook;
     348         252 :   gpgme_conf_comp_t comp = *comp_p;
     349             : #define NR_FIELDS 16
     350             :   char *field[NR_FIELDS];
     351         252 :   int fields = 0;
     352             : 
     353        1260 :   while (line && fields < NR_FIELDS)
     354             :     {
     355         756 :       field[fields++] = line;
     356         756 :       line = strchr (line, ':');
     357         756 :       if (line)
     358         504 :         *(line++) = '\0';
     359             :     }
     360             : 
     361             :   /* We require at least the first 3 fields.  */
     362         252 :   if (fields < 2)
     363           0 :     return trace_gpg_error (GPG_ERR_INV_ENGINE);
     364             : 
     365             :   /* Find the pointer to the new component in the list.  */
     366         924 :   while (comp && comp->next)
     367         420 :     comp = comp->next;
     368         252 :   if (comp)
     369         210 :     comp_p = &comp->next;
     370             : 
     371         252 :   comp = calloc (1, sizeof (*comp));
     372         252 :   if (!comp)
     373           0 :     return gpg_error_from_syserror ();
     374             :   /* Prepare return value.  */
     375         252 :   comp->_last_opt_p = &comp->options;
     376         252 :   *comp_p = comp;
     377             : 
     378         252 :   comp->name = strdup (field[0]);
     379         252 :   if (!comp->name)
     380           0 :     return gpg_error_from_syserror ();
     381             : 
     382         252 :   comp->description = strdup (field[1]);
     383         252 :   if (!comp->description)
     384           0 :     return gpg_error_from_syserror ();
     385             : 
     386         252 :   if (fields >= 3)
     387             :     {
     388         252 :       comp->program_name = strdup (field[2]);
     389         252 :       if (!comp->program_name)
     390           0 :         return gpg_error_from_syserror ();
     391             :     }
     392             : 
     393         252 :   return 0;
     394             : }
     395             : 
     396             : 
     397             : static gpgme_error_t
     398       11130 : gpgconf_parse_option (gpgme_conf_opt_t opt,
     399             :                       gpgme_conf_arg_t *arg_p, char *line)
     400             : {
     401             :   gpgme_error_t err;
     402             :   char *mark;
     403             : 
     404       11130 :   if (!line[0])
     405       10124 :     return 0;
     406             : 
     407        3060 :   while (line)
     408             :     {
     409             :       gpgme_conf_arg_t arg;
     410             : 
     411        1048 :       mark = strchr (line, ',');
     412        1048 :       if (mark)
     413          42 :         *mark = '\0';
     414             : 
     415        1048 :       arg = calloc (1, sizeof (*arg));
     416        1048 :       if (!arg)
     417           0 :         return gpg_error_from_syserror ();
     418        1048 :       *arg_p = arg;
     419        1048 :       arg_p = &arg->next;
     420             : 
     421        1048 :       if (*line == '\0')
     422           0 :         arg->no_arg = 1;
     423             :       else
     424             :         {
     425        1048 :           switch (opt->alt_type)
     426             :             {
     427             :               /* arg->value.count is an alias for arg->value.uint32.  */
     428             :             case GPGME_CONF_NONE:
     429             :             case GPGME_CONF_UINT32:
     430         482 :               arg->value.uint32 = strtoul (line, NULL, 0);
     431         482 :               break;
     432             : 
     433             :             case GPGME_CONF_INT32:
     434          84 :               arg->value.uint32 = strtol (line, NULL, 0);
     435          84 :               break;
     436             : 
     437             :             case GPGME_CONF_STRING:
     438             :               /* The complex types below are only here to silent the
     439             :                  compiler warning. */
     440             :             case GPGME_CONF_FILENAME:
     441             :             case GPGME_CONF_LDAP_SERVER:
     442             :             case GPGME_CONF_KEY_FPR:
     443             :             case GPGME_CONF_PUB_KEY:
     444             :             case GPGME_CONF_SEC_KEY:
     445             :             case GPGME_CONF_ALIAS_LIST:
     446             :               /* Skip quote character.  */
     447         482 :               line++;
     448             : 
     449         482 :               err = _gpgme_decode_percent_string (line, &arg->value.string,
     450             :                                                   0, 0);
     451         482 :               if (err)
     452           0 :                 return err;
     453         482 :               break;
     454             :             }
     455             :         }
     456             : 
     457             :       /* Find beginning of next value.  */
     458        1048 :       if (mark++ && *mark)
     459          42 :         line = mark;
     460             :       else
     461        1006 :         line = NULL;
     462             :     }
     463             : 
     464        1006 :   return 0;
     465             : }
     466             : 
     467             : 
     468             : static gpgme_error_t
     469        4998 : gpgconf_config_load_cb2 (void *hook, char *line)
     470             : {
     471             :   gpgme_error_t err;
     472        4998 :   gpgme_conf_comp_t comp = hook;
     473        4998 :   gpgme_conf_opt_t *opt_p = comp->_last_opt_p;
     474             :   gpgme_conf_opt_t opt;
     475             : #define NR_FIELDS 16
     476             :   char *field[NR_FIELDS];
     477        4998 :   int fields = 0;
     478             : 
     479       59976 :   while (line && fields < NR_FIELDS)
     480             :     {
     481       49980 :       field[fields++] = line;
     482       49980 :       line = strchr (line, ':');
     483       49980 :       if (line)
     484       44982 :         *(line++) = '\0';
     485             :     }
     486             : 
     487             :   /* We require at least the first 10 fields.  */
     488        4998 :   if (fields < 10)
     489           0 :     return trace_gpg_error (GPG_ERR_INV_ENGINE);
     490             : 
     491        4998 :   opt = calloc (1, sizeof (*opt));
     492        4998 :   if (!opt)
     493           0 :     return gpg_error_from_syserror ();
     494             : 
     495        4998 :   comp->_last_opt_p = &opt->next;
     496        4998 :   *opt_p = opt;
     497             : 
     498        4998 :   if (field[0][0])
     499             :     {
     500        4998 :       opt->name = strdup (field[0]);
     501        4998 :       if (!opt->name)
     502           0 :         return gpg_error_from_syserror ();
     503             :     }
     504             : 
     505        4998 :   opt->flags = strtoul (field[1], NULL, 0);
     506             : 
     507        4998 :   opt->level = strtoul (field[2], NULL, 0);
     508             : 
     509        4998 :   if (field[3][0])
     510             :     {
     511        4620 :       opt->description = strdup (field[3]);
     512        4620 :       if (!opt->description)
     513           0 :         return gpg_error_from_syserror ();
     514             :     }
     515             : 
     516        4998 :   opt->type = strtoul (field[4], NULL, 0);
     517             : 
     518        4998 :   opt->alt_type = strtoul (field[5], NULL, 0);
     519             : 
     520        4998 :   if (field[6][0])
     521             :     {
     522        1722 :       opt->argname = strdup (field[6]);
     523        1722 :       if (!opt->argname)
     524           0 :         return gpg_error_from_syserror ();
     525             :     }
     526             : 
     527        4998 :   if (opt->flags & GPGME_CONF_DEFAULT)
     528             :     {
     529        1134 :       err = gpgconf_parse_option (opt, &opt->default_value, field[7]);
     530        1134 :       if (err)
     531           0 :         return err;
     532             :     }
     533        3864 :   else if ((opt->flags & GPGME_CONF_DEFAULT_DESC) && field[7][0])
     534             :     {
     535           0 :       opt->default_description = strdup (field[7]);
     536           0 :       if (!opt->default_description)
     537           0 :         return gpg_error_from_syserror ();
     538             :     }
     539             : 
     540        4998 :   if (opt->flags & GPGME_CONF_NO_ARG_DESC)
     541             :     {
     542           0 :       opt->no_arg_description = strdup (field[8]);
     543           0 :       if (!opt->no_arg_description)
     544           0 :         return gpg_error_from_syserror ();
     545             :     }
     546             :   else
     547             :     {
     548        4998 :       err = gpgconf_parse_option (opt, &opt->no_arg_value, field[8]);
     549        4998 :       if (err)
     550           0 :         return err;
     551             :     }
     552             : 
     553        4998 :   err = gpgconf_parse_option (opt, &opt->value, field[9]);
     554        4998 :   if (err)
     555           0 :     return err;
     556             : 
     557        4998 :   return 0;
     558             : }
     559             : 
     560             : 
     561             : static gpgme_error_t
     562          42 : gpgconf_conf_load (void *engine, gpgme_conf_comp_t *comp_p)
     563             : {
     564             :   gpgme_error_t err;
     565          42 :   gpgme_conf_comp_t comp = NULL;
     566             :   gpgme_conf_comp_t cur_comp;
     567             : 
     568          42 :   *comp_p = NULL;
     569             : 
     570          42 :   err = gpgconf_read (engine, "--list-components", NULL,
     571             :                       gpgconf_config_load_cb, &comp);
     572          42 :   if (err)
     573             :     {
     574           0 :       gpgconf_release (comp);
     575           0 :       return err;
     576             :     }
     577             : 
     578          42 :   cur_comp = comp;
     579         336 :   while (!err && cur_comp)
     580             :     {
     581         252 :       err = gpgconf_read (engine, "--list-options", cur_comp->name,
     582             :                           gpgconf_config_load_cb2, cur_comp);
     583         252 :       cur_comp = cur_comp->next;
     584             :     }
     585             : 
     586          42 :   if (err)
     587             :     {
     588           0 :       gpgconf_release (comp);
     589           0 :       return err;
     590             :     }
     591             : 
     592          42 :   *comp_p = comp;
     593          42 :   return 0;
     594             : }
     595             : 
     596             : 
     597             : 
     598             : gpgme_error_t
     599        1078 : _gpgme_conf_arg_new (gpgme_conf_arg_t *arg_p,
     600             :                      gpgme_conf_type_t type, const void *value)
     601             : {
     602             :   gpgme_conf_arg_t arg;
     603             : 
     604        1078 :   arg = calloc (1, sizeof (*arg));
     605        1078 :   if (!arg)
     606           0 :     return gpg_error_from_syserror ();
     607             : 
     608        1078 :   if (!value)
     609           0 :     arg->no_arg = 1;
     610             :   else
     611             :     {
     612             :       /* We need to switch on type here because the alt-type is not
     613             :          yet known.  */
     614        1078 :       switch (type)
     615             :         {
     616             :         case GPGME_CONF_NONE:
     617             :         case GPGME_CONF_UINT32:
     618         472 :           arg->value.uint32 = *((unsigned int *) value);
     619         472 :           break;
     620             : 
     621             :         case GPGME_CONF_INT32:
     622          84 :           arg->value.int32 = *((int *) value);
     623          84 :           break;
     624             : 
     625             :         case GPGME_CONF_STRING:
     626             :         case GPGME_CONF_FILENAME:
     627             :         case GPGME_CONF_LDAP_SERVER:
     628             :         case GPGME_CONF_KEY_FPR:
     629             :         case GPGME_CONF_PUB_KEY:
     630             :         case GPGME_CONF_SEC_KEY:
     631             :         case GPGME_CONF_ALIAS_LIST:
     632         522 :           arg->value.string = strdup (value);
     633         522 :           if (!arg->value.string)
     634             :             {
     635           0 :               free (arg);
     636           0 :               return gpg_error_from_syserror ();
     637             :             }
     638         522 :           break;
     639             : 
     640             :         default:
     641           0 :           free (arg);
     642           0 :           return gpg_error (GPG_ERR_INV_VALUE);
     643             :         }
     644             :     }
     645             : 
     646        1078 :   *arg_p = arg;
     647        1078 :   return 0;
     648             : }
     649             : 
     650             : 
     651             : void
     652       12525 : _gpgme_conf_arg_release (gpgme_conf_arg_t arg, gpgme_conf_type_t type)
     653             : {
     654             :   /* Lacking the alt_type we need to switch on type here.  */
     655       12525 :   switch (type)
     656             :     {
     657             :     case GPGME_CONF_NONE:
     658             :     case GPGME_CONF_UINT32:
     659             :     case GPGME_CONF_INT32:
     660             :     case GPGME_CONF_STRING:
     661             :     default:
     662       12525 :       break;
     663             : 
     664             :     case GPGME_CONF_FILENAME:
     665             :     case GPGME_CONF_LDAP_SERVER:
     666             :     case GPGME_CONF_KEY_FPR:
     667             :     case GPGME_CONF_PUB_KEY:
     668             :     case GPGME_CONF_SEC_KEY:
     669             :     case GPGME_CONF_ALIAS_LIST:
     670           0 :       type = GPGME_CONF_STRING;
     671           0 :       break;
     672             :     }
     673             : 
     674       12525 :   release_arg (arg, type);
     675       12525 : }
     676             : 
     677             : 
     678             : gpgme_error_t
     679          40 : _gpgme_conf_opt_change (gpgme_conf_opt_t opt, int reset, gpgme_conf_arg_t arg)
     680             : {
     681          40 :   if (reset)
     682             :     {
     683           0 :       if (opt->new_value)
     684           0 :         release_arg (opt->new_value, opt->alt_type);
     685           0 :       opt->new_value = NULL;
     686           0 :       opt->change_value = 0;
     687             :     }
     688             :   else
     689             :     {
     690             :       /* Support self-assignment, for example for adding an item to an
     691             :          existing list.  */
     692          40 :       if (opt->new_value && arg != opt->new_value)
     693           0 :         release_arg (opt->new_value, opt->alt_type);
     694          40 :       opt->new_value = arg;
     695          40 :       opt->change_value = 1;
     696             :     }
     697          40 :   return 0;
     698             : }
     699             : 
     700             : 
     701             : /* FIXME: Major problem: We don't get errors from gpgconf.  */
     702             : 
     703             : static gpgme_error_t
     704          40 : gpgconf_write (void *engine, const char *arg1, char *arg2, gpgme_data_t conf)
     705             : {
     706          40 :   struct engine_gpgconf *gpgconf = engine;
     707          40 :   gpgme_error_t err = 0;
     708             : #define BUFLEN 1024
     709             :   char buf[BUFLEN];
     710          40 :   int buflen = 0;
     711             :   char *argv[7];
     712          40 :   int argc = 0;
     713          40 :   int rp[2] = { -1, -1 };
     714          40 :   int errp[2] = { -1, -1 };
     715          40 :   struct spawn_fd_item_s cfd[] = { {-1, 0 /* STDIN_FILENO */},
     716             :                                    {-1, 2 /* STDERR_FILENO */, -1},
     717             :                                    {-1, -1} };
     718             :   int status;
     719             :   int nwrite;
     720             : 
     721             :   /* _gpgme_engine_new guarantees that this is not NULL.  */
     722          40 :   argv[argc++] = gpgconf->file_name;
     723             : 
     724          40 :   if (gpgconf->home_dir && have_gpgconf_version (gpgconf, "2.1.13"))
     725             :     {
     726           0 :       argv[argc++] = (char*)"--homedir";
     727           0 :       argv[argc++] = gpgconf->home_dir;
     728             :     }
     729             : 
     730          40 :   argv[argc++] = (char*)"--runtime";
     731          40 :   argv[argc++] = (char*)arg1;
     732          40 :   argv[argc++] = arg2;
     733          40 :   argv[argc] = NULL;
     734          40 :   assert (argc < DIM (argv));
     735             : 
     736          40 :   if (_gpgme_io_pipe (rp, 0) < 0)
     737             :     {
     738           0 :       err = gpg_error_from_syserror ();
     739           0 :       goto leave;
     740             :     }
     741             : 
     742          40 :   if (_gpgme_io_pipe (errp, 1) < 0)
     743             :     {
     744           0 :       err = gpg_error_from_syserror ();
     745           0 :       goto leave;
     746             :     }
     747             : 
     748          40 :   cfd[0].fd = rp[0];
     749          40 :   cfd[1].fd = errp[1];
     750             : 
     751          40 :   status = _gpgme_io_spawn (gpgconf->file_name, argv,
     752             :                             IOSPAWN_FLAG_DETACHED, cfd, NULL, NULL, NULL);
     753          40 :   if (status < 0)
     754             :     {
     755           0 :       err = gpg_error_from_syserror ();
     756           0 :       goto leave;
     757             :     }
     758             : 
     759          40 :   rp[0] = -1;
     760          40 :   errp[1] = -1;
     761             : 
     762             :   for (;;)
     763             :     {
     764         120 :       if (buflen == 0)
     765             :         {
     766             :           do
     767             :             {
     768          80 :               buflen = gpgme_data_read (conf, buf, BUFLEN);
     769             :             }
     770          80 :           while (buflen < 0 && errno == EAGAIN);
     771             : 
     772          80 :           if (buflen < 0)
     773             :             {
     774           0 :               err = gpg_error_from_syserror ();
     775           0 :               goto leave;
     776             :             }
     777          80 :           else if (buflen == 0)
     778             :             {
     779             :               /* All is written.  */
     780          40 :               _gpgme_io_close (rp[1]);
     781          40 :               rp[1] = -1;
     782             : 
     783          11 :               for (;;)
     784             :                 {
     785             :                   do
     786             :                     {
     787          51 :                       buflen = _gpgme_io_read (errp[0], buf, BUFLEN);
     788             :                     }
     789          51 :                   while (buflen < 0 && errno == EAGAIN);
     790             : 
     791          51 :                   if (buflen == 0)
     792             :                     {
     793          40 :                       err = 0;
     794          40 :                       goto leave;
     795             :                     }
     796             :                   /* XXX: Do something useful with BUF.  */
     797             :                 }
     798             :             }
     799             :         }
     800             : 
     801             :       do
     802             :         {
     803          40 :           nwrite = _gpgme_io_write (rp[1], buf, buflen);
     804             :         }
     805          40 :       while (nwrite < 0 && errno == EAGAIN);
     806             : 
     807          40 :       if (nwrite > 0)
     808             :         {
     809          40 :           buflen -= nwrite;
     810          40 :           if (buflen > 0)
     811           0 :             memmove (&buf[0], &buf[nwrite], buflen);
     812             :         }
     813           0 :       else if (nwrite < 0)
     814             :         {
     815           0 :           err = gpg_error_from_syserror ();
     816           0 :           goto leave;
     817             :         }
     818             :     }
     819             : 
     820             :   assert (! "reached");
     821             : 
     822             :  leave:
     823          40 :   if (rp[0] != -1)
     824           0 :     _gpgme_io_close (rp[0]);
     825          40 :   if (rp[1] != -1)
     826           0 :   _gpgme_io_close (rp[1]);
     827          40 :   if (errp[0] != -1)
     828          40 :     _gpgme_io_close (errp[0]);
     829          40 :   if (errp[1] != -1)
     830           0 :   _gpgme_io_close (errp[1]);
     831             : 
     832          40 :   return err;
     833             : }
     834             : 
     835             : 
     836             : static gpgme_error_t
     837          30 : arg_to_data (gpgme_data_t conf, gpgme_conf_opt_t option, gpgme_conf_arg_t arg)
     838             : {
     839          30 :   gpgme_error_t err = 0;
     840          30 :   int amt = 0;
     841             :   char buf[16];
     842             : 
     843          90 :   while (amt >= 0 && arg)
     844             :     {
     845          30 :       switch (option->alt_type)
     846             :         {
     847             :         case GPGME_CONF_NONE:
     848             :         case GPGME_CONF_UINT32:
     849             :         default:
     850          10 :           snprintf (buf, sizeof (buf), "%u", arg->value.uint32);
     851          10 :           buf[sizeof (buf) - 1] = '\0';
     852          10 :           amt = gpgme_data_write (conf, buf, strlen (buf));
     853          10 :           break;
     854             : 
     855             :         case GPGME_CONF_INT32:
     856           0 :           snprintf (buf, sizeof (buf), "%i", arg->value.uint32);
     857           0 :           buf[sizeof (buf) - 1] = '\0';
     858           0 :           amt = gpgme_data_write (conf, buf, strlen (buf));
     859           0 :           break;
     860             : 
     861             : 
     862             :         case GPGME_CONF_STRING:
     863             :           /* The complex types below are only here to silent the
     864             :              compiler warning. */
     865             :         case GPGME_CONF_FILENAME:
     866             :         case GPGME_CONF_LDAP_SERVER:
     867             :         case GPGME_CONF_KEY_FPR:
     868             :         case GPGME_CONF_PUB_KEY:
     869             :         case GPGME_CONF_SEC_KEY:
     870             :         case GPGME_CONF_ALIAS_LIST:
     871          20 :           if (arg->value.string)
     872             :             {
     873             :               /* One quote character, and three times to allow for
     874             :                  percent escaping.  */
     875          20 :               char *ptr = arg->value.string;
     876          20 :               amt = gpgme_data_write (conf, "\"", 1);
     877          20 :               if (amt < 0)
     878           0 :                 break;
     879             : 
     880         345 :               while (!err && *ptr)
     881             :                 {
     882         305 :                   switch (*ptr)
     883             :                     {
     884             :                     case '%':
     885           0 :                       amt = gpgme_data_write (conf, "%25", 3);
     886           0 :                       break;
     887             : 
     888             :                     case ':':
     889          20 :                       amt = gpgme_data_write (conf, "%3a", 3);
     890          20 :                       break;
     891             : 
     892             :                     case ',':
     893           0 :                       amt = gpgme_data_write (conf, "%2c", 3);
     894           0 :                       break;
     895             : 
     896             :                     default:
     897         285 :                       amt = gpgme_data_write (conf, ptr, 1);
     898             :                     }
     899         305 :                   ptr++;
     900             :                 }
     901             :             }
     902          20 :           break;
     903             :         }
     904             : 
     905          30 :       if (amt < 0)
     906           0 :         break;
     907             : 
     908          30 :       arg = arg->next;
     909             :       /* Comma separator.  */
     910          30 :       if (arg)
     911           0 :         amt = gpgme_data_write (conf, ",", 1);
     912             :     }
     913             : 
     914          30 :   if (amt < 0)
     915           0 :     return gpg_error_from_syserror ();
     916             : 
     917          30 :   return 0;
     918             : }
     919             : 
     920             : 
     921             : static gpgme_error_t
     922         140 : gpgconf_conf_save (void *engine, gpgme_conf_comp_t comp)
     923             : {
     924             :   gpgme_error_t err;
     925         140 :   int amt = 0;
     926             :   /* We use a data object to store the new configuration.  */
     927             :   gpgme_data_t conf;
     928             :   gpgme_conf_opt_t option;
     929         140 :   int something_changed = 0;
     930             : 
     931         140 :   err = gpgme_data_new (&conf);
     932         140 :   if (err)
     933           0 :     return err;
     934             : 
     935         140 :   option = comp->options;
     936        3190 :   while (!err && amt >= 0 && option)
     937             :     {
     938        2910 :       if (option->change_value)
     939             :         {
     940          40 :           unsigned int flags = 0;
     941             :           char buf[16];
     942             : 
     943          40 :           something_changed = 1;
     944             : 
     945          40 :           amt = gpgme_data_write (conf, option->name, strlen (option->name));
     946          40 :           if (amt >= 0)
     947          40 :             amt = gpgme_data_write (conf, ":", 1);
     948          40 :           if (amt < 0)
     949           0 :             break;
     950             : 
     951          40 :           if (!option->new_value)
     952          10 :             flags |= GPGME_CONF_DEFAULT;
     953          40 :           snprintf (buf, sizeof (buf), "%u", flags);
     954          40 :           buf[sizeof (buf) - 1] = '\0';
     955             : 
     956          40 :           amt = gpgme_data_write (conf, buf, strlen (buf));
     957          40 :           if (amt >= 0)
     958          40 :             amt = gpgme_data_write (conf, ":", 1);
     959          40 :           if (amt < 0)
     960           0 :             break;
     961             : 
     962          40 :           if (option->new_value)
     963             :             {
     964          30 :               err = arg_to_data (conf, option, option->new_value);
     965          30 :               if (err)
     966           0 :                 break;
     967             :             }
     968          40 :           amt = gpgme_data_write (conf, "\n", 1);
     969             :         }
     970        2910 :       option = option->next;
     971             :     }
     972         140 :   if (!err && amt < 0)
     973           0 :     err = gpg_error_from_syserror ();
     974         140 :   if (err || !something_changed)
     975             :     goto bail;
     976             : 
     977          40 :   err = gpgme_data_seek (conf, 0, SEEK_SET);
     978          40 :   if (err)
     979           0 :     goto bail;
     980             : 
     981          40 :   err = gpgconf_write (engine, "--change-options", comp->name, conf);
     982             :  bail:
     983         140 :   gpgme_data_release (conf);
     984         140 :   return err;
     985             : }
     986             : 
     987             : 
     988             : /* Parse a line received from gpgconf --query-swdb.  This function may
     989             :  * modify LINE.  The result is stored at RESUL.  */
     990             : static gpg_error_t
     991           0 : parse_swdb_line (char *line, gpgme_query_swdb_result_t result)
     992             : {
     993             :   char *field[9];
     994           0 :   int fields = 0;
     995             :   gpg_err_code_t ec;
     996             : 
     997           0 :   while (line && fields < DIM (field))
     998             :     {
     999           0 :       field[fields++] = line;
    1000           0 :       line = strchr (line, ':');
    1001           0 :       if (line)
    1002           0 :         *line++ = 0;
    1003             :     }
    1004             :   /* We require that all fields exists - gpgme emits all these fields
    1005             :    * even on error.  They might be empty, though. */
    1006           0 :   if (fields < 9)
    1007           0 :     return gpg_error (GPG_ERR_INV_ENGINE);
    1008             : 
    1009           0 :   free (result->name);
    1010           0 :   result->name = strdup (field[0]);
    1011           0 :   if (!result->name)
    1012           0 :     return gpg_error_from_syserror ();
    1013             : 
    1014           0 :   free (result->iversion);
    1015           0 :   result->iversion = strdup (field[1]);
    1016           0 :   if (!result->iversion)
    1017           0 :     return gpg_error_from_syserror ();
    1018             : 
    1019           0 :   result->urgent = (strtol (field[3], NULL, 10) > 0);
    1020             : 
    1021           0 :   ec = gpg_err_code (strtoul (field[4], NULL, 10));
    1022             : 
    1023           0 :   result->created  = _gpgme_parse_timestamp (field[5], NULL);
    1024           0 :   result->retrieved= _gpgme_parse_timestamp (field[6], NULL);
    1025             : 
    1026           0 :   free (result->version);
    1027           0 :   result->version  = strdup (field[7]);
    1028           0 :   if (!result->version)
    1029           0 :     return gpg_error_from_syserror ();
    1030             : 
    1031           0 :   result->reldate  = _gpgme_parse_timestamp (field[8], NULL);
    1032             : 
    1033             :   /* Set other flags.  */
    1034           0 :   result->warning = !!ec;
    1035           0 :   result->update = 0;
    1036           0 :   result->noinfo = 0;
    1037           0 :   result->unknown = 0;
    1038           0 :   result->tooold = 0;
    1039           0 :   result->error = 0;
    1040             : 
    1041           0 :   switch (*field[2])
    1042             :     {
    1043           0 :     case '-': result->warning = 1; break;
    1044           0 :     case '?': result->unknown = result->warning = 1; break;
    1045           0 :     case 'u': result->update = 1; break;
    1046           0 :     case 'c': break;
    1047           0 :     case 'n': break;
    1048             :     default:
    1049           0 :       result->warning = 1;
    1050           0 :       if (!ec)
    1051           0 :         ec = GPG_ERR_INV_ENGINE;
    1052           0 :       break;
    1053             :     }
    1054             : 
    1055           0 :   if (ec == GPG_ERR_TOO_OLD)
    1056           0 :     result->tooold = 1;
    1057           0 :   else if (ec == GPG_ERR_ENOENT)
    1058           0 :     result->noinfo = 1;
    1059           0 :   else if (ec)
    1060           0 :     result->error = 1;
    1061             : 
    1062             : 
    1063           0 :   return 0;
    1064             : }
    1065             : 
    1066             : 
    1067             : static gpgme_error_t
    1068           0 : gpgconf_query_swdb (void *engine,
    1069             :                     const char *name, const char *iversion,
    1070             :                     gpgme_query_swdb_result_t result)
    1071             : {
    1072           0 :   struct engine_gpgconf *gpgconf = engine;
    1073           0 :   gpgme_error_t err = 0;
    1074             :   char *linebuf;
    1075             :   size_t linebufsize;
    1076             :   int linelen;
    1077             :   char *argv[7];
    1078           0 :   int argc = 0;
    1079             :   int rp[2];
    1080           0 :   struct spawn_fd_item_s cfd[] = { {-1, 1 /* STDOUT_FILENO */, -1, 0},
    1081             :                                    {-1, -1} };
    1082             :   int status;
    1083             :   int nread;
    1084           0 :   char *mark = NULL;
    1085             : 
    1086           0 :   if (!have_gpgconf_version (gpgconf, "2.1.16"))
    1087           0 :     return gpg_error (GPG_ERR_ENGINE_TOO_OLD);
    1088             : 
    1089             :   /* _gpgme_engine_new guarantees that this is not NULL.  */
    1090           0 :   argv[argc++] = gpgconf->file_name;
    1091             : 
    1092           0 :   if (gpgconf->home_dir)
    1093             :     {
    1094           0 :       argv[argc++] = (char*)"--homedir";
    1095           0 :       argv[argc++] = gpgconf->home_dir;
    1096             :     }
    1097             : 
    1098           0 :   argv[argc++] = (char*)"--query-swdb";
    1099           0 :   argv[argc++] = (char*)name;
    1100           0 :   argv[argc++] = (char*)iversion;
    1101           0 :   argv[argc] = NULL;
    1102           0 :   assert (argc < DIM (argv));
    1103             : 
    1104           0 :   if (_gpgme_io_pipe (rp, 1) < 0)
    1105           0 :     return gpg_error_from_syserror ();
    1106             : 
    1107           0 :   cfd[0].fd = rp[1];
    1108             : 
    1109           0 :   status = _gpgme_io_spawn (gpgconf->file_name, argv,
    1110             :                             IOSPAWN_FLAG_DETACHED, cfd, NULL, NULL, NULL);
    1111           0 :   if (status < 0)
    1112             :     {
    1113           0 :       _gpgme_io_close (rp[0]);
    1114           0 :       _gpgme_io_close (rp[1]);
    1115           0 :       return gpg_error_from_syserror ();
    1116             :     }
    1117             : 
    1118           0 :   linebufsize = 2048; /* Same as used by gpgconf.  */
    1119           0 :   linebuf = malloc (linebufsize);
    1120           0 :   if (!linebuf)
    1121             :     {
    1122           0 :       err = gpg_error_from_syserror ();
    1123           0 :       goto leave;
    1124             :     }
    1125           0 :   linelen = 0;
    1126             : 
    1127           0 :   while ((nread = _gpgme_io_read (rp[0], linebuf + linelen,
    1128           0 :                                   linebufsize - linelen - 1)))
    1129             :     {
    1130             :       char *line;
    1131           0 :       const char *lastmark = NULL;
    1132             :       size_t nused;
    1133             : 
    1134           0 :       if (nread < 0)
    1135             :         {
    1136           0 :           err = gpg_error_from_syserror ();
    1137           0 :           goto leave;
    1138             :         }
    1139             : 
    1140           0 :       linelen += nread;
    1141           0 :       linebuf[linelen] = '\0';
    1142             : 
    1143           0 :       for (line=linebuf; (mark = strchr (line, '\n')); line = mark+1 )
    1144             :         {
    1145           0 :           lastmark = mark;
    1146           0 :           if (mark > line && mark[-1] == '\r')
    1147           0 :             mark[-1] = '\0';
    1148             :           else
    1149           0 :             mark[0] = '\0';
    1150             : 
    1151             :           /* Got a full line.  Due to the CR removal code (which
    1152             :              occurs only on Windows) we might be one-off and thus
    1153             :              would see empty lines.  */
    1154           0 :           if (*line)
    1155             :             {
    1156           0 :               err = parse_swdb_line (line, result);
    1157           0 :               goto leave; /* Ready.  */
    1158             :             }
    1159             :           else /* empty line.  */
    1160           0 :             err = 0;
    1161             :         }
    1162             : 
    1163           0 :       nused = lastmark? (lastmark + 1 - linebuf) : 0;
    1164           0 :       memmove (linebuf, linebuf + nused, linelen - nused);
    1165           0 :       linelen -= nused;
    1166             : 
    1167           0 :       if (!(linelen < linebufsize - 1))
    1168             :         {
    1169             :           char *newlinebuf;
    1170             : 
    1171           0 :           if (linelen <  8 * 1024 - 1)
    1172           0 :             linebufsize = 8 * 1024;
    1173           0 :           else if (linelen < 64 * 1024 - 1)
    1174           0 :             linebufsize = 64 * 1024;
    1175             :           else
    1176             :             {
    1177             :               /* We reached our limit - give up.  */
    1178           0 :               err = gpg_error (GPG_ERR_LINE_TOO_LONG);
    1179           0 :               goto leave;
    1180             :             }
    1181             : 
    1182           0 :           newlinebuf = realloc (linebuf, linebufsize);
    1183           0 :           if (!newlinebuf)
    1184             :             {
    1185           0 :               err = gpg_error_from_syserror ();
    1186           0 :               goto leave;
    1187             :             }
    1188           0 :           linebuf = newlinebuf;
    1189             :         }
    1190             :     }
    1191             : 
    1192             :  leave:
    1193           0 :   free (linebuf);
    1194           0 :   _gpgme_io_close (rp[0]);
    1195           0 :   return err;
    1196             : }
    1197             : 
    1198             : 
    1199             : static void
    1200         182 : gpgconf_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
    1201             : {
    1202             :   (void)engine;
    1203             :   (void)io_cbs;
    1204             :   /* Nothing to do.  */
    1205         182 : }
    1206             : 
    1207             : 
    1208             : /* Currently, we do not use the engine interface for the various
    1209             :    operations.  */
    1210             : void
    1211         141 : _gpgme_conf_release (gpgme_conf_comp_t conf)
    1212             : {
    1213         141 :   gpgconf_config_release (conf);
    1214         141 : }
    1215             : 
    1216             : 
    1217             : struct engine_ops _gpgme_engine_ops_gpgconf =
    1218             :   {
    1219             :     /* Static functions.  */
    1220             :     _gpgme_get_default_gpgconf_name,
    1221             :     NULL,
    1222             :     gpgconf_get_version,
    1223             :     gpgconf_get_req_version,
    1224             :     gpgconf_new,
    1225             : 
    1226             :     /* Member functions.  */
    1227             :     gpgconf_release,
    1228             :     NULL,               /* reset */
    1229             :     NULL,               /* set_status_cb */
    1230             :     NULL,               /* set_status_handler */
    1231             :     NULL,               /* set_command_handler */
    1232             :     NULL,               /* set_colon_line_handler */
    1233             :     NULL,               /* set_locale */
    1234             :     NULL,               /* set_protocol */
    1235             :     NULL,               /* decrypt */
    1236             :     NULL,               /* decrypt_verify */
    1237             :     NULL,               /* delete */
    1238             :     NULL,               /* edit */
    1239             :     NULL,               /* encrypt */
    1240             :     NULL,               /* encrypt_sign */
    1241             :     NULL,               /* export */
    1242             :     NULL,               /* export_ext */
    1243             :     NULL,               /* genkey */
    1244             :     NULL,               /* import */
    1245             :     NULL,               /* keylist */
    1246             :     NULL,               /* keylist_ext */
    1247             :     NULL,               /* keysign */
    1248             :     NULL,               /* tofu_policy */
    1249             :     NULL,               /* sign */
    1250             :     NULL,               /* trustlist */
    1251             :     NULL,               /* verify */
    1252             :     NULL,               /* getauditlog */
    1253             :     NULL,               /* opassuan_transact */
    1254             :     gpgconf_conf_load,
    1255             :     gpgconf_conf_save,
    1256             :     gpgconf_query_swdb,
    1257             :     gpgconf_set_io_cbs,
    1258             :     NULL,               /* io_event */
    1259             :     NULL,               /* cancel */
    1260             :     NULL,               /* cancel_op */
    1261             :     NULL,               /* passwd */
    1262             :     NULL,               /* set_pinentry_mode */
    1263             :     NULL                /* opspawn */
    1264             :   };

Generated by: LCOV version 1.13