[git] GPGME - branch, master, updated. gpgme-1.10.0-197-ged10528

by Werner Koch cvs at cvs.gnupg.org
Wed Apr 18 11:20:38 CEST 2018


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "GnuPG Made Easy".

The branch, master has been updated
       via  ed1052842df633bc149b14c4cb17859e3c66afe4 (commit)
       via  e69b175e8ed5430b56e2e8f3d68c16a45f0fed17 (commit)
      from  01435da498af9f7538d7ee810392d7eaa407957e (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit ed1052842df633bc149b14c4cb17859e3c66afe4
Author: Werner Koch <wk at gnupg.org>
Date:   Wed Apr 18 11:12:46 2018 +0200

    json: Add command "getmore" to gpgme-json.
    
    * src/gpgme-json.c (MIN_REPLY_CHUNK_SIZE): New const.
    (DEF_REPLY_CHUNK_SIZE): New const.
    (MAX_REPLY_CHUNK_SIZE): New const.
    (pending_data): New var.
    (add_base64_to_object): Chnage to take a plain data pointer.
    (get_chunksize): New.
    (make_data_object): New.
    (op_encrypt): Get chunksize and use make_data_object.
    (op_getmore): New.
    (process_request): Release pending data for all commands but "getmore"
    and "help".
    --
    
    Native messaging has a limit on the data it may receive in one
    request.  Thus the caller needs to watch for the "more" flag and
    request the remaining data using "getmore" in a loop.
    
    Signed-off-by: Werner Koch <wk at gnupg.org>

diff --git a/src/gpgme-json.c b/src/gpgme-json.c
index 56d6946..576e63e 100644
--- a/src/gpgme-json.c
+++ b/src/gpgme-json.c
@@ -49,6 +49,16 @@ int main (void){fputs ("Build with Libgpg-error >= 1.28!\n", stderr);return 1;}
 /* We don't allow a request with more than 64 MiB.  */
 #define MAX_REQUEST_SIZE (64 * 1024 * 1024)
 
+/* Minimal, default and maximum chunk size for returned data. The
+ * first chunk is returned directly.  If the "more" flag is also
+ * returned, a "getmore" command needs to be used to get the next
+ * chunk.  Right now this value covers just the value of the "data"
+ * element; so to cover for the other returned objects this values
+ * needs to be lower than the maximum allowed size of the browser. */
+#define MIN_REPLY_CHUNK_SIZE  512
+#define DEF_REPLY_CHUNK_SIZE (512 * 1024)
+#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,
@@ -64,6 +74,16 @@ 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
@@ -147,11 +167,12 @@ xjson_AddBoolToObject (cjson_t object, const char *name, int abool)
   return ;
 }
 
-/* This is similar to cJSON_AddStringToObject but takes a gpgme DATA
- * object and adds it under NAME as a base 64 encoded string to
- * OBJECT.  Ownership of DATA is transferred to this function.  */
+/* 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, gpgme_data_t data)
+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);
@@ -161,7 +182,6 @@ add_base64_to_object (cjson_t object, const char *name, gpgme_data_t data)
   gpgrt_b64state_t state = NULL;
   cjson_t j_str = NULL;
   void *buffer = NULL;
-  size_t buflen;
 
   fp = es_fopenmem (0, "rwb");
   if (!fp)
@@ -176,20 +196,9 @@ add_base64_to_object (cjson_t object, const char *name, gpgme_data_t data)
       goto leave;
     }
 
-  gpgme_data_write (data, "", 1); /* Make sure we have  a string.  */
-  buffer = gpgme_data_release_and_get_mem (data, &buflen);
-  data = NULL;
-  if (!buffer)
-    {
-      err = gpg_error_from_syserror ();
-      goto leave;
-    }
-
-  err = gpgrt_b64enc_write (state, buffer, buflen);
+  err = gpgrt_b64enc_write (state, data, datalen);
   if (err)
     goto leave;
-  xfree (buffer);
-  buffer = NULL;
 
   err = gpgrt_b64enc_finish (state);
   state = NULL;
@@ -227,7 +236,6 @@ add_base64_to_object (cjson_t object, const char *name, gpgme_data_t data)
   cJSON_Delete (j_str);
   gpgrt_b64enc_finish (state);
   es_fclose (fp);
-  gpgme_data_release (data);
   return err;
 #endif
 }
@@ -355,6 +363,29 @@ get_protocol (cjson_t json, gpgme_protocol_t *r_protocol)
 }
 
 
