summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJinWang An <jinwang.an@samsung.com>2021-12-01 16:54:37 +0900
committerJinWang An <jinwang.an@samsung.com>2021-12-01 16:54:37 +0900
commitfd4d11c14daa6a54c81202dffc02cc419fa67568 (patch)
treee14910ad034ec7b4d10552a13e219174ccd4946d /src
parent442a1386c9708114c2b721afea60d5593e36c423 (diff)
downloadgpgme-fd4d11c14daa6a54c81202dffc02cc419fa67568.tar.gz
gpgme-fd4d11c14daa6a54c81202dffc02cc419fa67568.tar.bz2
gpgme-fd4d11c14daa6a54c81202dffc02cc419fa67568.zip
Imported Upstream version 1.11.0upstream/1.11.0
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am9
-rw-r--r--src/Makefile.in29
-rw-r--r--src/cJSON.c1404
-rw-r--r--src/cJSON.h187
-rw-r--r--src/context.h6
-rw-r--r--src/conversion.c46
-rw-r--r--src/decrypt.c81
-rw-r--r--src/encrypt-sign.c111
-rw-r--r--src/encrypt.c106
-rw-r--r--src/engine-assuan.c35
-rw-r--r--src/engine-backend.h3
-rw-r--r--src/engine-g13.c1
-rw-r--r--src/engine-gpg.c237
-rw-r--r--src/engine-gpgconf.c1
-rw-r--r--src/engine-gpgsm.c93
-rw-r--r--src/engine-spawn.c4
-rw-r--r--src/engine-uiserver.c60
-rw-r--r--src/engine.c30
-rw-r--r--src/engine.h3
-rw-r--r--src/get-env.c76
-rw-r--r--src/gpgme-json.c1772
-rw-r--r--src/gpgme-tool.c2
-rw-r--r--src/gpgme-w32spawn.c4
-rw-r--r--src/gpgme.c29
-rw-r--r--src/gpgme.def5
-rw-r--r--src/gpgme.h.in388
-rw-r--r--src/import.c9
-rw-r--r--src/keylist.c14
-rw-r--r--src/libgpgme.vers9
-rw-r--r--src/op-support.c18
-rw-r--r--src/ops.h4
-rw-r--r--src/priv-io.h2
-rw-r--r--src/util.h5
-rw-r--r--src/verify.c33
-rw-r--r--src/versioninfo.rc.in2
-rw-r--r--src/w32-io.c4
36 files changed, 4498 insertions, 324 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index ce6f1d4..c2d4a84 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -26,7 +26,7 @@ m4datadir = $(datadir)/aclocal
m4data_DATA = gpgme.m4
nodist_include_HEADERS = gpgme.h
-bin_PROGRAMS = gpgme-tool
+bin_PROGRAMS = gpgme-tool gpgme-json
if BUILD_W32_GLIB
ltlib_gpgme_glib = libgpgme-glib.la
@@ -95,13 +95,18 @@ if BUILD_W32_GLIB
libgpgme_glib_la_SOURCES = $(main_sources) w32-glib-io.c
endif
-# We use a global CFLAGS setting for all library
+# We use a global CFLAGS setting for all libraries
# versions, because then every object file is only compiled once.
AM_CFLAGS = @LIBASSUAN_CFLAGS@ @GLIB_CFLAGS@
gpgme_tool_SOURCES = gpgme-tool.c argparse.c argparse.h
gpgme_tool_LDADD = libgpgme.la @LIBASSUAN_LIBS@
+gpgme_json_SOURCES = gpgme-json.c cJSON.c cJSON.h
+gpgme_json_LDADD = -lm libgpgme.la $(GPG_ERROR_LIBS)
+# We use -no-install temporary during development.
+gpgme_json_LDFLAGS = -no-install
+
if HAVE_W32_SYSTEM
# Windows provides us with an endless stream of Tough Love. To spawn
diff --git a/src/Makefile.in b/src/Makefile.in
index dfdf550..5eac9e1 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -100,14 +100,14 @@ PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
-bin_PROGRAMS = gpgme-tool$(EXEEXT)
+bin_PROGRAMS = gpgme-tool$(EXEEXT) gpgme-json$(EXEEXT)
@HAVE_W32CE_SYSTEM_TRUE@am__append_1 = w32-ce.h w32-ce.c
@HAVE_W32CE_SYSTEM_FALSE@@HAVE_W32_SYSTEM_TRUE@libexec_PROGRAMS = gpgme-w32spawn$(EXEEXT)
subdir = src
DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
$(top_srcdir)/build-aux/mkinstalldirs \
$(srcdir)/versioninfo.rc.in $(srcdir)/gpgme.h.in \
- $(srcdir)/gpgme-config.in ttyname_r.c setenv.c stpcpy.c \
+ $(srcdir)/gpgme-config.in stpcpy.c setenv.c ttyname_r.c \
$(top_srcdir)/build-aux/depcomp
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \
@@ -231,6 +231,13 @@ libgpgme_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
$(libgpgme_la_LDFLAGS) $(LDFLAGS) -o $@
PROGRAMS = $(bin_PROGRAMS) $(libexec_PROGRAMS)
+am_gpgme_json_OBJECTS = gpgme-json.$(OBJEXT) cJSON.$(OBJEXT)
+gpgme_json_OBJECTS = $(am_gpgme_json_OBJECTS)
+am__DEPENDENCIES_2 =
+gpgme_json_DEPENDENCIES = libgpgme.la $(am__DEPENDENCIES_2)
+gpgme_json_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(gpgme_json_LDFLAGS) $(LDFLAGS) -o $@
am_gpgme_tool_OBJECTS = gpgme-tool.$(OBJEXT) argparse.$(OBJEXT)
gpgme_tool_OBJECTS = $(am_gpgme_tool_OBJECTS)
gpgme_tool_DEPENDENCIES = libgpgme.la
@@ -273,10 +280,10 @@ am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
am__v_CCLD_0 = @echo " CCLD " $@;
am__v_CCLD_1 =
SOURCES = $(libgpgme_glib_la_SOURCES) $(libgpgme_la_SOURCES) \
- $(gpgme_tool_SOURCES) gpgme-w32spawn.c
+ $(gpgme_json_SOURCES) $(gpgme_tool_SOURCES) gpgme-w32spawn.c
DIST_SOURCES = $(am__libgpgme_glib_la_SOURCES_DIST) \
- $(am__libgpgme_la_SOURCES_DIST) $(gpgme_tool_SOURCES) \
- gpgme-w32spawn.c
+ $(am__libgpgme_la_SOURCES_DIST) $(gpgme_json_SOURCES) \
+ $(gpgme_tool_SOURCES) gpgme-w32spawn.c
am__can_run_installinfo = \
case $$AM_UPDATE_INFO_DIR in \
n|no|NO) false;; \
@@ -551,11 +558,15 @@ main_sources = \
libgpgme_la_SOURCES = $(main_sources) $(system_components_not_extra)
@BUILD_W32_GLIB_TRUE@libgpgme_glib_la_SOURCES = $(main_sources) w32-glib-io.c
-# We use a global CFLAGS setting for all library
+# We use a global CFLAGS setting for all libraries
# versions, because then every object file is only compiled once.
AM_CFLAGS = @LIBASSUAN_CFLAGS@ @GLIB_CFLAGS@
gpgme_tool_SOURCES = gpgme-tool.c argparse.c argparse.h
gpgme_tool_LDADD = libgpgme.la @LIBASSUAN_LIBS@
+gpgme_json_SOURCES = gpgme-json.c cJSON.c cJSON.h
+gpgme_json_LDADD = -lm libgpgme.la $(GPG_ERROR_LIBS)
+# We use -no-install temporary during development.
+gpgme_json_LDFLAGS = -no-install
@HAVE_W32_SYSTEM_TRUE@RCCOMPILE = $(RC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES)
@HAVE_W32_SYSTEM_TRUE@LTRCCOMPILE = $(LIBTOOL) --mode=compile --tag=RC $(RCCOMPILE)
@HAVE_W32_SYSTEM_TRUE@SUFFIXES = .rc .lo
@@ -768,6 +779,10 @@ clean-libexecPROGRAMS:
echo " rm -f" $$list; \
rm -f $$list
+gpgme-json$(EXEEXT): $(gpgme_json_OBJECTS) $(gpgme_json_DEPENDENCIES) $(EXTRA_gpgme_json_DEPENDENCIES)
+ @rm -f gpgme-json$(EXEEXT)
+ $(AM_V_CCLD)$(gpgme_json_LINK) $(gpgme_json_OBJECTS) $(gpgme_json_LDADD) $(LIBS)
+
gpgme-tool$(EXEEXT): $(gpgme_tool_OBJECTS) $(gpgme_tool_DEPENDENCIES) $(EXTRA_gpgme_tool_DEPENDENCIES)
@rm -f gpgme-tool$(EXEEXT)
$(AM_V_CCLD)$(LINK) $(gpgme_tool_OBJECTS) $(gpgme_tool_LDADD) $(LIBS)
@@ -824,6 +839,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/assuan-support.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ath.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/b64dec.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cJSON.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/conversion.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/data-compat.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/data-fd.Plo@am__quote@
@@ -854,6 +870,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/get-env.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getauditlog.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gpgconf.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gpgme-json.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gpgme-tool.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gpgme-w32spawn.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gpgme.Plo@am__quote@
diff --git a/src/cJSON.c b/src/cJSON.c
new file mode 100644
index 0000000..cf0cb13
--- /dev/null
+++ b/src/cJSON.c
@@ -0,0 +1,1404 @@
+/* cJSON.c - JSON parser in C.
+ * Copyright (c) 2009 Dave Gamble
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * Note that this code has been modified from the original code taken
+ * from cjson-code-58.zip.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+#include <stdlib.h>
+#include <float.h>
+#include <limits.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "cJSON.h"
+
+/* We use malloc function wrappers from gpgrt (aka libgpg-error). */
+#if 1
+# 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))
+# define xtrycalloc(a,b) calloc ((a), (b))
+# define xtrystrdup(a) strdup ((a))
+# define xfree(a) free ((a))
+#endif
+
+
+static int
+cJSON_strcasecmp (const char *s1, const char *s2)
+{
+ if (!s1)
+ return (s1 == s2) ? 0 : 1;
+ if (!s2)
+ return 1;
+ for (; tolower (*(const unsigned char *)s1)
+ == tolower (*(const unsigned char *) s2); ++s1, ++s2)
+ if (*s1 == 0)
+ return 0;
+ return tolower (*(const unsigned char *) s1) -
+ tolower (*(const unsigned char *) s2);
+}
+
+/* Internal constructor. */
+static cJSON *
+cJSON_New_Item (void)
+{
+ return xtrycalloc (1, sizeof (cJSON));
+}
+
+/* Delete a cJSON structure. (Does not clobber ERRNO). */
+void
+cJSON_Delete (cJSON * c)
+{
+ cJSON *next;
+ int save_errno;
+
+ if (!c)
+ return;
+
+ save_errno = errno;
+ while (c)
+ {
+ next = c->next;
+ if (!(c->type & cJSON_IsReference) && c->child)
+ cJSON_Delete (c->child);
+ if (!(c->type & cJSON_IsReference) && c->valuestring)
+ xfree (c->valuestring);
+ if (c->string)
+ xfree (c->string);
+ xfree (c);
+ c = next;
+ }
+ errno = save_errno;
+}
+
+/* Parse the input text to generate a number, and populate the result
+ * into item. */
+static const char *
+parse_number (cJSON * item, const char *num)
+{
+ double n = 0, sign = 1, scale = 0;
+ int subscale = 0, signsubscale = 1;
+
+ if (*num == '-')
+ sign = -1, num++; /* Has sign? */
+ if (*num == '0')
+ num++; /* is zero */
+ if (*num >= '1' && *num <= '9')
+ do
+ n = (n * 10.0) + (*num++ - '0');
+ while (*num >= '0' && *num <= '9'); /* Number? */
+ if (*num == '.' && num[1] >= '0' && num[1] <= '9')
+ {
+ num++;
+ do
+ n = (n * 10.0) + (*num++ - '0'), scale--;
+ while (*num >= '0' && *num <= '9');
+ } /* Fractional part? */
+ if (*num == 'e' || *num == 'E') /* Exponent? */
+ {
+ num++;
+ if (*num == '+')
+ num++;
+ else if (*num == '-')
+ signsubscale = -1, num++; /* With sign? */
+ while (*num >= '0' && *num <= '9')
+ subscale = (subscale * 10) + (*num++ - '0'); /* Number? */
+ }
+
+ /* number = +/- number.fraction * 10^+/- exponent */
+ n = sign * n * pow (10.0, (scale + subscale * signsubscale));
+
+ item->valuedouble = n;
+ item->valueint = (int) n;
+ item->type = cJSON_Number;
+ return num;
+}
+
+/* Render the number nicely from the given item into a string. */
+static char *
+print_number (cJSON * item)
+{
+ char *str;
+ double d = item->valuedouble;
+ if (fabs (((double) item->valueint) - d) <= DBL_EPSILON && d <= INT_MAX
+ && d >= INT_MIN)
+ {
+ /* 2^64+1 can be represented in 21 chars. */
+ str = xtrymalloc (21);
+ if (str)
+ sprintf (str, "%d", item->valueint);
+ }
+ else
+ {
+ str = xtrymalloc (64); /* This is a nice tradeoff. */
+ if (str)
+ {
+ if (fabs (floor (d) - d) <= DBL_EPSILON && fabs (d) < 1.0e60)
+ sprintf (str, "%.0f", d);
+ else if (fabs (d) < 1.0e-6 || fabs (d) > 1.0e9)
+ sprintf (str, "%e", d);
+ else
+ sprintf (str, "%f", d);
+ }
+ }
+ return str;
+}
+
+static unsigned
+parse_hex4 (const char *str)
+{
+ unsigned h = 0;
+ if (*str >= '0' && *str <= '9')
+ h += (*str) - '0';
+ else if (*str >= 'A' && *str <= 'F')
+ h += 10 + (*str) - 'A';
+ else if (*str >= 'a' && *str <= 'f')
+ h += 10 + (*str) - 'a';
+ else
+ return 0;
+ h = h << 4;
+ str++;
+ if (*str >= '0' && *str <= '9')
+ h += (*str) - '0';
+ else if (*str >= 'A' && *str <= 'F')
+ h += 10 + (*str) - 'A';
+ else if (*str >= 'a' && *str <= 'f')
+ h += 10 + (*str) - 'a';
+ else
+ return 0;
+ h = h << 4;
+ str++;
+ if (*str >= '0' && *str <= '9')
+ h += (*str) - '0';
+ else if (*str >= 'A' && *str <= 'F')
+ h += 10 + (*str) - 'A';
+ else if (*str >= 'a' && *str <= 'f')
+ h += 10 + (*str) - 'a';
+ else
+ return 0;
+ h = h << 4;
+ str++;
+ if (*str >= '0' && *str <= '9')
+ h += (*str) - '0';
+ else if (*str >= 'A' && *str <= 'F')
+ h += 10 + (*str) - 'A';
+ else if (*str >= 'a' && *str <= 'f')
+ h += 10 + (*str) - 'a';
+ else
+ return 0;
+ return h;
+}
+
+/* Parse the input text into an unescaped cstring, and populate item. */
+static const unsigned char firstByteMark[7] =
+ { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
+static const char *
+parse_string (cJSON * item, const char *str, const char **ep)
+{
+ const char *ptr = str + 1;
+ char *ptr2;
+ char *out;
+ int len = 0;
+ unsigned uc, uc2;
+ if (*str != '\"')
+ {
+ *ep = str;
+ return 0;
+ } /* not a string! */
+
+ while (*ptr != '\"' && *ptr && ++len)
+ if (*ptr++ == '\\')
+ ptr++; /* Skip escaped quotes. */
+
+ out = xtrymalloc (len + 1); /* This is how long we need for the
+ string, roughly. */
+ if (!out)
+ return 0;
+
+ ptr = str + 1;
+ ptr2 = out;
+ while (*ptr != '\"' && *ptr)
+ {
+ if (*ptr != '\\')
+ *ptr2++ = *ptr++;
+ else
+ {
+ ptr++;
+ switch (*ptr)
+ {
+ case 'b':
+ *ptr2++ = '\b';
+ break;
+ case 'f':
+ *ptr2++ = '\f';
+ break;
+ case 'n':
+ *ptr2++ = '\n';
+ break;
+ case 'r':
+ *ptr2++ = '\r';
+ break;
+ case 't':
+ *ptr2++ = '\t';
+ break;
+ case 'u': /* transcode utf16 to utf8. */
+ uc = parse_hex4 (ptr + 1);
+ ptr += 4; /* get the unicode char. */
+
+ if ((uc >= 0xDC00 && uc <= 0xDFFF) || uc == 0)
+ 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;
+ if (uc2 < 0xDC00 || uc2 > 0xDFFF)
+ break; /* invalid second-half of surrogate. */
+ uc = 0x10000 + (((uc & 0x3FF) << 10) | (uc2 & 0x3FF));
+ }
+
+ len = 4;
+ if (uc < 0x80)
+ len = 1;
+ else if (uc < 0x800)
+ len = 2;
+ else if (uc < 0x10000)
+ len = 3;
+ ptr2 += len;
+
+ switch (len)
+ {
+ case 4:
+ *--ptr2 = ((uc | 0x80) & 0xBF);
+ uc >>= 6;
+ case 3:
+ *--ptr2 = ((uc | 0x80) & 0xBF);
+ uc >>= 6;
+ case 2:
+ *--ptr2 = ((uc | 0x80) & 0xBF);
+ uc >>= 6;
+ case 1:
+ *--ptr2 = (uc | firstByteMark[len]);
+ }
+ ptr2 += len;
+ break;
+ default:
+ *ptr2++ = *ptr;
+ break;
+ }
+ ptr++;
+ }
+ }
+ *ptr2 = 0;
+ if (*ptr == '\"')
+ ptr++;
+ item->valuestring = out;
+ item->type = cJSON_String;
+ return ptr;
+}
+
+/* Render the cstring provided to an escaped version that can be printed. */
+static char *
+print_string_ptr (const char *str)
+{
+ const char *ptr;
+ char *ptr2, *out;
+ int len = 0;
+ unsigned char token;
+
+ if (!str)
+ return xtrystrdup ("");
+ ptr = str;
+ while ((token = *ptr) && ++len)
+ {
+ if (strchr ("\"\\\b\f\n\r\t", token))
+ len++;
+ else if (token < 32)
+ len += 5;
+ ptr++;
+ }
+
+ out = xtrymalloc (len + 3);
+ if (!out)
+ return 0;
+
+ ptr2 = out;
+ ptr = str;
+ *ptr2++ = '\"';
+ while (*ptr)
+ {
+ if ((unsigned char) *ptr > 31 && *ptr != '\"' && *ptr != '\\')
+ *ptr2++ = *ptr++;
+ else
+ {
+ *ptr2++ = '\\';
+ switch (token = *ptr++)
+ {
+ case '\\':
+ *ptr2++ = '\\';
+ break;
+ case '\"':
+ *ptr2++ = '\"';
+ break;
+ case '\b':
+ *ptr2++ = 'b';
+ break;
+ case '\f':
+ *ptr2++ = 'f';
+ break;
+ case '\n':
+ *ptr2++ = 'n';
+ break;
+ case '\r':
+ *ptr2++ = 'r';
+ break;
+ case '\t':
+ *ptr2++ = 't';
+ break;
+ default:
+ sprintf (ptr2, "u%04x", token);
+ ptr2 += 5;
+ break; /* escape and print */
+ }
+ }
+ }
+ *ptr2++ = '\"';
+ *ptr2++ = 0;
+ return out;
+}
+
+/* Invote print_string_ptr (which is useful) on an item. */
+static char *
+print_string (cJSON * item)
+{
+ return print_string_ptr (item->valuestring);
+}
+
+/* Predeclare these prototypes. */
+static const char *parse_value (cJSON * item, const char *value,
+ const char **ep);
+static char *print_value (cJSON * item, int depth, int fmt);
+static const char *parse_array (cJSON * item, const char *value,
+ const char **ep);
+static char *print_array (cJSON * item, int depth, int fmt);
+static const char *parse_object (cJSON * item, const char *value,
+ const char **ep);
+static char *print_object (cJSON * item, int depth, int fmt);
+
+/* Utility to jump whitespace and cr/lf */
+static const char *
+skip (const char *in)
+{
+ while (in && *in && (unsigned char) *in <= 32)
+ in++;
+ return in;
+}
+
+/* Parse an object - create a new root, and populate. */
+cJSON *
+cJSON_ParseWithOpts (const char *value, const char **return_parse_end,
+ int require_null_terminated, size_t *r_erroff)
+{
+ const char *end = 0;
+ const char *ep = 0;
+ cJSON *c;
+
+ if (r_erroff)
+ *r_erroff = 0;
+
+ c = cJSON_New_Item ();
+ if (!c)
+ return NULL; /* memory fail */
+
+ end = parse_value (c, skip (value), &ep);
+ if (!end)
+ {
+ cJSON_Delete (c);
+ errno = EINVAL;
+ if (r_erroff)
+ *r_erroff = ep - value;
+ return 0;
+ } /* parse failure. ep is set. */
+
+ /* if we require null-terminated JSON without appended garbage, skip
+ and then check for a null terminator */
+ if (require_null_terminated)
+ {
+ end = skip (end);
+ if (*end)
+ {
+ cJSON_Delete (c);
+ ep = end;
+ errno = EINVAL;
+ if (r_erroff)
+ *r_erroff = ep - value;
+ return 0;
+ }
+ }
+ if (return_parse_end)
+ *return_parse_end = end;
+ return c;
+}
+
+/* Default options for cJSON_Parse */
+cJSON *
+cJSON_Parse (const char *value, size_t *r_erroff)
+{
+ return cJSON_ParseWithOpts (value, 0, 0, r_erroff);
+}
+
+/* Render a cJSON item/entity/structure to text. */
+char *
+cJSON_Print (cJSON * item)
+{
+ return print_value (item, 0, 1);
+}
+
+char *
+cJSON_PrintUnformatted (cJSON * item)
+{
+ return print_value (item, 0, 0);
+}
+
+/* Parser core - when encountering text, process appropriately. */
+static const char *
+parse_value (cJSON * item, const char *value, const char **ep)
+{
+ if (!value)
+ return 0; /* Fail on null. */
+ if (!strncmp (value, "null", 4))
+ {
+ item->type = cJSON_NULL;
+ return value + 4;
+ }
+ if (!strncmp (value, "false", 5))
+ {
+ item->type = cJSON_False;
+ return value + 5;
+ }
+ if (!strncmp (value, "true", 4))
+ {
+ item->type = cJSON_True;
+ item->valueint = 1;
+ return value + 4;
+ }
+ if (*value == '\"')
+ {
+ return parse_string (item, value, ep);
+ }
+ if (*value == '-' || (*value >= '0' && *value <= '9'))
+ {
+ return parse_number (item, value);
+ }
+ if (*value == '[')
+ {
+ return parse_array (item, value, ep);
+ }
+ if (*value == '{')
+ {
+ return parse_object (item, value, ep);
+ }
+
+ *ep = value;
+ return 0; /* failure. */
+}
+
+/* Render a value to text. */
+static char *
+print_value (cJSON * item, int depth, int fmt)
+{
+ char *out = 0;
+ if (!item)
+ return 0;
+ switch ((item->type) & 255)
+ {
+ case cJSON_NULL:
+ out = xtrystrdup ("null");
+ break;
+ case cJSON_False:
+ out = xtrystrdup ("false");
+ break;
+ case cJSON_True:
+ out = xtrystrdup ("true");
+ break;
+ case cJSON_Number:
+ out = print_number (item);
+ break;
+ case cJSON_String:
+ out = print_string (item);
+ break;
+ case cJSON_Array:
+ out = print_array (item, depth, fmt);
+ break;
+ case cJSON_Object:
+ out = print_object (item, depth, fmt);
+ break;
+ }
+ return out;
+}
+
+/* Build an array from input text. */
+static const char *
+parse_array (cJSON * item, const char *value, const char **ep)
+{
+ cJSON *child;
+ if (*value != '[')
+ {
+ *ep = value;
+ return 0;
+ } /* not an array! */
+
+ item->type = cJSON_Array;
+ value = skip (value + 1);
+ if (*value == ']')
+ return value + 1; /* empty array. */
+
+ item->child = child = cJSON_New_Item ();
+ if (!item->child)
+ return 0; /* memory fail */
+ /* skip any spacing, get the value. */
+ value = skip (parse_value (child, skip (value), ep));
+ if (!value)
+ return 0;
+
+ while (*value == ',')
+ {
+ cJSON *new_item;
+ if (!(new_item = cJSON_New_Item ()))
+ return 0; /* memory fail */
+ child->next = new_item;
+ new_item->prev = child;
+ child = new_item;
+ value = skip (parse_value (child, skip (value + 1), ep));
+ if (!value)
+ return 0; /* memory fail */
+ }
+
+ if (*value == ']')
+ return value + 1; /* end of array */
+ *ep = value;
+ return 0; /* malformed. */
+}
+
+/* Render an array to text */
+static char *
+print_array (cJSON * item, int depth, int fmt)
+{
+ char **entries;
+ char *out = 0, *ptr, *ret;
+ int len = 5;
+ cJSON *child = item->child;
+ int numentries = 0, i = 0, fail = 0;
+
+ /* How many entries in the array? */
+ while (child)
+ numentries++, child = child->next;
+ /* Explicitly handle numentries==0 */
+ if (!numentries)
+ {
+ out = xtrymalloc (3);
+ if (out)
+ strcpy (out, "[]");
+ return out;
+ }
+ /* Allocate an array to hold the values for each */
+ entries = xtrymalloc (numentries * sizeof (char *));
+ if (!entries)
+ return 0;
+ memset (entries, 0, numentries * sizeof (char *));
+ /* Retrieve all the results: */
+ child = item->child;
+ while (child && !fail)
+ {
+ ret = print_value (child, depth + 1, fmt);
+ entries[i++] = ret;
+ if (ret)
+ len += strlen (ret) + 2 + (fmt ? 1 : 0);
+ else
+ fail = 1;
+ child = child->next;
+ }
+
+ /* If we didn't fail, try to xtrymalloc the output string */
+ if (!fail)
+ out = xtrymalloc (len);
+ /* If that fails, we fail. */
+ if (!out)
+ fail = 1;
+
+ /* Handle failure. */
+ if (fail)
+ {
+ for (i = 0; i < numentries; i++)
+ if (entries[i])
+ xfree (entries[i]);
+ xfree (entries);
+ return 0;
+ }
+
+ /* Compose the output array. */
+ *out = '[';
+ ptr = out + 1;
+ *ptr = 0;
+ for (i = 0; i < numentries; i++)
+ {
+ strcpy (ptr, entries[i]);
+ ptr += strlen (entries[i]);
+ if (i != numentries - 1)
+ {
+ *ptr++ = ',';
+ if (fmt)
+ *ptr++ = ' ';
+ *ptr = 0;
+ }
+ xfree (entries[i]);
+ }
+ xfree (entries);
+ *ptr++ = ']';
+ *ptr++ = 0;
+ return out;
+}
+
+/* Build an object from the text. */
+static const char *
+parse_object (cJSON * item, const char *value, const char **ep)
+{
+ cJSON *child;
+ if (*value != '{')
+ {
+ *ep = value;
+ return 0;
+ } /* not an object! */
+
+ item->type = cJSON_Object;
+ value = skip (value + 1);
+ if (*value == '}')
+ return value + 1; /* empty array. */
+
+ item->child = child = cJSON_New_Item ();
+ if (!item->child)
+ return 0;
+ value = skip (parse_string (child, skip (value), ep));
+ if (!value)
+ return 0;
+ child->string = child->valuestring;
+ child->valuestring = 0;
+ if (*value != ':')
+ {
+ *ep = value;
+ return 0;
+ } /* fail! */
+ /* skip any spacing, get the value. */
+ value = skip (parse_value (child, skip (value + 1), ep));
+ if (!value)
+ return 0;
+
+ while (*value == ',')
+ {
+ cJSON *new_item;
+ if (!(new_item = cJSON_New_Item ()))
+ return 0; /* memory fail */
+ child->next = new_item;
+ new_item->prev = child;
+ child = new_item;
+ value = skip (parse_string (child, skip (value + 1), ep));
+ if (!value)
+ return 0;
+ child->string = child->valuestring;
+ child->valuestring = 0;
+ if (*value != ':')
+ {
+ *ep = value;
+ return 0;
+ } /* fail! */
+ /* skip any spacing, get the value. */
+ value = skip (parse_value (child, skip (value + 1), ep));
+ if (!value)
+ return 0;
+ }
+
+ if (*value == '}')
+ return value + 1; /* end of array */
+ *ep = value;
+ return 0; /* malformed. */
+}
+
+/* Render an object to text. */
+static char *
+print_object (cJSON * item, int depth, int fmt)
+{
+ char **entries = 0, **names = 0;
+ char *out = 0, *ptr, *ret, *str;
+ int len = 7, i = 0, j;
+ cJSON *child = item->child;
+ int numentries = 0, fail = 0;
+ /* Count the number of entries. */
+ while (child)
+ numentries++, child = child->next;
+ /* Explicitly handle empty object case */
+ if (!numentries)
+ {
+ out = xtrymalloc (fmt ? depth + 4 : 3);
+ if (!out)
+ return 0;
+ ptr = out;
+ *ptr++ = '{';
+ if (fmt)
+ {
+ *ptr++ = '\n';
+ for (i = 0; i < depth - 1; i++)
+ *ptr++ = '\t';
+ }
+ *ptr++ = '}';
+ *ptr++ = 0;
+ return out;
+ }
+ /* Allocate space for the names and the objects */
+ entries = xtrymalloc (numentries * sizeof (char *));
+ if (!entries)
+ return 0;
+ names = xtrymalloc (numentries * sizeof (char *));
+ if (!names)
+ {
+ xfree (entries);
+ return 0;
+ }
+ memset (entries, 0, sizeof (char *) * numentries);
+ memset (names, 0, sizeof (char *) * numentries);
+
+ /* Collect all the results into our arrays: */
+ child = item->child;
+ depth++;
+ if (fmt)
+ len += depth;
+ while (child)
+ {
+ names[i] = str = print_string_ptr (child->string);
+ entries[i++] = ret = print_value (child, depth, fmt);
+ if (str && ret)
+ len += strlen (ret) + strlen (str) + 2 + (fmt ? 2 + depth : 0);
+ else
+ fail = 1;
+ child = child->next;
+ }
+
+ /* Try to allocate the output string */
+ if (!fail)
+ out = xtrymalloc (len);
+ if (!out)
+ fail = 1;
+
+ /* Handle failure */
+ if (fail)
+ {
+ for (i = 0; i < numentries; i++)
+ {
+ if (names[i])
+ xfree (names[i]);
+ if (entries[i])
+ xfree (entries[i]);
+ }
+ xfree (names);
+ xfree (entries);
+ return 0;
+ }
+
+ /* Compose the output: */
+ *out = '{';
+ ptr = out + 1;
+ if (fmt)
+ *ptr++ = '\n';
+ *ptr = 0;
+ for (i = 0; i < numentries; i++)
+ {
+ if (fmt)
+ for (j = 0; j < depth; j++)
+ *ptr++ = '\t';
+ strcpy (ptr, names[i]);
+ ptr += strlen (names[i]);
+ *ptr++ = ':';
+ if (fmt)
+ *ptr++ = '\t';
+ strcpy (ptr, entries[i]);
+ ptr += strlen (entries[i]);
+ if (i != numentries - 1)
+ *ptr++ = ',';
+ if (fmt)
+ *ptr++ = '\n';
+ *ptr = 0;
+ xfree (names[i]);
+ xfree (entries[i]);
+ }
+
+ xfree (names);
+ xfree (entries);
+ if (fmt)
+ for (i = 0; i < depth - 1; i++)
+ *ptr++ = '\t';
+ *ptr++ = '}';
+ *ptr++ = 0;
+ return out;
+}
+
+/* Get Array size/item / object item. */
+int
+cJSON_GetArraySize (cJSON * array)
+{
+ cJSON *c = array->child;
+ int i = 0;
+ while (c)
+ i++, c = c->next;
+ return i;
+}
+
+cJSON *
+cJSON_GetArrayItem (cJSON * array, int item)
+{
+ cJSON *c = array->child;
+ while (c && item > 0)
+ item--, c = c->next;
+ return c;
+}
+
+cJSON *
+cJSON_GetObjectItem (cJSON * object, const char *string)
+{
+ cJSON *c = object->child;
+ while (c && cJSON_strcasecmp (c->string, string))
+ c = c->next;
+ return c;
+}
+
+/* Utility for array list handling. */
+static void
+suffix_object (cJSON * prev, cJSON * item)
+{
+ prev->next = item;
+ item->prev = prev;
+}
+
+/* Utility for handling references. */
+static cJSON *
+create_reference (cJSON * item)
+{
+ cJSON *ref = cJSON_New_Item ();
+ if (!ref)
+ return 0;
+ memcpy (ref, item, sizeof (cJSON));
+ ref->string = 0;
+ ref->type |= cJSON_IsReference;
+ ref->next = ref->prev = 0;
+ return ref;
+}
+
+/* Add item to array/object. */
+void
+cJSON_AddItemToArray (cJSON * array, cJSON * item)
+{
+ cJSON *c = array->child;
+ if (!item)
+ return;
+ if (!c)
+ {
+ array->child = item;
+ }
+ else
+ {
+ while (c && c->next)
+ c = c->next;
+ suffix_object (c, item);
+ }
+}
+
+cJSON *
+cJSON_AddItemToObject (cJSON * object, const char *string, cJSON * item)
+{
+ char *tmp;
+
+ if (!item)
+ return 0;
+ tmp = xtrystrdup (string);
+ if (!tmp)
+ return NULL;
+
+ if (item->string)
+ xfree (item->string);
+ item->string = tmp;
+ cJSON_AddItemToArray (object, item);
+ return object;
+}
+
+cJSON *
+cJSON_AddNullToObject (cJSON *object, const char *name)
+{
+ cJSON *obj, *tmp;
+
+ tmp = cJSON_CreateNull ();
+ if (!tmp)
+ return NULL;
+ obj = cJSON_AddItemToObject(object, name, tmp);
+ if (!obj)
+ cJSON_Delete (tmp);
+ return obj;
+}
+
+cJSON *
+cJSON_AddTrueToObject (cJSON *object, const char *name)
+{
+ cJSON *obj, *tmp;
+
+ tmp = cJSON_CreateTrue ();
+ if (!tmp)
+ return NULL;
+ obj = cJSON_AddItemToObject(object, name, tmp);
+ if (!obj)
+ cJSON_Delete (tmp);
+ return obj;
+}
+
+cJSON *
+cJSON_AddFalseToObject (cJSON *object, const char *name)
+{
+ cJSON *obj, *tmp;
+
+ tmp = cJSON_CreateFalse ();
+ if (!tmp)
+ return NULL;
+ obj = cJSON_AddItemToObject(object, name, tmp);
+ if (!obj)
+ cJSON_Delete (tmp);
+ return obj;
+}
+
+cJSON *
+cJSON_AddBoolToObject (cJSON *object, const char *name, int b)
+{
+ cJSON *obj, *tmp;
+
+ tmp = cJSON_CreateBool (b);
+ if (!tmp)
+ return NULL;
+ obj = cJSON_AddItemToObject(object, name, tmp);
+ if (!obj)
+ cJSON_Delete (tmp);
+ return obj;
+}
+
+cJSON *
+cJSON_AddNumberToObject (cJSON *object, const char *name, double num)
+{
+ cJSON *obj, *tmp;
+
+ tmp = cJSON_CreateNumber (num);
+ if (!tmp)
+ return NULL;
+ obj = cJSON_AddItemToObject(object, name, tmp);
+ if (!obj)
+ cJSON_Delete (tmp);
+ return obj;
+}
+
+cJSON *
+cJSON_AddStringToObject (cJSON *object, const char *name, const char *string)
+{
+ cJSON *obj, *tmp;
+
+ tmp = cJSON_CreateString (string);
+ if (!tmp)
+ return NULL;
+ obj = cJSON_AddItemToObject(object, name, tmp);
+ if (!obj)
+ cJSON_Delete (tmp);
+ return obj;
+}
+
+void
+cJSON_AddItemReferenceToArray (cJSON * array, cJSON * item)
+{
+ cJSON_AddItemToArray (array, create_reference (item));
+}
+
+void
+cJSON_AddItemReferenceToObject (cJSON * object, const char *string,
+ cJSON * item)
+{
+ cJSON_AddItemToObject (object, string, create_reference (item));
+}
+
+cJSON *
+cJSON_DetachItemFromArray (cJSON * array, int which)
+{
+ cJSON *c = array->child;
+ while (c && which > 0)
+ c = c->next, which--;
+ if (!c)
+ return 0;
+ if (c->prev)
+ c->prev->next = c->next;
+ if (c->next)
+ c->next->prev = c->prev;
+ if (c == array->child)
+ array->child = c->next;
+ c->prev = c->next = 0;
+ return c;
+}
+
+void
+cJSON_DeleteItemFromArray (cJSON * array, int which)
+{
+ cJSON_Delete (cJSON_DetachItemFromArray (array, which));
+}
+
+cJSON *
+cJSON_DetachItemFromObject (cJSON * object, const char *string)
+{
+ int i = 0;
+ cJSON *c = object->child;
+ while (c && cJSON_strcasecmp (c->string, string))
+ i++, c = c->next;
+ if (c)
+ return cJSON_DetachItemFromArray (object, i);
+ return 0;
+}
+
+void
+cJSON_DeleteItemFromObject (cJSON * object, const char *string)
+{
+ cJSON_Delete (cJSON_DetachItemFromObject (object, string));
+}
+
+/* Replace array/object items with new ones. */
+void
+cJSON_ReplaceItemInArray (cJSON * array, int which, cJSON * newitem)
+{
+ cJSON *c = array->child;
+ while (c && which > 0)
+ c = c->next, which--;
+ if (!c)
+ return;
+ newitem->next = c->next;
+ newitem->prev = c->prev;
+ if (newitem->next)
+ newitem->next->prev = newitem;
+ if (c == array->child)
+ array->child = newitem;
+ else
+ newitem->prev->next = newitem;
+ c->next = c->prev = 0;
+ cJSON_Delete (c);
+}
+
+void
+cJSON_ReplaceItemInObject (cJSON * object, const char *string,
+ cJSON * newitem)
+{
+ int i = 0;
+ cJSON *c = object->child;
+ while (c && cJSON_strcasecmp (c->string, string))
+ i++, c = c->next;
+ if (c)
+ {
+ newitem->string = xtrystrdup (string);
+ cJSON_ReplaceItemInArray (object, i, newitem);
+ }
+}
+
+/* Create basic types: */
+cJSON *
+cJSON_CreateNull (void)
+{
+ cJSON *item = cJSON_New_Item ();
+ if (item)
+ item->type = cJSON_NULL;
+ return item;
+}
+
+cJSON *
+cJSON_CreateTrue (void)
+{
+ cJSON *item = cJSON_New_Item ();
+ if (item)
+ item->type = cJSON_True;
+ return item;
+}
+
+cJSON *
+cJSON_CreateFalse (void)
+{
+ cJSON *item = cJSON_New_Item ();
+ if (item)
+ item->type = cJSON_False;
+ return item;
+}
+
+cJSON *
+cJSON_CreateBool (int b)
+{
+ cJSON *item = cJSON_New_Item ();
+ if (item)
+ item->type = b ? cJSON_True : cJSON_False;
+ return item;
+}
+
+cJSON *
+cJSON_CreateNumber (double num)
+{
+ cJSON *item = cJSON_New_Item ();
+ if (item)
+ {
+ item->type = cJSON_Number;
+ item->valuedouble = num;
+ item->valueint = (int) num;
+ }
+ return item;
+}
+
+cJSON *
+cJSON_CreateString (const char *string)
+{
+ cJSON *item = cJSON_New_Item ();
+ if (item)
+ {
+ item->type = cJSON_String;
+ item->valuestring = xtrystrdup (string);
+ }
+ return item;
+}
+
+cJSON *
+cJSON_CreateStringConvey (char *string)
+{
+ cJSON *item = cJSON_New_Item ();
+ if (item)
+ {
+ item->type = cJSON_String;
+ item->valuestring = string;
+ }
+ return item;
+}
+
+cJSON *
+cJSON_CreateArray (void)
+{
+ cJSON *item = cJSON_New_Item ();
+ if (item)
+ item->type = cJSON_Array;
+ return item;
+}
+
+cJSON *
+cJSON_CreateObject (void)
+{
+ cJSON *item = cJSON_New_Item ();
+ if (item)
+ item->type = cJSON_Object;
+ return item;
+}
+
+/* Create Arrays: */
+cJSON *
+cJSON_CreateIntArray (const int *numbers, int count)
+{
+ int i;
+ cJSON *n = 0, *p = 0, *a = cJSON_CreateArray ();
+ for (i = 0; a && i < count; i++)
+ {
+ n = cJSON_CreateNumber (numbers[i]);
+ if (!i)
+ a->child = n;
+ else
+ suffix_object (p, n);
+ p = n;
+ }
+ return a;
+}
+
+cJSON *
+cJSON_CreateFloatArray (const float *numbers, int count)
+{
+ int i;
+ cJSON *n = 0, *p = 0, *a = cJSON_CreateArray ();
+ for (i = 0; a && i < count; i++)
+ {
+ n = cJSON_CreateNumber (numbers[i]);
+ if (!i)
+ a->child = n;
+ else
+ suffix_object (p, n);
+ p = n;
+ }
+ return a;
+}
+
+cJSON *
+cJSON_CreateDoubleArray (const double *numbers, int count)
+{
+ int i;
+ cJSON *n = 0, *p = 0, *a = cJSON_CreateArray ();
+ for (i = 0; a && i < count; i++)
+ {
+ n = cJSON_CreateNumber (numbers[i]);
+ if (!i)
+ a->child = n;
+ else
+ suffix_object (p, n);
+ p = n;
+ }
+ return a;
+}
+
+cJSON *
+cJSON_CreateStringArray (const char **strings, int count)
+{
+ int i;
+ cJSON *n = 0, *p = 0, *a = cJSON_CreateArray ();
+ for (i = 0; a && i < count; i++)
+ {
+ n = cJSON_CreateString (strings[i]);
+ if (!i)
+ a->child = n;
+ else
+ suffix_object (p, n);
+ p = n;
+ }
+ return a;
+}
+
+/* Duplication */
+cJSON *
+cJSON_Duplicate (cJSON * item, int recurse)
+{
+ cJSON *newitem, *cptr, *nptr = 0, *newchild;
+ /* Bail on bad ptr */
+ if (!item)
+ return 0;
+ /* Create new item */
+ newitem = cJSON_New_Item ();
+ if (!newitem)
+ return 0;
+ /* Copy over all vars */
+ newitem->type = item->type & (~cJSON_IsReference), newitem->valueint =
+ item->valueint, newitem->valuedouble = item->valuedouble;
+ if (item->valuestring)
+ {
+ newitem->valuestring = xtrystrdup (item->valuestring);
+ if (!newitem->valuestring)
+ {
+ cJSON_Delete (newitem);
+ return 0;
+ }
+ }
+ if (item->string)
+ {
+ newitem->string = xtrystrdup (item->string);
+ if (!newitem->string)
+ {
+ cJSON_Delete (newitem);
+ return 0;
+ }
+ }
+ /* If non-recursive, then we're done! */
+ if (!recurse)
+ return newitem;
+ /* Walk the ->next chain for the child. */
+ cptr = item->child;
+ while (cptr)
+ {
+ /* Duplicate (with recurse) each item in the ->next chain */
+ newchild = cJSON_Duplicate (cptr, 1);
+ if (!newchild)
+ {
+ cJSON_Delete (newitem);
+ return 0;
+ }
+ if (nptr)
+ {
+ /* If newitem->child already set,
+ * then crosswire ->prev and ->next and move on. */
+ nptr->next = newchild, newchild->prev = nptr;
+ nptr = newchild;
+ }
+ else
+ {
+ /* Set newitem->child and move to it. */
+ newitem->child = newchild;
+ nptr = newchild;
+ }
+ cptr = cptr->next;
+ }
+ return newitem;
+}
+
+void
+cJSON_Minify (char *json)
+{
+ char *into = json;
+ while (*json)
+ {
+ if (*json == ' ')
+ json++;
+ else if (*json == '\t')
+ json++; /* Whitespace characters. */
+ else if (*json == '\r')
+ json++;
+ else if (*json == '\n')
+ json++;
+ else if (*json == '/' && json[1] == '/')
+ while (*json && *json != '\n')
+ json++; /* Double-slash comments, to end of line. */
+ else if (*json == '/' && json[1] == '*')
+ {
+ while (*json && !(*json == '*' && json[1] == '/'))
+ json++;
+ json += 2;
+ } /* Multiline comments. */
+ else if (*json == '\"')
+ {
+ *into++ = *json++;
+ while (*json && *json != '\"')
+ {
+ if (*json == '\\')
+ *into++ = *json++;
+ *into++ = *json++;
+ }
+ *into++ = *json++;
+ } /* String literals, which are \" sensitive. */
+ else
+ *into++ = *json++; /* All other characters. */
+ }
+ *into = 0; /* and null-terminate. */
+}
diff --git a/src/cJSON.h b/src/cJSON.h
new file mode 100644
index 0000000..a200c31
--- /dev/null
+++ b/src/cJSON.h
@@ -0,0 +1,187 @@
+/* cJSON.h
+ * Copyright (c) 2009 Dave Gamble
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * Note that this code has been modified from the original code taken
+ * from cjson-code-58.zip.
+ */
+
+#ifndef cJSON_h
+#define cJSON_h
+
+#ifdef __cplusplus
+extern "C"
+{
+#if 0 /*(to make Emacs auto-indent happy)*/
+}
+#endif
+#endif
+
+/* cJSON Types: */
+#define cJSON_False 0
+#define cJSON_True 1
+#define cJSON_NULL 2
+#define cJSON_Number 3
+#define cJSON_String 4
+#define cJSON_Array 5
+#define cJSON_Object 6
+
+#define cJSON_IsReference 256
+
+/* The cJSON structure: */
+typedef struct cJSON
+{
+ /* next/prev allow you to walk array/object chains. Alternatively,
+ use GetArraySize/GetArrayItem/GetObjectItem */
+ struct cJSON *next, *prev;
+
+ /* An array or object item will have a child pointer pointing to a
+ chain of the items in the array/object. */
+ struct cJSON *child;
+
+ int type; /* The type of the item, as above. */
+
+ char *valuestring; /* The item's string, if type==cJSON_String */
+ int valueint; /* The item's number, if type==cJSON_Number */
+ double valuedouble; /* The item's number, if type==cJSON_Number */
+
+ /* The item's name string, if this item is the child of, or is in
+ the list of subitems of an object. */
+ char *string;
+} cJSON;
+
+typedef struct cJSON *cjson_t;
+
+/* Macros to test the type of an object. */
+#define cjson_is_boolean(a) (!((a)->type & ~1))
+#define cjson_is_false(a) ((a)->type == cJSON_False)
+#define cjson_is_true(a) ((a)->type == cJSON_True)
+#define cjson_is_null(a) ((a)->type == cJSON_NULL)
+#define cjson_is_number(a) ((a)->type == cJSON_Number)
+#define cjson_is_string(a) ((a)->type == cJSON_String)
+#define cjson_is_array(a) ((a)->type == cJSON_Array)
+#define cjson_is_object(a) ((a)->type == cJSON_Object)
+
+/* Supply a block of JSON, and this returns a cJSON object you can
+ interrogate. Call cJSON_Delete when finished. */
+extern cJSON *cJSON_Parse(const char *value, size_t *r_erroff);
+
+/* Render a cJSON entity to text for transfer/storage. Free the char*
+ when finished. */
+extern char *cJSON_Print(cJSON *item);
+
+/* Render a cJSON entity to text for transfer/storage without any
+ formatting. Free the char* when finished. */
+extern char *cJSON_PrintUnformatted(cJSON *item);
+
+/* Delete a cJSON entity and all subentities. */
+extern void cJSON_Delete(cJSON *c);
+
+/* Returns the number of items in an array (or object). */
+extern int cJSON_GetArraySize(cJSON *array);
+
+/* Retrieve item number "item" from array "array". Returns NULL if
+ unsuccessful. */
+extern cJSON *cJSON_GetArrayItem(cJSON *array,int item);
+
+/* Get item "string" from object. Case insensitive. */
+extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string);
+
+/* These calls create a cJSON item of the appropriate type. */
+extern cJSON *cJSON_CreateNull(void);
+extern cJSON *cJSON_CreateTrue(void);
+extern cJSON *cJSON_CreateFalse(void);
+extern cJSON *cJSON_CreateBool(int b);
+extern cJSON *cJSON_CreateNumber(double num);
+extern cJSON *cJSON_CreateString(const char *string);
+extern cJSON *cJSON_CreateStringConvey (char *string);
+extern cJSON *cJSON_CreateArray(void);
+extern cJSON *cJSON_CreateObject(void);
+
+/* These utilities create an Array of count items. */
+extern cJSON *cJSON_CreateIntArray(const int *numbers,int count);
+extern cJSON *cJSON_CreateFloatArray(const float *numbers,int count);
+extern cJSON *cJSON_CreateDoubleArray(const double *numbers,int count);
+extern cJSON *cJSON_CreateStringArray(const char **strings,int count);
+
+/* Append item to the specified array. */
+extern void cJSON_AddItemToArray(cJSON *array, cJSON *item);
+
+/* Append item to the specified object. */
+extern cJSON *cJSON_AddItemToObject(cJSON *object, const char *name,
+ cJSON *item);
+extern cJSON *cJSON_AddNullToObject (cJSON *object, const char *name);
+extern cJSON *cJSON_AddTrueToObject (cJSON *object, const char *name);
+extern cJSON *cJSON_AddFalseToObject (cJSON *object, const char *name);
+extern cJSON *cJSON_AddBoolToObject (cJSON *object, const char *name, int b);
+extern cJSON *cJSON_AddNumberToObject (cJSON *object, const char *name,
+ double num);
+extern cJSON *cJSON_AddStringToObject (cJSON *object, const char *name,
+ const char *string);
+
+/* Append reference to item to the specified array/object. Use this
+ when you want to add an existing cJSON to a new cJSON, but don't
+ want to corrupt your existing cJSON. */
+extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
+extern void cJSON_AddItemReferenceToObject(cJSON *object,
+ const char *string,cJSON *item);
+
+/* Remove/Detatch items from Arrays/Objects. */
+extern cJSON *cJSON_DetachItemFromArray(cJSON *array,int which);
+extern void cJSON_DeleteItemFromArray(cJSON *array,int which);
+extern cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string);
+extern void cJSON_DeleteItemFromObject(cJSON *object,const char *string);
+
+/* Update array items. */
+extern void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem);
+extern void cJSON_ReplaceItemInObject(cJSON *object,
+ const char *string, cJSON *newitem);
+
+/* Duplicate a cJSON item */
+extern cJSON *cJSON_Duplicate(cJSON *item,int recurse);
+
+/* Duplicate will create a new, identical cJSON item to the one you
+ pass, in new memory that will need to be released. With recurse!=0,
+ it will duplicate any children connected to the item. The
+ item->next and ->prev pointers are always zero on return from
+ Duplicate. */
+
+/* ParseWithOpts allows you to require (and check) that the JSON is
+ null terminated, and to retrieve the pointer to the final byte
+ parsed. */
+extern cJSON *cJSON_ParseWithOpts(const char *value,
+ const char **return_parse_end,
+ int require_null_terminated,
+ size_t *r_erroff);
+
+extern void cJSON_Minify(char *json);
+
+/* When assigning an integer value, it needs to be propagated to
+ valuedouble too. */
+#define cJSON_SetIntValue(object,val) \
+ ((object)?(object)->valueint=(object)->valuedouble=(val):(val))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* cJSON_h */
diff --git a/src/context.h b/src/context.h
index 1e763d2..c8e75ba 100644
--- a/src/context.h
+++ b/src/context.h
@@ -121,6 +121,9 @@ struct gpgme_context
/* True if the option --auto-key-retrieve shall be passed to gpg. */
unsigned int auto_key_retrieve : 1;
+ /* Do not use the symmtric encryption passphrase cache. */
+ unsigned int no_symkey_cache : 1;
+
/* Flags for keylist mode. */
gpgme_keylist_mode_t keylist_mode;
@@ -145,6 +148,9 @@ struct gpgme_context
/* The gpg specific override session key or NULL. */
char *override_session_key;
+ /* The optional request origin. */
+ char *request_origin;
+
/* The locale for the pinentry. */
char *lc_ctype;
char *lc_messages;
diff --git a/src/conversion.c b/src/conversion.c
index 5b84f67..4bfd3d3 100644
--- a/src/conversion.c
+++ b/src/conversion.c
@@ -575,3 +575,49 @@ _gpgme_map_pk_algo (int algo, gpgme_protocol_t protocol)
return algo;
}
+
+
+/* Return a string with a cipher algorithm. */
+const char *
+_gpgme_cipher_algo_name (int algo, gpgme_protocol_t protocol)
+{
+ if (protocol == GPGME_PROTOCOL_OPENPGP)
+ {
+ /* The algo is given according to OpenPGP specs. */
+ switch (algo)
+ {
+ case 1: return "IDEA";
+ case 2: return "3DES";
+ case 3: return "CAST5";
+ case 4: return "BLOWFISH";
+ case 7: return "AES";
+ case 8: return "AES192";
+ case 9: return "AES256";
+ case 10: return "TWOFISH";
+ case 11: return "CAMELLIA128";
+ case 12: return "CAMELLIA192";
+ case 13: return "CAMELLIA256";
+ }
+ }
+
+ return "Unknown";
+}
+
+
+/* Return a string with the cipher mode. */
+const char *
+_gpgme_cipher_mode_name (int algo, gpgme_protocol_t protocol)
+{
+ if (protocol == GPGME_PROTOCOL_OPENPGP)
+ {
+ /* The algo is given according to OpenPGP specs. */
+ switch (algo)
+ {
+ case 0: return "CFB";
+ case 1: return "EAX";
+ case 2: return "OCB";
+ }
+ }
+
+ return "Unknown";
+}
diff --git a/src/decrypt.c b/src/decrypt.c
index 8c2cd4d..155e18e 100644
--- a/src/decrypt.c
+++ b/src/decrypt.c
@@ -69,14 +69,10 @@ release_op_data (void *hook)
op_data_t opd = (op_data_t) hook;
gpgme_recipient_t recipient = opd->result.recipients;
- if (opd->result.unsupported_algorithm)
- free (opd->result.unsupported_algorithm);
-
- if (opd->result.file_name)
- free (opd->result.file_name);
-
- if (opd->result.session_key)
- free (opd->result.session_key);
+ free (opd->result.unsupported_algorithm);
+ free (opd->result.file_name);
+ free (opd->result.session_key);
+ free (opd->result.symkey_algo);
while (recipient)
{
@@ -104,6 +100,17 @@ gpgme_op_decrypt_result (gpgme_ctx_t ctx)
return NULL;
}
+ /* Make sure that SYMKEY_ALGO has a value. */
+ if (!opd->result.symkey_algo)
+ {
+ opd->result.symkey_algo = strdup ("?.?");
+ if (!opd->result.symkey_algo)
+ {
+ TRACE_SUC0 ("result=(null)");
+ return NULL;
+ }
+ }
+
if (_gpgme_debug_trace ())
{
gpgme_recipient_t rcp;
@@ -263,6 +270,49 @@ parse_enc_to (char *args, gpgme_recipient_t *recp, gpgme_protocol_t protocol)
}
+/* Parse the ARGS of a
+ * DECRYPTION_INFO <mdc_method> <sym_algo> [<aead_algo>]
+ * status. Returns 0 on success and updates the OPD.
+ */
+static gpgme_error_t
+parse_decryption_info (char *args, op_data_t opd, gpgme_protocol_t protocol)
+{
+ char *field[3];
+ int nfields;
+ char *args2;
+ int mdc, mode;
+ const char *algostr, *modestr;
+
+ if (!args)
+ return trace_gpg_error (GPG_ERR_INV_ENGINE);
+
+ args2 = strdup (args); /* Split modifies the input string. */
+ nfields = _gpgme_split_fields (args2, field, DIM (field));
+ if (nfields < 2)
+ {
+ free (args2);
+ return trace_gpg_error (GPG_ERR_INV_ENGINE); /* Required arg missing. */
+ }
+
+ mdc = atoi (field[0]);
+ algostr = _gpgme_cipher_algo_name (atoi (field[1]), protocol);
+ mode = nfields < 3? 0 : atoi (field[2]);
+ modestr = _gpgme_cipher_mode_name (mode, protocol);
+
+ free (args2);
+
+ free (opd->result.symkey_algo);
+ if (!mode && mdc != 2)
+ opd->result.symkey_algo = _gpgme_strconcat (algostr, ".PGPCFB", NULL);
+ else
+ opd->result.symkey_algo = _gpgme_strconcat (algostr, ".", modestr, NULL);
+ if (!opd->result.symkey_algo)
+ return gpg_error_from_syserror ();
+
+ return 0;
+}
+
+
gpgme_error_t
_gpgme_decrypt_status_handler (void *priv, gpgme_status_code_t code,
char *args)
@@ -303,7 +353,9 @@ _gpgme_decrypt_status_handler (void *priv, gpgme_status_code_t code,
break;
case GPGME_STATUS_DECRYPTION_INFO:
- /* Fixme: Provide a way to return the used symmetric algorithm. */
+ err = parse_decryption_info (args, opd, ctx->protocol);
+ if (err)
+ return err;
break;
case GPGME_STATUS_DECRYPTION_OKAY:
@@ -357,9 +409,14 @@ _gpgme_decrypt_status_handler (void *priv, gpgme_status_code_t code,
break;
case GPGME_STATUS_PLAINTEXT:
- err = _gpgme_parse_plaintext (args, &opd->result.file_name);
- if (err)
- return err;
+ {
+ int mime = 0;
+ err = _gpgme_parse_plaintext (args, &opd->result.file_name, &mime);
+ if (err)
+ return err;
+ gpgrt_log_debug ("decrypt.c setting mime to %d\n", mime);
+ opd->result.is_mime = !!mime;
+ }
break;
case GPGME_STATUS_INQUIRE_MAXLEN:
diff --git a/src/encrypt-sign.c b/src/encrypt-sign.c
index af6de63..4db46e2 100644
--- a/src/encrypt-sign.c
+++ b/src/encrypt-sign.c
@@ -62,6 +62,7 @@ encrypt_sym_status_handler (void *priv, gpgme_status_code_t code, char *args)
static gpgme_error_t
encrypt_sign_start (gpgme_ctx_t ctx, int synchronous, gpgme_key_t recp[],
+ const char *recpstring,
gpgme_encrypt_flags_t flags,
gpgme_data_t plain, gpgme_data_t cipher)
{
@@ -72,7 +73,7 @@ encrypt_sign_start (gpgme_ctx_t ctx, int synchronous, gpgme_key_t recp[],
if (err)
return err;
- symmetric = !recp || (flags & GPGME_ENCRYPT_SYMMETRIC);
+ symmetric = (!recp && !recpstring) || (flags & GPGME_ENCRYPT_SYMMETRIC);
if (!plain)
return gpg_error (GPG_ERR_NO_DATA);
@@ -103,43 +104,75 @@ encrypt_sign_start (gpgme_ctx_t ctx, int synchronous, gpgme_key_t recp[],
: encrypt_sign_status_handler,
ctx);
- return _gpgme_engine_op_encrypt_sign (ctx->engine, recp, flags, plain,
+ return _gpgme_engine_op_encrypt_sign (ctx->engine, recp, recpstring,
+ flags, plain,
cipher, ctx->use_armor,
ctx /* FIXME */);
}
-/* Encrypt plaintext PLAIN within CTX for the recipients RECP and
- store the resulting ciphertext in CIPHER. Also sign the ciphertext
- with the signers in CTX. */
+/* Old version of gpgme_op_encrypt_sign_ext_start w/o RECPSTRING. */
gpgme_error_t
gpgme_op_encrypt_sign_start (gpgme_ctx_t ctx, gpgme_key_t recp[],
gpgme_encrypt_flags_t flags,
gpgme_data_t plain, gpgme_data_t cipher)
{
+ return gpgme_op_encrypt_sign_ext_start (ctx, recp, NULL,
+ flags, plain, cipher);
+}
+
+
+/* Old version of gpgme_op_encrypt_sign_ext w/o RECPSTRING. */
+gpgme_error_t
+gpgme_op_encrypt_sign (gpgme_ctx_t ctx, gpgme_key_t recp[],
+ gpgme_encrypt_flags_t flags,
+ gpgme_data_t plain, gpgme_data_t cipher)
+{
+ return gpgme_op_encrypt_sign_ext (ctx, recp, NULL, flags, plain, cipher);
+}
+
+
+/* Encrypt plaintext PLAIN within CTX for the recipients RECP and
+ * store the resulting ciphertext in CIPHER. Also sign the ciphertext
+ * with the signers in CTX. */
+gpgme_error_t
+gpgme_op_encrypt_sign_ext (gpgme_ctx_t ctx, gpgme_key_t recp[],
+ const char *recpstring,
+ gpgme_encrypt_flags_t flags,
+ gpgme_data_t plain, gpgme_data_t cipher)
+{
gpgme_error_t err;
- TRACE_BEG3 (DEBUG_CTX, "gpgme_op_encrypt_sign_start", ctx,
+ TRACE_BEG3 (DEBUG_CTX, "gpgme_op_encrypt_sign", ctx,
"flags=0x%x, plain=%p, cipher=%p", flags, plain, cipher);
if (!ctx)
return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
- if (_gpgme_debug_trace () && recp)
+ if (_gpgme_debug_trace () && (recp || recpstring))
{
- int i = 0;
-
- while (recp[i])
- {
- TRACE_LOG3 ("recipient[%i] = %p (%s)", i, recp[i],
- (recp[i]->subkeys && recp[i]->subkeys->fpr) ?
- recp[i]->subkeys->fpr : "invalid");
- i++;
- }
+ if (recp)
+ {
+ int i = 0;
+
+ while (recp[i])
+ {
+ TRACE_LOG3 ("recipient[%i] = %p (%s)", i, recp[i],
+ (recp[i]->subkeys && recp[i]->subkeys->fpr) ?
+ recp[i]->subkeys->fpr : "invalid");
+ i++;
+ }
+ }
+ else
+ {
+ TRACE_LOG1 ("recipients = '%s'", recpstring);
+ }
}
- err = encrypt_sign_start (ctx, 0, recp, flags, plain, cipher);
- return err;
+ err = encrypt_sign_start (ctx, 1, recp, recpstring, flags, plain, cipher);
+ if (!err)
+ err = _gpgme_wait_one (ctx);
+ return TRACE_ERR (err);
}
@@ -147,33 +180,39 @@ gpgme_op_encrypt_sign_start (gpgme_ctx_t ctx, gpgme_key_t recp[],
store the resulting ciphertext in CIPHER. Also sign the ciphertext
with the signers in CTX. */
gpgme_error_t
-gpgme_op_encrypt_sign (gpgme_ctx_t ctx, gpgme_key_t recp[],
- gpgme_encrypt_flags_t flags,
- gpgme_data_t plain, gpgme_data_t cipher)
+gpgme_op_encrypt_sign_ext_start (gpgme_ctx_t ctx, gpgme_key_t recp[],
+ const char *recpstring,
+ gpgme_encrypt_flags_t flags,
+ gpgme_data_t plain, gpgme_data_t cipher)
{
gpgme_error_t err;
- TRACE_BEG3 (DEBUG_CTX, "gpgme_op_encrypt_sign", ctx,
+ TRACE_BEG3 (DEBUG_CTX, "gpgme_op_encrypt_sign_start", ctx,
"flags=0x%x, plain=%p, cipher=%p", flags, plain, cipher);
if (!ctx)
return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
- if (_gpgme_debug_trace () && recp)
+ if (_gpgme_debug_trace () && (recp || recpstring))
{
- int i = 0;
-
- while (recp[i])
- {
- TRACE_LOG3 ("recipient[%i] = %p (%s)", i, recp[i],
- (recp[i]->subkeys && recp[i]->subkeys->fpr) ?
- recp[i]->subkeys->fpr : "invalid");
- i++;
- }
+ if (recp)
+ {
+ int i = 0;
+
+ while (recp[i])
+ {
+ TRACE_LOG3 ("recipient[%i] = %p (%s)", i, recp[i],
+ (recp[i]->subkeys && recp[i]->subkeys->fpr) ?
+ recp[i]->subkeys->fpr : "invalid");
+ i++;
+ }
+ }
+ else
+ {
+ TRACE_LOG1 ("recipients = '%s'", recpstring);
+ }
}
- err = encrypt_sign_start (ctx, 1, recp, flags, plain, cipher);
- if (!err)
- err = _gpgme_wait_one (ctx);
- return TRACE_ERR (err);
+ err = encrypt_sign_start (ctx, 0, recp, recpstring, flags, plain, cipher);
+ return err;
}
diff --git a/src/encrypt.c b/src/encrypt.c
index 4023654..2318497 100644
--- a/src/encrypt.c
+++ b/src/encrypt.c
@@ -214,6 +214,7 @@ _gpgme_op_encrypt_init_result (gpgme_ctx_t ctx)
static gpgme_error_t
encrypt_start (gpgme_ctx_t ctx, int synchronous, gpgme_key_t recp[],
+ const char *recpstring,
gpgme_encrypt_flags_t flags,
gpgme_data_t plain, gpgme_data_t cipher)
{
@@ -228,13 +229,13 @@ encrypt_start (gpgme_ctx_t ctx, int synchronous, gpgme_key_t recp[],
if (err)
return err;
- symmetric = !recp || (flags & GPGME_ENCRYPT_SYMMETRIC);
+ symmetric = (!recp && !recpstring) || (flags & GPGME_ENCRYPT_SYMMETRIC);
if (!plain)
return gpg_error (GPG_ERR_NO_DATA);
if (!cipher)
return gpg_error (GPG_ERR_INV_VALUE);
- if (recp && ! *recp)
+ if (recp && !*recp)
return gpg_error (GPG_ERR_INV_VALUE);
if (symmetric && ctx->passphrase_cb)
@@ -252,72 +253,111 @@ encrypt_start (gpgme_ctx_t ctx, int synchronous, gpgme_key_t recp[],
: encrypt_status_handler,
ctx);
- return _gpgme_engine_op_encrypt (ctx->engine, recp, flags, plain, cipher,
- ctx->use_armor);
+ return _gpgme_engine_op_encrypt (ctx->engine, recp, recpstring,
+ flags, plain, cipher, ctx->use_armor);
}
+/* Old version of gpgme_op_encrypt_ext without RECPSTRING. */
+gpgme_error_t
+gpgme_op_encrypt (gpgme_ctx_t ctx, gpgme_key_t recp[],
+ gpgme_encrypt_flags_t flags,
+ gpgme_data_t plain, gpgme_data_t cipher)
+{
+ return gpgme_op_encrypt_ext (ctx, recp, NULL, flags, plain, cipher);
+}
+
+
+/* Old version of gpgme_op_encrypt_ext_start without RECPSTRING. */
gpgme_error_t
gpgme_op_encrypt_start (gpgme_ctx_t ctx, gpgme_key_t recp[],
gpgme_encrypt_flags_t flags,
gpgme_data_t plain, gpgme_data_t cipher)
{
+ return gpgme_op_encrypt_ext_start (ctx, recp, NULL, flags, plain, cipher);
+}
+
+
+/* Encrypt plaintext PLAIN within CTX for the recipients RECP and
+ * store the resulting ciphertext in CIPHER. RECPSTRING can be used
+ * instead of the RECP array to directly specify recipients as LF
+ * delimited strings; these may be any kind of recipient specification
+ * patterns as supported by the backend. */
+gpgme_error_t
+gpgme_op_encrypt_ext (gpgme_ctx_t ctx, gpgme_key_t recp[],
+ const char *recpstring,
+ gpgme_encrypt_flags_t flags,
+ gpgme_data_t plain, gpgme_data_t cipher)
+{
gpgme_error_t err;
- TRACE_BEG3 (DEBUG_CTX, "gpgme_op_encrypt_start", ctx,
+ TRACE_BEG3 (DEBUG_CTX, "gpgme_op_encrypt", ctx,
"flags=0x%x, plain=%p, cipher=%p", flags, plain, cipher);
if (!ctx)
return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
- if (_gpgme_debug_trace () && recp)
+ if (_gpgme_debug_trace () && (recp || recpstring))
{
- int i = 0;
+ if (recp)
+ {
+ int i = 0;
- while (recp[i])
- {
- TRACE_LOG3 ("recipient[%i] = %p (%s)", i, recp[i],
+ while (recp[i])
+ {
+ TRACE_LOG3 ("recipient[%i] = %p (%s)", i, recp[i],
(recp[i]->subkeys && recp[i]->subkeys->fpr) ?
- recp[i]->subkeys->fpr : "invalid");
- i++;
- }
+ recp[i]->subkeys->fpr : "invalid");
+ i++;
+ }
+ }
+ else
+ {
+ TRACE_LOG1 ("recipients = '%s'", recpstring);
+ }
}
- err = encrypt_start (ctx, 0, recp, flags, plain, cipher);
+ err = encrypt_start (ctx, 1, recp, recpstring, flags, plain, cipher);
+ if (!err)
+ err = _gpgme_wait_one (ctx);
return TRACE_ERR (err);
}
-/* Encrypt plaintext PLAIN within CTX for the recipients RECP and
- store the resulting ciphertext in CIPHER. */
gpgme_error_t
-gpgme_op_encrypt (gpgme_ctx_t ctx, gpgme_key_t recp[],
- gpgme_encrypt_flags_t flags,
- gpgme_data_t plain, gpgme_data_t cipher)
+gpgme_op_encrypt_ext_start (gpgme_ctx_t ctx, gpgme_key_t recp[],
+ const char *recpstring,
+ gpgme_encrypt_flags_t flags,
+ gpgme_data_t plain, gpgme_data_t cipher)
{
gpgme_error_t err;
- TRACE_BEG3 (DEBUG_CTX, "gpgme_op_encrypt", ctx,
+ TRACE_BEG3 (DEBUG_CTX, "gpgme_op_encrypt_start", ctx,
"flags=0x%x, plain=%p, cipher=%p", flags, plain, cipher);
if (!ctx)
return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
- if (_gpgme_debug_trace () && recp)
+ if (_gpgme_debug_trace () && (recp || recpstring))
{
- int i = 0;
-
- while (recp[i])
- {
- TRACE_LOG3 ("recipient[%i] = %p (%s)", i, recp[i],
- (recp[i]->subkeys && recp[i]->subkeys->fpr) ?
- recp[i]->subkeys->fpr : "invalid");
- i++;
- }
+ if (recp)
+ {
+ int i = 0;
+
+ while (recp[i])
+ {
+ TRACE_LOG3 ("recipient[%i] = %p (%s)", i, recp[i],
+ (recp[i]->subkeys && recp[i]->subkeys->fpr) ?
+ recp[i]->subkeys->fpr : "invalid");
+ i++;
+ }
+ }
+ else
+ {
+ TRACE_LOG1 ("recipients = '%s'", recpstring);
+ }
}
- err = encrypt_start (ctx, 1, recp, flags, plain, cipher);
- if (!err)
- err = _gpgme_wait_one (ctx);
+ err = encrypt_start (ctx, 0, recp, recpstring, flags, plain, cipher);
return TRACE_ERR (err);
}
diff --git a/src/engine-assuan.c b/src/engine-assuan.c
index bb2290a..6e603d9 100644
--- a/src/engine-assuan.c
+++ b/src/engine-assuan.c
@@ -96,6 +96,7 @@ struct engine_llass
int gpg_agent:1; /* Assume this is a gpg-agent connection. */
} opt;
+ char request_origin[10]; /* Copy from the CTX. */
};
typedef struct engine_llass *engine_llass_t;
@@ -365,6 +366,24 @@ llass_new (void **engine, const char *file_name, const char *home_dir,
}
+/* Copy flags from CTX into the engine object. */
+static void
+llass_set_engine_flags (void *engine, const gpgme_ctx_t ctx)
+{
+ engine_llass_t llass = engine;
+
+ if (ctx->request_origin)
+ {
+ if (strlen (ctx->request_origin) + 1 > sizeof llass->request_origin)
+ strcpy (llass->request_origin, "xxx"); /* Too long - force error */
+ else
+ strcpy (llass->request_origin, ctx->request_origin);
+ }
+ else
+ *llass->request_origin = 0;
+}
+
+
static gpgme_error_t
llass_set_locale (void *engine, int category, const char *value)
{
@@ -660,6 +679,21 @@ start (engine_llass_t llass, const char *command)
int nfds;
int i;
+ if (*llass->request_origin && llass->opt.gpg_agent)
+ {
+ char *cmd;
+
+ cmd = _gpgme_strconcat ("OPTION pretend-request-origin=",
+ llass->request_origin, NULL);
+ if (!cmd)
+ return gpg_error_from_syserror ();
+ err = assuan_transact (llass->assuan_ctx, cmd, NULL, NULL, NULL,
+ NULL, NULL, NULL);
+ free (cmd);
+ if (err && gpg_err_code (err) != GPG_ERR_UNKNOWN_OPTION)
+ return err;
+ }
+
/* We need to know the fd used by assuan for reads. We do this by
using the assumption that the first returned fd from
assuan_get_active_fds() is always this one. */
@@ -775,6 +809,7 @@ struct engine_ops _gpgme_engine_ops_assuan =
NULL, /* set_colon_line_handler */
llass_set_locale,
NULL, /* set_protocol */
+ llass_set_engine_flags,
NULL, /* decrypt */
NULL, /* delete */
NULL, /* edit */
diff --git a/src/engine-backend.h b/src/engine-backend.h
index 421eb16..f692666 100644
--- a/src/engine-backend.h
+++ b/src/engine-backend.h
@@ -61,6 +61,7 @@ struct engine_ops
void *fnc_value);
gpgme_error_t (*set_locale) (void *engine, int category, const char *value);
gpgme_error_t (*set_protocol) (void *engine, gpgme_protocol_t protocol);
+ void (*set_engine_flags) (void *engine, gpgme_ctx_t ctx);
gpgme_error_t (*decrypt) (void *engine,
gpgme_decrypt_flags_t flags,
gpgme_data_t ciph,
@@ -71,10 +72,12 @@ struct engine_ops
gpgme_error_t (*edit) (void *engine, int type, gpgme_key_t key,
gpgme_data_t out, gpgme_ctx_t ctx /* FIXME */);
gpgme_error_t (*encrypt) (void *engine, gpgme_key_t recp[],
+ const char *recpstring,
gpgme_encrypt_flags_t flags,
gpgme_data_t plain, gpgme_data_t ciph,
int use_armor);
gpgme_error_t (*encrypt_sign) (void *engine, gpgme_key_t recp[],
+ const char *recpstring,
gpgme_encrypt_flags_t flags,
gpgme_data_t plain, gpgme_data_t ciph,
int use_armor, gpgme_ctx_t ctx /* FIXME */);
diff --git a/src/engine-g13.c b/src/engine-g13.c
index f8f3178..ec2f7af 100644
--- a/src/engine-g13.c
+++ b/src/engine-g13.c
@@ -790,6 +790,7 @@ struct engine_ops _gpgme_engine_ops_g13 =
NULL, /* set_colon_line_handler */
g13_set_locale,
NULL, /* set_protocol */
+ NULL, /* set_engine_flags */
NULL, /* decrypt */
NULL, /* delete */
NULL, /* edit */
diff --git a/src/engine-gpg.c b/src/engine-gpg.c
index bfe7d13..173e940 100644
--- a/src/engine-gpg.c
+++ b/src/engine-gpg.c
@@ -43,6 +43,7 @@
#include "sema.h"
#include "debug.h"
#include "data.h"
+#include "mbox-util.h"
#include "engine-backend.h"
@@ -143,6 +144,12 @@ struct engine_gpg
struct gpgme_io_cbs io_cbs;
gpgme_pinentry_mode_t pinentry_mode;
+ char request_origin[10];
+
+ struct {
+ unsigned int no_symkey_cache : 1;
+ unsigned int offline : 1;
+ } flags;
/* NULL or the data object fed to --override_session_key-fd. */
gpgme_data_t override_session_key;
@@ -628,6 +635,30 @@ gpg_new (void **engine, const char *file_name, const char *home_dir,
}
+/* Copy flags from CTX into the engine object. */
+static void
+gpg_set_engine_flags (void *engine, const gpgme_ctx_t ctx)
+{
+ engine_gpg_t gpg = engine;
+
+ if (ctx->request_origin && have_gpg_version (gpg, "2.2.6"))
+ {
+ if (strlen (ctx->request_origin) + 1 > sizeof gpg->request_origin)
+ strcpy (gpg->request_origin, "xxx"); /* Too long - force error */
+ else
+ strcpy (gpg->request_origin, ctx->request_origin);
+ }
+ else
+ *gpg->request_origin = 0;
+
+ 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"));
+
+}
+
+
static gpgme_error_t
gpg_set_locale (void *engine, int category, const char *value)
{
@@ -856,7 +887,8 @@ build_argv (engine_gpg_t gpg, const char *pgmname)
argc++;
if (!gpg->cmd.used)
argc++; /* --batch */
- argc += 1; /* --no-sk-comments */
+ argc += 4; /* --no-sk-comments, --request-origin, --no-symkey-cache */
+ /* --disable-dirmngr */
argv = calloc (argc + 1, sizeof *argv);
if (!argv)
@@ -904,6 +936,46 @@ build_argv (engine_gpg_t gpg, const char *pgmname)
argc++;
}
+ if (*gpg->request_origin)
+ {
+ argv[argc] = _gpgme_strconcat ("--request-origin=",
+ gpg->request_origin, NULL);
+ 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");
+ 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");
+ if (!argv[argc])
+ {
+ int saved_err = gpg_error_from_syserror ();
+ free (fd_data_map);
+ free_argv (argv);
+ return saved_err;
+ }
+ argc++;
+ }
+
if (gpg->pinentry_mode && have_gpg_version (gpg, "2.1.0"))
{
const char *s = NULL;
@@ -1838,8 +1910,70 @@ gpg_edit (void *engine, int type, gpgme_key_t key, gpgme_data_t out,
}
+/* Add a single argument from a key to an -r option. */
+static gpg_error_t
+add_arg_recipient (engine_gpg_t gpg, gpgme_encrypt_flags_t flags,
+ gpgme_key_t key)
+{
+ gpg_error_t err;
+
+ if ((flags & GPGME_ENCRYPT_WANT_ADDRESS))
+ {
+ /* We have no way to figure out which mail address was
+ * requested. FIXME: It would be possible to figure this out by
+ * consulting the SENDER property of the context. */
+ err = gpg_error (GPG_ERR_INV_USER_ID);
+ }
+ else
+ err = add_arg (gpg, key->subkeys->fpr);
+
+ return err;
+}
+
+
+/* Add a single argument from a USERID string to an -r option. */
+static gpg_error_t
+add_arg_recipient_string (engine_gpg_t gpg, gpgme_encrypt_flags_t flags,
+ const char *userid, int useridlen)
+{
+ gpg_error_t err;
+
+ if ((flags & GPGME_ENCRYPT_WANT_ADDRESS))
+ {
+ char *tmpstr, *mbox;
+
+ tmpstr = malloc (useridlen + 1);
+ if (!tmpstr)
+ err = gpg_error_from_syserror ();
+ else
+ {
+ memcpy (tmpstr, userid, useridlen);
+ tmpstr[useridlen] = 0;
+
+ mbox = _gpgme_mailbox_from_userid (tmpstr);
+ if (!mbox)
+ {
+ err = gpg_error_from_syserror ();
+ if (gpg_err_code (err) == GPG_ERR_EINVAL)
+ err = gpg_error (GPG_ERR_INV_USER_ID);
+ }
+ else
+ err = add_arg (gpg, mbox);
+
+ free (mbox);
+ free (tmpstr);
+ }
+ }
+ else
+ err = add_arg_len (gpg, NULL, userid, useridlen);
+
+ return err;
+}
+
+
static gpgme_error_t
-append_args_from_recipients (engine_gpg_t gpg, gpgme_key_t recp[])
+append_args_from_recipients (engine_gpg_t gpg, gpgme_encrypt_flags_t flags,
+ gpgme_key_t recp[])
{
gpgme_error_t err = 0;
int i = 0;
@@ -1851,7 +1985,7 @@ append_args_from_recipients (engine_gpg_t gpg, gpgme_key_t recp[])
if (!err)
err = add_arg (gpg, "-r");
if (!err)
- err = add_arg (gpg, recp[i]->subkeys->fpr);
+ err = add_arg_recipient (gpg, flags, recp[i]);
if (err)
break;
i++;
@@ -1860,17 +1994,86 @@ append_args_from_recipients (engine_gpg_t gpg, gpgme_key_t recp[])
}
+/* Take recipients from the LF delimited STRING and add -r args. */
+static gpg_error_t
+append_args_from_recipients_string (engine_gpg_t gpg,
+ gpgme_encrypt_flags_t flags,
+ const char *string)
+{
+ gpg_error_t err = 0;
+ gpgme_encrypt_flags_t orig_flags = flags;
+ int any = 0;
+ int ignore = 0;
+ int hidden = 0;
+ int file = 0;
+ const char *s;
+ int n;
+
+ do
+ {
+ /* Skip leading white space */
+ while (*string == ' ' || *string == '\t')
+ string++;
+ if (!*string)
+ break;
+
+ /* Look for the LF. */
+ s = strchr (string, '\n');
+ if (s)
+ n = s - string;
+ else
+ n = strlen (string);
+ while (n && (string[n-1] == ' ' || string[n-1] == '\t'))
+ n--;
+
+ if (!ignore && n == 2 && !memcmp (string, "--", 2))
+ ignore = 1;
+ else if (!ignore && n == 8 && !memcmp (string, "--hidden", 8))
+ hidden = 1;
+ else if (!ignore && n == 11 && !memcmp (string, "--no-hidden", 11))
+ hidden = 0;
+ else if (!ignore && n == 6 && !memcmp (string, "--file", 6))
+ {
+ file = 1;
+ /* Because the key is used as is we need to ignore this flag: */
+ flags &= ~GPGME_ENCRYPT_WANT_ADDRESS;
+ }
+ else if (!ignore && n == 9 && !memcmp (string, "--no-file", 9))
+ {
+ file = 0;
+ flags = orig_flags;
+ }
+ else if (n) /* Not empty - use it. */
+ {
+ err = add_arg (gpg, file? (hidden? "-F":"-f") : (hidden? "-R":"-r"));
+ if (!err)
+ err = add_arg_recipient_string (gpg, flags, string, n);
+ if (!err)
+ any = 1;
+ }
+
+ string += n + !!s;
+ }
+ while (!err);
+
+ if (!err && !any)
+ err = gpg_error (GPG_ERR_MISSING_KEY);
+ return err;
+}
+
+
static gpgme_error_t
-gpg_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags,
+gpg_encrypt (void *engine, gpgme_key_t recp[], const char *recpstring,
+ gpgme_encrypt_flags_t flags,
gpgme_data_t plain, gpgme_data_t ciph, int use_armor)
{
engine_gpg_t gpg = engine;
gpgme_error_t err = 0;
- if (recp)
+ if (recp || recpstring)
err = add_arg (gpg, "--encrypt");
- if (!err && ((flags & GPGME_ENCRYPT_SYMMETRIC) || !recp))
+ if (!err && ((flags & GPGME_ENCRYPT_SYMMETRIC) || (!recp && !recpstring)))
err = add_arg (gpg, "--symmetric");
if (!err && use_armor)
@@ -1897,7 +2100,7 @@ gpg_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags,
&& have_gpg_version (gpg, "2.1.14"))
err = add_arg (gpg, "--mimemode");
- if (recp)
+ if (recp || recpstring)
{
/* If we know that all recipients are valid (full or ultimate trust)
we can suppress further checks. */
@@ -1907,8 +2110,10 @@ gpg_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags,
if (!err && (flags & GPGME_ENCRYPT_NO_ENCRYPT_TO))
err = add_arg (gpg, "--no-encrypt-to");
- if (!err)
- err = append_args_from_recipients (gpg, recp);
+ if (!err && !recp && recpstring)
+ err = append_args_from_recipients_string (gpg, flags, recpstring);
+ else if (!err)
+ err = append_args_from_recipients (gpg, flags, recp);
}
/* Tell the gpg object about the data. */
@@ -1941,6 +2146,7 @@ gpg_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags,
static gpgme_error_t
gpg_encrypt_sign (void *engine, gpgme_key_t recp[],
+ const char *recpstring,
gpgme_encrypt_flags_t flags, gpgme_data_t plain,
gpgme_data_t ciph, int use_armor,
gpgme_ctx_t ctx /* FIXME */)
@@ -1948,10 +2154,10 @@ gpg_encrypt_sign (void *engine, gpgme_key_t recp[],
engine_gpg_t gpg = engine;
gpgme_error_t err = 0;
- if (recp)
+ if (recp || recpstring)
err = add_arg (gpg, "--encrypt");
- if (!err && ((flags & GPGME_ENCRYPT_SYMMETRIC) || !recp))
+ if (!err && ((flags & GPGME_ENCRYPT_SYMMETRIC) || (!recp && !recpstring)))
err = add_arg (gpg, "--symmetric");
if (!err)
@@ -1969,7 +2175,7 @@ gpg_encrypt_sign (void *engine, gpgme_key_t recp[],
&& have_gpg_version (gpg, "2.1.14"))
err = add_arg (gpg, "--mimemode");
- if (recp)
+ if (recp || recpstring)
{
/* If we know that all recipients are valid (full or ultimate trust)
we can suppress further checks. */
@@ -1979,8 +2185,10 @@ gpg_encrypt_sign (void *engine, gpgme_key_t recp[],
if (!err && (flags & GPGME_ENCRYPT_NO_ENCRYPT_TO))
err = add_arg (gpg, "--no-encrypt-to");
- if (!err)
- err = append_args_from_recipients (gpg, recp);
+ if (!err && !recp && recpstring)
+ err = append_args_from_recipients_string (gpg, flags, recpstring);
+ else if (!err)
+ err = append_args_from_recipients (gpg, flags, recp);
}
if (!err)
@@ -3090,6 +3298,7 @@ struct engine_ops _gpgme_engine_ops_gpg =
gpg_set_colon_line_handler,
gpg_set_locale,
NULL, /* set_protocol */
+ gpg_set_engine_flags, /* set_engine_flags */
gpg_decrypt,
gpg_delete,
gpg_edit,
diff --git a/src/engine-gpgconf.c b/src/engine-gpgconf.c
index 94ae67f..24867c7 100644
--- a/src/engine-gpgconf.c
+++ b/src/engine-gpgconf.c
@@ -1287,6 +1287,7 @@ struct engine_ops _gpgme_engine_ops_gpgconf =
NULL, /* set_colon_line_handler */
NULL, /* set_locale */
NULL, /* set_protocol */
+ NULL, /* set_engine_flags */
NULL, /* decrypt */
NULL, /* delete */
NULL, /* edit */
diff --git a/src/engine-gpgsm.c b/src/engine-gpgsm.c
index e337fed..da7e524 100644
--- a/src/engine-gpgsm.c
+++ b/src/engine-gpgsm.c
@@ -107,6 +107,8 @@ struct engine_gpgsm
gpgme_data_t inline_data; /* Used to collect D lines. */
+ char request_origin[10];
+
struct gpgme_io_cbs io_cbs;
};
@@ -521,6 +523,24 @@ gpgsm_new (void **engine, const char *file_name, const char *home_dir,
}
+/* Copy flags from CTX into the engine object. */
+static void
+gpgsm_set_engine_flags (void *engine, const gpgme_ctx_t ctx)
+{
+ engine_gpgsm_t gpgsm = engine;
+
+ if (ctx->request_origin)
+ {
+ if (strlen (ctx->request_origin) + 1 > sizeof gpgsm->request_origin)
+ strcpy (gpgsm->request_origin, "xxx"); /* Too long - force error */
+ else
+ strcpy (gpgsm->request_origin, ctx->request_origin);
+ }
+ else
+ *gpgsm->request_origin = 0;
+}
+
+
static gpgme_error_t
gpgsm_set_locale (void *engine, int category, const char *value)
{
@@ -1058,6 +1078,20 @@ start (engine_gpgsm_t gpgsm, const char *command)
int nfds;
int i;
+ if (*gpgsm->request_origin)
+ {
+ char *cmd;
+
+ cmd = _gpgme_strconcat ("OPTION request-origin=",
+ gpgsm->request_origin, NULL);
+ if (!cmd)
+ return gpg_error_from_syserror ();
+ err = gpgsm_assuan_simple_command (gpgsm, cmd, NULL, NULL);
+ free (cmd);
+ if (err && gpg_err_code (err) != GPG_ERR_UNKNOWN_OPTION)
+ return err;
+ }
+
/* We need to know the fd used by assuan for reads. We do this by
using the assumption that the first returned fd from
assuan_get_active_fds() is always this one. */
@@ -1293,8 +1327,57 @@ set_recipients (engine_gpgsm_t gpgsm, gpgme_key_t recp[])
}
+/* Take recipients from the LF delimited STRING and send RECIPIENT
+ * commands to gpgsm. */
static gpgme_error_t
-gpgsm_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags,
+set_recipients_from_string (engine_gpgsm_t gpgsm, const char *string)
+{
+ gpg_error_t err = 0;
+ char *line = NULL;
+ int no_pubkey = 0;
+ const char *s;
+ int n;
+
+ for (;;)
+ {
+ while (*string == ' ' || *string == '\t')
+ string++;
+ if (!*string)
+ break;
+
+ s = strchr (string, '\n');
+ if (s)
+ n = s - string;
+ else
+ n = strlen (string);
+ while (n && (string[n-1] == ' ' || string[n-1] == '\t'))
+ n--;
+
+ gpgrt_free (line);
+ if (gpgrt_asprintf (&line, "RECIPIENT %.*s", n, string) < 0)
+ {
+ err = gpg_error_from_syserror ();
+ break;
+ }
+ string += n + !!s;
+
+ err = gpgsm_assuan_simple_command (gpgsm, line, gpgsm->status.fnc,
+ gpgsm->status.fnc_value);
+
+ /* Fixme: Improve error reporting. */
+ if (gpg_err_code (err) == GPG_ERR_NO_PUBKEY)
+ no_pubkey++;
+ else if (err)
+ break;
+ }
+ gpgrt_free (line);
+ return err? err : no_pubkey? gpg_error (GPG_ERR_NO_PUBKEY) : 0;
+}
+
+
+static gpgme_error_t
+gpgsm_encrypt (void *engine, gpgme_key_t recp[], const char *recpstring,
+ gpgme_encrypt_flags_t flags,
gpgme_data_t plain, gpgme_data_t ciph, int use_armor)
{
engine_gpgsm_t gpgsm = engine;
@@ -1305,7 +1388,7 @@ gpgsm_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags,
if (!recp)
return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
- if (flags & GPGME_ENCRYPT_NO_ENCRYPT_TO)
+ if ((flags & GPGME_ENCRYPT_NO_ENCRYPT_TO))
{
err = gpgsm_assuan_simple_command (gpgsm,
"OPTION no-encrypt-to", NULL, NULL);
@@ -1325,7 +1408,10 @@ gpgsm_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags,
gpgsm_clear_fd (gpgsm, MESSAGE_FD);
gpgsm->inline_data = NULL;
- err = set_recipients (gpgsm, recp);
+ if (!recp && recpstring)
+ err = set_recipients_from_string (gpgsm, recpstring);
+ else
+ err = set_recipients (gpgsm, recp);
if (!err)
err = start (gpgsm, "ENCRYPT");
@@ -2102,6 +2188,7 @@ struct engine_ops _gpgme_engine_ops_gpgsm =
gpgsm_set_colon_line_handler,
gpgsm_set_locale,
NULL, /* set_protocol */
+ gpgsm_set_engine_flags,
gpgsm_decrypt,
gpgsm_delete, /* decrypt_verify */
NULL, /* edit */
diff --git a/src/engine-spawn.c b/src/engine-spawn.c
index 7044781..7b7a9cd 100644
--- a/src/engine-spawn.c
+++ b/src/engine-spawn.c
@@ -241,7 +241,8 @@ engspawn_start (engine_spawn_t esp, const char *file, const char *argv[],
spflags |= IOSPAWN_FLAG_DETACHED;
if ((flags & GPGME_SPAWN_ALLOW_SET_FG))
spflags |= IOSPAWN_FLAG_ALLOW_SET_FG;
-
+ if ((flags & GPGME_SPAWN_SHOW_WINDOW))
+ spflags |= IOSPAWN_FLAG_SHOW_WINDOW;
err = build_fd_data_map (esp);
if (err)
@@ -448,6 +449,7 @@ struct engine_ops _gpgme_engine_ops_spawn =
NULL, /* set_colon_line_handler */
NULL, /* set_locale */
NULL, /* set_protocol */
+ NULL, /* set_engine_flags */
NULL, /* decrypt */
NULL, /* delete */
NULL, /* edit */
diff --git a/src/engine-uiserver.c b/src/engine-uiserver.c
index bc3f3fb..d8f4fce 100644
--- a/src/engine-uiserver.c
+++ b/src/engine-uiserver.c
@@ -1075,8 +1075,58 @@ set_recipients (engine_uiserver_t uiserver, gpgme_key_t recp[])
}
+/* Take recipients from the LF delimited STRING and send RECIPIENT
+ * commands to gpgsm. */
static gpgme_error_t
-uiserver_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags,
+set_recipients_from_string (engine_uiserver_t uiserver, const char *string)
+{
+ gpg_error_t err = 0;
+ char *line = NULL;
+ int no_pubkey = 0;
+ const char *s;
+ int n;
+
+ for (;;)
+ {
+ while (*string == ' ' || *string == '\t')
+ string++;
+ if (!*string)
+ break;
+
+ s = strchr (string, '\n');
+ if (s)
+ n = s - string;
+ else
+ n = strlen (string);
+ while (n && (string[n-1] == ' ' || string[n-1] == '\t'))
+ n--;
+
+ gpgrt_free (line);
+ if (gpgrt_asprintf (&line, "RECIPIENT %.*s", n, string) < 0)
+ {
+ err = gpg_error_from_syserror ();
+ break;
+ }
+ string += n + !!s;
+
+ err = uiserver_assuan_simple_command (uiserver, line,
+ uiserver->status.fnc,
+ uiserver->status.fnc_value);
+
+ /* Fixme: Improve error reporting. */
+ if (gpg_err_code (err) == GPG_ERR_NO_PUBKEY)
+ no_pubkey++;
+ else if (err)
+ break;
+ }
+ gpgrt_free (line);
+ return err? err : no_pubkey? gpg_error (GPG_ERR_NO_PUBKEY) : 0;
+}
+
+
+static gpgme_error_t
+uiserver_encrypt (void *engine, gpgme_key_t recp[], const char *recpstring,
+ gpgme_encrypt_flags_t flags,
gpgme_data_t plain, gpgme_data_t ciph, int use_armor)
{
engine_uiserver_t uiserver = engine;
@@ -1140,9 +1190,12 @@ uiserver_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags,
uiserver->inline_data = NULL;
- if (recp)
+ if (recp || recpstring)
{
- err = set_recipients (uiserver, recp);
+ if (recp)
+ err = set_recipients (uiserver, recp);
+ else
+ err = set_recipients_from_string (uiserver, recpstring);
if (err)
{
gpgrt_free (cmd);
@@ -1368,6 +1421,7 @@ struct engine_ops _gpgme_engine_ops_uiserver =
uiserver_set_colon_line_handler,
uiserver_set_locale,
uiserver_set_protocol,
+ NULL, /* set_engine_flags */
uiserver_decrypt,
NULL, /* delete */
NULL, /* edit */
diff --git a/src/engine.c b/src/engine.c
index 28ba9fd..b716ca2 100644
--- a/src/engine.c
+++ b/src/engine.c
@@ -651,6 +651,26 @@ _gpgme_engine_set_protocol (engine_t engine, gpgme_protocol_t protocol)
}
+/* Pass information about the current context to the engine. The
+ * engine may use this context to retrieve context specific flags.
+ * Important: The engine is required to immediately copy the required
+ * flags to its own context!
+ *
+ * This function will eventually be used to reduce the number of
+ * explicit passed flags. */
+void
+_gpgme_engine_set_engine_flags (engine_t engine, gpgme_ctx_t ctx)
+{
+ if (!engine)
+ return;
+
+ if (!engine->ops->set_engine_flags)
+ return;
+
+ (*engine->ops->set_engine_flags) (engine->engine, ctx);
+}
+
+
gpgme_error_t
_gpgme_engine_op_decrypt (engine_t engine,
gpgme_decrypt_flags_t flags,
@@ -701,6 +721,7 @@ _gpgme_engine_op_edit (engine_t engine, int type, gpgme_key_t key,
gpgme_error_t
_gpgme_engine_op_encrypt (engine_t engine, gpgme_key_t recp[],
+ const char *recpstring,
gpgme_encrypt_flags_t flags,
gpgme_data_t plain, gpgme_data_t ciph, int use_armor)
{
@@ -710,13 +731,14 @@ _gpgme_engine_op_encrypt (engine_t engine, gpgme_key_t recp[],
if (!engine->ops->encrypt)
return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
- return (*engine->ops->encrypt) (engine->engine, recp, flags, plain, ciph,
- use_armor);
+ return (*engine->ops->encrypt) (engine->engine, recp, recpstring,
+ flags, plain, ciph, use_armor);
}
gpgme_error_t
_gpgme_engine_op_encrypt_sign (engine_t engine, gpgme_key_t recp[],
+ const char *recpstring,
gpgme_encrypt_flags_t flags,
gpgme_data_t plain, gpgme_data_t ciph,
int use_armor, gpgme_ctx_t ctx /* FIXME */)
@@ -727,8 +749,8 @@ _gpgme_engine_op_encrypt_sign (engine_t engine, gpgme_key_t recp[],
if (!engine->ops->encrypt_sign)
return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
- return (*engine->ops->encrypt_sign) (engine->engine, recp, flags,
- plain, ciph, use_armor, ctx);
+ return (*engine->ops->encrypt_sign) (engine->engine, recp, recpstring,
+ flags, plain, ciph, use_armor, ctx);
}
diff --git a/src/engine.h b/src/engine.h
index 0bf1bb2..8b692f2 100644
--- a/src/engine.h
+++ b/src/engine.h
@@ -69,6 +69,7 @@ gpgme_error_t _gpgme_engine_set_locale (engine_t engine, int category,
const char *value);
gpgme_error_t _gpgme_engine_set_protocol (engine_t engine,
gpgme_protocol_t protocol);
+void _gpgme_engine_set_engine_flags (engine_t engine, gpgme_ctx_t ctx);
void _gpgme_engine_release (engine_t engine);
void _gpgme_engine_set_status_cb (engine_t engine,
gpgme_status_cb_t cb, void *cb_value);
@@ -97,11 +98,13 @@ gpgme_error_t _gpgme_engine_op_edit (engine_t engine, int type,
gpgme_ctx_t ctx /* FIXME */);
gpgme_error_t _gpgme_engine_op_encrypt (engine_t engine,
gpgme_key_t recp[],
+ const char *recpstring,
gpgme_encrypt_flags_t flags,
gpgme_data_t plain, gpgme_data_t ciph,
int use_armor);
gpgme_error_t _gpgme_engine_op_encrypt_sign (engine_t engine,
gpgme_key_t recp[],
+ const char *recpstring,
gpgme_encrypt_flags_t flags,
gpgme_data_t plain,
gpgme_data_t ciph,
diff --git a/src/get-env.c b/src/get-env.c
index 57fd419..b13706f 100644
--- a/src/get-env.c
+++ b/src/get-env.c
@@ -28,17 +28,71 @@
#include "util.h"
-#if defined(HAVE_THREAD_SAFE_GETENV) || !defined (HAVE_GETENV_R)
-/* We prefer using getenv() if it is thread-safe. */
-
/* Retrieve the environment variable NAME and return a copy of it in a
malloc()'ed buffer in *VALUE. If the environment variable is not
set, return NULL in *VALUE. */
+
+#ifdef HAVE_GETENV_R
+#define INITIAL_GETENV_SIZE 32
+
+gpgme_error_t
+_gpgme_getenv (const char *name, char **value)
+{
+ size_t len = INITIAL_GETENV_SIZE;
+ char *env_value;
+
+ env_value = malloc (len);
+
+ while (1)
+ {
+ *value = env_value;
+ if (!env_value)
+ return gpg_error_from_syserror ();
+
+ if (getenv_r (name, env_value, len) == 0)
+ break;
+
+ if (errno == ERANGE)
+ {
+ len *= 2;
+ env_value = realloc (env_value, len);
+ }
+ else
+ {
+ int saved = errno;
+
+ free (env_value);
+ *value = NULL;
+ if (errno == ENOENT)
+ return 0;
+ else
+ return gpg_error_from_errno (saved);
+ }
+ }
+
+ return 0;
+}
+#else
+#ifndef HAVE_THREAD_SAFE_GETENV
+GPGRT_LOCK_DEFINE (environ_lock);
+#endif
+
gpgme_error_t
_gpgme_getenv (const char *name, char **value)
{
char *env_value;
+ gpgme_error_t err = 0;
+
+#ifndef HAVE_THREAD_SAFE_GETENV
+ gpg_err_code_t rc;
+ rc= gpgrt_lock_lock (&environ_lock);
+ if (rc)
+ {
+ err = gpg_error (rc);
+ goto leave;
+ }
+#endif
env_value = getenv (name);
if (!env_value)
*value = NULL;
@@ -46,14 +100,14 @@ _gpgme_getenv (const char *name, char **value)
{
*value = strdup (env_value);
if (!*value)
- return gpg_error_from_syserror ();
+ err = gpg_error_from_syserror ();
}
- return 0;
+#ifndef HAVE_THREAD_SAFE_GETENV
+ rc = gpgrt_lock_unlock (&environ_lock);
+ if (rc)
+ err = gpg_error (rc);
+ leave:
+#endif
+ return err;
}
-
-#else
-
-/* FIXME: Implement this when we have the specification for it. */
-#error Use of getenv_r not implemented.
-
#endif
diff --git a/src/gpgme-json.c b/src/gpgme-json.c
new file mode 100644
index 0000000..f1e9f25
--- /dev/null
+++ b/src/gpgme-json.c
@@ -0,0 +1,1772 @@
+/* gpgme-json.c - JSON based interface to gpgme (server)
+ * Copyright (C) 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+
+ */
+
+/* This is tool implements the Native Messaging protocol of web
+ * browsers and provides the server part of it. A Javascript based
+ * client can be found in lang/javascript.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#ifdef HAVE_LOCALE_H
+#include <locale.h>
+#endif
+#include <stdint.h>
+#include <sys/stat.h>
+
+#define GPGRT_ENABLE_ES_MACROS 1
+#define GPGRT_ENABLE_LOG_MACROS 1
+#define GPGRT_ENABLE_ARGPARSE_MACROS 1
+#include "gpgme.h"
+#include "cJSON.h"
+
+
+#if GPGRT_VERSION_NUMBER < 0x011c00 /* 1.28 */
+int main (void){fputs ("Build with Libgpg-error >= 1.28!\n", stderr);return 1;}
+#else /* libgpg-error >= 1.28 */
+
+/* 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)
+#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);
+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);
+
+
+/* True if interactive mode is active. */
+static int opt_interactive;
+/* True is debug mode is active. */
+static int opt_debug;
+
+/* Pending data to be returned by a getmore command. */
+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;
+
+
+/*
+ * 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) \
+ xoutofcore ("calloc"); \
+ _r; })
+#define xstrdup(a) ({ \
+ char *_r = gpgrt_strdup ((a)); \
+ if (!_r) \
+ xoutofcore ("strdup"); \
+ _r; })
+#define xstrconcat(a, ...) ({ \
+ char *_r = gpgrt_strconcat ((a), __VA_ARGS__); \
+ if (!_r) \
+ xoutofcore ("strconcat"); \
+ _r; })
+#define xfree(a) gpgrt_free ((a))
+
+#define spacep(p) (*(p) == ' ' || *(p) == '\t')
+
+#ifndef HAVE_STPCPY
+static GPGRT_INLINE char *
+_my_stpcpy (char *a, const char *b)
+{
+ while (*b)
+ *a++ = *b++;
+ *a = 0;
+ return a;
+}
+#define stpcpy(a,b) _my_stpcpy ((a), (b))
+#endif /*!HAVE_STPCPY*/
+
+
+
+static void
+xoutofcore (const char *type)
+{
+ gpg_error_t err = gpg_error_from_syserror ();
+ log_error ("%s failed: %s\n", type, gpg_strerror (err));
+ exit (2);
+}
+
+
+/* Call cJSON_CreateObject but terminate in case of an error. */
+static cjson_t
+xjson_CreateObject (void)
+{
+ cjson_t json = cJSON_CreateObject ();
+ if (!json)
+ xoutofcore ("cJSON_CreateObject");
+ return json;
+}
+
+
+/* Wrapper around cJSON_AddStringToObject which returns an gpg-error
+ * code instead of the NULL or the new object. */
+static gpg_error_t
+cjson_AddStringToObject (cjson_t object, const char *name, const char *string)
+{
+ if (!cJSON_AddStringToObject (object, name, string))
+ return gpg_error_from_syserror ();
+ return 0;
+}
+
+
+/* Same as cjson_AddStringToObject but prints an error message and
+ * terminates the process. */
+static void
+xjson_AddStringToObject (cjson_t object, const char *name, const char *string)
+{
+ if (!cJSON_AddStringToObject (object, name, string))
+ xoutofcore ("cJSON_AddStringToObject");
+}
+
+
+/* Wrapper around cJSON_AddBoolToObject which terminates the process
+ * in case of an error. */
+static void
+xjson_AddBoolToObject (cjson_t object, const char *name, int abool)
+{
+ if (!cJSON_AddBoolToObject (object, name, abool))
+ xoutofcore ("cJSON_AddStringToObject");
+ return ;
+}
+
+/* This is similar to cJSON_AddStringToObject but takes (DATA,
+ * DATALEN) and adds it under NAME as a base 64 encoded string to
+ * OBJECT. */
+static gpg_error_t
+add_base64_to_object (cjson_t object, const char *name,
+ const void *data, size_t datalen)
+{
+#if GPGRT_VERSION_NUMBER < 0x011d00 /* 1.29 */
+ return gpg_error (GPG_ERR_NOT_SUPPORTED);
+#else
+ gpg_err_code_t err;
+ estream_t fp = NULL;
+ gpgrt_b64state_t state = NULL;
+ cjson_t j_str = NULL;
+ void *buffer = NULL;
+
+ fp = es_fopenmem (0, "rwb");
+ if (!fp)
+ {
+ err = gpg_err_code_from_syserror ();
+ goto leave;
+ }
+ state = gpgrt_b64enc_start (fp, "");
+ if (!state)
+ {
+ err = gpg_err_code_from_syserror ();
+ goto leave;
+ }
+
+ err = gpgrt_b64enc_write (state, data, datalen);
+ if (err)
+ goto leave;
+
+ err = gpgrt_b64enc_finish (state);
+ state = NULL;
+ if (err)
+ return err;
+
+ es_fputc (0, fp);
+ if (es_fclose_snatch (fp, &buffer, NULL))
+ {
+ fp = NULL;
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ fp = NULL;
+
+ j_str = cJSON_CreateStringConvey (buffer);
+ if (!j_str)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ buffer = NULL;
+
+ if (!cJSON_AddItemToObject (object, name, j_str))
+ {
+ err = gpg_error_from_syserror ();
+ cJSON_Delete (j_str);
+ j_str = NULL;
+ goto leave;
+ }
+ j_str = NULL;
+
+ leave:
+ xfree (buffer);
+ cJSON_Delete (j_str);
+ gpgrt_b64enc_finish (state);
+ es_fclose (fp);
+ return err;
+#endif
+}
+
+
+/* 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)
+{
+ cjson_t response, j_tmp;
+ char *msg;
+
+ msg = gpgrt_vbsprintf (message, arg_ptr);
+ if (!msg)
+ xoutofcore ("error_object");
+
+ response = json? json : xjson_CreateObject ();
+
+ if (!(j_tmp = cJSON_GetObjectItem (response, "type")))
+ xjson_AddStringToObject (response, "type", "error");
+ else /* Replace existing "type". */
+ {
+ j_tmp = cJSON_CreateString ("error");
+ if (!j_tmp)
+ xoutofcore ("cJSON_CreateString");
+ cJSON_ReplaceItemInObject (response, "type", j_tmp);
+ }
+ xjson_AddStringToObject (response, "msg", msg);
+
+ xfree (msg);
+ return response;
+}
+
+
+/* Call cJSON_Print but terminate in case of an error. */
+static char *
+xjson_Print (cjson_t object)
+{
+ char *buf;
+ buf = cJSON_Print (object);
+ if (!buf)
+ xoutofcore ("cJSON_Print");
+ return buf;
+}
+
+
+static cjson_t
+error_object (cjson_t json, const char *message, ...)
+{
+ cjson_t response;
+ va_list arg_ptr;
+
+ va_start (arg_ptr, message);
+ response = error_object_v (json, message, arg_ptr);
+ va_end (arg_ptr);
+ return response;
+}
+
+
+static char *
+error_object_string (const char *message, ...)
+{
+ cjson_t response;
+ va_list arg_ptr;
+ char *msg;
+
+ va_start (arg_ptr, message);
+ response = error_object_v (NULL, message, arg_ptr);
+ va_end (arg_ptr);
+
+ msg = xjson_Print (response);
+ cJSON_Delete (response);
+ return msg;
+}
+
+
+/* Get the boolean property NAME from the JSON object and store true
+ * or valse at R_VALUE. If the name is unknown the value of DEF_VALUE
+ * is returned. If the type of the value is not boolean,
+ * GPG_ERR_INV_VALUE is returned and R_VALUE set to DEF_VALUE. */
+static gpg_error_t
+get_boolean_flag (cjson_t json, const char *name, int def_value, int *r_value)
+{
+ cjson_t j_item;
+
+ j_item = cJSON_GetObjectItem (json, name);
+ if (!j_item)
+ *r_value = def_value;
+ else if (cjson_is_true (j_item))
+ *r_value = 1;
+ else if (cjson_is_false (j_item))
+ *r_value = 0;
+ else
+ {
+ *r_value = def_value;
+ return gpg_error (GPG_ERR_INV_VALUE);
+ }
+
+ return 0;
+}
+
+
+/* Get the boolean property PROTOCOL from the JSON object and store
+ * its value at R_PROTOCOL. The default is OpenPGP. */
+static gpg_error_t
+get_protocol (cjson_t json, gpgme_protocol_t *r_protocol)
+{
+ cjson_t j_item;
+
+ *r_protocol = GPGME_PROTOCOL_OpenPGP;
+ j_item = cJSON_GetObjectItem (json, "protocol");
+ if (!j_item)
+ ;
+ else if (!cjson_is_string (j_item))
+ return gpg_error (GPG_ERR_INV_VALUE);
+ else if (!strcmp(j_item->valuestring, "openpgp"))
+ ;
+ else if (!strcmp(j_item->valuestring, "cms"))
+ *r_protocol = GPGME_PROTOCOL_CMS;
+ else
+ return gpg_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
+
+ return 0;
+}
+
+
+/* Get the chunksize from JSON and store it at R_CHUNKSIZE. */
+static gpg_error_t
+get_chunksize (cjson_t json, size_t *r_chunksize)
+{
+ cjson_t j_item;
+
+ *r_chunksize = DEF_REPLY_CHUNK_SIZE;
+ j_item = cJSON_GetObjectItem (json, "chunksize");
+ if (!j_item)
+ ;
+ else if (!cjson_is_number (j_item))
+ return gpg_error (GPG_ERR_INV_VALUE);
+ else if ((size_t)j_item->valueint < MIN_REPLY_CHUNK_SIZE)
+ *r_chunksize = MIN_REPLY_CHUNK_SIZE;
+ else if ((size_t)j_item->valueint > MAX_REPLY_CHUNK_SIZE)
+ *r_chunksize = MAX_REPLY_CHUNK_SIZE;
+ else
+ *r_chunksize = (size_t)j_item->valueint;
+
+ return 0;
+}
+
+
+/* Extract the keys from the "keys" array 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)
+{
+ cjson_t j_keys, j_item;
+ int i, nkeys;
+ char *p;
+ size_t length;
+
+ *r_keystring = NULL;
+
+ j_keys = cJSON_GetObjectItem (json, "keys");
+ if (!j_keys)
+ return gpg_error (GPG_ERR_NO_KEY);
+ if (!cjson_is_array (j_keys) && !cjson_is_string (j_keys))
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ /* Fixme: We should better use a membuf like thing. */
+ length = 1; /* For the EOS. */
+ if (cjson_is_string (j_keys))
+ {
+ nkeys = 1;
+ length += strlen (j_keys->valuestring);
+ if (strchr (j_keys->valuestring, '\n'))
+ return gpg_error (GPG_ERR_INV_USER_ID);
+ }
+ else
+ {
+ nkeys = cJSON_GetArraySize (j_keys);
+ if (!nkeys)
+ return gpg_error (GPG_ERR_NO_KEY);
+ for (i=0; i < nkeys; i++)
+ {
+ j_item = cJSON_GetArrayItem (j_keys, i);
+ if (!j_item || !cjson_is_string (j_item))
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (i)
+ length++; /* Space for delimiter. */
+ length += strlen (j_item->valuestring);
+ if (strchr (j_item->valuestring, '\n'))
+ return gpg_error (GPG_ERR_INV_USER_ID);
+ }
+ }
+
+ p = *r_keystring = xtrymalloc (length);
+ if (!p)
+ return gpg_error_from_syserror ();
+
+ if (cjson_is_string (j_keys))
+ {
+ strcpy (p, j_keys->valuestring);
+ }
+ else
+ {
+ for (i=0; i < nkeys; i++)
+ {
+ j_item = cJSON_GetArrayItem (j_keys, i);
+ if (i)
+ *p++ = '\n'; /* Add delimiter. */
+ p = stpcpy (p, j_item->valuestring);
+ }
+ }
+ return 0;
+}
+
+
+
+
+/*
+ * GPGME support functions.
+ */
+
+/* Helper for get_context. */
+static gpgme_ctx_t
+_create_new_context (gpgme_protocol_t proto)
+{
+ gpg_error_t err;
+ gpgme_ctx_t ctx;
+
+ err = gpgme_new (&ctx);
+ if (err)
+ log_fatal ("error creating GPGME context: %s\n", gpg_strerror (err));
+ gpgme_set_protocol (ctx, proto);
+ gpgme_set_ctx_flag (ctx, "request-origin", "browser");
+ return ctx;
+}
+
+
+/* Return a context object for protocol PROTO. This is currently a
+ * statuically allocated context initialized for PROTO. Termnates
+ * process on failure. */
+static gpgme_ctx_t
+get_context (gpgme_protocol_t proto)
+{
+ static gpgme_ctx_t ctx_openpgp, ctx_cms;
+
+ if (proto == GPGME_PROTOCOL_OpenPGP)
+ {
+ if (!ctx_openpgp)
+ ctx_openpgp = _create_new_context (proto);
+ return ctx_openpgp;
+ }
+ else if (proto == GPGME_PROTOCOL_CMS)
+ {
+ if (!ctx_cms)
+ ctx_cms = _create_new_context (proto);
+ return ctx_cms;
+ }
+ else
+ log_bug ("invalid protocol %d requested\n", proto);
+}
+
+
+
+/* Free context object retrieved by get_context. */
+static void
+release_context (gpgme_ctx_t ctx)
+{
+ /* Nothing to do right now. */
+ (void)ctx;
+}
+
+
+
+/* Given a Base-64 encoded string object in JSON return a gpgme data
+ * object at R_DATA. */
+static gpg_error_t
+data_from_base64_string (gpgme_data_t *r_data, cjson_t json)
+{
+#if GPGRT_VERSION_NUMBER < 0x011d00 /* 1.29 */
+ *r_data = NULL;
+ return gpg_error (GPG_ERR_NOT_SUPPORTED);
+#else
+ gpg_error_t err;
+ size_t len;
+ char *buf = NULL;
+ gpgrt_b64state_t state = NULL;
+ gpgme_data_t data = NULL;
+
+ *r_data = NULL;
+
+ /* A quick check on the JSON. */
+ if (!cjson_is_string (json))
+ {
+ err = gpg_error (GPG_ERR_INV_VALUE);
+ goto leave;
+ }
+
+ state = gpgrt_b64dec_start (NULL);
+ if (!state)
+ {
+ err = gpg_err_code_from_syserror ();
+ goto leave;
+ }
+
+ /* Fixme: Data duplication - we should see how to snatch the memory
+ * from the json object. */
+ len = strlen (json->valuestring);
+ buf = xtrystrdup (json->valuestring);
+ if (!buf)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+
+ err = gpgrt_b64dec_proc (state, buf, len, &len);
+ if (err)
+ goto leave;
+
+ err = gpgrt_b64dec_finish (state);
+ state = NULL;
+ if (err)
+ goto leave;
+
+ err = gpgme_data_new_from_mem (&data, buf, len, 1);
+ if (err)
+ goto leave;
+ *r_data = data;
+ data = NULL;
+
+ leave:
+ xfree (data);
+ xfree (buf);
+ gpgrt_b64dec_finish (state);
+ return err;
+#endif
+}
+
+
+
+/*
+ * Implementation of the commands.
+ */
+
+
+/* Create a "data" object and the "type", "base64" and "more" flags
+ * from DATA and append them to RESULT. Ownership if 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 take as rtue or false. Note that
+ * op_getmore has similar code but works on PENDING_DATA which is set
+ * here. */
+static gpg_error_t
+make_data_object (cjson_t result, gpgme_data_t data, size_t chunksize,
+ const char *type, int base64)
+{
+ gpg_error_t err;
+ char *buffer;
+ size_t buflen;
+ int c;
+
+ if (!base64 || base64 == -1) /* Make sure that we really have a string. */
+ gpgme_data_write (data, "", 1);
+
+ buffer = gpgme_data_release_and_get_mem (data, &buflen);
+ data = NULL;
+ if (!buffer)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+
+ if (base64 == -1)
+ {
+ base64 = 0;
+ if (!buflen)
+ log_fatal ("Appended Nul byte got lost\n");
+ if (memchr (buffer, 0, buflen-1))
+ {
+ buflen--; /* Adjust for the extra nul byte. */
+ base64 = 1;
+ }
+ /* Fixme: We might want to do more advanced heuristics than to
+ * only look for a Nul. */
+ }
+
+ /* 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)
+ {
+ xjson_AddBoolToObject (result, "more", 1);
+
+ 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;
+
+ pending_data.buffer = buffer;
+ buffer = NULL;
+ pending_data.length = buflen;
+ pending_data.written = chunksize;
+ pending_data.type = type;
+ pending_data.base64 = base64;
+ }
+ else
+ {
+ if (base64)
+ err = add_base64_to_object (result, "data", buffer, buflen);
+ else
+ err = cjson_AddStringToObject (result, "data", buffer);
+ }
+
+ leave:
+ gpgme_free (buffer);
+ return err;
+}
+
+
+
+static const char hlp_encrypt[] =
+ "op: \"encrypt\"\n"
+ "keys: Array of strings with the fingerprints or user-ids\n"
+ " of the keys to encrypt the data. For a single key\n"
+ " a String may be used instead of an array.\n"
+ "data: Input data. \n"
+ "\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"
+ "mime: Indicate that data is a MIME object.\n"
+ "armor: Request output in armored format.\n"
+ "always-trust: Request --always-trust option.\n"
+ "no-encrypt-to: Do not use a default recipient.\n"
+ "no-compress: Do not compress the plaintext first.\n"
+ "throw-keyids: Request the --throw-keyids option.\n"
+ "want-address: Require that the keys include a mail address.\n"
+ "wrap: Assume the input is an OpenPGP message.\n"
+ "\n"
+ "Response on success:\n"
+ "type: \"ciphertext\"\n"
+ "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.";
+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;
+ 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;
+
+ 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;
+
+ if ((err = get_boolean_flag (request, "armor", 0, &abool)))
+ goto leave;
+ gpgme_set_armor (ctx, abool);
+ if ((err = get_boolean_flag (request, "always-trust", 0, &abool)))
+ goto leave;
+ if (abool)
+ encrypt_flags |= GPGME_ENCRYPT_ALWAYS_TRUST;
+ if ((err = get_boolean_flag (request, "no-encrypt-to", 0,&abool)))
+ goto leave;
+ if (abool)
+ encrypt_flags |= GPGME_ENCRYPT_NO_ENCRYPT_TO;
+ if ((err = get_boolean_flag (request, "no-compress", 0, &abool)))
+ goto leave;
+ if (abool)
+ encrypt_flags |= GPGME_ENCRYPT_NO_COMPRESS;
+ if ((err = get_boolean_flag (request, "throw-keyids", 0, &abool)))
+ goto leave;
+ if (abool)
+ encrypt_flags |= GPGME_ENCRYPT_THROW_KEYIDS;
+ if ((err = get_boolean_flag (request, "wrap", 0, &abool)))
+ goto leave;
+ if (abool)
+ encrypt_flags |= GPGME_ENCRYPT_WRAP;
+ if ((err = get_boolean_flag (request, "want-address", 0, &abool)))
+ goto leave;
+ if (abool)
+ encrypt_flags |= GPGME_ENCRYPT_WANT_ADDRESS;
+
+
+ /* Get the keys. */
+ err = get_keys (request, &keystring);
+ if (err)
+ {
+ /* Provide a custom error response. */
+ error_object (result, "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)
+ {
+ 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;
+ }
+ }
+ if (opt_mime)
+ gpgme_data_set_encoding (input, GPGME_DATA_ENCODING_MIME);
+
+
+ /* Create an output data object. */
+ err = gpgme_data_new (&output);
+ if (err)
+ {
+ error_object (result, "Error creating output data object: %s",
+ gpg_strerror (err));
+ goto leave;
+ }
+
+ /* Encrypt. */
+ err = gpgme_op_encrypt_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));
+ 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;
+
+ leave:
+ xfree (keystring);
+ release_context (ctx);
+ gpgme_data_release (input);
+ gpgme_data_release (output);
+ return err;
+}
+
+
+
+static const char hlp_decrypt[] =
+ "op: \"decrypt\"\n"
+ "data: The encrypted data.\n"
+ "\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.";
+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;
+
+ 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);
+ 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));
+ goto leave;
+ }
+
+ /* Decrypt. */
+ err = gpgme_op_decrypt_ext (ctx, GPGME_DECRYPT_VERIFY,
+ input, output);
+ decrypt_result = gpgme_op_decrypt_result (ctx);
+ if (err)
+ {
+ error_object (result, "Decryption failed: %s", gpg_strerror (err));
+ goto leave;
+ }
+ gpgme_data_release (input);
+ input = NULL;
+
+ if (decrypt_result->is_mime)
+ xjson_AddBoolToObject (result, "mime", 1);
+
+ err = make_data_object (result, output, chunksize, "plaintext", -1);
+ output = NULL;
+
+ leave:
+ release_context (ctx);
+ gpgme_data_release (input);
+ gpgme_data_release (output);
+ return err;
+}
+
+
+
+static const char hlp_getmore[] =
+ "op: \"getmore\"\n"
+ "\n"
+ "Optional parameters:\n"
+ "chunksize: Max number of bytes in the \"data\" object.\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\".";
+static gpg_error_t
+op_getmore (cjson_t request, cjson_t result)
+{
+ gpg_error_t err;
+ int c;
+ size_t n;
+ size_t chunksize;
+
+ 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;
+
+ /* 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));
+ goto leave;
+ }
+
+ xjson_AddStringToObject (result, "type", pending_data.type);
+ xjson_AddBoolToObject (result, "base64", pending_data.base64);
+
+ if (pending_data.written >= pending_data.length)
+ {
+ /* EOF reached. This should not happen but we return an empty
+ * string once in case of client errors. */
+ gpgme_free (pending_data.buffer);
+ pending_data.buffer = NULL;
+ xjson_AddBoolToObject (result, "more", 0);
+ err = cjson_AddStringToObject (result, "data", "");
+ }
+ else
+ {
+ n = pending_data.length - pending_data.written;
+ if (n > chunksize)
+ {
+ n = chunksize;
+ xjson_AddBoolToObject (result, "more", 1);
+ }
+ else
+ xjson_AddBoolToObject (result, "more", 0);
+
+ 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));
+ 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);
+ pending_data.buffer = NULL;
+ }
+ }
+ }
+
+ leave:
+ return err;
+}
+
+
+
+static const char hlp_help[] =
+ "The tool expects a JSON object with the request and responds with\n"
+ "another JSON object. Even on error a JSON object is returned. The\n"
+ "property \"op\" is mandatory and its string value selects the\n"
+ "operation; if the property \"help\" with the value \"true\" exists, the\n"
+ "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"
+ " getmore Retrieve remaining data.\n"
+ " help Help overview.";
+static gpg_error_t
+op_help (cjson_t request, cjson_t result)
+{
+ cjson_t j_tmp;
+ char *buffer = NULL;
+ const char *msg;
+
+ j_tmp = cJSON_GetObjectItem (request, "interactive_help");
+ if (opt_interactive && j_tmp && cjson_is_string (j_tmp))
+ msg = buffer = xstrconcat (hlp_help, "\n", j_tmp->valuestring, NULL);
+ else
+ msg = hlp_help;
+
+ xjson_AddStringToObject (result, "type", "help");
+ xjson_AddStringToObject (result, "msg", msg);
+
+ xfree (buffer);
+ return 0;
+}
+
+
+
+/*
+ * Dispatcher
+ */
+
+/* Process a request and return the response. The response is a newly
+ * allocated string or NULL in case of an error. */
+static char *
+process_request (const char *request)
+{
+ static struct {
+ const char *op;
+ 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 },
+ { "getmore", op_getmore, hlp_getmore },
+ { "help", op_help, hlp_help },
+ { NULL }
+ };
+ size_t erroff;
+ cjson_t json;
+ cjson_t j_tmp, j_op;
+ cjson_t response;
+ int helpmode;
+ const char *op;
+ char *res;
+ int idx;
+
+ response = xjson_CreateObject ();
+
+ json = cJSON_Parse (request, &erroff);
+ if (!json)
+ {
+ log_string (GPGRT_LOGLVL_INFO, request);
+ log_info ("invalid JSON object at offset %zu\n", erroff);
+ error_object (response, "invalid JSON object at offset %zu\n", erroff);
+ goto leave;
+ }
+
+ j_tmp = cJSON_GetObjectItem (json, "help");
+ helpmode = (j_tmp && cjson_is_true (j_tmp));
+
+ j_op = cJSON_GetObjectItem (json, "op");
+ if (!j_op || !cjson_is_string (j_op))
+ {
+ if (!helpmode)
+ {
+ error_object (response, "Property \"op\" missing");
+ goto leave;
+ }
+ op = "help"; /* Help summary. */
+ }
+ else
+ op = j_op->valuestring;
+
+ for (idx=0; optbl[idx].op; idx++)
+ if (!strcmp (op, optbl[idx].op))
+ break;
+ if (optbl[idx].op)
+ {
+ if (helpmode && strcmp (op, "help"))
+ {
+ xjson_AddStringToObject (response, "type", "help");
+ xjson_AddStringToObject (response, "op", op);
+ xjson_AddStringToObject (response, "msg", optbl[idx].helpstr);
+ }
+ else
+ {
+ gpg_error_t err;
+
+ /* 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)
+ {
+ gpgme_free (pending_data.buffer);
+ pending_data.buffer = NULL;
+ }
+
+ err = optbl[idx].handler (json, response);
+ if (err)
+ {
+ if (!(j_tmp = cJSON_GetObjectItem (response, "type"))
+ || !cjson_is_string (j_tmp)
+ || strcmp (j_tmp->valuestring, "error"))
+ {
+ /* No error type response - provide a generic one. */
+ error_object (response, "Operation failed: %s",
+ gpg_strerror (err));
+ }
+
+ xjson_AddStringToObject (response, "op", op);
+ }
+ }
+ }
+ else /* Operation not supported. */
+ {
+ error_object (response, "Unknown operation '%s'", op);
+ xjson_AddStringToObject (response, "op", op);
+ }
+
+ leave:
+ cJSON_Delete (json);
+ if (opt_interactive)
+ res = cJSON_Print (response);
+ else
+ res = cJSON_PrintUnformatted (response);
+ if (!res)
+ log_error ("Printing JSON data failed\n");
+ cJSON_Delete (response);
+ return res;
+}
+
+
+
+/*
+ * Driver code
+ */
+
+static char *
+get_file (const char *fname)
+{
+ gpg_error_t err;
+ estream_t fp;
+ struct stat st;
+ char *buf;
+ size_t buflen;
+
+ fp = es_fopen (fname, "r");
+ if (!fp)
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("can't open '%s': %s\n", fname, gpg_strerror (err));
+ return NULL;
+ }
+
+ if (fstat (es_fileno(fp), &st))
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("can't stat '%s': %s\n", fname, gpg_strerror (err));
+ es_fclose (fp);
+ return NULL;
+ }
+
+ buflen = st.st_size;
+ buf = xmalloc (buflen+1);
+ if (es_fread (buf, buflen, 1, fp) != 1)
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error reading '%s': %s\n", fname, gpg_strerror (err));
+ es_fclose (fp);
+ xfree (buf);
+ return NULL;
+ }
+ buf[buflen] = 0;
+ es_fclose (fp);
+
+ return buf;
+}
+
+
+/* Return a malloced line or NULL on EOF. Terminate on read
+ * error. */
+static char *
+get_line (void)
+{
+ char *line = NULL;
+ size_t linesize = 0;
+ gpg_error_t err;
+ size_t maxlength = 2048;
+ int n;
+ const char *s;
+ char *p;
+
+ again:
+ n = es_read_line (es_stdin, &line, &linesize, &maxlength);
+ if (n < 0)
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error reading line: %s\n", gpg_strerror (err));
+ exit (1);
+ }
+ if (!n)
+ {
+ xfree (line);
+ line = NULL;
+ return NULL; /* EOF */
+ }
+ if (!maxlength)
+ {
+ log_info ("line too long - skipped\n");
+ goto again;
+ }
+ if (memchr (line, 0, n))
+ log_info ("warning: line shortened due to embedded Nul character\n");
+
+ if (line[n-1] == '\n')
+ line[n-1] = 0;
+
+ /* Trim leading spaces. */
+ for (s=line; spacep (s); s++)
+ ;
+ if (s != line)
+ {
+ for (p=line; *s;)
+ *p++ = *s++;
+ *p = 0;
+ n = p - line;
+ }
+
+ return line;
+}
+
+
+/* Process meta commands used with the standard REPL. */
+static char *
+process_meta_commands (const char *request)
+{
+ char *result = NULL;
+
+ while (spacep (request))
+ request++;
+
+ if (!strncmp (request, "help", 4) && (spacep (request+4) || !request[4]))
+ {
+ if (request[4])
+ {
+ char *buf = xstrconcat ("{ \"help\":true, \"op\":\"", request+5,
+ "\" }", NULL);
+ result = process_request (buf);
+ xfree (buf);
+ }
+ else
+ result = process_request ("{ \"op\": \"help\","
+ " \"interactive_help\": "
+ "\"\\nMeta commands:\\n"
+ " ,read FNAME Process data from FILE\\n"
+ " ,help CMD Print help for a command\\n"
+ " ,quit Terminate process\""
+ "}");
+ }
+ else if (!strncmp (request, "quit", 4) && (spacep (request+4) || !request[4]))
+ exit (0);
+ else if (!strncmp (request, "read", 4) && (spacep (request+4) || !request[4]))
+ {
+ if (!request[4])
+ log_info ("usage: ,read FILENAME\n");
+ else
+ {
+ char *buffer = get_file (request + 5);
+ if (buffer)
+ {
+ result = process_request (buffer);
+ xfree (buffer);
+ }
+ }
+ }
+ else
+ log_info ("invalid meta command\n");
+
+ return result;
+}
+
+
+/* If STRING has a help response, return the MSG property in a human
+ * readable format. */
+static char *
+get_help_msg (const char *string)
+{
+ cjson_t json, j_type, j_msg;
+ const char *msg;
+ char *buffer = NULL;
+ char *p;
+
+ json = cJSON_Parse (string, NULL);
+ if (json)
+ {
+ j_type = cJSON_GetObjectItem (json, "type");
+ if (j_type && cjson_is_string (j_type)
+ && !strcmp (j_type->valuestring, "help"))
+ {
+ j_msg = cJSON_GetObjectItem (json, "msg");
+ if (j_msg || cjson_is_string (j_msg))
+ {
+ msg = j_msg->valuestring;
+ buffer = malloc (strlen (msg)+1);
+ if (buffer)
+ {
+ for (p=buffer; *msg; msg++)
+ {
+ if (*msg == '\\' && msg[1] == '\n')
+ *p++ = '\n';
+ else
+ *p++ = *msg;
+ }
+ *p = 0;
+ }
+ }
+ }
+ cJSON_Delete (json);
+ }
+ return buffer;
+}
+
+
+/* An interactive standard REPL. */
+static void
+interactive_repl (void)
+{
+ char *line = NULL;
+ char *request = NULL;
+ char *response = NULL;
+ char *p;
+ int first;
+
+ es_setvbuf (es_stdin, NULL, _IONBF, 0);
+#if GPGRT_VERSION_NUMBER >= 0x011d00 /* 1.29 */
+ es_fprintf (es_stderr, "%s %s ready (enter \",help\" for help)\n",
+ gpgrt_strusage (11), gpgrt_strusage (13));
+#endif
+ do
+ {
+ es_fputs ("> ", es_stderr);
+ es_fflush (es_stderr);
+ es_fflush (es_stdout);
+ xfree (line);
+ line = get_line ();
+ es_fflush (es_stderr);
+ es_fflush (es_stdout);
+
+ first = !request;
+ if (line && *line)
+ {
+ if (!request)
+ request = xstrdup (line);
+ else
+ request = xstrconcat (request, "\n", line, NULL);
+ }
+
+ if (!line)
+ es_fputs ("\n", es_stderr);
+
+ if (!line || !*line || (first && *request == ','))
+ {
+ /* Process the input. */
+ xfree (response);
+ response = NULL;
+ if (request && *request == ',')
+ {
+ response = process_meta_commands (request+1);
+ }
+ else if (request)
+ {
+ response = process_request (request);
+ }
+ xfree (request);
+ request = NULL;
+
+ if (response)
+ {
+ if (opt_interactive)
+ {
+ char *msg = get_help_msg (response);
+ if (msg)
+ {
+ xfree (response);
+ response = msg;
+ }
+ }
+
+ es_fputs ("===> ", es_stderr);
+ es_fflush (es_stderr);
+ for (p=response; *p; p++)
+ {
+ if (*p == '\n')
+ {
+ es_fflush (es_stdout);
+ es_fputs ("\n===> ", es_stderr);
+ es_fflush (es_stderr);
+ }
+ else
+ es_putc (*p, es_stdout);
+ }
+ es_fflush (es_stdout);
+ es_fputs ("\n", es_stderr);
+ }
+ }
+ }
+ while (line);
+
+ xfree (request);
+ xfree (response);
+ xfree (line);
+}
+
+
+/* Read and process a single request. */
+static void
+read_and_process_single_request (void)
+{
+ char *line = NULL;
+ char *request = NULL;
+ char *response = NULL;
+ size_t n;
+
+ for (;;)
+ {
+ xfree (line);
+ line = get_line ();
+ if (line && *line)
+ request = (request? xstrconcat (request, "\n", line, NULL)
+ /**/ : xstrdup (line));
+ if (!line)
+ {
+ if (request)
+ {
+ xfree (response);
+ response = process_request (request);
+ if (response)
+ {
+ es_fputs (response, es_stdout);
+ if ((n = strlen (response)) && response[n-1] != '\n')
+ es_fputc ('\n', es_stdout);
+ }
+ es_fflush (es_stdout);
+ }
+ break;
+ }
+ }
+
+ xfree (response);
+ xfree (request);
+ xfree (line);
+}
+
+
+/* The Native Messaging processing loop. */
+static void
+native_messaging_repl (void)
+{
+ gpg_error_t err;
+ uint32_t nrequest, nresponse;
+ char *request = NULL;
+ char *response = NULL;
+ size_t n;
+
+ /* Due to the length octets we need to switch the I/O stream into
+ * binary mode. */
+ es_set_binary (es_stdin);
+ es_set_binary (es_stdout);
+ es_setbuf (es_stdin, NULL); /* stdin needs to be unbuffered! */
+
+ for (;;)
+ {
+ /* Read length. Note that the protocol uses native endianess.
+ * Is it allowed to call such a thing a well thought out
+ * protocol? */
+ if (es_read (es_stdin, &nrequest, sizeof nrequest, &n))
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error reading request header: %s\n", gpg_strerror (err));
+ break;
+ }
+ if (!n)
+ break; /* EOF */
+ if (n != sizeof nrequest)
+ {
+ log_error ("error reading request header: short read\n");
+ break;
+ }
+ if (nrequest > MAX_REQUEST_SIZE)
+ {
+ log_error ("error reading request: request too long (%zu MiB)\n",
+ (size_t)nrequest / (1024*1024));
+ /* Fixme: Shall we read the request to the bit bucket and
+ * return an error reponse or just return an error reponse
+ * and terminate? Needs some testing. */
+ break;
+ }
+
+ /* Read request. */
+ request = xtrymalloc (nrequest);
+ if (!request)
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error reading request: Not enough memory for %zu MiB)\n",
+ (size_t)nrequest / (1024*1024));
+ /* FIXME: See comment above. */
+ break;
+ }
+ if (es_read (es_stdin, request, nrequest, &n))
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error reading request: %s\n", gpg_strerror (err));
+ break;
+ }
+ if (n != nrequest)
+ {
+ /* That is a protocol violation. */
+ xfree (response);
+ response = error_object_string ("Invalid request:"
+ " short read (%zu of %zu bytes)\n",
+ n, (size_t)nrequest);
+ }
+ else /* Process request */
+ {
+ if (opt_debug)
+ log_debug ("request='%s'\n", request);
+ xfree (response);
+ response = process_request (request);
+ if (opt_debug)
+ log_debug ("response='%s'\n", response);
+ }
+ nresponse = strlen (response);
+
+ /* Write response */
+ if (es_write (es_stdout, &nresponse, sizeof nresponse, &n))
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error writing request header: %s\n", gpg_strerror (err));
+ break;
+ }
+ if (n != sizeof nrequest)
+ {
+ log_error ("error writing request header: short write\n");
+ break;
+ }
+ if (es_write (es_stdout, response, nresponse, &n))
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error writing request: %s\n", gpg_strerror (err));
+ break;
+ }
+ if (n != nresponse)
+ {
+ log_error ("error writing request: short write\n");
+ break;
+ }
+ if (es_fflush (es_stdout) || es_ferror (es_stdout))
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error writing request: %s\n", gpg_strerror (err));
+ break;
+ }
+ }
+
+ xfree (response);
+ xfree (request);
+}
+
+
+
+static const char *
+my_strusage( int level )
+{
+ const char *p;
+
+ switch (level)
+ {
+ case 9: p = "LGPL-2.1-or-later"; break;
+ case 11: p = "gpgme-json"; break;
+ case 13: p = PACKAGE_VERSION; break;
+ case 14: p = "Copyright (C) 2018 g10 Code GmbH"; break;
+ case 19: p = "Please report bugs to <" PACKAGE_BUGREPORT ">.\n"; break;
+ case 1:
+ case 40:
+ p = "Usage: gpgme-json [OPTIONS]";
+ break;
+ case 41:
+ p = "Native messaging based GPGME operations.\n";
+ break;
+ case 42:
+ p = "1"; /* Flag print 40 as part of 41. */
+ break;
+ default: p = NULL; break;
+ }
+ return p;
+}
+
+int
+main (int argc, char *argv[])
+{
+#if GPGRT_VERSION_NUMBER < 0x011d00 /* 1.29 */
+
+ fprintf (stderr, "WARNING: Old libgpg-error - using limited mode\n");
+ native_messaging_repl ();
+
+#else /* This is a modern libgp-error. */
+
+ enum { CMD_DEFAULT = 0,
+ CMD_INTERACTIVE = 'i',
+ CMD_SINGLE = 's',
+ CMD_LIBVERSION = 501,
+ } cmd = CMD_DEFAULT;
+ enum {
+ OPT_DEBUG = 600
+ };
+
+ static gpgrt_opt_t opts[] = {
+ ARGPARSE_c (CMD_INTERACTIVE, "interactive", "Interactive REPL"),
+ ARGPARSE_c (CMD_SINGLE, "single", "Single request mode"),
+ ARGPARSE_c (CMD_LIBVERSION, "lib-version", "Show library version"),
+ ARGPARSE_s_n(OPT_DEBUG, "debug", "Flyswatter"),
+
+ ARGPARSE_end()
+ };
+ gpgrt_argparse_t pargs = { &argc, &argv};
+
+ gpgrt_set_strusage (my_strusage);
+
+#ifdef HAVE_SETLOCALE
+ setlocale (LC_ALL, "");
+#endif
+ gpgme_check_version (NULL);
+#ifdef LC_CTYPE
+ gpgme_set_locale (NULL, LC_CTYPE, setlocale (LC_CTYPE, NULL));
+#endif
+#ifdef LC_MESSAGES
+ gpgme_set_locale (NULL, LC_MESSAGES, setlocale (LC_MESSAGES, NULL));
+#endif
+
+ while (gpgrt_argparse (NULL, &pargs, opts))
+ {
+ switch (pargs.r_opt)
+ {
+ case CMD_INTERACTIVE:
+ opt_interactive = 1;
+ /* Fall trough. */
+ case CMD_SINGLE:
+ case CMD_LIBVERSION:
+ cmd = pargs.r_opt;
+ break;
+
+ case OPT_DEBUG: opt_debug = 1; break;
+
+ default:
+ pargs.err = ARGPARSE_PRINT_WARNING;
+ break;
+ }
+ }
+ gpgrt_argparse (NULL, &pargs, NULL);
+
+ if (!opt_debug)
+ {
+ const char *s = getenv ("GPGME_JSON_DEBUG");
+ if (s && atoi (s) > 0)
+ opt_debug = 1;
+ }
+
+ if (opt_debug)
+ {
+ const char *home = getenv ("HOME");
+ char *file = xstrconcat ("socket://",
+ home? home:"/tmp",
+ "/.gnupg/S.gpgme-json.log", NULL);
+ log_set_file (file);
+ xfree (file);
+ }
+
+ if (opt_debug)
+ { int i;
+ for (i=0; argv[i]; i++)
+ log_debug ("argv[%d]='%s'\n", i, argv[i]);
+ }
+
+ switch (cmd)
+ {
+ case CMD_DEFAULT:
+ native_messaging_repl ();
+ break;
+
+ case CMD_SINGLE:
+ read_and_process_single_request ();
+ break;
+
+ case CMD_INTERACTIVE:
+ interactive_repl ();
+ break;
+
+ case CMD_LIBVERSION:
+ printf ("Version from header: %s (0x%06x)\n",
+ GPGME_VERSION, GPGME_VERSION_NUMBER);
+ printf ("Version from binary: %s\n", gpgme_check_version (NULL));
+ printf ("Copyright blurb ...:%s\n", gpgme_check_version ("\x01\x01"));
+ break;
+ }
+
+ if (opt_debug)
+ log_debug ("ready");
+
+#endif /* This is a modern libgp-error. */
+ return 0;
+}
+#endif /* libgpg-error >= 1.28 */
diff --git a/src/gpgme-tool.c b/src/gpgme-tool.c
index 3e2dc78..e7a7a6f 100644
--- a/src/gpgme-tool.c
+++ b/src/gpgme-tool.c
@@ -3101,7 +3101,7 @@ cmd_hash_algo_name (assuan_context_t ctx, char *line)
static const char hlp_identify[] =
- "IDENTIY\n"
+ "IDENTIFY\n"
"\n"
"Identify the type of data set with the INPUT command.";
static gpg_error_t
diff --git a/src/gpgme-w32spawn.c b/src/gpgme-w32spawn.c
index d86c850..868dbd5 100644
--- a/src/gpgme-w32spawn.c
+++ b/src/gpgme-w32spawn.c
@@ -121,8 +121,6 @@ my_spawn (char **argv, struct spawn_fd_item_s *fd_list, unsigned int flags)
int duped_stdout = 0;
int duped_stderr = 0;
HANDLE hnul = INVALID_HANDLE_VALUE;
- /* FIXME. */
- int debug_me = 0;
i = 0;
while (argv[i])
@@ -142,7 +140,7 @@ my_spawn (char **argv, struct spawn_fd_item_s *fd_list, unsigned int flags)
memset (&si, 0, sizeof si);
si.cb = sizeof (si);
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
- si.wShowWindow = debug_me ? SW_SHOW : SW_HIDE;
+ si.wShowWindow = (flags & IOSPAWN_FLAG_SHOW_WINDOW) ? SW_SHOW : SW_HIDE;
si.hStdInput = GetStdHandle (STD_INPUT_HANDLE);
si.hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE);
si.hStdError = GetStdHandle (STD_ERROR_HANDLE);
diff --git a/src/gpgme.c b/src/gpgme.c
index d0a5afe..82d6747 100644
--- a/src/gpgme.c
+++ b/src/gpgme.c
@@ -248,6 +248,7 @@ gpgme_release (gpgme_ctx_t ctx)
free (ctx->lc_ctype);
free (ctx->lc_messages);
free (ctx->override_session_key);
+ free (ctx->request_origin);
_gpgme_engine_info_release (ctx->engine_info);
ctx->engine_info = NULL;
DESTROY_LOCK (ctx->lock);
@@ -486,13 +487,8 @@ gpgme_get_armor (gpgme_ctx_t ctx)
}
-/* Set the flag NAME for CTX to VALUE. The supported flags are:
- *
- * - full-status :: With a value of "1" the status callback set by
- * gpgme_set_status_cb returns all status lines
- * except for PROGRESS lines. With the default of
- * "0" the status callback is only called in certain
- * situations.
+/* Set the flag NAME for CTX to VALUE. Please consult the manual for
+ * a description of the flags.
*/
gpgme_error_t
gpgme_set_ctx_flag (gpgme_ctx_t ctx, const char *name, const char *value)
@@ -535,6 +531,17 @@ gpgme_set_ctx_flag (gpgme_ctx_t ctx, const char *name, const char *value)
{
ctx->auto_key_retrieve = abool;
}
+ else if (!strcmp (name, "request-origin"))
+ {
+ free (ctx->request_origin);
+ ctx->request_origin = strdup (value);
+ if (!ctx->request_origin)
+ err = gpg_error_from_syserror ();
+ }
+ else if (!strcmp (name, "no-symkey-cache"))
+ {
+ ctx->no_symkey_cache = abool;
+ }
else
err = gpg_error (GPG_ERR_UNKNOWN_NAME);
@@ -576,6 +583,14 @@ gpgme_get_ctx_flag (gpgme_ctx_t ctx, const char *name)
{
return ctx->auto_key_retrieve? "1":"";
}
+ else if (!strcmp (name, "request-origin"))
+ {
+ return ctx->request_origin? ctx->request_origin : "";
+ }
+ else if (!strcmp (name, "no-symkey-cache"))
+ {
+ return ctx->no_symkey_cache? "1":"";
+ }
else
return NULL;
}
diff --git a/src/gpgme.def b/src/gpgme.def
index cad30f6..a01d89a 100644
--- a/src/gpgme.def
+++ b/src/gpgme.def
@@ -267,5 +267,10 @@ EXPORTS
gpgme_op_conf_dir @199
+ gpgme_op_encrypt_ext @200
+ gpgme_op_encrypt_ext_start @201
+ gpgme_op_encrypt_sign_ext @202
+ gpgme_op_encrypt_sign_ext_start @203
+
; END
diff --git a/src/gpgme.h.in b/src/gpgme.h.in
index 31a9060..49fafb9 100644
--- a/src/gpgme.h.in
+++ b/src/gpgme.h.in
@@ -1,6 +1,6 @@
/* gpgme.h - Public interface to GnuPG Made Easy. -*- c -*-
* Copyright (C) 2000 Werner Koch (dd9jn)
- * Copyright (C) 2001-2017 g10 Code GmbH
+ * Copyright (C) 2001-2018 g10 Code GmbH
*
* This file is part of GPGME.
*
@@ -16,6 +16,7 @@
*
* 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+
*
* Generated from gpgme.h.in for @GPGME_CONFIG_HOST@.
*/
@@ -37,14 +38,14 @@ extern "C" {
/* The version of this header should match the one of the library. Do
- not use this symbol in your application, use gpgme_check_version
- instead. The purpose of this macro is to let autoconf (using the
- AM_PATH_GPGME macro) check that this header matches the installed
- library. */
+ * not use this symbol in your application, use gpgme_check_version
+ * instead. The purpose of this macro is to let autoconf (using the
+ * AM_PATH_GPGME macro) check that this header matches the installed
+ * library. */
#define GPGME_VERSION "@PACKAGE_VERSION@"
/* The version number of this header. It may be used to handle minor
- API incompatibilities. */
+ * API incompatibilities. */
#define GPGME_VERSION_NUMBER @VERSION_NUMBER@
@@ -87,7 +88,7 @@ extern "C" {
/* The macro _GPGME_DEPRECATED_OUTSIDE_GPGME suppresses warnings for
- fields we must access in GPGME for ABI compatibility. */
+ * fields we must access in GPGME for ABI compatibility. */
#ifdef _GPGME_IN_GPGME
#define _GPGME_DEPRECATED_OUTSIDE_GPGME(a,b)
#else
@@ -113,7 +114,7 @@ extern "C" {
*/
/* The context holds some global state and configuration options, as
- well as the results of a crypto operation. */
+ * well as the results of a crypto operation. */
struct gpgme_context;
typedef struct gpgme_context *gpgme_ctx_t;
@@ -124,7 +125,8 @@ typedef struct gpgme_data *gpgme_data_t;
/*
- * Wrappers for the libgpg-error library.
+ * Wrappers for the libgpg-error library. They are generally not
+ * needed and the gpg-error versions may be used instead.
*/
typedef gpg_error_t gpgme_error_t;
@@ -140,7 +142,7 @@ gpgme_err_make (gpgme_err_source_t source, gpgme_err_code_t code)
/* The user can define GPGME_ERR_SOURCE_DEFAULT before including this
- file to specify a default source for gpgme_error. */
+ * file to specify a default source for gpgme_error. */
#ifndef GPGME_ERR_SOURCE_DEFAULT
#define GPGME_ERR_SOURCE_DEFAULT GPG_ERR_SOURCE_USER_1
#endif
@@ -167,45 +169,46 @@ gpgme_err_source (gpgme_error_t err)
/* Return a pointer to a string containing a description of the error
- code in the error value ERR. This function is not thread safe. */
+ * code in the error value ERR. This function is not thread safe. */
const char *gpgme_strerror (gpgme_error_t err);
/* Return the error string for ERR in the user-supplied buffer BUF of
- size BUFLEN. This function is, in contrast to gpg_strerror,
- thread-safe if a thread-safe strerror_r() function is provided by
- the system. If the function succeeds, 0 is returned and BUF
- contains the string describing the error. If the buffer was not
- large enough, ERANGE is returned and BUF contains as much of the
- beginning of the error string as fits into the buffer. */
+ * size BUFLEN. This function is, in contrast to gpg_strerror,
+ * thread-safe if a thread-safe strerror_r() function is provided by
+ * the system. If the function succeeds, 0 is returned and BUF
+ * contains the string describing the error. If the buffer was not
+ * large enough, ERANGE is returned and BUF contains as much of the
+ * beginning of the error string as fits into the buffer. */
int gpgme_strerror_r (gpg_error_t err, char *buf, size_t buflen);
/* Return a pointer to a string containing a description of the error
- source in the error value ERR. */
+ * source in the error value ERR. */
const char *gpgme_strsource (gpgme_error_t err);
/* Retrieve the error code for the system error ERR. This returns
- GPG_ERR_UNKNOWN_ERRNO if the system error is not mapped (report
- this). */
+ * GPG_ERR_UNKNOWN_ERRNO if the system error is not mapped (report
+ * this). */
gpgme_err_code_t gpgme_err_code_from_errno (int err);
/* Retrieve the system error for the error code CODE. This returns 0
- if CODE is not a system error code. */
+ * if CODE is not a system error code. */
int gpgme_err_code_to_errno (gpgme_err_code_t code);
/* Retrieve the error code directly from the ERRNO variable. This
- returns GPG_ERR_UNKNOWN_ERRNO if the system error is not mapped
- (report this) and GPG_ERR_MISSING_ERRNO if ERRNO has the value 0. */
+ * returns GPG_ERR_UNKNOWN_ERRNO if the system error is not mapped
+ * (report this) and GPG_ERR_MISSING_ERRNO if ERRNO has the value 0. */
gpgme_err_code_t gpgme_err_code_from_syserror (void);
/* Set the ERRNO variable. This function is the preferred way to set
- ERRNO due to peculiarities on WindowsCE. */
+ * ERRNO due to peculiarities on WindowsCE. */
void gpgme_err_set_errno (int err);
/* Return an error value with the error source SOURCE and the system
- error ERR. FIXME: Should be inline. */
+ * error ERR. FIXME: Should be inline. */
gpgme_error_t gpgme_err_make_from_errno (gpgme_err_source_t source, int err);
-/* Return an error value with the system error ERR. FIXME: Should be inline. */
+/* Return an error value with the system error ERR.
+ * inline. */
gpgme_error_t gpgme_error_from_errno (int err);
@@ -373,6 +376,8 @@ gpgme_protocol_t;
#define GPGME_KEYLIST_MODE_EPHEMERAL 128
#define GPGME_KEYLIST_MODE_VALIDATE 256
+#define GPGME_KEYLIST_MODE_LOCATE (1|2)
+
typedef unsigned int gpgme_keylist_mode_t;
@@ -417,7 +422,7 @@ struct _gpgme_sig_notation
struct _gpgme_sig_notation *next;
/* If NAME is a null pointer, then VALUE contains a policy URL
- rather than a notation. */
+ * rather than a notation. */
char *name;
/* The value of the notation data. */
@@ -632,10 +637,10 @@ struct _gpgme_key_sig
/* Same as in gpgme_signature_t. */
gpgme_error_t status;
+ /* Deprecated; use SIG_CLASS instead. */
#ifdef __cplusplus
unsigned int _obsolete_class _GPGME_DEPRECATED(0,4);
#else
- /* Must be set to SIG_CLASS below. */
unsigned int class _GPGME_DEPRECATED_OUTSIDE_GPGME(0,4);
#endif
@@ -874,10 +879,10 @@ gpgme_error_t gpgme_set_protocol (gpgme_ctx_t ctx, gpgme_protocol_t proto);
gpgme_protocol_t gpgme_get_protocol (gpgme_ctx_t ctx);
/* Set the crypto protocol to be used by CTX to PROTO.
- gpgme_set_protocol actually sets the backend engine. This sets the
- crypto protocol used in engines that support more than one crypto
- prococol (for example, an UISERVER can support OpenPGP and CMS).
- This is reset to the default with gpgme_set_protocol. */
+ * gpgme_set_protocol actually sets the backend engine. This sets the
+ * crypto protocol used in engines that support more than one crypto
+ * prococol (for example, an UISERVER can support OpenPGP and CMS).
+ * This is reset to the default with gpgme_set_protocol. */
gpgme_error_t gpgme_set_sub_protocol (gpgme_ctx_t ctx,
gpgme_protocol_t proto);
@@ -929,47 +934,47 @@ gpgme_error_t gpgme_set_pinentry_mode (gpgme_ctx_t ctx,
gpgme_pinentry_mode_t gpgme_get_pinentry_mode (gpgme_ctx_t ctx);
/* Set the passphrase callback function in CTX to CB. HOOK_VALUE is
- passed as first argument to the passphrase callback function. */
+ * passed as first argument to the passphrase callback function. */
void gpgme_set_passphrase_cb (gpgme_ctx_t ctx,
gpgme_passphrase_cb_t cb, void *hook_value);
/* Get the current passphrase callback function in *CB and the current
- hook value in *HOOK_VALUE. */
+ * hook value in *HOOK_VALUE. */
void gpgme_get_passphrase_cb (gpgme_ctx_t ctx, gpgme_passphrase_cb_t *cb,
void **hook_value);
/* Set the progress callback function in CTX to CB. HOOK_VALUE is
- passed as first argument to the progress callback function. */
+ * passed as first argument to the progress callback function. */
void gpgme_set_progress_cb (gpgme_ctx_t c, gpgme_progress_cb_t cb,
void *hook_value);
/* Get the current progress callback function in *CB and the current
- hook value in *HOOK_VALUE. */
+ * hook value in *HOOK_VALUE. */
void gpgme_get_progress_cb (gpgme_ctx_t ctx, gpgme_progress_cb_t *cb,
void **hook_value);
/* Set the status callback function in CTX to CB. HOOK_VALUE is
- passed as first argument to the status callback function. */
+ * passed as first argument to the status callback function. */
void gpgme_set_status_cb (gpgme_ctx_t c, gpgme_status_cb_t cb,
void *hook_value);
/* Get the current status callback function in *CB and the current
- hook value in *HOOK_VALUE. */
+ * hook value in *HOOK_VALUE. */
void gpgme_get_status_cb (gpgme_ctx_t ctx, gpgme_status_cb_t *cb,
void **hook_value);
/* This function sets the locale for the context CTX, or the default
- locale if CTX is a null pointer. */
+ * locale if CTX is a null pointer. */
gpgme_error_t gpgme_set_locale (gpgme_ctx_t ctx, int category,
const char *value);
/* Get the information about the configured engines. A pointer to the
- first engine in the statically allocated linked list is returned.
- The returned data is valid until the next gpgme_ctx_set_engine_info. */
+ * first engine in the statically allocated linked list is returned.
+ * The returned data is valid until the next gpgme_ctx_set_engine_info. */
gpgme_engine_info_t gpgme_ctx_get_engine_info (gpgme_ctx_t ctx);
/* Set the engine info for the context CTX, protocol PROTO, to the
- file name FILE_NAME and the home directory HOME_DIR. */
+ * file name FILE_NAME and the home directory HOME_DIR. */
gpgme_error_t gpgme_ctx_set_engine_info (gpgme_ctx_t ctx,
gpgme_protocol_t proto,
const char *file_name,
@@ -991,10 +996,10 @@ gpgme_key_t gpgme_signers_enum (const gpgme_ctx_t ctx, int seq);
void gpgme_sig_notation_clear (gpgme_ctx_t ctx);
/* Add the human-readable notation data with name NAME and value VALUE
- to the context CTX, using the flags FLAGS. If NAME is NULL, then
- VALUE should be a policy URL. The flag
- GPGME_SIG_NOTATION_HUMAN_READABLE is forced to be true for notation
- data, and false for policy URLs. */
+ * to the context CTX, using the flags FLAGS. If NAME is NULL, then
+ * VALUE should be a policy URL. The flag
+ * GPGME_SIG_NOTATION_HUMAN_READABLE is forced to be true for notation
+ * data, and false for policy URLs. */
gpgme_error_t gpgme_sig_notation_add (gpgme_ctx_t ctx, const char *name,
const char *value,
gpgme_sig_notation_flags_t flags);
@@ -1018,17 +1023,17 @@ const char *gpgme_get_sender (gpgme_ctx_t ctx);
typedef gpgme_error_t (*gpgme_io_cb_t) (void *data, int fd);
/* The type of a function that can register FNC as the I/O callback
- function for the file descriptor FD with direction dir (0: for writing,
- 1: for reading). FNC_DATA should be passed as DATA to FNC. The
- function should return a TAG suitable for the corresponding
- gpgme_remove_io_cb_t, and an error value. */
+ * function for the file descriptor FD with direction dir (0: for writing,
+ * 1: for reading). FNC_DATA should be passed as DATA to FNC. The
+ * function should return a TAG suitable for the corresponding
+ * gpgme_remove_io_cb_t, and an error value. */
typedef gpgme_error_t (*gpgme_register_io_cb_t) (void *data, int fd, int dir,
gpgme_io_cb_t fnc,
void *fnc_data, void **tag);
/* The type of a function that can remove a previously registered I/O
- callback function given TAG as returned by the register
- function. */
+ * callback function given TAG as returned by the register
+ * function. */
typedef void (*gpgme_remove_io_cb_t) (void *tag);
typedef enum
@@ -1043,7 +1048,7 @@ gpgme_event_io_t;
struct gpgme_io_event_done_data
{
/* A fatal IPC error or an operational error in state-less
- protocols. */
+ * protocols. */
gpgme_error_t err;
/* An operational errors in session-based protocols. */
@@ -1052,7 +1057,7 @@ struct gpgme_io_event_done_data
typedef struct gpgme_io_event_done_data *gpgme_io_event_done_data_t;
/* The type of a function that is called when a context finished an
- operation. */
+ * operation. */
typedef void (*gpgme_event_io_cb_t) (void *data, gpgme_event_io_t type,
void *type_data);
@@ -1073,13 +1078,13 @@ void gpgme_set_io_cbs (gpgme_ctx_t ctx, gpgme_io_cbs_t io_cbs);
void gpgme_get_io_cbs (gpgme_ctx_t ctx, gpgme_io_cbs_t io_cbs);
/* Wrappers around the internal I/O functions for use with
- gpgme_passphrase_cb_t and gpgme_interact_cb_t. */
+ * gpgme_passphrase_cb_t and gpgme_interact_cb_t. */
@API__SSIZE_T@ gpgme_io_read (int fd, void *buffer, size_t count);
@API__SSIZE_T@ gpgme_io_write (int fd, const void *buffer, size_t count);
int gpgme_io_writen (int fd, const void *buffer, size_t count);
/* Process the pending operation and, if HANG is non-zero, wait for
- the pending operation to finish. */
+ * the pending operation to finish. */
gpgme_ctx_t gpgme_wait (gpgme_ctx_t ctx, gpgme_error_t *status, int hang);
gpgme_ctx_t gpgme_wait_ext (gpgme_ctx_t ctx, gpgme_error_t *status,
@@ -1098,21 +1103,21 @@ gpgme_error_t gpgme_cancel_async (gpgme_ctx_t ctx);
*/
/* Read up to SIZE bytes into buffer BUFFER from the data object with
- the handle HANDLE. Return the number of characters read, 0 on EOF
- and -1 on error. If an error occurs, errno is set. */
+ * the handle HANDLE. Return the number of characters read, 0 on EOF
+ * and -1 on error. If an error occurs, errno is set. */
typedef @API__SSIZE_T@ (*gpgme_data_read_cb_t) (void *handle, void *buffer,
size_t size);
/* Write up to SIZE bytes from buffer BUFFER to the data object with
- the handle HANDLE. Return the number of characters written, or -1
- on error. If an error occurs, errno is set. */
+ * the handle HANDLE. Return the number of characters written, or -1
+ * on error. If an error occurs, errno is set. */
typedef @API__SSIZE_T@ (*gpgme_data_write_cb_t) (void *handle, const void *buffer,
size_t size);
/* Set the current position from where the next read or write starts
- in the data object with the handle HANDLE to OFFSET, relativ to
- WHENCE. Returns the new offset in bytes from the beginning of the
- data object. */
+ * in the data object with the handle HANDLE to OFFSET, relativ to
+ * WHENCE. Returns the new offset in bytes from the beginning of the
+ * data object. */
typedef @API__OFF_T@ (*gpgme_data_seek_cb_t) (void *handle,
@API__OFF_T@ offset, int whence);
@@ -1129,19 +1134,19 @@ struct gpgme_data_cbs
typedef struct gpgme_data_cbs *gpgme_data_cbs_t;
/* 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
- -1 on error. If an error occurs, errno is set. */
+ * the handle DH. Return the number of characters read, 0 on EOF and
+ * -1 on error. If an error occurs, errno is set. */
@API__SSIZE_T@ gpgme_data_read (gpgme_data_t dh, void *buffer, size_t size);
/* Write up to SIZE bytes from buffer BUFFER to the data object with
- the handle DH. Return the number of characters written, or -1 on
- error. If an error occurs, errno is set. */
+ * the handle DH. Return the number of characters written, or -1 on
+ * error. If an error occurs, errno is set. */
@API__SSIZE_T@ gpgme_data_write (gpgme_data_t dh, const void *buffer, size_t size);
/* Set the current position from where the next read or write starts
- in the data object with the handle DH to OFFSET, relativ to WHENCE.
- Returns the new offset in bytes from the beginning of the data
- object. */
+ * in the data object with the handle DH to OFFSET, relativ to WHENCE.
+ * Returns the new offset in bytes from the beginning of the data
+ * object. */
@API__OFF_T@ gpgme_data_seek (gpgme_data_t dh, @API__OFF_T@ offset, int whence);
/* Create a new data buffer and return it in R_DH. */
@@ -1151,19 +1156,19 @@ gpgme_error_t gpgme_data_new (gpgme_data_t *r_dh);
void gpgme_data_release (gpgme_data_t dh);
/* Create a new data buffer filled with SIZE bytes starting from
- BUFFER. If COPY is zero, copying is delayed until necessary, and
- the data is taken from the original location when needed. */
+ * BUFFER. If COPY is zero, copying is delayed until necessary, and
+ * the data is taken from the original location when needed. */
gpgme_error_t gpgme_data_new_from_mem (gpgme_data_t *r_dh,
const char *buffer, size_t size,
int copy);
/* Destroy the data buffer DH and return a pointer to its content.
- The memory has be to released with gpgme_free() by the user. It's
- size is returned in R_LEN. */
+ * The memory has be to released with gpgme_free() by the user. It's
+ * size is returned in R_LEN. */
char *gpgme_data_release_and_get_mem (gpgme_data_t dh, size_t *r_len);
/* Release the memory returned by gpgme_data_release_and_get_mem() and
- some other functions. */
+ * some other functions. */
void gpgme_free (void *buffer);
gpgme_error_t gpgme_data_new_from_cbs (gpgme_data_t *dh,
@@ -1182,11 +1187,11 @@ gpgme_error_t gpgme_data_set_encoding (gpgme_data_t dh,
gpgme_data_encoding_t enc);
/* Get the file name associated with the data object with handle DH, or
- NULL if there is none. */
+ * NULL if there is none. */
char *gpgme_data_get_file_name (gpgme_data_t dh);
/* Set the file name associated with the data object with handle DH to
- FILE_NAME. */
+ * FILE_NAME. */
gpgme_error_t gpgme_data_set_file_name (gpgme_data_t dh,
const char *file_name);
@@ -1199,15 +1204,15 @@ gpgme_data_type_t gpgme_data_identify (gpgme_data_t dh, int reserved);
/* Create a new data buffer filled with the content of file FNAME.
- COPY must be non-zero. For delayed read, please use
- gpgme_data_new_from_fd or gpgme_data_new_from_stream instead. */
+ * COPY must be non-zero. For delayed read, please use
+ * gpgme_data_new_from_fd or gpgme_data_new_from_stream instead. */
gpgme_error_t gpgme_data_new_from_file (gpgme_data_t *r_dh,
const char *fname,
int copy);
/* Create a new data buffer filled with LENGTH bytes starting from
- OFFSET within the file FNAME or stream FP (exactly one must be
- non-zero). */
+ * OFFSET within the file FNAME or stream FP (exactly one must be
+ * non-zero). */
gpgme_error_t gpgme_data_new_from_filepart (gpgme_data_t *r_dh,
const char *fname, FILE *fp,
@API__OFF_T@ offset, size_t length);
@@ -1222,7 +1227,7 @@ gpgme_error_t gpgme_data_rewind (gpgme_data_t dh);
*/
/* Get the key with the fingerprint FPR from the crypto backend. If
- SECRET is true, get the secret key. */
+ * SECRET is true, get the secret key. */
gpgme_error_t gpgme_get_key (gpgme_ctx_t ctx, const char *fpr,
gpgme_key_t *r_key, int secret);
@@ -1233,7 +1238,7 @@ gpgme_error_t gpgme_key_from_uid (gpgme_key_t *key, const char *name);
void gpgme_key_ref (gpgme_key_t key);
/* Release a reference to KEY. If this was the last one the key is
- destroyed. */
+ * destroyed. */
void gpgme_key_unref (gpgme_key_t key);
void gpgme_key_release (gpgme_key_t key);
@@ -1266,22 +1271,35 @@ typedef enum
GPGME_ENCRYPT_NO_COMPRESS = 16,
GPGME_ENCRYPT_SYMMETRIC = 32,
GPGME_ENCRYPT_THROW_KEYIDS = 64,
- GPGME_ENCRYPT_WRAP = 128
+ GPGME_ENCRYPT_WRAP = 128,
+ GPGME_ENCRYPT_WANT_ADDRESS = 256
}
gpgme_encrypt_flags_t;
/* Encrypt plaintext PLAIN within CTX for the recipients RECP and
- store the resulting ciphertext in CIPHER. */
+ * store the resulting ciphertext in CIPHER. */
gpgme_error_t gpgme_op_encrypt_start (gpgme_ctx_t ctx, gpgme_key_t recp[],
gpgme_encrypt_flags_t flags,
- gpgme_data_t plain, gpgme_data_t cipher);
+ gpgme_data_t plain,
+ gpgme_data_t cipher);
gpgme_error_t gpgme_op_encrypt (gpgme_ctx_t ctx, gpgme_key_t recp[],
gpgme_encrypt_flags_t flags,
- gpgme_data_t plain, gpgme_data_t cipher);
+ gpgme_data_t plain,
+ gpgme_data_t cipher);
+gpgme_error_t gpgme_op_encrypt_ext_start (gpgme_ctx_t ctx, gpgme_key_t recp[],
+ const char *recpstring,
+ gpgme_encrypt_flags_t flags,
+ gpgme_data_t plain,
+ gpgme_data_t cipher);
+gpgme_error_t gpgme_op_encrypt_ext (gpgme_ctx_t ctx, gpgme_key_t recp[],
+ const char *recpstring,
+ gpgme_encrypt_flags_t flags,
+ gpgme_data_t plain,
+ gpgme_data_t cipher);
/* Encrypt plaintext PLAIN within CTX for the recipients RECP and
- store the resulting ciphertext in CIPHER. Also sign the ciphertext
- with the signers in CTX. */
+ * store the resulting ciphertext in CIPHER. Also sign the ciphertext
+ * with the signers in CTX. */
gpgme_error_t gpgme_op_encrypt_sign_start (gpgme_ctx_t ctx,
gpgme_key_t recp[],
gpgme_encrypt_flags_t flags,
@@ -1289,7 +1307,19 @@ gpgme_error_t gpgme_op_encrypt_sign_start (gpgme_ctx_t ctx,
gpgme_data_t cipher);
gpgme_error_t gpgme_op_encrypt_sign (gpgme_ctx_t ctx, gpgme_key_t recp[],
gpgme_encrypt_flags_t flags,
- gpgme_data_t plain, gpgme_data_t cipher);
+ gpgme_data_t plain,
+ gpgme_data_t cipher);
+gpgme_error_t gpgme_op_encrypt_sign_ext_start (gpgme_ctx_t ctx,
+ gpgme_key_t recp[],
+ const char *recpstring,
+ gpgme_encrypt_flags_t flags,
+ gpgme_data_t plain,
+ gpgme_data_t cipher);
+gpgme_error_t gpgme_op_encrypt_sign_ext (gpgme_ctx_t ctx, gpgme_key_t recp[],
+ const char *recpstring,
+ gpgme_encrypt_flags_t flags,
+ gpgme_data_t plain,
+ gpgme_data_t cipher);
/*
@@ -1317,6 +1347,7 @@ struct _gpgme_recipient
};
typedef struct _gpgme_recipient *gpgme_recipient_t;
+
/* An object to return results from a decryption operation.
* This structure shall be considered read-only and an application
* must not allocate such a structure on its own. */
@@ -1331,24 +1362,33 @@ struct _gpgme_op_decrypt_result
* mode. */
unsigned int is_de_vs : 1;
+ /* The message claims that the content is a MIME object. */
+ unsigned int is_mime : 1;
+
/* Internal to GPGME, do not use. */
- int _unused : 30;
+ int _unused : 29;
gpgme_recipient_t recipients;
/* The original file name of the plaintext message, if
- available. */
+ * available. */
char *file_name;
/* A textual representation of the session key used to decrypt the
* message, if available */
char *session_key;
+
+ /* A string with the symmetric encryption algorithm and mode using
+ * the format "<algo>.<mode>". */
+ char *symkey_algo;
};
typedef struct _gpgme_op_decrypt_result *gpgme_decrypt_result_t;
+
/* Retrieve a pointer to the result of the decrypt operation. */
gpgme_decrypt_result_t gpgme_op_decrypt_result (gpgme_ctx_t ctx);
+
/* The valid decryption flags. */
typedef enum
{
@@ -1357,15 +1397,16 @@ typedef enum
}
gpgme_decrypt_flags_t;
+
/* Decrypt ciphertext CIPHER within CTX and store the resulting
- plaintext in PLAIN. */
+ * plaintext in PLAIN. */
gpgme_error_t gpgme_op_decrypt_start (gpgme_ctx_t ctx, gpgme_data_t cipher,
gpgme_data_t plain);
gpgme_error_t gpgme_op_decrypt (gpgme_ctx_t ctx,
gpgme_data_t cipher, gpgme_data_t plain);
/* Decrypt ciphertext CIPHER and make a signature verification within
- CTX and store the resulting plaintext in PLAIN. */
+ * CTX and store the resulting plaintext in PLAIN. */
gpgme_error_t gpgme_op_decrypt_verify_start (gpgme_ctx_t ctx,
gpgme_data_t cipher,
gpgme_data_t plain);
@@ -1407,7 +1448,7 @@ struct _gpgme_new_signature
gpgme_hash_algo_t hash_algo;
/* Internal to GPGME, do not use. Must be set to the same value as
- CLASS below. */
+ * CLASS below. */
unsigned long _obsolete_class;
/* Signature creation time. */
@@ -1416,10 +1457,10 @@ struct _gpgme_new_signature
/* The fingerprint of the signature. */
char *fpr;
+ /* Deprecated; use SIG_CLASS instead. */
#ifdef __cplusplus
unsigned int _obsolete_class_2;
#else
- /* Must be set to SIG_CLASS below. */
unsigned int class _GPGME_DEPRECATED_OUTSIDE_GPGME(0,4);
#endif
@@ -1440,6 +1481,7 @@ struct _gpgme_op_sign_result
};
typedef struct _gpgme_op_sign_result *gpgme_sign_result_t;
+
/* Retrieve a pointer to the result of the signing operation. */
gpgme_sign_result_t gpgme_op_sign_result (gpgme_ctx_t ctx);
@@ -1533,6 +1575,7 @@ struct _gpgme_signature
};
typedef struct _gpgme_signature *gpgme_signature_t;
+
/* An object to return the results of a verify operation.
* This structure shall be considered read-only and an application
* must not allocate such a structure on its own. */
@@ -1543,9 +1586,16 @@ struct _gpgme_op_verify_result
/* The original file name of the plaintext message, if
available. */
char *file_name;
+
+ /* The message claims that the content is a MIME object. */
+ unsigned int is_mime : 1;
+
+ /* Internal to GPGME; do not use. */
+ unsigned int _unused : 31;
};
typedef struct _gpgme_op_verify_result *gpgme_verify_result_t;
+
/* Retrieve a pointer to the result of the verify operation. */
gpgme_verify_result_t gpgme_op_verify_result (gpgme_ctx_t ctx);
@@ -1590,6 +1640,7 @@ struct _gpgme_import_status
};
typedef struct _gpgme_import_status *gpgme_import_status_t;
+
/* Import result object.
* This structure shall be considered read-only and an application
* must not allocate such a structure on its own. */
@@ -1639,9 +1690,13 @@ struct _gpgme_op_import_result
/* List of keys for which an import was attempted. */
gpgme_import_status_t imports;
+
+ /* Number of v3 keys skipped. */
+ int skipped_v3_keys;
};
typedef struct _gpgme_op_import_result *gpgme_import_result_t;
+
/* Retrieve a pointer to the result of the import operation. */
gpgme_import_result_t gpgme_op_import_result (gpgme_ctx_t ctx);
@@ -1699,6 +1754,7 @@ gpgme_error_t gpgme_op_export_keys (gpgme_ctx_t ctx,
#define GPGME_CREATE_FORCE (1 << 12) /* Force creation. */
#define GPGME_CREATE_NOEXPIRE (1 << 13) /* Create w/o expiration. */
+
/* An object to return result from a key generation.
* This structure shall be considered read-only and an application
* must not allocate such a structure on its own. */
@@ -1729,9 +1785,10 @@ struct _gpgme_op_genkey_result
};
typedef struct _gpgme_op_genkey_result *gpgme_genkey_result_t;
+
/* Generate a new keypair and add it to the keyring. PUBKEY and
- SECKEY should be null for now. PARMS specifies what keys should be
- generated. */
+ * SECKEY should be null for now. PARMS specifies what keys should be
+ * generated. */
gpgme_error_t gpgme_op_genkey_start (gpgme_ctx_t ctx, const char *parms,
gpgme_data_t pubkey, gpgme_data_t seckey);
gpgme_error_t gpgme_op_genkey (gpgme_ctx_t ctx, const char *parms,
@@ -1797,7 +1854,7 @@ gpgme_genkey_result_t gpgme_op_genkey_result (gpgme_ctx_t ctx);
/* Delete KEY from the keyring. If ALLOW_SECRET is non-zero, secret
- keys are also deleted. */
+ * keys are also deleted. */
gpgme_error_t gpgme_op_delete_start (gpgme_ctx_t ctx, const gpgme_key_t key,
int allow_secret);
gpgme_error_t gpgme_op_delete (gpgme_ctx_t ctx, const gpgme_key_t key,
@@ -1916,7 +1973,7 @@ gpgme_error_t gpgme_op_keylist_end (gpgme_ctx_t ctx);
*/
/* Change the passphrase for KEY. FLAGS is reserved for future use
- and must be passed as 0. */
+ * and must be passed as 0. */
gpgme_error_t gpgme_op_passwd_start (gpgme_ctx_t ctx, gpgme_key_t key,
unsigned int flags);
gpgme_error_t gpgme_op_passwd (gpgme_ctx_t ctx, gpgme_key_t key,
@@ -1981,7 +2038,7 @@ gpgme_error_t gpgme_op_trustlist_end (gpgme_ctx_t ctx);
void gpgme_trust_item_ref (gpgme_trust_item_t item);
/* Release a reference to ITEM. If this was the last one the trust
- item is destroyed. */
+ * item is destroyed. */
void gpgme_trust_item_unref (gpgme_trust_item_t item);
@@ -2007,11 +2064,12 @@ gpgme_error_t gpgme_op_getauditlog (gpgme_ctx_t ctx, gpgme_data_t output,
/* Flags for the spawn operations. */
#define GPGME_SPAWN_DETACHED 1
#define GPGME_SPAWN_ALLOW_SET_FG 2
+#define GPGME_SPAWN_SHOW_WINDOW 4
/* Run the command FILE with the arguments in ARGV. Connect stdin to
- DATAIN, stdout to DATAOUT, and STDERR to DATAERR. If one the data
- streams is NULL, connect to /dev/null instead. */
+ * DATAIN, stdout to DATAOUT, and STDERR to DATAERR. If one the data
+ * streams is NULL, connect to /dev/null instead. */
gpgme_error_t gpgme_op_spawn_start (gpgme_ctx_t ctx,
const char *file, const char *argv[],
gpgme_data_t datain,
@@ -2027,6 +2085,7 @@ gpgme_error_t gpgme_op_spawn (gpgme_ctx_t ctx,
/*
* Low-level Assuan protocol access.
*/
+
typedef gpgme_error_t (*gpgme_assuan_data_cb_t)
(void *opaque, const void *data, size_t datalen);
@@ -2038,7 +2097,7 @@ typedef gpgme_error_t (*gpgme_assuan_status_cb_t)
(void *opaque, const char *status, const char *args);
/* Send the Assuan COMMAND and return results via the callbacks.
- Asynchronous variant. */
+ * Asynchronous variant. */
gpgme_error_t gpgme_op_assuan_transact_start (gpgme_ctx_t ctx,
const char *command,
gpgme_assuan_data_cb_t data_cb,
@@ -2049,7 +2108,7 @@ gpgme_error_t gpgme_op_assuan_transact_start (gpgme_ctx_t ctx,
void *stat_cb_value);
/* Send the Assuan COMMAND and return results via the callbacks.
- Synchronous variant. */
+ * Synchronous variant. */
gpgme_error_t gpgme_op_assuan_transact_ext (gpgme_ctx_t ctx,
const char *command,
gpgme_assuan_data_cb_t data_cb,
@@ -2077,8 +2136,8 @@ typedef struct _gpgme_op_vfs_mount_result *gpgme_vfs_mount_result_t;
gpgme_vfs_mount_result_t gpgme_op_vfs_mount_result (gpgme_ctx_t ctx);
/* The container is automatically unmounted when the context is reset
- or destroyed. Transmission errors are returned directly,
- operational errors are returned in OP_ERR. */
+ * or destroyed. Transmission errors are returned directly,
+ * operational errors are returned in OP_ERR. */
gpgme_error_t gpgme_op_vfs_mount (gpgme_ctx_t ctx, const char *container_file,
const char *mount_dir, unsigned int flags,
gpgme_error_t *op_err);
@@ -2093,8 +2152,8 @@ gpgme_error_t gpgme_op_vfs_create (gpgme_ctx_t ctx, gpgme_key_t recp[],
*/
/* The expert level at which a configuration option or group of
- options should be displayed. See the gpgconf(1) documentation for
- more details. */
+ * options should be displayed. See the gpgconf(1) documentation for
+ * more details. */
typedef enum
{
GPGME_CONF_BASIC = 0,
@@ -2107,7 +2166,7 @@ gpgme_conf_level_t;
/* The data type of a configuration option argument. See the gpgconf(1)
- documentation for more details. */
+ * documentation for more details. */
typedef enum
{
/* Basic types. */
@@ -2131,7 +2190,7 @@ gpgme_conf_type_t;
/* This represents a single argument for a configuration option.
- Which of the members of value is used depends on the ALT_TYPE. */
+ * Which of the members of value is used depends on the ALT_TYPE. */
typedef struct gpgme_conf_arg
{
struct gpgme_conf_arg *next;
@@ -2148,7 +2207,7 @@ typedef struct gpgme_conf_arg
/* The flags of a configuration option. See the gpgconf
- documentation for details. */
+ * documentation for details. */
#define GPGME_CONF_GROUP (1 << 0)
#define GPGME_CONF_OPTIONAL (1 << 1)
#define GPGME_CONF_LIST (1 << 2)
@@ -2160,7 +2219,7 @@ typedef struct gpgme_conf_arg
/* The representation of a single configuration option. See the
- gpg-conf documentation for details. */
+ * gpg-conf documentation for details. */
typedef struct gpgme_conf_opt
{
struct gpgme_conf_opt *next;
@@ -2205,7 +2264,7 @@ typedef struct gpgme_conf_opt
/* The representation of a component that can be configured. See the
- gpg-conf documentation for details. */
+ * gpg-conf documentation for details. */
typedef struct gpgme_conf_comp
{
struct gpgme_conf_comp *next;
@@ -2228,9 +2287,9 @@ typedef struct gpgme_conf_comp
/* Allocate a new gpgme_conf_arg_t. If VALUE is NULL, a "no arg
- default" is prepared. If type is a string type, VALUE should point
- to the string. Else, it should point to an unsigned or signed
- integer respectively. */
+ * default" is prepared. If type is a string type, VALUE should point
+ * to the string. Else, it should point to an unsigned or signed
+ * integer respectively. */
gpgme_error_t gpgme_conf_arg_new (gpgme_conf_arg_t *arg_p,
gpgme_conf_type_t type, const void *value);
@@ -2238,10 +2297,10 @@ gpgme_error_t gpgme_conf_arg_new (gpgme_conf_arg_t *arg_p,
void gpgme_conf_arg_release (gpgme_conf_arg_t arg, gpgme_conf_type_t type);
/* Register a change for the value of OPT to ARG. If RESET is 1 (do
- not use any values but 0 or 1), ARG is ignored and the option is
- not changed (reverting a previous change). Otherwise, if ARG is
- NULL, the option is cleared or reset to its default. The change
- is done with gpgconf's --runtime option to immediately take effect. */
+ * not use any values but 0 or 1), ARG is ignored and the option is
+ * not changed (reverting a previous change). Otherwise, if ARG is
+ * NULL, the option is cleared or reset to its default. The change
+ * is done with gpgconf's --runtime option to immediately take effect. */
gpgme_error_t gpgme_conf_opt_change (gpgme_conf_opt_t opt, int reset,
gpgme_conf_arg_t arg);
@@ -2332,16 +2391,17 @@ gpgme_query_swdb_result_t gpgme_op_query_swdb_result (gpgme_ctx_t ctx);
int gpgme_set_global_flag (const char *name, const char *value);
/* Check that the library fulfills the version requirement. Note:
- This is here only for the case where a user takes a pointer from
- the old version of this function. The new version and macro for
- run-time checks are below. */
+ * This is here only for the case where a user takes a pointer from
+ * the old version of this function. The new version and macro for
+ * run-time checks are below. */
const char *gpgme_check_version (const char *req_version);
-/* Check that the library fulfills the version requirement and check
- for struct layout mismatch involving bitfields. */
+/* Do not call this directly; use the macro below. */
const char *gpgme_check_version_internal (const char *req_version,
size_t offset_sig_validity);
+/* Check that the library fulfills the version requirement and check
+ * for struct layout mismatch involving bitfields. */
#define gpgme_check_version(req_version) \
gpgme_check_version_internal (req_version, \
offsetof (struct _gpgme_signature, validity))
@@ -2350,19 +2410,19 @@ const char *gpgme_check_version_internal (const char *req_version,
const char *gpgme_get_dirinfo (const char *what);
/* Get the information about the configured and installed engines. A
- pointer to the first engine in the statically allocated linked list
- is returned in *INFO. If an error occurs, it is returned. The
- returned data is valid until the next gpgme_set_engine_info. */
+ * pointer to the first engine in the statically allocated linked list
+ * is returned in *INFO. If an error occurs, it is returned. The
+ * returned data is valid until the next gpgme_set_engine_info. */
gpgme_error_t gpgme_get_engine_info (gpgme_engine_info_t *engine_info);
/* Set the default engine info for the protocol PROTO to the file name
- FILE_NAME and the home directory HOME_DIR. */
+ * FILE_NAME and the home directory HOME_DIR. */
gpgme_error_t gpgme_set_engine_info (gpgme_protocol_t proto,
const char *file_name,
const char *home_dir);
/* Verify that the engine implementing PROTO is installed and
- available. */
+ * available. */
gpgme_error_t gpgme_engine_check_version (gpgme_protocol_t proto);
@@ -2371,15 +2431,15 @@ void gpgme_result_ref (void *result);
void gpgme_result_unref (void *result);
/* Return a public key algorithm string (e.g. "rsa2048"). Caller must
- free using gpgme_free. */
+ * free using gpgme_free. */
char *gpgme_pubkey_algo_string (gpgme_subkey_t subkey);
/* Return a statically allocated string with the name of the public
- key algorithm ALGO, or NULL if that name is not known. */
+ * key algorithm ALGO, or NULL if that name is not known. */
const char *gpgme_pubkey_algo_name (gpgme_pubkey_algo_t algo);
/* Return a statically allocated string with the name of the hash
- algorithm ALGO, or NULL if that name is not known. */
+ * algorithm ALGO, or NULL if that name is not known. */
const char *gpgme_hash_algo_name (gpgme_hash_algo_t algo);
/* Return the addr-spec from a user id. Caller must free the result
@@ -2531,7 +2591,7 @@ gpgme_error_t gpgme_op_card_edit (gpgme_ctx_t ctx, gpgme_key_t key,
_GPGME_DEPRECATED(1,7);
/* The possible signature stati. Deprecated, use error value in sig
- status. */
+ * status. */
typedef enum
{
GPGME_SIG_STAT_NONE = 0,
@@ -2548,7 +2608,7 @@ _gpgme_sig_stat_t;
typedef _gpgme_sig_stat_t gpgme_sig_stat_t _GPGME_DEPRECATED(0,4);
/* The available key and signature attributes. Deprecated, use the
- individual result structures instead. */
+ * individual result structures instead. */
typedef enum
{
GPGME_ATTR_KEYID = 1,
@@ -2588,18 +2648,18 @@ _gpgme_attr_t;
typedef _gpgme_attr_t gpgme_attr_t _GPGME_DEPRECATED(0,4);
/* Retrieve the signature status of signature IDX in CTX after a
- successful verify operation in R_STAT (if non-null). The creation
- time stamp of the signature is returned in R_CREATED (if non-null).
- The function returns a string containing the fingerprint.
- Deprecated, use verify result directly. */
+ * successful verify operation in R_STAT (if non-null). The creation
+ * time stamp of the signature is returned in R_CREATED (if non-null).
+ * The function returns a string containing the fingerprint.
+ * Deprecated, use verify result directly. */
const char *gpgme_get_sig_status (gpgme_ctx_t ctx, int idx,
_gpgme_sig_stat_t *r_stat,
time_t *r_created) _GPGME_DEPRECATED(0,4);
/* Retrieve certain attributes of a signature. IDX is the index
- number of the signature after a successful verify operation. WHAT
- is an attribute where GPGME_ATTR_EXPIRE is probably the most useful
- one. WHATIDX is to be passed as 0 for most attributes . */
+ * number of the signature after a successful verify operation. WHAT
+ * is an attribute where GPGME_ATTR_EXPIRE is probably the most useful
+ * one. WHATIDX is to be passed as 0 for most attributes . */
unsigned long gpgme_get_sig_ulong_attr (gpgme_ctx_t c, int idx,
_gpgme_attr_t what, int whatidx)
_GPGME_DEPRECATED(0,4);
@@ -2609,13 +2669,13 @@ const char *gpgme_get_sig_string_attr (gpgme_ctx_t c, int idx,
/* Get the key used to create signature IDX in CTX and return it in
- R_KEY. */
+ * R_KEY. */
gpgme_error_t gpgme_get_sig_key (gpgme_ctx_t ctx, int idx, gpgme_key_t *r_key)
_GPGME_DEPRECATED(0,4);
/* Create a new data buffer which retrieves the data from the callback
- function READ_CB. Deprecated, please use gpgme_data_new_from_cbs
- instead. */
+ * function READ_CB. Deprecated, please use gpgme_data_new_from_cbs
+ * instead. */
gpgme_error_t gpgme_data_new_with_read_cb (gpgme_data_t *r_dh,
int (*read_cb) (void*,char *,
size_t,size_t*),
@@ -2623,34 +2683,34 @@ gpgme_error_t gpgme_data_new_with_read_cb (gpgme_data_t *r_dh,
_GPGME_DEPRECATED(0,4);
/* Return the value of the attribute WHAT of KEY, which has to be
- representable by a string. IDX specifies the sub key or user ID
- for attributes related to sub keys or user IDs. Deprecated, use
- key structure directly instead. */
+ * representable by a string. IDX specifies the sub key or user ID
+ * for attributes related to sub keys or user IDs. Deprecated, use
+ * key structure directly instead. */
const char *gpgme_key_get_string_attr (gpgme_key_t key, _gpgme_attr_t what,
const void *reserved, int idx)
_GPGME_DEPRECATED(0,4);
/* Return the value of the attribute WHAT of KEY, which has to be
- representable by an unsigned integer. IDX specifies the sub key or
- user ID for attributes related to sub keys or user IDs.
- Deprecated, use key structure directly instead. */
+ * representable by an unsigned integer. IDX specifies the sub key or
+ * user ID for attributes related to sub keys or user IDs.
+ * Deprecated, use key structure directly instead. */
unsigned long gpgme_key_get_ulong_attr (gpgme_key_t key, _gpgme_attr_t what,
const void *reserved, int idx)
_GPGME_DEPRECATED(0,4);
/* Return the value of the attribute WHAT of a signature on user ID
- UID_IDX in KEY, which has to be representable by a string. IDX
- specifies the signature. Deprecated, use key structure directly
- instead. */
+ * UID_IDX in KEY, which has to be representable by a string. IDX
+ * specifies the signature. Deprecated, use key structure directly
+ * instead. */
const char *gpgme_key_sig_get_string_attr (gpgme_key_t key, int uid_idx,
_gpgme_attr_t what,
const void *reserved, int idx)
_GPGME_DEPRECATED(0,4);
/* Return the value of the attribute WHAT of a signature on user ID
- UID_IDX in KEY, which has to be representable by an unsigned
- integer string. IDX specifies the signature. Deprecated, use key
- structure directly instead. */
+ * UID_IDX in KEY, which has to be representable by an unsigned
+ * integer string. IDX specifies the signature. Deprecated, use key
+ * structure directly instead. */
unsigned long gpgme_key_sig_get_ulong_attr (gpgme_key_t key, int uid_idx,
_gpgme_attr_t what,
const void *reserved, int idx)
@@ -2661,21 +2721,21 @@ gpgme_error_t gpgme_op_import_ext (gpgme_ctx_t ctx, gpgme_data_t keydata,
int *nr) _GPGME_DEPRECATED(0,4);
/* Release the trust item ITEM. Deprecated, use
- gpgme_trust_item_unref. */
+ * gpgme_trust_item_unref. */
void gpgme_trust_item_release (gpgme_trust_item_t item) _GPGME_DEPRECATED(0,4);
/* Return the value of the attribute WHAT of ITEM, which has to be
- representable by a string. Deprecated, use trust item structure
- directly. */
+ * representable by a string. Deprecated, use trust item structure
+ * directly. */
const char *gpgme_trust_item_get_string_attr (gpgme_trust_item_t item,
_gpgme_attr_t what,
const void *reserved, int idx)
_GPGME_DEPRECATED(0,4);
/* Return the value of the attribute WHAT of KEY, which has to be
- representable by an integer. IDX specifies a running index if the
- attribute appears more than once in the key. Deprecated, use trust
- item structure directly. */
+ * representable by an integer. IDX specifies a running index if the
+ * attribute appears more than once in the key. Deprecated, use trust
+ * item structure directly. */
int gpgme_trust_item_get_int_attr (gpgme_trust_item_t item, _gpgme_attr_t what,
const void *reserved, int idx)
_GPGME_DEPRECATED(0,4);
diff --git a/src/import.c b/src/import.c
index 386ca72..f0d9d9f 100644
--- a/src/import.c
+++ b/src/import.c
@@ -94,8 +94,9 @@ gpgme_op_import_result (gpgme_ctx_t ctx)
TRACE_LOG3 ("%i secret keys, %i imported, %i unchanged",
opd->result.secret_read, opd->result.secret_imported,
opd->result.secret_unchanged);
- TRACE_LOG2 ("%i skipped new keys, %i not imported",
- opd->result.skipped_new_keys, opd->result.not_imported);
+ TRACE_LOG3 ("%i skipped new keys, %i not imported, %i v3 skipped",
+ opd->result.skipped_new_keys, opd->result.not_imported,
+ opd->result.skipped_v3_keys);
impstat = opd->result.imports;
i = 0;
@@ -212,6 +213,10 @@ parse_import_res (char *args, gpgme_import_result_t result)
PARSE_NEXT (result->secret_unchanged);
PARSE_NEXT (result->skipped_new_keys);
PARSE_NEXT (result->not_imported);
+ if (args && *args)
+ {
+ PARSE_NEXT (result->skipped_v3_keys);
+ }
return 0;
}
diff --git a/src/keylist.c b/src/keylist.c
index 24a9b0b..9c5bd4e 100644
--- a/src/keylist.c
+++ b/src/keylist.c
@@ -1261,7 +1261,7 @@ gpgme_get_key (gpgme_ctx_t ctx, const char *fpr, gpgme_key_t *r_key,
{
gpgme_ctx_t listctx;
gpgme_error_t err;
- gpgme_key_t key;
+ gpgme_key_t result, key;
TRACE_BEG2 (DEBUG_CTX, "gpgme_get_key", ctx,
"fpr=%s, secret=%i", fpr, secret);
@@ -1269,6 +1269,8 @@ gpgme_get_key (gpgme_ctx_t ctx, const char *fpr, gpgme_key_t *r_key,
if (!ctx || !r_key || !fpr)
return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
+ *r_key = NULL;
+
if (strlen (fpr) < 8) /* We have at least a key ID. */
return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
@@ -1295,7 +1297,7 @@ gpgme_get_key (gpgme_ctx_t ctx, const char *fpr, gpgme_key_t *r_key,
err = gpgme_op_keylist_start (listctx, fpr, secret);
if (!err)
- err = gpgme_op_keylist_next (listctx, r_key);
+ err = gpgme_op_keylist_next (listctx, &result);
if (!err)
{
try_next_key:
@@ -1305,9 +1307,9 @@ gpgme_get_key (gpgme_ctx_t ctx, const char *fpr, gpgme_key_t *r_key,
else
{
if (!err
- && *r_key && (*r_key)->subkeys && (*r_key)->subkeys->fpr
+ && result && result->subkeys && result->subkeys->fpr
&& key && key->subkeys && key->subkeys->fpr
- && !strcmp ((*r_key)->subkeys->fpr, key->subkeys->fpr))
+ && !strcmp (result->subkeys->fpr, key->subkeys->fpr))
{
/* The fingerprint is identical. We assume that this is
the same key and don't mark it as an ambiguous. This
@@ -1323,12 +1325,14 @@ gpgme_get_key (gpgme_ctx_t ctx, const char *fpr, gpgme_key_t *r_key,
gpgme_key_unref (key);
err = gpg_error (GPG_ERR_AMBIGUOUS_NAME);
}
- gpgme_key_unref (*r_key);
+ gpgme_key_unref (result);
+ result = NULL;
}
}
gpgme_release (listctx);
if (! err)
{
+ *r_key = result;
TRACE_LOG2 ("key=%p (%s)", *r_key,
((*r_key)->subkeys && (*r_key)->subkeys->fpr) ?
(*r_key)->subkeys->fpr : "invalid");
diff --git a/src/libgpgme.vers b/src/libgpgme.vers
index a95befb..b49c86d 100644
--- a/src/libgpgme.vers
+++ b/src/libgpgme.vers
@@ -215,10 +215,15 @@ GPGME_1.0 {
gpgme_op_edit;
gpgme_op_edit_start;
gpgme_op_encrypt;
- gpgme_op_encrypt_result;
+ gpgme_op_encrypt_start;
+ gpgme_op_encrypt_ext;
+ gpgme_op_encrypt_ext_start;
gpgme_op_encrypt_sign;
+ gpgme_op_encrypt_sign_ext;
gpgme_op_encrypt_sign_start;
- gpgme_op_encrypt_start;
+ gpgme_op_encrypt_sign_ext_start;
+ gpgme_op_encrypt_result;
+
gpgme_op_export;
gpgme_op_export_ext;
gpgme_op_export_ext_start;
diff --git a/src/op-support.c b/src/op-support.c
index 817c569..03f274c 100644
--- a/src/op-support.c
+++ b/src/op-support.c
@@ -141,6 +141,8 @@ _gpgme_op_reset (gpgme_ctx_t ctx, int type)
if (gpg_err_code (err) == GPG_ERR_NOT_IMPLEMENTED)
err = 0;
+ _gpgme_engine_set_engine_flags (ctx->engine, ctx);
+
if (!err)
{
err = _gpgme_engine_set_pinentry_mode (ctx->engine,
@@ -356,7 +358,7 @@ _gpgme_parse_key_considered (const char *args,
/* Parse the PLAINTEXT status line in ARGS and return the result in
FILENAMEP. */
gpgme_error_t
-_gpgme_parse_plaintext (char *args, char **filenamep)
+_gpgme_parse_plaintext (char *args, char **filenamep, int *r_mime)
{
char *tail;
@@ -365,7 +367,9 @@ _gpgme_parse_plaintext (char *args, char **filenamep)
if (*args == '\0')
return 0;
- /* First argument is file type. */
+ /* First argument is file type (a one byte uppercase hex value). */
+ if (args[0] == '6' && args[1] == 'D')
+ *r_mime = 1;
while (*args != ' ' && *args != '\0')
args++;
while (*args == ' ')
@@ -398,7 +402,13 @@ _gpgme_parse_plaintext (char *args, char **filenamep)
/* Parse a FAILURE status line and return the error code. ARGS is
- modified to contain the location part. */
+ * modified to contain the location part. Note that for now we ignore
+ * failure codes with a location of gpg-exit; they are too trouble
+ * some. Instead we should eventually record that error in the
+ * context and provide a function to return a fuller error
+ * description; this could then also show the location of the error
+ * (e.g. "option- parser") to make it easier for the user to detect
+ * the actual error. */
gpgme_error_t
_gpgme_parse_failure (char *args)
{
@@ -416,6 +426,8 @@ _gpgme_parse_failure (char *args)
*where = '\0';
where = args;
+ if (!strcmp (where, "gpg-exit"))
+ return 0;
return atoi (which);
}
diff --git a/src/ops.h b/src/ops.h
index cc61dc4..5955454 100644
--- a/src/ops.h
+++ b/src/ops.h
@@ -68,8 +68,8 @@ gpgme_error_t _gpgme_parse_inv_recp (char *args, int for_signing,
gpgme_invalid_key_t *key);
/* Parse the PLAINTEXT status line in ARGS and return the result in
- FILENAMEP. */
-gpgme_error_t _gpgme_parse_plaintext (char *args, char **filenamep);
+ FILENAMEP and R_MIME. */
+gpgme_error_t _gpgme_parse_plaintext (char *args, char **filenamep,int *r_mime);
/* Parse a FAILURE status line and return the error code. ARGS is
modified to contain the location part. */
diff --git a/src/priv-io.h b/src/priv-io.h
index 2306175..bc9d3d5 100644
--- a/src/priv-io.h
+++ b/src/priv-io.h
@@ -83,6 +83,8 @@ int _gpgme_io_set_nonblocking (int fd);
#define IOSPAWN_FLAG_ALLOW_SET_FG 2
/* Don't close any child FDs. */
#define IOSPAWN_FLAG_NOCLOSE 4
+/* Set show window to true for windows */
+#define IOSPAWN_FLAG_SHOW_WINDOW 8
/* Spawn the executable PATH with ARGV as arguments. After forking
close all fds except for those in FD_LIST in the child, then
diff --git a/src/util.h b/src/util.h
index b4043ed..da929eb 100644
--- a/src/util.h
+++ b/src/util.h
@@ -165,10 +165,11 @@ time_t _gpgme_parse_timestamp (const char *timestamp, char **endp);
* on error or missing timestamp. */
unsigned long _gpgme_parse_timestamp_ul (const char *timestamp);
-gpgme_error_t _gpgme_map_gnupg_error (char *err);
-
int _gpgme_map_pk_algo (int algo, gpgme_protocol_t protocol);
+const char *_gpgme_cipher_algo_name (int algo, gpgme_protocol_t protocol);
+const char *_gpgme_cipher_mode_name (int algo, gpgme_protocol_t protocol);
+
/*-- b64dec.c --*/
diff --git a/src/verify.c b/src/verify.c
index ee730a3..bd437c9 100644
--- a/src/verify.c
+++ b/src/verify.c
@@ -284,6 +284,7 @@ parse_new_sig (op_data_t opd, gpgme_status_code_t code, char *args,
gpgme_signature_t sig;
char *end = strchr (args, ' ');
char *tail;
+ int got_fpr = 0;
if (end)
{
@@ -370,7 +371,23 @@ parse_new_sig (op_data_t opd, gpgme_status_code_t code, char *args,
if (!*end)
goto parse_err_sig_fail;
- sig->status = strtoul (end, NULL, 10);
+ gpg_err_set_errno (0);
+ sig->status = strtoul (end, &tail, 10);
+ if (errno || end == tail || (*tail && *tail != ' '))
+ goto parse_err_sig_fail;
+ if (!*tail)
+ goto parse_err_sig_ok;
+ end = tail;
+ while (*end == ' ')
+ end++;
+
+ /* Parse the new fingerprint (from the ISSUER_FPR subpacket). */
+ if (!*end || (*end == '-' && (end[1] == ' ' || !end[1])))
+ goto parse_err_sig_ok; /* Okay (just trailing spaces). */
+ sig->fpr = strdup (end);
+ if (!sig->fpr)
+ return gpg_error_from_syserror ();
+ got_fpr = 1;
goto parse_err_sig_ok;
parse_err_sig_fail:
@@ -382,7 +399,7 @@ parse_new_sig (op_data_t opd, gpgme_status_code_t code, char *args,
return gpg_error (GPG_ERR_GENERAL);
}
- if (*args)
+ if (*args && !got_fpr)
{
sig->fpr = strdup (args);
if (!sig->fpr)
@@ -1074,9 +1091,15 @@ _gpgme_verify_status_handler (void *priv, gpgme_status_code_t code, char *args)
case GPGME_STATUS_PLAINTEXT:
if (++opd->plaintext_seen > 1)
return gpg_error (GPG_ERR_BAD_DATA);
- err = _gpgme_parse_plaintext (args, &opd->result.file_name);
- if (err)
- return err;
+ {
+ int mime = 0;
+ err = _gpgme_parse_plaintext (args, &opd->result.file_name, &mime);
+ if (err)
+ return err;
+ gpgrt_log_debug ("verify.c: setting mime to %d\n", mime);
+ opd->result.is_mime = !!mime;
+ }
+ break;
case GPGME_STATUS_VERIFICATION_COMPLIANCE_MODE:
PARSE_COMPLIANCE_FLAGS (args, opd->current_sig);
diff --git a/src/versioninfo.rc.in b/src/versioninfo.rc.in
index 2b1cc81..88b662e 100644
--- a/src/versioninfo.rc.in
+++ b/src/versioninfo.rc.in
@@ -39,7 +39,7 @@ BEGIN
VALUE "FileDescription", "GPGME - GnuPG Made Easy\0"
VALUE "FileVersion", "@LIBGPGME_LT_CURRENT@.@LIBGPGME_LT_AGE@.@LIBGPGME_LT_REVISION@.@BUILD_REVISION@\0"
VALUE "InternalName", "gpgme\0"
- VALUE "LegalCopyright", "Copyright © 2001-2017 g10 Code GmbH\0"
+ VALUE "LegalCopyright", "Copyright © 2001-2018 g10 Code GmbH\0"
VALUE "LegalTrademarks", "\0"
VALUE "OriginalFilename", "gpgme.dll\0"
VALUE "PrivateBuild", "\0"
diff --git a/src/w32-io.c b/src/w32-io.c
index eed8a00..05e11ee 100644
--- a/src/w32-io.c
+++ b/src/w32-io.c
@@ -1724,8 +1724,8 @@ _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags,
int written;
size_t len;
- if ((flags & IOSPAWN_FLAG_ALLOW_SET_FG))
- strcpy (line, "~1 \n");
+ if (flags)
+ snprintf (line, BUFFER_MAX, "~%i \n", flags);
else
strcpy (line, "\n");
for (i = 0; fd_list[i].fd != -1; i++)