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 <wk@gnupg.org>
This commit is contained in:
Werner Koch 2016-11-03 16:29:45 +01:00
parent 34a4e8017b
commit aad94cb7c3
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
22 changed files with 736 additions and 7 deletions

3
NEWS
View File

@ -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.

View 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

View 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

View 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;

View 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,

View 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);

View 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,

View 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,

View File

@ -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 */

View 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,

View 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 */

View 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,

View 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)
{

View 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,

View 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;
}

View 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

View 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);
/*
* Various functions.

View File

@ -122,6 +122,9 @@ GPGME_1.1 {
gpgme_set_sender;
gpgme_get_sender;
gpgme_op_query_swdb;
gpgme_op_query_swdb_result;
};

121
src/queryswdb.c Normal file
View File

@ -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;
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);
}

View File

@ -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

View 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

151
tests/run-swdb.c Normal file
View File

@ -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;
}