core: Add gpgme_op_query_swdb and helper.
authorWerner Koch <wk@gnupg.org>
Thu, 3 Nov 2016 15:29:45 +0000 (16:29 +0100)
committerWerner Koch <wk@gnupg.org>
Thu, 3 Nov 2016 16:32:30 +0000 (17:32 +0100)
* src/gpgme.h.in (gpgme_query_swdb_result_t): New.
(gpgme_op_query_swdb): New.
(gpgme_op_query_swdb_result): New.
* src/libgpgme.vers, src/gpgme.def: Add the two new functions.
* src/queryswdb.c: New.
* src/Makefile.am (main_sources): Add new file.
* src/context.h (OPDATA_QUERY_SWDB): New.
* src/engine-backend.h (struct engine_ops): Add field 'query_swdb'.
Adjust all initializer.
* src/engine.c (_gpgme_engine_op_query_swdb): New.
* src/engine-gpgconf.c (parse_swdb_line): New.
(gpgconf_query_swdb): New.
(_gpgme_engine_ops_gpgconf): Register that function.

* src/util.h (GPG_ERR_TOO_OLD): Define for older libgpg-error.
(GPG_ERR_ENGINE_TOO_OLD): Ditto.

* tests/run-swdb.c: New.
* tests/Makefile.am (noinst_PROGRAMS): Add new debug tool.

Signed-off-by: Werner Koch <wk@gnupg.org>
22 files changed:
NEWS
doc/gpgme.texi
src/Makefile.am
src/context.h
src/engine-assuan.c
src/engine-backend.h
src/engine-g13.c
src/engine-gpg.c
src/engine-gpgconf.c
src/engine-gpgsm.c
src/engine-spawn.c
src/engine-uiserver.c
src/engine.c
src/engine.h
src/gpgconf.c
src/gpgme.def
src/gpgme.h.in
src/libgpgme.vers
src/queryswdb.c [new file with mode: 0644]
src/util.h
tests/Makefile.am
tests/run-swdb.c [new file with mode: 0644]

diff --git a/NEWS b/NEWS
index 0274f9c..e43aa30 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -9,6 +9,9 @@ Noteworthy changes in version 1.7.2 (unreleased)
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  gpgme_set_sender                NEW.
  gpgme_get_sender                NEW.
+ gpgme_op_query_swdb             NEW.
+ gpgme_op_query_swdb_result      NEW.
+ gpgme_query_swdb_result_t       NEW.
  qt: DN                          NEW.
  qt: DN::Attribute               NEW.
 
index 9fae9aa..a70418d 100644 (file)
@@ -237,7 +237,9 @@ Encrypt
 
 Miscellaneous
 
-* Running other Programs::        Running other Programs
+* Running other Programs::        Running other Programs.
+* Using the Assuan protocol::     Using the Assuan protocol.
+* Checking for updates::          How to check for software updates.
 
 Run Control
 
@@ -5561,6 +5563,7 @@ Here are some support functions which are sometimes useful.
 @menu
 * Running other Programs::      Running other Programs
 * Using the Assuan protocol::   Using the Assuan protocol
+* Checking for updates::        How to check for software updates
 @end menu
 
 
@@ -5692,6 +5695,142 @@ Synchronous variant.
 @end deftypefun
 
 
