diff options
Diffstat (limited to 'src/engine-gpgconf.c')
| -rw-r--r-- | src/engine-gpgconf.c | 213 | 
1 files changed, 213 insertions, 0 deletions
| diff --git a/src/engine-gpgconf.c b/src/engine-gpgconf.c index 271a4dda..25c798e6 100644 --- a/src/engine-gpgconf.c +++ b/src/engine-gpgconf.c @@ -47,6 +47,7 @@  #include "engine-backend.h" +  struct engine_gpgconf  { @@ -941,6 +942,217 @@ gpgconf_conf_save (void *engine, gpgme_conf_comp_t comp)  } +/* Parse a line received from gpgconf --query-swdb.  This function may + * modify LINE.  The result is stored at RESUL.  */ +static gpg_error_t +parse_swdb_line (char *line, gpgme_query_swdb_result_t result) +{ +  char *field[9]; +  int fields = 0; +  gpg_err_code_t ec; + +  while (line && fields < DIM (field)) +    { +      field[fields++] = line; +      line = strchr (line, ':'); +      if (line) +	*line++ = 0; +    } +  /* We require that all fields exists - gpgme emits all these fields +   * even on error.  They might be empty, though. */ +  if (fields < 9) +    return gpg_error (GPG_ERR_INV_ENGINE); + +  free (result->name); +  result->name = strdup (field[0]); +  if (!result->name) +    return gpg_error_from_syserror (); + +  free (result->iversion); +  result->iversion = strdup (field[1]); +  if (!result->iversion) +    return gpg_error_from_syserror (); + +  result->urgent = (strtol (field[3], NULL, 10) > 0); + +  ec = gpg_err_code (strtoul (field[4], NULL, 10)); + +  result->created  = _gpgme_parse_timestamp (field[5], NULL); +  result->retrieved= _gpgme_parse_timestamp (field[6], NULL); + +  free (result->version); +  result->version  = strdup (field[7]); +  if (!result->version) +    return gpg_error_from_syserror (); + +  result->reldate  = _gpgme_parse_timestamp (field[8], NULL); + +  /* Set other flags.  */ +  result->warning = !!ec; +  result->update = 0; +  result->noinfo = 0; +  result->unknown = 0; +  result->tooold = 0; +  result->error = 0; + +  switch (*field[2]) +    { +    case '-': result->warning = 1; break; +    case '?': result->unknown = result->warning = 1; break; +    case 'u': result->update = 1; break; +    case 'c': break; +    case 'n': break; +    default: +      result->warning = 1; +      if (!ec) +        ec = GPG_ERR_INV_ENGINE; +      break; +    } + +  if (ec == GPG_ERR_TOO_OLD) +    result->tooold = 1; +  else if (ec == GPG_ERR_ENOENT) +    result->noinfo = 1; +  else if (ec) +    result->error = 1; + + +  return 0; +} + + +static gpgme_error_t +gpgconf_query_swdb (void *engine, +                    const char *name, const char *iversion, +                    gpgme_query_swdb_result_t result) +{ +  struct engine_gpgconf *gpgconf = engine; +  gpgme_error_t err = 0; +  char *linebuf; +  size_t linebufsize; +  int linelen; +  char *argv[7]; +  int argc = 0; +  int rp[2]; +  struct spawn_fd_item_s cfd[] = { {-1, 1 /* STDOUT_FILENO */, -1, 0}, +				   {-1, -1} }; +  int status; +  int nread; +  char *mark = NULL; + +  if (!have_gpgconf_version (gpgconf, "2.1.16")) +    return gpg_error (GPG_ERR_ENGINE_TOO_OLD); + +  /* _gpgme_engine_new guarantees that this is not NULL.  */ +  argv[argc++] = gpgconf->file_name; + +  if (gpgconf->home_dir) +    { +      argv[argc++] = (char*)"--homedir"; +      argv[argc++] = gpgconf->home_dir; +    } + +  argv[argc++] = (char*)"--query-swdb"; +  argv[argc++] = (char*)name; +  argv[argc++] = (char*)iversion; +  argv[argc] = NULL; +  assert (argc < DIM (argv)); + +  if (_gpgme_io_pipe (rp, 1) < 0) +    return gpg_error_from_syserror (); + +  cfd[0].fd = rp[1]; + +  status = _gpgme_io_spawn (gpgconf->file_name, argv, +                            IOSPAWN_FLAG_DETACHED, cfd, NULL, NULL, NULL); +  if (status < 0) +    { +      _gpgme_io_close (rp[0]); +      _gpgme_io_close (rp[1]); +      return gpg_error_from_syserror (); +    } + +  linebufsize = 2048; /* Same as used by gpgconf.  */ +  linebuf = malloc (linebufsize); +  if (!linebuf) +    { +      err = gpg_error_from_syserror (); +      goto leave; +    } +  linelen = 0; + +  while ((nread = _gpgme_io_read (rp[0], linebuf + linelen, +                                  linebufsize - linelen - 1))) +    { +      char *line; +      const char *lastmark = NULL; +      size_t nused; + +      if (nread < 0) +        { +          err = gpg_error_from_syserror (); +          goto leave; +        } + +      linelen += nread; +      linebuf[linelen] = '\0'; + +      for (line=linebuf; (mark = strchr (line, '\n')); line = mark+1 ) +        { +          lastmark = mark; +          if (mark > line && mark[-1] == '\r') +            mark[-1] = '\0'; +          else +            mark[0] = '\0'; + +          /* Got a full line.  Due to the CR removal code (which +             occurs only on Windows) we might be one-off and thus +             would see empty lines.  */ +          if (*line) +            { +              err = parse_swdb_line (line, result); +              goto leave; /* Ready.  */ +            } +          else /* empty line.  */ +            err = 0; +        } + +      nused = lastmark? (lastmark + 1 - linebuf) : 0; +      memmove (linebuf, linebuf + nused, linelen - nused); +      linelen -= nused; + +      if (!(linelen < linebufsize - 1)) +        { +          char *newlinebuf; + +          if (linelen <  8 * 1024 - 1) +            linebufsize = 8 * 1024; +          else if (linelen < 64 * 1024 - 1) +            linebufsize = 64 * 1024; +          else +            { +              /* We reached our limit - give up.  */ +              err = gpg_error (GPG_ERR_LINE_TOO_LONG); +              goto leave; +            } + +          newlinebuf = realloc (linebuf, linebufsize); +          if (!newlinebuf) +            { +              err = gpg_error_from_syserror (); +              goto leave; +            } +          linebuf = newlinebuf; +        } +    } + + leave: +  free (linebuf); +  _gpgme_io_close (rp[0]); +  return err; +} + +  static void  gpgconf_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)  { @@ -998,6 +1210,7 @@ struct engine_ops _gpgme_engine_ops_gpgconf =      NULL,               /* opassuan_transact */      gpgconf_conf_load,      gpgconf_conf_save, +    gpgconf_query_swdb,      gpgconf_set_io_cbs,      NULL,		/* io_event */      NULL,		/* cancel */ | 
