LCOV - code coverage report
Current view: top level - src - gpgme-json.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 1007 1627 61.9 %
Date: 2018-11-15 08:49:49 Functions: 59 74 79.7 %

          Line data    Source code
       1             : /* gpgme-json.c - JSON based interface to gpgme (server)
       2             :  * Copyright (C) 2018 g10 Code GmbH
       3             :  *
       4             :  * This file is part of GPGME.
       5             :  *
       6             :  * GPGME is free software; you can redistribute it and/or modify it
       7             :  * under the terms of the GNU Lesser General Public License as
       8             :  * published by the Free Software Foundation; either version 2.1 of
       9             :  * the License, or (at your option) any later version.
      10             :  *
      11             :  * GPGME is distributed in the hope that it will be useful, but
      12             :  * WITHOUT ANY WARRANTY; without even the implied warranty of
      13             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      14             :  * Lesser General Public License for more details.
      15             :  *
      16             :  * You should have received a copy of the GNU Lesser General Public
      17             :  * License along with this program; if not, see <https://www.gnu.org/licenses/>.
      18             :  * SPDX-License-Identifier: LGPL-2.1+
      19             :  */
      20             : 
      21             : /* This is tool implements the Native Messaging protocol of web
      22             :  * browsers and provides the server part of it.  A Javascript based
      23             :  * client can be found in lang/javascript.
      24             :  */
      25             : 
      26             : #include <config.h>
      27             : #include <stdio.h>
      28             : #include <stdlib.h>
      29             : #include <string.h>
      30             : #include <stdarg.h>
      31             : #ifdef HAVE_LOCALE_H
      32             : #include <locale.h>
      33             : #endif
      34             : #include <stdint.h>
      35             : #include <sys/stat.h>
      36             : 
      37             : #define GPGRT_ENABLE_ES_MACROS 1
      38             : #define GPGRT_ENABLE_LOG_MACROS 1
      39             : #define GPGRT_ENABLE_ARGPARSE_MACROS 1
      40             : #include "gpgme.h"
      41             : #include "cJSON.h"
      42             : 
      43             : 
      44             : #if GPGRT_VERSION_NUMBER < 0x011c00 /* 1.28 */
      45             : int main (void){fputs ("Build with Libgpg-error >= 1.28!\n", stderr);return 1;}
      46             : #else /* libgpg-error >= 1.28 */
      47             : 
      48             : /* We don't allow a request with more than 64 MiB.  */
      49             : #define MAX_REQUEST_SIZE (64 * 1024 * 1024)
      50             : 
      51             : /* Minimal chunk size for returned data.*/
      52             : #define MIN_REPLY_CHUNK_SIZE  30
      53             : 
      54             : /* If no chunksize is provided we print everything.  Changing
      55             :  * this to a positive value will result in all messages being
      56             :  * chunked. */
      57             : #define DEF_REPLY_CHUNK_SIZE  0
      58             : #define MAX_REPLY_CHUNK_SIZE (10 * 1024 * 1024)
      59             : 
      60             : 
      61             : static void xoutofcore (const char *type) GPGRT_ATTR_NORETURN;
      62             : static cjson_t error_object_v (cjson_t json, const char *message,
      63             :                                va_list arg_ptr, gpg_error_t err)
      64             :                                GPGRT_ATTR_PRINTF(2,0);
      65             : static cjson_t error_object (cjson_t json, const char *message,
      66             :                             ...) GPGRT_ATTR_PRINTF(2,3);
      67             : static char *error_object_string (const char *message,
      68             :                                   ...) GPGRT_ATTR_PRINTF(1,2);
      69             : static char *process_request (const char *request);
      70             : 
      71             : 
      72             : /* True if interactive mode is active.  */
      73             : static int opt_interactive;
      74             : /* True is debug mode is active.  */
      75             : static int opt_debug;
      76             : 
      77             : /* Pending data to be returned by a getmore command.  */
      78             : static struct
      79             : {
      80             :   char  *buffer;   /* Malloced data or NULL if not used.  */
      81             :   size_t length;   /* Length of that data.  */
      82             :   size_t written;  /* # of already written bytes from BUFFER.  */
      83             : } pending_data;
      84             : 
      85             : 
      86             : /*
      87             :  * Helper functions and macros
      88             :  */
      89             : 
      90             : #define xtrystrdup(a)  gpgrt_strdup ((a))
      91             : #define xcalloc(a,b) ({                         \
      92             :       void *_r = gpgrt_calloc ((a), (b));       \
      93             :       if (!_r)                                  \
      94             :         xoutofcore ("calloc");                  \
      95             :       _r; })
      96             : #define xstrdup(a) ({                           \
      97             :       char *_r = gpgrt_strdup ((a));            \
      98             :       if (!_r)                                  \
      99             :         xoutofcore ("strdup");                  \
     100             :       _r; })
     101             : #define xstrconcat(a, ...) ({                           \
     102             :       char *_r = gpgrt_strconcat ((a), __VA_ARGS__);    \
     103             :       if (!_r)                                          \
     104             :         xoutofcore ("strconcat");                       \
     105             :       _r; })
     106             : #define xfree(a) gpgrt_free ((a))
     107             : 
     108             : /* Only use calloc. */
     109             : #define CALLOC_ONLY 1
     110             : 
     111             : #if CALLOC_ONLY
     112             : #define xtrymalloc(a)  gpgrt_calloc (1, (a))
     113             : #define xmalloc(a) xcalloc(1, (a))
     114             : #else
     115             : #define xtrymalloc(a)  gpgrt_malloc ((a))
     116             : #define xmalloc(a) ({                           \
     117             :       void *_r = gpgrt_malloc ((a));            \
     118             :       if (!_r)                                  \
     119             :         xoutofcore ("malloc");                  \
     120             :       _r; })
     121             : #endif
     122             : 
     123             : #define spacep(p)   (*(p) == ' ' || *(p) == '\t')
     124             : 
     125             : #ifndef HAVE_STPCPY
     126             : static GPGRT_INLINE char *
     127             : _my_stpcpy (char *a, const char *b)
     128             : {
     129             :   while (*b)
     130             :     *a++ = *b++;
     131             :   *a = 0;
     132             :   return a;
     133             : }
     134             : #define stpcpy(a,b) _my_stpcpy ((a), (b))
     135             : #endif /*!HAVE_STPCPY*/
     136             : 
     137             : 
     138             : /* Free a NULL terminated array */
     139             : static void
     140           8 : xfree_array (char **array)
     141             : {
     142           8 :   if (array)
     143             :     {
     144             :       int idx;
     145          16 :       for (idx = 0; array[idx]; idx++)
     146           9 :         xfree (array[idx]);
     147           7 :       xfree (array);
     148             :     }
     149           8 : }
     150             : 
     151             : 
     152             : static void
     153           0 : xoutofcore (const char *type)
     154             : {
     155           0 :   gpg_error_t err = gpg_error_from_syserror ();
     156           0 :   log_error ("%s failed: %s\n", type, gpg_strerror (err));
     157           0 :   exit (2);
     158             : }
     159             : 
     160             : 
     161             : /* Call cJSON_CreateObject but terminate in case of an error.  */
     162             : static cjson_t
     163          97 : xjson_CreateObject (void)
     164             : {
     165          97 :   cjson_t json = cJSON_CreateObject ();
     166          97 :   if (!json)
     167           0 :     xoutofcore ("cJSON_CreateObject");
     168          97 :   return json;
     169             : }
     170             : 
     171             : /* Call cJSON_CreateArray but terminate in case of an error.  */
     172             : static cjson_t
     173          30 : xjson_CreateArray (void)
     174             : {
     175          30 :   cjson_t json = cJSON_CreateArray ();
     176          30 :   if (!json)
     177           0 :     xoutofcore ("cJSON_CreateArray");
     178          30 :   return json;
     179             : }
     180             : 
     181             : 
     182             : /* Wrapper around cJSON_AddStringToObject which returns an gpg-error
     183             :  * code instead of the NULL or the new object.  */
     184             : static gpg_error_t
     185           7 : cjson_AddStringToObject (cjson_t object, const char *name, const char *string)
     186             : {
     187           7 :   if (!cJSON_AddStringToObject (object, name, string))
     188           0 :     return gpg_error_from_syserror ();
     189           7 :   return 0;
     190             : }
     191             : 
     192             : 
     193             : /* Same as cjson_AddStringToObject but prints an error message and
     194             :  * terminates the process.  */
     195             : static void
     196         235 : xjson_AddStringToObject (cjson_t object, const char *name, const char *string)
     197             : {
     198         235 :   if (!cJSON_AddStringToObject (object, name, string))
     199           0 :     xoutofcore ("cJSON_AddStringToObject");
     200         235 : }
     201             : 
     202             : 
     203             : /* Same as xjson_AddStringToObject but ignores NULL strings */
     204             : static void
     205         347 : xjson_AddStringToObject0 (cjson_t object, const char *name, const char *string)
     206             : {
     207         347 :   if (!string)
     208         120 :     return;
     209         227 :   xjson_AddStringToObject (object, name, string);
     210             : }
     211             : 
     212             : /* Wrapper around cJSON_AddBoolToObject which terminates the process
     213             :  * in case of an error.  */
     214             : static void
     215         265 : xjson_AddBoolToObject (cjson_t object, const char *name, int abool)
     216             : {
     217         265 :   if (!cJSON_AddBoolToObject (object, name, abool))
     218           0 :     xoutofcore ("cJSON_AddStringToObject");
     219         265 :   return ;
     220             : }
     221             : 
     222             : /* Wrapper around cJSON_AddNumberToObject which terminates the process
     223             :  * in case of an error.  */
     224             : static void
     225         204 : xjson_AddNumberToObject (cjson_t object, const char *name, double dbl)
     226             : {
     227         204 :   if (!cJSON_AddNumberToObject (object, name, dbl))
     228           0 :     xoutofcore ("cJSON_AddNumberToObject");
     229         204 :   return ;
     230             : }
     231             : 
     232             : /* Wrapper around cJSON_AddItemToObject which terminates the process
     233             :  * in case of an error.  */
     234             : static void
     235          36 : xjson_AddItemToObject (cjson_t object, const char *name, cjson_t item)
     236             : {
     237          36 :   if (!cJSON_AddItemToObject (object, name, item))
     238           0 :     xoutofcore ("cJSON_AddItemToObject");
     239          36 :   return ;
     240             : }
     241             : 
     242             : /* This is similar to cJSON_AddStringToObject but takes (DATA,
     243             :  * DATALEN) and adds it under NAME as a base 64 encoded string to
     244             :  * OBJECT.  */
     245             : static gpg_error_t
     246           2 : add_base64_to_object (cjson_t object, const char *name,
     247             :                       const void *data, size_t datalen)
     248             : {
     249             : #if GPGRT_VERSION_NUMBER < 0x011d00 /* 1.29 */
     250             :   return gpg_error (GPG_ERR_NOT_SUPPORTED);
     251             : #else
     252             :   gpg_err_code_t err;
     253           2 :   estream_t fp = NULL;
     254           2 :   gpgrt_b64state_t state = NULL;
     255           2 :   cjson_t j_str = NULL;
     256           2 :   void *buffer = NULL;
     257             : 
     258           2 :   fp = es_fopenmem (0, "rwb");
     259           2 :   if (!fp)
     260             :     {
     261           0 :       err = gpg_err_code_from_syserror ();
     262           0 :       goto leave;
     263             :     }
     264           2 :   state = gpgrt_b64enc_start (fp, "");
     265           2 :   if (!state)
     266             :     {
     267           0 :       err = gpg_err_code_from_syserror ();
     268           0 :       goto leave;
     269             :     }
     270             : 
     271           2 :   err = gpgrt_b64enc_write (state, data, datalen);
     272           2 :   if (err)
     273           0 :     goto leave;
     274             : 
     275           2 :   err = gpgrt_b64enc_finish (state);
     276           2 :   state = NULL;
     277           2 :   if (err)
     278           0 :     return err;
     279             : 
     280           2 :   es_fputc (0, fp);
     281           2 :   if (es_fclose_snatch (fp, &buffer, NULL))
     282             :     {
     283           0 :       fp = NULL;
     284           0 :       err = gpg_error_from_syserror ();
     285           0 :       goto leave;
     286             :     }
     287           2 :   fp = NULL;
     288             : 
     289           2 :   j_str = cJSON_CreateStringConvey (buffer);
     290           2 :   if (!j_str)
     291             :     {
     292           0 :       err = gpg_error_from_syserror ();
     293           0 :       goto leave;
     294             :     }
     295           2 :   buffer = NULL;
     296             : 
     297           2 :   if (!cJSON_AddItemToObject (object, name, j_str))
     298             :     {
     299           0 :       err = gpg_error_from_syserror ();
     300           0 :       cJSON_Delete (j_str);
     301           0 :       j_str = NULL;
     302           0 :       goto leave;
     303             :     }
     304           2 :   j_str = NULL;
     305             : 
     306             :  leave:
     307           2 :   xfree (buffer);
     308           2 :   cJSON_Delete (j_str);
     309           2 :   gpgrt_b64enc_finish (state);
     310           2 :   es_fclose (fp);
     311           2 :   return err;
     312             : #endif
     313             : }
     314             : 
     315             : 
     316             : /* Create a JSON error object.  If JSON is not NULL the error message
     317             :  * is appended to that object.  An existing "type" item will be replaced. */
     318             : static cjson_t
     319           0 : error_object_v (cjson_t json, const char *message, va_list arg_ptr,
     320             :                 gpg_error_t err)
     321             : {
     322             :   cjson_t response, j_tmp;
     323             :   char *msg;
     324             : 
     325           0 :   msg = gpgrt_vbsprintf (message, arg_ptr);
     326           0 :   if (!msg)
     327           0 :     xoutofcore ("error_object");
     328             : 
     329           0 :   response = json? json : xjson_CreateObject ();
     330             : 
     331           0 :   if (!(j_tmp = cJSON_GetObjectItem (response, "type")))
     332           0 :     xjson_AddStringToObject (response, "type", "error");
     333             :   else /* Replace existing "type".  */
     334             :     {
     335           0 :       j_tmp = cJSON_CreateString ("error");
     336           0 :       if (!j_tmp)
     337           0 :         xoutofcore ("cJSON_CreateString");
     338           0 :       cJSON_ReplaceItemInObject (response, "type", j_tmp);
     339             :      }
     340           0 :   xjson_AddStringToObject (response, "msg", msg);
     341           0 :   xfree (msg);
     342             : 
     343           0 :   xjson_AddNumberToObject (response, "code", err);
     344             : 
     345           0 :   return response;
     346             : }
     347             : 
     348             : 
     349             : /* Call cJSON_Print but terminate in case of an error.  */
     350             : static char *
     351           0 : xjson_Print (cjson_t object)
     352             : {
     353             :   char *buf;
     354           0 :   buf = cJSON_Print (object);
     355           0 :   if (!buf)
     356           0 :     xoutofcore ("cJSON_Print");
     357           0 :   return buf;
     358             : }
     359             : 
     360             : 
     361             : static cjson_t
     362           0 : error_object (cjson_t json, const char *message, ...)
     363             : {
     364             :   cjson_t response;
     365             :   va_list arg_ptr;
     366             : 
     367           0 :   va_start (arg_ptr, message);
     368           0 :   response = error_object_v (json, message, arg_ptr, 0);
     369           0 :   va_end (arg_ptr);
     370           0 :   return response;
     371             : }
     372             : 
     373             : 
     374             : static cjson_t
     375           0 : gpg_error_object (cjson_t json, gpg_error_t err, const char *message, ...)
     376             : {
     377             :   cjson_t response;
     378             :   va_list arg_ptr;
     379             : 
     380           0 :   va_start (arg_ptr, message);
     381           0 :   response = error_object_v (json, message, arg_ptr, err);
     382           0 :   va_end (arg_ptr);
     383           0 :   return response;
     384             : }
     385             : 
     386             : 
     387             : static char *
     388           0 : error_object_string (const char *message, ...)
     389             : {
     390             :   cjson_t response;
     391             :   va_list arg_ptr;
     392             :   char *msg;
     393             : 
     394           0 :   va_start (arg_ptr, message);
     395           0 :   response = error_object_v (NULL, message, arg_ptr, 0);
     396           0 :   va_end (arg_ptr);
     397             : 
     398           0 :   msg = xjson_Print (response);
     399           0 :   cJSON_Delete (response);
     400           0 :   return msg;
     401             : }
     402             : 
     403             : 
     404             : /* Get the boolean property NAME from the JSON object and store true
     405             :  * or valse at R_VALUE.  If the name is unknown the value of DEF_VALUE
     406             :  * is returned.  If the type of the value is not boolean,
     407             :  * GPG_ERR_INV_VALUE is returned and R_VALUE set to DEF_VALUE.  */
     408             : static gpg_error_t
     409          72 : get_boolean_flag (cjson_t json, const char *name, int def_value, int *r_value)
     410             : {
     411             :   cjson_t j_item;
     412             : 
     413          72 :   j_item = cJSON_GetObjectItem (json, name);
     414          72 :   if (!j_item)
     415          59 :     *r_value = def_value;
     416          13 :   else if (cjson_is_true (j_item))
     417          10 :     *r_value = 1;
     418           3 :   else if (cjson_is_false (j_item))
     419           3 :     *r_value = 0;
     420             :   else
     421             :     {
     422           0 :       *r_value = def_value;
     423           0 :       return gpg_error (GPG_ERR_INV_VALUE);
     424             :     }
     425             : 
     426          72 :   return 0;
     427             : }
     428             : 
     429             : 
     430             : /* Get the boolean property PROTOCOL from the JSON object and store
     431             :  * its value at R_PROTOCOL.  The default is OpenPGP.  */
     432             : static gpg_error_t
     433          14 : get_protocol (cjson_t json, gpgme_protocol_t *r_protocol)
     434             : {
     435             :   cjson_t j_item;
     436             : 
     437          14 :   *r_protocol = GPGME_PROTOCOL_OpenPGP;
     438          14 :   j_item = cJSON_GetObjectItem (json, "protocol");
     439          14 :   if (!j_item)
     440             :     ;
     441           0 :   else if (!cjson_is_string (j_item))
     442           0 :     return gpg_error (GPG_ERR_INV_VALUE);
     443           0 :   else if (!strcmp(j_item->valuestring, "openpgp"))
     444             :     ;
     445           0 :   else if (!strcmp(j_item->valuestring, "cms"))
     446           0 :     *r_protocol = GPGME_PROTOCOL_CMS;
     447             :   else
     448           0 :     return gpg_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
     449             : 
     450          14 :   return 0;
     451             : }
     452             : 
     453             : 
     454             : /* Get the chunksize from JSON and store it at R_CHUNKSIZE.  */
     455             : static gpg_error_t
     456          19 : get_chunksize (cjson_t json, size_t *r_chunksize)
     457             : {
     458             :   cjson_t j_item;
     459             : 
     460          19 :   *r_chunksize = DEF_REPLY_CHUNK_SIZE;
     461          19 :   j_item = cJSON_GetObjectItem (json, "chunksize");
     462          19 :   if (!j_item)
     463             :     ;
     464           2 :   else if (!cjson_is_number (j_item))
     465           0 :     return gpg_error (GPG_ERR_INV_VALUE);
     466           2 :   else if ((size_t)j_item->valueint < MIN_REPLY_CHUNK_SIZE)
     467           0 :     *r_chunksize = MIN_REPLY_CHUNK_SIZE;
     468           2 :   else if ((size_t)j_item->valueint > MAX_REPLY_CHUNK_SIZE)
     469           0 :     *r_chunksize = MAX_REPLY_CHUNK_SIZE;
     470             :   else
     471           2 :     *r_chunksize = (size_t)j_item->valueint;
     472             : 
     473          19 :   return 0;
     474             : }
     475             : 
     476             : 
     477             : /* Extract the keys from the array or string with the name "name"
     478             :  * in the JSON object.  On success a string with the keys identifiers
     479             :  * is stored at R_KEYS.
     480             :  * The keys in that string are LF delimited.  On failure an error code
     481             :  * is returned.  */
     482             : static gpg_error_t
     483          10 : get_keys (cjson_t json, const char *name, char **r_keystring)
     484             : {
     485             :   cjson_t j_keys, j_item;
     486             :   int i, nkeys;
     487             :   char *p;
     488             :   size_t length;
     489             : 
     490          10 :   *r_keystring = NULL;
     491             : 
     492          10 :   j_keys = cJSON_GetObjectItem (json, name);
     493          10 :   if (!j_keys)
     494           1 :     return gpg_error (GPG_ERR_NO_KEY);
     495           9 :   if (!cjson_is_array (j_keys) && !cjson_is_string (j_keys))
     496           0 :     return gpg_error (GPG_ERR_INV_VALUE);
     497             : 
     498             :   /* Fixme: We should better use a membuf like thing.  */
     499           9 :   length = 1; /* For the EOS.  */
     500           9 :   if (cjson_is_string (j_keys))
     501             :     {
     502           3 :       nkeys = 1;
     503           3 :       length += strlen (j_keys->valuestring);
     504           3 :       if (strchr (j_keys->valuestring, '\n'))
     505           0 :         return gpg_error (GPG_ERR_INV_USER_ID);
     506             :     }
     507             :   else
     508             :     {
     509           6 :       nkeys = cJSON_GetArraySize (j_keys);
     510           6 :       if (!nkeys)
     511           0 :         return gpg_error (GPG_ERR_NO_KEY);
     512          16 :       for (i=0; i < nkeys; i++)
     513             :         {
     514          10 :           j_item = cJSON_GetArrayItem (j_keys, i);
     515          10 :           if (!j_item || !cjson_is_string (j_item))
     516           0 :             return gpg_error (GPG_ERR_INV_VALUE);
     517          10 :           if (i)
     518           4 :             length++; /* Space for delimiter. */
     519          10 :           length += strlen (j_item->valuestring);
     520          10 :           if (strchr (j_item->valuestring, '\n'))
     521           0 :             return gpg_error (GPG_ERR_INV_USER_ID);
     522             :         }
     523             :     }
     524             : 
     525           9 :   p = *r_keystring = xtrymalloc (length);
     526           9 :   if (!p)
     527           0 :     return gpg_error_from_syserror ();
     528             : 
     529           9 :   if (cjson_is_string (j_keys))
     530             :     {
     531           3 :       strcpy (p, j_keys->valuestring);
     532             :     }
     533             :   else
     534             :     {
     535          16 :       for (i=0; i < nkeys; i++)
     536             :         {
     537          10 :           j_item = cJSON_GetArrayItem (j_keys, i);
     538          10 :           if (i)
     539           4 :             *p++ = '\n'; /* Add delimiter.  */
     540          10 :           p = stpcpy (p, j_item->valuestring);
     541             :         }
     542             :     }
     543           9 :   return 0;
     544             : }
     545             : 
     546             : 
     547             : 
     548             : 
     549             : /*
     550             :  *  GPGME support functions.
     551             :  */
     552             : 
     553             : /* Helper for get_context.  */
     554             : static gpgme_ctx_t
     555          19 : _create_new_context (gpgme_protocol_t proto)
     556             : {
     557             :   gpg_error_t err;
     558             :   gpgme_ctx_t ctx;
     559             : 
     560          19 :   err = gpgme_new (&ctx);
     561          19 :   if (err)
     562           0 :     log_fatal ("error creating GPGME context: %s\n", gpg_strerror (err));
     563          19 :   gpgme_set_protocol (ctx, proto);
     564          19 :   gpgme_set_ctx_flag (ctx, "request-origin", "browser");
     565          19 :   return ctx;
     566             : }
     567             : 
     568             : 
     569             : /* Return a context object for protocol PROTO.  This is currently a
     570             :  * statically allocated context initialized for PROTO.  Terminates
     571             :  * process on failure.  */
     572             : static gpgme_ctx_t
     573          17 : get_context (gpgme_protocol_t proto)
     574             : {
     575             :   static gpgme_ctx_t ctx_openpgp, ctx_cms, ctx_conf;
     576             : 
     577          17 :   if (proto == GPGME_PROTOCOL_OpenPGP)
     578             :     {
     579          15 :       if (!ctx_openpgp)
     580          14 :         ctx_openpgp = _create_new_context (proto);
     581          15 :       return ctx_openpgp;
     582             :     }
     583           2 :   else if (proto == GPGME_PROTOCOL_CMS)
     584             :     {
     585           0 :       if (!ctx_cms)
     586           0 :         ctx_cms = _create_new_context (proto);
     587           0 :       return ctx_cms;
     588             :     }
     589           2 :   else if (proto == GPGME_PROTOCOL_GPGCONF)
     590             :     {
     591           2 :       if (!ctx_conf)
     592           2 :         ctx_conf = _create_new_context (proto);
     593           2 :       return ctx_conf;
     594             :     }
     595             :   else
     596           0 :     log_bug ("invalid protocol %d requested\n", proto);
     597             : }
     598             : 
     599             : 
     600             : /* Free context object retrieved by get_context.  */
     601             : static void
     602          14 : release_context (gpgme_ctx_t ctx)
     603             : {
     604             :   /* Nothing to do right now.  */
     605             :   (void)ctx;
     606          14 : }
     607             : 
     608             : 
     609             : /* Create an addition context for short operations. */
     610             : static gpgme_ctx_t
     611           3 : create_onetime_context (gpgme_protocol_t proto)
     612             : {
     613           3 :   return _create_new_context (proto);
     614             : 
     615             : }
     616             : 
     617             : 
     618             : /* Release a one-time context.  */
     619             : static void
     620           6 : release_onetime_context (gpgme_ctx_t ctx)
     621             : {
     622           6 :   return gpgme_release (ctx);
     623             : 
     624             : }
     625             : 
     626             : 
     627             : /* Given a Base-64 encoded string object in JSON return a gpgme data
     628             :  * object at R_DATA.  */
     629             : static gpg_error_t
     630           2 : data_from_base64_string (gpgme_data_t *r_data, cjson_t json)
     631             : {
     632             : #if GPGRT_VERSION_NUMBER < 0x011d00 /* 1.29 */
     633             :   *r_data = NULL;
     634             :   return gpg_error (GPG_ERR_NOT_SUPPORTED);
     635             : #else
     636             :   gpg_error_t err;
     637             :   size_t len;
     638           2 :   char *buf = NULL;
     639           2 :   gpgrt_b64state_t state = NULL;
     640           2 :   gpgme_data_t data = NULL;
     641             : 
     642           2 :   *r_data = NULL;
     643             : 
     644             :   /* A quick check on the JSON.  */
     645           2 :   if (!cjson_is_string (json))
     646             :     {
     647           0 :       err = gpg_error (GPG_ERR_INV_VALUE);
     648           0 :       goto leave;
     649             :     }
     650             : 
     651           2 :   state = gpgrt_b64dec_start (NULL);
     652           2 :   if (!state)
     653             :     {
     654           0 :       err = gpg_err_code_from_syserror ();
     655           0 :       goto leave;
     656             :     }
     657             : 
     658             :   /* Fixme: Data duplication - we should see how to snatch the memory
     659             :    * from the json object.  */
     660           2 :   len = strlen (json->valuestring);
     661           2 :   buf = xtrystrdup (json->valuestring);
     662           2 :   if (!buf)
     663             :     {
     664           0 :       err = gpg_error_from_syserror ();
     665           0 :       goto leave;
     666             :     }
     667             : 
     668           2 :   err = gpgrt_b64dec_proc (state, buf, len, &len);
     669           2 :   if (err)
     670           0 :     goto leave;
     671             : 
     672           2 :   err = gpgrt_b64dec_finish (state);
     673           2 :   state = NULL;
     674           2 :   if (err)
     675           0 :     goto leave;
     676             : 
     677           2 :   err = gpgme_data_new_from_mem (&data, buf, len, 1);
     678           2 :   if (err)
     679           0 :     goto leave;
     680           2 :   *r_data = data;
     681           2 :   data = NULL;
     682             : 
     683             :  leave:
     684           2 :   xfree (data);
     685           2 :   xfree (buf);
     686           2 :   gpgrt_b64dec_finish (state);
     687           2 :   return err;
     688             : #endif
     689             : }
     690             : 
     691             : 
     692             : /* Create a keylist pattern array from a json keys object
     693             :  * in the request. Returns either a malloced NULL terminated
     694             :  * string array which can be used as patterns for
     695             :  * op_keylist_ext or NULL. */
     696             : static char **
     697           8 : create_keylist_patterns (cjson_t request, const char *name)
     698             : {
     699             :   char *keystring;
     700             :   char *p;
     701             :   char *tmp;
     702             :   char **ret;
     703           8 :   int cnt = 2; /* Last NULL and one is not newline delimited */
     704           8 :   int i = 0;
     705             : 
     706           8 :   if (get_keys (request, name, &keystring))
     707           1 :     return NULL;
     708             : 
     709         208 :   for (p = keystring; *p; p++)
     710         201 :     if (*p == '\n')
     711           2 :       cnt++;
     712             : 
     713           7 :   ret = xcalloc (cnt, sizeof *ret);
     714             : 
     715         208 :   for (p = keystring, tmp = keystring; *p; p++)
     716             :     {
     717         201 :       if (*p != '\n')
     718         199 :         continue;
     719           2 :       *p = '\0';
     720           2 :       ret[i++] = xstrdup (tmp);
     721           2 :       tmp = p + 1;
     722             :     }
     723             :   /* The last key is not newline delimited. */
     724           7 :   ret[i] = *tmp ? xstrdup (tmp) : NULL;
     725             : 
     726           7 :   xfree (keystring);
     727           7 :   return ret;
     728             : }
     729             : 
     730             : 
     731             : /* Do a secret keylisting for protocol proto and add the fingerprints of
     732             :    the secret keys for patterns to the result as "sec-fprs" array. */
     733             : static gpg_error_t
     734           1 : add_secret_fprs (const char **patterns, gpgme_protocol_t protocol,
     735             :                  cjson_t result)
     736             : {
     737             :   gpgme_ctx_t ctx;
     738             :   gpg_error_t err;
     739           1 :   gpgme_key_t key = NULL;
     740           1 :   cjson_t j_fprs = xjson_CreateArray ();
     741             : 
     742           1 :   ctx = create_onetime_context (protocol);
     743             : 
     744           1 :   gpgme_set_keylist_mode (ctx, GPGME_KEYLIST_MODE_LOCAL |
     745             :                                GPGME_KEYLIST_MODE_WITH_SECRET);
     746             : 
     747           1 :   err = gpgme_op_keylist_ext_start (ctx, patterns, 1, 0);
     748             : 
     749           1 :   if (err)
     750             :     {
     751           0 :       gpg_error_object (result, err, "Error listing keys: %s",
     752             :                         gpg_strerror (err));
     753           0 :       goto leave;
     754             :     }
     755             : 
     756           3 :   while (!(err = gpgme_op_keylist_next (ctx, &key)))
     757             :     {
     758           1 :       if (!key || !key->fpr)
     759           0 :         continue;
     760           1 :       cJSON_AddItemToArray (j_fprs, cJSON_CreateString (key->fpr));
     761           1 :       gpgme_key_unref (key);
     762           1 :       key = NULL;
     763             :     }
     764           1 :   err = 0;
     765             : 
     766           1 :   release_onetime_context (ctx);
     767           1 :   ctx = NULL;
     768             : 
     769           1 :   xjson_AddItemToObject (result, "sec-fprs", j_fprs);
     770             : 
     771             : leave:
     772           1 :   release_onetime_context (ctx);
     773           1 :   gpgme_key_unref (key);
     774             : 
     775           1 :   return err;
     776             : }
     777             : 
     778             : 
     779             : /* Create sigsum json array */
     780             : static cjson_t
     781           3 : sigsum_to_json (gpgme_sigsum_t summary)
     782             : {
     783           3 :   cjson_t result = xjson_CreateObject ();
     784           3 :   cjson_t sigsum_array = xjson_CreateArray ();
     785             : 
     786           3 :   if ( (summary & GPGME_SIGSUM_VALID      ))
     787           0 :     cJSON_AddItemToArray (sigsum_array,
     788             :         cJSON_CreateString ("valid"));
     789           3 :   if ( (summary & GPGME_SIGSUM_GREEN      ))
     790           0 :     cJSON_AddItemToArray (sigsum_array,
     791             :         cJSON_CreateString ("green"));
     792           3 :   if ( (summary & GPGME_SIGSUM_RED        ))
     793           0 :     cJSON_AddItemToArray (sigsum_array,
     794             :         cJSON_CreateString ("red"));
     795           3 :   if ( (summary & GPGME_SIGSUM_KEY_REVOKED))
     796           0 :     cJSON_AddItemToArray (sigsum_array,
     797             :         cJSON_CreateString ("revoked"));
     798           3 :   if ( (summary & GPGME_SIGSUM_KEY_EXPIRED))
     799           0 :     cJSON_AddItemToArray (sigsum_array,
     800             :         cJSON_CreateString ("key-expired"));
     801           3 :   if ( (summary & GPGME_SIGSUM_SIG_EXPIRED))
     802           0 :     cJSON_AddItemToArray (sigsum_array,
     803             :         cJSON_CreateString ("sig-expired"));
     804           3 :   if ( (summary & GPGME_SIGSUM_KEY_MISSING))
     805           0 :     cJSON_AddItemToArray (sigsum_array,
     806             :         cJSON_CreateString ("key-missing"));
     807           3 :   if ( (summary & GPGME_SIGSUM_CRL_MISSING))
     808           0 :     cJSON_AddItemToArray (sigsum_array,
     809             :         cJSON_CreateString ("crl-missing"));
     810           3 :   if ( (summary & GPGME_SIGSUM_CRL_TOO_OLD))
     811           0 :     cJSON_AddItemToArray (sigsum_array,
     812             :         cJSON_CreateString ("crl-too-old"));
     813           3 :   if ( (summary & GPGME_SIGSUM_BAD_POLICY ))
     814           0 :     cJSON_AddItemToArray (sigsum_array,
     815             :         cJSON_CreateString ("bad-policy"));
     816           3 :   if ( (summary & GPGME_SIGSUM_SYS_ERROR  ))
     817           0 :     cJSON_AddItemToArray (sigsum_array,
     818             :         cJSON_CreateString ("sys-error"));
     819             :   /* The signature summary as string array. */
     820           3 :   xjson_AddItemToObject (result, "sigsum", sigsum_array);
     821             : 
     822             :   /* Bools for the same. */
     823           3 :   xjson_AddBoolToObject (result, "valid",
     824           3 :                          (summary & GPGME_SIGSUM_VALID      ));
     825           3 :   xjson_AddBoolToObject (result, "green",
     826           3 :                          (summary & GPGME_SIGSUM_GREEN      ));
     827           3 :   xjson_AddBoolToObject (result, "red",
     828           3 :                          (summary & GPGME_SIGSUM_RED        ));
     829           3 :   xjson_AddBoolToObject (result, "revoked",
     830           3 :                          (summary & GPGME_SIGSUM_KEY_REVOKED));
     831           3 :   xjson_AddBoolToObject (result, "key-expired",
     832           3 :                          (summary & GPGME_SIGSUM_KEY_EXPIRED));
     833           3 :   xjson_AddBoolToObject (result, "sig-expired",
     834           3 :                          (summary & GPGME_SIGSUM_SIG_EXPIRED));
     835           3 :   xjson_AddBoolToObject (result, "key-missing",
     836           3 :                          (summary & GPGME_SIGSUM_KEY_MISSING));
     837           3 :   xjson_AddBoolToObject (result, "crl-missing",
     838           3 :                          (summary & GPGME_SIGSUM_CRL_MISSING));
     839           3 :   xjson_AddBoolToObject (result, "crl-too-old",
     840           3 :                          (summary & GPGME_SIGSUM_CRL_TOO_OLD));
     841           3 :   xjson_AddBoolToObject (result, "bad-policy",
     842           3 :                          (summary & GPGME_SIGSUM_BAD_POLICY ));
     843           3 :   xjson_AddBoolToObject (result, "sys-error",
     844           3 :                          (summary & GPGME_SIGSUM_SYS_ERROR  ));
     845             : 
     846           3 :   return result;
     847             : }
     848             : 
     849             : 
     850             : /* Helper for summary formatting */
     851             : static const char *
     852          19 : validity_to_string (gpgme_validity_t val)
     853             : {
     854          19 :   switch (val)
     855             :     {
     856           0 :     case GPGME_VALIDITY_UNDEFINED:return "undefined";
     857           0 :     case GPGME_VALIDITY_NEVER:    return "never";
     858           0 :     case GPGME_VALIDITY_MARGINAL: return "marginal";
     859           0 :     case GPGME_VALIDITY_FULL:     return "full";
     860           0 :     case GPGME_VALIDITY_ULTIMATE: return "ultimate";
     861             :     case GPGME_VALIDITY_UNKNOWN:
     862          19 :     default:                      return "unknown";
     863             :     }
     864             : }
     865             : 
     866             : static const char *
     867          11 : protocol_to_string (gpgme_protocol_t proto)
     868             : {
     869          11 :   switch (proto)
     870             :     {
     871           6 :     case GPGME_PROTOCOL_OpenPGP: return "OpenPGP";
     872           1 :     case GPGME_PROTOCOL_CMS:     return "CMS";
     873           1 :     case GPGME_PROTOCOL_GPGCONF: return "gpgconf";
     874           1 :     case GPGME_PROTOCOL_ASSUAN:  return "assuan";
     875           0 :     case GPGME_PROTOCOL_G13:     return "g13";
     876           1 :     case GPGME_PROTOCOL_UISERVER:return "uiserver";
     877           1 :     case GPGME_PROTOCOL_SPAWN:   return "spawn";
     878             :     default:
     879           0 :                                  return "unknown";
     880             :     }
     881             : }
     882             : 
     883             : /* Create a sig_notation json object */
     884             : static cjson_t
     885           3 : sig_notation_to_json (gpgme_sig_notation_t not)
     886             : {
     887           3 :   cjson_t result = xjson_CreateObject ();
     888           3 :   xjson_AddBoolToObject (result, "human_readable", not->human_readable);
     889           3 :   xjson_AddBoolToObject (result, "critical", not->critical);
     890             : 
     891           3 :   xjson_AddStringToObject0 (result, "name", not->name);
     892           3 :   xjson_AddStringToObject0 (result, "value", not->value);
     893             : 
     894           3 :   xjson_AddNumberToObject (result, "flags", not->flags);
     895             : 
     896           3 :   return result;
     897             : }
     898             : 
     899             : /* Create a key_sig json object */
     900             : static cjson_t
     901           0 : key_sig_to_json (gpgme_key_sig_t sig)
     902             : {
     903           0 :   cjson_t result = xjson_CreateObject ();
     904             : 
     905           0 :   xjson_AddBoolToObject (result, "revoked", sig->revoked);
     906           0 :   xjson_AddBoolToObject (result, "expired", sig->expired);
     907           0 :   xjson_AddBoolToObject (result, "invalid", sig->invalid);
     908           0 :   xjson_AddBoolToObject (result, "exportable", sig->exportable);
     909             : 
     910           0 :   xjson_AddStringToObject0 (result, "pubkey_algo_name",
     911             :                             gpgme_pubkey_algo_name (sig->pubkey_algo));
     912           0 :   xjson_AddStringToObject0 (result, "keyid", sig->keyid);
     913           0 :   xjson_AddStringToObject0 (result, "status", gpgme_strerror (sig->status));
     914           0 :   xjson_AddStringToObject0 (result, "name", sig->name);
     915           0 :   xjson_AddStringToObject0 (result, "email", sig->email);
     916           0 :   xjson_AddStringToObject0 (result, "comment", sig->comment);
     917             : 
     918           0 :   xjson_AddNumberToObject (result, "pubkey_algo", sig->pubkey_algo);
     919           0 :   xjson_AddNumberToObject (result, "timestamp", sig->timestamp);
     920           0 :   xjson_AddNumberToObject (result, "expires", sig->expires);
     921           0 :   xjson_AddNumberToObject (result, "status_code", sig->status);
     922           0 :   xjson_AddNumberToObject (result, "sig_class", sig->sig_class);
     923             : 
     924           0 :   if (sig->notations)
     925             :     {
     926             :       gpgme_sig_notation_t not;
     927           0 :       cjson_t array = xjson_CreateArray ();
     928           0 :       for (not = sig->notations; not; not = not->next)
     929           0 :         cJSON_AddItemToArray (array, sig_notation_to_json (not));
     930           0 :       xjson_AddItemToObject (result, "notations", array);
     931             :     }
     932             : 
     933           0 :   return result;
     934             : }
     935             : 
     936             : /* Create a tofu info object */
     937             : static cjson_t
     938           0 : tofu_to_json (gpgme_tofu_info_t tofu)
     939             : {
     940           0 :   cjson_t result = xjson_CreateObject ();
     941             : 
     942           0 :   xjson_AddStringToObject0 (result, "description", tofu->description);
     943             : 
     944           0 :   xjson_AddNumberToObject (result, "validity", tofu->validity);
     945           0 :   xjson_AddNumberToObject (result, "policy", tofu->policy);
     946           0 :   xjson_AddNumberToObject (result, "signcount", tofu->signcount);
     947           0 :   xjson_AddNumberToObject (result, "encrcount", tofu->encrcount);
     948           0 :   xjson_AddNumberToObject (result, "signfirst", tofu->signfirst);
     949           0 :   xjson_AddNumberToObject (result, "signlast", tofu->signlast);
     950           0 :   xjson_AddNumberToObject (result, "encrfirst", tofu->encrfirst);
     951           0 :   xjson_AddNumberToObject (result, "encrlast", tofu->encrlast);
     952             : 
     953           0 :   return result;
     954             : }
     955             : 
     956             : /* Create a userid json object */
     957             : static cjson_t
     958          11 : uid_to_json (gpgme_user_id_t uid)
     959             : {
     960          11 :   cjson_t result = xjson_CreateObject ();
     961             : 
     962          11 :   xjson_AddBoolToObject (result, "revoked", uid->revoked);
     963          11 :   xjson_AddBoolToObject (result, "invalid", uid->invalid);
     964             : 
     965          11 :   xjson_AddStringToObject0 (result, "validity",
     966             :                             validity_to_string (uid->validity));
     967          11 :   xjson_AddStringToObject0 (result, "uid", uid->uid);
     968          11 :   xjson_AddStringToObject0 (result, "name", uid->name);
     969          11 :   xjson_AddStringToObject0 (result, "email", uid->email);
     970          11 :   xjson_AddStringToObject0 (result, "comment", uid->comment);
     971          11 :   xjson_AddStringToObject0 (result, "address", uid->address);
     972             : 
     973          11 :   xjson_AddNumberToObject (result, "origin", uid->origin);
     974          11 :   xjson_AddNumberToObject (result, "last_update", uid->last_update);
     975             : 
     976             :   /* Key sigs */
     977          11 :   if (uid->signatures)
     978             :     {
     979           0 :       cjson_t sig_array = xjson_CreateArray ();
     980             :       gpgme_key_sig_t sig;
     981             : 
     982           0 :       for (sig = uid->signatures; sig; sig = sig->next)
     983           0 :         cJSON_AddItemToArray (sig_array, key_sig_to_json (sig));
     984             : 
     985           0 :       xjson_AddItemToObject (result, "signatures", sig_array);
     986             :     }
     987             : 
     988             :   /* TOFU info */
     989          11 :   if (uid->tofu)
     990             :     {
     991             :       gpgme_tofu_info_t tofu;
     992           0 :       cjson_t array = xjson_CreateArray ();
     993           0 :       for (tofu = uid->tofu; tofu; tofu = tofu->next)
     994           0 :         cJSON_AddItemToArray (array, tofu_to_json (tofu));
     995           0 :       xjson_AddItemToObject (result, "tofu", array);
     996             :     }
     997             : 
     998          11 :   return result;
     999             : }
    1000             : 
    1001             : /* Create a subkey json object */
    1002             : static cjson_t
    1003          10 : subkey_to_json (gpgme_subkey_t sub)
    1004             : {
    1005          10 :   cjson_t result = xjson_CreateObject ();
    1006             : 
    1007          10 :   xjson_AddBoolToObject (result, "revoked", sub->revoked);
    1008          10 :   xjson_AddBoolToObject (result, "expired", sub->expired);
    1009          10 :   xjson_AddBoolToObject (result, "disabled", sub->disabled);
    1010          10 :   xjson_AddBoolToObject (result, "invalid", sub->invalid);
    1011          10 :   xjson_AddBoolToObject (result, "can_encrypt", sub->can_encrypt);
    1012          10 :   xjson_AddBoolToObject (result, "can_sign", sub->can_sign);
    1013          10 :   xjson_AddBoolToObject (result, "can_certify", sub->can_certify);
    1014          10 :   xjson_AddBoolToObject (result, "can_authenticate", sub->can_authenticate);
    1015          10 :   xjson_AddBoolToObject (result, "secret", sub->secret);
    1016          10 :   xjson_AddBoolToObject (result, "is_qualified", sub->is_qualified);
    1017          10 :   xjson_AddBoolToObject (result, "is_cardkey", sub->is_cardkey);
    1018          10 :   xjson_AddBoolToObject (result, "is_de_vs", sub->is_de_vs);
    1019             : 
    1020          10 :   xjson_AddStringToObject0 (result, "pubkey_algo_name",
    1021             :                             gpgme_pubkey_algo_name (sub->pubkey_algo));
    1022          10 :   xjson_AddStringToObject0 (result, "pubkey_algo_string",
    1023          10 :                             gpgme_pubkey_algo_string (sub));
    1024          10 :   xjson_AddStringToObject0 (result, "keyid", sub->keyid);
    1025          10 :   xjson_AddStringToObject0 (result, "card_number", sub->card_number);
    1026          10 :   xjson_AddStringToObject0 (result, "curve", sub->curve);
    1027          10 :   xjson_AddStringToObject0 (result, "keygrip", sub->keygrip);
    1028             : 
    1029          10 :   xjson_AddNumberToObject (result, "pubkey_algo", sub->pubkey_algo);
    1030          10 :   xjson_AddNumberToObject (result, "length", sub->length);
    1031          10 :   xjson_AddNumberToObject (result, "timestamp", sub->timestamp);
    1032          10 :   xjson_AddNumberToObject (result, "expires", sub->expires);
    1033             : 
    1034          10 :   return result;
    1035             : }
    1036             : 
    1037             : /* Create a key json object */
    1038             : static cjson_t
    1039           5 : key_to_json (gpgme_key_t key)
    1040             : {
    1041           5 :   cjson_t result = xjson_CreateObject ();
    1042             : 
    1043           5 :   xjson_AddBoolToObject (result, "revoked", key->revoked);
    1044           5 :   xjson_AddBoolToObject (result, "expired", key->expired);
    1045           5 :   xjson_AddBoolToObject (result, "disabled", key->disabled);
    1046           5 :   xjson_AddBoolToObject (result, "invalid", key->invalid);
    1047           5 :   xjson_AddBoolToObject (result, "can_encrypt", key->can_encrypt);
    1048           5 :   xjson_AddBoolToObject (result, "can_sign", key->can_sign);
    1049           5 :   xjson_AddBoolToObject (result, "can_certify", key->can_certify);
    1050           5 :   xjson_AddBoolToObject (result, "can_authenticate", key->can_authenticate);
    1051           5 :   xjson_AddBoolToObject (result, "secret", key->secret);
    1052           5 :   xjson_AddBoolToObject (result, "is_qualified", key->is_qualified);
    1053             : 
    1054           5 :   xjson_AddStringToObject0 (result, "protocol",
    1055             :                             protocol_to_string (key->protocol));
    1056           5 :   xjson_AddStringToObject0 (result, "issuer_serial", key->issuer_serial);
    1057           5 :   xjson_AddStringToObject0 (result, "issuer_name", key->issuer_name);
    1058           5 :   xjson_AddStringToObject0 (result, "fingerprint", key->fpr);
    1059           5 :   xjson_AddStringToObject0 (result, "chain_id", key->chain_id);
    1060           5 :   xjson_AddStringToObject0 (result, "owner_trust",
    1061             :                             validity_to_string (key->owner_trust));
    1062             : 
    1063           5 :   xjson_AddNumberToObject (result, "origin", key->origin);
    1064           5 :   xjson_AddNumberToObject (result, "last_update", key->last_update);
    1065             : 
    1066             :   /* Add subkeys */
    1067           5 :   if (key->subkeys)
    1068             :     {
    1069           5 :       cjson_t subkey_array = xjson_CreateArray ();
    1070             :       gpgme_subkey_t sub;
    1071          15 :       for (sub = key->subkeys; sub; sub = sub->next)
    1072          10 :         cJSON_AddItemToArray (subkey_array, subkey_to_json (sub));
    1073             : 
    1074           5 :       xjson_AddItemToObject (result, "subkeys", subkey_array);
    1075             :     }
    1076             : 
    1077             :   /* User Ids */
    1078           5 :   if (key->uids)
    1079             :     {
    1080           5 :       cjson_t uid_array = xjson_CreateArray ();
    1081             :       gpgme_user_id_t uid;
    1082          16 :       for (uid = key->uids; uid; uid = uid->next)
    1083          11 :         cJSON_AddItemToArray (uid_array, uid_to_json (uid));
    1084             : 
    1085           5 :       xjson_AddItemToObject (result, "userids", uid_array);
    1086             :     }
    1087             : 
    1088           5 :   return result;
    1089             : }
    1090             : 
    1091             : 
    1092             : /* Create a signature json object */
    1093             : static cjson_t
    1094           3 : signature_to_json (gpgme_signature_t sig)
    1095             : {
    1096           3 :   cjson_t result = xjson_CreateObject ();
    1097             : 
    1098           3 :   xjson_AddItemToObject (result, "summary", sigsum_to_json (sig->summary));
    1099             : 
    1100           3 :   xjson_AddBoolToObject (result, "wrong_key_usage", sig->wrong_key_usage);
    1101           3 :   xjson_AddBoolToObject (result, "chain_model", sig->chain_model);
    1102           3 :   xjson_AddBoolToObject (result, "is_de_vs", sig->is_de_vs);
    1103             : 
    1104           3 :   xjson_AddStringToObject0 (result, "status_string",
    1105             :                             gpgme_strerror (sig->status));
    1106           3 :   xjson_AddStringToObject0 (result, "fingerprint", sig->fpr);
    1107           3 :   xjson_AddStringToObject0 (result, "validity_string",
    1108             :                             validity_to_string (sig->validity));
    1109           3 :   xjson_AddStringToObject0 (result, "pubkey_algo_name",
    1110             :                             gpgme_pubkey_algo_name (sig->pubkey_algo));
    1111           3 :   xjson_AddStringToObject0 (result, "hash_algo_name",
    1112             :                             gpgme_hash_algo_name (sig->hash_algo));
    1113           3 :   xjson_AddStringToObject0 (result, "pka_address", sig->pka_address);
    1114             : 
    1115           3 :   xjson_AddNumberToObject (result, "status_code", sig->status);
    1116           3 :   xjson_AddNumberToObject (result, "timestamp", sig->timestamp);
    1117           3 :   xjson_AddNumberToObject (result, "exp_timestamp", sig->exp_timestamp);
    1118           3 :   xjson_AddNumberToObject (result, "pka_trust", sig->pka_trust);
    1119           3 :   xjson_AddNumberToObject (result, "validity", sig->validity);
    1120           3 :   xjson_AddNumberToObject (result, "validity_reason", sig->validity_reason);
    1121             : 
    1122           3 :   if (sig->notations)
    1123             :     {
    1124             :       gpgme_sig_notation_t not;
    1125           1 :       cjson_t array = xjson_CreateArray ();
    1126           4 :       for (not = sig->notations; not; not = not->next)
    1127           3 :         cJSON_AddItemToArray (array, sig_notation_to_json (not));
    1128           1 :       xjson_AddItemToObject (result, "notations", array);
    1129             :     }
    1130             : 
    1131           3 :   return result;
    1132             : }
    1133             : 
    1134             : 
    1135             : /* Create a JSON object from a gpgme_verify result */
    1136             : static cjson_t
    1137           3 : verify_result_to_json (gpgme_verify_result_t verify_result)
    1138             : {
    1139           3 :   cjson_t result = xjson_CreateObject ();
    1140             : 
    1141           3 :   xjson_AddBoolToObject (result, "is_mime", verify_result->is_mime);
    1142             : 
    1143           3 :   if (verify_result->signatures)
    1144             :     {
    1145           3 :       cjson_t array = xjson_CreateArray ();
    1146             :       gpgme_signature_t sig;
    1147             : 
    1148           6 :       for (sig = verify_result->signatures; sig; sig = sig->next)
    1149           3 :         cJSON_AddItemToArray (array, signature_to_json (sig));
    1150           3 :       xjson_AddItemToObject (result, "signatures", array);
    1151             :     }
    1152             : 
    1153           3 :   return result;
    1154             : }
    1155             : 
    1156             : /* Create a recipient json object */
    1157             : static cjson_t
    1158           3 : recipient_to_json (gpgme_recipient_t recp)
    1159             : {
    1160           3 :   cjson_t result = xjson_CreateObject ();
    1161             : 
    1162           3 :   xjson_AddStringToObject0 (result, "keyid", recp->keyid);
    1163           3 :   xjson_AddStringToObject0 (result, "pubkey_algo_name",
    1164             :                             gpgme_pubkey_algo_name (recp->pubkey_algo));
    1165           3 :   xjson_AddStringToObject0 (result, "status_string",
    1166             :                             gpgme_strerror (recp->status));
    1167             : 
    1168           3 :   xjson_AddNumberToObject (result, "status_code", recp->status);
    1169             : 
    1170           3 :   return result;
    1171             : }
    1172             : 
    1173             : 
    1174             : /* Create a JSON object from a gpgme_decrypt result */
    1175             : static cjson_t
    1176           2 : decrypt_result_to_json (gpgme_decrypt_result_t decrypt_result)
    1177             : {
    1178           2 :   cjson_t result = xjson_CreateObject ();
    1179             : 
    1180           2 :   xjson_AddStringToObject0 (result, "file_name", decrypt_result->file_name);
    1181           2 :   xjson_AddStringToObject0 (result, "symkey_algo",
    1182           2 :                             decrypt_result->symkey_algo);
    1183             : 
    1184           2 :   xjson_AddBoolToObject (result, "wrong_key_usage",
    1185           2 :                          decrypt_result->wrong_key_usage);
    1186           2 :   xjson_AddBoolToObject (result, "is_de_vs",
    1187           2 :                          decrypt_result->is_de_vs);
    1188           2 :   xjson_AddBoolToObject (result, "is_mime", decrypt_result->is_mime);
    1189           2 :   xjson_AddBoolToObject (result, "legacy_cipher_nomdc",
    1190           2 :                          decrypt_result->legacy_cipher_nomdc);
    1191             : 
    1192           2 :   if (decrypt_result->recipients)
    1193             :     {
    1194           2 :       cjson_t array = xjson_CreateArray ();
    1195             :       gpgme_recipient_t recp;
    1196             : 
    1197           5 :       for (recp = decrypt_result->recipients; recp; recp = recp->next)
    1198           3 :         cJSON_AddItemToArray (array, recipient_to_json (recp));
    1199           2 :       xjson_AddItemToObject (result, "recipients", array);
    1200             :     }
    1201             : 
    1202           2 :   return result;
    1203             : }
    1204             : 
    1205             : 
    1206             : /* Create a JSON object from an engine_info */
    1207             : static cjson_t
    1208           6 : engine_info_to_json (gpgme_engine_info_t info)
    1209             : {
    1210           6 :   cjson_t result = xjson_CreateObject ();
    1211             : 
    1212           6 :   xjson_AddStringToObject0 (result, "protocol",
    1213             :                             protocol_to_string (info->protocol));
    1214           6 :   xjson_AddStringToObject0 (result, "fname", info->file_name);
    1215           6 :   xjson_AddStringToObject0 (result, "version", info->version);
    1216           6 :   xjson_AddStringToObject0 (result, "req_version", info->req_version);
    1217           6 :   xjson_AddStringToObject0 (result, "homedir", info->home_dir ?
    1218             :                                                 info->home_dir :
    1219             :                                                 "default");
    1220           6 :   return result;
    1221             : }
    1222             : 
    1223             : 
    1224             : /* Create a JSON object from an import_status */
    1225             : static cjson_t
    1226           1 : import_status_to_json (gpgme_import_status_t sts)
    1227             : {
    1228           1 :   cjson_t result = xjson_CreateObject ();
    1229             : 
    1230           1 :   xjson_AddStringToObject0 (result, "fingerprint", sts->fpr);
    1231           1 :   xjson_AddStringToObject0 (result, "error_string",
    1232             :                             gpgme_strerror (sts->result));
    1233             : 
    1234           1 :   xjson_AddNumberToObject (result, "status", sts->status);
    1235             : 
    1236           1 :   return result;
    1237             : }
    1238             : 
    1239             : /* Create a JSON object from an import result */
    1240             : static cjson_t
    1241           1 : import_result_to_json (gpgme_import_result_t imp)
    1242             : {
    1243           1 :   cjson_t result = xjson_CreateObject ();
    1244             : 
    1245           1 :   xjson_AddNumberToObject (result, "considered", imp->considered);
    1246           1 :   xjson_AddNumberToObject (result, "no_user_id", imp->no_user_id);
    1247           1 :   xjson_AddNumberToObject (result, "imported", imp->imported);
    1248           1 :   xjson_AddNumberToObject (result, "imported_rsa", imp->imported_rsa);
    1249           1 :   xjson_AddNumberToObject (result, "unchanged", imp->unchanged);
    1250           1 :   xjson_AddNumberToObject (result, "new_user_ids", imp->new_user_ids);
    1251           1 :   xjson_AddNumberToObject (result, "new_sub_keys", imp->new_sub_keys);
    1252           1 :   xjson_AddNumberToObject (result, "new_signatures", imp->new_signatures);
    1253           1 :   xjson_AddNumberToObject (result, "new_revocations", imp->new_revocations);
    1254           1 :   xjson_AddNumberToObject (result, "secret_read", imp->secret_read);
    1255           1 :   xjson_AddNumberToObject (result, "secret_imported", imp->secret_imported);
    1256           1 :   xjson_AddNumberToObject (result, "secret_unchanged", imp->secret_unchanged);
    1257           1 :   xjson_AddNumberToObject (result, "skipped_new_keys", imp->skipped_new_keys);
    1258           1 :   xjson_AddNumberToObject (result, "not_imported", imp->not_imported);
    1259           1 :   xjson_AddNumberToObject (result, "skipped_v3_keys", imp->skipped_v3_keys);
    1260             : 
    1261             : 
    1262           1 :   if (imp->imports)
    1263             :     {
    1264           1 :       cjson_t array = xjson_CreateArray ();
    1265             :       gpgme_import_status_t status;
    1266             : 
    1267           2 :       for (status = imp->imports; status; status = status->next)
    1268           1 :         cJSON_AddItemToArray (array, import_status_to_json (status));
    1269           1 :       xjson_AddItemToObject (result, "imports", array);
    1270             :     }
    1271             : 
    1272           1 :   return result;
    1273             : }
    1274             : 
    1275             : 
    1276             : /* Create a JSON object from a gpgconf arg */
    1277             : static cjson_t
    1278           3 : conf_arg_to_json (gpgme_conf_arg_t arg, gpgme_conf_type_t type)
    1279             : {
    1280           3 :   cjson_t result = xjson_CreateObject ();
    1281           3 :   int is_none = 0;
    1282           3 :   switch (type)
    1283             :     {
    1284             :       case GPGME_CONF_STRING:
    1285             :       case GPGME_CONF_PATHNAME:
    1286             :       case GPGME_CONF_LDAP_SERVER:
    1287             :       case GPGME_CONF_KEY_FPR:
    1288             :       case GPGME_CONF_PUB_KEY:
    1289             :       case GPGME_CONF_SEC_KEY:
    1290             :       case GPGME_CONF_ALIAS_LIST:
    1291           3 :         xjson_AddStringToObject0 (result, "string", arg->value.string);
    1292           3 :         break;
    1293             : 
    1294             :       case GPGME_CONF_UINT32:
    1295           0 :         xjson_AddNumberToObject (result, "number", arg->value.uint32);
    1296           0 :         break;
    1297             : 
    1298             :       case GPGME_CONF_INT32:
    1299           0 :         xjson_AddNumberToObject (result, "number", arg->value.int32);
    1300           0 :         break;
    1301             : 
    1302             :       case GPGME_CONF_NONE:
    1303             :       default:
    1304           0 :         is_none = 1;
    1305           0 :         break;
    1306             :     }
    1307           3 :   xjson_AddBoolToObject (result, "is_none", is_none);
    1308           3 :   return result;
    1309             : }
    1310             : 
    1311             : 
    1312             : /* Create a JSON object from a gpgconf option */
    1313             : static cjson_t
    1314          23 : conf_opt_to_json (gpgme_conf_opt_t opt)
    1315             : {
    1316          23 :   cjson_t result = xjson_CreateObject ();
    1317             : 
    1318          23 :   xjson_AddStringToObject0 (result, "name", opt->name);
    1319          23 :   xjson_AddStringToObject0 (result, "description", opt->description);
    1320          23 :   xjson_AddStringToObject0 (result, "argname", opt->argname);
    1321          23 :   xjson_AddStringToObject0 (result, "default_description",
    1322          23 :                             opt->default_description);
    1323          23 :   xjson_AddStringToObject0 (result, "no_arg_description",
    1324          23 :                             opt->no_arg_description);
    1325             : 
    1326          23 :   xjson_AddNumberToObject (result, "flags", opt->flags);
    1327          23 :   xjson_AddNumberToObject (result, "level", opt->level);
    1328          23 :   xjson_AddNumberToObject (result, "type", opt->type);
    1329          23 :   xjson_AddNumberToObject (result, "alt_type", opt->alt_type);
    1330             : 
    1331          23 :   if (opt->default_value)
    1332             :     {
    1333           3 :       cjson_t array = xjson_CreateArray ();
    1334             :       gpgme_conf_arg_t arg;
    1335             : 
    1336           6 :       for (arg = opt->default_value; arg; arg = arg->next)
    1337           3 :         cJSON_AddItemToArray (array, conf_arg_to_json (arg, opt->alt_type));
    1338           3 :       xjson_AddItemToObject (result, "default_value", array);
    1339             :     }
    1340             : 
    1341          23 :   if (opt->no_arg_value)
    1342             :     {
    1343           0 :       cjson_t array = xjson_CreateArray ();
    1344             :       gpgme_conf_arg_t arg;
    1345             : 
    1346           0 :       for (arg = opt->no_arg_value; arg; arg = arg->next)
    1347           0 :         cJSON_AddItemToArray (array, conf_arg_to_json (arg, opt->alt_type));
    1348           0 :       xjson_AddItemToObject (result, "no_arg_value", array);
    1349             :     }
    1350             : 
    1351          23 :   if (opt->value)
    1352             :     {
    1353           0 :       cjson_t array = xjson_CreateArray ();
    1354             :       gpgme_conf_arg_t arg;
    1355             : 
    1356           0 :       for (arg = opt->value; arg; arg = arg->next)
    1357           0 :         cJSON_AddItemToArray (array, conf_arg_to_json (arg, opt->alt_type));
    1358           0 :       xjson_AddItemToObject (result, "value", array);
    1359             :     }
    1360          23 :   return result;
    1361             : }
    1362             : 
    1363             : 
    1364             : /* Create a JSON object from a gpgconf component*/
    1365             : static cjson_t
    1366           1 : conf_comp_to_json (gpgme_conf_comp_t cmp)
    1367             : {
    1368           1 :   cjson_t result = xjson_CreateObject ();
    1369             : 
    1370           1 :   xjson_AddStringToObject0 (result, "name", cmp->name);
    1371           1 :   xjson_AddStringToObject0 (result, "description", cmp->description);
    1372           1 :   xjson_AddStringToObject0 (result, "program_name", cmp->program_name);
    1373             : 
    1374             : 
    1375           1 :   if (cmp->options)
    1376             :     {
    1377           1 :       cjson_t array = xjson_CreateArray ();
    1378             :       gpgme_conf_opt_t opt;
    1379             : 
    1380          23 :       for (opt = cmp->options; opt; opt = opt->next)
    1381          22 :         cJSON_AddItemToArray (array, conf_opt_to_json (opt));
    1382           1 :       xjson_AddItemToObject (result, "options", array);
    1383             :     }
    1384             : 
    1385           1 :   return result;
    1386             : }
    1387             : 
    1388             : 
    1389             : /* Create a gpgme_data from json string data named "name"
    1390             :  * in the request. Takes the base64 option into account.
    1391             :  *
    1392             :  * Adds an error to the "result" on error. */
    1393             : static gpg_error_t
    1394          10 : get_string_data (cjson_t request, cjson_t result, const char *name,
    1395             :                  gpgme_data_t *r_data)
    1396             : {
    1397             :   gpgme_error_t err;
    1398             :   int opt_base64;
    1399             :   cjson_t j_data;
    1400             : 
    1401          10 :   if ((err = get_boolean_flag (request, "base64", 0, &opt_base64)))
    1402           0 :     return err;
    1403             : 
    1404             :   /* Get the data.  Note that INPUT is a shallow data object with the
    1405             :    * storage hold in REQUEST.  */
    1406          10 :   j_data = cJSON_GetObjectItem (request, name);
    1407          10 :   if (!j_data)
    1408             :     {
    1409           1 :       return gpg_error (GPG_ERR_NO_DATA);
    1410             :     }
    1411           9 :   if (!cjson_is_string (j_data))
    1412             :     {
    1413           0 :       return gpg_error (GPG_ERR_INV_VALUE);
    1414             :     }
    1415           9 :   if (opt_base64)
    1416             :     {
    1417           2 :       err = data_from_base64_string (r_data, j_data);
    1418           2 :       if (err)
    1419             :         {
    1420           0 :           gpg_error_object (result, err,
    1421             :                             "Error decoding Base-64 encoded '%s': %s",
    1422             :                             name, gpg_strerror (err));
    1423           0 :           return err;
    1424             :         }
    1425             :     }
    1426             :   else
    1427             :     {
    1428           7 :       err = gpgme_data_new_from_mem (r_data, j_data->valuestring,
    1429           7 :                                      strlen (j_data->valuestring), 0);
    1430           7 :       if (err)
    1431             :         {
    1432           0 :           gpg_error_object (result, err, "Error getting '%s': %s",
    1433             :                             name, gpg_strerror (err));
    1434           0 :           return err;
    1435             :         }
    1436             :     }
    1437           9 :   return 0;
    1438             : }
    1439             : 
    1440             : 
    1441             : /* Create a "data" object and the "type" and "base64" flags
    1442             :  * from DATA and append them to RESULT.  Ownership of DATA is
    1443             :  * transferred to this function.  TYPE must be a fixed string.
    1444             :  * If BASE64 is -1 the need for base64 encoding is determined
    1445             :  * by the content of DATA, all other values are taken as true
    1446             :  * or false. */
    1447             : static gpg_error_t
    1448           8 : make_data_object (cjson_t result, gpgme_data_t data,
    1449             :                   const char *type, int base64)
    1450             : {
    1451             :   gpg_error_t err;
    1452             :   char *buffer;
    1453             :   const char *s;
    1454             :   size_t buflen, n;
    1455             : 
    1456           8 :   if (!base64 || base64 == -1) /* Make sure that we really have a string.  */
    1457           7 :     gpgme_data_write (data, "", 1);
    1458             : 
    1459           8 :   buffer = gpgme_data_release_and_get_mem (data, &buflen);
    1460           8 :   data = NULL;
    1461           8 :   if (!buffer)
    1462             :     {
    1463           0 :       err = gpg_error_from_syserror ();
    1464           0 :       goto leave;
    1465             :     }
    1466             : 
    1467           8 :   if (base64 == -1)
    1468             :     {
    1469           3 :       base64 = 0;
    1470           3 :       if (!buflen)
    1471           0 :         log_fatal ("Appended Nul byte got lost\n");
    1472             :       /* Figure out if there is any Nul octet in the buffer.  In that
    1473             :        * case we need to Base-64 the buffer.  Due to problems with the
    1474             :        * browser's Javascript we use Base-64 also in case an UTF-8
    1475             :        * character is in the buffer.  This is because the chunking may
    1476             :        * split an UTF-8 characters and JS can't handle this.  */
    1477          32 :       for (s=buffer, n=0; n < buflen -1; s++, n++)
    1478          29 :         if (!*s || (*s & 0x80))
    1479             :           {
    1480           0 :             buflen--; /* Adjust for the extra nul byte.  */
    1481           0 :             base64 = 1;
    1482           0 :             break;
    1483             :           }
    1484             :     }
    1485             : 
    1486           8 :   xjson_AddStringToObject (result, "type", type);
    1487           8 :   xjson_AddBoolToObject (result, "base64", base64);
    1488             : 
    1489           8 :   if (base64)
    1490           1 :     err = add_base64_to_object (result, "data", buffer, buflen);
    1491             :   else
    1492           7 :     err = cjson_AddStringToObject (result, "data", buffer);
    1493             : 
    1494             :  leave:
    1495           8 :   gpgme_free (buffer);
    1496           8 :   return err;
    1497             : }
    1498             : 
    1499             : 
    1500             : /* Encode and chunk response.
    1501             :  *
    1502             :  * If necessary this base64 encodes and chunks the response
    1503             :  * for getmore so that we always return valid json independent
    1504             :  * of the chunksize.
    1505             :  *
    1506             :  * A chunked response contains the base64 encoded chunk
    1507             :  * as a string and a boolean if there is still more data
    1508             :  * available for getmore like:
    1509             :  * {
    1510             :  *   chunk: "SGVsbG8gV29ybGQK"
    1511             :  *   more: true
    1512             :  * }
    1513             :  *
    1514             :  * Chunking is only done if the response is larger then the
    1515             :  * chunksize.
    1516             :  *
    1517             :  * caller has to xfree the return value.
    1518             :  */
    1519             : static char *
    1520          18 : encode_and_chunk (cjson_t request, cjson_t response)
    1521             : {
    1522             :   char *data;
    1523          18 :   gpg_error_t err = 0;
    1524          18 :   size_t chunksize = 0;
    1525          18 :   char *getmore_request = NULL;
    1526             : 
    1527          18 :   if (opt_interactive)
    1528           0 :     data = cJSON_Print (response);
    1529             :   else
    1530          18 :     data = cJSON_PrintUnformatted (response);
    1531             : 
    1532          18 :   if (!data)
    1533             :     {
    1534           0 :       err = GPG_ERR_NO_DATA;
    1535           0 :       goto leave;
    1536             :     }
    1537             : 
    1538          18 :   if (!request)
    1539             :     {
    1540           0 :       goto leave;
    1541             :     }
    1542             : 
    1543          18 :   if ((err = get_chunksize (request, &chunksize)))
    1544             :     {
    1545           0 :       err = GPG_ERR_INV_VALUE;
    1546           0 :       goto leave;
    1547             :     }
    1548             : 
    1549          18 :   if (!chunksize)
    1550          17 :     goto leave;
    1551             : 
    1552           1 :   pending_data.buffer = data;
    1553             :   /* Data should already be encoded so that it does not
    1554             :      contain 0.*/
    1555           1 :   pending_data.length = strlen (data);
    1556           1 :   pending_data.written = 0;
    1557             : 
    1558           1 :   if (gpgrt_asprintf (&getmore_request,
    1559             :                   "{ \"op\":\"getmore\", \"chunksize\": %i }",
    1560             :                   (int) chunksize) == -1)
    1561             :     {
    1562           0 :       err = gpg_error_from_syserror ();
    1563           0 :       goto leave;
    1564             :     }
    1565             : 
    1566           1 :   data = process_request (getmore_request);
    1567             : 
    1568             : leave:
    1569          18 :   xfree (getmore_request);
    1570             : 
    1571          18 :   if (!err && !data)
    1572             :     {
    1573           0 :       err = GPG_ERR_GENERAL;
    1574             :     }
    1575             : 
    1576          18 :   if (err)
    1577             :     {
    1578           0 :       cjson_t err_obj = gpg_error_object (NULL, err,
    1579             :                                           "Encode and chunk failed: %s",
    1580             :                                           gpgme_strerror (err));
    1581           0 :       xfree (data);
    1582           0 :       if (opt_interactive)
    1583           0 :         data = cJSON_Print (err_obj);
    1584           0 :       data = cJSON_PrintUnformatted (err_obj);
    1585             : 
    1586           0 :       cJSON_Delete (err_obj);
    1587             :     }
    1588             : 
    1589          18 :   return data;
    1590             : }
    1591             : 
    1592             : 
    1593             : 
    1594             : /*
    1595             :  * Implementation of the commands.
    1596             :  */
    1597             : static const char hlp_encrypt[] =
    1598             :   "op:     \"encrypt\"\n"
    1599             :   "keys:   Array of strings with the fingerprints or user-ids\n"
    1600             :   "        of the keys to encrypt the data.  For a single key\n"
    1601             :   "        a String may be used instead of an array.\n"
    1602             :   "data:   Input data. \n"
    1603             :   "\n"
    1604             :   "Optional parameters:\n"
    1605             :   "protocol:      Either \"openpgp\" (default) or \"cms\".\n"
    1606             :   "signing_keys:  Similar to the keys parameter for added signing.\n"
    1607             :   "               (openpgp only)"
    1608             :   "file_name:     The file name associated with the data.\n"
    1609             :   "sender:        Sender info to embed in a signature.\n"
    1610             :   "\n"
    1611             :   "Optional boolean flags (default is false):\n"
    1612             :   "base64:        Input data is base64 encoded.\n"
    1613             :   "mime:          Indicate that data is a MIME object.\n"
    1614             :   "armor:         Request output in armored format.\n"
    1615             :   "always-trust:  Request --always-trust option.\n"
    1616             :   "no-encrypt-to: Do not use a default recipient.\n"
    1617             :   "no-compress:   Do not compress the plaintext first.\n"
    1618             :   "throw-keyids:  Request the --throw-keyids option.\n"
    1619             :   "want-address:  Require that the keys include a mail address.\n"
    1620             :   "wrap:          Assume the input is an OpenPGP message.\n"
    1621             :   "\n"
    1622             :   "Response on success:\n"
    1623             :   "type:   \"ciphertext\"\n"
    1624             :   "data:   Unless armor mode is used a Base64 encoded binary\n"
    1625             :   "        ciphertext.  In armor mode a string with an armored\n"
    1626             :   "        OpenPGP or a PEM message.\n"
    1627             :   "base64: Boolean indicating whether data is base64 encoded.";
    1628             : static gpg_error_t
    1629           2 : op_encrypt (cjson_t request, cjson_t result)
    1630             : {
    1631             :   gpg_error_t err;
    1632           2 :   gpgme_ctx_t ctx = NULL;
    1633             :   gpgme_protocol_t protocol;
    1634           2 :   char **signing_patterns = NULL;
    1635             :   int opt_mime;
    1636           2 :   char *keystring = NULL;
    1637           2 :   char *file_name = NULL;
    1638           2 :   gpgme_data_t input = NULL;
    1639           2 :   gpgme_data_t output = NULL;
    1640             :   int abool;
    1641           2 :   gpgme_encrypt_flags_t encrypt_flags = 0;
    1642           2 :   gpgme_ctx_t keylist_ctx = NULL;
    1643           2 :   gpgme_key_t key = NULL;
    1644           2 :   cjson_t j_tmp = NULL;
    1645             : 
    1646           2 :   if ((err = get_protocol (request, &protocol)))
    1647           0 :     goto leave;
    1648           2 :   ctx = get_context (protocol);
    1649             : 
    1650           2 :   if ((err = get_boolean_flag (request, "mime", 0, &opt_mime)))
    1651           0 :     goto leave;
    1652             : 
    1653           2 :   if ((err = get_boolean_flag (request, "armor", 0, &abool)))
    1654           0 :     goto leave;
    1655           2 :   gpgme_set_armor (ctx, abool);
    1656           2 :   if ((err = get_boolean_flag (request, "always-trust", 0, &abool)))
    1657           0 :     goto leave;
    1658           2 :   if (abool)
    1659           2 :     encrypt_flags |= GPGME_ENCRYPT_ALWAYS_TRUST;
    1660           2 :   if ((err = get_boolean_flag (request, "no-encrypt-to", 0,&abool)))
    1661           0 :     goto leave;
    1662           2 :   if (abool)
    1663           0 :     encrypt_flags |= GPGME_ENCRYPT_NO_ENCRYPT_TO;
    1664           2 :   if ((err = get_boolean_flag (request, "no-compress", 0, &abool)))
    1665           0 :     goto leave;
    1666           2 :   if (abool)
    1667           0 :     encrypt_flags |= GPGME_ENCRYPT_NO_COMPRESS;
    1668           2 :   if ((err = get_boolean_flag (request, "throw-keyids", 0, &abool)))
    1669           0 :     goto leave;
    1670           2 :   if (abool)
    1671           0 :     encrypt_flags |= GPGME_ENCRYPT_THROW_KEYIDS;
    1672           2 :   if ((err = get_boolean_flag (request, "wrap", 0, &abool)))
    1673           0 :     goto leave;
    1674           2 :   if (abool)
    1675           0 :     encrypt_flags |= GPGME_ENCRYPT_WRAP;
    1676           2 :   if ((err = get_boolean_flag (request, "want-address", 0, &abool)))
    1677           0 :     goto leave;
    1678           2 :   if (abool)
    1679           0 :     encrypt_flags |= GPGME_ENCRYPT_WANT_ADDRESS;
    1680             : 
    1681           2 :   j_tmp = cJSON_GetObjectItem (request, "file_name");
    1682           2 :   if (j_tmp && cjson_is_string (j_tmp))
    1683             :     {
    1684           1 :       file_name = j_tmp->valuestring;
    1685             :     }
    1686             : 
    1687           2 :   j_tmp = cJSON_GetObjectItem (request, "sender");
    1688           2 :   if (j_tmp && cjson_is_string (j_tmp))
    1689             :     {
    1690           0 :       gpgme_set_sender (ctx, j_tmp->valuestring);
    1691             :     }
    1692             : 
    1693             :   /* Get the keys.  */
    1694           2 :   err = get_keys (request, "keys", &keystring);
    1695           2 :   if (err)
    1696             :     {
    1697             :       /* Provide a custom error response.  */
    1698           0 :       gpg_error_object (result, err, "Error getting keys: %s",
    1699             :                         gpg_strerror (err));
    1700           0 :       goto leave;
    1701             :     }
    1702             : 
    1703             :   /* Do we have signing keys ? */
    1704           2 :   signing_patterns = create_keylist_patterns (request, "signing_keys");
    1705           2 :   if (signing_patterns)
    1706             :     {
    1707           1 :       keylist_ctx = create_onetime_context (protocol);
    1708           1 :       gpgme_set_keylist_mode (keylist_ctx, GPGME_KEYLIST_MODE_LOCAL);
    1709             : 
    1710           1 :       err = gpgme_op_keylist_ext_start (keylist_ctx,
    1711             :                                         (const char **) signing_patterns,
    1712             :                                         1, 0);
    1713           1 :       if (err)
    1714             :         {
    1715           0 :           gpg_error_object (result, err, "Error listing keys: %s",
    1716             :                             gpg_strerror (err));
    1717           0 :           goto leave;
    1718             :         }
    1719           3 :       while (!(err = gpgme_op_keylist_next (keylist_ctx, &key)))
    1720             :         {
    1721           1 :           if ((err = gpgme_signers_add (ctx, key)))
    1722             :             {
    1723           0 :               gpg_error_object (result, err, "Error adding signer: %s",
    1724             :                                 gpg_strerror (err));
    1725           0 :               goto leave;
    1726             :             }
    1727           1 :           gpgme_key_unref (key);
    1728           1 :           key = NULL;
    1729             :         }
    1730           1 :       release_onetime_context (keylist_ctx);
    1731           1 :       keylist_ctx = NULL;
    1732             :     }
    1733             : 
    1734           2 :   if ((err = get_string_data (request, result, "data", &input)))
    1735           0 :       goto leave;
    1736             : 
    1737           2 :   if (opt_mime)
    1738           0 :     gpgme_data_set_encoding (input, GPGME_DATA_ENCODING_MIME);
    1739             : 
    1740           2 :   if (file_name)
    1741             :     {
    1742           1 :       gpgme_data_set_file_name (input, file_name);
    1743             :     }
    1744             : 
    1745             :   /* Create an output data object.  */
    1746           2 :   err = gpgme_data_new (&output);
    1747           2 :   if (err)
    1748             :     {
    1749           0 :       gpg_error_object (result, err, "Error creating output data object: %s",
    1750             :                         gpg_strerror (err));
    1751           0 :       goto leave;
    1752             :     }
    1753             : 
    1754             :   /* Encrypt.  */
    1755           2 :   if (!signing_patterns)
    1756             :     {
    1757           1 :       err = gpgme_op_encrypt_ext (ctx, NULL, keystring, encrypt_flags,
    1758             :                                   input, output);
    1759             :     }
    1760             :   else
    1761             :     {
    1762           1 :       err = gpgme_op_encrypt_sign_ext (ctx, NULL, keystring, encrypt_flags,
    1763             :                                        input, output);
    1764             : 
    1765             :     }
    1766             :   /* encrypt_result = gpgme_op_encrypt_result (ctx); */
    1767           2 :   if (err)
    1768             :     {
    1769           0 :       gpg_error_object (result, err, "Encryption failed: %s",
    1770             :                         gpg_strerror (err));
    1771           0 :       goto leave;
    1772             :     }
    1773           2 :   gpgme_data_release (input);
    1774           2 :   input = NULL;
    1775             : 
    1776             :   /* We need to base64 if armoring has not been requested.  */
    1777           2 :   err = make_data_object (result, output,
    1778           2 :                           "ciphertext", !gpgme_get_armor (ctx));
    1779           2 :   output = NULL;
    1780             : 
    1781             :  leave:
    1782           2 :   xfree_array (signing_patterns);
    1783           2 :   xfree (keystring);
    1784           2 :   release_onetime_context (keylist_ctx);
    1785             :   /* Reset sender in case the context is reused */
    1786           2 :   gpgme_set_sender (ctx, NULL);
    1787           2 :   gpgme_key_unref (key);
    1788           2 :   gpgme_signers_clear (ctx);
    1789           2 :   release_context (ctx);
    1790           2 :   gpgme_data_release (input);
    1791           2 :   gpgme_data_release (output);
    1792           2 :   return err;
    1793             : }
    1794             : 
    1795             : 
    1796             : 
    1797             : static const char hlp_decrypt[] =
    1798             :   "op:     \"decrypt\"\n"
    1799             :   "data:   The encrypted data.\n"
    1800             :   "\n"
    1801             :   "Optional parameters:\n"
    1802             :   "protocol:      Either \"openpgp\" (default) or \"cms\".\n"
    1803             :   "\n"
    1804             :   "Optional boolean flags (default is false):\n"
    1805             :   "base64:        Input data is base64 encoded.\n"
    1806             :   "\n"
    1807             :   "Response on success:\n"
    1808             :   "type:     \"plaintext\"\n"
    1809             :   "data:     The decrypted data.  This may be base64 encoded.\n"
    1810             :   "base64:   Boolean indicating whether data is base64 encoded.\n"
    1811             :   "mime:     deprecated - use dec_info is_mime instead\n"
    1812             :   "dec_info: An object with decryption information. (gpgme_decrypt_result_t)\n"
    1813             :   " Boolean values:\n"
    1814             :   "  wrong_key_usage:     Key should not have been used for encryption.\n"
    1815             :   "  is_de_vs:            Message was encrypted in compliance to the de-vs\n"
    1816             :   "                       mode.\n"
    1817             :   "  is_mime:             Message claims that the content is a MIME Message.\n"
    1818             :   "  legacy_cipher_nomdc: The message was made by a legacy algorithm\n"
    1819             :   "                       without integrity protection.\n"
    1820             :   " String values:\n"
    1821             :   "  file_name:   The filename contained in the decrypt result.\n"
    1822             :   "  symkey_algo: A string with the symmetric encryption algorithm and\n"
    1823             :   "               mode using the format \"<algo>.<mode>\".\n"
    1824             :   " Array values:\n"
    1825             :   "  recipients:  The list of recipients (gpgme_recipient_t).\n"
    1826             :   "   String values:\n"
    1827             :   "    keyid:            The keyid of the recipient.\n"
    1828             :   "    pubkey_algo_name: gpgme_pubkey_algo_name of used algo.\n"
    1829             :   "    status_string:    The status code as localized gpg-error string\n"
    1830             :   "   Number values:\n"
    1831             :   "    status_code:      The status as a number. (gpg_error_t)\n"
    1832             :   "info:     Optional an object with verification information.\n"
    1833             :   "          (gpgme_verify_result_t)\n"
    1834             :   " file_name: The filename contained in the verify result.\n"
    1835             :   " is_mime:   The is_mime info contained in the verify result.\n"
    1836             :   " signatures: Array of signatures\n"
    1837             :   "  summary: Object containing summary information.\n"
    1838             :   "   Boolean values: (Check gpgme_sigsum_t doc for meaning)\n"
    1839             :   "    valid\n"
    1840             :   "    green\n"
    1841             :   "    red\n"
    1842             :   "    revoked\n"
    1843             :   "    key-expired\n"
    1844             :   "    sig-expired\n"
    1845             :   "    key-missing\n"
    1846             :   "    crl-missing\n"
    1847             :   "    crl-too-old\n"
    1848             :   "    bad-policy\n"
    1849             :   "    sys-error\n"
    1850             :   "   sigsum: Array of strings representing the sigsum.\n"
    1851             :   "  Boolean values:\n"
    1852             :   "   wrong_key_usage: Key should not have been used for signing.\n"
    1853             :   "   chain_model:     Validity has been verified using the chain model.\n"
    1854             :   "   is_de_vs:        signature is in compliance to the de-vs mode.\n"
    1855             :   "  String values:\n"
    1856             :   "   status_string:      The status code as localized gpg-error string\n"
    1857             :   "   fingerprint:        The fingerprint of the signing key.\n"
    1858             :   "   validity_string:    The validity as string.\n"
    1859             :   "   pubkey_algo_name:   gpgme_pubkey_algo_name of used algo.\n"
    1860             :   "   hash_algo_name:     gpgme_hash_algo_name of used hash algo\n"
    1861             :   "   pka_address:        The mailbox from the PKA information.\n"
    1862             :   "  Number values:\n"
    1863             :   "   status_code:     The status as a number. (gpg_error_t)\n"
    1864             :   "   timestamp:       Signature creation time. (secs since epoch)\n"
    1865             :   "   exp_timestamp:   Signature expiration or 0. (secs since epoch)\n"
    1866             :   "   pka_trust: PKA status: 0 = not available, 1 = bad, 2 = okay, 3 = RFU.\n"
    1867             :   "   validity: validity as number (gpgme_validity_t)\n"
    1868             :   "   validity_reason: (gpg_error_t)\n"
    1869             :   "  Array values:\n"
    1870             :   "   notations: Notation data and policy urls (gpgme_sig_notation_t)\n"
    1871             :   "    Boolean values:\n"
    1872             :   "     human_readable\n"
    1873             :   "     critical\n"
    1874             :   "    String values:\n"
    1875             :   "     name\n"
    1876             :   "     value\n"
    1877             :   "    Number values:\n"
    1878             :   "     flags\n";
    1879             : static gpg_error_t
    1880           2 : op_decrypt (cjson_t request, cjson_t result)
    1881             : {
    1882             :   gpg_error_t err;
    1883           2 :   gpgme_ctx_t ctx = NULL;
    1884             :   gpgme_protocol_t protocol;
    1885           2 :   gpgme_data_t input = NULL;
    1886           2 :   gpgme_data_t output = NULL;
    1887             :   gpgme_decrypt_result_t decrypt_result;
    1888             :   gpgme_verify_result_t verify_result;
    1889             : 
    1890           2 :   if ((err = get_protocol (request, &protocol)))
    1891           0 :     goto leave;
    1892           2 :   ctx = get_context (protocol);
    1893             : 
    1894           2 :   if ((err = get_string_data (request, result, "data", &input)))
    1895           0 :       goto leave;
    1896             : 
    1897             :   /* Create an output data object.  */
    1898           2 :   err = gpgme_data_new (&output);
    1899           2 :   if (err)
    1900             :     {
    1901           0 :       gpg_error_object (result, err,
    1902             :                         "Error creating output data object: %s",
    1903             :                         gpg_strerror (err));
    1904           0 :       goto leave;
    1905             :     }
    1906             : 
    1907             :   /* Decrypt.  */
    1908           2 :   err = gpgme_op_decrypt_ext (ctx, GPGME_DECRYPT_VERIFY,
    1909             :                               input, output);
    1910           2 :   decrypt_result = gpgme_op_decrypt_result (ctx);
    1911           2 :   if (err)
    1912             :     {
    1913           0 :       gpg_error_object (result, err, "Decryption failed: %s",
    1914             :                         gpg_strerror (err));
    1915           0 :       goto leave;
    1916             :     }
    1917           2 :   gpgme_data_release (input);
    1918           2 :   input = NULL;
    1919             : 
    1920           2 :   if (decrypt_result->is_mime)
    1921           0 :     xjson_AddBoolToObject (result, "mime", 1);
    1922             : 
    1923           2 :   xjson_AddItemToObject (result, "dec_info",
    1924             :                          decrypt_result_to_json (decrypt_result));
    1925             : 
    1926           2 :   verify_result = gpgme_op_verify_result (ctx);
    1927           2 :   if (verify_result && verify_result->signatures)
    1928             :     {
    1929           1 :       xjson_AddItemToObject (result, "info",
    1930             :                              verify_result_to_json (verify_result));
    1931             :     }
    1932             : 
    1933           2 :   err = make_data_object (result, output, "plaintext", -1);
    1934           2 :   output = NULL;
    1935             : 
    1936           2 :   if (err)
    1937             :     {
    1938           0 :       gpg_error_object (result, err, "Plaintext output failed: %s",
    1939             :                         gpg_strerror (err));
    1940           0 :       goto leave;
    1941             :     }
    1942             : 
    1943             :  leave:
    1944           2 :   release_context (ctx);
    1945           2 :   gpgme_data_release (input);
    1946           2 :   gpgme_data_release (output);
    1947           2 :   return err;
    1948             : }
    1949             : 
    1950             : 
    1951             : 
    1952             : static const char hlp_sign[] =
    1953             :   "op:     \"sign\"\n"
    1954             :   "keys:   Array of strings with the fingerprints of the signing key.\n"
    1955             :   "        For a single key a String may be used instead of an array.\n"
    1956             :   "data:   Input data. \n"
    1957             :   "\n"
    1958             :   "Optional parameters:\n"
    1959             :   "protocol:      Either \"openpgp\" (default) or \"cms\".\n"
    1960             :   "sender:        The mail address of the sender.\n"
    1961             :   "mode:          A string with the signing mode can be:\n"
    1962             :   "               detached (default)\n"
    1963             :   "               opaque\n"
    1964             :   "               clearsign\n"
    1965             :   "\n"
    1966             :   "Optional boolean flags (default is false):\n"
    1967             :   "base64:        Input data is base64 encoded.\n"
    1968             :   "armor:         Request output in armored format.\n"
    1969             :   "\n"
    1970             :   "Response on success:\n"
    1971             :   "type:   \"signature\"\n"
    1972             :   "data:   Unless armor mode is used a Base64 encoded binary\n"
    1973             :   "        signature.  In armor mode a string with an armored\n"
    1974             :   "        OpenPGP or a PEM message.\n"
    1975             :   "base64: Boolean indicating whether data is base64 encoded.\n";
    1976             : static gpg_error_t
    1977           1 : op_sign (cjson_t request, cjson_t result)
    1978             : {
    1979             :   gpg_error_t err;
    1980           1 :   gpgme_ctx_t ctx = NULL;
    1981             :   gpgme_protocol_t protocol;
    1982           1 :   char **patterns = NULL;
    1983           1 :   gpgme_data_t input = NULL;
    1984           1 :   gpgme_data_t output = NULL;
    1985             :   int abool;
    1986             :   cjson_t j_tmp;
    1987           1 :   gpgme_sig_mode_t mode = GPGME_SIG_MODE_DETACH;
    1988           1 :   gpgme_ctx_t keylist_ctx = NULL;
    1989           1 :   gpgme_key_t key = NULL;
    1990             : 
    1991           1 :   if ((err = get_protocol (request, &protocol)))
    1992           0 :     goto leave;
    1993           1 :   ctx = get_context (protocol);
    1994             : 
    1995           1 :   if ((err = get_boolean_flag (request, "armor", 0, &abool)))
    1996           0 :     goto leave;
    1997           1 :   gpgme_set_armor (ctx, abool);
    1998             : 
    1999           1 :   j_tmp = cJSON_GetObjectItem (request, "mode");
    2000           1 :   if (j_tmp && cjson_is_string (j_tmp))
    2001             :     {
    2002           0 :       if (!strcmp (j_tmp->valuestring, "opaque"))
    2003             :         {
    2004           0 :           mode = GPGME_SIG_MODE_NORMAL;
    2005             :         }
    2006           0 :       else if (!strcmp (j_tmp->valuestring, "clearsign"))
    2007             :         {
    2008           0 :           mode = GPGME_SIG_MODE_CLEAR;
    2009             :         }
    2010             :     }
    2011             : 
    2012           1 :   j_tmp = cJSON_GetObjectItem (request, "sender");
    2013           1 :   if (j_tmp && cjson_is_string (j_tmp))
    2014             :     {
    2015           1 :       gpgme_set_sender (ctx, j_tmp->valuestring);
    2016             :     }
    2017             : 
    2018           1 :   patterns = create_keylist_patterns (request, "keys");
    2019           1 :   if (!patterns)
    2020             :     {
    2021           0 :       gpg_error_object (result, err, "Error getting keys: %s",
    2022             :                         gpg_strerror (gpg_error (GPG_ERR_NO_KEY)));
    2023           0 :       goto leave;
    2024             :     }
    2025             : 
    2026             :   /* Do a keylisting and add the keys */
    2027           1 :   keylist_ctx = create_onetime_context (protocol);
    2028           1 :   gpgme_set_keylist_mode (keylist_ctx, GPGME_KEYLIST_MODE_LOCAL);
    2029             : 
    2030           1 :   err = gpgme_op_keylist_ext_start (keylist_ctx,
    2031             :                                     (const char **) patterns, 1, 0);
    2032           1 :   if (err)
    2033             :     {
    2034           0 :       gpg_error_object (result, err, "Error listing keys: %s",
    2035             :                         gpg_strerror (err));
    2036           0 :       goto leave;
    2037             :     }
    2038           3 :   while (!(err = gpgme_op_keylist_next (keylist_ctx, &key)))
    2039             :     {
    2040           1 :       if ((err = gpgme_signers_add (ctx, key)))
    2041             :         {
    2042           0 :           gpg_error_object (result, err, "Error adding signer: %s",
    2043             :                             gpg_strerror (err));
    2044           0 :           goto leave;
    2045             :         }
    2046           1 :       gpgme_key_unref (key);
    2047           1 :       key = NULL;
    2048             :     }
    2049             : 
    2050           1 :   if ((err = get_string_data (request, result, "data", &input)))
    2051           0 :     goto leave;
    2052             : 
    2053             :   /* Create an output data object.  */
    2054           1 :   err = gpgme_data_new (&output);
    2055           1 :   if (err)
    2056             :     {
    2057           0 :       gpg_error_object (result, err, "Error creating output data object: %s",
    2058             :                         gpg_strerror (err));
    2059           0 :       goto leave;
    2060             :     }
    2061             : 
    2062             :   /* Sign. */
    2063           1 :   err = gpgme_op_sign (ctx, input, output, mode);
    2064           1 :   if (err)
    2065             :     {
    2066           0 :       gpg_error_object (result, err, "Signing failed: %s",
    2067             :                         gpg_strerror (err));
    2068           0 :       goto leave;
    2069             :     }
    2070             : 
    2071           1 :   gpgme_data_release (input);
    2072           1 :   input = NULL;
    2073             : 
    2074             :   /* We need to base64 if armoring has not been requested.  */
    2075           1 :   err = make_data_object (result, output,
    2076           1 :                           "signature", !gpgme_get_armor (ctx));
    2077           1 :   output = NULL;
    2078             : 
    2079             :  leave:
    2080           1 :   xfree_array (patterns);
    2081           1 :   gpgme_signers_clear (ctx);
    2082           1 :   gpgme_key_unref (key);
    2083           1 :   release_onetime_context (keylist_ctx);
    2084           1 :   release_context (ctx);
    2085           1 :   gpgme_data_release (input);
    2086           1 :   gpgme_data_release (output);
    2087           1 :   return err;
    2088             : }
    2089             : 
    2090             : 
    2091             : 
    2092             : static const char hlp_verify[] =
    2093             :   "op:     \"verify\"\n"
    2094             :   "data:   The data to verify.\n"
    2095             :   "\n"
    2096             :   "Optional parameters:\n"
    2097             :   "protocol:      Either \"openpgp\" (default) or \"cms\".\n"
    2098             :   "signature:     A detached signature. If missing opaque is assumed.\n"
    2099             :   "\n"
    2100             :   "Optional boolean flags (default is false):\n"
    2101             :   "base64:        Input data is base64 encoded.\n"
    2102             :   "\n"
    2103             :   "Response on success:\n"
    2104             :   "type:   \"plaintext\"\n"
    2105             :   "data:   The verified data.  This may be base64 encoded.\n"
    2106             :   "base64: Boolean indicating whether data is base64 encoded.\n"
    2107             :   "info:   An object with verification information (gpgme_verify_result_t).\n"
    2108             :   " is_mime:    Boolean that is true if the messages claims it is MIME.\n"
    2109             :   "             Note that this flag is not covered by the signature.)\n"
    2110             :   " signatures: Array of signatures\n"
    2111             :   "  summary: Object containing summary information.\n"
    2112             :   "   Boolean values: (Check gpgme_sigsum_t doc for meaning)\n"
    2113             :   "    valid\n"
    2114             :   "    green\n"
    2115             :   "    red\n"
    2116             :   "    revoked\n"
    2117             :   "    key-expired\n"
    2118             :   "    sig-expired\n"
    2119             :   "    key-missing\n"
    2120             :   "    crl-missing\n"
    2121             :   "    crl-too-old\n"
    2122             :   "    bad-policy\n"
    2123             :   "    sys-error\n"
    2124             :   "   sigsum: Array of strings representing the sigsum.\n"
    2125             :   "  Boolean values:\n"
    2126             :   "   wrong_key_usage: Key should not have been used for signing.\n"
    2127             :   "   chain_model:     Validity has been verified using the chain model.\n"
    2128             :   "   is_de_vs:        signature is in compliance to the de-vs mode.\n"
    2129             :   "  String values:\n"
    2130             :   "   status_string:      The status code as localized gpg-error string\n"
    2131             :   "   fingerprint:        The fingerprint of the signing key.\n"
    2132             :   "   validity_string:    The validity as string.\n"
    2133             :   "   pubkey_algo_name:   gpgme_pubkey_algo_name of used algo.\n"
    2134             :   "   hash_algo_name:     gpgme_hash_algo_name of used hash algo\n"
    2135             :   "   pka_address:        The mailbox from the PKA information.\n"
    2136             :   "  Number values:\n"
    2137             :   "   status_code:     The status as a number. (gpg_error_t)\n"
    2138             :   "   timestamp:       Signature creation time. (secs since epoch)\n"
    2139             :   "   exp_timestamp:   Signature expiration or 0. (secs since epoch)\n"
    2140             :   "   pka_trust: PKA status: 0 = not available, 1 = bad, 2 = okay, 3 = RFU.\n"
    2141             :   "   validity: validity as number (gpgme_validity_t)\n"
    2142             :   "   validity_reason: (gpg_error_t)\n"
    2143             :   "  Array values:\n"
    2144             :   "   notations: Notation data and policy urls (gpgme_sig_notation_t)\n"
    2145             :   "    Boolean values:\n"
    2146             :   "     human_readable\n"
    2147             :   "     critical\n"
    2148             :   "    String values:\n"
    2149             :   "     name\n"
    2150             :   "     value\n"
    2151             :   "    Number values:\n"
    2152             :   "     flags\n";
    2153             : static gpg_error_t
    2154           2 : op_verify (cjson_t request, cjson_t result)
    2155             : {
    2156             :   gpg_error_t err;
    2157           2 :   gpgme_ctx_t ctx = NULL;
    2158             :   gpgme_protocol_t protocol;
    2159           2 :   gpgme_data_t input = NULL;
    2160           2 :   gpgme_data_t signature = NULL;
    2161           2 :   gpgme_data_t output = NULL;
    2162             :   gpgme_verify_result_t verify_result;
    2163             : 
    2164           2 :   if ((err = get_protocol (request, &protocol)))
    2165           0 :     goto leave;
    2166           2 :   ctx = get_context (protocol);
    2167             : 
    2168           2 :   if ((err = get_string_data (request, result, "data", &input)))
    2169           0 :     goto leave;
    2170             : 
    2171           2 :   err = get_string_data (request, result, "signature", &signature);
    2172             :   /* Signature data is optional otherwise we expect opaque or clearsigned. */
    2173           2 :   if (err && err != gpg_error (GPG_ERR_NO_DATA))
    2174           0 :     goto leave;
    2175             : 
    2176           2 :   if (!signature)
    2177             :     {
    2178             :       /* Verify opaque or clearsigned we need an output data object.  */
    2179           1 :       err = gpgme_data_new (&output);
    2180           1 :       if (err)
    2181             :         {
    2182           0 :           gpg_error_object (result, err,
    2183             :                             "Error creating output data object: %s",
    2184             :                             gpg_strerror (err));
    2185           0 :           goto leave;
    2186             :         }
    2187           1 :       err = gpgme_op_verify (ctx, input, 0, output);
    2188             :     }
    2189             :   else
    2190             :     {
    2191           1 :       err = gpgme_op_verify (ctx, signature, input, NULL);
    2192             :     }
    2193             : 
    2194           2 :   if (err)
    2195             :     {
    2196           0 :       gpg_error_object (result, err, "Verify failed: %s", gpg_strerror (err));
    2197           0 :       goto leave;
    2198             :     }
    2199           2 :   gpgme_data_release (input);
    2200           2 :   input = NULL;
    2201           2 :   gpgme_data_release (signature);
    2202           2 :   signature = NULL;
    2203             : 
    2204           2 :   verify_result = gpgme_op_verify_result (ctx);
    2205           2 :   if (verify_result && verify_result->signatures)
    2206             :     {
    2207           2 :       xjson_AddItemToObject (result, "info",
    2208             :                              verify_result_to_json (verify_result));
    2209             :     }
    2210             : 
    2211           2 :   if (output)
    2212             :     {
    2213           1 :       err = make_data_object (result, output, "plaintext", -1);
    2214           1 :       output = NULL;
    2215             : 
    2216           1 :       if (err)
    2217             :         {
    2218           0 :           gpg_error_object (result, err, "Plaintext output failed: %s",
    2219             :                             gpg_strerror (err));
    2220           0 :           goto leave;
    2221             :         }
    2222             :     }
    2223             : 
    2224             :  leave:
    2225           2 :   release_context (ctx);
    2226           2 :   gpgme_data_release (input);
    2227           2 :   gpgme_data_release (output);
    2228           2 :   gpgme_data_release (signature);
    2229           2 :   return err;
    2230             : }
    2231             : 
    2232             : 
    2233             : 
    2234             : static const char hlp_version[] =
    2235             :   "op:     \"version\"\n"
    2236             :   "\n"
    2237             :   "Response on success:\n"
    2238             :   "gpgme:  The GPGME Version.\n"
    2239             :   "info:   dump of engine info. containing:\n"
    2240             :   "        protocol: The protocol.\n"
    2241             :   "        fname:    The file name.\n"
    2242             :   "        version:  The version.\n"
    2243             :   "        req_ver:  The required version.\n"
    2244             :   "        homedir:  The homedir of the engine or \"default\".\n";
    2245             : static gpg_error_t
    2246           1 : op_version (cjson_t request, cjson_t result)
    2247             : {
    2248           1 :   gpg_error_t err = 0;
    2249           1 :   gpgme_engine_info_t ei = NULL;
    2250           1 :   cjson_t infos = xjson_CreateArray ();
    2251             : 
    2252             :   (void)request;
    2253             : 
    2254           1 :   if (!cJSON_AddStringToObject (result, "gpgme", gpgme_check_version (NULL)))
    2255             :     {
    2256           0 :       cJSON_Delete (infos);
    2257           0 :       return gpg_error_from_syserror ();
    2258             :     }
    2259             : 
    2260           1 :   if ((err = gpgme_get_engine_info (&ei)))
    2261             :     {
    2262           0 :       cJSON_Delete (infos);
    2263           0 :       return err;
    2264             :     }
    2265             : 
    2266           7 :   for (; ei; ei = ei->next)
    2267           6 :     cJSON_AddItemToArray (infos, engine_info_to_json (ei));
    2268             : 
    2269           1 :   if (!cJSON_AddItemToObject (result, "info", infos))
    2270             :     {
    2271           0 :       err = gpg_error_from_syserror ();
    2272           0 :       cJSON_Delete (infos);
    2273           0 :       return err;
    2274             :     }
    2275             : 
    2276           1 :   return 0;
    2277             : }
    2278             : 
    2279             : 
    2280             : 
    2281             : static const char hlp_keylist[] =
    2282             :   "op:     \"keylist\"\n"
    2283             :   "\n"
    2284             :   "Optional parameters:\n"
    2285             :   "keys:          Array of strings or fingerprints to lookup\n"
    2286             :   "               For a single key a String may be used instead of an array.\n"
    2287             :   "               default lists all keys.\n"
    2288             :   "protocol:      Either \"openpgp\" (default) or \"cms\".\n"
    2289             :   "\n"
    2290             :   "Optional boolean flags (default is false):\n"
    2291             :   "secret:        List only secret keys.\n"
    2292             :   "with-secret:   Add KEYLIST_MODE_WITH_SECRET.\n"
    2293             :   "extern:        Add KEYLIST_MODE_EXTERN.\n"
    2294             :   "local:         Add KEYLIST_MODE_LOCAL. (default mode).\n"
    2295             :   "sigs:          Add KEYLIST_MODE_SIGS.\n"
    2296             :   "notations:     Add KEYLIST_MODE_SIG_NOTATIONS.\n"
    2297             :   "tofu:          Add KEYLIST_MODE_WITH_TOFU.\n"
    2298             :   "ephemeral:     Add KEYLIST_MODE_EPHEMERAL.\n"
    2299             :   "validate:      Add KEYLIST_MODE_VALIDATE.\n"
    2300             :   "locate:        Add KEYLIST_MODE_LOCATE.\n"
    2301             :   "\n"
    2302             :   "Response on success:\n"
    2303             :   "keys:   Array of keys.\n"
    2304             :   "  Boolean values:\n"
    2305             :   "   revoked\n"
    2306             :   "   expired\n"
    2307             :   "   disabled\n"
    2308             :   "   invalid\n"
    2309             :   "   can_encrypt\n"
    2310             :   "   can_sign\n"
    2311             :   "   can_certify\n"
    2312             :   "   can_authenticate\n"
    2313             :   "   secret\n"
    2314             :   "   is_qualified\n"
    2315             :   "  String values:\n"
    2316             :   "   protocol\n"
    2317             :   "   issuer_serial (CMS Only)\n"
    2318             :   "   issuer_name (CMS Only)\n"
    2319             :   "   chain_id (CMS Only)\n"
    2320             :   "   owner_trust (OpenPGP only)\n"
    2321             :   "   fingerprint\n"
    2322             :   "  Number values:\n"
    2323             :   "   last_update\n"
    2324             :   "   origin\n"
    2325             :   "  Array values:\n"
    2326             :   "   subkeys\n"
    2327             :   "    Boolean values:\n"
    2328             :   "     revoked\n"
    2329             :   "     expired\n"
    2330             :   "     disabled\n"
    2331             :   "     invalid\n"
    2332             :   "     can_encrypt\n"
    2333             :   "     can_sign\n"
    2334             :   "     can_certify\n"
    2335             :   "     can_authenticate\n"
    2336             :   "     secret\n"
    2337             :   "     is_qualified\n"
    2338             :   "     is_cardkey\n"
    2339             :   "     is_de_vs\n"
    2340             :   "    String values:\n"
    2341             :   "     pubkey_algo_name\n"
    2342             :   "     pubkey_algo_string\n"
    2343             :   "     keyid\n"
    2344             :   "     card_number\n"
    2345             :   "     curve\n"
    2346             :   "     keygrip\n"
    2347             :   "    Number values:\n"
    2348             :   "     pubkey_algo\n"
    2349             :   "     length\n"
    2350             :   "     timestamp\n"
    2351             :   "     expires\n"
    2352             :   "   userids\n"
    2353             :   "    Boolean values:\n"
    2354             :   "     revoked\n"
    2355             :   "     invalid\n"
    2356             :   "    String values:\n"
    2357             :   "     validity\n"
    2358             :   "     uid\n"
    2359             :   "     name\n"
    2360             :   "     email\n"
    2361             :   "     comment\n"
    2362             :   "     address\n"
    2363             :   "    Number values:\n"
    2364             :   "     origin\n"
    2365             :   "     last_update\n"
    2366             :   "    Array values:\n"
    2367             :   "     signatures\n"
    2368             :   "      Boolean values:\n"
    2369             :   "       revoked\n"
    2370             :   "       expired\n"
    2371             :   "       invalid\n"
    2372             :   "       exportable\n"
    2373             :   "      String values:\n"
    2374             :   "       pubkey_algo_name\n"
    2375             :   "       keyid\n"
    2376             :   "       status\n"
    2377             :   "       uid\n"
    2378             :   "       name\n"
    2379             :   "       email\n"
    2380             :   "       comment\n"
    2381             :   "      Number values:\n"
    2382             :   "       pubkey_algo\n"
    2383             :   "       timestamp\n"
    2384             :   "       expires\n"
    2385             :   "       status_code\n"
    2386             :   "       sig_class\n"
    2387             :   "      Array values:\n"
    2388             :   "       notations\n"
    2389             :   "        Boolean values:\n"
    2390             :   "         human_readable\n"
    2391             :   "         critical\n"
    2392             :   "        String values:\n"
    2393             :   "         name\n"
    2394             :   "         value\n"
    2395             :   "        Number values:\n"
    2396             :   "         flags\n"
    2397             :   "     tofu\n"
    2398             :   "      String values:\n"
    2399             :   "       description\n"
    2400             :   "      Number values:\n"
    2401             :   "       validity\n"
    2402             :   "       policy\n"
    2403             :   "       signcount\n"
    2404             :   "       encrcount\n"
    2405             :   "       signfirst\n"
    2406             :   "       signlast\n"
    2407             :   "       encrfirst\n"
    2408             :   "       encrlast\n";
    2409             : static gpg_error_t
    2410           3 : op_keylist (cjson_t request, cjson_t result)
    2411             : {
    2412             :   gpg_error_t err;
    2413           3 :   gpgme_ctx_t ctx = NULL;
    2414             :   gpgme_protocol_t protocol;
    2415           3 :   char **patterns = NULL;
    2416             :   int abool;
    2417           3 :   int secret_only = 0;
    2418           3 :   gpgme_keylist_mode_t mode = 0;
    2419           3 :   gpgme_key_t key = NULL;
    2420           3 :   cjson_t keyarray = xjson_CreateArray ();
    2421             : 
    2422           3 :   if ((err = get_protocol (request, &protocol)))
    2423           0 :     goto leave;
    2424           3 :   ctx = get_context (protocol);
    2425             : 
    2426             :   /* Handle the various keylist mode bools. */
    2427           3 :   if ((err = get_boolean_flag (request, "secret", 0, &abool)))
    2428           0 :     goto leave;
    2429           3 :   if (abool)
    2430             :     {
    2431           0 :       mode |= GPGME_KEYLIST_MODE_WITH_SECRET;
    2432           0 :       secret_only = 1;
    2433             :     }
    2434           3 :   if ((err = get_boolean_flag (request, "with-secret", 0, &abool)))
    2435           0 :     goto leave;
    2436           3 :   if (abool)
    2437           1 :     mode |= GPGME_KEYLIST_MODE_WITH_SECRET;
    2438           3 :   if ((err = get_boolean_flag (request, "extern", 0, &abool)))
    2439           0 :     goto leave;
    2440           3 :   if (abool)
    2441           0 :     mode |= GPGME_KEYLIST_MODE_EXTERN;
    2442             : 
    2443           3 :   if ((err = get_boolean_flag (request, "local", 0, &abool)))
    2444           0 :     goto leave;
    2445           3 :   if (abool)
    2446           0 :     mode |= GPGME_KEYLIST_MODE_LOCAL;
    2447             : 
    2448           3 :   if ((err = get_boolean_flag (request, "sigs", 0, &abool)))
    2449           0 :     goto leave;
    2450           3 :   if (abool)
    2451           0 :     mode |= GPGME_KEYLIST_MODE_SIGS;
    2452             : 
    2453           3 :   if ((err = get_boolean_flag (request, "notations", 0, &abool)))
    2454           0 :     goto leave;
    2455           3 :   if (abool)
    2456           0 :     mode |= GPGME_KEYLIST_MODE_SIG_NOTATIONS;
    2457             : 
    2458           3 :   if ((err = get_boolean_flag (request, "tofu", 0, &abool)))
    2459           0 :     goto leave;
    2460           3 :   if (abool)
    2461           0 :     mode |= GPGME_KEYLIST_MODE_WITH_TOFU;
    2462             : 
    2463           3 :   if ((err = get_boolean_flag (request, "ephemeral", 0, &abool)))
    2464           0 :     goto leave;
    2465           3 :   if (abool)
    2466           0 :     mode |= GPGME_KEYLIST_MODE_EPHEMERAL;
    2467             : 
    2468           3 :   if ((err = get_boolean_flag (request, "validate", 0, &abool)))
    2469           0 :     goto leave;
    2470           3 :   if (abool)
    2471           0 :     mode |= GPGME_KEYLIST_MODE_VALIDATE;
    2472             : 
    2473           3 :   if ((err = get_boolean_flag (request, "locate", 0, &abool)))
    2474           0 :     goto leave;
    2475           3 :   if (abool)
    2476           0 :     mode |= GPGME_KEYLIST_MODE_LOCATE;
    2477             : 
    2478           3 :   if (!mode)
    2479             :     {
    2480             :       /* default to local */
    2481           2 :       mode = GPGME_KEYLIST_MODE_LOCAL;
    2482             :     }
    2483             : 
    2484             :   /* Get the keys.  */
    2485           3 :   patterns = create_keylist_patterns (request, "keys");
    2486             : 
    2487             :   /* Do a keylisting and add the keys */
    2488           3 :   gpgme_set_keylist_mode (ctx, mode);
    2489             : 
    2490           3 :   err = gpgme_op_keylist_ext_start (ctx, (const char **) patterns,
    2491             :                                     secret_only, 0);
    2492           3 :   if (err)
    2493             :     {
    2494           0 :       gpg_error_object (result, err, "Error listing keys: %s",
    2495             :                         gpg_strerror (err));
    2496           0 :       goto leave;
    2497             :     }
    2498             : 
    2499          11 :   while (!(err = gpgme_op_keylist_next (ctx, &key)))
    2500             :     {
    2501           5 :       cJSON_AddItemToArray (keyarray, key_to_json (key));
    2502           5 :       gpgme_key_unref (key);
    2503             :     }
    2504           3 :   err = 0;
    2505             : 
    2506           3 :   if (!cJSON_AddItemToObject (result, "keys", keyarray))
    2507             :     {
    2508           0 :       err = gpg_error_from_syserror ();
    2509           0 :       goto leave;
    2510             :     }
    2511             : 
    2512             :  leave:
    2513           3 :   xfree_array (patterns);
    2514           3 :   if (err)
    2515             :     {
    2516           0 :       cJSON_Delete (keyarray);
    2517             :     }
    2518           3 :   return err;
    2519             : }
    2520             : 
    2521             : 
    2522             : 
    2523             : static const char hlp_import[] =
    2524             :   "op:     \"import\"\n"
    2525             :   "data:   The data to import.\n"
    2526             :   "\n"
    2527             :   "Optional parameters:\n"
    2528             :   "protocol:      Either \"openpgp\" (default) or \"cms\".\n"
    2529             :   "\n"
    2530             :   "Optional boolean flags (default is false):\n"
    2531             :   "base64:        Input data is base64 encoded.\n"
    2532             :   "\n"
    2533             :   "Response on success:\n"
    2534             :   "result: The import result.\n"
    2535             :   "  Number values:\n"
    2536             :   "   considered\n"
    2537             :   "   no_user_id\n"
    2538             :   "   imported\n"
    2539             :   "   imported_rsa\n"
    2540             :   "   unchanged\n"
    2541             :   "   new_user_ids\n"
    2542             :   "   new_sub_keys\n"
    2543             :   "   new_signatures\n"
    2544             :   "   new_revocations\n"
    2545             :   "   secret_read\n"
    2546             :   "   secret_imported\n"
    2547             :   "   secret_unchanged\n"
    2548             :   "   skipped_new_keys\n"
    2549             :   "   not_imported\n"
    2550             :   "   skipped_v3_keys\n"
    2551             :   "  Array values:\n"
    2552             :   "   imports: List of keys for which an import was attempted\n"
    2553             :   "    String values:\n"
    2554             :   "     fingerprint\n"
    2555             :   "     error_string\n"
    2556             :   "    Number values:\n"
    2557             :   "     error_code\n"
    2558             :   "     status\n";
    2559             : static gpg_error_t
    2560           1 : op_import (cjson_t request, cjson_t result)
    2561             : {
    2562             :   gpg_error_t err;
    2563           1 :   gpgme_ctx_t ctx = NULL;
    2564           1 :   gpgme_data_t input = NULL;
    2565             :   gpgme_import_result_t import_result;
    2566             :   gpgme_protocol_t protocol;
    2567             : 
    2568           1 :   if ((err = get_protocol (request, &protocol)))
    2569           0 :     goto leave;
    2570           1 :   ctx = get_context (protocol);
    2571             : 
    2572           1 :   if ((err = get_string_data (request, result, "data", &input)))
    2573           0 :       goto leave;
    2574             : 
    2575             :   /* Import.  */
    2576           1 :   err = gpgme_op_import (ctx, input);
    2577           1 :   import_result = gpgme_op_import_result (ctx);
    2578           1 :   if (err)
    2579             :     {
    2580           0 :       gpg_error_object (result, err, "Import failed: %s",
    2581             :                         gpg_strerror (err));
    2582           0 :       goto leave;
    2583             :     }
    2584           1 :   gpgme_data_release (input);
    2585           1 :   input = NULL;
    2586             : 
    2587           1 :   xjson_AddItemToObject (result, "result",
    2588             :                          import_result_to_json (import_result));
    2589             : 
    2590             :  leave:
    2591           1 :   release_context (ctx);
    2592           1 :   gpgme_data_release (input);
    2593           1 :   return err;
    2594             : }
    2595             : 
    2596             : 
    2597             : static const char hlp_export[] =
    2598             :   "op:     \"export\"\n"
    2599             :   "\n"
    2600             :   "Optional parameters:\n"
    2601             :   "keys:          Array of strings or fingerprints to lookup\n"
    2602             :   "               For a single key a String may be used instead of an array.\n"
    2603             :   "               default exports all keys.\n"
    2604             :   "protocol:      Either \"openpgp\" (default) or \"cms\".\n"
    2605             :   "\n"
    2606             :   "Optional boolean flags (default is false):\n"
    2607             :   "armor:         Request output in armored format.\n"
    2608             :   "extern:        Add EXPORT_MODE_EXTERN.\n"
    2609             :   "minimal:       Add EXPORT_MODE_MINIMAL.\n"
    2610             :   "raw:           Add EXPORT_MODE_RAW.\n"
    2611             :   "pkcs12:        Add EXPORT_MODE_PKCS12.\n"
    2612             :   "with-sec-fprs: Add the sec-fprs array to the result.\n"
    2613             :   "\n"
    2614             :   "Response on success:\n"
    2615             :   "type:     \"keys\"\n"
    2616             :   "data:     Unless armor mode is used a Base64 encoded binary.\n"
    2617             :   "          In armor mode a string with an armored\n"
    2618             :   "          OpenPGP or a PEM / PKCS12 key.\n"
    2619             :   "base64:   Boolean indicating whether data is base64 encoded.\n"
    2620             :   "sec-fprs: Optional, only if with-secret is set. An array containing\n"
    2621             :   "          the fingerprints of the keys in the export for which a secret\n"
    2622             :   "          key is available";
    2623             : static gpg_error_t
    2624           2 : op_export (cjson_t request, cjson_t result)
    2625             : {
    2626             :   gpg_error_t err;
    2627           2 :   gpgme_ctx_t ctx = NULL;
    2628             :   gpgme_protocol_t protocol;
    2629           2 :   char **patterns = NULL;
    2630             :   int abool;
    2631           2 :   int with_secret = 0;
    2632           2 :   gpgme_export_mode_t mode = 0;
    2633           2 :   gpgme_data_t output = NULL;
    2634             : 
    2635           2 :   if ((err = get_protocol (request, &protocol)))
    2636           0 :     goto leave;
    2637           2 :   ctx = get_context (protocol);
    2638             : 
    2639           2 :   if ((err = get_boolean_flag (request, "armor", 0, &abool)))
    2640           0 :     goto leave;
    2641           2 :   gpgme_set_armor (ctx, abool);
    2642             : 
    2643             :   /* Handle the various export mode bools. */
    2644           2 :   if ((err = get_boolean_flag (request, "secret", 0, &abool)))
    2645           0 :     goto leave;
    2646           2 :   if (abool)
    2647             :     {
    2648           0 :       err = gpg_error (GPG_ERR_FORBIDDEN);
    2649           0 :       goto leave;
    2650             :     }
    2651             : 
    2652           2 :   if ((err = get_boolean_flag (request, "extern", 0, &abool)))
    2653           0 :     goto leave;
    2654           2 :   if (abool)
    2655           0 :     mode |= GPGME_EXPORT_MODE_EXTERN;
    2656             : 
    2657           2 :   if ((err = get_boolean_flag (request, "minimal", 0, &abool)))
    2658           0 :     goto leave;
    2659           2 :   if (abool)
    2660           0 :     mode |= GPGME_EXPORT_MODE_MINIMAL;
    2661             : 
    2662           2 :   if ((err = get_boolean_flag (request, "raw", 0, &abool)))
    2663           0 :     goto leave;
    2664           2 :   if (abool)
    2665           0 :     mode |= GPGME_EXPORT_MODE_RAW;
    2666             : 
    2667           2 :   if ((err = get_boolean_flag (request, "pkcs12", 0, &abool)))
    2668           0 :     goto leave;
    2669           2 :   if (abool)
    2670           0 :     mode |= GPGME_EXPORT_MODE_PKCS12;
    2671             : 
    2672           2 :   if ((err = get_boolean_flag (request, "with-sec-fprs", 0, &abool)))
    2673           0 :     goto leave;
    2674           2 :   if (abool)
    2675           1 :     with_secret = 1;
    2676             : 
    2677             :   /* Get the export patterns.  */
    2678           2 :   patterns = create_keylist_patterns (request, "keys");
    2679             : 
    2680             :   /* Create an output data object.  */
    2681           2 :   err = gpgme_data_new (&output);
    2682           2 :   if (err)
    2683             :     {
    2684           0 :       gpg_error_object (result, err, "Error creating output data object: %s",
    2685             :                         gpg_strerror (err));
    2686           0 :       goto leave;
    2687             :     }
    2688             : 
    2689           2 :   err = gpgme_op_export_ext (ctx, (const char **) patterns,
    2690             :                              mode, output);
    2691           2 :   if (err)
    2692             :     {
    2693           0 :       gpg_error_object (result, err, "Error exporting keys: %s",
    2694             :                         gpg_strerror (err));
    2695           0 :       goto leave;
    2696             :     }
    2697             : 
    2698             :   /* We need to base64 if armoring has not been requested.  */
    2699           2 :   err = make_data_object (result, output,
    2700           2 :                           "keys", !gpgme_get_armor (ctx));
    2701           2 :   output = NULL;
    2702             : 
    2703           2 :   if (!err && with_secret)
    2704             :     {
    2705           1 :       err = add_secret_fprs ((const char **) patterns, protocol, result);
    2706             :     }
    2707             : 
    2708             : leave:
    2709           2 :   xfree_array (patterns);
    2710           2 :   release_context (ctx);
    2711           2 :   gpgme_data_release (output);
    2712             : 
    2713           2 :   return err;
    2714             : }
    2715             : 
    2716             : 
    2717             : static const char hlp_delete[] =
    2718             :   "op:     \"delete\"\n"
    2719             :   "key:    Fingerprint of the key to delete.\n"
    2720             :   "\n"
    2721             :   "Optional parameters:\n"
    2722             :   "protocol:      Either \"openpgp\" (default) or \"cms\".\n"
    2723             :   "\n"
    2724             :   "Response on success:\n"
    2725             :   "success:   Boolean true.\n";
    2726             : static gpg_error_t
    2727           1 : op_delete (cjson_t request, cjson_t result)
    2728             : {
    2729             :   gpg_error_t err;
    2730           1 :   gpgme_ctx_t ctx = NULL;
    2731           1 :   gpgme_ctx_t keylist_ctx = NULL;
    2732             :   gpgme_protocol_t protocol;
    2733           1 :   gpgme_key_t key = NULL;
    2734           1 :   int secret = 0;
    2735           1 :   cjson_t j_key = NULL;
    2736             : 
    2737           1 :   if ((err = get_protocol (request, &protocol)))
    2738           0 :     goto leave;
    2739           1 :   ctx = get_context (protocol);
    2740           1 :   keylist_ctx = get_context (protocol);
    2741             : 
    2742           1 :   if ((err = get_boolean_flag (request, "secret", 0, &secret)))
    2743           0 :     goto leave;
    2744           1 :   if (secret)
    2745             :     {
    2746           0 :       err = gpg_error (GPG_ERR_FORBIDDEN);
    2747           0 :       goto leave;
    2748             :     }
    2749             : 
    2750           1 :   j_key = cJSON_GetObjectItem (request, "key");
    2751           1 :   if (!j_key)
    2752             :     {
    2753           0 :       err = gpg_error (GPG_ERR_NO_KEY);
    2754           0 :       goto leave;
    2755             :     }
    2756           1 :   if (!cjson_is_string (j_key))
    2757             :     {
    2758           0 :       err = gpg_error (GPG_ERR_INV_VALUE);
    2759           0 :       goto leave;
    2760             :     }
    2761             : 
    2762             :   /* Get the key */
    2763           1 :   if ((err = gpgme_get_key (keylist_ctx, j_key->valuestring, &key, 0)))
    2764             :     {
    2765           0 :       gpg_error_object (result, err, "Error fetching key for delete: %s",
    2766             :                         gpg_strerror (err));
    2767           0 :       goto leave;
    2768             :     }
    2769             : 
    2770           1 :   err = gpgme_op_delete (ctx, key, 0);
    2771           1 :   if (err)
    2772             :     {
    2773           0 :       gpg_error_object (result, err, "Error deleting key: %s",
    2774             :                         gpg_strerror (err));
    2775           0 :       goto leave;
    2776             :     }
    2777             : 
    2778           1 :   xjson_AddBoolToObject (result, "success", 1);
    2779             : 
    2780             : leave:
    2781           1 :   gpgme_key_unref (key);
    2782           1 :   release_context (ctx);
    2783           1 :   release_context (keylist_ctx);
    2784             : 
    2785           1 :   return err;
    2786             : }
    2787             : 
    2788             : 
    2789             : static const char hlp_config_opt[] =
    2790             :   "op:       \"config_opt\"\n"
    2791             :   "component: The component of the option.\n"
    2792             :   "option:    The name of the option.\n"
    2793             :   "\n"
    2794             :   "Response on success:\n"
    2795             :   "\n"
    2796             :   "option: Information about the option.\n"
    2797             :   " String values:\n"
    2798             :   "  name: The name of the option\n"
    2799             :   "  description: Localized description of the opt.\n"
    2800             :   "  argname: Thhe argument name e.g. --verbose\n"
    2801             :   "  default_description\n"
    2802             :   "  no_arg_description\n"
    2803             :   " Number values:\n"
    2804             :   "  flags: Flags for this option.\n"
    2805             :   "  level: the level of the description. See gpgme_conf_level_t.\n"
    2806             :   "  type: The type of the option. See gpgme_conf_type_t.\n"
    2807             :   "  alt_type: Alternate type of the option. See gpgme_conf_type_t\n"
    2808             :   " Arg type values: (see desc. below)\n"
    2809             :   "  default_value: Array of the default value.\n"
    2810             :   "  no_arg_value: Array of the value if it is not set.\n"
    2811             :   "  value: Array for the current value if the option is set.\n"
    2812             :   "\n"
    2813             :   "If the response is empty the option was not found\n"
    2814             :   "";
    2815             : static gpg_error_t
    2816           1 : op_config_opt (cjson_t request, cjson_t result)
    2817             : {
    2818             :   gpg_error_t err;
    2819           1 :   gpgme_ctx_t ctx = NULL;
    2820           1 :   gpgme_conf_comp_t conf = NULL;
    2821           1 :   gpgme_conf_comp_t comp = NULL;
    2822             :   cjson_t j_tmp;
    2823           1 :   char *comp_name = NULL;
    2824           1 :   char *opt_name = NULL;
    2825             : 
    2826           1 :   ctx = get_context (GPGME_PROTOCOL_GPGCONF);
    2827             : 
    2828           1 :   j_tmp = cJSON_GetObjectItem (request, "component");
    2829           1 :   if (!j_tmp || !cjson_is_string (j_tmp))
    2830             :     {
    2831           0 :       err = gpg_error (GPG_ERR_INV_VALUE);
    2832           0 :       goto leave;
    2833             :     }
    2834           1 :   comp_name = j_tmp->valuestring;
    2835             : 
    2836             : 
    2837           1 :   j_tmp = cJSON_GetObjectItem (request, "option");
    2838           1 :   if (!j_tmp || !cjson_is_string (j_tmp))
    2839             :     {
    2840           0 :       err = gpg_error (GPG_ERR_INV_VALUE);
    2841           0 :       goto leave;
    2842             :     }
    2843           1 :   opt_name = j_tmp->valuestring;
    2844             : 
    2845             :   /* Load the config */
    2846           1 :   err = gpgme_op_conf_load (ctx, &conf);
    2847           1 :   if (err)
    2848             :     {
    2849           0 :       goto leave;
    2850             :     }
    2851             : 
    2852           1 :   comp = conf;
    2853           1 :   for (comp = conf; comp; comp = comp->next)
    2854             :     {
    2855           1 :       gpgme_conf_opt_t opt = NULL;
    2856           1 :       int found = 0;
    2857           1 :       if (!comp->name || strcmp (comp->name, comp_name))
    2858             :         {
    2859             :           /* Skip components if a single one is specified */
    2860           0 :           continue;
    2861             :         }
    2862          10 :       for (opt = comp->options; opt; opt = opt->next)
    2863             :         {
    2864           5 :           if (!opt->name || strcmp (opt->name, opt_name))
    2865             :             {
    2866             :               /* Skip components if a single one is specified */
    2867           4 :               continue;
    2868             :             }
    2869           1 :           xjson_AddItemToObject (result, "option", conf_opt_to_json (opt));
    2870           1 :           found = 1;
    2871           1 :           break;
    2872             :         }
    2873           1 :       if (found)
    2874           1 :         break;
    2875             :     }
    2876             : 
    2877             : leave:
    2878           1 :   gpgme_conf_release (conf);
    2879           1 :   release_context (ctx);
    2880             : 
    2881           1 :   return err;
    2882             : }
    2883             : 
    2884             : 
    2885             : static const char hlp_config[] =
    2886             :   "op:     \"config\"\n"
    2887             :   "\n"
    2888             :   "Optional parameters:\n"
    2889             :   "component:    Component of entries to list.\n"
    2890             :   "              Default: all\n"
    2891             :   "\n"
    2892             :   "Response on success:\n"
    2893             :   "   components: Array of the component program configs.\n"
    2894             :   "     name:         The component name.\n"
    2895             :   "     description:  Description of the component.\n"
    2896             :   "     program_name: The absolute path to the program.\n"
    2897             :   "     options: Array of config options\n"
    2898             :   "      String values:\n"
    2899             :   "       name: The name of the option\n"
    2900             :   "       description: Localized description of the opt.\n"
    2901             :   "       argname: Thhe argument name e.g. --verbose\n"
    2902             :   "       default_description\n"
    2903             :   "       no_arg_description\n"
    2904             :   "      Number values:\n"
    2905             :   "       flags: Flags for this option.\n"
    2906             :   "       level: the level of the description. See gpgme_conf_level_t.\n"
    2907             :   "       type: The type of the option. See gpgme_conf_type_t.\n"
    2908             :   "       alt_type: Alternate type of the option. See gpgme_conf_type_t\n"
    2909             :   "      Arg type values: (see desc. below)\n"
    2910             :   "       default_value: Array of the default value.\n"
    2911             :   "       no_arg_value: Array of the value if it is not set.\n"
    2912             :   "       value: Array for the current value if the option is set.\n"
    2913             :   "\n"
    2914             :   "Conf type values are an array of values that are either\n"
    2915             :   "of type number named \"number\" or of type string,\n"
    2916             :   "named \"string\".\n"
    2917             :   "If the type is none the bool value is_none is true.\n"
    2918             :   "";
    2919             : static gpg_error_t
    2920           1 : op_config (cjson_t request, cjson_t result)
    2921             : {
    2922             :   gpg_error_t err;
    2923           1 :   gpgme_ctx_t ctx = NULL;
    2924           1 :   gpgme_conf_comp_t conf = NULL;
    2925           1 :   gpgme_conf_comp_t comp = NULL;
    2926             :   cjson_t j_tmp;
    2927           1 :   char *comp_name = NULL;
    2928           1 :   cjson_t j_comps = xjson_CreateArray ();
    2929             : 
    2930           1 :   ctx = get_context (GPGME_PROTOCOL_GPGCONF);
    2931             : 
    2932           1 :   j_tmp = cJSON_GetObjectItem (request, "component");
    2933           1 :   if (j_tmp && cjson_is_string (j_tmp))
    2934             :     {
    2935           1 :       comp_name = j_tmp->valuestring;
    2936             :     }
    2937           0 :   else if (j_tmp && !cjson_is_string (j_tmp))
    2938             :     {
    2939           0 :       err = gpg_error (GPG_ERR_INV_VALUE);
    2940           0 :       goto leave;
    2941             :     }
    2942             : 
    2943             :   /* Load the config */
    2944           1 :   err = gpgme_op_conf_load (ctx, &conf);
    2945           1 :   if (err)
    2946             :     {
    2947           0 :       goto leave;
    2948             :     }
    2949             : 
    2950           1 :   comp = conf;
    2951           7 :   for (comp = conf; comp; comp = comp->next)
    2952             :     {
    2953           6 :       if (comp_name && comp->name && strcmp (comp->name, comp_name))
    2954             :         {
    2955             :           /* Skip components if a single one is specified */
    2956           5 :           continue;
    2957             :         }
    2958           1 :       cJSON_AddItemToArray (j_comps, conf_comp_to_json (comp));
    2959             :     }
    2960           1 :   xjson_AddItemToObject (result, "components", j_comps);
    2961             : 
    2962             : leave:
    2963           1 :   gpgme_conf_release (conf);
    2964           1 :   release_context (ctx);
    2965             : 
    2966           1 :   return err;
    2967             : }
    2968             : 
    2969             : 
    2970             : 
    2971             : static const char hlp_createkey[] =
    2972             :   "op:      \"createkey\"\n"
    2973             :   "userid:  The user id. E.g. \"Foo Bar <foo@bar.baz>\"\n"
    2974             :   "\n"
    2975             :   "Optional parameters:\n"
    2976             :   "algo:        Algo of the key as string.  See doc for gpg --quick-gen-key.\n"
    2977             :   "             Supported values are \"default\" and \"future-default\".\n"
    2978             :   "expires:     Seconds from now to expiry as Number.  0 means no expiry.\n"
    2979             :   "             The default is to use a standard expiration interval.\n"
    2980             :   "\n"
    2981             :   "Response on success:\n"
    2982             :   "fingerprint:   The fingerprint of the created key.\n"
    2983             :   "\n"
    2984             :   "Note: This interface does not allow key generation if the userid\n"
    2985             :   "of the new key already exists in the keyring.\n";
    2986             : static gpg_error_t
    2987           1 : op_createkey (cjson_t request, cjson_t result)
    2988             : {
    2989             :   gpg_error_t err;
    2990           1 :   gpgme_ctx_t ctx = NULL;
    2991           1 :   unsigned int flags = GPGME_CREATE_FORCE; /* Always force as the GUI should
    2992             :                                               handle checks, if required. */
    2993           1 :   unsigned long expires = 0;
    2994             :   cjson_t j_tmp;
    2995           1 :   const char *algo = "default";
    2996             :   const char *userid;
    2997             :   gpgme_genkey_result_t res;
    2998             : 
    2999             : #ifdef GPG_AGENT_ALLOWS_KEYGEN_THROUGH_BROWSER
    3000             :   /* GnuPG forbids keygen through the browser socket so for
    3001             :      this we create an unrestricted context.
    3002             :      See GnuPG-Bug-Id: T4010 for more info */
    3003             :   ctx = get_context (GPGME_PROTOCOL_OpenPGP);
    3004             : #else
    3005           1 :     err = gpgme_new (&ctx);
    3006           1 :   if (err)
    3007           0 :     log_fatal ("error creating GPGME context: %s\n", gpg_strerror (err));
    3008           1 :   gpgme_set_protocol (ctx, GPGME_PROTOCOL_OpenPGP);
    3009             : #endif
    3010             : 
    3011           1 :   j_tmp = cJSON_GetObjectItem (request, "algo");
    3012           1 :   if (j_tmp && cjson_is_string (j_tmp))
    3013             :     {
    3014           1 :       algo = j_tmp->valuestring;
    3015             :     }
    3016             : 
    3017           1 :   j_tmp = cJSON_GetObjectItem (request, "userid");
    3018           1 :   if (!j_tmp || !cjson_is_string (j_tmp))
    3019             :     {
    3020           0 :       err = gpg_error (GPG_ERR_INV_VALUE);
    3021           0 :       goto leave;
    3022             :     }
    3023             : 
    3024           1 :   userid = j_tmp->valuestring;
    3025             : 
    3026           1 :   j_tmp = cJSON_GetObjectItem (request, "expires");
    3027           1 :   if (j_tmp)
    3028             :     {
    3029           0 :       if (!cjson_is_number (j_tmp))
    3030             :         {
    3031           0 :           err = gpg_error (GPG_ERR_INV_VALUE);
    3032           0 :           goto leave;
    3033             :         }
    3034           0 :       expires = j_tmp->valueint;
    3035             : 
    3036           0 :       if (!expires)
    3037           0 :         flags |= GPGME_CREATE_NOEXPIRE;
    3038             :     }
    3039             : 
    3040             : 
    3041           1 :   if ((err = gpgme_op_createkey (ctx, userid, algo, 0, expires, NULL, flags)))
    3042           0 :     goto leave;
    3043             : 
    3044           1 :   res = gpgme_op_genkey_result (ctx);
    3045           1 :   if (!res)
    3046             :     {
    3047           0 :       err = gpg_error (GPG_ERR_GENERAL);
    3048           0 :       goto leave;
    3049             :     }
    3050             : 
    3051           1 :   xjson_AddStringToObject0 (result, "fingerprint", res->fpr);
    3052             : 
    3053             : leave:
    3054             : #ifdef GPG_AGENT_ALLOWS_KEYGEN_THROUGH_BROWSER
    3055             :   release_context (ctx);
    3056             : #else
    3057           1 :   gpgme_release (ctx);
    3058             : #endif
    3059             : 
    3060           1 :   return err;
    3061             : }
    3062             : 
    3063             : 
    3064             : 
    3065             : static const char hlp_getmore[] =
    3066             :   "op:     \"getmore\"\n"
    3067             :   "\n"
    3068             :   "Response on success:\n"
    3069             :   "response:       base64 encoded json response.\n"
    3070             :   "more:           Another getmore is required.\n"
    3071             :   "base64:         boolean if the response is base64 encoded.\n";
    3072             : static gpg_error_t
    3073           1 : op_getmore (cjson_t request, cjson_t result)
    3074             : {
    3075             :   gpg_error_t err;
    3076             :   int c;
    3077             :   size_t n;
    3078             :   size_t chunksize;
    3079             : 
    3080           1 :   if ((err = get_chunksize (request, &chunksize)))
    3081           0 :     goto leave;
    3082             : 
    3083             :   /* For the meta data we need 41 bytes:
    3084             :      {"more":true,"base64":true,"response":""} */
    3085           1 :   chunksize -= 41;
    3086             : 
    3087             :   /* Adjust the chunksize for the base64 conversion.  */
    3088           1 :   chunksize = (chunksize / 4) * 3;
    3089             : 
    3090             :   /* Do we have anything pending?  */
    3091           1 :   if (!pending_data.buffer)
    3092             :     {
    3093           0 :       err = gpg_error (GPG_ERR_NO_DATA);
    3094           0 :       gpg_error_object (result, err, "Operation not possible: %s",
    3095             :                         gpg_strerror (err));
    3096           0 :       goto leave;
    3097             :     }
    3098             : 
    3099             :   /* We currently always use base64 encoding for simplicity. */
    3100           1 :   xjson_AddBoolToObject (result, "base64", 1);
    3101             : 
    3102           1 :   if (pending_data.written >= pending_data.length)
    3103             :     {
    3104             :       /* EOF reached.  This should not happen but we return an empty
    3105             :        * string once in case of client errors.  */
    3106           0 :       gpgme_free (pending_data.buffer);
    3107           0 :       pending_data.buffer = NULL;
    3108           0 :       xjson_AddBoolToObject (result, "more", 0);
    3109           0 :       err = cjson_AddStringToObject (result, "response", "");
    3110             :     }
    3111             :   else
    3112             :     {
    3113           1 :       n = pending_data.length - pending_data.written;
    3114           1 :       if (n > chunksize)
    3115             :         {
    3116           1 :           n = chunksize;
    3117           1 :           xjson_AddBoolToObject (result, "more", 1);
    3118             :         }
    3119             :       else
    3120           0 :         xjson_AddBoolToObject (result, "more", 0);
    3121             : 
    3122           1 :       c = pending_data.buffer[pending_data.written + n];
    3123           1 :       pending_data.buffer[pending_data.written + n] = 0;
    3124           1 :       err = add_base64_to_object (result, "response",
    3125           1 :                                   (pending_data.buffer
    3126           1 :                                    + pending_data.written), n);
    3127           1 :       pending_data.buffer[pending_data.written + n] = c;
    3128           1 :       if (!err)
    3129             :         {
    3130           1 :           pending_data.written += n;
    3131           1 :           if (pending_data.written >= pending_data.length)
    3132             :             {
    3133           0 :               xfree (pending_data.buffer);
    3134           0 :               pending_data.buffer = NULL;
    3135             :             }
    3136             :         }
    3137             :     }
    3138             : 
    3139             :  leave:
    3140           1 :   return err;
    3141             : }
    3142             : 
    3143             : 
    3144             : 
    3145             : static const char hlp_help[] =
    3146             :   "The tool expects a JSON object with the request and responds with\n"
    3147             :   "another JSON object.  Even on error a JSON object is returned.  The\n"
    3148             :   "property \"op\" is mandatory and its string value selects the\n"
    3149             :   "operation; if the property \"help\" with the value \"true\" exists, the\n"
    3150             :   "operation is not performned but a string with the documentation\n"
    3151             :   "returned.  To list all operations it is allowed to leave out \"op\" in\n"
    3152             :   "help mode.  Supported values for \"op\" are:\n\n"
    3153             :   "  config      Read configuration values.\n"
    3154             :   "  config_opt  Read a single configuration value.\n"
    3155             :   "  decrypt     Decrypt data.\n"
    3156             :   "  delete      Delete a key.\n"
    3157             :   "  encrypt     Encrypt data.\n"
    3158             :   "  export      Export keys.\n"
    3159             :   "  createkey   Generate a keypair (OpenPGP only).\n"
    3160             :   "  import      Import data.\n"
    3161             :   "  keylist     List keys.\n"
    3162             :   "  sign        Sign data.\n"
    3163             :   "  verify      Verify data.\n"
    3164             :   "  version     Get engine information.\n"
    3165             :   "  getmore     Retrieve remaining data if chunksize was used.\n"
    3166             :   "  help        Help overview.\n"
    3167             :   "\n"
    3168             :   "If the data needs to be transferred in smaller chunks the\n"
    3169             :   "property \"chunksize\" with an integer value can be added.\n"
    3170             :   "When \"chunksize\" is set the response (including json) will\n"
    3171             :   "not be larger then \"chunksize\" but might be smaller.\n"
    3172             :   "The chunked result will be transferred in base64 encoded chunks\n"
    3173             :   "using the \"getmore\" operation. See help getmore for more info.";
    3174             : static gpg_error_t
    3175           0 : op_help (cjson_t request, cjson_t result)
    3176             : {
    3177             :   cjson_t j_tmp;
    3178           0 :   char *buffer = NULL;
    3179             :   const char *msg;
    3180             : 
    3181           0 :   j_tmp = cJSON_GetObjectItem (request, "interactive_help");
    3182           0 :   if (opt_interactive && j_tmp && cjson_is_string (j_tmp))
    3183           0 :     msg = buffer = xstrconcat (hlp_help, "\n", j_tmp->valuestring, NULL);
    3184             :   else
    3185           0 :     msg = hlp_help;
    3186             : 
    3187           0 :   xjson_AddStringToObject (result, "type", "help");
    3188           0 :   xjson_AddStringToObject (result, "msg", msg);
    3189             : 
    3190           0 :   xfree (buffer);
    3191           0 :   return 0;
    3192             : }
    3193             : 
    3194             : 
    3195             : 
    3196             : /*
    3197             :  * Dispatcher
    3198             :  */
    3199             : 
    3200             : /* Process a request and return the response.  The response is a newly
    3201             :  * allocated string or NULL in case of an error.  */
    3202             : static char *
    3203          19 : process_request (const char *request)
    3204             : {
    3205             :   static struct {
    3206             :     const char *op;
    3207             :     gpg_error_t (*handler)(cjson_t request, cjson_t result);
    3208             :     const char * const helpstr;
    3209             :   } optbl[] = {
    3210             :     { "config",     op_config,     hlp_config },
    3211             :     { "config_opt", op_config_opt, hlp_config_opt },
    3212             :     { "encrypt",    op_encrypt,    hlp_encrypt },
    3213             :     { "export",     op_export,     hlp_export },
    3214             :     { "decrypt",    op_decrypt,    hlp_decrypt },
    3215             :     { "delete",     op_delete,     hlp_delete },
    3216             :     { "createkey",  op_createkey,  hlp_createkey },
    3217             :     { "keylist",    op_keylist,    hlp_keylist },
    3218             :     { "import",     op_import,     hlp_import },
    3219             :     { "sign",       op_sign,       hlp_sign },
    3220             :     { "verify",     op_verify,     hlp_verify },
    3221             :     { "version",    op_version,    hlp_version },
    3222             :     { "getmore",    op_getmore,    hlp_getmore },
    3223             :     { "help",       op_help,       hlp_help },
    3224             :     { NULL }
    3225             :   };
    3226             :   size_t erroff;
    3227             :   cjson_t json;
    3228             :   cjson_t j_tmp, j_op;
    3229             :   cjson_t response;
    3230             :   int helpmode;
    3231          19 :   int is_getmore = 0;
    3232             :   const char *op;
    3233          19 :   char *res = NULL;
    3234             :   int idx;
    3235             : 
    3236          19 :   response = xjson_CreateObject ();
    3237             : 
    3238          19 :   json = cJSON_Parse (request, &erroff);
    3239          19 :   if (!json)
    3240             :     {
    3241           0 :       log_string (GPGRT_LOGLVL_INFO, request);
    3242           0 :       log_info ("invalid JSON object at offset %zu\n", erroff);
    3243           0 :       error_object (response, "invalid JSON object at offset %zu\n", erroff);
    3244           0 :       goto leave;
    3245             :     }
    3246             : 
    3247          19 :   j_tmp = cJSON_GetObjectItem (json, "help");
    3248          19 :   helpmode = (j_tmp && cjson_is_true (j_tmp));
    3249             : 
    3250          19 :   j_op = cJSON_GetObjectItem (json, "op");
    3251          19 :   if (!j_op || !cjson_is_string (j_op))
    3252             :     {
    3253           0 :       if (!helpmode)
    3254             :         {
    3255           0 :           error_object (response, "Property \"op\" missing");
    3256           0 :           goto leave;
    3257             :         }
    3258           0 :       op = "help";  /* Help summary.  */
    3259             :     }
    3260             :   else
    3261          19 :     op = j_op->valuestring;
    3262             : 
    3263         130 :   for (idx=0; optbl[idx].op; idx++)
    3264         130 :     if (!strcmp (op, optbl[idx].op))
    3265          19 :       break;
    3266          19 :   if (optbl[idx].op)
    3267             :     {
    3268          19 :       if (helpmode && strcmp (op, "help"))
    3269             :         {
    3270           0 :           xjson_AddStringToObject (response, "type", "help");
    3271           0 :           xjson_AddStringToObject (response, "op", op);
    3272           0 :           xjson_AddStringToObject (response, "msg", optbl[idx].helpstr);
    3273             :         }
    3274             :       else
    3275             :         {
    3276             :           gpg_error_t err;
    3277          19 :           is_getmore = optbl[idx].handler == op_getmore;
    3278             :           /* If this is not the "getmore" command and we have any
    3279             :            * pending data release that data.  */
    3280          19 :           if (pending_data.buffer && optbl[idx].handler != op_getmore)
    3281             :             {
    3282           0 :               gpgme_free (pending_data.buffer);
    3283           0 :               pending_data.buffer = NULL;
    3284             :             }
    3285             : 
    3286          19 :           err = optbl[idx].handler (json, response);
    3287          19 :           if (err)
    3288             :             {
    3289           0 :               if (!(j_tmp = cJSON_GetObjectItem (response, "type"))
    3290           0 :                   || !cjson_is_string (j_tmp)
    3291           0 :                   || strcmp (j_tmp->valuestring, "error"))
    3292             :                 {
    3293             :                   /* No error type response - provide a generic one.  */
    3294           0 :                   gpg_error_object (response, err, "Operation failed: %s",
    3295             :                                     gpg_strerror (err));
    3296             :                 }
    3297             : 
    3298           0 :               xjson_AddStringToObject (response, "op", op);
    3299             :             }
    3300             :         }
    3301             :     }
    3302             :   else  /* Operation not supported.  */
    3303             :     {
    3304           0 :       error_object (response, "Unknown operation '%s'", op);
    3305           0 :       xjson_AddStringToObject (response, "op", op);
    3306             :     }
    3307             : 
    3308             :  leave:
    3309          19 :   if (is_getmore)
    3310             :     {
    3311             :       /* For getmore we bypass the encode_and_chunk. */
    3312           1 :       if (opt_interactive)
    3313           0 :         res = cJSON_Print (response);
    3314             :       else
    3315           1 :         res = cJSON_PrintUnformatted (response);
    3316             :     }
    3317             :   else
    3318          18 :     res = encode_and_chunk (json, response);
    3319          19 :   if (!res)
    3320             :     {
    3321             :       cjson_t err_obj;
    3322             : 
    3323           0 :       log_error ("printing JSON data failed\n");
    3324             : 
    3325           0 :       err_obj = error_object (NULL, "Printing JSON data failed");
    3326           0 :       if (opt_interactive)
    3327           0 :         res = cJSON_Print (err_obj);
    3328           0 :       res = cJSON_PrintUnformatted (err_obj);
    3329           0 :       cJSON_Delete (err_obj);
    3330             :     }
    3331             : 
    3332          19 :   cJSON_Delete (json);
    3333          19 :   cJSON_Delete (response);
    3334             : 
    3335          19 :   if (!res)
    3336             :     {
    3337             :       /* Can't happen unless we created a broken error_object above */
    3338           0 :       return xtrystrdup ("Bug: Fatal error in process request\n");
    3339             :     }
    3340          19 :   return res;
    3341             : }
    3342             : 
    3343             : 
    3344             : 
    3345             : /*
    3346             :  *  Driver code
    3347             :  */
    3348             : 
    3349             : static char *
    3350           0 : get_file (const char *fname)
    3351             : {
    3352             :   gpg_error_t err;
    3353             :   estream_t fp;
    3354             :   struct stat st;
    3355             :   char *buf;
    3356             :   size_t buflen;
    3357             : 
    3358           0 :   fp = es_fopen (fname, "r");
    3359           0 :   if (!fp)
    3360             :     {
    3361           0 :       err = gpg_error_from_syserror ();
    3362           0 :       log_error ("can't open '%s': %s\n", fname, gpg_strerror (err));
    3363           0 :       return NULL;
    3364             :     }
    3365             : 
    3366           0 :   if (fstat (es_fileno(fp), &st))
    3367             :     {
    3368           0 :       err = gpg_error_from_syserror ();
    3369           0 :       log_error ("can't stat '%s': %s\n", fname, gpg_strerror (err));
    3370           0 :       es_fclose (fp);
    3371           0 :       return NULL;
    3372             :     }
    3373             : 
    3374           0 :   buflen = st.st_size;
    3375           0 :   buf = xmalloc (buflen+1);
    3376           0 :   if (es_fread (buf, buflen, 1, fp) != 1)
    3377             :     {
    3378           0 :       err = gpg_error_from_syserror ();
    3379           0 :       log_error ("error reading '%s': %s\n", fname, gpg_strerror (err));
    3380           0 :       es_fclose (fp);
    3381           0 :       xfree (buf);
    3382           0 :       return NULL;
    3383             :     }
    3384           0 :   buf[buflen] = 0;
    3385           0 :   es_fclose (fp);
    3386             : 
    3387           0 :   return buf;
    3388             : }
    3389             : 
    3390             : 
    3391             : /* Return a malloced line or NULL on EOF.  Terminate on read
    3392             :  * error.  */
    3393             : static char *
    3394         112 : get_line (void)
    3395             : {
    3396         112 :   char *line = NULL;
    3397         112 :   size_t linesize = 0;
    3398             :   gpg_error_t err;
    3399         112 :   size_t maxlength = 2048;
    3400             :   int n;
    3401             :   const char *s;
    3402             :   char *p;
    3403             : 
    3404             :  again:
    3405         112 :   n = es_read_line (es_stdin, &line, &linesize, &maxlength);
    3406         112 :   if (n < 0)
    3407             :     {
    3408           0 :       err = gpg_error_from_syserror ();
    3409           0 :       log_error ("error reading line: %s\n", gpg_strerror (err));
    3410           0 :       exit (1);
    3411             :     }
    3412         112 :   if (!n)
    3413             :     {
    3414          18 :       xfree (line);
    3415          18 :       line = NULL;
    3416          18 :       return NULL;  /* EOF */
    3417             :     }
    3418          94 :   if (!maxlength)
    3419             :     {
    3420           0 :       log_info ("line too long - skipped\n");
    3421           0 :       goto again;
    3422             :     }
    3423          94 :   if (memchr (line, 0, n))
    3424           0 :     log_info ("warning: line shortened due to embedded Nul character\n");
    3425             : 
    3426          94 :   if (line[n-1] == '\n')
    3427          94 :     line[n-1] = 0;
    3428             : 
    3429             :   /* Trim leading spaces.  */
    3430          94 :   for (s=line; spacep (s); s++)
    3431             :     ;
    3432          94 :   if (s != line)
    3433             :     {
    3434        4724 :       for (p=line; *s;)
    3435        4608 :         *p++ = *s++;
    3436          58 :       *p = 0;
    3437          58 :       n = p - line;
    3438             :     }
    3439             : 
    3440          94 :   return line;
    3441             : }
    3442             : 
    3443             : 
    3444             : /* Process meta commands used with the standard REPL.  */
    3445             : static char *
    3446           0 : process_meta_commands (const char *request)
    3447             : {
    3448           0 :   char *result = NULL;
    3449             : 
    3450           0 :   while (spacep (request))
    3451           0 :     request++;
    3452             : 
    3453           0 :   if (!strncmp (request, "help", 4) && (spacep (request+4) || !request[4]))
    3454             :     {
    3455           0 :       if (request[4])
    3456             :         {
    3457           0 :           char *buf = xstrconcat ("{ \"help\":true, \"op\":\"", request+5,
    3458             :                                   "\" }", NULL);
    3459           0 :           result = process_request (buf);
    3460           0 :           xfree (buf);
    3461             :         }
    3462             :       else
    3463           0 :         result = process_request ("{ \"op\": \"help\","
    3464             :                                   " \"interactive_help\": "
    3465             :                                   "\"\\nMeta commands:\\n"
    3466             :                                   "  ,read FNAME Process data from FILE\\n"
    3467             :                                   "  ,help CMD   Print help for a command\\n"
    3468             :                                   "  ,quit       Terminate process\""
    3469             :                                   "}");
    3470             :     }
    3471           0 :   else if (!strncmp (request, "quit", 4) && (spacep (request+4) || !request[4]))
    3472           0 :     exit (0);
    3473           0 :   else if (!strncmp (request, "read", 4) && (spacep (request+4) || !request[4]))
    3474             :     {
    3475           0 :       if (!request[4])
    3476           0 :         log_info ("usage: ,read FILENAME\n");
    3477             :       else
    3478             :         {
    3479           0 :           char *buffer = get_file (request + 5);
    3480           0 :           if (buffer)
    3481             :             {
    3482           0 :               result = process_request (buffer);
    3483           0 :               xfree (buffer);
    3484             :             }
    3485             :         }
    3486             :     }
    3487             :   else
    3488           0 :     log_info ("invalid meta command\n");
    3489             : 
    3490           0 :   return result;
    3491             : }
    3492             : 
    3493             : 
    3494             : /* If STRING has a help response, return the MSG property in a human
    3495             :  * readable format.  */
    3496             : static char *
    3497           0 : get_help_msg (const char *string)
    3498             : {
    3499             :   cjson_t json, j_type, j_msg;
    3500             :   const char *msg;
    3501           0 :   char *buffer = NULL;
    3502             :   char *p;
    3503             : 
    3504           0 :   json = cJSON_Parse (string, NULL);
    3505           0 :   if (json)
    3506             :     {
    3507           0 :       j_type = cJSON_GetObjectItem (json, "type");
    3508           0 :       if (j_type && cjson_is_string (j_type)
    3509           0 :           && !strcmp (j_type->valuestring, "help"))
    3510             :         {
    3511           0 :           j_msg = cJSON_GetObjectItem (json, "msg");
    3512           0 :           if (j_msg || cjson_is_string (j_msg))
    3513             :             {
    3514           0 :               msg = j_msg->valuestring;
    3515           0 :               buffer = malloc (strlen (msg)+1);
    3516           0 :               if (buffer)
    3517             :                 {
    3518           0 :                   for (p=buffer; *msg; msg++)
    3519             :                     {
    3520           0 :                       if (*msg == '\\' && msg[1] == '\n')
    3521           0 :                         *p++ = '\n';
    3522             :                       else
    3523           0 :                         *p++ = *msg;
    3524             :                     }
    3525           0 :                   *p = 0;
    3526             :                 }
    3527             :             }
    3528             :         }
    3529           0 :       cJSON_Delete (json);
    3530             :     }
    3531           0 :   return buffer;
    3532             : }
    3533             : 
    3534             : 
    3535             : /* An interactive standard REPL.  */
    3536             : static void
    3537           0 : interactive_repl (void)
    3538             : {
    3539           0 :   char *line = NULL;
    3540           0 :   char *request = NULL;
    3541           0 :   char *response = NULL;
    3542             :   char *p;
    3543             :   int first;
    3544             : 
    3545           0 :   es_setvbuf (es_stdin, NULL, _IONBF, 0);
    3546             : #if GPGRT_VERSION_NUMBER >= 0x011d00 /* 1.29 */
    3547           0 :   es_fprintf (es_stderr, "%s %s ready (enter \",help\" for help)\n",
    3548             :               gpgrt_strusage (11), gpgrt_strusage (13));
    3549             : #endif
    3550             :   do
    3551             :     {
    3552           0 :       es_fputs ("> ", es_stderr);
    3553           0 :       es_fflush (es_stderr);
    3554           0 :       es_fflush (es_stdout);
    3555           0 :       xfree (line);
    3556           0 :       line = get_line ();
    3557           0 :       es_fflush (es_stderr);
    3558           0 :       es_fflush (es_stdout);
    3559             : 
    3560           0 :       first = !request;
    3561           0 :       if (line && *line)
    3562             :         {
    3563           0 :           if (!request)
    3564           0 :             request = xstrdup (line);
    3565             :           else
    3566           0 :             request = xstrconcat (request, "\n", line, NULL);
    3567             :         }
    3568             : 
    3569           0 :       if (!line)
    3570           0 :         es_fputs ("\n", es_stderr);
    3571             : 
    3572           0 :       if (!line || !*line || (first && *request == ','))
    3573             :         {
    3574             :           /* Process the input.  */
    3575           0 :           xfree (response);
    3576           0 :           response = NULL;
    3577           0 :           if (request && *request == ',')
    3578             :             {
    3579           0 :               response = process_meta_commands (request+1);
    3580             :             }
    3581           0 :           else if (request)
    3582             :             {
    3583           0 :               response = process_request (request);
    3584             :             }
    3585           0 :           xfree (request);
    3586           0 :           request = NULL;
    3587             : 
    3588           0 :           if (response)
    3589             :             {
    3590           0 :               if (opt_interactive)
    3591             :                 {
    3592           0 :                   char *msg = get_help_msg (response);
    3593           0 :                   if (msg)
    3594             :                     {
    3595           0 :                       xfree (response);
    3596           0 :                       response = msg;
    3597             :                     }
    3598             :                 }
    3599             : 
    3600           0 :               es_fputs ("===> ", es_stderr);
    3601           0 :               es_fflush (es_stderr);
    3602           0 :               for (p=response; *p; p++)
    3603             :                 {
    3604           0 :                   if (*p == '\n')
    3605             :                     {
    3606           0 :                       es_fflush (es_stdout);
    3607           0 :                       es_fputs ("\n===> ", es_stderr);
    3608           0 :                       es_fflush (es_stderr);
    3609             :                     }
    3610             :                   else
    3611           0 :                     es_putc (*p, es_stdout);
    3612             :                 }
    3613           0 :               es_fflush (es_stdout);
    3614           0 :               es_fputs ("\n", es_stderr);
    3615             :             }
    3616             :         }
    3617             :     }
    3618           0 :   while (line);
    3619             : 
    3620           0 :   xfree (request);
    3621           0 :   xfree (response);
    3622           0 :   xfree (line);
    3623           0 : }
    3624             : 
    3625             : 
    3626             : /* Read and process a single request.  */
    3627             : static void
    3628          18 : read_and_process_single_request (void)
    3629             : {
    3630          18 :   char *line = NULL;
    3631          18 :   char *request = NULL;
    3632          18 :   char *response = NULL;
    3633             :   size_t n;
    3634             : 
    3635             :   for (;;)
    3636             :     {
    3637         206 :       xfree (line);
    3638         112 :       line = get_line ();
    3639         112 :       if (line && *line)
    3640         170 :         request = (request? xstrconcat (request, "\n", line, NULL)
    3641         170 :                    /**/   : xstrdup (line));
    3642         112 :       if (!line)
    3643             :         {
    3644          18 :           if (request)
    3645             :             {
    3646          18 :               xfree (response);
    3647          18 :               response = process_request (request);
    3648          18 :               if (response)
    3649             :                 {
    3650          18 :                   es_fputs (response, es_stdout);
    3651          18 :                   if ((n = strlen (response)) && response[n-1] != '\n')
    3652          18 :                     es_fputc ('\n', es_stdout);
    3653             :                 }
    3654          18 :               es_fflush (es_stdout);
    3655             :             }
    3656          18 :           break;
    3657             :         }
    3658             :     }
    3659             : 
    3660          18 :   xfree (response);
    3661          18 :   xfree (request);
    3662          18 :   xfree (line);
    3663          18 : }
    3664             : 
    3665             : 
    3666             : /* The Native Messaging processing loop.  */
    3667             : static void
    3668           0 : native_messaging_repl (void)
    3669             : {
    3670             :   gpg_error_t err;
    3671             :   uint32_t nrequest, nresponse;
    3672           0 :   char *request = NULL;
    3673           0 :   char *response = NULL;
    3674             :   size_t n;
    3675             : 
    3676             :   /* Due to the length octets we need to switch the I/O stream into
    3677             :    * binary mode.  */
    3678           0 :   es_set_binary (es_stdin);
    3679           0 :   es_set_binary (es_stdout);
    3680           0 :   es_setbuf (es_stdin, NULL);  /* stdin needs to be unbuffered! */
    3681             : 
    3682             :   for (;;)
    3683             :     {
    3684             :       /* Read length.  Note that the protocol uses native endianness.
    3685             :        * Is it allowed to call such a thing a well thought out
    3686             :        * protocol?  */
    3687           0 :       if (es_read (es_stdin, &nrequest, sizeof nrequest, &n))
    3688             :         {
    3689           0 :           err = gpg_error_from_syserror ();
    3690           0 :           log_error ("error reading request header: %s\n", gpg_strerror (err));
    3691           0 :           break;
    3692             :         }
    3693           0 :       if (!n)
    3694           0 :         break;  /* EOF */
    3695           0 :       if (n != sizeof nrequest)
    3696             :         {
    3697           0 :           log_error ("error reading request header: short read\n");
    3698           0 :           break;
    3699             :         }
    3700           0 :       if (nrequest > MAX_REQUEST_SIZE)
    3701             :         {
    3702           0 :           log_error ("error reading request: request too long (%zu MiB)\n",
    3703             :                      (size_t)nrequest / (1024*1024));
    3704             :           /* Fixme: Shall we read the request to the bit bucket and
    3705             :            * return an error response or just return an error response
    3706             :            * and terminate?  Needs some testing.  */
    3707           0 :           break;
    3708             :         }
    3709             : 
    3710             :       /* Read request.  */
    3711           0 :       request = xtrymalloc (nrequest + 1);
    3712           0 :       if (!request)
    3713             :         {
    3714           0 :           err = gpg_error_from_syserror ();
    3715           0 :           log_error ("error reading request: Not enough memory for %zu MiB)\n",
    3716             :                      (size_t)nrequest / (1024*1024));
    3717             :           /* FIXME: See comment above.  */
    3718           0 :           break;
    3719             :         }
    3720           0 :       if (es_read (es_stdin, request, nrequest, &n))
    3721             :         {
    3722           0 :           err = gpg_error_from_syserror ();
    3723           0 :           log_error ("error reading request: %s\n", gpg_strerror (err));
    3724           0 :           break;
    3725             :         }
    3726           0 :       if (n != nrequest)
    3727             :         {
    3728             :           /* That is a protocol violation.  */
    3729           0 :           xfree (response);
    3730           0 :           response = error_object_string ("Invalid request:"
    3731             :                                           " short read (%zu of %zu bytes)\n",
    3732             :                                           n, (size_t)nrequest);
    3733             :         }
    3734             :       else /* Process request  */
    3735             :         {
    3736           0 :           request[n] = '\0'; /* Ensure that request has an end */
    3737           0 :           if (opt_debug)
    3738           0 :             log_debug ("request='%s'\n", request);
    3739           0 :           xfree (response);
    3740           0 :           response = process_request (request);
    3741           0 :           if (opt_debug)
    3742           0 :             log_debug ("response='%s'\n", response);
    3743             :         }
    3744           0 :       nresponse = strlen (response);
    3745             : 
    3746             :       /* Write response */
    3747           0 :       if (es_write (es_stdout, &nresponse, sizeof nresponse, &n))
    3748             :         {
    3749           0 :           err = gpg_error_from_syserror ();
    3750           0 :           log_error ("error writing request header: %s\n", gpg_strerror (err));
    3751           0 :           break;
    3752             :         }
    3753           0 :       if (n != sizeof nrequest)
    3754             :         {
    3755           0 :           log_error ("error writing request header: short write\n");
    3756           0 :           break;
    3757             :         }
    3758           0 :       if (es_write (es_stdout, response, nresponse, &n))
    3759             :         {
    3760           0 :           err = gpg_error_from_syserror ();
    3761           0 :           log_error ("error writing request: %s\n", gpg_strerror (err));
    3762           0 :           break;
    3763             :         }
    3764           0 :       if (n != nresponse)
    3765             :         {
    3766           0 :           log_error ("error writing request: short write\n");
    3767           0 :           break;
    3768             :         }
    3769           0 :       if (es_fflush (es_stdout) || es_ferror (es_stdout))
    3770             :         {
    3771           0 :           err = gpg_error_from_syserror ();
    3772           0 :           log_error ("error writing request: %s\n", gpg_strerror (err));
    3773           0 :           break;
    3774             :         }
    3775           0 :       xfree (response);
    3776           0 :       response = NULL;
    3777           0 :       xfree (request);
    3778           0 :       request = NULL;
    3779             :     }
    3780             : 
    3781           0 :   xfree (response);
    3782           0 :   xfree (request);
    3783           0 : }
    3784             : 
    3785             : 
    3786             : 
    3787             : static const char *
    3788           0 : my_strusage( int level )
    3789             : {
    3790             :   const char *p;
    3791             : 
    3792           0 :   switch (level)
    3793             :     {
    3794           0 :     case  9: p = "LGPL-2.1-or-later"; break;
    3795           0 :     case 11: p = "gpgme-json"; break;
    3796           0 :     case 13: p = PACKAGE_VERSION; break;
    3797           0 :     case 14: p = "Copyright (C) 2018 g10 Code GmbH"; break;
    3798           0 :     case 19: p = "Please report bugs to <" PACKAGE_BUGREPORT ">.\n"; break;
    3799             :     case 1:
    3800             :     case 40:
    3801           0 :       p = "Usage: gpgme-json [OPTIONS]";
    3802           0 :       break;
    3803             :     case 41:
    3804           0 :       p = "Native messaging based GPGME operations.\n";
    3805           0 :       break;
    3806             :     case 42:
    3807           0 :       p = "1"; /* Flag print 40 as part of 41. */
    3808           0 :       break;
    3809           0 :     default: p = NULL; break;
    3810             :     }
    3811           0 :   return p;
    3812             : }
    3813             : 
    3814             : int
    3815          18 : main (int argc, char *argv[])
    3816             : {
    3817             : #if GPGRT_VERSION_NUMBER < 0x011d00 /* 1.29 */
    3818             : 
    3819             :   fprintf (stderr, "WARNING: Old libgpg-error - using limited mode\n");
    3820             :   native_messaging_repl ();
    3821             : 
    3822             : #else /* This is a modern libgp-error.  */
    3823             : 
    3824             :   enum { CMD_DEFAULT     = 0,
    3825             :          CMD_INTERACTIVE = 'i',
    3826             :          CMD_SINGLE      = 's',
    3827             :          CMD_LIBVERSION  = 501,
    3828          18 :   } cmd = CMD_DEFAULT;
    3829             :   enum {
    3830             :     OPT_DEBUG = 600
    3831             :   };
    3832             : 
    3833             :   static gpgrt_opt_t opts[] = {
    3834             :     ARGPARSE_c  (CMD_INTERACTIVE, "interactive", "Interactive REPL"),
    3835             :     ARGPARSE_c  (CMD_SINGLE,      "single",      "Single request mode"),
    3836             :     ARGPARSE_c  (CMD_LIBVERSION,  "lib-version", "Show library version"),
    3837             :     ARGPARSE_s_n(OPT_DEBUG,       "debug",       "Flyswatter"),
    3838             : 
    3839             :     ARGPARSE_end()
    3840             :   };
    3841          18 :   gpgrt_argparse_t pargs = { &argc, &argv};
    3842             : 
    3843          18 :   int log_file_set = 0;
    3844             : 
    3845          18 :   gpgrt_set_strusage (my_strusage);
    3846             : 
    3847             : #ifdef HAVE_SETLOCALE
    3848          18 :   setlocale (LC_ALL, "");
    3849             : #endif
    3850          18 :   gpgme_check_version (NULL);
    3851             : #ifdef LC_CTYPE
    3852          18 :   gpgme_set_locale (NULL, LC_CTYPE, setlocale (LC_CTYPE, NULL));
    3853             : #endif
    3854             : #ifdef LC_MESSAGES
    3855          18 :   gpgme_set_locale (NULL, LC_MESSAGES, setlocale (LC_MESSAGES, NULL));
    3856             : #endif
    3857             : 
    3858          54 :   while (gpgrt_argparse (NULL, &pargs, opts))
    3859             :     {
    3860          18 :       switch (pargs.r_opt)
    3861             :         {
    3862             :         case CMD_INTERACTIVE:
    3863           0 :           opt_interactive = 1;
    3864             :           /* Fall trough.  */
    3865             :         case CMD_SINGLE:
    3866             :         case CMD_LIBVERSION:
    3867          18 :           cmd = pargs.r_opt;
    3868          18 :           break;
    3869             : 
    3870           0 :         case OPT_DEBUG: opt_debug = 1; break;
    3871             : 
    3872             :         default:
    3873           0 :           pargs.err = ARGPARSE_PRINT_WARNING;
    3874           0 :           break;
    3875             :         }
    3876             :     }
    3877          18 :   gpgrt_argparse (NULL, &pargs, NULL);
    3878             : 
    3879          18 :   if (!opt_debug)
    3880             :     {
    3881             :       /* Handling is similar to GPGME_DEBUG */
    3882          18 :       const char *s = getenv ("GPGME_JSON_DEBUG");
    3883             :       const char *s1;
    3884             : 
    3885          18 :       if (s && atoi (s) > 0)
    3886             :         {
    3887           0 :           opt_debug = 1;
    3888           0 :           s1 = strchr (s, PATHSEP_C);
    3889           0 :           if (s1 && strlen (s1) > 2)
    3890             :             {
    3891           0 :               s1++;
    3892           0 :               log_set_file (s1);
    3893           0 :               log_file_set = 1;
    3894             :             }
    3895             :         }
    3896             :     }
    3897             : 
    3898          18 :   if (opt_debug && !log_file_set)
    3899             :     {
    3900           0 :       const char *home = getenv ("HOME");
    3901           0 :       char *file = xstrconcat ("socket://",
    3902             :                                home? home:"/tmp",
    3903             :                                "/.gnupg/S.gpgme-json.log", NULL);
    3904           0 :       log_set_file (file);
    3905           0 :       xfree (file);
    3906             :     }
    3907             : 
    3908          18 :   if (opt_debug)
    3909             :     { int i;
    3910           0 :       for (i=0; argv[i]; i++)
    3911           0 :         log_debug ("argv[%d]='%s'\n", i, argv[i]);
    3912             :     }
    3913             : 
    3914          18 :   switch (cmd)
    3915             :     {
    3916             :     case CMD_DEFAULT:
    3917           0 :       native_messaging_repl ();
    3918           0 :       break;
    3919             : 
    3920             :     case CMD_SINGLE:
    3921          18 :       read_and_process_single_request ();
    3922          18 :       break;
    3923             : 
    3924             :     case CMD_INTERACTIVE:
    3925           0 :       interactive_repl ();
    3926           0 :       break;
    3927             : 
    3928             :     case CMD_LIBVERSION:
    3929           0 :       printf ("Version from header: %s (0x%06x)\n",
    3930             :               GPGME_VERSION, GPGME_VERSION_NUMBER);
    3931           0 :       printf ("Version from binary: %s\n", gpgme_check_version (NULL));
    3932           0 :       printf ("Copyright blurb ...:%s\n", gpgme_check_version ("\x01\x01"));
    3933           0 :       break;
    3934             :     }
    3935             : 
    3936          18 :   if (opt_debug)
    3937           0 :     log_debug ("ready");
    3938             : 
    3939             : #endif /* This is a modern libgp-error.  */
    3940          18 :   return 0;
    3941             : }
    3942             : #endif /* libgpg-error >= 1.28 */

Generated by: LCOV version 1.13