+/* 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
@@ -549,10 +580,80 @@ data_from_base64_string (gpgme_data_t *r_data, cjson_t json)
 
 

 /*
- * Implementaion of the commands.
+ * 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.  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;
+
+  /* Adjust the chunksize if we need to do base64 conversion.  */
+  if (base64)
+    chunksize = (chunksize / 4) * 3;
+
+  if (!base64) /* 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;
+    }
+
+
+  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"
@@ -562,6 +663,7 @@ static const char hlp_encrypt[] =
   "\n"
   "Optional parameters:\n"
   "protocol:      Either \"openpgp\" (default) or \"cms\".\n"
+  "chunksize:     Max number of bytes in the resulting \"data\".\n"
   "\n"
   "Optional boolean flags (default is false):\n"
   "base64:        Input data is base64 encoded.\n"
@@ -578,13 +680,15 @@ static const char hlp_encrypt[] =
   "data:   Unless armor mode is used a Base64 encoded binary\n"
   "        ciphertext.  In armor mode a string with an armored\n"
   "        OpenPGP or a PEM message.\n"
-  "base64: Boolean indicating whether data is base64 encoded.";
+  "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;
   char *keystring = NULL;
   cjson_t j_input;
@@ -596,6 +700,8 @@ op_encrypt (cjson_t request, cjson_t result)
   if ((err = get_protocol (request, &protocol)))
     goto leave;
   ctx = get_context (protocol);
+  if ((err = get_chunksize (request, &chunksize)))
+    goto leave;
 
   if ((err = get_boolean_flag (request, "base64", 0, &opt_base64)))
     goto leave;
@@ -693,43 +799,106 @@ op_encrypt (cjson_t request, cjson_t result)
   gpgme_data_release (input);
   input = NULL;
 
-  xjson_AddStringToObject (result, "type", "ciphertext");
-  /* If armoring is used we do not need to base64 the output.  */
-  xjson_AddBoolToObject (result, "base64", !gpgme_get_armor (ctx));
-  if (gpgme_get_armor (ctx))
+  /* 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_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)
     {
-      char *buffer;
+      err = gpg_error (GPG_ERR_NO_DATA);
+      error_object (result, "Operation not possible: %s", gpg_strerror (err));
+      goto leave;
+    }
 
-      /* Make sure that we really have a string.  */
-      gpgme_data_write (output, "", 1);
-      buffer = gpgme_data_release_and_get_mem (output, NULL);
-      if (!buffer)
-        {
-          err = gpg_error_from_syserror ();
-          goto leave;
-        }
-      err = cjson_AddStringToObject (result, "data", buffer);
-      gpgme_free (buffer);
-      if (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
     {
-      err = add_base64_to_object (result, "data", output);
-      output = NULL;
-      if (err)
-        goto leave;
+      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:
-  xfree (keystring);
-  release_context (ctx);
-  gpgme_data_release (input);
   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"
@@ -739,6 +908,7 @@ static const char hlp_help[] =
   "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)
@@ -761,6 +931,10 @@ op_help (cjson_t request, cjson_t result)
 }
 
 
+

+/*
+ * Dispatcher
+ */
 
 /* Process a request and return the response.  The response is a newly
  * allocated string or NULL in case of an error.  */
@@ -774,7 +948,7 @@ process_request (const char *request)
   } optbl[] = {
     { "encrypt", op_encrypt, hlp_encrypt },
 
-
+    { "getmore", op_getmore, hlp_getmore },
     { "help",    op_help,    hlp_help },
     { NULL }
   };
@@ -829,6 +1003,14 @@ process_request (const char *request)
         {
           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)
             {

commit e69b175e8ed5430b56e2e8f3d68c16a45f0fed17
Author: Werner Koch <wk at gnupg.org>
Date:   Wed Apr 18 09:26:33 2018 +0200

    json: Add meta command ,read to gpgme-json.
    
    * src/gpgme-json.c: Include stat.h.
    (get_file): New.
    (process_meta_commands): Implement ",read".
    
    Signed-off-by: Werner Koch <wk at gnupg.org>

diff --git a/src/gpgme-json.c b/src/gpgme-json.c
index c73bebd..56d6946 100644
--- a/src/gpgme-json.c
+++ b/src/gpgme-json.c
@@ -33,6 +33,7 @@
 #include <locale.h>
 #endif
 #include <stdint.h>
+#include <sys/stat.h>
 
 #define GPGRT_ENABLE_ES_MACROS 1
 #define GPGRT_ENABLE_LOG_MACROS 1
@@ -842,7 +843,6 @@ process_request (const char *request)
 
               xjson_AddStringToObject (response, "op", op);
             }
-
         }
     }
   else  /* Operation not supported.  */
@@ -869,6 +869,48 @@ process_request (const char *request)
  *  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 *
@@ -935,11 +977,26 @@ process_meta_commands (const char *request)
     result = process_request ("{ \"op\": \"help\","
                               " \"interactive_help\": "
                               "\"\\nMeta commands:\\n"
+                              "  ,read FNAME Process data from FILE\\n"
                               "  ,help       This help\\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");
 

-----------------------------------------------------------------------

Summary of changes:
 src/gpgme-json.c | 333 +++++++++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 286 insertions(+), 47 deletions(-)


hooks/post-receive
-- 
GnuPG Made Easy
http://git.gnupg.org




More information about the Gnupg-commits mailing list