diff options
Diffstat (limited to '')
| -rw-r--r-- | src/Makefile.am | 1 | ||||
| -rw-r--r-- | src/cJSON.c | 89 | ||||
| -rw-r--r-- | src/context.h | 7 | ||||
| -rw-r--r-- | src/data-estream.c | 99 | ||||
| -rw-r--r-- | src/data-mem.c | 34 | ||||
| -rw-r--r-- | src/data.c | 285 | ||||
| -rw-r--r-- | src/data.h | 29 | ||||
| -rw-r--r-- | src/decrypt-verify.c | 6 | ||||
| -rw-r--r-- | src/decrypt.c | 88 | ||||
| -rw-r--r-- | src/edit.c | 6 | ||||
| -rw-r--r-- | src/encrypt-sign.c | 2 | ||||
| -rw-r--r-- | src/encrypt.c | 2 | ||||
| -rw-r--r-- | src/engine-backend.h | 2 | ||||
| -rw-r--r-- | src/engine-gpg.c | 169 | ||||
| -rw-r--r-- | src/engine-gpgsm.c | 7 | ||||
| -rw-r--r-- | src/engine.c | 6 | ||||
| -rw-r--r-- | src/engine.h | 3 | ||||
| -rw-r--r-- | src/genkey.c | 8 | ||||
| -rw-r--r-- | src/getauditlog.c | 9 | ||||
| -rw-r--r-- | src/gpgme-json.c | 2524 | ||||
| -rw-r--r-- | src/gpgme.c | 20 | ||||
| -rw-r--r-- | src/gpgme.h.in | 10 | ||||
| -rw-r--r-- | src/keysign.c | 2 | ||||
| -rw-r--r-- | src/ops.h | 3 | ||||
| -rw-r--r-- | src/passwd.c | 2 | ||||
| -rw-r--r-- | src/sign.c | 2 | 
26 files changed, 2923 insertions, 492 deletions
| diff --git a/src/Makefile.am b/src/Makefile.am index 0a196e0c..1394c028 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -70,6 +70,7 @@ main_sources =								\  	parsetlv.c parsetlv.h                                           \  	mbox-util.c mbox-util.h                                         \  	data.h data.c data-fd.c data-stream.c data-mem.c data-user.c	\ +	data-estream.c                                                  \  	data-compat.c data-identify.c					\  	signers.c sig-notation.c					\  	wait.c wait-global.c wait-private.c wait-user.c wait.h		\ diff --git a/src/cJSON.c b/src/cJSON.c index cf0cb132..9e53012e 100644 --- a/src/cJSON.c +++ b/src/cJSON.c @@ -22,7 +22,14 @@   * SPDX-License-Identifier: MIT   *   * Note that this code has been modified from the original code taken - * from cjson-code-58.zip. + * from cjson-code-58.zip before 2014 (my first local commit was in + * 2014 but I may used the code even earlier).  Since 2016 the project + * was revived and moved to https://github.com/DaveGamble/cJSON.git. + * It is now a lot more complex and has substantial changes so that it + * is not possible to merge them directly.  In any case we only need a + * simple parser and not a complete library.  I have looked through + * the commits and fixed a few things which should apply; I also added + * a few references to the upstream code.  Regression test are missing!   */  #ifdef HAVE_CONFIG_H @@ -38,20 +45,42 @@  #include <ctype.h>  #include <errno.h> +#include <gpg-error.h> +  #include "cJSON.h" +/* Only use calloc. */ +#define CALLOC_ONLY 1 + +/* To avoid that a compiler optimizes certain memset calls away, these +   macros may be used instead. */ +#define wipememory2(_ptr,_set,_len) do { \ +        volatile char *_vptr=(volatile char *)(_ptr); \ +        size_t _vlen=(_len); \ +        while(_vlen) { *_vptr=(_set); _vptr++; _vlen--; } \ +    } while(0) +#define wipememory(_ptr,_len) wipememory2(_ptr,0,_len) +  /* We use malloc function wrappers from gpgrt (aka libgpg-error).  */ -#if 1 +#if GPGRT_VERSION_NUMBER >= 0x011c00 /* 1.28 */  # include <gpgrt.h> -# define xtrymalloc(a)   gpgrt_malloc ((a))  # define xtrycalloc(a,b) gpgrt_calloc ((a), (b))  # define xtrystrdup(a)   gpgrt_strdup ((a))  # define xfree(a)        gpgrt_free ((a)) -#else -# define xtrymalloc(a)   malloc ((a)) +# if CALLOC_ONLY +#  define xtrymalloc(a)  gpgrt_calloc (1, (a)) +# else +#  define xtrymalloc(a)  gpgrt_malloc ((a)) +# endif +#else /* Without gpgrt (aka libgpg-error).  */  # define xtrycalloc(a,b) calloc ((a), (b))  # define xtrystrdup(a)   strdup ((a))  # define xfree(a)        free ((a)) +# if CALLOC_ONLY +#  define xtrymalloc(a)  calloc (1, (a)) +# else +#  define xtrymalloc(a)  malloc ((a)) +# endif  #endif @@ -94,9 +123,15 @@ cJSON_Delete (cJSON * c)        if (!(c->type & cJSON_IsReference) && c->child)  	cJSON_Delete (c->child);        if (!(c->type & cJSON_IsReference) && c->valuestring) -	xfree (c->valuestring); +        { +          wipememory (c->valuestring, strlen (c->valuestring)); +          xfree (c->valuestring); +        }        if (c->string) -	xfree (c->string); +        { +          wipememory (c->string, strlen (c->string)); +          xfree (c->string); +        }        xfree (c);        c = next;      } @@ -232,6 +267,9 @@ parse_string (cJSON * item, const char *str, const char **ep)    char *out;    int len = 0;    unsigned uc, uc2; + +  /* FIXME: We should consider eary failure like it is done with +   * commit 8656386c4f4a12f1cf3d6b26158407fd05e65029 in upstream.  */    if (*str != '\"')      {        *ep = str; @@ -239,11 +277,13 @@ parse_string (cJSON * item, const char *str, const char **ep)      }				/* not a string! */    while (*ptr != '\"' && *ptr && ++len) -    if (*ptr++ == '\\') +    if (*ptr++ == '\\' && *ptr)        ptr++;			/* Skip escaped quotes. */ -  out = xtrymalloc (len + 1);	/* This is how long we need for the -                                   string, roughly. */ +  out = xtrymalloc (len + 2);	/* This is how long we need for the +                                 * string, roughly.  We add one extra +                                 * byte in case the last input +                                 * character is a backslash.  */    if (!out)      return 0; @@ -256,6 +296,8 @@ parse_string (cJSON * item, const char *str, const char **ep)        else  	{  	  ptr++; +          if (!*ptr) +            break;  	  switch (*ptr)  	    {  	    case 'b': @@ -275,17 +317,22 @@ parse_string (cJSON * item, const char *str, const char **ep)  	      break;  	    case 'u':		/* transcode utf16 to utf8. */  	      uc = parse_hex4 (ptr + 1); +              if (!uc) +                break;          /* Bad hex; continue right after the 'u'. */  	      ptr += 4;		/* get the unicode char. */ -	      if ((uc >= 0xDC00 && uc <= 0xDFFF) || uc == 0) +	      if ((uc >= 0xDC00 && uc <= 0xDFFF))  		break;		/* check for invalid.   */  	      if (uc >= 0xD800 && uc <= 0xDBFF)	/* UTF16 surrogate pairs. */  		{  		  if (ptr[1] != '\\' || ptr[2] != 'u')  		    break;	/* missing second-half of surrogate.    */ -		  uc2 = parse_hex4 (ptr + 3); -		  ptr += 6; +                  ptr += 2; +		  uc2 = parse_hex4 (ptr + 1); +                  if (!uc2) +                    break;      /* Bad hex; continue right after the 'u'. */ +		  ptr += 4;  		  if (uc2 < 0xDC00 || uc2 > 0xDFFF)  		    break;	/* invalid second-half of surrogate.    */  		  uc = 0x10000 + (((uc & 0x3FF) << 10) | (uc2 & 0x3FF)); @@ -317,6 +364,8 @@ parse_string (cJSON * item, const char *str, const char **ep)  	      ptr2 += len;  	      break;  	    default: +              /* Fixme: Should we fail here: See +               * https://github.com/DaveGamble/cJSON/issues/10  */  	      *ptr2++ = *ptr;  	      break;  	    } @@ -929,9 +978,11 @@ create_reference (cJSON * item)  void  cJSON_AddItemToArray (cJSON * array, cJSON * item)  { -  cJSON *c = array->child; -  if (!item) +  cJSON *c; + +  if (!item || !array)      return; +  c = array->child;    if (!c)      {        array->child = item; @@ -1132,6 +1183,8 @@ cJSON_ReplaceItemInObject (cJSON * object, const char *string,      i++, c = c->next;    if (c)      { +      /* FIXME: I guess we should free newitem->string here.  See +       * upstream commit 0d10e279c8b604f71829b5d49d092719f4ae96b6.  */        newitem->string = xtrystrdup (string);        cJSON_ReplaceItemInArray (object, i, newitem);      } @@ -1393,9 +1446,11 @@ cJSON_Minify (char *json)  	    {  	      if (*json == '\\')  		*into++ = *json++; -	      *into++ = *json++; +	      if (*json) +	        *into++ = *json++;  	    } -	  *into++ = *json++; +          if (*json) +            *into++ = *json++;  	}			/* String literals, which are \" sensitive.  */        else  	*into++ = *json++;	/* All other characters.  */ diff --git a/src/context.h b/src/context.h index c8e75ba0..1c9379b8 100644 --- a/src/context.h +++ b/src/context.h @@ -124,6 +124,10 @@ struct gpgme_context    /* Do not use the symmtric encryption passphrase cache.  */    unsigned int no_symkey_cache : 1; +  /* Pass --ignore-mdc-error to gpg.  Note that this flag is reset +   * after the operation.  */ +  unsigned int ignore_mdc_error : 1; +    /* Flags for keylist mode.  */    gpgme_keylist_mode_t keylist_mode; @@ -151,6 +155,9 @@ struct gpgme_context    /* The optional request origin.  */    char *request_origin; +  /* The optional auto key locate options.  */ +  char *auto_key_locate; +    /* The locale for the pinentry.  */    char *lc_ctype;    char *lc_messages; diff --git a/src/data-estream.c b/src/data-estream.c new file mode 100644 index 00000000..34f88a7f --- /dev/null +++ b/src/data-estream.c @@ -0,0 +1,99 @@ +/* data-stream.c - A stream based data object. + * Copyright (C) 2002, 2004, 2018 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/>. + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#if HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif + +#include "debug.h" +#include "data.h" + + +static gpgme_ssize_t +stream_es_read (gpgme_data_t dh, void *buffer, size_t size) +{ +  size_t amt = gpgrt_fread (buffer, 1, size, dh->data.e_stream); +  if (amt > 0) +    return amt; +  return gpgrt_ferror (dh->data.e_stream) ? -1 : 0; +} + + +static gpgme_ssize_t +stream_es_write (gpgme_data_t dh, const void *buffer, size_t size) +{ +  size_t amt = gpgrt_fwrite (buffer, 1, size, dh->data.e_stream); +  if (amt > 0) +    return amt; +  return gpgrt_ferror (dh->data.e_stream) ? -1 : 0; +} + + +static gpgme_off_t +stream_es_seek (gpgme_data_t dh, gpgme_off_t offset, int whence) +{ +  int err; + +  err = gpgrt_fseeko (dh->data.e_stream, offset, whence); +  if (err) +    return -1; + +  return gpgrt_ftello (dh->data.e_stream); +} + + +static int +stream_es_get_fd (gpgme_data_t dh) +{ +  gpgrt_fflush (dh->data.e_stream); +  return gpgrt_fileno (dh->data.e_stream); +} + + +static struct _gpgme_data_cbs stream_es_cbs = +  { +    stream_es_read, +    stream_es_write, +    stream_es_seek, +    NULL, +    stream_es_get_fd +  }; + + + +gpgme_error_t +gpgme_data_new_from_estream (gpgme_data_t *r_dh, gpgrt_stream_t stream) +{ +  gpgme_error_t err; +  TRACE_BEG1 (DEBUG_DATA, "gpgme_data_new_from_estream", r_dh, "estream=%p", +	      stream); + +  err = _gpgme_data_new (r_dh, &stream_es_cbs); +  if (err) +    return TRACE_ERR (err); + +  (*r_dh)->data.e_stream = stream; +  return TRACE_SUC1 ("dh=%p", *r_dh); +} diff --git a/src/data-mem.c b/src/data-mem.c index a498b826..7569f7df 100644 --- a/src/data-mem.c +++ b/src/data-mem.c @@ -224,7 +224,10 @@ gpgme_data_new_from_mem (gpgme_data_t *r_dh, const char *buffer,  char *  gpgme_data_release_and_get_mem (gpgme_data_t dh, size_t *r_len)  { +  gpg_error_t err;    char *str = NULL; +  size_t len; +  int blankout;    TRACE_BEG1 (DEBUG_DATA, "gpgme_data_release_and_get_mem", dh,  	      "r_len=%p", r_len); @@ -236,10 +239,22 @@ gpgme_data_release_and_get_mem (gpgme_data_t dh, size_t *r_len)        return NULL;      } +  err = _gpgme_data_get_prop (dh, 0, DATA_PROP_BLANKOUT, &blankout); +  if (err) +    { +      gpgme_data_release (dh); +      TRACE_ERR (err); +      return NULL; +    } +    str = dh->data.mem.buffer; +  len = dh->data.mem.length; +  if (blankout && len) +    len = 1; +    if (!str && dh->data.mem.orig_buffer)      { -      str = malloc (dh->data.mem.length); +      str = malloc (len);        if (!str)  	{  	  int saved_err = gpg_error_from_syserror (); @@ -247,15 +262,22 @@ gpgme_data_release_and_get_mem (gpgme_data_t dh, size_t *r_len)  	  TRACE_ERR (saved_err);  	  return NULL;  	} -      memcpy (str, dh->data.mem.orig_buffer, dh->data.mem.length); +      if (blankout) +        memset (str, 0, len); +      else +        memcpy (str, dh->data.mem.orig_buffer, len);      }    else -    /* Prevent mem_release from releasing the buffer memory.  We must -       not fail from this point.  */ -    dh->data.mem.buffer = NULL; +    { +      if (blankout && len) +        *str = 0; +      /* Prevent mem_release from releasing the buffer memory.  We +       * must not fail from this point.  */ +      dh->data.mem.buffer = NULL; +    }    if (r_len) -    *r_len = dh->data.mem.length; +    *r_len = len;    gpgme_data_release (dh); @@ -28,6 +28,7 @@  #endif  #include <errno.h>  #include <string.h> +#include <assert.h>  #include "gpgme.h"  #include "data.h" @@ -36,10 +37,271 @@  #include "priv-io.h"  #include "debug.h" + +/* The property table which has an entry for each active data object. + * The data object itself uses an index into this table and the table + * has a pointer back to the data object.  All access to that table is + * controlled by the property_table_lock. + * + * We use a separate table instead of linking all data objects + * together for faster locating properties of the data object using + * the data objects serial number.  We use 64 bit for the serial + * number which is good enough to create a new data object every + * nanosecond for more than 500 years.  Thus no wrap around will ever + * happen. + */ +struct property_s +{ +  gpgme_data_t dh;   /* The data objcet or NULL if the slot is not used.  */ +  uint64_t dserial;  /* The serial number of the data object.  */ +  struct { +    unsigned int blankout : 1;  /* Void the held data.  */ +  } flags; +}; +typedef struct property_s *property_t; + +static property_t property_table; +static unsigned int property_table_size; +DEFINE_STATIC_LOCK (property_table_lock); +#define PROPERTY_TABLE_ALLOCATION_CHUNK 32 + + + +/* Insert the newly created data object DH into the property table and + * store the index of it at R_IDX.  An error code is returned on error + * and the table is not changed.  */ +static gpg_error_t +insert_into_property_table (gpgme_data_t dh, unsigned int *r_idx) +{ +  static uint64_t last_dserial; +  gpg_error_t err; +  unsigned int idx; + +  LOCK (property_table_lock); +  if (!property_table) +    { +      property_table_size = PROPERTY_TABLE_ALLOCATION_CHUNK; +      property_table = calloc (property_table_size, sizeof *property_table); +      if (!property_table) +        { +          err = gpg_error_from_syserror (); +          goto leave; +        } +    } + +  /* Find an empty slot.  */ +  for (idx = 0; idx < property_table_size; idx++) +    if (!property_table[idx].dh) +      break; +  if (!(idx < property_table_size)) +    { +      /* No empty slot found.  Enlarge the table.  */ +      property_t newtbl; +      unsigned int newsize; + +      newsize = property_table_size + PROPERTY_TABLE_ALLOCATION_CHUNK;; +      if ((newsize * sizeof *property_table) +          < (property_table_size * sizeof *property_table)) +        { +          err = gpg_error (GPG_ERR_ENOMEM); +          goto leave; +        } +      newtbl = realloc (property_table, newsize * sizeof *property_table); +      if (!newtbl) +        { +          err = gpg_error_from_syserror (); +          goto leave; +        } +      property_table = newtbl; +      for (idx = property_table_size; idx < newsize; idx++) +        property_table[idx].dh = NULL; +      idx = property_table_size; +      property_table_size = newsize; +    } + +  /* Slot found. */ +  property_table[idx].dh = dh; +  property_table[idx].dserial = ++last_dserial; +  memset (&property_table[idx].flags, 0, sizeof property_table[idx].flags); +  *r_idx = idx; +  err = 0; + + leave: +  UNLOCK (property_table_lock); +  return err; +} + + +/* Remove the data object at PROPIDX from the table.  DH is only used + * for cross checking.  */ +static void +remove_from_property_table (gpgme_data_t dh, unsigned int propidx) +{ +  LOCK (property_table_lock); +  assert (property_table); +  assert (propidx < property_table_size); +  assert (property_table[propidx].dh == dh); +  property_table[propidx].dh = NULL; +  UNLOCK (property_table_lock); +} + + +/* Return the data object's serial number for handle DH.  This is a + * unique serial number for each created data object.  */ +uint64_t +_gpgme_data_get_dserial (gpgme_data_t dh) +{ +  uint64_t dserial; +  unsigned int idx; + +  if (!dh) +    return 0; + +  idx = dh->propidx; +  LOCK (property_table_lock); +  assert (property_table); +  assert (idx < property_table_size); +  assert (property_table[idx].dh == dh); +  dserial = property_table[idx].dserial; +  UNLOCK (property_table_lock); + +  return dserial; +} + + +/* Set an internal property of a data object.  The data object may + * either be identified by the usual DH or by using the data serial + * number DSERIAL.  */ +gpg_error_t +_gpgme_data_set_prop (gpgme_data_t dh, uint64_t dserial, +                      data_prop_t name, int value) +{ +  gpg_error_t err = 0; +  int idx; +  TRACE_BEG3 (DEBUG_DATA, "gpgme_data_set_prop", dh, +	      "dserial=%llu %lu=%d", +              (unsigned long long)dserial, +              (unsigned long)name, value); + +  LOCK (property_table_lock); +  if ((!dh && !dserial) || (dh && dserial)) +    { +      err = gpg_error (GPG_ERR_INV_VALUE); +      goto leave; +    } +  if (dh) /* Lookup via handle.  */ +    { +      idx = dh->propidx; +      assert (property_table); +      assert (idx < property_table_size); +      assert (property_table[idx].dh == dh); +    } +  else /* Lookup via DSERIAL.  */ +    { +      if (!property_table) +        { +          err = gpg_error (GPG_ERR_NOT_FOUND); +          goto leave; +        } +      for (idx = 0; idx < property_table_size; idx++) +        if (property_table[idx].dh && property_table[idx].dserial == dserial) +          break; +      if (!(idx < property_table_size)) +        { +          err = gpg_error (GPG_ERR_NOT_FOUND); +          goto leave; +        } +    } + +  switch (name) +    { +    case DATA_PROP_NONE: /* Nothing to to do.  */ +      break; +    case DATA_PROP_BLANKOUT: +      property_table[idx].flags.blankout = !!value; +      break; + +    default: +      err = gpg_error (GPG_ERR_UNKNOWN_NAME); +      break; +    } + + leave: +  UNLOCK (property_table_lock); +  return TRACE_ERR (err); +} + + +/* Get an internal property of a data object.  This is the counter + * part to _gpgme_data_set_property.  The value of the property is + * stored at R_VALUE.  On error 0 is stored at R_VALUE.  */ +gpg_error_t +_gpgme_data_get_prop (gpgme_data_t dh, uint64_t dserial, +                      data_prop_t name, int *r_value) +{ +  gpg_error_t err = 0; +  int idx; +  TRACE_BEG2 (DEBUG_DATA, "gpgme_data_get_prop", dh, +	      "dserial=%llu %lu", +              (unsigned long long)dserial, +              (unsigned long)name); + +  *r_value = 0; + +  LOCK (property_table_lock); +  if ((!dh && !dserial) || (dh && dserial)) +    { +      err = gpg_error (GPG_ERR_INV_VALUE); +      goto leave; +    } +  if (dh) /* Lookup via handle.  */ +    { +      idx = dh->propidx; +      assert (property_table); +      assert (idx < property_table_size); +      assert (property_table[idx].dh == dh); +    } +  else /* Lookup via DSERIAL.  */ +    { +      if (!property_table) +        { +          err = gpg_error (GPG_ERR_NOT_FOUND); +          goto leave; +        } +      for (idx = 0; idx < property_table_size; idx++) +        if (property_table[idx].dh && property_table[idx].dserial == dserial) +          break; +      if (!(idx < property_table_size)) +        { +          err = gpg_error (GPG_ERR_NOT_FOUND); +          goto leave; +        } +    } + +  switch (name) +    { +    case DATA_PROP_NONE: /* Nothing to to do.  */ +      break; +    case DATA_PROP_BLANKOUT: +      *r_value = property_table[idx].flags.blankout; +      break; + +    default: +      err = gpg_error (GPG_ERR_UNKNOWN_NAME); +      break; +    } + + leave: +  UNLOCK (property_table_lock); +  return TRACE_ERR (err); +} + +  gpgme_error_t  _gpgme_data_new (gpgme_data_t *r_dh, struct _gpgme_data_cbs *cbs)  { +  gpgme_error_t err;    gpgme_data_t dh;    if (!r_dh) @@ -56,6 +318,13 @@ _gpgme_data_new (gpgme_data_t *r_dh, struct _gpgme_data_cbs *cbs)    dh->cbs = cbs; +  err = insert_into_property_table (dh, &dh->propidx); +  if (err) +    { +      free (dh); +      return err; +    } +    *r_dh = dh;    return 0;  } @@ -67,11 +336,13 @@ _gpgme_data_release (gpgme_data_t dh)    if (!dh)      return; +  remove_from_property_table (dh, dh->propidx);    if (dh->file_name)      free (dh->file_name);    free (dh);  } +  /* Read up to SIZE bytes into buffer BUFFER from the data object with     the handle DH.  Return the number of characters read, 0 on EOF and @@ -80,6 +351,7 @@ gpgme_ssize_t  gpgme_data_read (gpgme_data_t dh, void *buffer, size_t size)  {    gpgme_ssize_t res; +  int blankout;    TRACE_BEG2 (DEBUG_DATA, "gpgme_data_read", dh,  	      "buffer=%p, size=%u", buffer, size); @@ -93,9 +365,16 @@ gpgme_data_read (gpgme_data_t dh, void *buffer, size_t size)        gpg_err_set_errno (ENOSYS);        return TRACE_SYSRES (-1);      } -  do -    res = (*dh->cbs->read) (dh, buffer, size); -  while (res < 0 && errno == EINTR); + +  if (_gpgme_data_get_prop (dh, 0, DATA_PROP_BLANKOUT, &blankout) +      || blankout) +    res = 0; +  else +    { +      do +        res = (*dh->cbs->read) (dh, buffer, size); +      while (res < 0 && errno == EINTR); +    }    return TRACE_SYSRES (res);  } @@ -29,6 +29,7 @@  # include <sys/types.h>  #endif  #include <limits.h> +#include <stdint.h>  #include "gpgme.h" @@ -73,6 +74,7 @@ struct gpgme_data  {    struct _gpgme_data_cbs *cbs;    gpgme_data_encoding_t encoding; +  unsigned int propidx;  /* Index into the property table.  */  #ifdef PIPE_BUF  #define BUFFER_SIZE PIPE_BUF @@ -89,7 +91,7 @@ struct gpgme_data    /* File name of the data object.  */    char *file_name; -  /* Hint on the to be expected toatl size of the data.  */ +  /* Hint on the to be expected total size of the data.  */    gpgme_off_t size_hint;    union @@ -100,6 +102,9 @@ struct gpgme_data      /* For gpgme_data_new_from_stream.  */      FILE *stream; +    /* For gpgme_data_new_from_estream.  */ +    gpgrt_stream_t e_stream; +      /* For gpgme_data_new_from_cbs.  */      struct      { @@ -127,7 +132,28 @@ struct gpgme_data    } data;  }; + +/* The data property types.  */ +typedef enum +  { +    DATA_PROP_NONE = 0,   /* Dummy property. */ +    DATA_PROP_BLANKOUT    /* Do not return the held data.  */ +  } data_prop_t; + + +/* Return the data object's serial number for handle DH.  */ +uint64_t _gpgme_data_get_dserial (gpgme_data_t dh); + +/* Set an internal property of a data object.  */ +gpg_error_t _gpgme_data_set_prop (gpgme_data_t dh, uint64_t dserial, +                                  data_prop_t name, int value); + +/* Get an internal property of a data object.  */ +gpg_error_t _gpgme_data_get_prop (gpgme_data_t dh, uint64_t dserial, +                                  data_prop_t name, int *r_value); + +/* Create a new data object.  */  gpgme_error_t _gpgme_data_new (gpgme_data_t *r_dh,  			       struct _gpgme_data_cbs *cbs); @@ -140,4 +166,5 @@ int _gpgme_data_get_fd (gpgme_data_t dh);  /* Get the size-hint value for DH or 0 if not available.  */  gpgme_off_t _gpgme_data_get_size_hint (gpgme_data_t dh); +  #endif	/* DATA_H */ diff --git a/src/decrypt-verify.c b/src/decrypt-verify.c index 17f79acd..224edc10 100644 --- a/src/decrypt-verify.c +++ b/src/decrypt-verify.c @@ -58,7 +58,7 @@ decrypt_verify_start (gpgme_ctx_t ctx, int synchronous,    if (err)      return err; -  err = _gpgme_op_decrypt_init_result (ctx); +  err = _gpgme_op_decrypt_init_result (ctx, plain);    if (err)      return err; @@ -74,7 +74,7 @@ decrypt_verify_start (gpgme_ctx_t ctx, int synchronous,    if (ctx->passphrase_cb)      {        err = _gpgme_engine_set_command_handler -	(ctx->engine, _gpgme_passphrase_command_handler, ctx, NULL); +	(ctx->engine, _gpgme_passphrase_command_handler, ctx);        if (err)  	return err;      } @@ -127,6 +127,7 @@ gpgme_op_decrypt_verify (gpgme_ctx_t ctx, gpgme_data_t cipher,    err = decrypt_verify_start (ctx, 1, GPGME_DECRYPT_VERIFY, cipher, plain);    if (!err)      err = _gpgme_wait_one (ctx); +  ctx->ignore_mdc_error = 0;  /* Always reset.  */    return TRACE_ERR (err);  } @@ -177,5 +178,6 @@ gpgme_op_decrypt_ext (gpgme_ctx_t ctx,      err = _gpgme_decrypt_start (ctx, 1, flags, cipher, plain);    if (!err)      err = _gpgme_wait_one (ctx); +  ctx->ignore_mdc_error = 0;  /* Always reset.  */    return TRACE_ERR (err);  } diff --git a/src/decrypt.c b/src/decrypt.c index ecd9c144..b51603a3 100644 --- a/src/decrypt.c +++ b/src/decrypt.c @@ -32,7 +32,7 @@  #include "util.h"  #include "context.h"  #include "ops.h" - +#include "data.h"  typedef struct @@ -53,18 +53,25 @@ typedef struct     * status lines for each key the message has been encrypted to but     * that secret key is not available.  This can't be done for hidden     * recipients, though.  We track it here to allow for a better error -   * message that the general DECRYPTION_FAILED. */ +   * message than the general DECRYPTION_FAILED. */    int any_no_seckey;    /* If the engine emits a DECRYPTION_INFO status and that does not -   * indicate that an integrity proetction mode is active, this flag +   * indicate that an integrity protection mode is active, this flag     * is set.  */    int not_integrity_protected; +  /* The error code from the first ERROR line.  This is in some cases +   * used to return a better matching error code to the caller.  */ +  gpg_error_t first_status_error; +    /* A pointer to the next pointer of the last recipient in the list.       This makes appending new invalid signers painless while       preserving the order.  */    gpgme_recipient_t *last_recipient_p; + +  /* The data object serial number of the plaintext.  */ +  uint64_t plaintext_dserial;  } *op_data_t; @@ -97,6 +104,8 @@ gpgme_op_decrypt_result (gpgme_ctx_t ctx)    TRACE_BEG (DEBUG_CTX, "gpgme_op_decrypt_result", ctx); +  ctx->ignore_mdc_error = 0;  /* Always reset this flag.  */ +    err = _gpgme_op_data_lookup (ctx, OPDATA_DECRYPT, &hook, -1, NULL);    opd = hook;    if (err || !opd) @@ -214,6 +223,15 @@ parse_status_error (char *args, op_data_t opd)            break;          }      } +  else if (!strcmp (field[0], "nomdc_with_legacy_cipher")) +    { +      opd->result.legacy_cipher_nomdc = 1; +      opd->not_integrity_protected = 1; +    } + +  /* Record the first error code.  */ +  if (err && !opd->first_status_error) +    opd->first_status_error = err;    free (args2); @@ -353,16 +371,43 @@ _gpgme_decrypt_status_handler (void *priv, gpgme_status_code_t code,         * only a warning.         * Fixme: These error values should probably be attributed to         * the underlying crypto engine (as error source).  */ -      if (opd->failed && opd->pkdecrypt_failed) -        return opd->pkdecrypt_failed; -      else if (opd->failed && opd->any_no_seckey) -	return gpg_error (GPG_ERR_NO_SECKEY); -      else if (opd->failed || opd->not_integrity_protected) -	return gpg_error (GPG_ERR_DECRYPT_FAILED); +      if (opd->failed) +        { +          /* This comes from a specialized ERROR status line.  */ +          if (opd->pkdecrypt_failed) +            return opd->pkdecrypt_failed; + +          /* For an integrity failure return just DECRYPTION_FAILED; +           * the actual cause can be taken from an already set +           * decryption result flag.  */ +          if ((opd->not_integrity_protected && !ctx->ignore_mdc_error)) +            return gpg_error (GPG_ERR_DECRYPT_FAILED); + +          /* If we have any other ERROR code we prefer that over +           * NO_SECKEY because it is probably the better matching +           * code.  For example a garbled message with multiple +           * plaintext will return BAD_DATA here but may also have +           * indicated a NO_SECKEY.  */ +          if (opd->first_status_error) +            return opd->first_status_error; + +          /* No secret key is pretty common reason.  */ +          if (opd->any_no_seckey) +            return gpg_error (GPG_ERR_NO_SECKEY); + +          /* Generic decryption failed error code.  */ +          return gpg_error (GPG_ERR_DECRYPT_FAILED); +        }        else if (!opd->okay) -	return gpg_error (GPG_ERR_NO_DATA); +        { +          /* No data was found.  */ +          return gpg_error (GPG_ERR_NO_DATA); +        }        else if (opd->failure_code) -        return opd->failure_code; +        { +          /* The engine returned failure code at program exit.  */ +          return opd->failure_code; +        }        break;      case GPGME_STATUS_DECRYPTION_INFO: @@ -377,12 +422,21 @@ _gpgme_decrypt_status_handler (void *priv, gpgme_status_code_t code,      case GPGME_STATUS_DECRYPTION_FAILED:        opd->failed = 1; +      /* Tell the data object that it shall not return any data.  We +       * use the serial number because the data object may be owned by +       * another thread.  We also don't check for an error because it +       * is possible that the data object has already been destroyed +       * and we are then not interested in returning an error.  */ +      if (!ctx->ignore_mdc_error) +        _gpgme_data_set_prop (NULL, opd->plaintext_dserial, +                              DATA_PROP_BLANKOUT, 1);        break;      case GPGME_STATUS_ERROR:        /* Note that this is an informational status code which should -         not lead to an error return unless it is something not -         related to the backend.  */ +       * not lead to an error return unless it is something not +       * related to the backend.  However, it is used to return a +       * better matching final error code.  */        err = parse_status_error (args, opd);        if (err)          return err; @@ -465,7 +519,7 @@ decrypt_status_handler (void *priv, gpgme_status_code_t code, char *args)  gpgme_error_t -_gpgme_op_decrypt_init_result (gpgme_ctx_t ctx) +_gpgme_op_decrypt_init_result (gpgme_ctx_t ctx, gpgme_data_t plaintext)  {    gpgme_error_t err;    void *hook; @@ -478,6 +532,7 @@ _gpgme_op_decrypt_init_result (gpgme_ctx_t ctx)      return err;    opd->last_recipient_p = &opd->result.recipients; +  opd->plaintext_dserial = _gpgme_data_get_dserial (plaintext);    return 0;  } @@ -495,7 +550,7 @@ _gpgme_decrypt_start (gpgme_ctx_t ctx, int synchronous,    if (err)      return err; -  err = _gpgme_op_decrypt_init_result (ctx); +  err = _gpgme_op_decrypt_init_result (ctx, plain);    if (err)      return err; @@ -510,7 +565,7 @@ _gpgme_decrypt_start (gpgme_ctx_t ctx, int synchronous,    if (ctx->passphrase_cb)      {        err = _gpgme_engine_set_command_handler -	(ctx->engine, _gpgme_passphrase_command_handler, ctx, NULL); +	(ctx->engine, _gpgme_passphrase_command_handler, ctx);        if (err)  	return err;      } @@ -559,5 +614,6 @@ gpgme_op_decrypt (gpgme_ctx_t ctx, gpgme_data_t cipher, gpgme_data_t plain)    err = _gpgme_decrypt_start (ctx, 1, 0, cipher, plain);    if (!err)      err = _gpgme_wait_one (ctx); +  ctx->ignore_mdc_error = 0;  /* Always reset.  */    return TRACE_ERR (err);  } @@ -139,8 +139,7 @@ interact_start (gpgme_ctx_t ctx, int synchronous, gpgme_key_t key,    opd->fnc_old = NULL;    opd->fnc_value = fnc_value; -  err = _gpgme_engine_set_command_handler (ctx->engine, command_handler, -					   ctx, out); +  err = _gpgme_engine_set_command_handler (ctx->engine, command_handler, ctx);    if (err)      return err; @@ -219,8 +218,7 @@ edit_start (gpgme_ctx_t ctx, int synchronous, int type, gpgme_key_t key,    opd->fnc_old = fnc;    opd->fnc_value = fnc_value; -  err = _gpgme_engine_set_command_handler (ctx->engine, command_handler, -					   ctx, out); +  err = _gpgme_engine_set_command_handler (ctx->engine, command_handler, ctx);    if (err)      return err; diff --git a/src/encrypt-sign.c b/src/encrypt-sign.c index 4db46e25..cc34fbd5 100644 --- a/src/encrypt-sign.c +++ b/src/encrypt-sign.c @@ -93,7 +93,7 @@ encrypt_sign_start (gpgme_ctx_t ctx, int synchronous, gpgme_key_t recp[],    if (ctx->passphrase_cb)      {        err = _gpgme_engine_set_command_handler -	(ctx->engine, _gpgme_passphrase_command_handler, ctx, NULL); +	(ctx->engine, _gpgme_passphrase_command_handler, ctx);        if (err)  	return err;      } diff --git a/src/encrypt.c b/src/encrypt.c index 2318497e..a27a53ac 100644 --- a/src/encrypt.c +++ b/src/encrypt.c @@ -242,7 +242,7 @@ encrypt_start (gpgme_ctx_t ctx, int synchronous, gpgme_key_t recp[],      {        /* Symmetric encryption requires a passphrase.  */        err = _gpgme_engine_set_command_handler -	(ctx->engine, _gpgme_passphrase_command_handler, ctx, NULL); +	(ctx->engine, _gpgme_passphrase_command_handler, ctx);        if (err)  	return err;      } diff --git a/src/engine-backend.h b/src/engine-backend.h index f6926662..4f33da1c 100644 --- a/src/engine-backend.h +++ b/src/engine-backend.h @@ -55,7 +55,7 @@ struct engine_ops  			      void *fnc_value);    gpgme_error_t (*set_command_handler) (void *engine,  					engine_command_handler_t fnc, -					void *fnc_value, gpgme_data_t data); +					void *fnc_value);    gpgme_error_t (*set_colon_line_handler) (void *engine,  					   engine_colon_line_handler_t fnc,  					   void *fnc_value); diff --git a/src/engine-gpg.c b/src/engine-gpg.c index 173e940c..be78957f 100644 --- a/src/engine-gpg.c +++ b/src/engine-gpg.c @@ -26,7 +26,6 @@  #include <stdlib.h>  #include <string.h>  #include <assert.h> -#include <errno.h>  #ifdef HAVE_UNISTD_H  # include <unistd.h>  #endif @@ -136,23 +135,24 @@ struct engine_gpg      char *keyword;       /* what has been requested (malloced) */      engine_command_handler_t fnc;      void *fnc_value; -    /* The kludges never end.  This is used to couple command handlers -       with output data in edit key mode.  */ -    gpgme_data_t linked_data; -    int linked_idx;    } cmd;    struct gpgme_io_cbs io_cbs;    gpgme_pinentry_mode_t pinentry_mode;    char request_origin[10]; +  char *auto_key_locate;    struct {      unsigned int no_symkey_cache : 1;      unsigned int offline : 1; +    unsigned int ignore_mdc_error : 1;    } flags;    /* NULL or the data object fed to --override_session_key-fd.  */    gpgme_data_t override_session_key; + +  /* Memory data containing diagnostics (--logger-fd) of gpg */ +  gpgme_data_t diagnostics;  };  typedef struct engine_gpg *engine_gpg_t; @@ -454,8 +454,10 @@ gpg_release (void *engine)      free_argv (gpg->argv);    if (gpg->cmd.keyword)      free (gpg->cmd.keyword); +  free (gpg->auto_key_locate);    gpgme_data_release (gpg->override_session_key); +  gpgme_data_release (gpg->diagnostics);    free (gpg);  } @@ -503,8 +505,6 @@ gpg_new (void **engine, const char *file_name, const char *home_dir,    gpg->colon.fd[1] = -1;    gpg->cmd.fd = -1;    gpg->cmd.idx = -1; -  gpg->cmd.linked_data = NULL; -  gpg->cmd.linked_idx = -1;    /* Allocate the read buffer for the status pipe.  */    gpg->status.bufsize = 1024; @@ -626,6 +626,16 @@ gpg_new (void **engine, const char *file_name, const char *home_dir,  	}      } +  rc = gpgme_data_new (&gpg->diagnostics); +  if (rc) +    goto leave; + +  rc = add_arg (gpg, "--logger-fd"); +  if (rc) +    goto leave; + +  rc = add_data (gpg, gpg->diagnostics, -2, 1); +   leave:    if (rc)      gpg_release (gpg); @@ -651,11 +661,20 @@ gpg_set_engine_flags (void *engine, const gpgme_ctx_t ctx)    else      *gpg->request_origin = 0; +  if (ctx->auto_key_locate && have_gpg_version (gpg, "2.1.18")) +    { +      if (gpg->auto_key_locate) +        free (gpg->auto_key_locate); +      gpg->auto_key_locate = _gpgme_strconcat ("--auto-key-locate=", +                                               ctx->auto_key_locate, NULL); +    } +    gpg->flags.no_symkey_cache = (ctx->no_symkey_cache                                  && have_gpg_version (gpg, "2.2.7")); -    gpg->flags.offline = (ctx->offline && have_gpg_version (gpg, "2.1.23")); +  gpg->flags.ignore_mdc_error = !!ctx->ignore_mdc_error; +  } @@ -793,14 +812,14 @@ command_handler (void *opaque, int fd) -/* The Fnc will be called to get a value for one of the commands with -   a key KEY.  If the Code passed to FNC is 0, the function may release -   resources associated with the returned value from another call.  To -   match such a second call to a first call, the returned value from -   the first call is passed as keyword.  */ +/* The FNC will be called to get a value for one of the commands with + * a key KEY.  If the code passed to FNC is 0, the function may + * release resources associated with the returned value from another + * call.  To match such a second call to a first call, the returned + * value from the first call is passed as keyword.  */  static gpgme_error_t  gpg_set_command_handler (void *engine, engine_command_handler_t fnc, -			 void *fnc_value, gpgme_data_t linked_data) +			 void *fnc_value)  {    engine_gpg_t gpg = engine;    gpgme_error_t rc; @@ -819,7 +838,6 @@ gpg_set_command_handler (void *engine, engine_command_handler_t fnc,    gpg->cmd.fnc = fnc;    gpg->cmd.cb_data = (void *) &gpg->cmd;    gpg->cmd.fnc_value = fnc_value; -  gpg->cmd.linked_data = linked_data;    gpg->cmd.used = 1;    return 0;  } @@ -950,6 +968,19 @@ build_argv (engine_gpg_t gpg, const char *pgmname)        argc++;      } +  if (gpg->auto_key_locate) +    { +      argv[argc] = strdup (gpg->auto_key_locate); +      if (!argv[argc]) +        { +          int saved_err = gpg_error_from_syserror (); +          free (fd_data_map); +          free_argv (argv); +          return saved_err; +        } +      argc++; +    } +    if (gpg->flags.no_symkey_cache)      {        argv[argc] = strdup ("--no-symkey-cache"); @@ -963,6 +994,19 @@ build_argv (engine_gpg_t gpg, const char *pgmname)        argc++;      } +  if (gpg->flags.ignore_mdc_error) +    { +      argv[argc] = strdup ("--ignore-mdc-error"); +      if (!argv[argc]) +	{ +          int saved_err = gpg_error_from_syserror (); +	  free (fd_data_map); +	  free_argv (argv); +	  return saved_err; +        } +      argc++; +    } +    if (gpg->flags.offline)      {        argv[argc] = strdup ("--disable-dirmngr"); @@ -1039,10 +1083,10 @@ build_argv (engine_gpg_t gpg, const char *pgmname)  	    if (_gpgme_io_pipe (fds, fd_data_map[datac].inbound ? 1 : 0)  		== -1)  	      { -		int saved_errno = errno; +		int saved_err = gpg_error_from_syserror ();  		free (fd_data_map);  		free_argv (argv); -		return gpg_error (saved_errno); +		return saved_err;  	      }  	    if (_gpgme_io_set_close_notify (fds[0],  					    close_notify_handler, gpg) @@ -1077,11 +1121,6 @@ build_argv (engine_gpg_t gpg, const char *pgmname)  		  assert (gpg->cmd.idx == -1);  		  gpg->cmd.idx = datac;  		} -	      else if (gpg->cmd.linked_data == a->data) -		{ -		  assert (gpg->cmd.linked_idx == -1); -		  gpg->cmd.linked_idx = datac; -		}  	    }  	  fd_data_map[datac].data = a->data; @@ -1268,44 +1307,6 @@ read_status (engine_gpg_t gpg)  			  if (err)  			    return err;                          } - -		      if (r == GPGME_STATUS_END_STREAM) -			{ -			  if (gpg->cmd.used) -			    { -			      /* Before we can actually add the -				 command fd, we might have to flush -				 the linked output data pipe.  */ -			      if (gpg->cmd.linked_idx != -1 -				  && gpg->fd_data_map[gpg->cmd.linked_idx].fd -				  != -1) -				{ -				  struct io_select_fd_s fds; -				  fds.fd = -				    gpg->fd_data_map[gpg->cmd.linked_idx].fd; -				  fds.for_read = 1; -				  fds.for_write = 0; -				  fds.opaque = NULL; -				  do -				    { -				      fds.signaled = 0; -				      _gpgme_io_select (&fds, 1, 1); -				      if (fds.signaled) -					_gpgme_data_inbound_handler -					  (gpg->cmd.linked_data, fds.fd); -				    } -				  while (fds.signaled); -				} - -			      /* XXX We must check if there are any -				 more fds active after removing this -				 one.  */ -			      (*gpg->io_cbs.remove) -				(gpg->fd_data_map[gpg->cmd.idx].tag); -			      gpg->cmd.fd = gpg->fd_data_map[gpg->cmd.idx].fd; -			      gpg->fd_data_map[gpg->cmd.idx].fd = -1; -			    } -                        }                      }                  }  	      /* To reuse the buffer for the next line we have to @@ -3279,6 +3280,52 @@ gpg_set_pinentry_mode (void *engine, gpgme_pinentry_mode_t mode)  } +static gpgme_error_t +gpg_getauditlog (void *engine, gpgme_data_t output, unsigned int flags) +{ +  engine_gpg_t gpg = engine; +#define MYBUFLEN 4096 +  char buf[MYBUFLEN]; +  int nread; +  int any_written = 0; + +  if (!(flags & GPGME_AUDITLOG_DIAG)) +    { +      return gpg_error (GPG_ERR_NOT_IMPLEMENTED); +    } + +  if (!gpg || !output) +    { +      return gpg_error (GPG_ERR_INV_VALUE); +    } + +  if (!gpg->diagnostics) +    { +      return gpg_error (GPG_ERR_GENERAL); +    } + +  gpgme_data_rewind (gpg->diagnostics); + +  while ((nread = gpgme_data_read (gpg->diagnostics, buf, MYBUFLEN)) > 0) +    { +      any_written = 1; +      if (gpgme_data_write (output, buf, nread) == -1) +        return gpg_error_from_syserror (); +    } +  if (!any_written) +    { +      return gpg_error (GPG_ERR_NO_DATA); +    } + +  if (nread == -1) +    return gpg_error_from_syserror (); + +  gpgme_data_rewind (output); +  return 0; +#undef MYBUFLEN +} + +  struct engine_ops _gpgme_engine_ops_gpg =    { @@ -3316,7 +3363,7 @@ struct engine_ops _gpgme_engine_ops_gpg =      gpg_sign,      gpg_trustlist,      gpg_verify, -    NULL,		/* getauditlog */ +    gpg_getauditlog,      NULL,               /* opassuan_transact */      NULL,		/* conf_load */      NULL,		/* conf_save */ diff --git a/src/engine-gpgsm.c b/src/engine-gpgsm.c index 7b221831..3266e360 100644 --- a/src/engine-gpgsm.c +++ b/src/engine-gpgsm.c @@ -37,7 +37,6 @@  #include <locale.h>  #endif  #include <fcntl.h> /* FIXME */ -#include <errno.h>  #include "gpgme.h"  #include "util.h" @@ -986,8 +985,7 @@ status_handler (void *opaque, int fd)            while (linelen > 0)              {                nwritten = gpgme_data_write (gpgsm->inline_data, src, linelen); -              if (!nwritten || (nwritten < 0 && errno != EINTR) -                  || nwritten > linelen) +              if (nwritten <= 0 || nwritten > linelen)                  {                    err = gpg_error_from_syserror ();                    break; @@ -2066,6 +2064,9 @@ gpgsm_getauditlog (void *engine, gpgme_data_t output, unsigned int flags)    if (!gpgsm || !output)      return gpg_error (GPG_ERR_INV_VALUE); +  if ((flags & GPGME_AUDITLOG_DIAG)) +    return gpg_error (GPG_ERR_NOT_IMPLEMENTED); +  #if USE_DESCRIPTOR_PASSING    gpgsm->output_cb.data = output;    err = gpgsm_set_fd (gpgsm, OUTPUT_FD, 0); diff --git a/src/engine.c b/src/engine.c index b716ca24..b629bea7 100644 --- a/src/engine.c +++ b/src/engine.c @@ -596,8 +596,7 @@ _gpgme_engine_set_status_handler (engine_t engine,  gpgme_error_t  _gpgme_engine_set_command_handler (engine_t engine,  				   engine_command_handler_t fnc, -				   void *fnc_value, -				   gpgme_data_t linked_data) +				   void *fnc_value)  {    if (!engine)      return gpg_error (GPG_ERR_INV_VALUE); @@ -605,8 +604,7 @@ _gpgme_engine_set_command_handler (engine_t engine,    if (!engine->ops->set_command_handler)      return gpg_error (GPG_ERR_NOT_IMPLEMENTED); -  return (*engine->ops->set_command_handler) (engine->engine, -					      fnc, fnc_value, linked_data); +  return (*engine->ops->set_command_handler) (engine->engine, fnc, fnc_value);  }  gpgme_error_t diff --git a/src/engine.h b/src/engine.h index 8b692f2e..c512a252 100644 --- a/src/engine.h +++ b/src/engine.h @@ -78,8 +78,7 @@ void _gpgme_engine_set_status_handler (engine_t engine,  				       void *fnc_value);  gpgme_error_t _gpgme_engine_set_command_handler (engine_t engine,  						 engine_command_handler_t fnc, -						 void *fnc_value, -						 gpgme_data_t data); +						 void *fnc_value);  gpgme_error_t  _gpgme_engine_set_colon_line_handler (engine_t engine,  				      engine_colon_line_handler_t fnc, diff --git a/src/genkey.c b/src/genkey.c index 16484ecc..ffca7e8e 100644 --- a/src/genkey.c +++ b/src/genkey.c @@ -259,7 +259,7 @@ genkey_start (gpgme_ctx_t ctx, int synchronous, const char *parms,    if (ctx->passphrase_cb)      {        err = _gpgme_engine_set_command_handler -        (ctx->engine, _gpgme_passphrase_command_handler, ctx, NULL); +        (ctx->engine, _gpgme_passphrase_command_handler, ctx);        if (err)          return err;      } @@ -345,7 +345,7 @@ createkey_start (gpgme_ctx_t ctx, int synchronous,    if (ctx->passphrase_cb)      {        err = _gpgme_engine_set_command_handler -        (ctx->engine, _gpgme_passphrase_command_handler, ctx, NULL); +        (ctx->engine, _gpgme_passphrase_command_handler, ctx);        if (err)          return err;      } @@ -433,7 +433,7 @@ createsubkey_start (gpgme_ctx_t ctx, int synchronous,    if (ctx->passphrase_cb)      {        err = _gpgme_engine_set_command_handler -        (ctx->engine, _gpgme_passphrase_command_handler, ctx, NULL); +        (ctx->engine, _gpgme_passphrase_command_handler, ctx);        if (err)          return err;      } @@ -519,7 +519,7 @@ addrevuid_start (gpgme_ctx_t ctx, int synchronous, int extraflags,    if (ctx->passphrase_cb)      {        err = _gpgme_engine_set_command_handler -        (ctx->engine, _gpgme_passphrase_command_handler, ctx, NULL); +        (ctx->engine, _gpgme_passphrase_command_handler, ctx);        if (err)          return err;      } diff --git a/src/getauditlog.c b/src/getauditlog.c index dbaf260e..d70e66fd 100644 --- a/src/getauditlog.c +++ b/src/getauditlog.c @@ -47,9 +47,12 @@ getauditlog_start (gpgme_ctx_t ctx, int synchronous,    if (!output)      return gpg_error (GPG_ERR_INV_VALUE); -  err = _gpgme_op_reset (ctx, ((synchronous&255) | 256) ); -  if (err) -    return err; +  if (!(flags & GPGME_AUDITLOG_DIAG)) +    { +      err = _gpgme_op_reset (ctx, ((synchronous&255) | 256) ); +      if (err) +        return err; +    }    _gpgme_engine_set_status_handler (ctx->engine,                                      getauditlog_status_handler, ctx); diff --git a/src/gpgme-json.c b/src/gpgme-json.c index a755500d..d636ddbe 100644 --- a/src/gpgme-json.c +++ b/src/gpgme-json.c @@ -48,24 +48,25 @@ int main (void){fputs ("Build with Libgpg-error >= 1.28!\n", stderr);return 1;}  /* We don't allow a request with more than 64 MiB.  */  #define MAX_REQUEST_SIZE (64 * 1024 * 1024) -/* Minimal, default and maximum chunk size for returned data. The - * first chunk is returned directly.  If the "more" flag is also - * returned, a "getmore" command needs to be used to get the next - * chunk.  Right now this value covers just the value of the "data" - * element; so to cover for the other returned objects this values - * needs to be lower than the maximum allowed size of the browser. */ -#define MIN_REPLY_CHUNK_SIZE  512 -#define DEF_REPLY_CHUNK_SIZE (512 * 1024) +/* Minimal chunk size for returned data.*/ +#define MIN_REPLY_CHUNK_SIZE  30 + +/* If no chunksize is provided we print everything.  Changing + * this to a positive value will result in all messages beeing + * chunked. */ +#define DEF_REPLY_CHUNK_SIZE  0  #define MAX_REPLY_CHUNK_SIZE (10 * 1024 * 1024)  static void xoutofcore (const char *type) GPGRT_ATTR_NORETURN;  static cjson_t error_object_v (cjson_t json, const char *message, -                              va_list arg_ptr) GPGRT_ATTR_PRINTF(2,0); +                               va_list arg_ptr, gpg_error_t err) +                               GPGRT_ATTR_PRINTF(2,0);  static cjson_t error_object (cjson_t json, const char *message,                              ...) GPGRT_ATTR_PRINTF(2,3);  static char *error_object_string (const char *message,                                    ...) GPGRT_ATTR_PRINTF(1,2); +static char *process_request (const char *request);  /* True if interactive mode is active.  */ @@ -79,8 +80,6 @@ static struct    char  *buffer;   /* Malloced data or NULL if not used.  */    size_t length;   /* Length of that data.  */    size_t written;  /* # of already written bytes from BUFFER.  */ -  const char *type;/* The "type" of the data.  */ -  int base64;      /* The "base64" flag of the data.  */  } pending_data; @@ -88,13 +87,7 @@ static struct   * Helper functions and macros   */ -#define xtrymalloc(a)  gpgrt_malloc ((a))  #define xtrystrdup(a)  gpgrt_strdup ((a)) -#define xmalloc(a) ({                           \ -      void *_r = gpgrt_malloc ((a));            \ -      if (!_r)                                  \ -        xoutofcore ("malloc");                  \ -      _r; })  #define xcalloc(a,b) ({                         \        void *_r = gpgrt_calloc ((a), (b));       \        if (!_r)                                  \ @@ -112,6 +105,21 @@ static struct        _r; })  #define xfree(a) gpgrt_free ((a)) +/* Only use calloc. */ +#define CALLOC_ONLY 1 + +#if CALLOC_ONLY +#define xtrymalloc(a)  gpgrt_calloc (1, (a)) +#define xmalloc(a) xcalloc(1, (a)) +#else +#define xtrymalloc(a)  gpgrt_malloc ((a)) +#define xmalloc(a) ({                           \ +      void *_r = gpgrt_malloc ((a));            \ +      if (!_r)                                  \ +        xoutofcore ("malloc");                  \ +      _r; }) +#endif +  #define spacep(p)   (*(p) == ' ' || *(p) == '\t')  #ifndef HAVE_STPCPY @@ -127,6 +135,19 @@ _my_stpcpy (char *a, const char *b)  #endif /*!HAVE_STPCPY*/ +/* Free a NULL terminated array */ +static void +xfree_array (char **array) +{ +  if (array) +    { +      int idx; +      for (idx = 0; array[idx]; idx++) +        xfree (array[idx]); +      xfree (array); +    } +} +  static void  xoutofcore (const char *type) @@ -179,6 +200,15 @@ xjson_AddStringToObject (cjson_t object, const char *name, const char *string)  } +/* Same as xjson_AddStringToObject but ignores NULL strings */ +static void +xjson_AddStringToObject0 (cjson_t object, const char *name, const char *string) +{ +  if (!string) +    return; +  xjson_AddStringToObject (object, name, string); +} +  /* Wrapper around cJSON_AddBoolToObject which terminates the process   * in case of an error.  */  static void @@ -189,6 +219,26 @@ xjson_AddBoolToObject (cjson_t object, const char *name, int abool)    return ;  } +/* Wrapper around cJSON_AddNumberToObject which terminates the process + * in case of an error.  */ +static void +xjson_AddNumberToObject (cjson_t object, const char *name, double dbl) +{ +  if (!cJSON_AddNumberToObject (object, name, dbl)) +    xoutofcore ("cJSON_AddNumberToObject"); +  return ; +} + +/* Wrapper around cJSON_AddItemToObject which terminates the process + * in case of an error.  */ +static void +xjson_AddItemToObject (cjson_t object, const char *name, cjson_t item) +{ +  if (!cJSON_AddItemToObject (object, name, item)) +    xoutofcore ("cJSON_AddItemToObject"); +  return ; +} +  /* This is similar to cJSON_AddStringToObject but takes (DATA,   * DATALEN) and adds it under NAME as a base 64 encoded string to   * OBJECT.  */ @@ -266,7 +316,8 @@ add_base64_to_object (cjson_t object, const char *name,  /* Create a JSON error object.  If JSON is not NULL the error message   * is appended to that object.  An existing "type" item will be replaced. */  static cjson_t -error_object_v (cjson_t json, const char *message, va_list arg_ptr) +error_object_v (cjson_t json, const char *message, va_list arg_ptr, +                gpg_error_t err)  {    cjson_t response, j_tmp;    char *msg; @@ -287,8 +338,10 @@ error_object_v (cjson_t json, const char *message, va_list arg_ptr)        cJSON_ReplaceItemInObject (response, "type", j_tmp);       }    xjson_AddStringToObject (response, "msg", msg); -    xfree (msg); + +  xjson_AddNumberToObject (response, "code", err); +    return response;  } @@ -312,7 +365,20 @@ error_object (cjson_t json, const char *message, ...)    va_list arg_ptr;    va_start (arg_ptr, message); -  response = error_object_v (json, message, arg_ptr); +  response = error_object_v (json, message, arg_ptr, 0); +  va_end (arg_ptr); +  return response; +} + + +static cjson_t +gpg_error_object (cjson_t json, gpg_error_t err, const char *message, ...) +{ +  cjson_t response; +  va_list arg_ptr; + +  va_start (arg_ptr, message); +  response = error_object_v (json, message, arg_ptr, err);    va_end (arg_ptr);    return response;  } @@ -326,7 +392,7 @@ error_object_string (const char *message, ...)    char *msg;    va_start (arg_ptr, message); -  response = error_object_v (NULL, message, arg_ptr); +  response = error_object_v (NULL, message, arg_ptr, 0);    va_end (arg_ptr);    msg = xjson_Print (response); @@ -408,12 +474,13 @@ get_chunksize (cjson_t json, size_t *r_chunksize)  } -/* Extract the keys from the "keys" array in the JSON object.  On - * success a string with the keys identifiers is stored at R_KEYS. +/* Extract the keys from the array or string with the name "name" + * in the JSON object.  On success a string with the keys identifiers + * is stored at R_KEYS.   * The keys in that string are LF delimited.  On failure an error code   * is returned.  */  static gpg_error_t -get_keys (cjson_t json, char **r_keystring) +get_keys (cjson_t json, const char *name, char **r_keystring)  {    cjson_t j_keys, j_item;    int i, nkeys; @@ -422,7 +489,7 @@ get_keys (cjson_t json, char **r_keystring)    *r_keystring = NULL; -  j_keys = cJSON_GetObjectItem (json, "keys"); +  j_keys = cJSON_GetObjectItem (json, name);    if (!j_keys)      return gpg_error (GPG_ERR_NO_KEY);    if (!cjson_is_array (j_keys) && !cjson_is_string (j_keys)) @@ -505,7 +572,7 @@ _create_new_context (gpgme_protocol_t proto)  static gpgme_ctx_t  get_context (gpgme_protocol_t proto)  { -  static gpgme_ctx_t ctx_openpgp, ctx_cms; +  static gpgme_ctx_t ctx_openpgp, ctx_cms, ctx_conf;    if (proto == GPGME_PROTOCOL_OpenPGP)      { @@ -519,12 +586,17 @@ get_context (gpgme_protocol_t proto)          ctx_cms = _create_new_context (proto);        return ctx_cms;      } +  else if (proto == GPGME_PROTOCOL_GPGCONF) +    { +      if (!ctx_conf) +        ctx_conf = _create_new_context (proto); +      return ctx_conf; +    }    else      log_bug ("invalid protocol %d requested\n", proto);  } -  /* Free context object retrieved by get_context.  */  static void  release_context (gpgme_ctx_t ctx) @@ -534,6 +606,23 @@ release_context (gpgme_ctx_t ctx)  } +/* Create an addition context for short operations. */ +static gpgme_ctx_t +create_onetime_context (gpgme_protocol_t proto) +{ +  return _create_new_context (proto); + +} + + +/* Release a one-time context.  */ +static void +release_onetime_context (gpgme_ctx_t ctx) +{ +  return gpgme_release (ctx); + +} +  /* Given a Base-64 encoded string object in JSON return a gpgme data   * object at R_DATA.  */ @@ -600,46 +689,161 @@ data_from_base64_string (gpgme_data_t *r_data, cjson_t json)  } -/* Helper for summary formatting */ -static void -add_summary_to_object (cjson_t result, gpgme_sigsum_t summary) +/* Create a keylist pattern array from a json keys object + * in the request. Returns either a malloced NULL terminated + * string array which can be used as patterns for + * op_keylist_ext or NULL. */ +static char ** +create_keylist_patterns (cjson_t request, const char *name) +{ +  char *keystring; +  char *p; +  char *tmp; +  char **ret; +  int cnt = 2; /* Last NULL and one is not newline delimited */ +  int i = 0; + +  if (get_keys (request, name, &keystring)) +    return NULL; + +  for (p = keystring; *p; p++) +    if (*p == '\n') +      cnt++; + +  ret = xcalloc (cnt, sizeof *ret); + +  for (p = keystring, tmp = keystring; *p; p++) +    { +      if (*p != '\n') +        continue; +      *p = '\0'; +      ret[i++] = xstrdup (tmp); +      tmp = p + 1; +    } +  /* The last key is not newline delimted. */ +  ret[i] = *tmp ? xstrdup (tmp) : NULL; + +  xfree (keystring); +  return ret; +} + + +/* Do a secret keylisting for protocol proto and add the fingerprints of +   the secret keys for patterns to the result as "sec-fprs" array. */ +static gpg_error_t +add_secret_fprs (const char **patterns, gpgme_protocol_t protocol, +                 cjson_t result) +{ +  gpgme_ctx_t ctx; +  gpg_error_t err; +  gpgme_key_t key = NULL; +  cjson_t j_fprs = xjson_CreateArray (); + +  ctx = create_onetime_context (protocol); + +  gpgme_set_keylist_mode (ctx, GPGME_KEYLIST_MODE_LOCAL | +                               GPGME_KEYLIST_MODE_WITH_SECRET); + +  err = gpgme_op_keylist_ext_start (ctx, patterns, 1, 0); + +  if (err) +    { +      gpg_error_object (result, err, "Error listing keys: %s", +                        gpg_strerror (err)); +      goto leave; +    } + +  while (!(err = gpgme_op_keylist_next (ctx, &key))) +    { +      if (!key || !key->fpr) +        continue; +      cJSON_AddItemToArray (j_fprs, cJSON_CreateString (key->fpr)); +      gpgme_key_unref (key); +      key = NULL; +    } +  err = 0; + +  release_onetime_context (ctx); +  ctx = NULL; + +  xjson_AddItemToObject (result, "sec-fprs", j_fprs); + +leave: +  release_onetime_context (ctx); +  gpgme_key_unref (key); + +  return err; +} + + +/* Create sigsum json array */ +static cjson_t +sigsum_to_json (gpgme_sigsum_t summary)  { -  cjson_t response = xjson_CreateArray (); +  cjson_t result = xjson_CreateObject (); +  cjson_t sigsum_array = xjson_CreateArray (); +    if ( (summary & GPGME_SIGSUM_VALID      )) -    cJSON_AddItemToArray (response, +    cJSON_AddItemToArray (sigsum_array,          cJSON_CreateString ("valid"));    if ( (summary & GPGME_SIGSUM_GREEN      )) -    cJSON_AddItemToArray (response, +    cJSON_AddItemToArray (sigsum_array,          cJSON_CreateString ("green"));    if ( (summary & GPGME_SIGSUM_RED        )) -    cJSON_AddItemToArray (response, +    cJSON_AddItemToArray (sigsum_array,          cJSON_CreateString ("red"));    if ( (summary & GPGME_SIGSUM_KEY_REVOKED)) -    cJSON_AddItemToArray (response, +    cJSON_AddItemToArray (sigsum_array,          cJSON_CreateString ("revoked"));    if ( (summary & GPGME_SIGSUM_KEY_EXPIRED)) -    cJSON_AddItemToArray (response, +    cJSON_AddItemToArray (sigsum_array,          cJSON_CreateString ("key-expired"));    if ( (summary & GPGME_SIGSUM_SIG_EXPIRED)) -    cJSON_AddItemToArray (response, +    cJSON_AddItemToArray (sigsum_array,          cJSON_CreateString ("sig-expired"));    if ( (summary & GPGME_SIGSUM_KEY_MISSING)) -    cJSON_AddItemToArray (response, +    cJSON_AddItemToArray (sigsum_array,          cJSON_CreateString ("key-missing"));    if ( (summary & GPGME_SIGSUM_CRL_MISSING)) -    cJSON_AddItemToArray (response, +    cJSON_AddItemToArray (sigsum_array,          cJSON_CreateString ("crl-missing"));    if ( (summary & GPGME_SIGSUM_CRL_TOO_OLD)) -    cJSON_AddItemToArray (response, +    cJSON_AddItemToArray (sigsum_array,          cJSON_CreateString ("crl-too-old"));    if ( (summary & GPGME_SIGSUM_BAD_POLICY )) -    cJSON_AddItemToArray (response, +    cJSON_AddItemToArray (sigsum_array,          cJSON_CreateString ("bad-policy"));    if ( (summary & GPGME_SIGSUM_SYS_ERROR  )) -    cJSON_AddItemToArray (response, +    cJSON_AddItemToArray (sigsum_array,          cJSON_CreateString ("sys-error")); +  /* The signature summary as string array. */ +  xjson_AddItemToObject (result, "sigsum", sigsum_array); + +  /* Bools for the same. */ +  xjson_AddBoolToObject (result, "valid", +                         (summary & GPGME_SIGSUM_VALID      )); +  xjson_AddBoolToObject (result, "green", +                         (summary & GPGME_SIGSUM_GREEN      )); +  xjson_AddBoolToObject (result, "red", +                         (summary & GPGME_SIGSUM_RED        )); +  xjson_AddBoolToObject (result, "revoked", +                         (summary & GPGME_SIGSUM_KEY_REVOKED)); +  xjson_AddBoolToObject (result, "key-expired", +                         (summary & GPGME_SIGSUM_KEY_EXPIRED)); +  xjson_AddBoolToObject (result, "sig-expired", +                         (summary & GPGME_SIGSUM_SIG_EXPIRED)); +  xjson_AddBoolToObject (result, "key-missing", +                         (summary & GPGME_SIGSUM_KEY_MISSING)); +  xjson_AddBoolToObject (result, "crl-missing", +                         (summary & GPGME_SIGSUM_CRL_MISSING)); +  xjson_AddBoolToObject (result, "crl-too-old", +                         (summary & GPGME_SIGSUM_CRL_TOO_OLD)); +  xjson_AddBoolToObject (result, "bad-policy", +                         (summary & GPGME_SIGSUM_BAD_POLICY )); +  xjson_AddBoolToObject (result, "sys-error", +                         (summary & GPGME_SIGSUM_SYS_ERROR  )); -  cJSON_AddItemToObject (result, "summary", response); +  return result;  } @@ -659,151 +863,596 @@ validity_to_string (gpgme_validity_t val)      }  } +static const char * +protocol_to_string (gpgme_protocol_t proto) +{ +  switch (proto) +    { +    case GPGME_PROTOCOL_OpenPGP: return "OpenPGP"; +    case GPGME_PROTOCOL_CMS:     return "CMS"; +    case GPGME_PROTOCOL_GPGCONF: return "gpgconf"; +    case GPGME_PROTOCOL_ASSUAN:  return "assuan"; +    case GPGME_PROTOCOL_G13:     return "g13"; +    case GPGME_PROTOCOL_UISERVER:return "uiserver"; +    case GPGME_PROTOCOL_SPAWN:   return "spawn"; +    default: +                                 return "unknown"; +    } +} -/* Add a single signature to a result */ -static gpg_error_t -add_signature_to_object (cjson_t result, gpgme_signature_t sig) +/* Create a sig_notation json object */ +static cjson_t +sig_notation_to_json (gpgme_sig_notation_t not)  { -  gpg_error_t err = 0; +  cjson_t result = xjson_CreateObject (); +  xjson_AddBoolToObject (result, "human_readable", not->human_readable); +  xjson_AddBoolToObject (result, "critical", not->critical); + +  xjson_AddStringToObject0 (result, "name", not->name); +  xjson_AddStringToObject0 (result, "value", not->value); + +  xjson_AddNumberToObject (result, "flags", not->flags); + +  return result; +} + +/* Create a key_sig json object */ +static cjson_t +key_sig_to_json (gpgme_key_sig_t sig) +{ +  cjson_t result = xjson_CreateObject (); + +  xjson_AddBoolToObject (result, "revoked", sig->revoked); +  xjson_AddBoolToObject (result, "expired", sig->expired); +  xjson_AddBoolToObject (result, "invalid", sig->invalid); +  xjson_AddBoolToObject (result, "exportable", sig->exportable); -  if (!cJSON_AddStringToObject (result, "status", gpgme_strerror (sig->status))) +  xjson_AddStringToObject0 (result, "pubkey_algo_name", +                            gpgme_pubkey_algo_name (sig->pubkey_algo)); +  xjson_AddStringToObject0 (result, "keyid", sig->keyid); +  xjson_AddStringToObject0 (result, "status", gpgme_strerror (sig->status)); +  xjson_AddStringToObject0 (result, "name", sig->name); +  xjson_AddStringToObject0 (result, "email", sig->email); +  xjson_AddStringToObject0 (result, "comment", sig->comment); + +  xjson_AddNumberToObject (result, "pubkey_algo", sig->pubkey_algo); +  xjson_AddNumberToObject (result, "timestamp", sig->timestamp); +  xjson_AddNumberToObject (result, "expires", sig->expires); +  xjson_AddNumberToObject (result, "status_code", sig->status); +  xjson_AddNumberToObject (result, "sig_class", sig->sig_class); + +  if (sig->notations)      { -      err = gpg_error_from_syserror (); -      goto leave; +      gpgme_sig_notation_t not; +      cjson_t array = xjson_CreateArray (); +      for (not = sig->notations; not; not = not->next) +        cJSON_AddItemToArray (array, sig_notation_to_json (not)); +      xjson_AddItemToObject (result, "notations", array);      } -  if (!cJSON_AddNumberToObject (result, "code", sig->status)) +  return result; +} + +/* Create a tofu info object */ +static cjson_t +tofu_to_json (gpgme_tofu_info_t tofu) +{ +  cjson_t result = xjson_CreateObject (); + +  xjson_AddStringToObject0 (result, "description", tofu->description); + +  xjson_AddNumberToObject (result, "validity", tofu->validity); +  xjson_AddNumberToObject (result, "policy", tofu->policy); +  xjson_AddNumberToObject (result, "signcount", tofu->signcount); +  xjson_AddNumberToObject (result, "encrcount", tofu->encrcount); +  xjson_AddNumberToObject (result, "signfirst", tofu->signfirst); +  xjson_AddNumberToObject (result, "signlast", tofu->signlast); +  xjson_AddNumberToObject (result, "encrfirst", tofu->encrfirst); +  xjson_AddNumberToObject (result, "encrlast", tofu->encrlast); + +  return result; +} + +/* Create a userid json object */ +static cjson_t +uid_to_json (gpgme_user_id_t uid) +{ +  cjson_t result = xjson_CreateObject (); + +  xjson_AddBoolToObject (result, "revoked", uid->revoked); +  xjson_AddBoolToObject (result, "invalid", uid->invalid); + +  xjson_AddStringToObject0 (result, "validity", +                            validity_to_string (uid->validity)); +  xjson_AddStringToObject0 (result, "uid", uid->uid); +  xjson_AddStringToObject0 (result, "name", uid->name); +  xjson_AddStringToObject0 (result, "email", uid->email); +  xjson_AddStringToObject0 (result, "comment", uid->comment); +  xjson_AddStringToObject0 (result, "address", uid->address); + +  xjson_AddNumberToObject (result, "origin", uid->origin); +  xjson_AddNumberToObject (result, "last_update", uid->last_update); + +  /* Key sigs */ +  if (uid->signatures)      { -      err = gpg_error_from_syserror (); -      goto leave; -    } +      cjson_t sig_array = xjson_CreateArray (); +      gpgme_key_sig_t sig; -  add_summary_to_object (result, sig->summary); +      for (sig = uid->signatures; sig; sig = sig->next) +        cJSON_AddItemToArray (sig_array, key_sig_to_json (sig)); -  if (!cJSON_AddStringToObject (result, "fingerprint", sig->fpr)) +      xjson_AddItemToObject (result, "signatures", sig_array); +    } + +  /* TOFU info */ +  if (uid->tofu)      { -      err = gpg_error_from_syserror (); -      goto leave; +      gpgme_tofu_info_t tofu; +      cjson_t array = xjson_CreateArray (); +      for (tofu = uid->tofu; tofu; tofu = tofu->next) +        cJSON_AddItemToArray (array, tofu_to_json (tofu)); +      xjson_AddItemToObject (result, "tofu", array);      } -  if (!cJSON_AddNumberToObject (result, "created", sig->timestamp)) +  return result; +} + +/* Create a subkey json object */ +static cjson_t +subkey_to_json (gpgme_subkey_t sub) +{ +  cjson_t result = xjson_CreateObject (); + +  xjson_AddBoolToObject (result, "revoked", sub->revoked); +  xjson_AddBoolToObject (result, "expired", sub->expired); +  xjson_AddBoolToObject (result, "disabled", sub->disabled); +  xjson_AddBoolToObject (result, "invalid", sub->invalid); +  xjson_AddBoolToObject (result, "can_encrypt", sub->can_encrypt); +  xjson_AddBoolToObject (result, "can_sign", sub->can_sign); +  xjson_AddBoolToObject (result, "can_certify", sub->can_certify); +  xjson_AddBoolToObject (result, "can_authenticate", sub->can_authenticate); +  xjson_AddBoolToObject (result, "secret", sub->secret); +  xjson_AddBoolToObject (result, "is_qualified", sub->is_qualified); +  xjson_AddBoolToObject (result, "is_cardkey", sub->is_cardkey); +  xjson_AddBoolToObject (result, "is_de_vs", sub->is_de_vs); + +  xjson_AddStringToObject0 (result, "pubkey_algo_name", +                            gpgme_pubkey_algo_name (sub->pubkey_algo)); +  xjson_AddStringToObject0 (result, "pubkey_algo_string", +                            gpgme_pubkey_algo_string (sub)); +  xjson_AddStringToObject0 (result, "keyid", sub->keyid); +  xjson_AddStringToObject0 (result, "card_number", sub->card_number); +  xjson_AddStringToObject0 (result, "curve", sub->curve); +  xjson_AddStringToObject0 (result, "keygrip", sub->keygrip); + +  xjson_AddNumberToObject (result, "pubkey_algo", sub->pubkey_algo); +  xjson_AddNumberToObject (result, "length", sub->length); +  xjson_AddNumberToObject (result, "timestamp", sub->timestamp); +  xjson_AddNumberToObject (result, "expires", sub->expires); + +  return result; +} + +/* Create a key json object */ +static cjson_t +key_to_json (gpgme_key_t key) +{ +  cjson_t result = xjson_CreateObject (); + +  xjson_AddBoolToObject (result, "revoked", key->revoked); +  xjson_AddBoolToObject (result, "expired", key->expired); +  xjson_AddBoolToObject (result, "disabled", key->disabled); +  xjson_AddBoolToObject (result, "invalid", key->invalid); +  xjson_AddBoolToObject (result, "can_encrypt", key->can_encrypt); +  xjson_AddBoolToObject (result, "can_sign", key->can_sign); +  xjson_AddBoolToObject (result, "can_certify", key->can_certify); +  xjson_AddBoolToObject (result, "can_authenticate", key->can_authenticate); +  xjson_AddBoolToObject (result, "secret", key->secret); +  xjson_AddBoolToObject (result, "is_qualified", key->is_qualified); + +  xjson_AddStringToObject0 (result, "protocol", +                            protocol_to_string (key->protocol)); +  xjson_AddStringToObject0 (result, "issuer_serial", key->issuer_serial); +  xjson_AddStringToObject0 (result, "issuer_name", key->issuer_name); +  xjson_AddStringToObject0 (result, "fingerprint", key->fpr); +  xjson_AddStringToObject0 (result, "chain_id", key->chain_id); +  xjson_AddStringToObject0 (result, "owner_trust", +                            validity_to_string (key->owner_trust)); + +  xjson_AddNumberToObject (result, "origin", key->origin); +  xjson_AddNumberToObject (result, "last_update", key->last_update); + +  /* Add subkeys */ +  if (key->subkeys)      { -      err = gpg_error_from_syserror (); -      goto leave; +      cjson_t subkey_array = xjson_CreateArray (); +      gpgme_subkey_t sub; +      for (sub = key->subkeys; sub; sub = sub->next) +        cJSON_AddItemToArray (subkey_array, subkey_to_json (sub)); + +      xjson_AddItemToObject (result, "subkeys", subkey_array);      } -  if (!cJSON_AddNumberToObject (result, "expired", sig->exp_timestamp)) +  /* User Ids */ +  if (key->uids)      { -      err = gpg_error_from_syserror (); -      goto leave; +      cjson_t uid_array = xjson_CreateArray (); +      gpgme_user_id_t uid; +      for (uid = key->uids; uid; uid = uid->next) +        cJSON_AddItemToArray (uid_array, uid_to_json (uid)); + +      xjson_AddItemToObject (result, "userids", uid_array);      } -  if (!cJSON_AddStringToObject (result, "validity", -                                validity_to_string (sig->validity))) +  return result; +} + + +/* Create a signature json object */ +static cjson_t +signature_to_json (gpgme_signature_t sig) +{ +  cjson_t result = xjson_CreateObject (); + +  xjson_AddItemToObject (result, "summary", sigsum_to_json (sig->summary)); + +  xjson_AddBoolToObject (result, "wrong_key_usage", sig->wrong_key_usage); +  xjson_AddBoolToObject (result, "chain_model", sig->chain_model); +  xjson_AddBoolToObject (result, "is_de_vs", sig->is_de_vs); + +  xjson_AddStringToObject0 (result, "status_string", +                            gpgme_strerror (sig->status)); +  xjson_AddStringToObject0 (result, "fingerprint", sig->fpr); +  xjson_AddStringToObject0 (result, "validity_string", +                            validity_to_string (sig->validity)); +  xjson_AddStringToObject0 (result, "pubkey_algo_name", +                            gpgme_pubkey_algo_name (sig->pubkey_algo)); +  xjson_AddStringToObject0 (result, "hash_algo_name", +                            gpgme_hash_algo_name (sig->hash_algo)); +  xjson_AddStringToObject0 (result, "pka_address", sig->pka_address); + +  xjson_AddNumberToObject (result, "status_code", sig->status); +  xjson_AddNumberToObject (result, "timestamp", sig->timestamp); +  xjson_AddNumberToObject (result, "exp_timestamp", sig->exp_timestamp); +  xjson_AddNumberToObject (result, "pka_trust", sig->pka_trust); +  xjson_AddNumberToObject (result, "validity", sig->validity); +  xjson_AddNumberToObject (result, "validity_reason", sig->validity_reason); + +  if (sig->notations)      { -      err = gpg_error_from_syserror (); -      goto leave; +      gpgme_sig_notation_t not; +      cjson_t array = xjson_CreateArray (); +      for (not = sig->notations; not; not = not->next) +        cJSON_AddItemToArray (array, sig_notation_to_json (not)); +      xjson_AddItemToObject (result, "notations", array);      } -leave: -  return err; +  return result;  } -/* Add multiple signatures as an array to a result */ -static gpg_error_t -add_signatures_to_object (cjson_t result, gpgme_signature_t signatures) +/* Create a JSON object from a gpgme_verify result */ +static cjson_t +verify_result_to_json (gpgme_verify_result_t verify_result)  { -  cjson_t response = xjson_CreateArray (); -  gpg_error_t err = 0; -  gpgme_signature_t sig; +  cjson_t result = xjson_CreateObject (); -  for (sig = signatures; sig; sig = sig->next) +  xjson_AddStringToObject0 (result, "file_name", verify_result->file_name); +  xjson_AddBoolToObject (result, "is_mime", verify_result->is_mime); + +  if (verify_result->signatures)      { -      cjson_t sig_obj = xjson_CreateObject (); -      err = add_signature_to_object (sig_obj, sig); -      if (err) -        { -          cJSON_Delete (sig_obj); -          sig_obj = NULL; -          goto leave; -        } +      cjson_t array = xjson_CreateArray (); +      gpgme_signature_t sig; -      cJSON_AddItemToArray (response, sig_obj); +      for (sig = verify_result->signatures; sig; sig = sig->next) +        cJSON_AddItemToArray (array, signature_to_json (sig)); +      xjson_AddItemToObject (result, "signatures", array);      } -  if (!cJSON_AddItemToObject (result, "signatures", response)) +  return result; +} + +/* Create a recipient json object */ +static cjson_t +recipient_to_json (gpgme_recipient_t recp) +{ +  cjson_t result = xjson_CreateObject (); + +  xjson_AddStringToObject0 (result, "keyid", recp->keyid); +  xjson_AddStringToObject0 (result, "pubkey_algo_name", +                            gpgme_pubkey_algo_name (recp->pubkey_algo)); +  xjson_AddStringToObject0 (result, "status_string", +                            gpgme_strerror (recp->status)); + +  xjson_AddNumberToObject (result, "status_code", recp->status); + +  return result; +} + + +/* Create a JSON object from a gpgme_decrypt result */ +static cjson_t +decrypt_result_to_json (gpgme_decrypt_result_t decrypt_result) +{ +  cjson_t result = xjson_CreateObject (); + +  xjson_AddStringToObject0 (result, "file_name", decrypt_result->file_name); +  xjson_AddStringToObject0 (result, "symkey_algo", +                            decrypt_result->symkey_algo); + +  xjson_AddBoolToObject (result, "wrong_key_usage", +                         decrypt_result->wrong_key_usage); +  xjson_AddBoolToObject (result, "is_de_vs", +                         decrypt_result->is_de_vs); +  xjson_AddBoolToObject (result, "is_mime", decrypt_result->is_mime); +  xjson_AddBoolToObject (result, "legacy_cipher_nomdc", +                         decrypt_result->legacy_cipher_nomdc); + +  if (decrypt_result->recipients)      { -      err = gpg_error_from_syserror (); -      cJSON_Delete (response); -      response = NULL; -      return err; +      cjson_t array = xjson_CreateArray (); +      gpgme_recipient_t recp; + +      for (recp = decrypt_result->recipients; recp; recp = recp->next) +        cJSON_AddItemToArray (array, recipient_to_json (recp)); +      xjson_AddItemToObject (result, "recipients", array);      } -  response = NULL; -leave: -  if (err && response) +  return result; +} + + +/* Create a JSON object from an engine_info */ +static cjson_t +engine_info_to_json (gpgme_engine_info_t info) +{ +  cjson_t result = xjson_CreateObject (); + +  xjson_AddStringToObject0 (result, "protocol", +                            protocol_to_string (info->protocol)); +  xjson_AddStringToObject0 (result, "fname", info->file_name); +  xjson_AddStringToObject0 (result, "version", info->version); +  xjson_AddStringToObject0 (result, "req_version", info->req_version); +  xjson_AddStringToObject0 (result, "homedir", info->home_dir ? +                                                info->home_dir : +                                                "default"); +  return result; +} + + +/* Create a JSON object from an import_status */ +static cjson_t +import_status_to_json (gpgme_import_status_t sts) +{ +  cjson_t result = xjson_CreateObject (); + +  xjson_AddStringToObject0 (result, "fingerprint", sts->fpr); +  xjson_AddStringToObject0 (result, "error_string", +                            gpgme_strerror (sts->result)); + +  xjson_AddNumberToObject (result, "status", sts->status); + +  return result; +} + +/* Create a JSON object from an import result */ +static cjson_t +import_result_to_json (gpgme_import_result_t imp) +{ +  cjson_t result = xjson_CreateObject (); + +  xjson_AddNumberToObject (result, "considered", imp->considered); +  xjson_AddNumberToObject (result, "no_user_id", imp->no_user_id); +  xjson_AddNumberToObject (result, "imported", imp->imported); +  xjson_AddNumberToObject (result, "imported_rsa", imp->imported_rsa); +  xjson_AddNumberToObject (result, "unchanged", imp->unchanged); +  xjson_AddNumberToObject (result, "new_user_ids", imp->new_user_ids); +  xjson_AddNumberToObject (result, "new_sub_keys", imp->new_sub_keys); +  xjson_AddNumberToObject (result, "new_signatures", imp->new_signatures); +  xjson_AddNumberToObject (result, "new_revocations", imp->new_revocations); +  xjson_AddNumberToObject (result, "secret_read", imp->secret_read); +  xjson_AddNumberToObject (result, "secret_imported", imp->secret_imported); +  xjson_AddNumberToObject (result, "secret_unchanged", imp->secret_unchanged); +  xjson_AddNumberToObject (result, "skipped_new_keys", imp->skipped_new_keys); +  xjson_AddNumberToObject (result, "not_imported", imp->not_imported); +  xjson_AddNumberToObject (result, "skipped_v3_keys", imp->skipped_v3_keys); + + +  if (imp->imports)      { -      cJSON_Delete (response); -      response = NULL; +      cjson_t array = xjson_CreateArray (); +      gpgme_import_status_t status; + +      for (status = imp->imports; status; status = status->next) +        cJSON_AddItemToArray (array, import_status_to_json (status)); +      xjson_AddItemToObject (result, "imports", array);      } -  return err; + +  return result;  } -/* Add an array of signature informations under the name "name". */ -static gpg_error_t -add_signatures_object (cjson_t result, const char *name, -                           gpgme_verify_result_t verify_result) +/* Create a JSON object from a gpgconf arg */ +static cjson_t +conf_arg_to_json (gpgme_conf_arg_t arg, gpgme_conf_type_t type)  { -  cjson_t response = xjson_CreateObject (); -  gpg_error_t err = 0; +  cjson_t result = xjson_CreateObject (); +  int is_none = 0; +  switch (type) +    { +      case GPGME_CONF_STRING: +      case GPGME_CONF_PATHNAME: +      case GPGME_CONF_LDAP_SERVER: +      case GPGME_CONF_KEY_FPR: +      case GPGME_CONF_PUB_KEY: +      case GPGME_CONF_SEC_KEY: +      case GPGME_CONF_ALIAS_LIST: +        xjson_AddStringToObject0 (result, "string", arg->value.string); +        break; + +      case GPGME_CONF_UINT32: +        xjson_AddNumberToObject (result, "number", arg->value.uint32); +        break; + +      case GPGME_CONF_INT32: +        xjson_AddNumberToObject (result, "number", arg->value.int32); +        break; + +      case GPGME_CONF_NONE: +      default: +        is_none = 1; +        break; +    } +  xjson_AddBoolToObject (result, "is_none", is_none); +  return result; +} -  err = add_signatures_to_object (response, verify_result->signatures); -  if (err) +/* Create a JSON object from a gpgconf option */ +static cjson_t +conf_opt_to_json (gpgme_conf_opt_t opt) +{ +  cjson_t result = xjson_CreateObject (); + +  xjson_AddStringToObject0 (result, "name", opt->name); +  xjson_AddStringToObject0 (result, "description", opt->description); +  xjson_AddStringToObject0 (result, "argname", opt->argname); +  xjson_AddStringToObject0 (result, "default_description", +                            opt->default_description); +  xjson_AddStringToObject0 (result, "no_arg_description", +                            opt->no_arg_description); + +  xjson_AddNumberToObject (result, "flags", opt->flags); +  xjson_AddNumberToObject (result, "level", opt->level); +  xjson_AddNumberToObject (result, "type", opt->type); +  xjson_AddNumberToObject (result, "alt_type", opt->alt_type); + +  if (opt->default_value)      { -      goto leave; +      cjson_t array = xjson_CreateArray (); +      gpgme_conf_arg_t arg; + +      for (arg = opt->default_value; arg; arg = arg->next) +        cJSON_AddItemToArray (array, conf_arg_to_json (arg, opt->alt_type)); +      xjson_AddItemToObject (result, "default_value", array);      } -  if (!cJSON_AddItemToObject (result, name, response)) +  if (opt->no_arg_value)      { -      err = gpg_error_from_syserror (); -      goto leave; +      cjson_t array = xjson_CreateArray (); +      gpgme_conf_arg_t arg; + +      for (arg = opt->no_arg_value; arg; arg = arg->next) +        cJSON_AddItemToArray (array, conf_arg_to_json (arg, opt->alt_type)); +      xjson_AddItemToObject (result, "no_arg_value", array);      } - leave: -  if (err) + +  if (opt->value)      { -      cJSON_Delete (response); -      response = NULL; +      cjson_t array = xjson_CreateArray (); +      gpgme_conf_arg_t arg; + +      for (arg = opt->value; arg; arg = arg->next) +        cJSON_AddItemToArray (array, conf_arg_to_json (arg, opt->alt_type)); +      xjson_AddItemToObject (result, "value", array);      } -  return err; +  return result;  } - -/* - * Implementation of the commands. - */ +/* Create a JSON object from a gpgconf component*/ +static cjson_t +conf_comp_to_json (gpgme_conf_comp_t cmp) +{ +  cjson_t result = xjson_CreateObject (); + +  xjson_AddStringToObject0 (result, "name", cmp->name); +  xjson_AddStringToObject0 (result, "description", cmp->description); +  xjson_AddStringToObject0 (result, "program_name", cmp->program_name); + + +  if (cmp->options) +    { +      cjson_t array = xjson_CreateArray (); +      gpgme_conf_opt_t opt; + +      for (opt = cmp->options; opt; opt = opt->next) +        cJSON_AddItemToArray (array, conf_opt_to_json (opt)); +      xjson_AddItemToObject (result, "options", array); +    } + +  return result; +} -/* Create a "data" object and the "type", "base64" and "more" flags +/* Create a gpgme_data from json string data named "name" + * in the request. Takes the base64 option into account. + * + * Adds an error to the "result" on error. */ +static gpg_error_t +get_string_data (cjson_t request, cjson_t result, const char *name, +                 gpgme_data_t *r_data) +{ +  gpgme_error_t err; +  int opt_base64; +  cjson_t j_data; + +  if ((err = get_boolean_flag (request, "base64", 0, &opt_base64))) +    return err; + +  /* Get the data.  Note that INPUT is a shallow data object with the +   * storage hold in REQUEST.  */ +  j_data = cJSON_GetObjectItem (request, name); +  if (!j_data) +    { +      return gpg_error (GPG_ERR_NO_DATA); +    } +  if (!cjson_is_string (j_data)) +    { +      return gpg_error (GPG_ERR_INV_VALUE); +    } +  if (opt_base64) +    { +      err = data_from_base64_string (r_data, j_data); +      if (err) +        { +          gpg_error_object (result, err, +                            "Error decoding Base-64 encoded '%s': %s", +                            name, gpg_strerror (err)); +          return err; +        } +    } +  else +    { +      err = gpgme_data_new_from_mem (r_data, j_data->valuestring, +                                     strlen (j_data->valuestring), 0); +      if (err) +        { +          gpg_error_object (result, err, "Error getting '%s': %s", +                            name, gpg_strerror (err)); +          return err; +        } +    } +  return 0; +} + + +/* Create a "data" object and the "type" and "base64" flags   * from DATA and append them to RESULT.  Ownership of DATA is   * transferred to this function.  TYPE must be a fixed string. - * CHUNKSIZE is the chunksize requested from the caller.  If BASE64 is - * -1 the need for base64 encoding is determined by the content of - * DATA, all other values are taken as true or false.  Note that - * op_getmore has similar code but works on PENDING_DATA which is set - * here.  */ + * If BASE64 is -1 the need for base64 encoding is determined + * by the content of DATA, all other values are taken as true + * or false. */  static gpg_error_t -make_data_object (cjson_t result, gpgme_data_t data, size_t chunksize, +make_data_object (cjson_t result, gpgme_data_t data,                    const char *type, int base64)  {    gpg_error_t err;    char *buffer;    const char *s;    size_t buflen, n; -  int c;    if (!base64 || base64 == -1) /* Make sure that we really have a string.  */      gpgme_data_write (data, "", 1); @@ -835,49 +1484,118 @@ make_data_object (cjson_t result, gpgme_data_t data, size_t chunksize,            }      } -  /* Adjust the chunksize if we need to do base64 conversion.  */ -  if (base64) -    chunksize = (chunksize / 4) * 3; -    xjson_AddStringToObject (result, "type", type);    xjson_AddBoolToObject (result, "base64", base64); -  if (buflen > chunksize) +  if (base64) +    err = add_base64_to_object (result, "data", buffer, buflen); +  else +    err = cjson_AddStringToObject (result, "data", buffer); + + leave: +  gpgme_free (buffer); +  return err; +} + + +/* Encode and chunk response. + * + * If neccessary this base64 encodes and chunks the repsonse + * for getmore so that we always return valid json independent + * of the chunksize. + * + * A chunked repsonse contains the base64 encoded chunk + * as a string and a boolean if there is still more data + * available for getmore like: + * { + *   chunk: "SGVsbG8gV29ybGQK" + *   more: true + * } + * + * Chunking is only done if the response is larger then the + * chunksize. + * + * caller has to xfree the return value. + */ +static char * +encode_and_chunk (cjson_t request, cjson_t response) +{ +  char *data; +  gpg_error_t err = 0; +  size_t chunksize; +  char *getmore_request = NULL; + +  if (opt_interactive) +    data = cJSON_Print (response); +  else +    data = cJSON_PrintUnformatted (response); + +  if (!data)      { -      xjson_AddBoolToObject (result, "more", 1); +      err = GPG_ERR_NO_DATA; +      goto leave; +    } -      c = buffer[chunksize]; -      buffer[chunksize] = 0; -      if (base64) -        err = add_base64_to_object (result, "data", buffer, chunksize); -      else -        err = cjson_AddStringToObject (result, "data", buffer); -      buffer[chunksize] = c; -      if (err) -        goto leave; +  if (!request) +    { +      err = GPG_ERR_INV_VALUE; +      goto leave; +    } -      pending_data.buffer = buffer; -      buffer = NULL; -      pending_data.length = buflen; -      pending_data.written = chunksize; -      pending_data.type = type; -      pending_data.base64 = base64; +  if ((err = get_chunksize (request, &chunksize))) +    { +      err = GPG_ERR_INV_VALUE; +      goto leave;      } -  else + +  if (!chunksize) +    goto leave; + +  pending_data.buffer = data; +  /* Data should already be encoded so that it does not +     contain 0.*/ +  pending_data.length = strlen (data); +  pending_data.written = 0; + +  if (gpgrt_asprintf (&getmore_request, +                  "{ \"op\":\"getmore\", \"chunksize\": %i }", +                  (int) chunksize) == -1)      { -      if (base64) -        err = add_base64_to_object (result, "data", buffer, buflen); -      else -        err = cjson_AddStringToObject (result, "data", buffer); +      err = gpg_error_from_syserror (); +      goto leave;      } - leave: -  gpgme_free (buffer); -  return err; +  data = process_request (getmore_request); + +leave: +  xfree (getmore_request); + +  if (!err && !data) +    { +      err = GPG_ERR_GENERAL; +    } + +  if (err) +    { +      cjson_t err_obj = gpg_error_object (NULL, err, +                                          "Encode and chunk failed: %s", +                                          gpgme_strerror (err)); +      xfree (data); +      if (opt_interactive) +        data = cJSON_Print (err_obj); +      data = cJSON_PrintUnformatted (err_obj); + +      cJSON_Delete (err_obj); +    } + +  return data;  } +/* + * Implementation of the commands. + */  static const char hlp_encrypt[] =    "op:     \"encrypt\"\n"    "keys:   Array of strings with the fingerprints or user-ids\n" @@ -887,7 +1605,8 @@ static const char hlp_encrypt[] =    "\n"    "Optional parameters:\n"    "protocol:      Either \"openpgp\" (default) or \"cms\".\n" -  "chunksize:     Max number of bytes in the resulting \"data\".\n" +  "signing_keys:  Similar to the keys parameter for added signing.\n" +  "               (openpgp only)"    "\n"    "Optional boolean flags (default is false):\n"    "base64:        Input data is base64 encoded.\n" @@ -905,32 +1624,27 @@ static const char hlp_encrypt[] =    "data:   Unless armor mode is used a Base64 encoded binary\n"    "        ciphertext.  In armor mode a string with an armored\n"    "        OpenPGP or a PEM message.\n" -  "base64: Boolean indicating whether data is base64 encoded.\n" -  "more:   Optional boolean indicating that \"getmore\" is required."; +  "base64: Boolean indicating whether data is base64 encoded.";  static gpg_error_t  op_encrypt (cjson_t request, cjson_t result)  {    gpg_error_t err;    gpgme_ctx_t ctx = NULL;    gpgme_protocol_t protocol; -  size_t chunksize; -  int opt_base64; +  char **signing_patterns = NULL;    int opt_mime;    char *keystring = NULL; -  cjson_t j_input;    gpgme_data_t input = NULL;    gpgme_data_t output = NULL;    int abool;    gpgme_encrypt_flags_t encrypt_flags = 0; +  gpgme_ctx_t keylist_ctx = NULL; +  gpgme_key_t key = NULL;    if ((err = get_protocol (request, &protocol)))      goto leave;    ctx = get_context (protocol); -  if ((err = get_chunksize (request, &chunksize))) -    goto leave; -  if ((err = get_boolean_flag (request, "base64", 0, &opt_base64))) -    goto leave;    if ((err = get_boolean_flag (request, "mime", 0, &opt_mime)))      goto leave; @@ -964,47 +1678,49 @@ op_encrypt (cjson_t request, cjson_t result)    /* Get the keys.  */ -  err = get_keys (request, &keystring); +  err = get_keys (request, "keys", &keystring);    if (err)      {        /* Provide a custom error response.  */ -      error_object (result, "Error getting keys: %s", gpg_strerror (err)); +      gpg_error_object (result, err, "Error getting keys: %s", +                        gpg_strerror (err));        goto leave;      } -  /* Get the data.  Note that INPUT is a shallow data object with the -   * storage hold in REQUEST.  */ -  j_input = cJSON_GetObjectItem (request, "data"); -  if (!j_input) -    { -      err = gpg_error (GPG_ERR_NO_DATA); -      goto leave; -    } -  if (!cjson_is_string (j_input)) -    { -      err = gpg_error (GPG_ERR_INV_VALUE); -      goto leave; -    } -  if (opt_base64) +  /* Do we have signing keys ? */ +  signing_patterns = create_keylist_patterns (request, "signing_keys"); +  if (signing_patterns)      { -      err = data_from_base64_string (&input, j_input); +      keylist_ctx = create_onetime_context (protocol); +      gpgme_set_keylist_mode (keylist_ctx, GPGME_KEYLIST_MODE_LOCAL); + +      err = gpgme_op_keylist_ext_start (keylist_ctx, +                                        (const char **) signing_patterns, +                                        1, 0);        if (err)          { -          error_object (result, "Error decoding Base-64 encoded 'data': %s", -                        gpg_strerror (err)); +          gpg_error_object (result, err, "Error listing keys: %s", +                            gpg_strerror (err));            goto leave;          } -    } -  else -    { -      err = gpgme_data_new_from_mem (&input, j_input->valuestring, -                                     strlen (j_input->valuestring), 0); -      if (err) +      while (!(err = gpgme_op_keylist_next (keylist_ctx, &key)))          { -          error_object (result, "Error getting 'data': %s", gpg_strerror (err)); -          goto leave; +          if ((err = gpgme_signers_add (ctx, key))) +            { +              gpg_error_object (result, err, "Error adding signer: %s", +                                gpg_strerror (err)); +              goto leave; +            } +          gpgme_key_unref (key); +          key = NULL;          } +      release_onetime_context (keylist_ctx); +      keylist_ctx = NULL;      } + +  if ((err = get_string_data (request, result, "data", &input))) +      goto leave; +    if (opt_mime)      gpgme_data_set_encoding (input, GPGME_DATA_ENCODING_MIME); @@ -1013,30 +1729,44 @@ op_encrypt (cjson_t request, cjson_t result)    err = gpgme_data_new (&output);    if (err)      { -      error_object (result, "Error creating output data object: %s", -                    gpg_strerror (err)); +      gpg_error_object (result, err, "Error creating output data object: %s", +                        gpg_strerror (err));        goto leave;      }    /* Encrypt.  */ -  err = gpgme_op_encrypt_ext (ctx, NULL, keystring, encrypt_flags, -                              input, output); +  if (!signing_patterns) +    { +      err = gpgme_op_encrypt_ext (ctx, NULL, keystring, encrypt_flags, +                                  input, output); +    } +  else +    { +      err = gpgme_op_encrypt_sign_ext (ctx, NULL, keystring, encrypt_flags, +                                       input, output); + +    }    /* encrypt_result = gpgme_op_encrypt_result (ctx); */    if (err)      { -      error_object (result, "Encryption failed: %s", gpg_strerror (err)); +      gpg_error_object (result, err, "Encryption failed: %s", +                        gpg_strerror (err));        goto leave;      }    gpgme_data_release (input);    input = NULL;    /* We need to base64 if armoring has not been requested.  */ -  err = make_data_object (result, output, chunksize, +  err = make_data_object (result, output,                            "ciphertext", !gpgme_get_armor (ctx));    output = NULL;   leave: +  xfree_array (signing_patterns);    xfree (keystring); +  release_onetime_context (keylist_ctx); +  gpgme_key_unref (key); +  gpgme_signers_clear (ctx);    release_context (ctx);    gpgme_data_release (input);    gpgme_data_release (output); @@ -1051,27 +1781,88 @@ static const char hlp_decrypt[] =    "\n"    "Optional parameters:\n"    "protocol:      Either \"openpgp\" (default) or \"cms\".\n" -  "chunksize:     Max number of bytes in the resulting \"data\".\n"    "\n"    "Optional boolean flags (default is false):\n"    "base64:        Input data is base64 encoded.\n"    "\n"    "Response on success:\n" -  "type:   \"plaintext\"\n" -  "data:   The decrypted data.  This may be base64 encoded.\n" -  "base64: Boolean indicating whether data is base64 encoded.\n" -  "mime:   A Boolean indicating whether the data is a MIME object.\n" -  "info:   An optional object with extra information.\n" -  "more:   Optional boolean indicating that \"getmore\" is required."; +  "type:     \"plaintext\"\n" +  "data:     The decrypted data.  This may be base64 encoded.\n" +  "base64:   Boolean indicating whether data is base64 encoded.\n" +  "mime:     deprecated - use dec_info is_mime instead\n" +  "dec_info: An object with decryption information. (gpgme_decrypt_result_t)\n" +  " Boolean values:\n" +  "  wrong_key_usage:     Key should not have been used for encryption.\n" +  "  is_de_vs:            Message was encrypted in compliance to the de-vs\n" +  "                       mode.\n" +  "  is_mime:             Message claims that the content is a MIME Message.\n" +  "  legacy_cipher_nomdc: The message was made by a legacy algorithm\n" +  "                       without integrity protection.\n" +  " String values:\n" +  "  file_name:   The filename contained in the decrypt result.\n" +  "  symkey_algo: A string with the symmetric encryption algorithm and\n" +  "               mode using the format \"<algo>.<mode>\".\n" +  " Array values:\n" +  "  recipients:  The list of recipients (gpgme_recipient_t).\n" +  "   String values:\n" +  "    keyid:            The keyid of the recipient.\n" +  "    pubkey_algo_name: gpgme_pubkey_algo_name of used algo.\n" +  "    status_string:    The status code as localized gpg-error string\n" +  "   Number values:\n" +  "    status_code:      The status as a number. (gpg_error_t)\n" +  "info:     Optional an object with verification information.\n" +  "          (gpgme_verify_result_t)\n" +  " file_name: The filename contained in the verify result.\n" +  " is_mime:   The is_mime info contained in the verify result.\n" +  " signatures: Array of signatures\n" +  "  summary: Object containing summary information.\n" +  "   Boolean values: (Check gpgme_sigsum_t doc for meaning)\n" +  "    valid\n" +  "    green\n" +  "    red\n" +  "    revoked\n" +  "    key-expired\n" +  "    sig-expired\n" +  "    key-missing\n" +  "    crl-missing\n" +  "    crl-too-old\n" +  "    bad-policy\n" +  "    sys-error\n" +  "   sigsum: Array of strings representing the sigsum.\n" +  "  Boolean values:\n" +  "   wrong_key_usage: Key should not have been used for signing.\n" +  "   chain_model:     Validity has been verified using the chain model.\n" +  "   is_de_vs:        signature is in compliance to the de-vs mode.\n" +  "  String values:\n" +  "   status_string:      The status code as localized gpg-error string\n" +  "   fingerprint:        The fingerprint of the signing key.\n" +  "   validity_string:    The validity as string.\n" +  "   pubkey_algo_name:   gpgme_pubkey_algo_name of used algo.\n" +  "   hash_algo_name:     gpgme_hash_algo_name of used hash algo\n" +  "   pka_address:        The mailbox from the PKA information.\n" +  "  Number values:\n" +  "   status_code:     The status as a number. (gpg_error_t)\n" +  "   timestamp:       Signature creation time. (secs since epoch)\n" +  "   exp_timestamp:   Signature expiration or 0. (secs since epoch)\n" +  "   pka_trust: PKA status: 0 = not available, 1 = bad, 2 = okay, 3 = RFU.\n" +  "   validity: validity as number (gpgme_validity_t)\n" +  "   validity_reason: (gpg_error_t)\n" +  "  Array values:\n" +  "   notations: Notation data and policy urls (gpgme_sig_notation_t)\n" +  "    Boolean values:\n" +  "     human_readable\n" +  "     critical\n" +  "    String values:\n" +  "     name\n" +  "     value\n" +  "    Number values:\n" +  "     flags\n";  static gpg_error_t  op_decrypt (cjson_t request, cjson_t result)  {    gpg_error_t err;    gpgme_ctx_t ctx = NULL;    gpgme_protocol_t protocol; -  size_t chunksize; -  int opt_base64; -  cjson_t j_input;    gpgme_data_t input = NULL;    gpgme_data_t output = NULL;    gpgme_decrypt_result_t decrypt_result; @@ -1080,52 +1871,17 @@ op_decrypt (cjson_t request, cjson_t result)    if ((err = get_protocol (request, &protocol)))      goto leave;    ctx = get_context (protocol); -  if ((err = get_chunksize (request, &chunksize))) -    goto leave; - -  if ((err = get_boolean_flag (request, "base64", 0, &opt_base64))) -    goto leave; -  /* Get the data.  Note that INPUT is a shallow data object with the -   * storage hold in REQUEST.  */ -  j_input = cJSON_GetObjectItem (request, "data"); -  if (!j_input) -    { -      err = gpg_error (GPG_ERR_NO_DATA); -      goto leave; -    } -  if (!cjson_is_string (j_input)) -    { -      err = gpg_error (GPG_ERR_INV_VALUE); +  if ((err = get_string_data (request, result, "data", &input)))        goto leave; -    } -  if (opt_base64) -    { -      err = data_from_base64_string (&input, j_input); -      if (err) -        { -          error_object (result, "Error decoding Base-64 encoded 'data': %s", -                        gpg_strerror (err)); -          goto leave; -        } -    } -  else -    { -      err = gpgme_data_new_from_mem (&input, j_input->valuestring, -                                     strlen (j_input->valuestring), 0); -      if (err) -        { -          error_object (result, "Error getting 'data': %s", gpg_strerror (err)); -          goto leave; -        } -    }    /* Create an output data object.  */    err = gpgme_data_new (&output);    if (err)      { -      error_object (result, "Error creating output data object: %s", -                    gpg_strerror (err)); +      gpg_error_object (result, err, +                        "Error creating output data object: %s", +                        gpg_strerror (err));        goto leave;      } @@ -1135,7 +1891,8 @@ op_decrypt (cjson_t request, cjson_t result)    decrypt_result = gpgme_op_decrypt_result (ctx);    if (err)      { -      error_object (result, "Decryption failed: %s", gpg_strerror (err)); +      gpg_error_object (result, err, "Decryption failed: %s", +                        gpg_strerror (err));        goto leave;      }    gpgme_data_release (input); @@ -1144,24 +1901,23 @@ op_decrypt (cjson_t request, cjson_t result)    if (decrypt_result->is_mime)      xjson_AddBoolToObject (result, "mime", 1); +  xjson_AddItemToObject (result, "dec_info", +                         decrypt_result_to_json (decrypt_result)); +    verify_result = gpgme_op_verify_result (ctx);    if (verify_result && verify_result->signatures)      { -      err = add_signatures_object (result, "info", verify_result); -    } - -  if (err) -    { -      error_object (result, "Info output failed: %s", gpg_strerror (err)); -      goto leave; +      xjson_AddItemToObject (result, "info", +                             verify_result_to_json (verify_result));      } -  err = make_data_object (result, output, chunksize, "plaintext", -1); +  err = make_data_object (result, output, "plaintext", -1);    output = NULL;    if (err)      { -      error_object (result, "Plaintext output failed: %s", gpg_strerror (err)); +      gpg_error_object (result, err, "Plaintext output failed: %s", +                        gpg_strerror (err));        goto leave;      } @@ -1182,7 +1938,6 @@ static const char hlp_sign[] =    "\n"    "Optional parameters:\n"    "protocol:      Either \"openpgp\" (default) or \"cms\".\n" -  "chunksize:     Max number of bytes in the resulting \"data\".\n"    "sender:        The mail address of the sender.\n"    "mode:          A string with the signing mode can be:\n"    "               detached (default)\n" @@ -1198,18 +1953,14 @@ static const char hlp_sign[] =    "data:   Unless armor mode is used a Base64 encoded binary\n"    "        signature.  In armor mode a string with an armored\n"    "        OpenPGP or a PEM message.\n" -  "base64: Boolean indicating whether data is base64 encoded.\n" -  "more:   Optional boolean indicating that \"getmore\" is required."; +  "base64: Boolean indicating whether data is base64 encoded.\n";  static gpg_error_t  op_sign (cjson_t request, cjson_t result)  {    gpg_error_t err;    gpgme_ctx_t ctx = NULL;    gpgme_protocol_t protocol; -  size_t chunksize; -  int opt_base64; -  char *keystring = NULL; -  cjson_t j_input; +  char **patterns = NULL;    gpgme_data_t input = NULL;    gpgme_data_t output = NULL;    int abool; @@ -1221,11 +1972,6 @@ op_sign (cjson_t request, cjson_t result)    if ((err = get_protocol (request, &protocol)))      goto leave;    ctx = get_context (protocol); -  if ((err = get_chunksize (request, &chunksize))) -    goto leave; - -  if ((err = get_boolean_flag (request, "base64", 0, &opt_base64))) -    goto leave;    if ((err = get_boolean_flag (request, "armor", 0, &abool)))      goto leave; @@ -1250,117 +1996,1112 @@ op_sign (cjson_t request, cjson_t result)        gpgme_set_sender (ctx, j_tmp->valuestring);      } -  /* Get the keys.  */ -  err = get_keys (request, &keystring); -  if (err) +  patterns = create_keylist_patterns (request, "keys"); +  if (!patterns)      { -      /* Provide a custom error response.  */ -      error_object (result, "Error getting keys: %s", gpg_strerror (err)); +      gpg_error_object (result, err, "Error getting keys: %s", +                        gpg_strerror (gpg_error (GPG_ERR_NO_KEY)));        goto leave;      }    /* Do a keylisting and add the keys */ -  if ((err = gpgme_new (&keylist_ctx))) -    goto leave; -  gpgme_set_protocol (keylist_ctx, protocol); +  keylist_ctx = create_onetime_context (protocol);    gpgme_set_keylist_mode (keylist_ctx, GPGME_KEYLIST_MODE_LOCAL); -  err = gpgme_op_keylist_start (ctx, keystring, 1); +  err = gpgme_op_keylist_ext_start (keylist_ctx, +                                    (const char **) patterns, 1, 0);    if (err)      { -      error_object (result, "Error listing keys: %s", gpg_strerror (err)); +      gpg_error_object (result, err, "Error listing keys: %s", +                        gpg_strerror (err));        goto leave;      } -  while (!(err = gpgme_op_keylist_next (ctx, &key))) +  while (!(err = gpgme_op_keylist_next (keylist_ctx, &key)))      {        if ((err = gpgme_signers_add (ctx, key)))          { -          error_object (result, "Error adding signer: %s", gpg_strerror (err)); +          gpg_error_object (result, err, "Error adding signer: %s", +                            gpg_strerror (err));            goto leave;          }        gpgme_key_unref (key); +      key = NULL;      } -  /* Get the data.  Note that INPUT is a shallow data object with the -   * storage hold in REQUEST.  */ -  j_input = cJSON_GetObjectItem (request, "data"); -  if (!j_input) +  if ((err = get_string_data (request, result, "data", &input))) +    goto leave; + +  /* Create an output data object.  */ +  err = gpgme_data_new (&output); +  if (err)      { -      err = gpg_error (GPG_ERR_NO_DATA); +      gpg_error_object (result, err, "Error creating output data object: %s", +                        gpg_strerror (err));        goto leave;      } -  if (!cjson_is_string (j_input)) + +  /* Sign. */ +  err = gpgme_op_sign (ctx, input, output, mode); +  if (err)      { -      err = gpg_error (GPG_ERR_INV_VALUE); +      gpg_error_object (result, err, "Signing failed: %s", +                        gpg_strerror (err));        goto leave;      } -  if (opt_base64) + +  gpgme_data_release (input); +  input = NULL; + +  /* We need to base64 if armoring has not been requested.  */ +  err = make_data_object (result, output, +                          "signature", !gpgme_get_armor (ctx)); +  output = NULL; + + leave: +  xfree_array (patterns); +  gpgme_signers_clear (ctx); +  gpgme_key_unref (key); +  release_onetime_context (keylist_ctx); +  release_context (ctx); +  gpgme_data_release (input); +  gpgme_data_release (output); +  return err; +} + + + +static const char hlp_verify[] = +  "op:     \"verify\"\n" +  "data:   The data to verify.\n" +  "\n" +  "Optional parameters:\n" +  "protocol:      Either \"openpgp\" (default) or \"cms\".\n" +  "signature:     A detached signature. If missing opaque is assumed.\n" +  "\n" +  "Optional boolean flags (default is false):\n" +  "base64:        Input data is base64 encoded.\n" +  "\n" +  "Response on success:\n" +  "type:   \"plaintext\"\n" +  "data:   The verified data.  This may be base64 encoded.\n" +  "base64: Boolean indicating whether data is base64 encoded.\n" +  "info:   An object with verification information (gpgme_verify_result_t).\n" +  " file_name: Optional string of the plaintext file name.\n" +  " is_mime:    Boolean that is true if the messages claims it is MIME.\n" +  " signatures: Array of signatures\n" +  "  summary: Object containing summary information.\n" +  "   Boolean values: (Check gpgme_sigsum_t doc for meaning)\n" +  "    valid\n" +  "    green\n" +  "    red\n" +  "    revoked\n" +  "    key-expired\n" +  "    sig-expired\n" +  "    key-missing\n" +  "    crl-missing\n" +  "    crl-too-old\n" +  "    bad-policy\n" +  "    sys-error\n" +  "   sigsum: Array of strings representing the sigsum.\n" +  "  Boolean values:\n" +  "   wrong_key_usage: Key should not have been used for signing.\n" +  "   chain_model:     Validity has been verified using the chain model.\n" +  "   is_de_vs:        signature is in compliance to the de-vs mode.\n" +  "  String values:\n" +  "   status_string:      The status code as localized gpg-error string\n" +  "   fingerprint:        The fingerprint of the signing key.\n" +  "   validity_string:    The validity as string.\n" +  "   pubkey_algo_name:   gpgme_pubkey_algo_name of used algo.\n" +  "   hash_algo_name:     gpgme_hash_algo_name of used hash algo\n" +  "   pka_address:        The mailbox from the PKA information.\n" +  "  Number values:\n" +  "   status_code:     The status as a number. (gpg_error_t)\n" +  "   timestamp:       Signature creation time. (secs since epoch)\n" +  "   exp_timestamp:   Signature expiration or 0. (secs since epoch)\n" +  "   pka_trust: PKA status: 0 = not available, 1 = bad, 2 = okay, 3 = RFU.\n" +  "   validity: validity as number (gpgme_validity_t)\n" +  "   validity_reason: (gpg_error_t)\n" +  "  Array values:\n" +  "   notations: Notation data and policy urls (gpgme_sig_notation_t)\n" +  "    Boolean values:\n" +  "     human_readable\n" +  "     critical\n" +  "    String values:\n" +  "     name\n" +  "     value\n" +  "    Number values:\n" +  "     flags\n"; +static gpg_error_t +op_verify (cjson_t request, cjson_t result) +{ +  gpg_error_t err; +  gpgme_ctx_t ctx = NULL; +  gpgme_protocol_t protocol; +  gpgme_data_t input = NULL; +  gpgme_data_t signature = NULL; +  gpgme_data_t output = NULL; +  gpgme_verify_result_t verify_result; + +  if ((err = get_protocol (request, &protocol))) +    goto leave; +  ctx = get_context (protocol); + +  if ((err = get_string_data (request, result, "data", &input))) +    goto leave; + +  err = get_string_data (request, result, "signature", &signature); +  /* Signature data is optional otherwise we expect opaque or clearsigned. */ +  if (err && err != gpg_error (GPG_ERR_NO_DATA)) +    goto leave; + +  /* Create an output data object.  */ +  err = gpgme_data_new (&output); +  if (err)      { -      err = data_from_base64_string (&input, j_input); -      if (err) -        { -          error_object (result, "Error decoding Base-64 encoded 'data': %s", +      gpg_error_object (result, err, "Error creating output data object: %s",                          gpg_strerror (err)); -          goto leave; -        } +      goto leave; +    } + +  /* Verify.  */ +  if (signature) +    { +      err = gpgme_op_verify (ctx, signature, input, output);      }    else      { -      err = gpgme_data_new_from_mem (&input, j_input->valuestring, -                                     strlen (j_input->valuestring), 0); -      if (err) -        { -          error_object (result, "Error getting 'data': %s", gpg_strerror (err)); -          goto leave; -        } +      err = gpgme_op_verify (ctx, input, 0, output); +    } +  if (err) +    { +      gpg_error_object (result, err, "Verify failed: %s", gpg_strerror (err)); +      goto leave;      } +  gpgme_data_release (input); +  input = NULL; +  gpgme_data_release (signature); +  signature = NULL; + +  verify_result = gpgme_op_verify_result (ctx); +  if (verify_result && verify_result->signatures) +    { +      xjson_AddItemToObject (result, "info", +                             verify_result_to_json (verify_result)); +    } + +  err = make_data_object (result, output, "plaintext", -1); +  output = NULL; -  /* Create an output data object.  */ -  err = gpgme_data_new (&output);    if (err)      { -      error_object (result, "Error creating output data object: %s", -                    gpg_strerror (err)); +      gpg_error_object (result, err, "Plaintext output failed: %s", +                        gpg_strerror (err));        goto leave;      } -  /* Sign. */ -  err = gpgme_op_sign (ctx, input, output, mode); + leave: +  release_context (ctx); +  gpgme_data_release (input); +  gpgme_data_release (output); +  gpgme_data_release (signature); +  return err; +} + + + +static const char hlp_version[] = +  "op:     \"version\"\n" +  "\n" +  "Response on success:\n" +  "gpgme:  The GPGME Version.\n" +  "info:   dump of engine info. containing:\n" +  "        protocol: The protocol.\n" +  "        fname:    The file name.\n" +  "        version:  The version.\n" +  "        req_ver:  The required version.\n" +  "        homedir:  The homedir of the engine or \"default\".\n"; +static gpg_error_t +op_version (cjson_t request, cjson_t result) +{ +  gpg_error_t err = 0; +  gpgme_engine_info_t ei = NULL; +  cjson_t infos = xjson_CreateArray (); + +  (void)request; + +  if (!cJSON_AddStringToObject (result, "gpgme", gpgme_check_version (NULL))) +    { +      cJSON_Delete (infos); +      return gpg_error_from_syserror (); +    } + +  if ((err = gpgme_get_engine_info (&ei))) +    { +      cJSON_Delete (infos); +      return err; +    } + +  for (; ei; ei = ei->next) +    cJSON_AddItemToArray (infos, engine_info_to_json (ei)); + +  if (!cJSON_AddItemToObject (result, "info", infos)) +    { +      err = gpg_error_from_syserror (); +      cJSON_Delete (infos); +      return err; +    } + +  return 0; +} + + + +static const char hlp_keylist[] = +  "op:     \"keylist\"\n" +  "\n" +  "Optional parameters:\n" +  "keys:          Array of strings or fingerprints to lookup\n" +  "               For a single key a String may be used instead of an array.\n" +  "               default lists all keys.\n" +  "protocol:      Either \"openpgp\" (default) or \"cms\".\n" +  "\n" +  "Optional boolean flags (default is false):\n" +  "secret:        List only secret keys.\n" +  "with-secret:   Add KEYLIST_MODE_WITH_SECRET.\n" +  "extern:        Add KEYLIST_MODE_EXTERN.\n" +  "local:         Add KEYLIST_MODE_LOCAL. (default mode).\n" +  "sigs:          Add KEYLIST_MODE_SIGS.\n" +  "notations:     Add KEYLIST_MODE_SIG_NOTATIONS.\n" +  "tofu:          Add KEYLIST_MODE_WITH_TOFU.\n" +  "ephemeral:     Add KEYLIST_MODE_EPHEMERAL.\n" +  "validate:      Add KEYLIST_MODE_VALIDATE.\n" +  "locate:        Add KEYLIST_MODE_LOCATE.\n" +  "\n" +  "Response on success:\n" +  "keys:   Array of keys.\n" +  "  Boolean values:\n" +  "   revoked\n" +  "   expired\n" +  "   disabled\n" +  "   invalid\n" +  "   can_encrypt\n" +  "   can_sign\n" +  "   can_certify\n" +  "   can_authenticate\n" +  "   secret\n" +  "   is_qualified\n" +  "  String values:\n" +  "   protocol\n" +  "   issuer_serial (CMS Only)\n" +  "   issuer_name (CMS Only)\n" +  "   chain_id (CMS Only)\n" +  "   owner_trust (OpenPGP only)\n" +  "   fingerprint\n" +  "  Number values:\n" +  "   last_update\n" +  "   origin\n" +  "  Array values:\n" +  "   subkeys\n" +  "    Boolean values:\n" +  "     revoked\n" +  "     expired\n" +  "     disabled\n" +  "     invalid\n" +  "     can_encrypt\n" +  "     can_sign\n" +  "     can_certify\n" +  "     can_authenticate\n" +  "     secret\n" +  "     is_qualified\n" +  "     is_cardkey\n" +  "     is_de_vs\n" +  "    String values:\n" +  "     pubkey_algo_name\n" +  "     pubkey_algo_string\n" +  "     keyid\n" +  "     card_number\n" +  "     curve\n" +  "     keygrip\n" +  "    Number values:\n" +  "     pubkey_algo\n" +  "     length\n" +  "     timestamp\n" +  "     expires\n" +  "   userids\n" +  "    Boolean values:\n" +  "     revoked\n" +  "     invalid\n" +  "    String values:\n" +  "     validity\n" +  "     uid\n" +  "     name\n" +  "     email\n" +  "     comment\n" +  "     address\n" +  "    Number values:\n" +  "     origin\n" +  "     last_update\n" +  "    Array values:\n" +  "     signatures\n" +  "      Boolean values:\n" +  "       revoked\n" +  "       expired\n" +  "       invalid\n" +  "       exportable\n" +  "      String values:\n" +  "       pubkey_algo_name\n" +  "       keyid\n" +  "       status\n" +  "       uid\n" +  "       name\n" +  "       email\n" +  "       comment\n" +  "      Number values:\n" +  "       pubkey_algo\n" +  "       timestamp\n" +  "       expires\n" +  "       status_code\n" +  "       sig_class\n" +  "      Array values:\n" +  "       notations\n" +  "        Boolean values:\n" +  "         human_readable\n" +  "         critical\n" +  "        String values:\n" +  "         name\n" +  "         value\n" +  "        Number values:\n" +  "         flags\n" +  "     tofu\n" +  "      String values:\n" +  "       description\n" +  "      Number values:\n" +  "       validity\n" +  "       policy\n" +  "       signcount\n" +  "       encrcount\n" +  "       signfirst\n" +  "       signlast\n" +  "       encrfirst\n" +  "       encrlast\n"; +static gpg_error_t +op_keylist (cjson_t request, cjson_t result) +{ +  gpg_error_t err; +  gpgme_ctx_t ctx = NULL; +  gpgme_protocol_t protocol; +  char **patterns = NULL; +  int abool; +  int secret_only = 0; +  gpgme_keylist_mode_t mode = 0; +  gpgme_key_t key = NULL; +  cjson_t keyarray = xjson_CreateArray (); + +  if ((err = get_protocol (request, &protocol))) +    goto leave; +  ctx = get_context (protocol); + +  /* Handle the various keylist mode bools. */ +  if ((err = get_boolean_flag (request, "secret", 0, &abool))) +    goto leave; +  if (abool) +    { +      mode |= GPGME_KEYLIST_MODE_WITH_SECRET; +      secret_only = 1; +    } +  if ((err = get_boolean_flag (request, "with-secret", 0, &abool))) +    goto leave; +  if (abool) +    mode |= GPGME_KEYLIST_MODE_WITH_SECRET; +  if ((err = get_boolean_flag (request, "extern", 0, &abool))) +    goto leave; +  if (abool) +    mode |= GPGME_KEYLIST_MODE_EXTERN; + +  if ((err = get_boolean_flag (request, "local", 0, &abool))) +    goto leave; +  if (abool) +    mode |= GPGME_KEYLIST_MODE_LOCAL; + +  if ((err = get_boolean_flag (request, "sigs", 0, &abool))) +    goto leave; +  if (abool) +    mode |= GPGME_KEYLIST_MODE_SIGS; + +  if ((err = get_boolean_flag (request, "notations", 0, &abool))) +    goto leave; +  if (abool) +    mode |= GPGME_KEYLIST_MODE_SIG_NOTATIONS; + +  if ((err = get_boolean_flag (request, "tofu", 0, &abool))) +    goto leave; +  if (abool) +    mode |= GPGME_KEYLIST_MODE_WITH_TOFU; + +  if ((err = get_boolean_flag (request, "ephemeral", 0, &abool))) +    goto leave; +  if (abool) +    mode |= GPGME_KEYLIST_MODE_EPHEMERAL; + +  if ((err = get_boolean_flag (request, "validate", 0, &abool))) +    goto leave; +  if (abool) +    mode |= GPGME_KEYLIST_MODE_VALIDATE; + +  if ((err = get_boolean_flag (request, "locate", 0, &abool))) +    goto leave; +  if (abool) +    mode |= GPGME_KEYLIST_MODE_LOCATE; + +  if (!mode) +    { +      /* default to local */ +      mode = GPGME_KEYLIST_MODE_LOCAL; +    } + +  /* Get the keys.  */ +  patterns = create_keylist_patterns (request, "keys"); + +  /* Do a keylisting and add the keys */ +  gpgme_set_keylist_mode (ctx, mode); + +  err = gpgme_op_keylist_ext_start (ctx, (const char **) patterns, +                                    secret_only, 0);    if (err)      { -      error_object (result, "Signing failed: %s", gpg_strerror (err)); +      gpg_error_object (result, err, "Error listing keys: %s", +                        gpg_strerror (err)); +      goto leave; +    } + +  while (!(err = gpgme_op_keylist_next (ctx, &key))) +    { +      cJSON_AddItemToArray (keyarray, key_to_json (key)); +      gpgme_key_unref (key); +    } +  err = 0; + +  if (!cJSON_AddItemToObject (result, "keys", keyarray)) +    { +      err = gpg_error_from_syserror ();        goto leave;      } + leave: +  xfree_array (patterns); +  if (err) +    { +      cJSON_Delete (keyarray); +    } +  return err; +} + + + +static const char hlp_import[] = +  "op:     \"import\"\n" +  "data:   The data to import.\n" +  "\n" +  "Optional parameters:\n" +  "protocol:      Either \"openpgp\" (default) or \"cms\".\n" +  "\n" +  "Optional boolean flags (default is false):\n" +  "base64:        Input data is base64 encoded.\n" +  "\n" +  "Response on success:\n" +  "result: The import result.\n" +  "  Number values:\n" +  "   considered\n" +  "   no_user_id\n" +  "   imported\n" +  "   imported_rsa\n" +  "   unchanged\n" +  "   new_user_ids\n" +  "   new_sub_keys\n" +  "   new_signatures\n" +  "   new_revocations\n" +  "   secret_read\n" +  "   secret_imported\n" +  "   secret_unchanged\n" +  "   skipped_new_keys\n" +  "   not_imported\n" +  "   skipped_v3_keys\n" +  "  Array values:\n" +  "   imports: List of keys for which an import was attempted\n" +  "    String values:\n" +  "     fingerprint\n" +  "     error_string\n" +  "    Number values:\n" +  "     error_code\n" +  "     status\n"; +static gpg_error_t +op_import (cjson_t request, cjson_t result) +{ +  gpg_error_t err; +  gpgme_ctx_t ctx = NULL; +  gpgme_data_t input = NULL; +  gpgme_import_result_t import_result; +  gpgme_protocol_t protocol; + +  if ((err = get_protocol (request, &protocol))) +    goto leave; +  ctx = get_context (protocol); + +  if ((err = get_string_data (request, result, "data", &input))) +      goto leave; + +  /* Import.  */ +  err = gpgme_op_import (ctx, input); +  import_result = gpgme_op_import_result (ctx); +  if (err) +    { +      gpg_error_object (result, err, "Import failed: %s", +                        gpg_strerror (err)); +      goto leave; +    }    gpgme_data_release (input);    input = NULL; -  /* We need to base64 if armoring has not been requested.  */ -  err = make_data_object (result, output, chunksize, -                          "ciphertext", !gpgme_get_armor (ctx)); -  output = NULL; +  xjson_AddItemToObject (result, "result", +                         import_result_to_json (import_result));   leave: -  xfree (keystring);    release_context (ctx); -  release_context (keylist_ctx);    gpgme_data_release (input); +  return err; +} + + +static const char hlp_export[] = +  "op:     \"export\"\n" +  "\n" +  "Optional parameters:\n" +  "keys:          Array of strings or fingerprints to lookup\n" +  "               For a single key a String may be used instead of an array.\n" +  "               default exports all keys.\n" +  "protocol:      Either \"openpgp\" (default) or \"cms\".\n" +  "\n" +  "Optional boolean flags (default is false):\n" +  "armor:         Request output in armored format.\n" +  "extern:        Add EXPORT_MODE_EXTERN.\n" +  "minimal:       Add EXPORT_MODE_MINIMAL.\n" +  "raw:           Add EXPORT_MODE_RAW.\n" +  "pkcs12:        Add EXPORT_MODE_PKCS12.\n" +  "with-sec-fprs: Add the sec-fprs array to the result.\n" +  "\n" +  "Response on success:\n" +  "type:     \"keys\"\n" +  "data:     Unless armor mode is used a Base64 encoded binary.\n" +  "          In armor mode a string with an armored\n" +  "          OpenPGP or a PEM / PKCS12 key.\n" +  "base64:   Boolean indicating whether data is base64 encoded.\n" +  "sec-fprs: Optional, only if with-secret is set. An array containing\n" +  "          the fingerprints of the keys in the export for which a secret\n" +  "          key is available"; +static gpg_error_t +op_export (cjson_t request, cjson_t result) +{ +  gpg_error_t err; +  gpgme_ctx_t ctx = NULL; +  gpgme_protocol_t protocol; +  char **patterns = NULL; +  int abool; +  int with_secret = 0; +  gpgme_export_mode_t mode = 0; +  gpgme_data_t output = NULL; + +  if ((err = get_protocol (request, &protocol))) +    goto leave; +  ctx = get_context (protocol); + +  if ((err = get_boolean_flag (request, "armor", 0, &abool))) +    goto leave; +  gpgme_set_armor (ctx, abool); + +  /* Handle the various export mode bools. */ +  if ((err = get_boolean_flag (request, "secret", 0, &abool))) +    goto leave; +  if (abool) +    { +      err = gpg_error (GPG_ERR_FORBIDDEN); +      goto leave; +    } + +  if ((err = get_boolean_flag (request, "extern", 0, &abool))) +    goto leave; +  if (abool) +    mode |= GPGME_EXPORT_MODE_EXTERN; + +  if ((err = get_boolean_flag (request, "minimal", 0, &abool))) +    goto leave; +  if (abool) +    mode |= GPGME_EXPORT_MODE_MINIMAL; + +  if ((err = get_boolean_flag (request, "raw", 0, &abool))) +    goto leave; +  if (abool) +    mode |= GPGME_EXPORT_MODE_RAW; + +  if ((err = get_boolean_flag (request, "pkcs12", 0, &abool))) +    goto leave; +  if (abool) +    mode |= GPGME_EXPORT_MODE_PKCS12; + +  if ((err = get_boolean_flag (request, "with-sec-fprs", 0, &abool))) +    goto leave; +  if (abool) +    with_secret = 1; + +  /* Get the export patterns.  */ +  patterns = create_keylist_patterns (request, "keys"); + +  /* Create an output data object.  */ +  err = gpgme_data_new (&output); +  if (err) +    { +      gpg_error_object (result, err, "Error creating output data object: %s", +                        gpg_strerror (err)); +      goto leave; +    } + +  err = gpgme_op_export_ext (ctx, (const char **) patterns, +                             mode, output); +  if (err) +    { +      gpg_error_object (result, err, "Error exporting keys: %s", +                        gpg_strerror (err)); +      goto leave; +    } + +  /* We need to base64 if armoring has not been requested.  */ +  err = make_data_object (result, output, +                          "keys", !gpgme_get_armor (ctx)); +  output = NULL; + +  if (!err && with_secret) +    { +      err = add_secret_fprs ((const char **) patterns, protocol, result); +    } + +leave: +  xfree_array (patterns); +  release_context (ctx);    gpgme_data_release (output); +    return err;  } + +static const char hlp_delete[] = +  "op:     \"delete\"\n" +  "key:    Fingerprint of the key to delete.\n" +  "\n" +  "Optional parameters:\n" +  "protocol:      Either \"openpgp\" (default) or \"cms\".\n" +  "\n" +  "Response on success:\n" +  "success:   Boolean true.\n"; +static gpg_error_t +op_delete (cjson_t request, cjson_t result) +{ +  gpg_error_t err; +  gpgme_ctx_t ctx = NULL; +  gpgme_ctx_t keylist_ctx = NULL; +  gpgme_protocol_t protocol; +  gpgme_key_t key = NULL; +  int secret = 0; +  cjson_t j_key = NULL; + +  if ((err = get_protocol (request, &protocol))) +    goto leave; +  ctx = get_context (protocol); +  keylist_ctx = get_context (protocol); + +  if ((err = get_boolean_flag (request, "secret", 0, &secret))) +    goto leave; +  if (secret) +    { +      err = gpg_error (GPG_ERR_FORBIDDEN); +      goto leave; +    } + +  j_key = cJSON_GetObjectItem (request, "key"); +  if (!j_key) +    { +      err = gpg_error (GPG_ERR_NO_KEY); +      goto leave; +    } +  if (!cjson_is_string (j_key)) +    { +      err = gpg_error (GPG_ERR_INV_VALUE); +      goto leave; +    } + +  /* Get the key */ +  if ((err = gpgme_get_key (keylist_ctx, j_key->valuestring, &key, 0))) +    { +      gpg_error_object (result, err, "Error fetching key for delete: %s", +                        gpg_strerror (err)); +      goto leave; +    } + +  err = gpgme_op_delete (ctx, key, 0); +  if (err) +    { +      gpg_error_object (result, err, "Error deleting key: %s", +                        gpg_strerror (err)); +      goto leave; +    } + +  xjson_AddBoolToObject (result, "success", 1); + +leave: +  gpgme_key_unref (key); +  release_context (ctx); +  release_context (keylist_ctx); + +  return err; +} + + +static const char hlp_config_opt[] = +  "op:       \"config_opt\"\n" +  "component: The component of the option.\n" +  "option:    The name of the option.\n" +  "\n" +  "Response on success:\n" +  "\n" +  "option: Information about the option.\n" +  " String values:\n" +  "  name: The name of the option\n" +  "  description: Localized description of the opt.\n" +  "  argname: Thhe argument name e.g. --verbose\n" +  "  default_description\n" +  "  no_arg_description\n" +  " Number values:\n" +  "  flags: Flags for this option.\n" +  "  level: the level of the description. See gpgme_conf_level_t.\n" +  "  type: The type of the option. See gpgme_conf_type_t.\n" +  "  alt_type: Alternate type of the option. See gpgme_conf_type_t\n" +  " Arg type values: (see desc. below)\n" +  "  default_value: Array of the default value.\n" +  "  no_arg_value: Array of the value if it is not set.\n" +  "  value: Array for the current value if the option is set.\n" +  "\n" +  "If the response is empty the option was not found\n" +  ""; +static gpg_error_t +op_config_opt (cjson_t request, cjson_t result) +{ +  gpg_error_t err; +  gpgme_ctx_t ctx = NULL; +  gpgme_conf_comp_t conf = NULL; +  gpgme_conf_comp_t comp = NULL; +  cjson_t j_tmp; +  char *comp_name = NULL; +  char *opt_name = NULL; + +  ctx = get_context (GPGME_PROTOCOL_GPGCONF); + +  j_tmp = cJSON_GetObjectItem (request, "component"); +  if (!j_tmp || !cjson_is_string (j_tmp)) +    { +      err = gpg_error (GPG_ERR_INV_VALUE); +      goto leave; +    } +  comp_name = j_tmp->valuestring; + + +  j_tmp = cJSON_GetObjectItem (request, "option"); +  if (!j_tmp || !cjson_is_string (j_tmp)) +    { +      err = gpg_error (GPG_ERR_INV_VALUE); +      goto leave; +    } +  opt_name = j_tmp->valuestring; + +  /* Load the config */ +  err = gpgme_op_conf_load (ctx, &conf); +  if (err) +    { +      goto leave; +    } + +  comp = conf; +  for (comp = conf; comp; comp = comp->next) +    { +      gpgme_conf_opt_t opt = NULL; +      int found = 0; +      if (!comp->name || strcmp (comp->name, comp_name)) +        { +          /* Skip components if a single one is specified */ +          continue; +        } +      for (opt = comp->options; opt; opt = opt->next) +        { +          if (!opt->name || strcmp (opt->name, opt_name)) +            { +              /* Skip components if a single one is specified */ +              continue; +            } +          xjson_AddItemToObject (result, "option", conf_opt_to_json (opt)); +          found = 1; +          break; +        } +      if (found) +        break; +    } + +leave: +  gpgme_conf_release (conf); +  release_context (ctx); + +  return err; +} + + +static const char hlp_config[] = +  "op:     \"config\"\n" +  "\n" +  "Optional parameters:\n" +  "component:    Component of entries to list.\n" +  "              Default: all\n" +  "\n" +  "Response on success:\n" +  "   components: Array of the component program configs.\n" +  "     name:         The component name.\n" +  "     description:  Description of the component.\n" +  "     program_name: The absolute path to the program.\n" +  "     options: Array of config options\n" +  "      String values:\n" +  "       name: The name of the option\n" +  "       description: Localized description of the opt.\n" +  "       argname: Thhe argument name e.g. --verbose\n" +  "       default_description\n" +  "       no_arg_description\n" +  "      Number values:\n" +  "       flags: Flags for this option.\n" +  "       level: the level of the description. See gpgme_conf_level_t.\n" +  "       type: The type of the option. See gpgme_conf_type_t.\n" +  "       alt_type: Alternate type of the option. See gpgme_conf_type_t\n" +  "      Arg type values: (see desc. below)\n" +  "       default_value: Array of the default value.\n" +  "       no_arg_value: Array of the value if it is not set.\n" +  "       value: Array for the current value if the option is set.\n" +  "\n" +  "Conf type values are an array of values that are either\n" +  "of type number named \"number\" or of type string,\n" +  "named \"string\".\n" +  "If the type is none the bool value is_none is true.\n" +  ""; +static gpg_error_t +op_config (cjson_t request, cjson_t result) +{ +  gpg_error_t err; +  gpgme_ctx_t ctx = NULL; +  gpgme_conf_comp_t conf = NULL; +  gpgme_conf_comp_t comp = NULL; +  cjson_t j_tmp; +  char *comp_name = NULL; +  cjson_t j_comps = xjson_CreateArray (); + +  ctx = get_context (GPGME_PROTOCOL_GPGCONF); + +  j_tmp = cJSON_GetObjectItem (request, "component"); +  if (j_tmp && cjson_is_string (j_tmp)) +    { +      comp_name = j_tmp->valuestring; +    } +  else if (j_tmp && !cjson_is_string (j_tmp)) +    { +      err = gpg_error (GPG_ERR_INV_VALUE); +      goto leave; +    } + +  /* Load the config */ +  err = gpgme_op_conf_load (ctx, &conf); +  if (err) +    { +      goto leave; +    } + +  comp = conf; +  for (comp = conf; comp; comp = comp->next) +    { +      if (comp_name && comp->name && strcmp (comp->name, comp_name)) +        { +          /* Skip components if a single one is specified */ +          continue; +        } +      cJSON_AddItemToArray (j_comps, conf_comp_to_json (comp)); +    } +  xjson_AddItemToObject (result, "components", j_comps); + +leave: +  gpgme_conf_release (conf); +  release_context (ctx); + +  return err; +} + + -static const char hlp_getmore[] = -  "op:     \"getmore\"\n" +static const char hlp_createkey[] = +  "op:      \"createkey\"\n" +  "userid:  The user id. E.g. \"Foo Bar <[email protected]>\"\n"    "\n"    "Optional parameters:\n" -  "chunksize:  Max number of bytes in the \"data\" object.\n" +  "algo:        Algo of the key as string. See doc for gpg --quick-gen-key.\n" +  "subkey-algo: Algo of the encryption subkey. If ommited the same as algo\n" +  "             is used.\n" +  "             Except for dsa and ed25519 where the according\n" +  "             elg / cv25519 algo will be used as subkey-algo.\n" +  "\n" +  "             If algo is omitted or default or future-default subkey-algo\n" +  "             is ignored.\n" +  "expires:     Seconds from now to expiry as Number. 0 means no expiry.\n" +  "\n" +  "Response on success:\n" +  "fingerprint:   The fingerprint of the created key.\n" +  "\n" +  "Note: This interface does not allow key generation if the userid\n" +  "of the new key already exists in the keyring.\n"; +static gpg_error_t +op_createkey (cjson_t request, cjson_t result) +{ +  gpg_error_t err; +  gpgme_ctx_t ctx = NULL; +  unsigned int flags = GPGME_CREATE_FORCE; /* Always force as the GUI should +                                              handle checks, if required. */ +  unsigned long expires = 0; +  cjson_t j_tmp; +  const char *algo = "default"; +  const char *userid; +  gpgme_genkey_result_t res; +  char *new_fpr = NULL; + +#ifdef GPG_AGENT_ALLOWS_KEYGEN_TRHOUGH_BROWSER +  /* GnuPG forbids keygen through the browser socket so for +     this we create an unrestricted context. +     See GnuPG-Bug-Id: T4010 for more info */ +  ctx = get_context (GPGME_PROTOCOL_OpenPGP); +#else +    err = gpgme_new (&ctx); +  if (err) +    log_fatal ("error creating GPGME context: %s\n", gpg_strerror (err)); +  gpgme_set_protocol (ctx, GPGME_PROTOCOL_OpenPGP); +#endif + +  j_tmp = cJSON_GetObjectItem (request, "algo"); +  if (j_tmp && cjson_is_string (j_tmp)) +    { +      algo = j_tmp->valuestring; +    } + +  j_tmp = cJSON_GetObjectItem (request, "userid"); +  if (!j_tmp || !cjson_is_string (j_tmp)) +    { +      err = gpg_error (GPG_ERR_INV_VALUE); +      goto leave; +    } + +  userid = j_tmp->valuestring; + +  j_tmp = cJSON_GetObjectItem (request, "expires"); +  if (j_tmp) +    { +      if (!cjson_is_number (j_tmp)) +        { +          err = gpg_error (GPG_ERR_INV_VALUE); +          goto leave; +        } +      expires = j_tmp->valueint; + +      if (!expires) +        flags |= GPGME_CREATE_NOEXPIRE; +    } + + +  if ((err = gpgme_op_createkey (ctx, userid, algo, 0, expires, NULL, flags))) +    goto leave; + +  res = gpgme_op_genkey_result (ctx); +  if (!res) +    { +      err = gpg_error (GPG_ERR_GENERAL); +      goto leave; +    } + +  /* Dup the fpr as the result might become invalid after context reuse. */ +  new_fpr = xstrdup (res->fpr); + +  if (algo && strcmp ("default", algo) && strcmp ("future-default", algo)) +    { +      /* We need to add the encryption subkey manually */ +      gpgme_ctx_t keylistctx = create_onetime_context (GPGME_PROTOCOL_OpenPGP); +      gpgme_key_t new_key = NULL; +      char *subkey_algo = NULL; + +      j_tmp = cJSON_GetObjectItem (request, "subkey_algo"); +      if (j_tmp && cjson_is_string (j_tmp)) +        { +          subkey_algo = xstrdup (j_tmp->valuestring); +        } + +      if (!subkey_algo) +        { +          subkey_algo = strdup (algo); +          if (!strncmp ("dsa", subkey_algo, 3)) +            { +              subkey_algo[0] = 'e'; +              subkey_algo[1] = 'l'; +              subkey_algo[2] = 'g'; +            } +          if (!strcmp ("ed25519", subkey_algo)) +            { +              strcpy (subkey_algo, "cv25519"); +            } +        } + +      err = gpgme_get_key (keylistctx, new_fpr, &new_key, 1); +      release_onetime_context (keylistctx); +      if (err) +        { +          gpg_error_object (result, err, "Error finding created key: %s", +                            gpg_strerror (err)); +          xfree (subkey_algo); +          goto leave; +        } + +      err = gpgme_op_createsubkey (ctx, new_key, subkey_algo, +                                   0, expires, flags |= GPGME_CREATE_ENCR); +      xfree (subkey_algo); +      if (err) +        goto leave; +    } + +  xjson_AddStringToObject0 (result, "fingerprint", new_fpr); + +leave: +  xfree (new_fpr); +#ifdef GPG_AGENT_ALLOWS_KEYGEN_TRHOUGH_BROWSER +  release_context (ctx); +#else +  gpgme_release (ctx); +#endif + +  return err; +} + + + +static const char hlp_getmore[] = +  "op:     \"getmore\"\n"    "\n"    "Response on success:\n" -  "type:       Type of the pending data\n" -  "data:       The next chunk of data\n" -  "base64:     Boolean indicating whether data is base64 encoded\n" -  "more:       Optional boolean requesting another \"getmore\"."; +  "response:       base64 encoded json response.\n" +  "more:           Another getmore is required.\n" +  "base64:         boolean if the response is base64 encoded.\n";  static gpg_error_t  op_getmore (cjson_t request, cjson_t result)  { @@ -1372,20 +3113,24 @@ op_getmore (cjson_t request, cjson_t result)    if ((err = get_chunksize (request, &chunksize)))      goto leave; -  /* Adjust the chunksize if we need to do base64 conversion.  */ -  if (pending_data.base64) -    chunksize = (chunksize / 4) * 3; +  /* For the meta data we need 41 bytes: +     {"more":true,"base64":true,"response":""} */ +  chunksize -= 41; + +  /* Adjust the chunksize for the base64 conversion.  */ +  chunksize = (chunksize / 4) * 3;    /* Do we have anything pending?  */    if (!pending_data.buffer)      {        err = gpg_error (GPG_ERR_NO_DATA); -      error_object (result, "Operation not possible: %s", gpg_strerror (err)); +      gpg_error_object (result, err, "Operation not possible: %s", +                        gpg_strerror (err));        goto leave;      } -  xjson_AddStringToObject (result, "type", pending_data.type); -  xjson_AddBoolToObject (result, "base64", pending_data.base64); +  /* We currently always use base64 encoding for simplicity. */ +  xjson_AddBoolToObject (result, "base64", 1);    if (pending_data.written >= pending_data.length)      { @@ -1394,7 +3139,7 @@ op_getmore (cjson_t request, cjson_t result)        gpgme_free (pending_data.buffer);        pending_data.buffer = NULL;        xjson_AddBoolToObject (result, "more", 0); -      err = cjson_AddStringToObject (result, "data", ""); +      err = cjson_AddStringToObject (result, "response", "");      }    else      { @@ -1409,21 +3154,16 @@ op_getmore (cjson_t request, cjson_t result)        c = pending_data.buffer[pending_data.written + n];        pending_data.buffer[pending_data.written + n] = 0; -      if (pending_data.base64) -        err = add_base64_to_object (result, "data", -                                    (pending_data.buffer -                                     + pending_data.written), n); -      else -        err = cjson_AddStringToObject (result, "data", -                                       (pending_data.buffer -                                        + pending_data.written)); +      err = add_base64_to_object (result, "response", +                                  (pending_data.buffer +                                   + pending_data.written), n);        pending_data.buffer[pending_data.written + n] = c;        if (!err)          {            pending_data.written += n;            if (pending_data.written >= pending_data.length)              { -              gpgme_free (pending_data.buffer); +              xfree (pending_data.buffer);                pending_data.buffer = NULL;              }          } @@ -1443,11 +3183,27 @@ static const char hlp_help[] =    "operation is not performned but a string with the documentation\n"    "returned.  To list all operations it is allowed to leave out \"op\" in\n"    "help mode.  Supported values for \"op\" are:\n\n" -  "  encrypt     Encrypt data.\n" +  "  config      Read configuration values.\n" +  "  config_opt  Read a single configuration value.\n"    "  decrypt     Decrypt data.\n" +  "  delete      Delete a key.\n" +  "  encrypt     Encrypt data.\n" +  "  export      Export keys.\n" +  "  createkey   Generate a keypair (OpenPGP only).\n" +  "  import      Import data.\n" +  "  keylist     List keys.\n"    "  sign        Sign data.\n" -  "  getmore     Retrieve remaining data.\n" -  "  help        Help overview."; +  "  verify      Verify data.\n" +  "  version     Get engine information.\n" +  "  getmore     Retrieve remaining data if chunksize was used.\n" +  "  help        Help overview.\n" +  "\n" +  "If the data needs to be transferred in smaller chunks the\n" +  "property \"chunksize\" with an integer value can be added.\n" +  "When \"chunksize\" is set the response (including json) will\n" +  "not be larger then \"chunksize\" but might be smaller.\n" +  "The chunked result will be transferred in base64 encoded chunks\n" +  "using the \"getmore\" operation. See help getmore for more info.";  static gpg_error_t  op_help (cjson_t request, cjson_t result)  { @@ -1484,11 +3240,20 @@ process_request (const char *request)      gpg_error_t (*handler)(cjson_t request, cjson_t result);      const char * const helpstr;    } optbl[] = { -    { "encrypt", op_encrypt, hlp_encrypt }, -    { "decrypt", op_decrypt, hlp_decrypt }, -    { "sign",    op_sign,    hlp_sign }, -    { "getmore", op_getmore, hlp_getmore }, -    { "help",    op_help,    hlp_help }, +    { "config",     op_config,     hlp_config }, +    { "config_opt", op_config_opt, hlp_config_opt }, +    { "encrypt",    op_encrypt,    hlp_encrypt }, +    { "export",     op_export,     hlp_export }, +    { "decrypt",    op_decrypt,    hlp_decrypt }, +    { "delete",     op_delete,     hlp_delete }, +    { "createkey",  op_createkey,  hlp_createkey }, +    { "keylist",    op_keylist,    hlp_keylist }, +    { "import",     op_import,     hlp_import }, +    { "sign",       op_sign,       hlp_sign }, +    { "verify",     op_verify,     hlp_verify }, +    { "version",    op_version,    hlp_version }, +    { "getmore",    op_getmore,    hlp_getmore }, +    { "help",       op_help,       hlp_help },      { NULL }    };    size_t erroff; @@ -1496,8 +3261,9 @@ process_request (const char *request)    cjson_t j_tmp, j_op;    cjson_t response;    int helpmode; +  int is_getmore = 0;    const char *op; -  char *res; +  char *res = NULL;    int idx;    response = xjson_CreateObject (); @@ -1541,7 +3307,7 @@ process_request (const char *request)        else          {            gpg_error_t err; - +          is_getmore = optbl[idx].handler == op_getmore;            /* If this is not the "getmore" command and we have any             * pending data release that data.  */            if (pending_data.buffer && optbl[idx].handler != op_getmore) @@ -1558,8 +3324,8 @@ process_request (const char *request)                    || strcmp (j_tmp->valuestring, "error"))                  {                    /* No error type response - provide a generic one.  */ -                  error_object (response, "Operation failed: %s", -                                gpg_strerror (err)); +                  gpg_error_object (response, err, "Operation failed: %s", +                                    gpg_strerror (err));                  }                xjson_AddStringToObject (response, "op", op); @@ -1573,14 +3339,37 @@ process_request (const char *request)      }   leave: -  cJSON_Delete (json); -  if (opt_interactive) -    res = cJSON_Print (response); +  if (is_getmore) +    { +      /* For getmore we bypass the encode_and_chunk. */ +      if (opt_interactive) +        res = cJSON_Print (response); +      else +        res = cJSON_PrintUnformatted (response); +    }    else -    res = cJSON_PrintUnformatted (response); +    res = encode_and_chunk (json, response);    if (!res) -    log_error ("Printing JSON data failed\n"); +    { +      cjson_t err_obj; + +      log_error ("printing JSON data failed\n"); + +      err_obj = error_object (NULL, "Printing JSON data failed"); +      if (opt_interactive) +        res = cJSON_Print (err_obj); +      res = cJSON_PrintUnformatted (err_obj); +      cJSON_Delete (err_obj); +    } + +  cJSON_Delete (json);    cJSON_Delete (response); + +  if (!res) +    { +      /* Can't happen unless we created a broken error_object above */ +      return xtrystrdup ("Bug: Fatal error in process request\n"); +    }    return res;  } @@ -1952,7 +3741,7 @@ native_messaging_repl (void)          }        /* Read request.  */ -      request = xtrymalloc (nrequest); +      request = xtrymalloc (nrequest + 1);        if (!request)          {            err = gpg_error_from_syserror (); @@ -1977,6 +3766,7 @@ native_messaging_repl (void)          }        else /* Process request  */          { +          request[n] = '\0'; /* Ensure that request has an end */            if (opt_debug)              log_debug ("request='%s'\n", request);            xfree (response); @@ -2015,6 +3805,10 @@ native_messaging_repl (void)            log_error ("error writing request: %s\n", gpg_strerror (err));            break;          } +      xfree (response); +      response = NULL; +      xfree (request); +      request = NULL;      }    xfree (response); @@ -2079,6 +3873,8 @@ main (int argc, char *argv[])    };    gpgrt_argparse_t pargs = { &argc, &argv}; +  int log_file_set = 0; +    gpgrt_set_strusage (my_strusage);  #ifdef HAVE_SETLOCALE @@ -2115,12 +3911,24 @@ main (int argc, char *argv[])    if (!opt_debug)      { +      /* Handling is similar to GPGME_DEBUG */        const char *s = getenv ("GPGME_JSON_DEBUG"); +      const char *s1; +        if (s && atoi (s) > 0) -        opt_debug = 1; +        { +          opt_debug = 1; +          s1 = strchr (s, PATHSEP_C); +          if (s1 && strlen (s1) > 2) +            { +              s1++; +              log_set_file (s1); +              log_file_set = 1; +            } +        }      } -  if (opt_debug) +  if (opt_debug && !log_file_set)      {        const char *home = getenv ("HOME");        char *file = xstrconcat ("socket://", diff --git a/src/gpgme.c b/src/gpgme.c index 82d67478..2d829d9b 100644 --- a/src/gpgme.c +++ b/src/gpgme.c @@ -249,6 +249,7 @@ gpgme_release (gpgme_ctx_t ctx)    free (ctx->lc_messages);    free (ctx->override_session_key);    free (ctx->request_origin); +  free (ctx->auto_key_locate);    _gpgme_engine_info_release (ctx->engine_info);    ctx->engine_info = NULL;    DESTROY_LOCK (ctx->lock); @@ -542,6 +543,17 @@ gpgme_set_ctx_flag (gpgme_ctx_t ctx, const char *name, const char *value)      {        ctx->no_symkey_cache = abool;      } +  else if (!strcmp (name, "ignore-mdc-error")) +    { +      ctx->ignore_mdc_error = abool; +    } +  else if (!strcmp (name, "auto-key-locate")) +    { +      free (ctx->auto_key_locate); +      ctx->auto_key_locate = strdup (value); +      if (!ctx->auto_key_locate) +        err = gpg_error_from_syserror (); +    }    else      err = gpg_error (GPG_ERR_UNKNOWN_NAME); @@ -591,6 +603,14 @@ gpgme_get_ctx_flag (gpgme_ctx_t ctx, const char *name)      {        return ctx->no_symkey_cache? "1":"";      } +  else if (!strcmp (name, "ignore-mdc-error")) +    { +      return ctx->ignore_mdc_error? "1":""; +    } +  else if (!strcmp (name, "auto-key-locate")) +    { +      return ctx->auto_key_locate? ctx->auto_key_locate : ""; +    }    else      return NULL;  } diff --git a/src/gpgme.h.in b/src/gpgme.h.in index 49fafb90..35968017 100644 --- a/src/gpgme.h.in +++ b/src/gpgme.h.in @@ -404,7 +404,9 @@ typedef unsigned int gpgme_export_mode_t;  /* Flags for the audit log functions.  */ +#define GPGME_AUDITLOG_DEFAULT   0  #define GPGME_AUDITLOG_HTML      1 +#define GPGME_AUDITLOG_DIAG      2  #define GPGME_AUDITLOG_WITH_HELP 128 @@ -1178,6 +1180,8 @@ gpgme_error_t gpgme_data_new_from_cbs (gpgme_data_t *dh,  gpgme_error_t gpgme_data_new_from_fd (gpgme_data_t *dh, int fd);  gpgme_error_t gpgme_data_new_from_stream (gpgme_data_t *dh, FILE *stream); +gpgme_error_t gpgme_data_new_from_estream (gpgme_data_t *r_dh, +                                           gpgrt_stream_t stream);  /* Return the encoding attribute of the data buffer DH */  gpgme_data_encoding_t gpgme_data_get_encoding (gpgme_data_t dh); @@ -1365,8 +1369,12 @@ struct _gpgme_op_decrypt_result    /* The message claims that the content is a MIME object.  */    unsigned int is_mime : 1; +  /* The message was made by a legacy algorithm without any integrity +   * protection.  This might be an old but legitimate message. */ +  unsigned int legacy_cipher_nomdc : 1; +    /* Internal to GPGME, do not use.  */ -  int _unused : 29; +  int _unused : 28;    gpgme_recipient_t recipients; diff --git a/src/keysign.c b/src/keysign.c index c2fcabb5..5e497935 100644 --- a/src/keysign.c +++ b/src/keysign.c @@ -171,7 +171,7 @@ keysign_start (gpgme_ctx_t ctx, int synchronous,    if (ctx->passphrase_cb)      {        err = _gpgme_engine_set_command_handler -        (ctx->engine, _gpgme_passphrase_command_handler, ctx, NULL); +        (ctx->engine, _gpgme_passphrase_command_handler, ctx);        if (err)          return err;      } @@ -85,7 +85,8 @@ gpgme_error_t _gpgme_verify_status_handler (void *priv,  /* From decrypt.c.  */ -gpgme_error_t _gpgme_op_decrypt_init_result (gpgme_ctx_t ctx); +gpgme_error_t _gpgme_op_decrypt_init_result (gpgme_ctx_t ctx, +                                             gpgme_data_t plaintext);  gpgme_error_t _gpgme_decrypt_status_handler (void *priv,  					     gpgme_status_code_t code,  					     char *args); diff --git a/src/passwd.c b/src/passwd.c index 5bd67a52..6c03002b 100644 --- a/src/passwd.c +++ b/src/passwd.c @@ -151,7 +151,7 @@ passwd_start (gpgme_ctx_t ctx, int synchronous, gpgme_key_t key,    if (ctx->passphrase_cb)      {        err = _gpgme_engine_set_command_handler -        (ctx->engine, _gpgme_passphrase_command_handler, ctx, NULL); +        (ctx->engine, _gpgme_passphrase_command_handler, ctx);        if (err)          return err;      } @@ -449,7 +449,7 @@ sign_start (gpgme_ctx_t ctx, int synchronous, gpgme_data_t plain,    if (ctx->passphrase_cb)      {        err = _gpgme_engine_set_command_handler -	(ctx->engine, _gpgme_passphrase_command_handler, ctx, NULL); +	(ctx->engine, _gpgme_passphrase_command_handler, ctx);        if (err)  	return err;      } | 
