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