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