+@node Checking for updates
+@subsection How to check for software updates
+
+The GnuPG Project operates a server to query the current versions of
+software packages related to GnuPG.  GPGME can be used to
+access this online database and check whether a new version of a
+software package is available.
+
+@deftp {Data type} {gpgme_query_swdb_result_t}
+This is a pointer to a structure used to store the result of a
+@code{gpgme_op_query_swdb} operation.  After success full call to that
+function, you can retrieve the pointer to the result with
+@code{gpgme_op_query_swdb_result}.  The structure contains the
+following member:
+
+@table @code
+@item name
+This is the name of the package.
+
+@item iversion
+The currently installed version or an empty string.  This value is
+either a copy of the argument given to @code{gpgme_op_query_swdb} or
+the version of the installed software as figured out by GPGME or GnuPG.
+
+@item created
+This gives the date the file with the list of version numbers has
+originally be created by the GnuPG project.
+
+@item retrieved
+This gives the date the file was downloaded.
+
+@item warning
+If this flag is set either an error has occurred or some of the
+information in this structure are not properly set.  For example if
+the version number of the installed software could not be figured out,
+the @code{update} flag may not reflect a required update status.
+
+@item update
+If this flag is set an update of the software is available.
+
+@item urgent
+If this flag is set an available update is important.
+
+@item noinfo
+If this flag is set, no valid information could be retrieved.
+
+@item unknown
+If this flag is set the given @code{name} is not known.
+
+@item tooold
+If this flag is set the available information is not fresh enough.
+
+@item error
+If this flag is set some other error has occured.
+
+@item version
+The version string of the latest released version.
+
+@item reldate
+The release date of the latest released version.
+
+@end table
+@end deftp
+
+@deftypefun gpgme_error_t gpgme_op_query_swdb @
+            (@w{gpgme_ctx_t @var{ctx}},       @
+             @w{const char *@var{name}},      @
+             @w{const char *@var{iversion}},  @
+             @w{gpgme_data_t @var{reserved}})
+
+Query the software version database for software package @var{name}
+and check against the installed version given by @var{iversion}.  If
+@var{iversion} is given as @code{NULL} a check is only done if GPGME
+can figure out the version by itself (for example when using
+"gpgme" or "gnupg").  If @code{NULL} is used for @var{name} the
+current gpgme version is checked.  @var{reserved} must be set to 0.
+
+@end deftypefun
+
+@deftypefun gpgme_query_swdb_result_t gpgme_op_query_swdb_result @
+            (@w{gpgme_ctx_t @var{ctx}})
+
+The function @code{gpgme_op_query_swdb_result} returns a
+@code{gpgme_query_swdb_result_t} pointer to a structure holding the
+result of a @code{gpgme_op_query_swdb} operation.  The pointer is only
+valid if the last operation on the context was a sucessful call to
+@code{gpgme_op_query_swdb}.  If that call failed, the result might
+be a @code{NULL} pointer.  The returned pointer is only valid until
+the next operation is started on the context @var{ctx}.
+@end deftypefun
+
+@noindent
+Here is an example on how to check whether GnuPG is current:
+
+@example
+#include <gpgme.h>
+
+int
+main (void)
+@{
+  gpg_error_t err;
+  gpgme_ctx_t ctx;
+  gpgme_query_swdb_result_t result;
+
+  gpgme_check_version (NULL);
+  err = gpgme_new (&ctx);
+  if (err)
+    fprintf (stderr, "error creating context: %s\n", gpg_strerror (err));
+  else
+    @{
+      gpgme_set_protocol (ctx, GPGME_PROTOCOL_GPGCONF);
+
+      err = gpgme_op_query_swdb (ctx, "gnupg", NULL, 0);
+      if (err)
+        fprintf (stderr, "error querying swdb: %s\n", gpg_strerror (err));
+      else
+        @{
+          result = gpgme_op_query_swdb_result (ctx);
+          if (!result)
+            fprintf (stderr, "error querying swdb\n");
+          if (!result->warning && !result->update)
+            printf ("GnuPG version %s is current\n",
+                    result->iversion);
+          else if (!result->warning && result->update)
+            printf ("GnuPG version %s can be updated to %s\n",
+                    result->iversion, result->version);
+          else
+            fprintf (stderr, "error finding the update status\n");
+        @}
+      gpgme_release (ctx);
+    @}
+  return 0;
+@}
+@end example
+
+
 @node Run Control
 @section Run Control
 @cindex run control
index f166f3b..eddd192 100644 (file)
@@ -91,7 +91,7 @@ main_sources =                                                                \
        $(uiserver_components)                                          \
        engine-g13.c vfs-mount.c vfs-create.c                           \
        engine-spawn.c                                                  \
-       gpgconf.c                                                       \
+       gpgconf.c queryswdb.c                                           \
        sema.h priv-io.h $(system_components) sys-util.h dirinfo.c      \
        debug.c debug.h gpgme.c version.c error.c
 
index f6c1ad1..00e2e77 100644 (file)
@@ -38,7 +38,8 @@ typedef enum
     OPDATA_DECRYPT, OPDATA_SIGN, OPDATA_ENCRYPT, OPDATA_PASSPHRASE,
     OPDATA_IMPORT, OPDATA_GENKEY, OPDATA_KEYLIST, OPDATA_EDIT,
     OPDATA_VERIFY, OPDATA_TRUSTLIST, OPDATA_ASSUAN, OPDATA_VFS_MOUNT,
