From aad94cb7c313d4501bed748f48830cbb93c67e20 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Thu, 3 Nov 2016 16:29:45 +0100 Subject: [PATCH] core: Add gpgme_op_query_swdb and helper. * 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 --- NEWS | 3 + doc/gpgme.texi | 141 +++++++++++++++++++++++++++- src/Makefile.am | 2 +- src/context.h | 3 +- src/engine-assuan.c | 1 + src/engine-backend.h | 4 + src/engine-g13.c | 1 + src/engine-gpg.c | 1 + src/engine-gpgconf.c | 213 ++++++++++++++++++++++++++++++++++++++++++ src/engine-gpgsm.c | 1 + src/engine-spawn.c | 1 + src/engine-uiserver.c | 1 + src/engine.c | 15 +++ src/engine.h | 6 ++ src/gpgconf.c | 4 +- src/gpgme.def | 3 + src/gpgme.h.in | 61 ++++++++++++ src/libgpgme.vers | 3 + src/queryswdb.c | 121 ++++++++++++++++++++++++ src/util.h | 5 + tests/Makefile.am | 2 +- tests/run-swdb.c | 151 ++++++++++++++++++++++++++++++ 22 files changed, 736 insertions(+), 7 deletions(-) create mode 100644 src/queryswdb.c create mode 100644 tests/run-swdb.c diff --git a/NEWS b/NEWS index 0274f9c9..e43aa30d 100644 --- 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. diff --git a/doc/gpgme.texi b/doc/gpgme.texi index 9fae9aaf..a70418db 100644 --- a/doc/gpgme.texi +++ b/doc/gpgme.texi @@ -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 + +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 diff --git a/src/Makefile.am b/src/Makefile.am index f166f3b2..eddd1920 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 diff --git a/src/context.h b/src/context.h index f6c1ad1e..00e2e779 100644 --- a/src/context.h +++ b/src/context.h @@ -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; diff --git a/src/engine-assuan.c b/src/engine-assuan.c index 65924eb2..4c7fe281 100644 --- a/src/engine-assuan.c +++ b/src/engine-assuan.c @@ -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, diff --git a/src/engine-backend.h b/src/engine-backend.h index e02c7157..a8b1ac60 100644 --- a/src/engine-backend.h +++ b/src/engine-backend.h @@ -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); diff --git a/src/engine-g13.c b/src/engine-g13.c index d34db829..972c3a8a 100644 --- a/src/engine-g13.c +++ b/src/engine-g13.c @@ -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, diff --git a/src/engine-gpg.c b/src/engine-gpg.c index cb52dea7..7725a001 100644 --- a/src/engine-gpg.c +++ b/src/engine-gpg.c @@ -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, 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 */ diff --git a/src/engine-gpgsm.c b/src/engine-gpgsm.c index 0ce4a6d1..a815cf00 100644 --- a/src/engine-gpgsm.c +++ b/src/engine-gpgsm.c @@ -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, diff --git a/src/engine-spawn.c b/src/engine-spawn.c index df90cb23..d2c7dd62 100644 --- a/src/engine-spawn.c +++ b/src/engine-spawn.c @@ -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 */ diff --git a/src/engine-uiserver.c b/src/engine-uiserver.c index 76fa4d79..47b7dc33 100644 --- a/src/engine-uiserver.c +++ b/src/engine-uiserver.c @@ -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, diff --git a/src/engine.c b/src/engine.c index f5dfe51f..4e513b6d 100644 --- a/src/engine.c +++ b/src/engine.c @@ -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) { diff --git a/src/engine.h b/src/engine.h index 2999ab64..15b0b5d4 100644 --- a/src/engine.h +++ b/src/engine.h @@ -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, diff --git a/src/gpgconf.c b/src/gpgconf.c index 65914529..b1b84a62 100644 --- a/src/gpgconf.c +++ b/src/gpgconf.c @@ -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; } - - diff --git a/src/gpgme.def b/src/gpgme.def index d633df57..2f6837da 100644 --- a/src/gpgme.def +++ b/src/gpgme.def @@ -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 diff --git a/src/gpgme.h.in b/src/gpgme.h.in index 94ef51de..4f470a03 100644 --- a/src/gpgme.h.in +++ b/src/gpgme.h.in @@ -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); + + + /* * Various functions. diff --git a/src/libgpgme.vers b/src/libgpgme.vers index 42f00d5a..5457daa4 100644 --- a/src/libgpgme.vers +++ b/src/libgpgme.vers @@ -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 index 00000000..ce50b1e8 --- /dev/null +++ b/src/queryswdb.c @@ -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 . + */ + +#if HAVE_CONFIG_H +#include +#endif +#include +#include + +#include "gpgme.h" +#include "debug.h" +#include "context.h" +#include "ops.h" + + +typedef struct +{ + struct _gpgme_op_query_swdb_result result; + +} *op_data_t; + + + +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); +} diff --git a/src/util.h b/src/util.h index 1474b411..a1be6e7e 100644 --- a/src/util.h +++ b/src/util.h @@ -49,6 +49,11 @@ # 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 diff --git a/tests/Makefile.am b/tests/Makefile.am index c71914f3..e8c7c564 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -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 index 00000000..91ed22f3 --- /dev/null +++ b/tests/run-swdb.c @@ -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 . + */ + +/* 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 +#endif + +#include +#include +#include +#include + +#include + +#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; +}