core: New encryption flag GPGME_ENCRYPT_WANT_ADDRESS.
[gpgme.git] / src / gpgme-json.c
1 /* gpgme-json.c - JSON based interface to gpgme (server)
2  * Copyright (C) 2018 g10 Code GmbH
3  *
4  * This file is part of GPGME.
5  *
6  * GPGME is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU Lesser General Public License as
8  * published by the Free Software Foundation; either version 2.1 of
9  * the License, or (at your option) any later version.
10  *
11  * GPGME is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this program; if not, see <http://www.gnu.org/licenses/>.
18  * SPDX-License-Identifier: LGPL-2.1+
19  */
20
21 /* This is tool implements the Native Messaging protocol of web
22  * browsers and provides the server part of it.  A Javascript based
23  * client can be found in lang/javascript.  The used data format is
24  * similar to the API of openpgpjs.
25  */
26
27 #include <config.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <stdarg.h>
32 #ifdef HAVE_LOCALE_H
33 #include <locale.h>
34 #endif
35 #include <stdint.h>
36
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
52 static void xoutofcore (const char *type) GPGRT_ATTR_NORETURN;
53 static cjson_t error_object_v (cjson_t json, const char *message,
54                               va_list arg_ptr) GPGRT_ATTR_PRINTF(2,0);
55 static cjson_t error_object (cjson_t json, const char *message,
56                             ...) GPGRT_ATTR_PRINTF(2,3);
57 static char *error_object_string (const char *message,
58                                   ...) GPGRT_ATTR_PRINTF(1,2);
59
60
61 /* True if interactive mode is active.  */
62 static int opt_interactive;
63 /* True is debug mode is active.  */
64 static int opt_debug;
65
66
67 /*
68  * Helper functions and macros
69  */
70
71 #define xtrymalloc(a)  gpgrt_malloc ((a))
72 #define xtrystrdup(a)  gpgrt_strdup ((a))
73 #define xmalloc(a) ({                           \
74       void *_r = gpgrt_malloc ((a));            \
75       if (!_r)                                  \
76         xoutofcore ("malloc");                  \
77       _r; })
78 #define xcalloc(a,b) ({                         \
79       void *_r = gpgrt_calloc ((a), (b));       \
80       if (!_r)                                  \
81         xoutofcore ("calloc");                  \
82       _r; })
83 #define xstrdup(a) ({                           \
84       char *_r = gpgrt_strdup ((a));            \
85       if (!_r)                                  \
86         xoutofcore ("strdup");                  \
87       _r; })
88 #define xstrconcat(a, ...) ({                           \
89       char *_r = gpgrt_strconcat ((a), __VA_ARGS__);    \
90       if (!_r)                                          \
91         xoutofcore ("strconcat");                       \
92       _r; })
93 #define xfree(a) gpgrt_free ((a))
94
95 #define spacep(p)   (*(p) == ' ' || *(p) == '\t')
96
97
98 static void
99 xoutofcore (const char *type)
100 {
101   gpg_error_t err = gpg_error_from_syserror ();
102   log_error ("%s failed: %s\n", type, gpg_strerror (err));
103   exit (2);
104 }
105
106
107 /* Call cJSON_CreateObject but terminate in case of an error.  */
108 static cjson_t
109 xjson_CreateObject (void)
110 {
111   cjson_t json = cJSON_CreateObject ();
112   if (!json)
113     xoutofcore ("cJSON_CreateObject");
114   return json;
115 }
116
117
118 /* Wrapper around cJSON_AddStringToObject which returns an gpg-error
119  * code instead of the NULL or the new object.  */
120 static gpg_error_t
121 cjson_AddStringToObject (cjson_t object, const char *name, const char *string)
122 {
123   if (!cJSON_AddStringToObject (object, name, string))
124     return gpg_error_from_syserror ();
125   return 0;
126 }
127
128
129 /* Same as cjson_AddStringToObject but prints an error message and
130  * terminates the process.  */
131 static void
132 xjson_AddStringToObject (cjson_t object, const char *name, const char *string)
133 {
134   if (!cJSON_AddStringToObject (object, name, string))
135     xoutofcore ("cJSON_AddStringToObject");
136 }
137
138
139 /* Wrapper around cJSON_AddBoolToObject which terminates the process
140  * in case of an error.  */
141 static void
142 xjson_AddBoolToObject (cjson_t object, const char *name, int abool)
143 {
144   if (!cJSON_AddBoolToObject (object, name, abool))
145     xoutofcore ("cJSON_AddStringToObject");
146   return ;
147 }
148
149 /* This is similar to cJSON_AddStringToObject but takes a gpgme DATA
150  * object and adds it under NAME as a base 64 encoded string to
151  * OBJECT.  Ownership of DATA is transferred to this function.  */
152 static gpg_error_t
153 add_base64_to_object (cjson_t object, const char *name, gpgme_data_t data)
154 {
155 #if GPGRT_VERSION_NUMBER < 0x011d00 /* 1.29 */
156   return gpg_error (GPG_ERR_NOT_SUPPORTED);
157 #else
158   gpg_err_code_t err;
159   estream_t fp = NULL;
160   gpgrt_b64state_t state = NULL;
161   cjson_t j_str = NULL;
162   void *buffer = NULL;
163   size_t buflen;
164
165   fp = es_fopenmem (0, "rwb");
166   if (!fp)
167     {
168       err = gpg_err_code_from_syserror ();
169       goto leave;
170     }
171   state = gpgrt_b64enc_start (fp, "");
172   if (!state)
173     {
174       err = gpg_err_code_from_syserror ();
175       goto leave;
176     }
177
178   gpgme_data_write (data, "", 1); /* Make sure we have  a string.  */
179   buffer = gpgme_data_release_and_get_mem (data, &buflen);
180   data = NULL;
181   if (!buffer)
182     {
183       err = gpg_error_from_syserror ();
184       goto leave;
185     }
186
187   err = gpgrt_b64enc_write (state, buffer, buflen);
188   if (err)
189     goto leave;
190   xfree (buffer);
191   buffer = NULL;
192
193   err = gpgrt_b64enc_finish (state);
194   state = NULL;
195   if (err)
196     return err;
197
198   es_fputc (0, fp);
199   if (es_fclose_snatch (fp, &buffer, NULL))
200     {
201       fp = NULL;
202       err = gpg_error_from_syserror ();
203       goto leave;
204     }
205   fp = NULL;
206
207   j_str = cJSON_CreateStringConvey (buffer);
208   if (!j_str)
209     {
210       err = gpg_error_from_syserror ();
211       goto leave;
212     }
213   buffer = NULL;
214
215   if (!cJSON_AddItemToObject (object, name, j_str))
216     {
217       err = gpg_error_from_syserror ();
218       cJSON_Delete (j_str);
219       j_str = NULL;
220       goto leave;
221     }
222   j_str = NULL;
223
224  leave:
225   xfree (buffer);
226   cJSON_Delete (j_str);
227   gpgrt_b64enc_finish (state);
228   es_fclose (fp);
229   gpgme_data_release (data);
230   return err;
231 #endif
232 }
233
234
235 /* Create a JSON error object.  If JSON is not NULL the error message
236  * is appended to that object.  An existing "type" item will be replaced. */
237 static cjson_t
238 error_object_v (cjson_t json, const char *message, va_list arg_ptr)
239 {
240   cjson_t response, j_tmp;
241   char *msg;
242
243   msg = gpgrt_vbsprintf (message, arg_ptr);
244   if (!msg)
245     xoutofcore ("error_object");
246
247   response = json? json : xjson_CreateObject ();
248
249   if (!(j_tmp = cJSON_GetObjectItem (response, "type")))
250     xjson_AddStringToObject (response, "type", "error");
251   else /* Replace existing "type".  */
252     {
253       j_tmp = cJSON_CreateString ("error");
254       if (!j_tmp)
255         xoutofcore ("cJSON_CreateString");
256       cJSON_ReplaceItemInObject (response, "type", j_tmp);
257      }
258   xjson_AddStringToObject (response, "msg", msg);
259
260   xfree (msg);
261   return response;
262 }
263
264
265 /* Call cJSON_Print but terminate in case of an error.  */
266 static char *
267 xjson_Print (cjson_t object)
268 {
269   char *buf;
270   buf = cJSON_Print (object);
271   if (!buf)
272     xoutofcore ("cJSON_Print");
273   return buf;
274 }
275
276
277 static cjson_t
278 error_object (cjson_t json, const char *message, ...)
279 {
280   cjson_t response;
281   va_list arg_ptr;
282
283   va_start (arg_ptr, message);
284   response = error_object_v (json, message, arg_ptr);
285   va_end (arg_ptr);
286   return response;
287 }
288
289
290 static char *
291 error_object_string (const char *message, ...)
292 {
293   cjson_t response;
294   va_list arg_ptr;
295   char *msg;
296
297   va_start (arg_ptr, message);
298   response = error_object_v (NULL, message, arg_ptr);
299   va_end (arg_ptr);
300
301   msg = xjson_Print (response);
302   cJSON_Delete (response);
303   return msg;
304 }
305
306
307 /* Get the boolean property NAME from the JSON object and store true
308  * or valse at R_VALUE.  If the name is unknown the value of DEF_VALUE
309  * is returned.  If the type of the value is not boolean,
310  * GPG_ERR_INV_VALUE is returned and R_VALUE set to DEF_VALUE.  */
311 static gpg_error_t
312 get_boolean_flag (cjson_t json, const char *name, int def_value, int *r_value)
313 {
314   cjson_t j_item;
315
316   j_item = cJSON_GetObjectItem (json, name);
317   if (!j_item)
318     *r_value = def_value;
319   else if (cjson_is_true (j_item))
320     *r_value = 1;
321   else if (cjson_is_false (j_item))
322     *r_value = 0;
323   else
324     {
325       *r_value = def_value;
326       return gpg_error (GPG_ERR_INV_VALUE);
327     }
328
329   return 0;
330 }
331
332
333 /* Get the boolean property PROTOCOL from the JSON object and store
334  * its value at R_PROTOCOL.  The default is OpenPGP.  */
335 static gpg_error_t
336 get_protocol (cjson_t json, gpgme_protocol_t *r_protocol)
337 {
338   cjson_t j_item;
339
340   *r_protocol = GPGME_PROTOCOL_OpenPGP;
341   j_item = cJSON_GetObjectItem (json, "protocol");
342   if (!j_item)
343     ;
344   else if (!cjson_is_string (j_item))
345     return gpg_error (GPG_ERR_INV_VALUE);
346   else if (!strcmp(j_item->valuestring, "openpgp"))
347     ;
348   else if (!strcmp(j_item->valuestring, "cms"))
349     *r_protocol = GPGME_PROTOCOL_CMS;
350   else
351     return gpg_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
352
353   return 0;
354 }
355
356
357 /* Extract the keys from the "keys" array in the JSON object.  On
358  * success a string with the keys identifiers is stored at R_KEYS.
359  * The keys in that string are LF delimited.  On failure an error code
360  * is returned.  */
361 static gpg_error_t
362 get_keys (cjson_t json, char **r_keystring)
363 {
364   cjson_t j_keys, j_item;
365   int i, nkeys;
366   char *p;
367   size_t length;
368
369   *r_keystring = NULL;
370
371   j_keys = cJSON_GetObjectItem (json, "keys");
372   if (!j_keys)
373     return gpg_error (GPG_ERR_NO_KEY);
374   if (!cjson_is_array (j_keys) && !cjson_is_string (j_keys))
375     return gpg_error (GPG_ERR_INV_VALUE);
376
377   /* Fixme: We should better use a membuf like thing.  */
378   length = 1; /* For the EOS.  */
379   if (cjson_is_string (j_keys))
380     {
381       nkeys = 1;
382       length += strlen (j_keys->valuestring);
383       if (strchr (j_keys->valuestring, '\n'))
384         return gpg_error (GPG_ERR_INV_USER_ID);
385     }
386   else
387     {
388       nkeys = cJSON_GetArraySize (j_keys);
389       if (!nkeys)
390         return gpg_error (GPG_ERR_NO_KEY);
391       for (i=0; i < nkeys; i++)
392         {
393           j_item = cJSON_GetArrayItem (j_keys, i);
394           if (!j_item || !cjson_is_string (j_item))
395             return gpg_error (GPG_ERR_INV_VALUE);
396           if (i)
397             length++; /* Space for delimiter. */
398           length += strlen (j_item->valuestring);
399           if (strchr (j_item->valuestring, '\n'))
400             return gpg_error (GPG_ERR_INV_USER_ID);
401         }
402     }
403
404   p = *r_keystring = xtrymalloc (length);
405   if (!p)
406     return gpg_error_from_syserror ();
407
408   if (cjson_is_string (j_keys))
409     {
410       strcpy (p, j_keys->valuestring);
411     }
412   else
413     {
414       for (i=0; i < nkeys; i++)
415         {
416           j_item = cJSON_GetArrayItem (j_keys, i);
417           if (i)
418             *p++ = '\n'; /* Add delimiter.  */
419           p = stpcpy (p, j_item->valuestring);
420         }
421     }
422   return 0;
423 }
424
425
426
427 \f
428 /*
429  *  GPGME support functions.
430  */
431
432 /* Helper for get_context.  */
433 static gpgme_ctx_t
434 _create_new_context (gpgme_protocol_t proto)
435 {
436   gpg_error_t err;
437   gpgme_ctx_t ctx;
438
439   err = gpgme_new (&ctx);
440   if (err)
441     log_fatal ("error creating GPGME context: %s\n", gpg_strerror (err));
442   gpgme_set_protocol (ctx, proto);
443   gpgme_set_ctx_flag (ctx, "request-origin", "browser");
444   return ctx;
445 }
446
447
448 /* Return a context object for protocol PROTO.  This is currently a
449  * statuically allocated context initialized for PROTO.  Termnates
450  * process on failure.  */
451 static gpgme_ctx_t
452 get_context (gpgme_protocol_t proto)
453 {
454   static gpgme_ctx_t ctx_openpgp, ctx_cms;
455
456   if (proto == GPGME_PROTOCOL_OpenPGP)
457     {
458       if (!ctx_openpgp)
459         ctx_openpgp = _create_new_context (proto);
460       return ctx_openpgp;
461     }
462   else if (proto == GPGME_PROTOCOL_CMS)
463     {
464       if (!ctx_cms)
465         ctx_cms = _create_new_context (proto);
466       return ctx_cms;
467     }
468   else
469     log_bug ("invalid protocol %d requested\n", proto);
470 }
471
472
473
474 /* Free context object retrieved by get_context.  */
475 static void
476 release_context (gpgme_ctx_t ctx)
477 {
478   /* Nothing to do right now.  */
479   (void)ctx;
480 }
481
482
483
484 /* Given a Base-64 encoded string object in JSON return a gpgme data
485  * object at R_DATA.  */
486 static gpg_error_t
487 data_from_base64_string (gpgme_data_t *r_data, cjson_t json)
488 {
489 #if GPGRT_VERSION_NUMBER < 0x011d00 /* 1.29 */
490   *r_data = NULL;
491   return gpg_error (GPG_ERR_NOT_SUPPORTED);
492 #else
493   gpg_error_t err;
494   size_t len;
495   char *buf = NULL;
496   gpgrt_b64state_t state = NULL;
497   gpgme_data_t data = NULL;
498
499   *r_data = NULL;
500
501   /* A quick check on the JSON.  */
502   if (!cjson_is_string (json))
503     {
504       err = gpg_error (GPG_ERR_INV_VALUE);
505       goto leave;
506     }
507
508   state = gpgrt_b64dec_start (NULL);
509   if (!state)
510     {
511       err = gpg_err_code_from_syserror ();
512       goto leave;
513     }
514
515   /* Fixme: Data duplication - we should see how to snatch the memory
516    * from the json object.  */
517   len = strlen (json->valuestring);
518   buf = xtrystrdup (json->valuestring);
519   if (!buf)
520     {
521       err = gpg_error_from_syserror ();
522       goto leave;
523     }
524
525   err = gpgrt_b64dec_proc (state, buf, len, &len);
526   if (err)
527     goto leave;
528
529   err = gpgrt_b64dec_finish (state);
530   state = NULL;
531   if (err)
532     goto leave;
533
534   err = gpgme_data_new_from_mem (&data, buf, len, 1);
535   if (err)
536     goto leave;
537   *r_data = data;
538   data = NULL;
539
540  leave:
541   xfree (data);
542   xfree (buf);
543   gpgrt_b64dec_finish (state);
544   return err;
545 #endif
546 }
547
548
549 \f
550 /*
551  * Implementaion of the commands.
552  */
553
554
555 static const char hlp_encrypt[] =
556   "op:     \"encrypt\"\n"
557   "keys:   Array of strings with the fingerprints or user-ids\n"
558   "        of the keys to encrypt the data.  For a single key\n"
559   "        a String may be used instead of an array.\n"
560   "data:   Input data. \n"
561   "\n"
562   "Optional parameters:\n"
563   "protocol:      Either \"openpgp\" (default) or \"cms\".\n"
564   "\n"
565   "Optional boolean flags (default is false):\n"
566   "base64:        Input data is base64 encoded.\n"
567   "armor:         Request output in armored format.\n"
568   "always-trust:  Request --always-trust option.\n"
569   "no-encrypt-to: Do not use a default recipient.\n"
570   "no-compress:   Do not compress the plaintext first.\n"
571   "throw-keyids:  Request the --throw-keyids option.\n"
572   "want-address:  Require that the keys include a mail address.\n"
573   "wrap:          Assume the input is an OpenPGP message.\n"
574   "\n"
575   "Response on success:\n"
576   "type:   \"ciphertext\"\n"
577   "data:   Unless armor mode is used a Base64 encoded binary\n"
578   "        ciphertext.  In armor mode a string with an armored\n"
579   "        OpenPGP or a PEM message.\n"
580   "base64: Boolean indicating whether data is base64 encoded.";
581 static gpg_error_t
582 op_encrypt (cjson_t request, cjson_t result)
583 {
584   gpg_error_t err;
585   gpgme_ctx_t ctx = NULL;
586   gpgme_protocol_t protocol;
587   int opt_base64;
588   char *keystring = NULL;
589   cjson_t j_input;
590   gpgme_data_t input = NULL;
591   gpgme_data_t output = NULL;
592   int abool;
593   gpgme_encrypt_flags_t encrypt_flags = 0;
594
595   if ((err = get_protocol (request, &protocol)))
596     goto leave;
597   ctx = get_context (protocol);
598
599   if ((err = get_boolean_flag (request, "base64", 0, &opt_base64)))
600     goto leave;
601
602   if ((err = get_boolean_flag (request, "armor", 0, &abool)))
603     goto leave;
604   gpgme_set_armor (ctx, abool);
605   if ((err = get_boolean_flag (request, "always-trust", 0, &abool)))
606     goto leave;
607   if (abool)
608     encrypt_flags |= GPGME_ENCRYPT_ALWAYS_TRUST;
609   if ((err = get_boolean_flag (request, "no-encrypt-to", 0,&abool)))
610     goto leave;
611   if (abool)
612     encrypt_flags |= GPGME_ENCRYPT_NO_ENCRYPT_TO;
613   if ((err = get_boolean_flag (request, "no-compress", 0, &abool)))
614     goto leave;
615   if (abool)
616     encrypt_flags |= GPGME_ENCRYPT_NO_COMPRESS;
617   if ((err = get_boolean_flag (request, "throw-keyids", 0, &abool)))
618     goto leave;
619   if (abool)
620     encrypt_flags |= GPGME_ENCRYPT_THROW_KEYIDS;
621   if ((err = get_boolean_flag (request, "wrap", 0, &abool)))
622     goto leave;
623   if (abool)
624     encrypt_flags |= GPGME_ENCRYPT_WRAP;
625   if ((err = get_boolean_flag (request, "want-address", 0, &abool)))
626     goto leave;
627   if (abool)
628     encrypt_flags |= GPGME_ENCRYPT_WANT_ADDRESS;
629
630
631   /* Get the keys.  */
632   err = get_keys (request, &keystring);
633   if (err)
634     {
635       /* Provide a custom error response.  */
636       error_object (result, "Error getting keys: %s", gpg_strerror (err));
637       goto leave;
638     }
639
640   /* Get the data.  Note that INPUT is a shallow data object with the
641    * storage hold in REQUEST.  */
642   j_input = cJSON_GetObjectItem (request, "data");
643   if (!j_input)
644     {
645       err = gpg_error (GPG_ERR_NO_DATA);
646       goto leave;
647     }
648   if (!cjson_is_string (j_input))
649     {
650       err = gpg_error (GPG_ERR_INV_VALUE);
651       goto leave;
652     }
653   if (opt_base64)
654     {
655       err = data_from_base64_string (&input, j_input);
656       if (err)
657         {
658           error_object (result, "Error decoding Base-64 encoded 'data': %s",
659                         gpg_strerror (err));
660           goto leave;
661         }
662     }
663   else
664     {
665       err = gpgme_data_new_from_mem (&input, j_input->valuestring,
666                                      strlen (j_input->valuestring), 0);
667       if (err)
668         {
669           error_object (result, "Error getting 'data': %s", gpg_strerror (err));
670           goto leave;
671         }
672     }
673
674   /* Create an output data object.  */
675   err = gpgme_data_new (&output);
676   if (err)
677     {
678       error_object (result, "Error creating output data object: %s",
679                     gpg_strerror (err));
680       goto leave;
681     }
682
683   /* Encrypt.  */
684   err = gpgme_op_encrypt_ext (ctx, NULL, keystring, encrypt_flags,
685                               input, output);
686   /* encrypt_result = gpgme_op_encrypt_result (ctx); */
687   if (err)
688     {
689       error_object (result, "Encryption failed: %s", gpg_strerror (err));
690       goto leave;
691     }
692   gpgme_data_release (input);
693   input = NULL;
694
695   xjson_AddStringToObject (result, "type", "ciphertext");
696   /* If armoring is used we do not need to base64 the output.  */
697   xjson_AddBoolToObject (result, "base64", !gpgme_get_armor (ctx));
698   if (gpgme_get_armor (ctx))
699     {
700       char *buffer;
701
702       /* Make sure that we really have a string.  */
703       gpgme_data_write (output, "", 1);
704       buffer = gpgme_data_release_and_get_mem (output, NULL);
705       if (!buffer)
706         {
707           err = gpg_error_from_syserror ();
708           goto leave;
709         }
710       err = cjson_AddStringToObject (result, "data", buffer);
711       gpgme_free (buffer);
712       if (err)
713         goto leave;
714     }
715   else
716     {
717       err = add_base64_to_object (result, "data", output);
718       output = NULL;
719       if (err)
720         goto leave;
721     }
722
723  leave:
724   xfree (keystring);
725   release_context (ctx);
726   gpgme_data_release (input);
727   return err;
728 }
729
730
731
732 static const char hlp_help[] =
733   "The tool expects a JSON object with the request and responds with\n"
734   "another JSON object.  Even on error a JSON object is returned.  The\n"
735   "property \"op\" is mandatory and its string value selects the\n"
736   "operation; if the property \"help\" with the value \"true\" exists, the\n"
737   "operation is not performned but a string with the documentation\n"
738   "returned.  To list all operations it is allowed to leave out \"op\" in\n"
739   "help mode.  Supported values for \"op\" are:\n\n"
740   "  encrypt     Encrypt data.\n"
741   "  help        Help overview.";
742 static gpg_error_t
743 op_help (cjson_t request, cjson_t result)
744 {
745   cjson_t j_tmp;
746   char *buffer = NULL;
747   const char *msg;
748
749   j_tmp = cJSON_GetObjectItem (request, "interactive_help");
750   if (opt_interactive && j_tmp && cjson_is_string (j_tmp))
751     msg = buffer = xstrconcat (hlp_help, "\n", j_tmp->valuestring, NULL);
752   else
753     msg = hlp_help;
754
755   xjson_AddStringToObject (result, "type", "help");
756   xjson_AddStringToObject (result, "msg", msg);
757
758   xfree (buffer);
759   return 0;
760 }
761
762
763
764 /* Process a request and return the response.  The response is a newly
765  * allocated string or NULL in case of an error.  */
766 static char *
767 process_request (const char *request)
768 {
769   static struct {
770     const char *op;
771     gpg_error_t (*handler)(cjson_t request, cjson_t result);
772     const char * const helpstr;
773   } optbl[] = {
774     { "encrypt", op_encrypt, hlp_encrypt },
775
776
777     { "help",    op_help,    hlp_help },
778     { NULL }
779   };
780   size_t erroff;
781   cjson_t json;
782   cjson_t j_tmp, j_op;
783   cjson_t response;
784   int helpmode;
785   const char *op;
786   char *res;
787   int idx;
788
789   response = xjson_CreateObject ();
790
791   json = cJSON_Parse (request, &erroff);
792   if (!json)
793     {
794       log_string (GPGRT_LOGLVL_INFO, request);
795       log_info ("invalid JSON object at offset %zu\n", erroff);
796       error_object (response, "invalid JSON object at offset %zu\n", erroff);
797       goto leave;
798     }
799
800   j_tmp = cJSON_GetObjectItem (json, "help");
801   helpmode = (j_tmp && cjson_is_true (j_tmp));
802
803   j_op = cJSON_GetObjectItem (json, "op");
804   if (!j_op || !cjson_is_string (j_op))
805     {
806       if (!helpmode)
807         {
808           error_object (response, "Property \"op\" missing");
809           goto leave;
810         }
811       op = "help";  /* Help summary.  */
812     }
813   else
814     op = j_op->valuestring;
815
816   for (idx=0; optbl[idx].op; idx++)
817     if (!strcmp (op, optbl[idx].op))
818       break;
819   if (optbl[idx].op)
820     {
821       if (helpmode && strcmp (op, "help"))
822         {
823           xjson_AddStringToObject (response, "type", "help");
824           xjson_AddStringToObject (response, "op", op);
825           xjson_AddStringToObject (response, "msg", optbl[idx].helpstr);
826         }
827       else
828         {
829           gpg_error_t err;
830
831           err = optbl[idx].handler (json, response);
832           if (err)
833             {
834               if (!(j_tmp = cJSON_GetObjectItem (response, "type"))
835                   || !cjson_is_string (j_tmp)
836                   || strcmp (j_tmp->valuestring, "error"))
837                 {
838                   /* No error type response - provide a generic one.  */
839                   error_object (response, "Operation failed: %s",
840                                 gpg_strerror (err));
841                 }
842
843               xjson_AddStringToObject (response, "op", op);
844             }
845
846         }
847     }
848   else  /* Operation not supported.  */
849     {
850       error_object (response, "Unknown operation '%s'", op);
851       xjson_AddStringToObject (response, "op", op);
852     }
853
854  leave:
855   cJSON_Delete (json);
856   if (opt_interactive)
857     res = cJSON_Print (response);
858   else
859     res = cJSON_PrintUnformatted (response);
860   if (!res)
861     log_error ("Printing JSON data failed\n");
862   cJSON_Delete (response);
863   return res;
864 }
865
866
867 \f
868 /*
869  *  Driver code
870  */
871
872 /* Return a malloced line or NULL on EOF.  Terminate on read
873  * error.  */
874 static char *
875 get_line (void)
876 {
877   char *line = NULL;
878   size_t linesize = 0;
879   gpg_error_t err;
880   size_t maxlength = 2048;
881   int n;
882   const char *s;
883   char *p;
884
885  again:
886   n = es_read_line (es_stdin, &line, &linesize, &maxlength);
887   if (n < 0)
888     {
889       err = gpg_error_from_syserror ();
890       log_error ("error reading line: %s\n", gpg_strerror (err));
891       exit (1);
892     }
893   if (!n)
894     {
895       xfree (line);
896       line = NULL;
897       return NULL;  /* EOF */
898     }
899   if (!maxlength)
900     {
901       log_info ("line too long - skipped\n");
902       goto again;
903     }
904   if (memchr (line, 0, n))
905     log_info ("warning: line shortened due to embedded Nul character\n");
906
907   if (line[n-1] == '\n')
908     line[n-1] = 0;
909
910   /* Trim leading spaces.  */
911   for (s=line; spacep (s); s++)
912     ;
913   if (s != line)
914     {
915       for (p=line; *s;)
916         *p++ = *s++;
917       *p = 0;
918       n = p - line;
919     }
920
921   return line;
922 }
923
924
925 /* Process meta commands used with the standard REPL.  */
926 static char *
927 process_meta_commands (const char *request)
928 {
929   char *result = NULL;
930
931   while (spacep (request))
932     request++;
933
934   if (!strncmp (request, "help", 4) && (spacep (request+4) || !request[4]))
935     result = process_request ("{ \"op\": \"help\","
936                               " \"interactive_help\": "
937                               "\"\\nMeta commands:\\n"
938                               "  ,help       This help\\n"
939                               "  ,quit       Terminate process\""
940                               "}");
941   else if (!strncmp (request, "quit", 4) && (spacep (request+4) || !request[4]))
942     exit (0);
943   else
944     log_info ("invalid meta command\n");
945
946   return result;
947 }
948
949
950 /* If STRING has a help response, return the MSG property in a human
951  * readable format.  */
952 static char *
953 get_help_msg (const char *string)
954 {
955   cjson_t json, j_type, j_msg;
956   const char *msg;
957   char *buffer = NULL;
958   char *p;
959
960   json = cJSON_Parse (string, NULL);
961   if (json)
962     {
963       j_type = cJSON_GetObjectItem (json, "type");
964       if (j_type && cjson_is_string (j_type)
965           && !strcmp (j_type->valuestring, "help"))
966         {
967           j_msg = cJSON_GetObjectItem (json, "msg");
968           if (j_msg || cjson_is_string (j_msg))
969             {
970               msg = j_msg->valuestring;
971               buffer = malloc (strlen (msg)+1);
972               if (buffer)
973                 {
974                   for (p=buffer; *msg; msg++)
975                     {
976                       if (*msg == '\\' && msg[1] == '\n')
977                         *p++ = '\n';
978                       else
979                         *p++ = *msg;
980                     }
981                   *p = 0;
982                 }
983             }
984         }
985       cJSON_Delete (json);
986     }
987   return buffer;
988 }
989
990
991 /* An interactive standard REPL.  */
992 static void
993 interactive_repl (void)
994 {
995   char *line = NULL;
996   char *request = NULL;
997   char *response = NULL;
998   char *p;
999   int first;
1000
1001   es_setvbuf (es_stdin, NULL, _IONBF, 0);
1002 #if GPGRT_VERSION_NUMBER >= 0x011d00 /* 1.29 */
1003   es_fprintf (es_stderr, "%s %s ready (enter \",help\" for help)\n",
1004               gpgrt_strusage (11), gpgrt_strusage (13));
1005 #endif
1006   do
1007     {
1008       es_fputs ("> ", es_stderr);
1009       es_fflush (es_stderr);
1010       es_fflush (es_stdout);
1011       xfree (line);
1012       line = get_line ();
1013       es_fflush (es_stderr);
1014       es_fflush (es_stdout);
1015
1016       first = !request;
1017       if (line && *line)
1018         {
1019           if (!request)
1020             request = xstrdup (line);
1021           else
1022             request = xstrconcat (request, "\n", line, NULL);
1023         }
1024
1025       if (!line)
1026         es_fputs ("\n", es_stderr);
1027
1028       if (!line || !*line || (first && *request == ','))
1029         {
1030           /* Process the input.  */
1031           xfree (response);
1032           response = NULL;
1033           if (request && *request == ',')
1034             {
1035               response = process_meta_commands (request+1);
1036             }
1037           else if (request)
1038             {
1039               response = process_request (request);
1040             }
1041           xfree (request);
1042           request = NULL;
1043
1044           if (response)
1045             {
1046               if (opt_interactive)
1047                 {
1048                   char *msg = get_help_msg (response);
1049                   if (msg)
1050                     {
1051                       xfree (response);
1052                       response = msg;
1053                     }
1054                 }
1055
1056               es_fputs ("===> ", es_stderr);
1057               es_fflush (es_stderr);
1058               for (p=response; *p; p++)
1059                 {
1060                   if (*p == '\n')
1061                     {
1062                       es_fflush (es_stdout);
1063                       es_fputs ("\n===> ", es_stderr);
1064                       es_fflush (es_stderr);
1065                     }
1066                   else
1067                     es_putc (*p, es_stdout);
1068                 }
1069               es_fflush (es_stdout);
1070               es_fputs ("\n", es_stderr);
1071             }
1072         }
1073     }
1074   while (line);
1075
1076   xfree (request);
1077   xfree (response);
1078   xfree (line);
1079 }
1080
1081
1082 /* Read and process a single request.  */
1083 static void
1084 read_and_process_single_request (void)
1085 {
1086   char *line = NULL;
1087   char *request = NULL;
1088   char *response = NULL;
1089   size_t n;
1090
1091   for (;;)
1092     {
1093       xfree (line);
1094       line = get_line ();
1095       if (line && *line)
1096         request = (request? xstrconcat (request, "\n", line, NULL)
1097                    /**/   : xstrdup (line));
1098       if (!line)
1099         {
1100           if (request)
1101             {
1102               xfree (response);
1103               response = process_request (request);
1104               if (response)
1105                 {
1106                   es_fputs (response, es_stdout);
1107                   if ((n = strlen (response)) && response[n-1] != '\n')
1108                     es_fputc ('\n', es_stdout);
1109                 }
1110               es_fflush (es_stdout);
1111             }
1112           break;
1113         }
1114     }
1115
1116   xfree (response);
1117   xfree (request);
1118   xfree (line);
1119 }
1120
1121
1122 /* The Native Messaging processing loop.  */
1123 static void
1124 native_messaging_repl (void)
1125 {
1126   gpg_error_t err;
1127   uint32_t nrequest, nresponse;
1128   char *request = NULL;
1129   char *response = NULL;
1130   size_t n;
1131
1132   /* Due to the length octets we need to switch the I/O stream into
1133    * binary mode.  */
1134   es_set_binary (es_stdin);
1135   es_set_binary (es_stdout);
1136   es_setbuf (es_stdin, NULL);  /* stdin needs to be unbuffered! */
1137
1138   for (;;)
1139     {
1140       /* Read length.  Note that the protocol uses native endianess.
1141        * Is it allowed to call such a thing a well thought out
1142        * protocol?  */
1143       if (es_read (es_stdin, &nrequest, sizeof nrequest, &n))
1144         {
1145           err = gpg_error_from_syserror ();
1146           log_error ("error reading request header: %s\n", gpg_strerror (err));
1147           break;
1148         }
1149       if (!n)
1150         break;  /* EOF */
1151       if (n != sizeof nrequest)
1152         {
1153           log_error ("error reading request header: short read\n");
1154           break;
1155         }
1156       if (nrequest > MAX_REQUEST_SIZE)
1157         {
1158           log_error ("error reading request: request too long (%zu MiB)\n",
1159                      (size_t)nrequest / (1024*1024));
1160           /* Fixme: Shall we read the request to the bit bucket and
1161            * return an error reponse or just return an error reponse
1162            * and terminate?  Needs some testing.  */
1163           break;
1164         }
1165
1166       /* Read request.  */
1167       request = xtrymalloc (nrequest);
1168       if (!request)
1169         {
1170           err = gpg_error_from_syserror ();
1171           log_error ("error reading request: Not enough memory for %zu MiB)\n",
1172                      (size_t)nrequest / (1024*1024));
1173           /* FIXME: See comment above.  */
1174           break;
1175         }
1176       if (es_read (es_stdin, request, nrequest, &n))
1177         {
1178           err = gpg_error_from_syserror ();
1179           log_error ("error reading request: %s\n", gpg_strerror (err));
1180           break;
1181         }
1182       if (n != nrequest)
1183         {
1184           /* That is a protocol violation.  */
1185           xfree (response);
1186           response = error_object_string ("Invalid request:"
1187                                           " short read (%zu of %zu bytes)\n",
1188                                           n, (size_t)nrequest);
1189         }
1190       else /* Process request  */
1191         {
1192           if (opt_debug)
1193             log_debug ("request='%s'\n", request);
1194           xfree (response);
1195           response = process_request (request);
1196           if (opt_debug)
1197             log_debug ("response='%s'\n", response);
1198         }
1199       nresponse = strlen (response);
1200
1201       /* Write response */
1202       if (es_write (es_stdout, &nresponse, sizeof nresponse, &n))
1203         {
1204           err = gpg_error_from_syserror ();
1205           log_error ("error writing request header: %s\n", gpg_strerror (err));
1206           break;
1207         }
1208       if (n != sizeof nrequest)
1209         {
1210           log_error ("error writing request header: short write\n");
1211           break;
1212         }
1213       if (es_write (es_stdout, response, nresponse, &n))
1214         {
1215           err = gpg_error_from_syserror ();
1216           log_error ("error writing request: %s\n", gpg_strerror (err));
1217           break;
1218         }
1219       if (n != nresponse)
1220         {
1221           log_error ("error writing request: short write\n");
1222           break;
1223         }
1224       if (es_fflush (es_stdout) || es_ferror (es_stdout))
1225         {
1226           err = gpg_error_from_syserror ();
1227           log_error ("error writing request: %s\n", gpg_strerror (err));
1228           break;
1229         }
1230     }
1231
1232   xfree (response);
1233   xfree (request);
1234 }
1235
1236
1237 \f
1238 static const char *
1239 my_strusage( int level )
1240 {
1241   const char *p;
1242
1243   switch (level)
1244     {
1245     case  9: p = "LGPL-2.1-or-later"; break;
1246     case 11: p = "gpgme-json"; break;
1247     case 13: p = PACKAGE_VERSION; break;
1248     case 14: p = "Copyright (C) 2018 g10 Code GmbH"; break;
1249     case 19: p = "Please report bugs to <" PACKAGE_BUGREPORT ">.\n"; break;
1250     case 1:
1251     case 40:
1252       p = "Usage: gpgme-json [OPTIONS]";
1253       break;
1254     case 41:
1255       p = "Native messaging based GPGME operations.\n";
1256       break;
1257     case 42:
1258       p = "1"; /* Flag print 40 as part of 41. */
1259       break;
1260     default: p = NULL; break;
1261     }
1262   return p;
1263 }
1264
1265 int
1266 main (int argc, char *argv[])
1267 {
1268 #if GPGRT_VERSION_NUMBER < 0x011d00 /* 1.29 */
1269
1270   fprintf (stderr, "WARNING: Old libgpg-error - using limited mode\n");
1271   native_messaging_repl ();
1272
1273 #else /* This is a modern libgp-error.  */
1274
1275   enum { CMD_DEFAULT     = 0,
1276          CMD_INTERACTIVE = 'i',
1277          CMD_SINGLE      = 's',
1278          CMD_LIBVERSION  = 501,
1279   } cmd = CMD_DEFAULT;
1280   enum {
1281     OPT_DEBUG = 600
1282   };
1283
1284   static gpgrt_opt_t opts[] = {
1285     ARGPARSE_c  (CMD_INTERACTIVE, "interactive", "Interactive REPL"),
1286     ARGPARSE_c  (CMD_SINGLE,      "single",      "Single request mode"),
1287     ARGPARSE_c  (CMD_LIBVERSION,  "lib-version", "Show library version"),
1288     ARGPARSE_s_n(OPT_DEBUG,       "debug",       "Flyswatter"),
1289
1290     ARGPARSE_end()
1291   };
1292   gpgrt_argparse_t pargs = { &argc, &argv};
1293
1294   gpgrt_set_strusage (my_strusage);
1295
1296 #ifdef HAVE_SETLOCALE
1297   setlocale (LC_ALL, "");
1298 #endif
1299   gpgme_check_version (NULL);
1300 #ifdef LC_CTYPE
1301   gpgme_set_locale (NULL, LC_CTYPE, setlocale (LC_CTYPE, NULL));
1302 #endif
1303 #ifdef LC_MESSAGES
1304   gpgme_set_locale (NULL, LC_MESSAGES, setlocale (LC_MESSAGES, NULL));
1305 #endif
1306
1307   while (gpgrt_argparse (NULL, &pargs, opts))
1308     {
1309       switch (pargs.r_opt)
1310         {
1311         case CMD_INTERACTIVE:
1312           opt_interactive = 1;
1313           /* Fall trough.  */
1314         case CMD_SINGLE:
1315         case CMD_LIBVERSION:
1316           cmd = pargs.r_opt;
1317           break;
1318
1319         case OPT_DEBUG: opt_debug = 1; break;
1320
1321         default:
1322           pargs.err = ARGPARSE_PRINT_WARNING;
1323           break;
1324         }
1325     }
1326   gpgrt_argparse (NULL, &pargs, NULL);
1327
1328   if (!opt_debug)
1329     {
1330       const char *s = getenv ("GPGME_JSON_DEBUG");
1331       if (s && atoi (s) > 0)
1332         opt_debug = 1;
1333     }
1334
1335   if (opt_debug)
1336     {
1337       const char *home = getenv ("HOME");
1338       char *file = xstrconcat ("socket://",
1339                                home? home:"/tmp",
1340                                "/.gnupg/S.gpgme-json.log", NULL);
1341       log_set_file (file);
1342       xfree (file);
1343     }
1344
1345   if (opt_debug)
1346     { int i;
1347       for (i=0; argv[i]; i++)
1348         log_debug ("argv[%d]='%s'\n", i, argv[i]);
1349     }
1350
1351   switch (cmd)
1352     {
1353     case CMD_DEFAULT:
1354       native_messaging_repl ();
1355       break;
1356
1357     case CMD_SINGLE:
1358       read_and_process_single_request ();
1359       break;
1360
1361     case CMD_INTERACTIVE:
1362       interactive_repl ();
1363       break;
1364
1365     case CMD_LIBVERSION:
1366       printf ("Version from header: %s (0x%06x)\n",
1367               GPGME_VERSION, GPGME_VERSION_NUMBER);
1368       printf ("Version from binary: %s\n", gpgme_check_version (NULL));
1369       printf ("Copyright blurb ...:%s\n", gpgme_check_version ("\x01\x01"));
1370       break;
1371     }
1372
1373   if (opt_debug)
1374     log_debug ("ready");
1375
1376 #endif /* This is a modern libgp-error.  */
1377   return 0;
1378 }
1379 #endif /* libgpg-error >= 1.28 */