json: Add command "decrypt" to gpgme-json.
[gpgme.git] / src / gpgme-json.c
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 <http://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.  The used data format is
24  * similar to the API of openpgpjs.
25  */
26
27 #include <config.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <stdarg.h>
32 #ifdef HAVE_LOCALE_H
33 #include <locale.h>
34 #endif
35 #include <stdint.h>
36 #include <sys/stat.h>
37
38 #define GPGRT_ENABLE_ES_MACROS 1
39 #define GPGRT_ENABLE_LOG_MACROS 1
40 #define GPGRT_ENABLE_ARGPARSE_MACROS 1
41 #include "gpgme.h"
42 #include "cJSON.h"
43
44
45 #if GPGRT_VERSION_NUMBER < 0x011c00 /* 1.28 */
46 int main (void){fputs ("Build with Libgpg-error >= 1.28!\n", stderr);return 1;}
47 #else /* libgpg-error >= 1.28 */
48
49 /* We don't allow a request with more than 64 MiB.  */
50 #define MAX_REQUEST_SIZE (64 * 1024 * 1024)
51
52 /* Minimal, default and maximum chunk size for returned data. The
53  * first chunk is returned directly.  If the "more" flag is also
54  * returned, a "getmore" command needs to be used to get the next
55  * chunk.  Right now this value covers just the value of the "data"
56  * element; so to cover for the other returned objects this values
57  * needs to be lower than the maximum allowed size of the browser. */
58 #define MIN_REPLY_CHUNK_SIZE  512
59 #define DEF_REPLY_CHUNK_SIZE (512 * 1024)
60 #define MAX_REPLY_CHUNK_SIZE (10 * 1024 * 1024)
61
62
63 static void xoutofcore (const char *type) GPGRT_ATTR_NORETURN;
64 static cjson_t error_object_v (cjson_t json, const char *message,
65                               va_list arg_ptr) GPGRT_ATTR_PRINTF(2,0);
66 static cjson_t error_object (cjson_t json, const char *message,
67                             ...) GPGRT_ATTR_PRINTF(2,3);
68 static char *error_object_string (const char *message,
69                                   ...) GPGRT_ATTR_PRINTF(1,2);
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   const char *type;/* The "type" of the data.  */
84   int base64;      /* The "base64" flag of the data.  */
85 } pending_data;
86
87
88 /*
89  * Helper functions and macros
90  */
91
92 #define xtrymalloc(a)  gpgrt_malloc ((a))
93 #define xtrystrdup(a)  gpgrt_strdup ((a))
94 #define xmalloc(a) ({                           \
95       void *_r = gpgrt_malloc ((a));            \
96       if (!_r)                                  \
97         xoutofcore ("malloc");                  \
98       _r; })
99 #define xcalloc(a,b) ({                         \
100       void *_r = gpgrt_calloc ((a), (b));       \
101       if (!_r)                                  \
102         xoutofcore ("calloc");                  \
103       _r; })
104 #define xstrdup(a) ({                           \
105       char *_r = gpgrt_strdup ((a));            \
106       if (!_r)                                  \
107         xoutofcore ("strdup");                  \
108       _r; })
109 #define xstrconcat(a, ...) ({                           \
110       char *_r = gpgrt_strconcat ((a), __VA_ARGS__);    \
111       if (!_r)                                          \
112         xoutofcore ("strconcat");                       \
113       _r; })
114 #define xfree(a) gpgrt_free ((a))
115
116 #define spacep(p)   (*(p) == ' ' || *(p) == '\t')
117
118
119 static void
120 xoutofcore (const char *type)
121 {
122   gpg_error_t err = gpg_error_from_syserror ();
123   log_error ("%s failed: %s\n", type, gpg_strerror (err));
124   exit (2);
125 }
126
127
128 /* Call cJSON_CreateObject but terminate in case of an error.  */
129 static cjson_t
130 xjson_CreateObject (void)
131 {
132   cjson_t json = cJSON_CreateObject ();
133   if (!json)
134     xoutofcore ("cJSON_CreateObject");
135   return json;
136 }
137
138
139 /* Wrapper around cJSON_AddStringToObject which returns an gpg-error
140  * code instead of the NULL or the new object.  */
141 static gpg_error_t
142 cjson_AddStringToObject (cjson_t object, const char *name, const char *string)
143 {
144   if (!cJSON_AddStringToObject (object, name, string))
145     return gpg_error_from_syserror ();
146   return 0;
147 }
148
149
150 /* Same as cjson_AddStringToObject but prints an error message and
151  * terminates the process.  */
152 static void
153 xjson_AddStringToObject (cjson_t object, const char *name, const char *string)
154 {
155   if (!cJSON_AddStringToObject (object, name, string))
156     xoutofcore ("cJSON_AddStringToObject");
157 }
158
159
160 /* Wrapper around cJSON_AddBoolToObject which terminates the process
161  * in case of an error.  */
162 static void
163 xjson_AddBoolToObject (cjson_t object, const char *name, int abool)
164 {
165   if (!cJSON_AddBoolToObject (object, name, abool))
166     xoutofcore ("cJSON_AddStringToObject");
167   return ;
168 }
169
170 /* This is similar to cJSON_AddStringToObject but takes (DATA,
171  * DATALEN) and adds it under NAME as a base 64 encoded string to
172  * OBJECT.  */
173 static gpg_error_t
174 add_base64_to_object (cjson_t object, const char *name,
175                       const void *data, size_t datalen)
176 {
177 #if GPGRT_VERSION_NUMBER < 0x011d00 /* 1.29 */
178   return gpg_error (GPG_ERR_NOT_SUPPORTED);
179 #else
180   gpg_err_code_t err;
181   estream_t fp = NULL;
182   gpgrt_b64state_t state = NULL;
183   cjson_t j_str = NULL;
184   void *buffer = NULL;
185
186   fp = es_fopenmem (0, "rwb");
187   if (!fp)
188     {
189       err = gpg_err_code_from_syserror ();
190       goto leave;
191     }
192   state = gpgrt_b64enc_start (fp, "");
193   if (!state)
194     {
195       err = gpg_err_code_from_syserror ();
196       goto leave;
197     }
198
199   err = gpgrt_b64enc_write (state, data, datalen);
200   if (err)
201     goto leave;
202
203   err = gpgrt_b64enc_finish (state);
204   state = NULL;
205   if (err)
206     return err;
207
208   es_fputc (0, fp);
209   if (es_fclose_snatch (fp, &buffer, NULL))
210     {
211       fp = NULL;
212       err = gpg_error_from_syserror ();
213       goto leave;
214     }
215   fp = NULL;
216
217   j_str = cJSON_CreateStringConvey (buffer);
218   if (!j_str)
219     {
220       err = gpg_error_from_syserror ();
221       goto leave;
222     }
223   buffer = NULL;
224
225   if (!cJSON_AddItemToObject (object, name, j_str))
226     {
227       err = gpg_error_from_syserror ();
228       cJSON_Delete (j_str);
229       j_str = NULL;
230       goto leave;
231     }
232   j_str = NULL;
233
234  leave:
235   xfree (buffer);
236   cJSON_Delete (j_str);
237   gpgrt_b64enc_finish (state);
238   es_fclose (fp);
239   return err;
240 #endif
241 }
242
243
244 /* Create a JSON error object.  If JSON is not NULL the error message
245  * is appended to that object.  An existing "type" item will be replaced. */
246 static cjson_t
247 error_object_v (cjson_t json, const char *message, va_list arg_ptr)
248 {
249   cjson_t response, j_tmp;
250   char *msg;
251
252   msg = gpgrt_vbsprintf (message, arg_ptr);
253   if (!msg)
254     xoutofcore ("error_object");
255
256   response = json? json : xjson_CreateObject ();
257
258   if (!(j_tmp = cJSON_GetObjectItem (response, "type")))
259     xjson_AddStringToObject (response, "type", "error");
260   else /* Replace existing "type".  */
261     {
262       j_tmp = cJSON_CreateString ("error");
263       if (!j_tmp)
264         xoutofcore ("cJSON_CreateString");
265       cJSON_ReplaceItemInObject (response, "type", j_tmp);
266      }
267   xjson_AddStringToObject (response, "msg", msg);
268
269   xfree (msg);
270   return response;
271 }
272
273
274 /* Call cJSON_Print but terminate in case of an error.  */
275 static char *
276 xjson_Print (cjson_t object)
277 {
278   char *buf;
279   buf = cJSON_Print (object);
280   if (!buf)
281     xoutofcore ("cJSON_Print");
282   return buf;
283 }
284
285
286 static cjson_t
287 error_object (cjson_t json, const char *message, ...)
288 {
289   cjson_t response;
290   va_list arg_ptr;
291
292   va_start (arg_ptr, message);
293   response = error_object_v (json, message, arg_ptr);
294   va_end (arg_ptr);
295   return response;
296 }
297
298
299 static char *
300 error_object_string (const char *message, ...)
301 {
302   cjson_t response;
303   va_list arg_ptr;
304   char *msg;
305
306   va_start (arg_ptr, message);
307   response = error_object_v (NULL, message, arg_ptr);
308   va_end (arg_ptr);
309
310   msg = xjson_Print (response);
311   cJSON_Delete (response);
312   return msg;
313 }
314
315
316 /* Get the boolean property NAME from the JSON object and store true
317  * or valse at R_VALUE.  If the name is unknown the value of DEF_VALUE
318  * is returned.  If the type of the value is not boolean,
319  * GPG_ERR_INV_VALUE is returned and R_VALUE set to DEF_VALUE.  */
320 static gpg_error_t
321 get_boolean_flag (cjson_t json, const char *name, int def_value, int *r_value)
322 {
323   cjson_t j_item;
324
325   j_item = cJSON_GetObjectItem (json, name);
326   if (!j_item)
327     *r_value = def_value;
328   else if (cjson_is_true (j_item))
329     *r_value = 1;
330   else if (cjson_is_false (j_item))
331     *r_value = 0;
332   else
333     {
334       *r_value = def_value;
335       return gpg_error (GPG_ERR_INV_VALUE);
336     }
337
338   return 0;
339 }
340
341
342 /* Get the boolean property PROTOCOL from the JSON object and store
343  * its value at R_PROTOCOL.  The default is OpenPGP.  */
344 static gpg_error_t
345 get_protocol (cjson_t json, gpgme_protocol_t *r_protocol)
346 {
347   cjson_t j_item;
348
349   *r_protocol = GPGME_PROTOCOL_OpenPGP;
350   j_item = cJSON_GetObjectItem (json, "protocol");
351   if (!j_item)
352     ;
353   else if (!cjson_is_string (j_item))
354     return gpg_error (GPG_ERR_INV_VALUE);
355   else if (!strcmp(j_item->valuestring, "openpgp"))
356     ;
357   else if (!strcmp(j_item->valuestring, "cms"))
358     *r_protocol = GPGME_PROTOCOL_CMS;
359   else
360     return gpg_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
361
362   return 0;
363 }
364
365
366 /* Get the chunksize from JSON and store it at R_CHUNKSIZE.  */
367 static gpg_error_t
368 get_chunksize (cjson_t json, size_t *r_chunksize)
369 {
370   cjson_t j_item;
371
372   *r_chunksize = DEF_REPLY_CHUNK_SIZE;
373   j_item = cJSON_GetObjectItem (json, "chunksize");
374   if (!j_item)
375     ;
376   else if (!cjson_is_number (j_item))
377     return gpg_error (GPG_ERR_INV_VALUE);
378   else if ((size_t)j_item->valueint < MIN_REPLY_CHUNK_SIZE)
379     *r_chunksize = MIN_REPLY_CHUNK_SIZE;
380   else if ((size_t)j_item->valueint > MAX_REPLY_CHUNK_SIZE)
381     *r_chunksize = MAX_REPLY_CHUNK_SIZE;
382   else
383     *r_chunksize = (size_t)j_item->valueint;
384
385   return 0;
386 }
387
388
389 /* Extract the keys from the "keys" array in the JSON object.  On
390  * success a string with the keys identifiers is stored at R_KEYS.
391  * The keys in that string are LF delimited.  On failure an error code
392  * is returned.  */
393 static gpg_error_t
394 get_keys (cjson_t json, char **r_keystring)
395 {
396   cjson_t j_keys, j_item;
397   int i, nkeys;
398   char *p;
399   size_t length;
400
401   *r_keystring = NULL;
402
403   j_keys = cJSON_GetObjectItem (json, "keys");
404   if (!j_keys)
405     return gpg_error (GPG_ERR_NO_KEY);
406   if (!cjson_is_array (j_keys) && !cjson_is_string (j_keys))
407     return gpg_error (GPG_ERR_INV_VALUE);
408
409   /* Fixme: We should better use a membuf like thing.  */
410   length = 1; /* For the EOS.  */
411   if (cjson_is_string (j_keys))
412     {
413       nkeys = 1;
414       length += strlen (j_keys->valuestring);
415       if (strchr (j_keys->valuestring, '\n'))
416         return gpg_error (GPG_ERR_INV_USER_ID);
417     }
418   else
419     {
420       nkeys = cJSON_GetArraySize (j_keys);
421       if (!nkeys)
422         return gpg_error (GPG_ERR_NO_KEY);
423       for (i=0; i < nkeys; i++)
424         {
425           j_item = cJSON_GetArrayItem (j_keys, i);
426           if (!j_item || !cjson_is_string (j_item))
427             return gpg_error (GPG_ERR_INV_VALUE);
428           if (i)
429             length++; /* Space for delimiter. */
430           length += strlen (j_item->valuestring);
431           if (strchr (j_item->valuestring, '\n'))
432             return gpg_error (GPG_ERR_INV_USER_ID);
433         }
434     }
435
436   p = *r_keystring = xtrymalloc (length);
437   if (!p)
438     return gpg_error_from_syserror ();
439
440   if (cjson_is_string (j_keys))
441     {
442       strcpy (p, j_keys->valuestring);
443     }
444   else
445     {
446       for (i=0; i < nkeys; i++)
447         {
448           j_item = cJSON_GetArrayItem (j_keys, i);
449           if (i)
450             *p++ = '\n'; /* Add delimiter.  */
451           p = stpcpy (p, j_item->valuestring);
452         }
453     }
454   return 0;
455 }
456
457
458
459 \f
460 /*
461  *  GPGME support functions.
462  */
463
464 /* Helper for get_context.  */
465 static gpgme_ctx_t
466 _create_new_context (gpgme_protocol_t proto)
467 {
468   gpg_error_t err;
469   gpgme_ctx_t ctx;
470
471   err = gpgme_new (&ctx);
472   if (err)
473     log_fatal ("error creating GPGME context: %s\n", gpg_strerror (err));
474   gpgme_set_protocol (ctx, proto);
475   gpgme_set_ctx_flag (ctx, "request-origin", "browser");
476   return ctx;
477 }
478
479
480 /* Return a context object for protocol PROTO.  This is currently a
481  * statuically allocated context initialized for PROTO.  Termnates
482  * process on failure.  */
483 static gpgme_ctx_t
484 get_context (gpgme_protocol_t proto)
485 {
486   static gpgme_ctx_t ctx_openpgp, ctx_cms;
487
488   if (proto == GPGME_PROTOCOL_OpenPGP)
489     {
490       if (!ctx_openpgp)
491         ctx_openpgp = _create_new_context (proto);
492       return ctx_openpgp;
493     }
494   else if (proto == GPGME_PROTOCOL_CMS)
495     {
496       if (!ctx_cms)
497         ctx_cms = _create_new_context (proto);
498       return ctx_cms;
499     }
500   else
501     log_bug ("invalid protocol %d requested\n", proto);
502 }
503
504
505
506 /* Free context object retrieved by get_context.  */
507 static void
508 release_context (gpgme_ctx_t ctx)
509 {
510   /* Nothing to do right now.  */
511   (void)ctx;
512 }
513
514
515
516 /* Given a Base-64 encoded string object in JSON return a gpgme data
517  * object at R_DATA.  */
518 static gpg_error_t
519 data_from_base64_string (gpgme_data_t *r_data, cjson_t json)
520 {
521 #if GPGRT_VERSION_NUMBER < 0x011d00 /* 1.29 */
522   *r_data = NULL;
523   return gpg_error (GPG_ERR_NOT_SUPPORTED);
524 #else
525   gpg_error_t err;
526   size_t len;
527   char *buf = NULL;
528   gpgrt_b64state_t state = NULL;
529   gpgme_data_t data = NULL;
530
531   *r_data = NULL;
532
533   /* A quick check on the JSON.  */
534   if (!cjson_is_string (json))
535     {
536       err = gpg_error (GPG_ERR_INV_VALUE);
537       goto leave;
538     }
539
540   state = gpgrt_b64dec_start (NULL);
541   if (!state)
542     {
543       err = gpg_err_code_from_syserror ();
544       goto leave;
545     }
546
547   /* Fixme: Data duplication - we should see how to snatch the memory
548    * from the json object.  */
549   len = strlen (json->valuestring);
550   buf = xtrystrdup (json->valuestring);
551   if (!buf)
552     {
553       err = gpg_error_from_syserror ();
554       goto leave;
555     }
556
557   err = gpgrt_b64dec_proc (state, buf, len, &len);
558   if (err)
559     goto leave;
560
561   err = gpgrt_b64dec_finish (state);
562   state = NULL;
563   if (err)
564     goto leave;
565
566   err = gpgme_data_new_from_mem (&data, buf, len, 1);
567   if (err)
568     goto leave;
569   *r_data = data;
570   data = NULL;
571
572  leave:
573   xfree (data);
574   xfree (buf);
575   gpgrt_b64dec_finish (state);
576   return err;
577 #endif
578 }
579
580
581 \f
582 /*
583  * Implementation of the commands.
584  */
585
586
587 /* Create a "data" object and the "type", "base64" and "more" flags
588  * from DATA and append them to RESULT.  Ownership if DATA is
589  * transferred to this function.  TYPE must be a fixed string.
590  * CHUNKSIZE is the chunksize requested from the caller.  If BASE64 is
591  * -1 the need for base64 encoding is determined by the content of
592  * DATA, all other values are take as rtue or false.  Note that
593  * op_getmore has similar code but works on PENDING_DATA which is set
594  * here.  */
595 static gpg_error_t
596 make_data_object (cjson_t result, gpgme_data_t data, size_t chunksize,
597                   const char *type, int base64)
598 {
599   gpg_error_t err;
600   char *buffer;
601   size_t buflen;
602   int c;
603
604   if (!base64 || base64 == -1) /* Make sure that we really have a string.  */
605     gpgme_data_write (data, "", 1);
606
607   buffer = gpgme_data_release_and_get_mem (data, &buflen);
608   data = NULL;
609   if (!buffer)
610     {
611       err = gpg_error_from_syserror ();
612       goto leave;
613     }
614
615   if (base64 == -1)
616     {
617       base64 = 0;
618       if (!buflen)
619         log_fatal ("Appended Nul byte got lost\n");
620       if (memchr (buffer, 0, buflen-1))
621         {
622           buflen--; /* Adjust for the extra nul byte.  */
623           base64 = 1;
624         }
625       /* Fixme: We might want to do more advanced heuristics than to
626        * only look for a Nul.  */
627     }
628
629   /* Adjust the chunksize if we need to do base64 conversion.  */
630   if (base64)
631     chunksize = (chunksize / 4) * 3;
632
633   xjson_AddStringToObject (result, "type", type);
634   xjson_AddBoolToObject (result, "base64", base64);
635
636   if (buflen > chunksize)
637     {
638       xjson_AddBoolToObject (result, "more", 1);
639
640       c = buffer[chunksize];
641       buffer[chunksize] = 0;
642       if (base64)
643         err = add_base64_to_object (result, "data", buffer, chunksize);
644       else
645         err = cjson_AddStringToObject (result, "data", buffer);
646       buffer[chunksize] = c;
647       if (err)
648         goto leave;
649
650       pending_data.buffer = buffer;
651       buffer = NULL;
652       pending_data.length = buflen;
653       pending_data.written = chunksize;
654       pending_data.type = type;
655       pending_data.base64 = base64;
656     }
657   else
658     {
659       if (base64)
660         err = add_base64_to_object (result, "data", buffer, buflen);
661       else
662         err = cjson_AddStringToObject (result, "data", buffer);
663     }
664
665  leave:
666   gpgme_free (buffer);
667   return err;
668 }
669
670
671 \f
672 static const char hlp_encrypt[] =
673   "op:     \"encrypt\"\n"
674   "keys:   Array of strings with the fingerprints or user-ids\n"
675   "        of the keys to encrypt the data.  For a single key\n"
676   "        a String may be used instead of an array.\n"
677   "data:   Input data. \n"
678   "\n"
679   "Optional parameters:\n"
680   "protocol:      Either \"openpgp\" (default) or \"cms\".\n"
681   "chunksize:     Max number of bytes in the resulting \"data\".\n"
682   "\n"
683   "Optional boolean flags (default is false):\n"
684   "base64:        Input data is base64 encoded.\n"
685   "mime:          Indicate that data is a MIME object.\n"
686   "armor:         Request output in armored format.\n"
687   "always-trust:  Request --always-trust option.\n"
688   "no-encrypt-to: Do not use a default recipient.\n"
689   "no-compress:   Do not compress the plaintext first.\n"
690   "throw-keyids:  Request the --throw-keyids option.\n"
691   "want-address:  Require that the keys include a mail address.\n"
692   "wrap:          Assume the input is an OpenPGP message.\n"
693   "\n"
694   "Response on success:\n"
695   "type:   \"ciphertext\"\n"
696   "data:   Unless armor mode is used a Base64 encoded binary\n"
697   "        ciphertext.  In armor mode a string with an armored\n"
698   "        OpenPGP or a PEM message.\n"
699   "base64: Boolean indicating whether data is base64 encoded.\n"
700   "more:   Optional boolean indicating that \"getmore\" is required.";
701 static gpg_error_t
702 op_encrypt (cjson_t request, cjson_t result)
703 {
704   gpg_error_t err;
705   gpgme_ctx_t ctx = NULL;
706   gpgme_protocol_t protocol;
707   size_t chunksize;
708   int opt_base64;
709   int opt_mime;
710   char *keystring = NULL;
711   cjson_t j_input;
712   gpgme_data_t input = NULL;
713   gpgme_data_t output = NULL;
714   int abool;
715   gpgme_encrypt_flags_t encrypt_flags = 0;
716
717   if ((err = get_protocol (request, &protocol)))
718     goto leave;
719   ctx = get_context (protocol);
720   if ((err = get_chunksize (request, &chunksize)))
721     goto leave;
722
723   if ((err = get_boolean_flag (request, "base64", 0, &opt_base64)))
724     goto leave;
725   if ((err = get_boolean_flag (request, "mime", 0, &opt_mime)))
726     goto leave;
727
728   if ((err = get_boolean_flag (request, "armor", 0, &abool)))
729     goto leave;
730   gpgme_set_armor (ctx, abool);
731   if ((err = get_boolean_flag (request, "always-trust", 0, &abool)))
732     goto leave;
733   if (abool)
734     encrypt_flags |= GPGME_ENCRYPT_ALWAYS_TRUST;
735   if ((err = get_boolean_flag (request, "no-encrypt-to", 0,&abool)))
736     goto leave;
737   if (abool)
738     encrypt_flags |= GPGME_ENCRYPT_NO_ENCRYPT_TO;
739   if ((err = get_boolean_flag (request, "no-compress", 0, &abool)))
740     goto leave;
741   if (abool)
742     encrypt_flags |= GPGME_ENCRYPT_NO_COMPRESS;
743   if ((err = get_boolean_flag (request, "throw-keyids", 0, &abool)))
744     goto leave;
745   if (abool)
746     encrypt_flags |= GPGME_ENCRYPT_THROW_KEYIDS;
747   if ((err = get_boolean_flag (request, "wrap", 0, &abool)))
748     goto leave;
749   if (abool)
750     encrypt_flags |= GPGME_ENCRYPT_WRAP;
751   if ((err = get_boolean_flag (request, "want-address", 0, &abool)))
752     goto leave;
753   if (abool)
754     encrypt_flags |= GPGME_ENCRYPT_WANT_ADDRESS;
755
756
757   /* Get the keys.  */
758   err = get_keys (request, &keystring);
759   if (err)
760     {
761       /* Provide a custom error response.  */
762       error_object (result, "Error getting keys: %s", gpg_strerror (err));
763       goto leave;
764     }
765
766   /* Get the data.  Note that INPUT is a shallow data object with the
767    * storage hold in REQUEST.  */
768   j_input = cJSON_GetObjectItem (request, "data");
769   if (!j_input)
770     {
771       err = gpg_error (GPG_ERR_NO_DATA);
772       goto leave;
773     }
774   if (!cjson_is_string (j_input))
775     {
776       err = gpg_error (GPG_ERR_INV_VALUE);
777       goto leave;
778     }
779   if (opt_base64)
780     {
781       err = data_from_base64_string (&input, j_input);
782       if (err)
783         {
784           error_object (result, "Error decoding Base-64 encoded 'data': %s",
785                         gpg_strerror (err));
786           goto leave;
787         }
788     }
789   else
790     {
791       err = gpgme_data_new_from_mem (&input, j_input->valuestring,
792                                      strlen (j_input->valuestring), 0);
793       if (err)
794         {
795           error_object (result, "Error getting 'data': %s", gpg_strerror (err));
796           goto leave;
797         }
798     }
799   if (opt_mime)
800     gpgme_data_set_encoding (input, GPGME_DATA_ENCODING_MIME);
801
802
803   /* Create an output data object.  */
804   err = gpgme_data_new (&output);
805   if (err)
806     {
807       error_object (result, "Error creating output data object: %s",
808                     gpg_strerror (err));
809       goto leave;
810     }
811
812   /* Encrypt.  */
813   err = gpgme_op_encrypt_ext (ctx, NULL, keystring, encrypt_flags,
814                               input, output);
815   /* encrypt_result = gpgme_op_encrypt_result (ctx); */
816   if (err)
817     {
818       error_object (result, "Encryption failed: %s", gpg_strerror (err));
819       goto leave;
820     }
821   gpgme_data_release (input);
822   input = NULL;
823
824   /* We need to base64 if armoring has not been requested.  */
825   err = make_data_object (result, output, chunksize,
826                           "ciphertext", !gpgme_get_armor (ctx));
827   output = NULL;
828
829  leave:
830   xfree (keystring);
831   release_context (ctx);
832   gpgme_data_release (input);
833   gpgme_data_release (output);
834   return err;
835 }
836
837
838 \f
839 static const char hlp_decrypt[] =
840   "op:     \"decrypt\"\n"
841   "data:   The encrypted data.\n"
842   "\n"
843   "Optional parameters:\n"
844   "protocol:      Either \"openpgp\" (default) or \"cms\".\n"
845   "chunksize:     Max number of bytes in the resulting \"data\".\n"
846   "\n"
847   "Optional boolean flags (default is false):\n"
848   "base64:        Input data is base64 encoded.\n"
849   "\n"
850   "Response on success:\n"
851   "type:   \"plaintext\"\n"
852   "data:   The decrypted data.  This may be base64 encoded.\n"
853   "base64: Boolean indicating whether data is base64 encoded.\n"
854   "mime:   A Boolean indicating whether the data is a MIME object.\n"
855   "info:   An optional object with extra information.\n"
856   "more:   Optional boolean indicating that \"getmore\" is required.";
857 static gpg_error_t
858 op_decrypt (cjson_t request, cjson_t result)
859 {
860   gpg_error_t err;
861   gpgme_ctx_t ctx = NULL;
862   gpgme_protocol_t protocol;
863   size_t chunksize;
864   int opt_base64;
865   cjson_t j_input;
866   gpgme_data_t input = NULL;
867   gpgme_data_t output = NULL;
868   gpgme_decrypt_result_t decrypt_result;
869
870   if ((err = get_protocol (request, &protocol)))
871     goto leave;
872   ctx = get_context (protocol);
873   if ((err = get_chunksize (request, &chunksize)))
874     goto leave;
875
876   if ((err = get_boolean_flag (request, "base64", 0, &opt_base64)))
877     goto leave;
878
879   /* Get the data.  Note that INPUT is a shallow data object with the
880    * storage hold in REQUEST.  */
881   j_input = cJSON_GetObjectItem (request, "data");
882   if (!j_input)
883     {
884       err = gpg_error (GPG_ERR_NO_DATA);
885       goto leave;
886     }
887   if (!cjson_is_string (j_input))
888     {
889       err = gpg_error (GPG_ERR_INV_VALUE);
890       goto leave;
891     }
892   if (opt_base64)
893     {
894       err = data_from_base64_string (&input, j_input);
895       if (err)
896         {
897           error_object (result, "Error decoding Base-64 encoded 'data': %s",
898                         gpg_strerror (err));
899           goto leave;
900         }
901     }
902   else
903     {
904       err = gpgme_data_new_from_mem (&input, j_input->valuestring,
905                                      strlen (j_input->valuestring), 0);
906       if (err)
907         {
908           error_object (result, "Error getting 'data': %s", gpg_strerror (err));
909           goto leave;
910         }
911     }
912
913   /* Create an output data object.  */
914   err = gpgme_data_new (&output);
915   if (err)
916     {
917       error_object (result, "Error creating output data object: %s",
918                     gpg_strerror (err));
919       goto leave;
920     }
921
922   /* Decrypt.  */
923   err = gpgme_op_decrypt_ext (ctx, GPGME_DECRYPT_VERIFY,
924                               input, output);
925   decrypt_result = gpgme_op_decrypt_result (ctx);
926   if (err)
927     {
928       error_object (result, "Decryption failed: %s", gpg_strerror (err));
929       goto leave;
930     }
931   gpgme_data_release (input);
932   input = NULL;
933
934   if (decrypt_result->is_mime)
935     xjson_AddBoolToObject (result, "mime", 1);
936
937   err = make_data_object (result, output, chunksize, "plaintext", -1);
938   output = NULL;
939
940  leave:
941   release_context (ctx);
942   gpgme_data_release (input);
943   gpgme_data_release (output);
944   return err;
945 }
946
947
948 \f
949 static const char hlp_getmore[] =
950   "op:     \"getmore\"\n"
951   "\n"
952   "Optional parameters:\n"
953   "chunksize:  Max number of bytes in the \"data\" object.\n"
954   "\n"
955   "Response on success:\n"
956   "type:       Type of the pending data\n"
957   "data:       The next chunk of data\n"
958   "base64:     Boolean indicating whether data is base64 encoded\n"
959   "more:       Optional boolean requesting another \"getmore\".";
960 static gpg_error_t
961 op_getmore (cjson_t request, cjson_t result)
962 {
963   gpg_error_t err;
964   int c;
965   size_t n;
966   size_t chunksize;
967
968   if ((err = get_chunksize (request, &chunksize)))
969     goto leave;
970
971   /* Adjust the chunksize if we need to do base64 conversion.  */
972   if (pending_data.base64)
973     chunksize = (chunksize / 4) * 3;
974
975   /* Do we have anything pending?  */
976   if (!pending_data.buffer)
977     {
978       err = gpg_error (GPG_ERR_NO_DATA);
979       error_object (result, "Operation not possible: %s", gpg_strerror (err));
980       goto leave;
981     }
982
983   xjson_AddStringToObject (result, "type", pending_data.type);
984   xjson_AddBoolToObject (result, "base64", pending_data.base64);
985
986   if (pending_data.written >= pending_data.length)
987     {
988       /* EOF reached.  This should not happen but we return an empty
989        * string once in case of client errors.  */
990       gpgme_free (pending_data.buffer);
991       pending_data.buffer = NULL;
992       xjson_AddBoolToObject (result, "more", 0);
993       err = cjson_AddStringToObject (result, "data", "");
994     }
995   else
996     {
997       n = pending_data.length - pending_data.written;
998       if (n > chunksize)
999         {
1000           n = chunksize;
1001           xjson_AddBoolToObject (result, "more", 1);
1002         }
1003       else
1004         xjson_AddBoolToObject (result, "more", 0);
1005
1006       c = pending_data.buffer[pending_data.written + n];
1007       pending_data.buffer[pending_data.written + n] = 0;
1008       if (pending_data.base64)
1009         err = add_base64_to_object (result, "data",
1010                                     (pending_data.buffer
1011                                      + pending_data.written), n);
1012       else
1013         err = cjson_AddStringToObject (result, "data",
1014                                        (pending_data.buffer
1015                                         + pending_data.written));
1016       pending_data.buffer[pending_data.written + n] = c;
1017       if (!err)
1018         {
1019           pending_data.written += n;
1020           if (pending_data.written >= pending_data.length)
1021             {
1022               gpgme_free (pending_data.buffer);
1023               pending_data.buffer = NULL;
1024             }
1025         }
1026     }
1027
1028  leave:
1029   return err;
1030 }
1031
1032
1033 \f
1034 static const char hlp_help[] =
1035   "The tool expects a JSON object with the request and responds with\n"
1036   "another JSON object.  Even on error a JSON object is returned.  The\n"
1037   "property \"op\" is mandatory and its string value selects the\n"
1038   "operation; if the property \"help\" with the value \"true\" exists, the\n"
1039   "operation is not performned but a string with the documentation\n"
1040   "returned.  To list all operations it is allowed to leave out \"op\" in\n"
1041   "help mode.  Supported values for \"op\" are:\n\n"
1042   "  encrypt     Encrypt data.\n"
1043   "  getmore     Retrieve remaining data.\n"
1044   "  help        Help overview.";
1045 static gpg_error_t
1046 op_help (cjson_t request, cjson_t result)
1047 {
1048   cjson_t j_tmp;
1049   char *buffer = NULL;
1050   const char *msg;
1051
1052   j_tmp = cJSON_GetObjectItem (request, "interactive_help");
1053   if (opt_interactive && j_tmp && cjson_is_string (j_tmp))
1054     msg = buffer = xstrconcat (hlp_help, "\n", j_tmp->valuestring, NULL);
1055   else
1056     msg = hlp_help;
1057
1058   xjson_AddStringToObject (result, "type", "help");
1059   xjson_AddStringToObject (result, "msg", msg);
1060
1061   xfree (buffer);
1062   return 0;
1063 }
1064
1065
1066 \f
1067 /*
1068  * Dispatcher
1069  */
1070
1071 /* Process a request and return the response.  The response is a newly
1072  * allocated string or NULL in case of an error.  */
1073 static char *
1074 process_request (const char *request)
1075 {
1076   static struct {
1077     const char *op;
1078     gpg_error_t (*handler)(cjson_t request, cjson_t result);
1079     const char * const helpstr;
1080   } optbl[] = {
1081     { "encrypt", op_encrypt, hlp_encrypt },
1082     { "decrypt", op_decrypt, hlp_decrypt },
1083     { "getmore", op_getmore, hlp_getmore },
1084     { "help",    op_help,    hlp_help },
1085     { NULL }
1086   };
1087   size_t erroff;
1088   cjson_t json;
1089   cjson_t j_tmp, j_op;
1090   cjson_t response;
1091   int helpmode;
1092   const char *op;
1093   char *res;
1094   int idx;
1095
1096   response = xjson_CreateObject ();
1097
1098   json = cJSON_Parse (request, &erroff);
1099   if (!json)
1100     {
1101       log_string (GPGRT_LOGLVL_INFO, request);
1102       log_info ("invalid JSON object at offset %zu\n", erroff);
1103       error_object (response, "invalid JSON object at offset %zu\n", erroff);
1104       goto leave;
1105     }
1106
1107   j_tmp = cJSON_GetObjectItem (json, "help");
1108   helpmode = (j_tmp && cjson_is_true (j_tmp));
1109
1110   j_op = cJSON_GetObjectItem (json, "op");
1111   if (!j_op || !cjson_is_string (j_op))
1112     {
1113       if (!helpmode)
1114         {
1115           error_object (response, "Property \"op\" missing");
1116           goto leave;
1117         }
1118       op = "help";  /* Help summary.  */
1119     }
1120   else
1121     op = j_op->valuestring;
1122
1123   for (idx=0; optbl[idx].op; idx++)
1124     if (!strcmp (op, optbl[idx].op))
1125       break;
1126   if (optbl[idx].op)
1127     {
1128       if (helpmode && strcmp (op, "help"))
1129         {
1130           xjson_AddStringToObject (response, "type", "help");
1131           xjson_AddStringToObject (response, "op", op);
1132           xjson_AddStringToObject (response, "msg", optbl[idx].helpstr);
1133         }
1134       else
1135         {
1136           gpg_error_t err;
1137
1138           /* If this is not the "getmore" command and we have any
1139            * pending data release that data.  */
1140           if (pending_data.buffer && optbl[idx].handler != op_getmore)
1141             {
1142               gpgme_free (pending_data.buffer);
1143               pending_data.buffer = NULL;
1144             }
1145
1146           err = optbl[idx].handler (json, response);
1147           if (err)
1148             {
1149               if (!(j_tmp = cJSON_GetObjectItem (response, "type"))
1150                   || !cjson_is_string (j_tmp)
1151                   || strcmp (j_tmp->valuestring, "error"))
1152                 {
1153                   /* No error type response - provide a generic one.  */
1154                   error_object (response, "Operation failed: %s",
1155                                 gpg_strerror (err));
1156                 }
1157
1158               xjson_AddStringToObject (response, "op", op);
1159             }
1160         }
1161     }
1162   else  /* Operation not supported.  */
1163     {
1164       error_object (response, "Unknown operation '%s'", op);
1165       xjson_AddStringToObject (response, "op", op);
1166     }
1167
1168  leave:
1169   cJSON_Delete (json);
1170   if (opt_interactive)
1171     res = cJSON_Print (response);
1172   else
1173     res = cJSON_PrintUnformatted (response);
1174   if (!res)
1175     log_error ("Printing JSON data failed\n");
1176   cJSON_Delete (response);
1177   return res;
1178 }
1179
1180
1181 \f
1182 /*
1183  *  Driver code
1184  */
1185
1186 static char *
1187 get_file (const char *fname)
1188 {
1189   gpg_error_t err;
1190   estream_t fp;
1191   struct stat st;
1192   char *buf;
1193   size_t buflen;
1194
1195   fp = es_fopen (fname, "r");
1196   if (!fp)
1197     {
1198       err = gpg_error_from_syserror ();
1199       log_error ("can't open '%s': %s\n", fname, gpg_strerror (err));
1200       return NULL;
1201     }
1202
1203   if (fstat (es_fileno(fp), &st))
1204     {
1205       err = gpg_error_from_syserror ();
1206       log_error ("can't stat '%s': %s\n", fname, gpg_strerror (err));
1207       es_fclose (fp);
1208       return NULL;
1209     }
1210
1211   buflen = st.st_size;
1212   buf = xmalloc (buflen+1);
1213   if (es_fread (buf, buflen, 1, fp) != 1)
1214     {
1215       err = gpg_error_from_syserror ();
1216       log_error ("error reading '%s': %s\n", fname, gpg_strerror (err));
1217       es_fclose (fp);
1218       xfree (buf);
1219       return NULL;
1220     }
1221   buf[buflen] = 0;
1222   es_fclose (fp);
1223
1224   return buf;
1225 }
1226
1227
1228 /* Return a malloced line or NULL on EOF.  Terminate on read
1229  * error.  */
1230 static char *
1231 get_line (void)
1232 {
1233   char *line = NULL;
1234   size_t linesize = 0;
1235   gpg_error_t err;
1236   size_t maxlength = 2048;
1237   int n;
1238   const char *s;
1239   char *p;
1240
1241  again:
1242   n = es_read_line (es_stdin, &line, &linesize, &maxlength);
1243   if (n < 0)
1244     {
1245       err = gpg_error_from_syserror ();
1246       log_error ("error reading line: %s\n", gpg_strerror (err));
1247       exit (1);
1248     }
1249   if (!n)
1250     {
1251       xfree (line);
1252       line = NULL;
1253       return NULL;  /* EOF */
1254     }
1255   if (!maxlength)
1256     {
1257       log_info ("line too long - skipped\n");
1258       goto again;
1259     }
1260   if (memchr (line, 0, n))
1261     log_info ("warning: line shortened due to embedded Nul character\n");
1262
1263   if (line[n-1] == '\n')
1264     line[n-1] = 0;
1265
1266   /* Trim leading spaces.  */
1267   for (s=line; spacep (s); s++)
1268     ;
1269   if (s != line)
1270     {
1271       for (p=line; *s;)
1272         *p++ = *s++;
1273       *p = 0;
1274       n = p - line;
1275     }
1276
1277   return line;
1278 }
1279
1280
1281 /* Process meta commands used with the standard REPL.  */
1282 static char *
1283 process_meta_commands (const char *request)
1284 {
1285   char *result = NULL;
1286
1287   while (spacep (request))
1288     request++;
1289
1290   if (!strncmp (request, "help", 4) && (spacep (request+4) || !request[4]))
1291     {
1292       if (request[4])
1293         {
1294           char *buf = xstrconcat ("{ \"help\":true, \"op\":\"", request+5,
1295                                   "\" }", NULL);
1296           result = process_request (buf);
1297           xfree (buf);
1298         }
1299       else
1300         result = process_request ("{ \"op\": \"help\","
1301                                   " \"interactive_help\": "
1302                                   "\"\\nMeta commands:\\n"
1303                                   "  ,read FNAME Process data from FILE\\n"
1304                                   "  ,help CMD   Print help for a command\\n"
1305                                   "  ,quit       Terminate process\""
1306                                   "}");
1307     }
1308   else if (!strncmp (request, "quit", 4) && (spacep (request+4) || !request[4]))
1309     exit (0);
1310   else if (!strncmp (request, "read", 4) && (spacep (request+4) || !request[4]))
1311     {
1312       if (!request[4])
1313         log_info ("usage: ,read FILENAME\n");
1314       else
1315         {
1316           char *buffer = get_file (request + 5);
1317           if (buffer)
1318             {
1319               result = process_request (buffer);
1320               xfree (buffer);
1321             }
1322         }
1323     }
1324   else
1325     log_info ("invalid meta command\n");
1326
1327   return result;
1328 }
1329
1330
1331 /* If STRING has a help response, return the MSG property in a human
1332  * readable format.  */
1333 static char *
1334 get_help_msg (const char *string)
1335 {
1336   cjson_t json, j_type, j_msg;
1337   const char *msg;
1338   char *buffer = NULL;
1339   char *p;
1340
1341   json = cJSON_Parse (string, NULL);
1342   if (json)
1343     {
1344       j_type = cJSON_GetObjectItem (json, "type");
1345       if (j_type && cjson_is_string (j_type)
1346           && !strcmp (j_type->valuestring, "help"))
1347         {
1348           j_msg = cJSON_GetObjectItem (json, "msg");
1349           if (j_msg || cjson_is_string (j_msg))
1350             {
1351               msg = j_msg->valuestring;
1352               buffer = malloc (strlen (msg)+1);
1353               if (buffer)
1354                 {
1355                   for (p=buffer; *msg; msg++)
1356                     {
1357                       if (*msg == '\\' && msg[1] == '\n')
1358                         *p++ = '\n';
1359                       else
1360                         *p++ = *msg;
1361                     }
1362                   *p = 0;
1363                 }
1364             }
1365         }
1366       cJSON_Delete (json);
1367     }
1368   return buffer;
1369 }
1370
1371
1372 /* An interactive standard REPL.  */
1373 static void
1374 interactive_repl (void)
1375 {
1376   char *line = NULL;
1377   char *request = NULL;
1378   char *response = NULL;
1379   char *p;
1380   int first;
1381
1382   es_setvbuf (es_stdin, NULL, _IONBF, 0);
1383 #if GPGRT_VERSION_NUMBER >= 0x011d00 /* 1.29 */
1384   es_fprintf (es_stderr, "%s %s ready (enter \",help\" for help)\n",
1385               gpgrt_strusage (11), gpgrt_strusage (13));
1386 #endif
1387   do
1388     {
1389       es_fputs ("> ", es_stderr);
1390       es_fflush (es_stderr);
1391       es_fflush (es_stdout);
1392       xfree (line);
1393       line = get_line ();
1394       es_fflush (es_stderr);
1395       es_fflush (es_stdout);
1396
1397       first = !request;
1398       if (line && *line)
1399         {
1400           if (!request)
1401             request = xstrdup (line);
1402           else
1403             request = xstrconcat (request, "\n", line, NULL);
1404         }
1405
1406       if (!line)
1407         es_fputs ("\n", es_stderr);
1408
1409       if (!line || !*line || (first && *request == ','))
1410         {
1411           /* Process the input.  */
1412           xfree (response);
1413           response = NULL;
1414           if (request && *request == ',')
1415             {
1416               response = process_meta_commands (request+1);
1417             }
1418           else if (request)
1419             {
1420               response = process_request (request);
1421             }
1422           xfree (request);
1423           request = NULL;
1424
1425           if (response)
1426             {
1427               if (opt_interactive)
1428                 {
1429                   char *msg = get_help_msg (response);
1430                   if (msg)
1431                     {
1432                       xfree (response);
1433                       response = msg;
1434                     }
1435                 }
1436
1437               es_fputs ("===> ", es_stderr);
1438               es_fflush (es_stderr);
1439               for (p=response; *p; p++)
1440                 {
1441                   if (*p == '\n')
1442                     {
1443                       es_fflush (es_stdout);
1444                       es_fputs ("\n===> ", es_stderr);
1445                       es_fflush (es_stderr);
1446                     }
1447                   else
1448                     es_putc (*p, es_stdout);
1449                 }
1450               es_fflush (es_stdout);
1451               es_fputs ("\n", es_stderr);
1452             }
1453         }
1454     }
1455   while (line);
1456
1457   xfree (request);
1458   xfree (response);
1459   xfree (line);
1460 }
1461
1462
1463 /* Read and process a single request.  */
1464 static void
1465 read_and_process_single_request (void)
1466 {
1467   char *line = NULL;
1468   char *request = NULL;
1469   char *response = NULL;
1470   size_t n;
1471
1472   for (;;)
1473     {
1474       xfree (line);
1475       line = get_line ();
1476       if (line && *line)
1477         request = (request? xstrconcat (request, "\n", line, NULL)
1478                    /**/   : xstrdup (line));
1479       if (!line)
1480         {
1481           if (request)
1482             {
1483               xfree (response);
1484               response = process_request (request);
1485               if (response)
1486                 {
1487                   es_fputs (response, es_stdout);
1488                   if ((n = strlen (response)) && response[n-1] != '\n')
1489                     es_fputc ('\n', es_stdout);
1490                 }
1491               es_fflush (es_stdout);
1492             }
1493           break;
1494         }
1495     }
1496
1497   xfree (response);
1498   xfree (request);
1499   xfree (line);
1500 }
1501
1502
1503 /* The Native Messaging processing loop.  */
1504 static void
1505 native_messaging_repl (void)
1506 {
1507   gpg_error_t err;
1508   uint32_t nrequest, nresponse;
1509   char *request = NULL;
1510   char *response = NULL;
1511   size_t n;
1512
1513   /* Due to the length octets we need to switch the I/O stream into
1514    * binary mode.  */
1515   es_set_binary (es_stdin);
1516   es_set_binary (es_stdout);
1517   es_setbuf (es_stdin, NULL);  /* stdin needs to be unbuffered! */
1518
1519   for (;;)
1520     {
1521       /* Read length.  Note that the protocol uses native endianess.
1522        * Is it allowed to call such a thing a well thought out
1523        * protocol?  */
1524       if (es_read (es_stdin, &nrequest, sizeof nrequest, &n))
1525         {
1526           err = gpg_error_from_syserror ();
1527           log_error ("error reading request header: %s\n", gpg_strerror (err));
1528           break;
1529         }
1530       if (!n)
1531         break;  /* EOF */
1532       if (n != sizeof nrequest)
1533         {
1534           log_error ("error reading request header: short read\n");
1535           break;
1536         }
1537       if (nrequest > MAX_REQUEST_SIZE)
1538         {
1539           log_error ("error reading request: request too long (%zu MiB)\n",
1540                      (size_t)nrequest / (1024*1024));
1541           /* Fixme: Shall we read the request to the bit bucket and
1542            * return an error reponse or just return an error reponse
1543            * and terminate?  Needs some testing.  */
1544           break;
1545         }
1546
1547       /* Read request.  */
1548       request = xtrymalloc (nrequest);
1549       if (!request)
1550         {
1551           err = gpg_error_from_syserror ();
1552           log_error ("error reading request: Not enough memory for %zu MiB)\n",
1553                      (size_t)nrequest / (1024*1024));
1554           /* FIXME: See comment above.  */
1555           break;
1556         }
1557       if (es_read (es_stdin, request, nrequest, &n))
1558         {
1559           err = gpg_error_from_syserror ();
1560           log_error ("error reading request: %s\n", gpg_strerror (err));
1561           break;
1562         }
1563       if (n != nrequest)
1564         {
1565           /* That is a protocol violation.  */
1566           xfree (response);
1567           response = error_object_string ("Invalid request:"
1568                                           " short read (%zu of %zu bytes)\n",
1569                                           n, (size_t)nrequest);
1570         }
1571       else /* Process request  */
1572         {
1573           if (opt_debug)
1574             log_debug ("request='%s'\n", request);
1575           xfree (response);
1576           response = process_request (request);
1577           if (opt_debug)
1578             log_debug ("response='%s'\n", response);
1579         }
1580       nresponse = strlen (response);
1581
1582       /* Write response */
1583       if (es_write (es_stdout, &nresponse, sizeof nresponse, &n))
1584         {
1585           err = gpg_error_from_syserror ();
1586           log_error ("error writing request header: %s\n", gpg_strerror (err));
1587           break;
1588         }
1589       if (n != sizeof nrequest)
1590         {
1591           log_error ("error writing request header: short write\n");
1592           break;
1593         }
1594       if (es_write (es_stdout, response, nresponse, &n))
1595         {
1596           err = gpg_error_from_syserror ();
1597           log_error ("error writing request: %s\n", gpg_strerror (err));
1598           break;
1599         }
1600       if (n != nresponse)
1601         {
1602           log_error ("error writing request: short write\n");
1603           break;
1604         }
1605       if (es_fflush (es_stdout) || es_ferror (es_stdout))
1606         {
1607           err = gpg_error_from_syserror ();
1608           log_error ("error writing request: %s\n", gpg_strerror (err));
1609           break;
1610         }
1611     }
1612
1613   xfree (response);
1614   xfree (request);
1615 }
1616
1617
1618 \f
1619 static const char *
1620 my_strusage( int level )
1621 {
1622   const char *p;
1623
1624   switch (level)
1625     {
1626     case  9: p = "LGPL-2.1-or-later"; break;
1627     case 11: p = "gpgme-json"; break;
1628     case 13: p = PACKAGE_VERSION; break;
1629     case 14: p = "Copyright (C) 2018 g10 Code GmbH"; break;
1630     case 19: p = "Please report bugs to <" PACKAGE_BUGREPORT ">.\n"; break;
1631     case 1:
1632     case 40:
1633       p = "Usage: gpgme-json [OPTIONS]";
1634       break;
1635     case 41:
1636       p = "Native messaging based GPGME operations.\n";
1637       break;
1638     case 42:
1639       p = "1"; /* Flag print 40 as part of 41. */
1640       break;
1641     default: p = NULL; break;
1642     }
1643   return p;
1644 }
1645
1646 int
1647 main (int argc, char *argv[])
1648 {
1649 #if GPGRT_VERSION_NUMBER < 0x011d00 /* 1.29 */
1650
1651   fprintf (stderr, "WARNING: Old libgpg-error - using limited mode\n");
1652   native_messaging_repl ();
1653
1654 #else /* This is a modern libgp-error.  */
1655
1656   enum { CMD_DEFAULT     = 0,
1657          CMD_INTERACTIVE = 'i',
1658          CMD_SINGLE      = 's',
1659          CMD_LIBVERSION  = 501,
1660   } cmd = CMD_DEFAULT;
1661   enum {
1662     OPT_DEBUG = 600
1663   };
1664
1665   static gpgrt_opt_t opts[] = {
1666     ARGPARSE_c  (CMD_INTERACTIVE, "interactive", "Interactive REPL"),
1667     ARGPARSE_c  (CMD_SINGLE,      "single",      "Single request mode"),
1668     ARGPARSE_c  (CMD_LIBVERSION,  "lib-version", "Show library version"),
1669     ARGPARSE_s_n(OPT_DEBUG,       "debug",       "Flyswatter"),
1670
1671     ARGPARSE_end()
1672   };
1673   gpgrt_argparse_t pargs = { &argc, &argv};
1674
1675   gpgrt_set_strusage (my_strusage);
1676
1677 #ifdef HAVE_SETLOCALE
1678   setlocale (LC_ALL, "");
1679 #endif
1680   gpgme_check_version (NULL);
1681 #ifdef LC_CTYPE
1682   gpgme_set_locale (NULL, LC_CTYPE, setlocale (LC_CTYPE, NULL));
1683 #endif
1684 #ifdef LC_MESSAGES
1685   gpgme_set_locale (NULL, LC_MESSAGES, setlocale (LC_MESSAGES, NULL));
1686 #endif
1687
1688   while (gpgrt_argparse (NULL, &pargs, opts))
1689     {
1690       switch (pargs.r_opt)
1691         {
1692         case CMD_INTERACTIVE:
1693           opt_interactive = 1;
1694           /* Fall trough.  */
1695         case CMD_SINGLE:
1696         case CMD_LIBVERSION:
1697           cmd = pargs.r_opt;
1698           break;
1699
1700         case OPT_DEBUG: opt_debug = 1; break;
1701
1702         default:
1703           pargs.err = ARGPARSE_PRINT_WARNING;
1704           break;
1705         }
1706     }
1707   gpgrt_argparse (NULL, &pargs, NULL);
1708
1709   if (!opt_debug)
1710     {
1711       const char *s = getenv ("GPGME_JSON_DEBUG");
1712       if (s && atoi (s) > 0)
1713         opt_debug = 1;
1714     }
1715
1716   if (opt_debug)
1717     {
1718       const char *home = getenv ("HOME");
1719       char *file = xstrconcat ("socket://",
1720                                home? home:"/tmp",
1721                                "/.gnupg/S.gpgme-json.log", NULL);
1722       log_set_file (file);
1723       xfree (file);
1724     }
1725
1726   if (opt_debug)
1727     { int i;
1728       for (i=0; argv[i]; i++)
1729         log_debug ("argv[%d]='%s'\n", i, argv[i]);
1730     }
1731
1732   switch (cmd)
1733     {
1734     case CMD_DEFAULT:
1735       native_messaging_repl ();
1736       break;
1737
1738     case CMD_SINGLE:
1739       read_and_process_single_request ();
1740       break;
1741
1742     case CMD_INTERACTIVE:
1743       interactive_repl ();
1744       break;
1745
1746     case CMD_LIBVERSION:
1747       printf ("Version from header: %s (0x%06x)\n",
1748               GPGME_VERSION, GPGME_VERSION_NUMBER);
1749       printf ("Version from binary: %s\n", gpgme_check_version (NULL));
1750       printf ("Copyright blurb ...:%s\n", gpgme_check_version ("\x01\x01"));
1751       break;
1752     }
1753
1754   if (opt_debug)
1755     log_debug ("ready");
1756
1757 #endif /* This is a modern libgp-error.  */
1758   return 0;
1759 }
1760 #endif /* libgpg-error >= 1.28 */