-    OPDATA_PASSWD, OPDATA_EXPORT, OPDATA_KEYSIGN, OPDATA_TOFU_POLICY
+    OPDATA_PASSWD, OPDATA_EXPORT, OPDATA_KEYSIGN, OPDATA_TOFU_POLICY,
+    OPDATA_QUERY_SWDB
   } ctx_op_data_id_t;
 
 
index 65924eb..4c7fe28 100644 (file)
@@ -796,6 +796,7 @@ struct engine_ops _gpgme_engine_ops_assuan =
     llass_transact,     /* opassuan_transact */
     NULL,              /* conf_load */
     NULL,              /* conf_save */
+    NULL,               /* query_swdb */
     llass_set_io_cbs,
     llass_io_event,
     llass_cancel,
index e02c715..a8b1ac6 100644 (file)
@@ -127,6 +127,10 @@ struct engine_ops
   gpgme_error_t  (*conf_load) (void *engine, gpgme_conf_comp_t *conf_p);
   gpgme_error_t  (*conf_save) (void *engine, gpgme_conf_comp_t conf);
 
+  gpgme_error_t  (*query_swdb) (void *engine,
+                                const char *name, const char *iversion,
+                                gpgme_query_swdb_result_t result);
+
   void (*set_io_cbs) (void *engine, gpgme_io_cbs_t io_cbs);
   void (*io_event) (void *engine, gpgme_event_io_t type, void *type_data);
 
index d34db82..972c3a8 100644 (file)
@@ -811,6 +811,7 @@ struct engine_ops _gpgme_engine_ops_g13 =
     g13_transact,
     NULL,              /* conf_load */
     NULL,              /* conf_save */
+    NULL,               /* query_swdb */
     g13_set_io_cbs,
     g13_io_event,
     g13_cancel,
index cb52dea..7725a00 100644 (file)
@@ -2969,6 +2969,7 @@ struct engine_ops _gpgme_engine_ops_gpg =
     NULL,               /* opassuan_transact */
     NULL,              /* conf_load */
     NULL,              /* conf_save */
+    NULL,               /* query_swdb */
     gpg_set_io_cbs,
     gpg_io_event,
     gpg_cancel,
index 271a4dd..25c798e 100644 (file)
@@ -47,6 +47,7 @@
 
 #include "engine-backend.h"
 
+
 \f
 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 */
index 0ce4a6d..a815cf0 100644 (file)
@@ -2101,6 +2101,7 @@ struct engine_ops _gpgme_engine_ops_gpgsm =
     NULL,               /* opassuan_transact */
     NULL,              /* conf_load */
     NULL,              /* conf_save */
+    NULL,               /* query_swdb */
     gpgsm_set_io_cbs,
     gpgsm_io_event,
     gpgsm_cancel,
index df90cb2..d2c7dd6 100644 (file)
@@ -469,6 +469,7 @@ struct engine_ops _gpgme_engine_ops_spawn =
     NULL,               /* opassuan_transact */
     NULL,              /* conf_load */
     NULL,              /* conf_save */
+    NULL,               /* query_swdb */
     engspawn_set_io_cbs,
     engspawn_io_event, /* io_event */
     engspawn_cancel,   /* cancel */
index 76fa4d7..47b7dc3 100644 (file)
@@ -1393,6 +1393,7 @@ struct engine_ops _gpgme_engine_ops_uiserver =
     NULL,               /* opassuan_transact */
     NULL,              /* conf_load */
     NULL,              /* conf_save */
+    NULL,               /* query_swdb */
     uiserver_set_io_cbs,
     uiserver_io_event,
     uiserver_cancel,
index f5dfe51..4e513b6 100644 (file)
@@ -980,6 +980,21 @@ _gpgme_engine_op_conf_save (engine_t engine, gpgme_conf_comp_t conf)
 }
 
 
