json: Improve auto-base64 encoding to not split UTF-8 chars.
[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   const char *s;
614   size_t buflen, n;
615   int c;
616
617   if (!base64 || base64 == -1) /* Make sure that we really have a string.  */
618     gpgme_data_write (data, "", 1);
619
620   buffer = gpgme_data_release_and_get_mem (data, &buflen);
621   data = NULL;
622   if (!buffer)
623     {
624       err = gpg_error_from_syserror ();
625       goto leave;
626     }
627
628   if (base64 == -1)
629     {
630       base64 = 0;
631       if (!buflen)
632         log_fatal ("Appended Nul byte got lost\n");
633       /* Figure out if there is any Nul octet in the buffer.  In that
634        * case we need to Base-64 the buffer.  Due to problems with the
635        * browser's Javascript we use Base-64 also in case an UTF-8
636        * character is in the buffer.  This is because the chunking may
637        * split an UTF-8 characters and JS can't handle this.  */
638       for (s=buffer, n=0; n < buflen -1; s++, n++)
639         if (!*s || (*s & 0x80))
640           {
641             buflen--; /* Adjust for the extra nul byte.  */
642             base64 = 1;
643             break;
644           }
645     }
646
647   /* Adjust the chunksize if we need to do base64 conversion.  */
648   if (base64)
649     chunksize = (chunksize / 4) * 3;
650
651   xjson_AddStringToObject (result, "type", type);
652   xjson_AddBoolToObject (result, "base64", base64);
653
654   if (buflen > chunksize)
655     {
656       xjson_AddBoolToObject (result, "more", 1);
657
658       c = buffer[chunksize];
659       buffer[chunksize] = 0;
660       if (base64)
661         err = add_base64_to_object (result, "data", buffer, chunksize);
662       else
663         err = cjson_AddStringToObject (result, "data", buffer);
664       buffer[chunksize] = c;
665       if (err)
666         goto leave;
667
668       pending_data.buffer = buffer;
669       buffer = NULL;
670       pending_data.length = buflen;
671       pending_data.written = chunksize;
672       pending_data.type = type;
673       pending_data.base64 = base64;
674     }
675   else
676     {
677       if (base64)
678         err = add_base64_to_object (result, "data", buffer, buflen);
679       else
680         err = cjson_AddStringToObject (result, "data", buffer);
681     }
682
683  leave:
684   gpgme_free (buffer);
685   return err;
686 }
687
688
689 \f
690 static const char hlp_encrypt[] =
691   "op:     \"encrypt\"\n"
692   "keys:   Array of strings with the fingerprints or user-ids\n"
693   "        of the keys to encrypt the data.  For a single key\n"
694   "        a String may be used instead of an array.\n"
695   "data:   Input data. \n"
696   "\n"
697   "Optional parameters:\n"
698   "protocol:      Either \"openpgp\" (default) or \"cms\".\n"
699   "chunksize:     Max number of bytes in the resulting \"data\".\n"
700   "\n"
701   "Optional boolean flags (default is false):\n"
702   "base64:        Input data is base64 encoded.\n"
703   "mime:          Indicate that data is a MIME object.\n"
704   "armor:         Request output in armored format.\n"
705   "always-trust:  Request --always-trust option.\n"
706   "no-encrypt-to: Do not use a default recipient.\n"
707   "no-compress:   Do not compress the plaintext first.\n"
708   "throw-keyids:  Request the --throw-keyids option.\n"
709   "want-address:  Require that the keys include a mail address.\n"
710   "wrap:          Assume the input is an OpenPGP message.\n"
711   "\n"
712   "Response on success:\n"
713   "type:   \"ciphertext\"\n"
714   "data:   Unless armor mode is used a Base64 encoded binary\n"
715   "        ciphertext.  In armor mode a string with an armored\n"
716   "        OpenPGP or a PEM message.\n"
717   "base64: Boolean indicating whether data is base64 encoded.\n"
718   "more:   Optional boolean indicating that \"getmore\" is required.";
719 static gpg_error_t
720 op_encrypt (cjson_t request, cjson_t result)
721 {
722   gpg_error_t err;
723   gpgme_ctx_t ctx = NULL;
724   gpgme_protocol_t protocol;
725   size_t chunksize;
726   int opt_base64;
727   int opt_mime;
728   char *keystring = NULL;
729   cjson_t j_input;
730   gpgme_data_t input = NULL;
731   gpgme_data_t output = NULL;
732   int abool;
733   gpgme_encrypt_flags_t encrypt_flags = 0;
734
735   if ((err = get_protocol (request, &protocol)))
736     goto leave;
737   ctx = get_context (protocol);
738   if ((err = get_chunksize (request, &chunksize)))
739     goto leave;
740
741   if ((err = get_boolean_flag (request, "base64", 0, &opt_base64)))
742     goto leave;
743   if ((err = get_boolean_flag (request, "mime", 0, &opt_mime)))
744     goto leave;
745
746   if ((err = get_boolean_flag (request, "armor", 0, &abool)))
747     goto leave;
748   gpgme_set_armor (ctx, abool);
749   if ((err = get_boolean_flag (request, "always-trust", 0, &abool)))
750     goto leave;
751   if (abool)
752     encrypt_flags |= GPGME_ENCRYPT_ALWAYS_TRUST;
753   if ((err = get_boolean_flag (request, "no-encrypt-to", 0,&abool)))
754     goto leave;
755   if (abool)
756     encrypt_flags |= GPGME_ENCRYPT_NO_ENCRYPT_TO;
757   if ((err = get_boolean_flag (request, "no-compress", 0, &abool)))
758     goto leave;
759   if (abool)
760     encrypt_flags |= GPGME_ENCRYPT_NO_COMPRESS;
761   if ((err = get_boolean_flag (request, "throw-keyids", 0, &abool)))
762     goto leave;
763   if (abool)
764     encrypt_flags |= GPGME_ENCRYPT_THROW_KEYIDS;
765   if ((err = get_boolean_flag (request, "wrap", 0, &abool)))
766     goto leave;
767   if (abool)
768     encrypt_flags |= GPGME_ENCRYPT_WRAP;
769   if ((err = get_boolean_flag (request, "want-address", 0, &abool)))
770     goto leave;
771   if (abool)
772     encrypt_flags |= GPGME_ENCRYPT_WANT_ADDRESS;
773
774
775   /* Get the keys.  */
776   err = get_keys (request, &keystring);
777   if (err)
778     {
779       /* Provide a custom error response.  */
780       error_object (result, "Error getting keys: %s", gpg_strerror (err));
781       goto leave;
782     }
783
784   /* Get the data.  Note that INPUT is a shallow data object with the
785    * storage hold in REQUEST.  */
786   j_input = cJSON_GetObjectItem (request, "data");
787   if (!j_input)
788     {
789       err = gpg_error (GPG_ERR_NO_DATA);
790       goto leave;
791     }
792   if (!cjson_is_string (j_input))
793     {
794       err = gpg_error (GPG_ERR_INV_VALUE);
795       goto leave;
796     }
797   if (opt_base64)
798     {
799       err = data_from_base64_string (&input, j_input);
800       if (err)
801         {
802           error_object (result, "Error decoding Base-64 encoded 'data': %s",
803                         gpg_strerror (err));
804           goto leave;
805         }
806     }
807   else
808     {
809       err = gpgme_data_new_from_mem (&input, j_input->valuestring,
810                                      strlen (j_input->valuestring), 0);
811       if (err)
812         {
813           error_object (result, "Error getting 'data': %s", gpg_strerror (err));
814           goto leave;
815         }
816     }
817   if (opt_mime)
818     gpgme_data_set_encoding (input, GPGME_DATA_ENCODING_MIME);
819
820
821   /* Create an output data object.  */
822   err = gpgme_data_new (&output);
823   if (err)
824     {
825       error_object (result, "Error creating output data object: %s",
826                     gpg_strerror (err));
827       goto leave;
828     }
829
830   /* Encrypt.  */
831   err = gpgme_op_encrypt_ext (ctx, NULL, keystring, encrypt_flags,
832                               input, output);
833   /* encrypt_result = gpgme_op_encrypt_result (ctx); */
834   if (err)
835     {
836       error_object (result, "Encryption failed: %s", gpg_strerror (err));
837       goto leave;
838     }
839   gpgme_data_release (input);
840   input = NULL;
841
842   /* We need to base64 if armoring has not been requested.  */
843   err = make_data_object (result, output, chunksize,
844                           "ciphertext", !gpgme_get_armor (ctx));
845   output = NULL;
846
847  leave:
848   xfree (keystring);
849   release_context (ctx);
850   gpgme_data_release (input);
851   gpgme_data_release (output);
852   return err;
853 }
854
855
856 \f
857 static const char hlp_decrypt[] =
858   "op:     \"decrypt\"\n"
859   "data:   The encrypted data.\n"
860   "\n"
861   "Optional parameters:\n"
862   "protocol:      Either \"openpgp\" (default) or \"cms\".\n"
863   "chunksize:     Max number of bytes in the resulting \"data\".\n"
864   "\n"
865   "Optional boolean flags (default is false):\n"
866   "base64:        Input data is base64 encoded.\n"
867   "\n"
868   "Response on success:\n"
869   "type:   \"plaintext\"\n"
870   "data:   The decrypted data.  This may be base64 encoded.\n"
871   "base64: Boolean indicating whether data is base64 encoded.\n"
872   "mime:   A Boolean indicating whether the data is a MIME object.\n"
873   "info:   An optional object with extra information.\n"
874   "more:   Optional boolean indicating that \"getmore\" is required.";
875 static gpg_error_t
876 op_decrypt (cjson_t request, cjson_t result)
877 {
878   gpg_error_t err;
879   gpgme_ctx_t ctx = NULL;
880   gpgme_protocol_t protocol;
881   size_t chunksize;
882   int opt_base64;
883   cjson_t j_input;
884   gpgme_data_t input = NULL;
885   gpgme_data_t output = NULL;
886   gpgme_decrypt_result_t decrypt_result;
887
888   if ((err = get_protocol (request, &protocol)))
889     goto leave;
890   ctx = get_context (protocol);
891   if ((err = get_chunksize (request, &chunksize)))
892     goto leave;
893
894   if ((err = get_boolean_flag (request, "base64", 0, &opt_base64)))
895     goto leave;
896
897   /* Get the data.  Note that INPUT is a shallow data object with the
898    * storage hold in REQUEST.  */
899   j_input = cJSON_GetObjectItem (request, "data");
900   if (!j_input)
901     {
902       err = gpg_error (GPG_ERR_NO_DATA);
903       goto leave;
904     }
905   if (!cjson_is_string (j_input))
906     {
907       err = gpg_error (GPG_ERR_INV_VALUE);
908       goto leave;
909     }
910   if (opt_base64)
911     {
912       err = data_from_base64_string (&input, j_input);
913       if (err)
914         {
915           error_object (result, "Error decoding Base-64 encoded 'data': %s",
916                         gpg_strerror (err));
917           goto leave;
918         }
919     }
920   else
921     {
922       err = gpgme_data_new_from_mem (&input, j_input->valuestring,
923                                      strlen (j_input->valuestring), 0);
924       if (err)
925         {
926           error_object (result, "Error getting 'data': %s", gpg_strerror (err));
927           goto leave;
928         }
929     }
930
931   /* Create an output data object.  */
932   err = gpgme_data_new (&output);
933   if (err)
934     {
935       error_object (result, "Error creating output data object: %s",
936                     gpg_strerror (err));
937       goto leave;
938     }
939
940   /* Decrypt.  */
941   err = gpgme_op_decrypt_ext (ctx, GPGME_DECRYPT_VERIFY,
942                               input, output);
943   decrypt_result = gpgme_op_decrypt_result (ctx);
944   if (err)
945     {
946       error_object (result, "Decryption failed: %s", gpg_strerror (err));
947       goto leave;
948     }
949   gpgme_data_release (input);
950   input = NULL;
951
952   if (decrypt_result->is_mime)
953     xjson_AddBoolToObject (result, "mime", 1);
954
955   err = make_data_object (result, output, chunksize, "plaintext", -1);
956   output = NULL;
957
958  leave:
959   release_context (ctx);
960   gpgme_data_release (input);
961   gpgme_data_release (output);
962   return err;
963 }
964
965
966 \f
967 static const char hlp_getmore[] =
968   "op:     \"getmore\"\n"
969   "\n"
970   "Optional parameters:\n"
971   "chunksize:  Max number of bytes in the \"data\" object.\n"
972   "\n"
973   "Response on success:\n"
974   "type:       Type of the pending data\n"
975   "data:       The next chunk of data\n"
976   "base64:     Boolean indicating whether data is base64 encoded\n"
977   "more:       Optional boolean requesting another \"getmore\".";
978 static gpg_error_t
979 op_getmore (cjson_t request, cjson_t result)
980 {
981   gpg_error_t err;
982   int c;
983   size_t n;
984   size_t chunksize;
985
986   if ((err = get_chunksize (request, &chunksize)))
987     goto leave;
988
989   /* Adjust the chunksize if we need to do base64 conversion.  */
990   if (pending_data.base64)
991     chunksize = (chunksize / 4) * 3;
992
993   /* Do we have anything pending?  */
994   if (!pending_data.buffer)
995     {
996       err = gpg_error (GPG_ERR_NO_DATA);
997       error_object (result, "Operation not possible: %s", gpg_strerror (err));
998       goto leave;
999     }
1000
1001   xjson_AddStringToObject (result, "type", pending_data.type);
1002   xjson_AddBoolToObject (result, "base64", pending_data.base64);
1003
1004   if (pending_data.written >= pending_data.length)
1005     {
1006       /* EOF reached.  This should not happen but we return an empty
1007        * string once in case of client errors.  */
1008       gpgme_free (pending_data.buffer);
1009       pending_data.buffer = NULL;
1010       xjson_AddBoolToObject (result, "more", 0);
1011       err = cjson_AddStringToObject (result, "data", "");
1012     }
1013   else
1014     {
1015       n = pending_data.length - pending_data.written;
1016       if (n > chunksize)
1017         {
1018           n = chunksize;
1019           xjson_AddBoolToObject (result, "more", 1);
1020         }
1021       else
1022         xjson_AddBoolToObject (result, "more", 0);
1023
1024       c = pending_data.buffer[pending_data.written + n];
1025       pending_data.buffer[pending_data.written + n] = 0;
1026       if (pending_data.base64)
1027         err = add_base64_to_object (result, "data",
1028                                     (pending_data.buffer
1029                                      + pending_data.written), n);
1030       else
1031         err = cjson_AddStringToObject (result, "data",
1032                                        (pending_data.buffer
1033                                         + pending_data.written));
1034       pending_data.buffer[pending_data.written + n] = c;
1035       if (!err)
1036         {
1037           pending_data.written += n;
1038           if (pending_data.written >= pending_data.length)
1039             {
1040               gpgme_free (pending_data.buffer);
1041               pending_data.buffer = NULL;
1042             }
1043         }
1044     }
1045
1046  leave:
1047   return err;
1048 }
1049
1050
1051 \f
1052 static const char hlp_help[] =
1053   "The tool expects a JSON object with the request and responds with\n"
1054   "another JSON object.  Even on error a JSON object is returned.  The\n"
1055   "property \"op\" is mandatory and its string value selects the\n"
1056   "operation; if the property \"help\" with the value \"true\" exists, the\n"
1057   "operation is not performned but a string with the documentation\n"
1058   "returned.  To list all operations it is allowed to leave out \"op\" in\n"
1059   "help mode.  Supported values for \"op\" are:\n\n"
1060   "  encrypt     Encrypt data.\n"
1061   "  getmore     Retrieve remaining data.\n"
1062   "  help        Help overview.";
1063 static gpg_error_t
1064 op_help (cjson_t request, cjson_t result)
1065 {
1066   cjson_t j_tmp;
1067   char *buffer = NULL;
1068   const char *msg;
1069
1070   j_tmp = cJSON_GetObjectItem (request, "interactive_help");
1071   if (opt_interactive && j_tmp && cjson_is_string (j_tmp))
1072     msg = buffer = xstrconcat (hlp_help, "\n", j_tmp->valuestring, NULL);
1073   else
1074     msg = hlp_help;
1075
1076   xjson_AddStringToObject (result, "type", "help");
1077   xjson_AddStringToObject (result, "msg", msg);
1078
1079   xfree (buffer);
1080   return 0;
1081 }
1082
1083
1084 \f
1085 /*
1086  * Dispatcher
1087  */
1088
1089 /* Process a request and return the response.  The response is a newly
1090  * allocated string or NULL in case of an error.  */
1091 static char *
1092 process_request (const char *request)
1093 {
1094   static struct {
1095     const char *op;
1096     gpg_error_t (*handler)(cjson_t request, cjson_t result);
1097     const char * const helpstr;
1098   } optbl[] = {
1099     { "encrypt", op_encrypt, hlp_encrypt },
1100     { "decrypt", op_decrypt, hlp_decrypt },
1101     { "getmore", op_getmore, hlp_getmore },
1102     { "help",    op_help,    hlp_help },
1103     { NULL }
1104   };
1105   size_t erroff;
1106   cjson_t json;
1107   cjson_t j_tmp, j_op;
1108   cjson_t response;
1109   int helpmode;
1110   const char *op;
1111   char *res;
1112   int idx;
1113
1114   response = xjson_CreateObject ();
1115
1116   json = cJSON_Parse (request, &erroff);
1117   if (!json)
1118     {
1119       log_string (GPGRT_LOGLVL_INFO, request);
1120       log_info ("invalid JSON object at offset %zu\n", erroff);
1121       error_object (response, "invalid JSON object at offset %zu\n", erroff);
1122       goto leave;
1123     }
1124
1125   j_tmp = cJSON_GetObjectItem (json, "help");
1126   helpmode = (j_tmp && cjson_is_true (j_tmp));
1127
1128   j_op = cJSON_GetObjectItem (json, "op");
1129   if (!j_op || !cjson_is_string (j_op))
1130     {
1131       if (!helpmode)
1132         {
1133           error_object (response, "Property \"op\" missing");
1134           goto leave;
1135         }
1136       op = "help";  /* Help summary.  */
1137     }
1138   else
1139     op = j_op->valuestring;
1140
1141   for (idx=0; optbl[idx].op; idx++)
1142     if (!strcmp (op, optbl[idx].op))
1143       break;
1144   if (optbl[idx].op)
1145     {
1146       if (helpmode && strcmp (op, "help"))
1147         {
1148           xjson_AddStringToObject (response, "type", "help");
1149           xjson_AddStringToObject (response, "op", op);
1150           xjson_AddStringToObject (response, "msg", optbl[idx].helpstr);
1151         }
1152       else
1153         {
1154           gpg_error_t err;
1155
1156           /* If this is not the "getmore" command and we have any
1157            * pending data release that data.  */
1158           if (pending_data.buffer && optbl[idx].handler != op_getmore)
1159             {
1160               gpgme_free (pending_data.buffer);
1161               pending_data.buffer = NULL;
1162             }
1163
1164           err = optbl[idx].handler (json, response);
1165           if (err)
1166             {
1167               if (!(j_tmp = cJSON_GetObjectItem (response, "type"))
1168                   || !cjson_is_string (j_tmp)
1169                   || strcmp (j_tmp->valuestring, "error"))
1170                 {
1171                   /* No error type response - provide a generic one.  */
1172                   error_object (response, "Operation failed: %s",
1173                                 gpg_strerror (err));
1174                 }
1175
1176               xjson_AddStringToObject (response, "op", op);
1177             }
1178         }
1179     }
1180   else  /* Operation not supported.  */
1181     {
1182       error_object (response, "Unknown operation '%s'", op);
1183       xjson_AddStringToObject (response, "op", op);
1184     }
1185
1186  leave:
1187   cJSON_Delete (json);
1188   if (opt_interactive)
1189     res = cJSON_Print (response);
1190   else
1191     res = cJSON_PrintUnformatted (response);
1192   if (!res)
1193     log_error ("Printing JSON data failed\n");
1194   cJSON_Delete (response);
1195   return res;
1196 }
1197
1198
1199 \f
1200 /*
1201  *  Driver code
1202  */
1203
1204 static char *
1205 get_file (const char *fname)
1206 {
1207   gpg_error_t err;
1208   estream_t fp;
1209   struct stat st;
1210   char *buf;
1211   size_t buflen;
1212
1213   fp = es_fopen (fname, "r");
1214   if (!fp)
1215     {
1216       err = gpg_error_from_syserror ();
1217       log_error ("can't open '%s': %s\n", fname, gpg_strerror (err));
1218       return NULL;
1219     }
1220
1221   if (fstat (es_fileno(fp), &st))
1222     {
1223       err = gpg_error_from_syserror ();
1224       log_error ("can't stat '%s': %s\n", fname, gpg_strerror (err));
1225       es_fclose (fp);
1226       return NULL;
1227     }
1228
1229   buflen = st.st_size;
1230   buf = xmalloc (buflen+1);
1231   if (es_fread (buf, buflen, 1, fp) != 1)
1232     {
1233       err = gpg_error_from_syserror ();
1234       log_error ("error reading '%s': %s\n", fname, gpg_strerror (err));
1235       es_fclose (fp);
1236       xfree (buf);
1237       return NULL;
1238     }
1239   buf[buflen] = 0;
1240   es_fclose (fp);
1241
1242   return buf;
1243 }
1244
1245
1246 /* Return a malloced line or NULL on EOF.  Terminate on read
1247  * error.  */
1248 static char *
1249 get_line (void)
1250 {
1251   char *line = NULL;
1252   size_t linesize = 0;
1253   gpg_error_t err;
1254   size_t maxlength = 2048;
1255   int n;
1256   const char *s;
1257   char *p;
1258
1259  again:
1260   n = es_read_line (es_stdin, &line, &linesize, &maxlength);
1261   if (n < 0)
1262     {
1263       err = gpg_error_from_syserror ();
1264       log_error ("error reading line: %s\n", gpg_strerror (err));
1265       exit (1);
1266     }
1267   if (!n)
1268     {
1269       xfree (line);
1270       line = NULL;
1271       return NULL;  /* EOF */
1272     }
1273   if (!maxlength)
1274     {
1275       log_info ("line too long - skipped\n");
1276       goto again;
1277     }
1278   if (memchr (line, 0, n))
1279     log_info ("warning: line shortened due to embedded Nul character\n");
1280
1281   if (line[n-1] == '\n')
1282     line[n-1] = 0;
1283
1284   /* Trim leading spaces.  */
1285   for (s=line; spacep (s); s++)
1286     ;
1287   if (s != line)
1288     {
1289       for (p=line; *s;)
1290         *p++ = *s++;
1291       *p = 0;
1292       n = p - line;
1293     }
1294
1295   return line;
1296 }
1297
1298
1299 /* Process meta commands used with the standard REPL.  */
1300 static char *
1301 process_meta_commands (const char *request)
1302 {
1303   char *result = NULL;
1304
1305   while (spacep (request))
1306     request++;
1307
1308   if (!strncmp (request, "help", 4) && (spacep (request+4) || !request[4]))
1309     {
1310       if (request[4])
1311         {
1312           char *buf = xstrconcat ("{ \"help\":true, \"op\":\"", request+5,
1313                                   "\" }", NULL);
1314           result = process_request (buf);
1315           xfree (buf);
1316         }
1317       else
1318         result = process_request ("{ \"op\": \"help\","
1319                                   " \"interactive_help\": "
1320                                   "\"\\nMeta commands:\\n"
1321                                   "  ,read FNAME Process data from FILE\\n"
1322                                   "  ,help CMD   Print help for a command\\n"
1323                                   "  ,quit       Terminate process\""
1324                                   "}");
1325     }
1326   else if (!strncmp (request, "quit", 4) && (spacep (request+4) || !request[4]))
1327     exit (0);
1328   else if (!strncmp (request, "read", 4) && (spacep (request+4) || !request[4]))
1329     {
1330       if (!request[4])
1331         log_info ("usage: ,read FILENAME\n");
1332       else
1333         {
1334           char *buffer = get_file (request + 5);
1335           if (buffer)
1336             {
1337               result = process_request (buffer);
1338               xfree (buffer);
1339             }
1340         }
1341     }
1342   else
1343     log_info ("invalid meta command\n");
1344
1345   return result;
1346 }
1347
1348
1349 /* If STRING has a help response, return the MSG property in a human
1350  * readable format.  */
1351 static char *
1352 get_help_msg (const char *string)
1353 {
1354   cjson_t json, j_type, j_msg;
1355   const char *msg;
1356   char *buffer = NULL;
1357   char *p;
1358
1359   json = cJSON_Parse (string, NULL);
1360   if (json)
1361     {
1362       j_type = cJSON_GetObjectItem (json, "type");
1363       if (j_type && cjson_is_string (j_type)
1364           && !strcmp (j_type->valuestring, "help"))
1365         {
1366           j_msg = cJSON_GetObjectItem (json, "msg");
1367           if (j_msg || cjson_is_string (j_msg))
1368             {
1369               msg = j_msg->valuestring;
1370               buffer = malloc (strlen (msg)+1);
1371               if (buffer)
1372                 {
1373                   for (p=buffer; *msg; msg++)
1374                     {
1375                       if (*msg == '\\' && msg[1] == '\n')
1376                         *p++ = '\n';
1377                       else
1378                         *p++ = *msg;
1379                     }
1380                   *p = 0;
1381                 }
1382             }
1383         }
1384       cJSON_Delete (json);
1385     }
1386   return buffer;
1387 }
1388
1389
1390 /* An interactive standard REPL.  */
1391 static void
1392 interactive_repl (void)
1393 {
1394   char *line = NULL;
1395   char *request = NULL;
1396   char *response = NULL;
1397   char *p;
1398   int first;
1399
1400   es_setvbuf (es_stdin, NULL, _IONBF, 0);
1401 #if GPGRT_VERSION_NUMBER >= 0x011d00 /* 1.29 */
1402   es_fprintf (es_stderr, "%s %s ready (enter \",help\" for help)\n",
1403               gpgrt_strusage (11), gpgrt_strusage (13));
1404 #endif
1405   do
1406     {
1407       es_fputs ("> ", es_stderr);
1408       es_fflush (es_stderr);
1409       es_fflush (es_stdout);
1410       xfree (line);
1411       line = get_line ();
1412       es_fflush (es_stderr);
1413       es_fflush (es_stdout);
1414
1415       first = !request;
1416       if (line && *line)
1417         {
1418           if (!request)
1419             request = xstrdup (line);
1420           else
1421             request = xstrconcat (request, "\n", line, NULL);
1422         }
1423
1424       if (!line)
1425         es_fputs ("\n", es_stderr);
1426
1427       if (!line || !*line || (first && *request == ','))
1428         {
1429           /* Process the input.  */
1430           xfree (response);
1431           response = NULL;
1432           if (request && *request == ',')
1433             {
1434               response = process_meta_commands (request+1);
1435             }
1436           else if (request)
1437             {
1438               response = process_request (request);
1439             }
1440           xfree (request);
1441           request = NULL;
1442
1443           if (response)
1444             {
1445               if (opt_interactive)
1446                 {
1447                   char *msg = get_help_msg (response);
1448                   if (msg)
1449                     {
1450                       xfree (response);
1451                       response = msg;
1452                     }
1453                 }
1454
1455               es_fputs ("===> ", es_stderr);
1456               es_fflush (es_stderr);
1457               for (p=response; *p; p++)
1458                 {
1459                   if (*p == '\n')
1460                     {
1461                       es_fflush (es_stdout);
1462                       es_fputs ("\n===> ", es_stderr);
1463                       es_fflush (es_stderr);
1464                     }
1465                   else
1466                     es_putc (*p, es_stdout);
1467                 }
1468               es_fflush (es_stdout);
1469               es_fputs ("\n", es_stderr);
1470             }
1471         }
1472     }
1473   while (line);
1474
1475   xfree (request);
1476   xfree (response);
1477   xfree (line);
1478 }
1479
1480
1481 /* Read and process a single request.  */
1482 static void
1483 read_and_process_single_request (void)
1484 {
1485   char *line = NULL;
1486   char *request = NULL;
1487   char *response = NULL;
1488   size_t n;
1489
1490   for (;;)
1491     {
1492       xfree (line);
1493       line = get_line ();
1494       if (line && *line)
1495         request = (request? xstrconcat (request, "\n", line, NULL)
1496                    /**/   : xstrdup (line));
1497       if (!line)
1498         {
1499           if (request)
1500             {
1501               xfree (response);
1502               response = process_request (request);
1503               if (response)
1504                 {
1505                   es_fputs (response, es_stdout);
1506                   if ((n = strlen (response)) && response[n-1] != '\n')
1507                     es_fputc ('\n', es_stdout);
1508                 }
1509               es_fflush (es_stdout);
1510             }
1511           break;
1512         }
1513     }
1514
1515   xfree (response);
1516   xfree (request);
1517   xfree (line);
1518 }
1519
1520
1521 /* The Native Messaging processing loop.  */
1522 static void
1523 native_messaging_repl (void)
1524 {
1525   gpg_error_t err;
1526   uint32_t nrequest, nresponse;
1527   char *request = NULL;
1528   char *response = NULL;
1529   size_t n;
1530
1531   /* Due to the length octets we need to switch the I/O stream into
1532    * binary mode.  */
1533   es_set_binary (es_stdin);
1534   es_set_binary (es_stdout);
1535   es_setbuf (es_stdin, NULL);  /* stdin needs to be unbuffered! */
1536
1537   for (;;)
1538     {
1539       /* Read length.  Note that the protocol uses native endianess.
1540        * Is it allowed to call such a thing a well thought out
1541        * protocol?  */
1542       if (es_read (es_stdin, &nrequest, sizeof nrequest, &n))
1543         {
1544           err = gpg_error_from_syserror ();
1545           log_error ("error reading request header: %s\n", gpg_strerror (err));
1546           break;
1547         }
1548       if (!n)
1549         break;  /* EOF */
1550       if (n != sizeof nrequest)
1551         {
1552           log_error ("error reading request header: short read\n");
1553           break;
1554         }
1555       if (nrequest > MAX_REQUEST_SIZE)
1556         {
1557           log_error ("error reading request: request too long (%zu MiB)\n",
1558                      (size_t)nrequest / (1024*1024));
1559           /* Fixme: Shall we read the request to the bit bucket and
1560            * return an error reponse or just return an error reponse
1561            * and terminate?  Needs some testing.  */
1562           break;
1563         }
1564
1565       /* Read request.  */
1566       request = xtrymalloc (nrequest);
1567       if (!request)
1568         {
1569           err = gpg_error_from_syserror ();
1570           log_error ("error reading request: Not enough memory for %zu MiB)\n",
1571                      (size_t)nrequest / (1024*1024));
1572           /* FIXME: See comment above.  */
1573           break;
1574         }
1575       if (es_read (es_stdin, request, nrequest, &n))
1576         {
1577           err = gpg_error_from_syserror ();
1578           log_error ("error reading request: %s\n", gpg_strerror (err));
1579           break;
1580         }
1581       if (n != nrequest)
1582         {
1583           /* That is a protocol violation.  */
1584           xfree (response);
1585           response = error_object_string ("Invalid request:"
1586                                           " short read (%zu of %zu bytes)\n",
1587                                           n, (size_t)nrequest);
1588         }
1589       else /* Process request  */
1590         {
1591           if (opt_debug)
1592             log_debug ("request='%s'\n", request);
1593           xfree (response);
1594           response = process_request (request);
1595           if (opt_debug)
1596             log_debug ("response='%s'\n", response);
1597         }
1598       nresponse = strlen (response);
1599
1600       /* Write response */
1601       if (es_write (es_stdout, &nresponse, sizeof nresponse, &n))
1602         {
1603           err = gpg_error_from_syserror ();
1604           log_error ("error writing request header: %s\n", gpg_strerror (err));
1605           break;
1606         }
1607       if (n != sizeof nrequest)
1608         {
1609           log_error ("error writing request header: short write\n");
1610           break;
1611         }
1612       if (es_write (es_stdout, response, nresponse, &n))
1613         {
1614           err = gpg_error_from_syserror ();
1615           log_error ("error writing request: %s\n", gpg_strerror (err));
1616           break;
1617         }
1618       if (n != nresponse)
1619         {
1620           log_error ("error writing request: short write\n");
1621           break;
1622         }
1623       if (es_fflush (es_stdout) || es_ferror (es_stdout))
1624         {
1625           err = gpg_error_from_syserror ();
1626           log_error ("error writing request: %s\n", gpg_strerror (err));
1627           break;
1628         }
1629     }
1630
1631   xfree (response);
1632   xfree (request);
1633 }
1634
1635
1636 \f
1637 static const char *
1638 my_strusage( int level )
1639 {
1640   const char *p;
1641
1642   switch (level)
1643     {
1644     case  9: p = "LGPL-2.1-or-later"; break;
1645     case 11: p = "gpgme-json"; break;
1646     case 13: p = PACKAGE_VERSION; break;
1647     case 14: p = "Copyright (C) 2018 g10 Code GmbH"; break;
1648     case 19: p = "Please report bugs to <" PACKAGE_BUGREPORT ">.\n"; break;
1649     case 1:
1650     case 40:
1651       p = "Usage: gpgme-json [OPTIONS]";
1652       break;
1653     case 41:
1654       p = "Native messaging based GPGME operations.\n";
1655       break;
1656     case 42:
1657       p = "1"; /* Flag print 40 as part of 41. */
1658       break;
1659     default: p = NULL; break;
1660     }
1661   return p;
1662 }
1663
1664 int
1665 main (int argc, char *argv[])
1666 {
1667 #if GPGRT_VERSION_NUMBER < 0x011d00 /* 1.29 */
1668
1669   fprintf (stderr, "WARNING: Old libgpg-error - using limited mode\n");
1670   native_messaging_repl ();
1671
1672 #else /* This is a modern libgp-error.  */
1673
1674   enum { CMD_DEFAULT     = 0,
1675          CMD_INTERACTIVE = 'i',
1676          CMD_SINGLE      = 's',
1677          CMD_LIBVERSION  = 501,
1678   } cmd = CMD_DEFAULT;
1679   enum {
1680     OPT_DEBUG = 600
1681   };
1682
1683   static gpgrt_opt_t opts[] = {
1684     ARGPARSE_c  (CMD_INTERACTIVE, "interactive", "Interactive REPL"),
1685     ARGPARSE_c  (CMD_SINGLE,      "single",      "Single request mode"),
1686     ARGPARSE_c  (CMD_LIBVERSION,  "lib-version", "Show library version"),
1687     ARGPARSE_s_n(OPT_DEBUG,       "debug",       "Flyswatter"),
1688
1689     ARGPARSE_end()
1690   };
1691   gpgrt_argparse_t pargs = { &argc, &argv};
1692
1693   gpgrt_set_strusage (my_strusage);
1694
1695 #ifdef HAVE_SETLOCALE
1696   setlocale (LC_ALL, "");
1697 #endif
1698   gpgme_check_version (NULL);
1699 #ifdef LC_CTYPE
1700   gpgme_set_locale (NULL, LC_CTYPE, setlocale (LC_CTYPE, NULL));
1701 #endif
1702 #ifdef LC_MESSAGES
1703   gpgme_set_locale (NULL, LC_MESSAGES, setlocale (LC_MESSAGES, NULL));
1704 #endif
1705
1706   while (gpgrt_argparse (NULL, &pargs, opts))
1707     {
1708       switch (pargs.r_opt)
1709         {
1710         case CMD_INTERACTIVE:
1711           opt_interactive = 1;
1712           /* Fall trough.  */
1713         case CMD_SINGLE:
1714         case CMD_LIBVERSION:
1715           cmd = pargs.r_opt;
1716           break;
1717
1718         case OPT_DEBUG: opt_debug = 1; break;
1719
1720         default:
1721           pargs.err = ARGPARSE_PRINT_WARNING;
1722           break;
1723         }
1724     }
1725   gpgrt_argparse (NULL, &pargs, NULL);
1726
1727   if (!opt_debug)
1728     {
1729       const char *s = getenv ("GPGME_JSON_DEBUG");
1730       if (s && atoi (s) > 0)
1731         opt_debug = 1;
1732     }
1733
1734   if (opt_debug)
1735     {
1736       const char *home = getenv ("HOME");
1737       char *file = xstrconcat ("socket://",
1738                                home? home:"/tmp",
1739                                "/.gnupg/S.gpgme-json.log", NULL);
1740       log_set_file (file);
1741       xfree (file);
1742     }
1743
1744   if (opt_debug)
1745     { int i;
1746       for (i=0; argv[i]; i++)
1747         log_debug ("argv[%d]='%s'\n", i, argv[i]);
1748     }
1749
1750   switch (cmd)
1751     {
1752     case CMD_DEFAULT:
1753       native_messaging_repl ();
1754       break;
1755
1756     case CMD_SINGLE:
1757       read_and_process_single_request ();
1758       break;
1759
1760     case CMD_INTERACTIVE:
1761       interactive_repl ();
1762       break;
1763
1764     case CMD_LIBVERSION:
1765       printf ("Version from header: %s (0x%06x)\n",
1766               GPGME_VERSION, GPGME_VERSION_NUMBER);
1767       printf ("Version from binary: %s\n", gpgme_check_version (NULL));
1768       printf ("Copyright blurb ...:%s\n", gpgme_check_version ("\x01\x01"));
1769       break;
1770     }
1771
1772   if (opt_debug)
1773     log_debug ("ready");
1774
1775 #endif /* This is a modern libgp-error.  */
1776   return 0;
1777 }
1778 #endif /* libgpg-error >= 1.28 */