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