+gpgme_error_t
+_gpgme_engine_op_query_swdb (engine_t engine,
+                             const char *name, const char *iversion,
+                             gpgme_query_swdb_result_t result)
+{
+  if (!engine)
+    return gpg_error (GPG_ERR_INV_VALUE);
+
+  if (!engine->ops->query_swdb)
+    return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+
+  return (*engine->ops->query_swdb) (engine->engine, name, iversion, result);
+}
+
+
 void
 _gpgme_engine_set_io_cbs (engine_t engine, gpgme_io_cbs_t io_cbs)
 {
index 2999ab6..15b0b5d 100644 (file)
@@ -173,6 +173,12 @@ gpgme_error_t _gpgme_engine_op_conf_load (engine_t engine,
 gpgme_error_t _gpgme_engine_op_conf_save (engine_t engine,
                                          gpgme_conf_comp_t conf);
 
+gpgme_error_t _gpgme_engine_op_query_swdb (engine_t engine,
+                                           const char *name,
+                                           const char *iversion,
+                                           gpgme_query_swdb_result_t result);
+
+
 void _gpgme_engine_set_io_cbs (engine_t engine,
                               gpgme_io_cbs_t io_cbs);
 void _gpgme_engine_io_event (engine_t engine,
index 6591452..b1b84a6 100644 (file)
@@ -65,7 +65,7 @@ gpgme_conf_release (gpgme_conf_comp_t conf)
 }
 
 
-/* Public function to release load a configuration list.  No
+/* Public function to load a configuration list.  No
    asynchronous interface for now.  */
 gpgme_error_t
 gpgme_op_conf_load (gpgme_ctx_t ctx, gpgme_conf_comp_t *conf_p)
@@ -108,5 +108,3 @@ gpgme_op_conf_save (gpgme_ctx_t ctx, gpgme_conf_comp_t comp)
   ctx->protocol = proto;
   return err;
 }
-
-
index d633df5..2f6837d 100644 (file)
@@ -249,5 +249,8 @@ EXPORTS
     gpgme_set_sender                      @187
     gpgme_get_sender                      @188
 
+    gpgme_op_query_swdb                   @189
+    gpgme_op_query_swdb_result            @190
+
 ; END
 
index 94ef51d..4f470a0 100644 (file)
@@ -2418,6 +2418,67 @@ gpgme_error_t gpgme_op_conf_load (gpgme_ctx_t ctx, gpgme_conf_comp_t *conf_p);
 gpgme_error_t gpgme_op_conf_save (gpgme_ctx_t ctx, gpgme_conf_comp_t comp);
 
 
+/* Information about software versions.  */
+typedef struct _gpgme_op_query_swdb_result
+{
+  /* RFU */
+  struct _gpgme_op_query_swdb_result *next;
+
+  /* The name of the package (e.g. "gpgme", "gnupg") */
+  char *name;
+
+  /* The version number of the installed version.  */
+  char *iversion;
+
+  /* The time the online info was created.  */
+  unsigned long created;
+
+  /* The time the online info was retrieved.  */
+  unsigned long retrieved;
+
+  /* This bit is set if an error occured or some of the information
+   * in this structure may not be set.  */
+  unsigned int warning : 1;
+
+  /* An update is available.  */
+  unsigned int update : 1;
+
+  /* The update is important.  */
+  unsigned int urgent : 1;
+
+  /* No information at all available.  */
+  unsigned int noinfo : 1;
+
+  /* The package name is not known. */
+  unsigned int unknown : 1;
+
+  /* The information here is too old.  */
+  unsigned int tooold : 1;
+
+  /* Other error.  */
+  unsigned int error : 1;
+
+  unsigned int _reserved : 25;
+
+  /* The version number of the latest released version.  */
+  char *version;
+
+  /* The release date of that version.  */
+  unsigned long reldate;
+
+} *gpgme_query_swdb_result_t;
+
+
+/* Run the gpgconf --query-swdb command.  */
+gpgme_error_t gpgme_op_query_swdb (gpgme_ctx_t ctx,
+                                   const char *name, const char *iversion,
+                                   unsigned int reserved);
+
+/* Return the result from the last query_swdb operation.  */
+gpgme_query_swdb_result_t gpgme_op_query_swdb_result (gpgme_ctx_t ctx);
+
+
+
 \f
 /*
  * Various functions.
index 42f00d5..5457daa 100644 (file)
@@ -122,6 +122,9 @@ GPGME_1.1 {
 
     gpgme_set_sender;
     gpgme_get_sender;
+
+    gpgme_op_query_swdb;
+    gpgme_op_query_swdb_result;
 };
 
 
diff --git a/src/queryswdb.c b/src/queryswdb.c
new file mode 100644 (file)
index 0000000..ce50b1e
--- /dev/null
@@ -0,0 +1,121 @@
+/* queryswdb.c - Access to the SWDB file
+ * Copyright (C) 2016 g10 Code GmbH
+ *
+ * This file is part of GPGME.
+ *
+ * GPGME is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * GPGME is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdlib.h>
+#include <assert.h>
+
+#include "gpgme.h"
+#include "debug.h"
+#include "context.h"
+#include "ops.h"
+
+
+typedef struct
+{
+  struct _gpgme_op_query_swdb_result result;
+
+} *op_data_t;
+
+
+\f
+static void
+release_op_data (void *hook)
+{
+  op_data_t opd = (op_data_t) hook;
+  gpgme_query_swdb_result_t result = &opd->result;
+
+  assert (!result->next);
+  free (result->name);
+  free (result->iversion);
+  free (result->version);
+}
+
+
+gpgme_query_swdb_result_t
+gpgme_op_query_swdb_result (gpgme_ctx_t ctx)
+{
+  void *hook;
+  op_data_t opd;
+  gpgme_error_t err;
+
+  TRACE_BEG (DEBUG_CTX, "gpgme_op_query_swdb_result", ctx);
+
+  err = _gpgme_op_data_lookup (ctx, OPDATA_QUERY_SWDB, &hook, -1, NULL);
+  opd = hook;
+
+  if (err || !opd)
+    {
+      TRACE_SUC0 ("result=(null)");
+      return NULL;
+    }
+
+  TRACE_SUC1 ("result=%p", &opd->result);
+  return &opd->result;
+}
+
+
+
+/* Query the swdb for software package NAME and check against the
+ * installed version given by IVERSION.  If IVERSION is NULL a check
+ * is only done if GPGME can figure out the version by itself
+ * (e.g. for "gpgme" or "gnupg").  RESERVED should be 0.
+ *
+ * Note that we only implemented the synchronous variant of this
+ * function but the API is prepared for an asynchronous variant.
+ */
+gpgme_error_t
+gpgme_op_query_swdb (gpgme_ctx_t ctx, const char *name, const char *iversion,
+                     unsigned int reserved)
+{
+  gpgme_error_t err;
+  void *hook;
+  op_data_t opd;
+
+  TRACE_BEG2 (DEBUG_CTX, "gpgme_op_query_swdb", ctx,
+             "name=%s, iversion=%a", name, iversion);
+
+  if (!ctx || reserved)
+    return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+
+  if (ctx->protocol != GPGME_PROTOCOL_GPGCONF)
+    return TRACE_ERR (gpg_error (GPG_ERR_UNSUPPORTED_PROTOCOL));
+
+  if (!name)
+    name = "gpgme";
+
+  if (!iversion && !strcmp (name, "gpgme"))
+    iversion = VERSION;
+
+  err = _gpgme_op_reset (ctx, 1);
+  if (err)
+    return err;
+
+  err = _gpgme_op_data_lookup (ctx, OPDATA_QUERY_SWDB, &hook,
+                               sizeof (*opd), release_op_data);
+  opd = hook;
+  if (err)
+    return TRACE_ERR (err);
+
+  err = _gpgme_engine_op_query_swdb (ctx->engine, name, iversion,
+                                     &opd->result);
+  return TRACE_ERR (err);
+}
index 1474b41..a1be6e7 100644 (file)
 # define GPG_ERR_FALSE 256
 #endif
 
+#if GPG_ERROR_VERSION_NUMBER < 0x011900 /* 1.25 */
+# define GPG_ERR_ENGINE_TOO_OLD 300
+# define GPG_ERR_TOO_OLD        308
+#endif
+
 #ifndef GPGRT_ATTR_SENTINEL
 # define GPGRT_ATTR_SENTINEL(a)  /* */
 #endif
index c71914f..e8c7c56 100644 (file)
@@ -33,7 +33,7 @@ noinst_HEADERS = run-support.h
 
 noinst_PROGRAMS = $(TESTS) run-keylist run-export run-import run-sign \
                  run-verify run-encrypt run-identify run-decrypt run-genkey \
-                 run-keysign run-tofu
+                 run-keysign run-tofu run-swdb
 
 
 if RUN_GPG_TESTS
diff --git a/tests/run-swdb.c b/tests/run-swdb.c
new file mode 100644 (file)
index 0000000..91ed22f
--- /dev/null
@@ -0,0 +1,151 @@
+/* run-swdb.c  - Test tool for SWDB function
+ * Copyright (C) 2016 g10 Code GmbH
+ *
+ * This file is part of GPGME.
+ *
+ * GPGME is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * GPGME is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* We need to include config.h so that we know whether we are building
+   with large file system (LFS) support. */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#include <gpgme.h>
+
+#define PGM "run-swdb"
+
+#include "run-support.h"
+
+
+static int verbose;
+
+
+static const char *
+isotimestr (unsigned long value)
+{
+  time_t t;
+  static char buffer[25+5];
+  struct tm *tp;
+
+  if (!value)
+    return "none";
+  t = value;
+
+  tp = gmtime (&t);
+  snprintf (buffer, sizeof buffer, "%04d-%02d-%02d %02d:%02d:%02d",
+            1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday,
+            tp->tm_hour, tp->tm_min, tp->tm_sec);
+  return buffer;
+}
+
+
+static int
+show_usage (int ex)
+{
+  fputs ("usage: " PGM " [options] NAME [VERSION]\n\n"
+         "Options:\n"
+         "  --verbose        run in verbose mode\n"
+         "  --status         print status lines from the backend\n"
+         , stderr);
+  exit (ex);
+}
+
+
+int
+main (int argc, char **argv)
+{
+  int last_argc = -1;
+  gpgme_error_t err;
+  gpgme_ctx_t ctx;
+  gpgme_protocol_t protocol = GPGME_PROTOCOL_GPGCONF;
+  const char *name;
+  const char *iversion;
+  gpgme_query_swdb_result_t result;
+
+  if (argc)
+    { argc--; argv++; }
+
+  while (argc && last_argc != argc )
+    {
+      last_argc = argc;
+      if (!strcmp (*argv, "--"))
+        {
+          argc--; argv++;
+          break;
+        }
+      else if (!strcmp (*argv, "--help"))
+        show_usage (0);
+      else if (!strcmp (*argv, "--verbose"))
+        {
+          verbose = 1;
+          argc--; argv++;
+        }
+      else if (!strncmp (*argv, "--", 2))
+        show_usage (1);
+    }
+
+  if (argc < 1 || argc > 2)
+    show_usage (1);
+  name = argv[0];
+  iversion = argc > 1? argv[1] : NULL;
+
+  init_gpgme (protocol);
+
+  err = gpgme_new (&ctx);
+  fail_if_err (err);
+  gpgme_set_protocol (ctx, protocol);
+
+  err = gpgme_op_query_swdb (ctx, name, iversion, 0);
+  if (err)
+    {
+      fprintf (stderr, PGM ": error querying swdb: %s\n", gpg_strerror (err));
+      exit (1);
+    }
+
+  result = gpgme_op_query_swdb_result (ctx);
+  if (!result)
+    {
+      fprintf (stderr, PGM ": error querying swdb: %s\n", "no result");
+      exit (1);
+    }
+
+  printf ("package ...: %s\n"
+          "iversion ..: %s\n"
+          "version ...: %s\n",
+          nonnull (result->name),
+          nonnull (result->iversion),
+          nonnull (result->version));
+  printf ("reldate ...: %s\n", isotimestr (result->reldate));
+  printf ("created ...: %s\n", isotimestr (result->created));
+  printf ("retrieved .: %s\n", isotimestr (result->retrieved));
+  printf ("flags .....:%s%s%s%s%s%s%s\n",
+          result->warning? " warning" : "",
+          result->update?  " update"  : "",
+          result->urgent?  " urgent"  : "",
+          result->unknown? " unknown" : "",
+          result->tooold?  " tooold"  : "",
+          result->noinfo?  " noinfo"  : "",
+          result->error?   " error"   : "" );
+
+
+  gpgme_release (ctx);
+  return 0;
+}