json: Add code to gpg_error based messages
[gpgme.git] / src / gpgme-json.c
1 /* gpgme-json.c - JSON based interface to gpgme (server)
2  * Copyright (C) 2018 g10 Code GmbH
3  *
4  * This file is part of GPGME.
5  *
6  * GPGME is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU Lesser General Public License as
8  * published by the Free Software Foundation; either version 2.1 of
9  * the License, or (at your option) any later version.
10  *
11  * GPGME is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this program; if not, see <http://www.gnu.org/licenses/>.
18  * SPDX-License-Identifier: LGPL-2.1+
19  */
20
21 /* This is tool implements the Native Messaging protocol of web
22  * browsers and provides the server part of it.  A Javascript based
23  * client can be found in lang/javascript.
24  */
25
26 #include <config.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <stdarg.h>
31 #ifdef HAVE_LOCALE_H
32 #include <locale.h>
33 #endif
34 #include <stdint.h>
35 #include <sys/stat.h>
36
37 #define GPGRT_ENABLE_ES_MACROS 1
38 #define GPGRT_ENABLE_LOG_MACROS 1
39 #define GPGRT_ENABLE_ARGPARSE_MACROS 1
40 #include "gpgme.h"
41 #include "cJSON.h"
42
43
44 #if GPGRT_VERSION_NUMBER < 0x011c00 /* 1.28 */
45 int main (void){fputs ("Build with Libgpg-error >= 1.28!\n", stderr);return 1;}
46 #else /* libgpg-error >= 1.28 */
47
48 /* We don't allow a request with more than 64 MiB.  */
49 #define MAX_REQUEST_SIZE (64 * 1024 * 1024)
50
51 /* Minimal, default and maximum chunk size for returned data. The
52  * first chunk is returned directly.  If the "more" flag is also
53  * returned, a "getmore" command needs to be used to get the next
54  * chunk.  Right now this value covers just the value of the "data"
55  * element; so to cover for the other returned objects this values
56  * needs to be lower than the maximum allowed size of the browser. */
57 #define MIN_REPLY_CHUNK_SIZE  512
58 #define DEF_REPLY_CHUNK_SIZE (512 * 1024)
59 #define MAX_REPLY_CHUNK_SIZE (10 * 1024 * 1024)
60
61
62 static void xoutofcore (const char *type) GPGRT_ATTR_NORETURN;
63 static cjson_t error_object_v (cjson_t json, const char *message,
64                                va_list arg_ptr, gpg_error_t err)
65                                GPGRT_ATTR_PRINTF(2,0);
66 static cjson_t error_object (cjson_t json, const char *message,
67                             ...) GPGRT_ATTR_PRINTF(2,3);
68 static char *error_object_string (const char *message,
69                                   ...) GPGRT_ATTR_PRINTF(1,2);
70
71
72 /* True if interactive mode is active.  */
73 static int opt_interactive;
74 /* True is debug mode is active.  */
75 static int opt_debug;
76
77 /* Pending data to be returned by a getmore command.  */
78 static struct
79 {
80   char  *buffer;   /* Malloced data or NULL if not used.  */
81   size_t length;   /* Length of that data.  */
82   size_t written;  /* # of already written bytes from BUFFER.  */
83   const char *type;/* The "type" of the data.  */
84   int base64;      /* The "base64" flag of the data.  */
85 } pending_data;
86
87
88 /*
89  * Helper functions and macros
90  */
91
92 #define xtrymalloc(a)  gpgrt_malloc ((a))
93 #define xtrystrdup(a)  gpgrt_strdup ((a))
94 #define xmalloc(a) ({                           \
95       void *_r = gpgrt_malloc ((a));            \
96       if (!_r)                                  \
97         xoutofcore ("malloc");                  \
98       _r; })
99 #define xcalloc(a,b) ({                         \
100       void *_r = gpgrt_calloc ((a), (b));       \
101       if (!_r)                                  \
102         xoutofcore ("calloc");                  \
103       _r; })
104 #define xstrdup(a) ({                           \
105       char *_r = gpgrt_strdup ((a));            \
106       if (!_r)                                  \
107         xoutofcore ("strdup");                  \
108       _r; })
109 #define xstrconcat(a, ...) ({                           \
110       char *_r = gpgrt_strconcat ((a), __VA_ARGS__);    \
111       if (!_r)                                          \
112         xoutofcore ("strconcat");                       \
113       _r; })
114 #define xfree(a) gpgrt_free ((a))
115
116 #define spacep(p)   (*(p) == ' ' || *(p) == '\t')
117
118 #ifndef HAVE_STPCPY
119 static GPGRT_INLINE char *
120 _my_stpcpy (char *a, const char *b)
121 {
122   while (*b)
123     *a++ = *b++;
124   *a = 0;
125   return a;
126 }
127 #define stpcpy(a,b) _my_stpcpy ((a), (b))
128 #endif /*!HAVE_STPCPY*/
129
130
131
132 static void
133 xoutofcore (const char *type)
134 {
135   gpg_error_t err = gpg_error_from_syserror ();
136   log_error ("%s failed: %s\n", type, gpg_strerror (err));
137   exit (2);
138 }
139
140
141 /* Call cJSON_CreateObject but terminate in case of an error.  */
142 static cjson_t
143 xjson_CreateObject (void)
144 {
145   cjson_t json = cJSON_CreateObject ();
146   if (!json)
147     xoutofcore ("cJSON_CreateObject");
148   return json;
149 }
150
151 /* Call cJSON_CreateArray but terminate in case of an error.  */
152 static cjson_t
153 xjson_CreateArray (void)
154 {
155   cjson_t json = cJSON_CreateArray ();
156   if (!json)
157     xoutofcore ("cJSON_CreateArray");
158   return json;
159 }
160
161
162 /* Wrapper around cJSON_AddStringToObject which returns an gpg-error
163  * code instead of the NULL or the new object.  */
164 static gpg_error_t
165 cjson_AddStringToObject (cjson_t object, const char *name, const char *string)
166 {
167   if (!cJSON_AddStringToObject (object, name, string))
168     return gpg_error_from_syserror ();
169   return 0;
170 }
171
172
173 /* Same as cjson_AddStringToObject but prints an error message and
174  * terminates the process.  */
175 static void
176 xjson_AddStringToObject (cjson_t object, const char *name, const char *string)
177 {
178   if (!cJSON_AddStringToObject (object, name, string))
179     xoutofcore ("cJSON_AddStringToObject");
180 }
181
182
183 /* Wrapper around cJSON_AddBoolToObject which terminates the process
184  * in case of an error.  */
185 static void
186 xjson_AddBoolToObject (cjson_t object, const char *name, int abool)
187 {
188   if (!cJSON_AddBoolToObject (object, name, abool))
189     xoutofcore ("cJSON_AddStringToObject");
190   return ;
191 }
192
193 /* Wrapper around cJSON_AddNumberToObject which terminates the process
194  * in case of an error.  */
195 static void
196 xjson_AddNumberToObject (cjson_t object, const char *name, double dbl)
197 {
198   if (!cJSON_AddNumberToObject (object, name, dbl))
199     xoutofcore ("cJSON_AddNumberToObject");
200   return ;
201 }
202
203 /* This is similar to cJSON_AddStringToObject but takes (DATA,
204  * DATALEN) and adds it under NAME as a base 64 encoded string to
205  * OBJECT.  */
206 static gpg_error_t
207 add_base64_to_object (cjson_t object, const char *name,
208                       const void *data, size_t datalen)
209 {
210 #if GPGRT_VERSION_NUMBER < 0x011d00 /* 1.29 */
211   return gpg_error (GPG_ERR_NOT_SUPPORTED);
212 #else
213   gpg_err_code_t err;
214   estream_t fp = NULL;
215   gpgrt_b64state_t state = NULL;
216   cjson_t j_str = NULL;
217   void *buffer = NULL;
218
219   fp = es_fopenmem (0, "rwb");
220   if (!fp)
221     {
222       err = gpg_err_code_from_syserror ();
223       goto leave;
224     }
225   state = gpgrt_b64enc_start (fp, "");
226   if (!state)
227     {
228       err = gpg_err_code_from_syserror ();
229       goto leave;
230     }
231
232   err = gpgrt_b64enc_write (state, data, datalen);
233   if (err)
234     goto leave;
235
236   err = gpgrt_b64enc_finish (state);
237   state = NULL;
238   if (err)
239     return err;
240
241   es_fputc (0, fp);
242   if (es_fclose_snatch (fp, &buffer, NULL))
243     {
244       fp = NULL;
245       err = gpg_error_from_syserror ();
246       goto leave;
247     }
248   fp = NULL;
249
250   j_str = cJSON_CreateStringConvey (buffer);
251   if (!j_str)
252     {
253       err = gpg_error_from_syserror ();
254       goto leave;
255     }
256   buffer = NULL;
257
258   if (!cJSON_AddItemToObject (object, name, j_str))
259     {
260       err = gpg_error_from_syserror ();
261       cJSON_Delete (j_str);
262       j_str = NULL;
263       goto leave;
264     }
265   j_str = NULL;
266
267  leave:
268   xfree (buffer);
269   cJSON_Delete (j_str);
270   gpgrt_b64enc_finish (state);
271   es_fclose (fp);
272   return err;
273 #endif
274 }
275
276
277 /* Create a JSON error object.  If JSON is not NULL the error message
278  * is appended to that object.  An existing "type" item will be replaced. */
279 static cjson_t
280 error_object_v (cjson_t json, const char *message, va_list arg_ptr,
281                 gpg_error_t err)
282 {
283   cjson_t response, j_tmp;
284   char *msg;
285
286   msg = gpgrt_vbsprintf (message, arg_ptr);
287   if (!msg)
288     xoutofcore ("error_object");
289
290   response = json? json : xjson_CreateObject ();
291
292   if (!(j_tmp = cJSON_GetObjectItem (response, "type")))
293     xjson_AddStringToObject (response, "type", "error");
294   else /* Replace existing "type".  */
295     {
296       j_tmp = cJSON_CreateString ("error");
297       if (!j_tmp)
298         xoutofcore ("cJSON_CreateString");
299       cJSON_ReplaceItemInObject (response, "type", j_tmp);
300      }
301   xjson_AddStringToObject (response, "msg", msg);
302   xfree (msg);
303
304   xjson_AddNumberToObject (response, "code", err);
305
306   return response;
307 }
308
309
310 /* Call cJSON_Print but terminate in case of an error.  */
311 static char *
312 xjson_Print (cjson_t object)
313 {
314   char *buf;
315   buf = cJSON_Print (object);
316   if (!buf)
317     xoutofcore ("cJSON_Print");
318   return buf;
319 }
320
321
322 static cjson_t
323 error_object (cjson_t json, const char *message, ...)
324 {
325   cjson_t response;
326   va_list arg_ptr;
327
328   va_start (arg_ptr, message);
329   response = error_object_v (json, message, arg_ptr, 0);
330   va_end (arg_ptr);
331   return response;
332 }
333
334
335 static cjson_t
336 gpg_error_object (cjson_t json, gpg_error_t err, const char *message, ...)
337 {
338   cjson_t response;
339   va_list arg_ptr;
340
341   va_start (arg_ptr, message);
342   response = error_object_v (json, message, arg_ptr, err);
343   va_end (arg_ptr);
344   return response;
345 }
346
347
348 static char *
349 error_object_string (const char *message, ...)
350 {
351   cjson_t response;
352   va_list arg_ptr;
353   char *msg;
354
355   va_start (arg_ptr, message);
356   response = error_object_v (NULL, message, arg_ptr, 0);
357   va_end (arg_ptr);
358
359   msg = xjson_Print (response);
360   cJSON_Delete (response);
361   return msg;
362 }
363
364
365 /* Get the boolean property NAME from the JSON object and store true
366  * or valse at R_VALUE.  If the name is unknown the value of DEF_VALUE
367  * is returned.  If the type of the value is not boolean,
368  * GPG_ERR_INV_VALUE is returned and R_VALUE set to DEF_VALUE.  */
369 static gpg_error_t
370 get_boolean_flag (cjson_t json, const char *name, int def_value, int *r_value)
371 {
372   cjson_t j_item;
373
374   j_item = cJSON_GetObjectItem (json, name);
375   if (!j_item)
376     *r_value = def_value;
377   else if (cjson_is_true (j_item))
378     *r_value = 1;
379   else if (cjson_is_false (j_item))
380     *r_value = 0;
381   else
382     {
383       *r_value = def_value;
384       return gpg_error (GPG_ERR_INV_VALUE);
385     }
386
387   return 0;
388 }
389
390
391 /* Get the boolean property PROTOCOL from the JSON object and store
392  * its value at R_PROTOCOL.  The default is OpenPGP.  */
393 static gpg_error_t
394 get_protocol (cjson_t json, gpgme_protocol_t *r_protocol)
395 {
396   cjson_t j_item;
397
398   *r_protocol = GPGME_PROTOCOL_OpenPGP;
399   j_item = cJSON_GetObjectItem (json, "protocol");
400   if (!j_item)
401     ;
402   else if (!cjson_is_string (j_item))
403     return gpg_error (GPG_ERR_INV_VALUE);
404   else if (!strcmp(j_item->valuestring, "openpgp"))
405     ;
406   else if (!strcmp(j_item->valuestring, "cms"))
407     *r_protocol = GPGME_PROTOCOL_CMS;
408   else
409     return gpg_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
410
411   return 0;
412 }
413
414
415 /* Get the chunksize from JSON and store it at R_CHUNKSIZE.  */
416 static gpg_error_t
417 get_chunksize (cjson_t json, size_t *r_chunksize)
418 {
419   cjson_t j_item;
420
421   *r_chunksize = DEF_REPLY_CHUNK_SIZE;
422   j_item = cJSON_GetObjectItem (json, "chunksize");
423   if (!j_item)
424     ;
425   else if (!cjson_is_number (j_item))
426     return gpg_error (GPG_ERR_INV_VALUE);
427   else if ((size_t)j_item->valueint < MIN_REPLY_CHUNK_SIZE)
428     *r_chunksize = MIN_REPLY_CHUNK_SIZE;
429   else if ((size_t)j_item->valueint > MAX_REPLY_CHUNK_SIZE)
430     *r_chunksize = MAX_REPLY_CHUNK_SIZE;
431   else
432     *r_chunksize = (size_t)j_item->valueint;
433
434   return 0;
435 }
436
437
438 /* Extract the keys from the "keys" array in the JSON object.  On
439  * success a string with the keys identifiers is stored at R_KEYS.
440  * The keys in that string are LF delimited.  On failure an error code
441  * is returned.  */
442 static gpg_error_t
443 get_keys (cjson_t json, char **r_keystring)
444 {
445   cjson_t j_keys, j_item;
446   int i, nkeys;
447   char *p;
448   size_t length;
449
450   *r_keystring = NULL;
451
452   j_keys = cJSON_GetObjectItem (json, "keys");
453   if (!j_keys)
454     return gpg_error (GPG_ERR_NO_KEY);
455   if (!cjson_is_array (j_keys) && !cjson_is_string (j_keys))
456     return gpg_error (GPG_ERR_INV_VALUE);
457
458   /* Fixme: We should better use a membuf like thing.  */
459   length = 1; /* For the EOS.  */
460   if (cjson_is_string (j_keys))
461     {
462       nkeys = 1;
463       length += strlen (j_keys->valuestring);
464       if (strchr (j_keys->valuestring, '\n'))
465         return gpg_error (GPG_ERR_INV_USER_ID);
466     }
467   else
468     {
469       nkeys = cJSON_GetArraySize (j_keys);
470       if (!nkeys)
471         return gpg_error (GPG_ERR_NO_KEY);
472       for (i=0; i < nkeys; i++)
473         {
474           j_item = cJSON_GetArrayItem (j_keys, i);
475           if (!j_item || !cjson_is_string (j_item))
476             return gpg_error (GPG_ERR_INV_VALUE);
477           if (i)
478             length++; /* Space for delimiter. */
479           length += strlen (j_item->valuestring);
480           if (strchr (j_item->valuestring, '\n'))
481             return gpg_error (GPG_ERR_INV_USER_ID);
482         }
483     }
484
485   p = *r_keystring = xtrymalloc (length);
486   if (!p)
487     return gpg_error_from_syserror ();
488
489   if (cjson_is_string (j_keys))
490     {
491       strcpy (p, j_keys->valuestring);
492     }
493   else
494     {
495       for (i=0; i < nkeys; i++)
496         {
497           j_item = cJSON_GetArrayItem (j_keys, i);
498           if (i)
499             *p++ = '\n'; /* Add delimiter.  */
500           p = stpcpy (p, j_item->valuestring);
501         }
502     }
503   return 0;
504 }
505
506
507
508 \f
509 /*
510  *  GPGME support functions.
511  */
512
513 /* Helper for get_context.  */
514 static gpgme_ctx_t
515 _create_new_context (gpgme_protocol_t proto)
516 {
517   gpg_error_t err;
518   gpgme_ctx_t ctx;
519
520   err = gpgme_new (&ctx);
521   if (err)
522     log_fatal ("error creating GPGME context: %s\n", gpg_strerror (err));
523   gpgme_set_protocol (ctx, proto);
524   gpgme_set_ctx_flag (ctx, "request-origin", "browser");
525   return ctx;
526 }
527
528
529 /* Return a context object for protocol PROTO.  This is currently a
530  * statically allocated context initialized for PROTO.  Terminates
531  * process on failure.  */
532 static gpgme_ctx_t
533 get_context (gpgme_protocol_t proto)
534 {
535   static gpgme_ctx_t ctx_openpgp, ctx_cms;
536
537   if (proto == GPGME_PROTOCOL_OpenPGP)
538     {
539       if (!ctx_openpgp)
540         ctx_openpgp = _create_new_context (proto);
541       return ctx_openpgp;
542     }
543   else if (proto == GPGME_PROTOCOL_CMS)
544     {
545       if (!ctx_cms)
546         ctx_cms = _create_new_context (proto);
547       return ctx_cms;
548     }
549   else
550     log_bug ("invalid protocol %d requested\n", proto);
551 }
552
553
554
555 /* Free context object retrieved by get_context.  */
556 static void
557 release_context (gpgme_ctx_t ctx)
558 {
559   /* Nothing to do right now.  */
560   (void)ctx;
561 }
562
563
564
565 /* Given a Base-64 encoded string object in JSON return a gpgme data
566  * object at R_DATA.  */
567 static gpg_error_t
568 data_from_base64_string (gpgme_data_t *r_data, cjson_t json)
569 {
570 #if GPGRT_VERSION_NUMBER < 0x011d00 /* 1.29 */
571   *r_data = NULL;
572   return gpg_error (GPG_ERR_NOT_SUPPORTED);
573 #else
574   gpg_error_t err;
575   size_t len;
576   char *buf = NULL;
577   gpgrt_b64state_t state = NULL;
578   gpgme_data_t data = NULL;
579
580   *r_data = NULL;
581
582   /* A quick check on the JSON.  */
583   if (!cjson_is_string (json))
584     {
585       err = gpg_error (GPG_ERR_INV_VALUE);
586       goto leave;
587     }
588
589   state = gpgrt_b64dec_start (NULL);
590   if (!state)
591     {
592       err = gpg_err_code_from_syserror ();
593       goto leave;
594     }
595
596   /* Fixme: Data duplication - we should see how to snatch the memory
597    * from the json object.  */
598   len = strlen (json->valuestring);
599   buf = xtrystrdup (json->valuestring);
600   if (!buf)
601     {
602       err = gpg_error_from_syserror ();
603       goto leave;
604     }
605
606   err = gpgrt_b64dec_proc (state, buf, len, &len);
607   if (err)
608     goto leave;
609
610   err = gpgrt_b64dec_finish (state);
611   state = NULL;
612   if (err)
613     goto leave;
614
615   err = gpgme_data_new_from_mem (&data, buf, len, 1);
616   if (err)
617     goto leave;
618   *r_data = data;
619   data = NULL;
620
621  leave:
622   xfree (data);
623   xfree (buf);
624   gpgrt_b64dec_finish (state);
625   return err;
626 #endif
627 }
628
629
630 /* Helper for summary formatting */
631 static void
632 add_summary_to_object (cjson_t result, gpgme_sigsum_t summary)
633 {
634   cjson_t response = xjson_CreateArray ();
635   if ( (summary & GPGME_SIGSUM_VALID      ))
636     cJSON_AddItemToArray (response,
637         cJSON_CreateString ("valid"));
638   if ( (summary & GPGME_SIGSUM_GREEN      ))
639     cJSON_AddItemToArray (response,
640         cJSON_CreateString ("green"));
641   if ( (summary & GPGME_SIGSUM_RED        ))
642     cJSON_AddItemToArray (response,
643         cJSON_CreateString ("red"));
644   if ( (summary & GPGME_SIGSUM_KEY_REVOKED))
645     cJSON_AddItemToArray (response,
646         cJSON_CreateString ("revoked"));
647   if ( (summary & GPGME_SIGSUM_KEY_EXPIRED))
648     cJSON_AddItemToArray (response,
649         cJSON_CreateString ("key-expired"));
650   if ( (summary & GPGME_SIGSUM_SIG_EXPIRED))
651     cJSON_AddItemToArray (response,
652         cJSON_CreateString ("sig-expired"));
653   if ( (summary & GPGME_SIGSUM_KEY_MISSING))
654     cJSON_AddItemToArray (response,
655         cJSON_CreateString ("key-missing"));
656   if ( (summary & GPGME_SIGSUM_CRL_MISSING))
657     cJSON_AddItemToArray (response,
658         cJSON_CreateString ("crl-missing"));
659   if ( (summary & GPGME_SIGSUM_CRL_TOO_OLD))
660     cJSON_AddItemToArray (response,
661         cJSON_CreateString ("crl-too-old"));
662   if ( (summary & GPGME_SIGSUM_BAD_POLICY ))
663     cJSON_AddItemToArray (response,
664         cJSON_CreateString ("bad-policy"));
665   if ( (summary & GPGME_SIGSUM_SYS_ERROR  ))
666     cJSON_AddItemToArray (response,
667         cJSON_CreateString ("sys-error"));
668
669   cJSON_AddItemToObject (result, "summary", response);
670 }
671
672
673 /* Helper for summary formatting */
674 static const char *
675 validity_to_string (gpgme_validity_t val)
676 {
677   switch (val)
678     {
679     case GPGME_VALIDITY_UNDEFINED:return "undefined";
680     case GPGME_VALIDITY_NEVER:    return "never";
681     case GPGME_VALIDITY_MARGINAL: return "marginal";
682     case GPGME_VALIDITY_FULL:     return "full";
683     case GPGME_VALIDITY_ULTIMATE: return "ultimate";
684     case GPGME_VALIDITY_UNKNOWN:
685     default:                      return "unknown";
686     }
687 }
688
689
690 /* Add a single signature to a result */
691 static gpg_error_t
692 add_signature_to_object (cjson_t result, gpgme_signature_t sig)
693 {
694   gpg_error_t err = 0;
695
696   if (!cJSON_AddStringToObject (result, "status",
697                                 gpgme_strerror (sig->status)))
698     {
699       err = gpg_error_from_syserror ();
700       goto leave;
701     }
702
703   if (!cJSON_AddNumberToObject (result, "code", sig->status))
704     {
705       err = gpg_error_from_syserror ();
706       goto leave;
707     }
708
709   add_summary_to_object (result, sig->summary);
710
711   if (!cJSON_AddStringToObject (result, "fingerprint", sig->fpr))
712     {
713       err = gpg_error_from_syserror ();
714       goto leave;
715     }
716
717   if (!cJSON_AddNumberToObject (result, "created", sig->timestamp))
718     {
719       err = gpg_error_from_syserror ();
720       goto leave;
721     }
722
723   if (!cJSON_AddNumberToObject (result, "expired", sig->exp_timestamp))
724     {
725       err = gpg_error_from_syserror ();
726       goto leave;
727     }
728
729   if (!cJSON_AddStringToObject (result, "validity",
730                                 validity_to_string (sig->validity)))
731     {
732       err = gpg_error_from_syserror ();
733       goto leave;
734     }
735
736 leave:
737   return err;
738 }
739
740
741 /* Add multiple signatures as an array to a result */
742 static gpg_error_t
743 add_signatures_to_object (cjson_t result, gpgme_signature_t signatures)
744 {
745   cjson_t response = xjson_CreateArray ();
746   gpg_error_t err = 0;
747   gpgme_signature_t sig;
748
749   for (sig = signatures; sig; sig = sig->next)
750     {
751       cjson_t sig_obj = xjson_CreateObject ();
752       err = add_signature_to_object (sig_obj, sig);
753       if (err)
754         {
755           cJSON_Delete (sig_obj);
756           sig_obj = NULL;
757           goto leave;
758         }
759
760       cJSON_AddItemToArray (response, sig_obj);
761     }
762
763   if (!cJSON_AddItemToObject (result, "signatures", response))
764     {
765       err = gpg_error_from_syserror ();
766       cJSON_Delete (response);
767       response = NULL;
768       return err;
769     }
770   response = NULL;
771
772 leave:
773   if (err && response)
774     {
775       cJSON_Delete (response);
776       response = NULL;
777     }
778   return err;
779 }
780
781
782 /* Add an array of signature informations under the name "name". */
783 static gpg_error_t
784 add_signatures_object (cjson_t result, const char *name,
785                        gpgme_verify_result_t verify_result)
786 {
787   cjson_t response = xjson_CreateObject ();
788   gpg_error_t err = 0;
789
790   err = add_signatures_to_object (response, verify_result->signatures);
791
792   if (err)
793     {
794       goto leave;
795     }
796
797   if (!cJSON_AddItemToObject (result, name, response))
798     {
799       err = gpg_error_from_syserror ();
800       goto leave;
801     }
802  leave:
803   if (err)
804     {
805       cJSON_Delete (response);
806       response = NULL;
807     }
808   return err;
809 }
810
811 static const char *
812 protocol_to_string (gpgme_protocol_t proto)
813 {
814   switch (proto)
815     {
816     case GPGME_PROTOCOL_OpenPGP: return "OpenPGP";
817     case GPGME_PROTOCOL_CMS:     return "CMS";
818     case GPGME_PROTOCOL_GPGCONF: return "gpgconf";
819     case GPGME_PROTOCOL_ASSUAN:  return "assuan";
820     case GPGME_PROTOCOL_G13:     return "g13";
821     case GPGME_PROTOCOL_UISERVER:return "uiserver";
822     case GPGME_PROTOCOL_SPAWN:   return "spawn";
823     default:
824                                  return "unknown";
825     }
826 }
827
828 static gpg_error_t
829 add_ei_to_object (cjson_t result, gpgme_engine_info_t info)
830 {
831   if (!cJSON_AddStringToObject (result, "protocol",
832                                 protocol_to_string (info->protocol)))
833     return gpg_error_from_syserror ();
834   if (!cJSON_AddStringToObject (result, "fname", info->file_name))
835     return gpg_error_from_syserror ();
836   if (!cJSON_AddStringToObject (result, "version", info->version))
837     return gpg_error_from_syserror ();
838   if (!cJSON_AddStringToObject (result, "req_version", info->req_version))
839     return gpg_error_from_syserror ();
840   if (!cJSON_AddStringToObject (result, "homedir",
841                                 info->home_dir ?
842                                 info->home_dir :
843                                 "default"))
844     return gpg_error_from_syserror ();
845   return 0;
846 }
847 \f
848 /*
849  * Implementation of the commands.
850  */
851
852
853 /* Create a "data" object and the "type", "base64" and "more" flags
854  * from DATA and append them to RESULT.  Ownership of DATA is
855  * transferred to this function.  TYPE must be a fixed string.
856  * CHUNKSIZE is the chunksize requested from the caller.  If BASE64 is
857  * -1 the need for base64 encoding is determined by the content of
858  * DATA, all other values are taken as true or false.  Note that
859  * op_getmore has similar code but works on PENDING_DATA which is set
860  * here.  */
861 static gpg_error_t
862 make_data_object (cjson_t result, gpgme_data_t data, size_t chunksize,
863                   const char *type, int base64)
864 {
865   gpg_error_t err;
866   char *buffer;
867   const char *s;
868   size_t buflen, n;
869   int c;
870
871   if (!base64 || base64 == -1) /* Make sure that we really have a string.  */
872     gpgme_data_write (data, "", 1);
873
874   buffer = gpgme_data_release_and_get_mem (data, &buflen);
875   data = NULL;
876   if (!buffer)
877     {
878       err = gpg_error_from_syserror ();
879       goto leave;
880     }
881
882   if (base64 == -1)
883     {
884       base64 = 0;
885       if (!buflen)
886         log_fatal ("Appended Nul byte got lost\n");
887       /* Figure out if there is any Nul octet in the buffer.  In that
888        * case we need to Base-64 the buffer.  Due to problems with the
889        * browser's Javascript we use Base-64 also in case an UTF-8
890        * character is in the buffer.  This is because the chunking may
891        * split an UTF-8 characters and JS can't handle this.  */
892       for (s=buffer, n=0; n < buflen -1; s++, n++)
893         if (!*s || (*s & 0x80))
894           {
895             buflen--; /* Adjust for the extra nul byte.  */
896             base64 = 1;
897             break;
898           }
899     }
900
901   /* Adjust the chunksize if we need to do base64 conversion.  */
902   if (base64)
903     chunksize = (chunksize / 4) * 3;
904
905   xjson_AddStringToObject (result, "type", type);
906   xjson_AddBoolToObject (result, "base64", base64);
907
908   if (buflen > chunksize)
909     {
910       xjson_AddBoolToObject (result, "more", 1);
911
912       c = buffer[chunksize];
913       buffer[chunksize] = 0;
914       if (base64)
915         err = add_base64_to_object (result, "data", buffer, chunksize);
916       else
917         err = cjson_AddStringToObject (result, "data", buffer);
918       buffer[chunksize] = c;
919       if (err)
920         goto leave;
921
922       pending_data.buffer = buffer;
923       buffer = NULL;
924       pending_data.length = buflen;
925       pending_data.written = chunksize;
926       pending_data.type = type;
927       pending_data.base64 = base64;
928     }
929   else
930     {
931       if (base64)
932         err = add_base64_to_object (result, "data", buffer, buflen);
933       else
934         err = cjson_AddStringToObject (result, "data", buffer);
935     }
936
937  leave:
938   gpgme_free (buffer);
939   return err;
940 }
941
942
943 \f
944 static const char hlp_encrypt[] =
945   "op:     \"encrypt\"\n"
946   "keys:   Array of strings with the fingerprints or user-ids\n"
947   "        of the keys to encrypt the data.  For a single key\n"
948   "        a String may be used instead of an array.\n"
949   "data:   Input data. \n"
950   "\n"
951   "Optional parameters:\n"
952   "protocol:      Either \"openpgp\" (default) or \"cms\".\n"
953   "chunksize:     Max number of bytes in the resulting \"data\".\n"
954   "\n"
955   "Optional boolean flags (default is false):\n"
956   "base64:        Input data is base64 encoded.\n"
957   "mime:          Indicate that data is a MIME object.\n"
958   "armor:         Request output in armored format.\n"
959   "always-trust:  Request --always-trust option.\n"
960   "no-encrypt-to: Do not use a default recipient.\n"
961   "no-compress:   Do not compress the plaintext first.\n"
962   "throw-keyids:  Request the --throw-keyids option.\n"
963   "want-address:  Require that the keys include a mail address.\n"
964   "wrap:          Assume the input is an OpenPGP message.\n"
965   "\n"
966   "Response on success:\n"
967   "type:   \"ciphertext\"\n"
968   "data:   Unless armor mode is used a Base64 encoded binary\n"
969   "        ciphertext.  In armor mode a string with an armored\n"
970   "        OpenPGP or a PEM message.\n"
971   "base64: Boolean indicating whether data is base64 encoded.\n"
972   "more:   Optional boolean indicating that \"getmore\" is required.";
973 static gpg_error_t
974 op_encrypt (cjson_t request, cjson_t result)
975 {
976   gpg_error_t err;
977   gpgme_ctx_t ctx = NULL;
978   gpgme_protocol_t protocol;
979   size_t chunksize;
980   int opt_base64;
981   int opt_mime;
982   char *keystring = NULL;
983   cjson_t j_input;
984   gpgme_data_t input = NULL;
985   gpgme_data_t output = NULL;
986   int abool;
987   gpgme_encrypt_flags_t encrypt_flags = 0;
988
989   if ((err = get_protocol (request, &protocol)))
990     goto leave;
991   ctx = get_context (protocol);
992   if ((err = get_chunksize (request, &chunksize)))
993     goto leave;
994
995   if ((err = get_boolean_flag (request, "base64", 0, &opt_base64)))
996     goto leave;
997   if ((err = get_boolean_flag (request, "mime", 0, &opt_mime)))
998     goto leave;
999
1000   if ((err = get_boolean_flag (request, "armor", 0, &abool)))
1001     goto leave;
1002   gpgme_set_armor (ctx, abool);
1003   if ((err = get_boolean_flag (request, "always-trust", 0, &abool)))
1004     goto leave;
1005   if (abool)
1006     encrypt_flags |= GPGME_ENCRYPT_ALWAYS_TRUST;
1007   if ((err = get_boolean_flag (request, "no-encrypt-to", 0,&abool)))
1008     goto leave;
1009   if (abool)
1010     encrypt_flags |= GPGME_ENCRYPT_NO_ENCRYPT_TO;
1011   if ((err = get_boolean_flag (request, "no-compress", 0, &abool)))
1012     goto leave;
1013   if (abool)
1014     encrypt_flags |= GPGME_ENCRYPT_NO_COMPRESS;
1015   if ((err = get_boolean_flag (request, "throw-keyids", 0, &abool)))
1016     goto leave;
1017   if (abool)
1018     encrypt_flags |= GPGME_ENCRYPT_THROW_KEYIDS;
1019   if ((err = get_boolean_flag (request, "wrap", 0, &abool)))
1020     goto leave;
1021   if (abool)
1022     encrypt_flags |= GPGME_ENCRYPT_WRAP;
1023   if ((err = get_boolean_flag (request, "want-address", 0, &abool)))
1024     goto leave;
1025   if (abool)
1026     encrypt_flags |= GPGME_ENCRYPT_WANT_ADDRESS;
1027
1028
1029   /* Get the keys.  */
1030   err = get_keys (request, &keystring);
1031   if (err)
1032     {
1033       /* Provide a custom error response.  */
1034       gpg_error_object (result, err, "Error getting keys: %s",
1035                         gpg_strerror (err));
1036       goto leave;
1037     }
1038
1039   /* Get the data.  Note that INPUT is a shallow data object with the
1040    * storage hold in REQUEST.  */
1041   j_input = cJSON_GetObjectItem (request, "data");
1042   if (!j_input)
1043     {
1044       err = gpg_error (GPG_ERR_NO_DATA);
1045       goto leave;
1046     }
1047   if (!cjson_is_string (j_input))
1048     {
1049       err = gpg_error (GPG_ERR_INV_VALUE);
1050       goto leave;
1051     }
1052   if (opt_base64)
1053     {
1054       err = data_from_base64_string (&input, j_input);
1055       if (err)
1056         {
1057           gpg_error_object (result, err, "Error decoding Base-64 encoded "
1058                             "'data': %s", gpg_strerror (err));
1059           goto leave;
1060         }
1061     }
1062   else
1063     {
1064       err = gpgme_data_new_from_mem (&input, j_input->valuestring,
1065                                      strlen (j_input->valuestring), 0);
1066       if (err)
1067         {
1068           gpg_error_object (result, err, "Error getting 'data': %s",
1069                             gpg_strerror (err));
1070           goto leave;
1071         }
1072     }
1073   if (opt_mime)
1074     gpgme_data_set_encoding (input, GPGME_DATA_ENCODING_MIME);
1075
1076
1077   /* Create an output data object.  */
1078   err = gpgme_data_new (&output);
1079   if (err)
1080     {
1081       gpg_error_object (result, err, "Error creating output data object: %s",
1082                         gpg_strerror (err));
1083       goto leave;
1084     }
1085
1086   /* Encrypt.  */
1087   err = gpgme_op_encrypt_ext (ctx, NULL, keystring, encrypt_flags,
1088                               input, output);
1089   /* encrypt_result = gpgme_op_encrypt_result (ctx); */
1090   if (err)
1091     {
1092       gpg_error_object (result, err, "Encryption failed: %s",
1093                         gpg_strerror (err));
1094       goto leave;
1095     }
1096   gpgme_data_release (input);
1097   input = NULL;
1098
1099   /* We need to base64 if armoring has not been requested.  */
1100   err = make_data_object (result, output, chunksize,
1101                           "ciphertext", !gpgme_get_armor (ctx));
1102   output = NULL;
1103
1104  leave:
1105   xfree (keystring);
1106   release_context (ctx);
1107   gpgme_data_release (input);
1108   gpgme_data_release (output);
1109   return err;
1110 }
1111
1112
1113 \f
1114 static const char hlp_decrypt[] =
1115   "op:     \"decrypt\"\n"
1116   "data:   The encrypted data.\n"
1117   "\n"
1118   "Optional parameters:\n"
1119   "protocol:      Either \"openpgp\" (default) or \"cms\".\n"
1120   "chunksize:     Max number of bytes in the resulting \"data\".\n"
1121   "\n"
1122   "Optional boolean flags (default is false):\n"
1123   "base64:        Input data is base64 encoded.\n"
1124   "\n"
1125   "Response on success:\n"
1126   "type:   \"plaintext\"\n"
1127   "data:   The decrypted data.  This may be base64 encoded.\n"
1128   "base64: Boolean indicating whether data is base64 encoded.\n"
1129   "mime:   A Boolean indicating whether the data is a MIME object.\n"
1130   "info:   An optional object with extra information.\n"
1131   "more:   Optional boolean indicating that \"getmore\" is required.";
1132 static gpg_error_t
1133 op_decrypt (cjson_t request, cjson_t result)
1134 {
1135   gpg_error_t err;
1136   gpgme_ctx_t ctx = NULL;
1137   gpgme_protocol_t protocol;
1138   size_t chunksize;
1139   int opt_base64;
1140   cjson_t j_input;
1141   gpgme_data_t input = NULL;
1142   gpgme_data_t output = NULL;
1143   gpgme_decrypt_result_t decrypt_result;
1144   gpgme_verify_result_t verify_result;
1145
1146   if ((err = get_protocol (request, &protocol)))
1147     goto leave;
1148   ctx = get_context (protocol);
1149   if ((err = get_chunksize (request, &chunksize)))
1150     goto leave;
1151
1152   if ((err = get_boolean_flag (request, "base64", 0, &opt_base64)))
1153     goto leave;
1154
1155   /* Get the data.  Note that INPUT is a shallow data object with the
1156    * storage hold in REQUEST.  */
1157   j_input = cJSON_GetObjectItem (request, "data");
1158   if (!j_input)
1159     {
1160       err = gpg_error (GPG_ERR_NO_DATA);
1161       goto leave;
1162     }
1163   if (!cjson_is_string (j_input))
1164     {
1165       err = gpg_error (GPG_ERR_INV_VALUE);
1166       goto leave;
1167     }
1168   if (opt_base64)
1169     {
1170       err = data_from_base64_string (&input, j_input);
1171       if (err)
1172         {
1173           gpg_error_object (result, err,
1174                             "Error decoding Base-64 encoded 'data': %s",
1175                             gpg_strerror (err));
1176           goto leave;
1177         }
1178     }
1179   else
1180     {
1181       err = gpgme_data_new_from_mem (&input, j_input->valuestring,
1182                                      strlen (j_input->valuestring), 0);
1183       if (err)
1184         {
1185           gpg_error_object (result, err, "Error getting 'data': %s",
1186                             gpg_strerror (err));
1187           goto leave;
1188         }
1189     }
1190
1191   /* Create an output data object.  */
1192   err = gpgme_data_new (&output);
1193   if (err)
1194     {
1195       gpg_error_object (result, err,
1196                         "Error creating output data object: %s",
1197                         gpg_strerror (err));
1198       goto leave;
1199     }
1200
1201   /* Decrypt.  */
1202   err = gpgme_op_decrypt_ext (ctx, GPGME_DECRYPT_VERIFY,
1203                               input, output);
1204   decrypt_result = gpgme_op_decrypt_result (ctx);
1205   if (err)
1206     {
1207       gpg_error_object (result, err, "Decryption failed: %s",
1208                         gpg_strerror (err));
1209       goto leave;
1210     }
1211   gpgme_data_release (input);
1212   input = NULL;
1213
1214   if (decrypt_result->is_mime)
1215     xjson_AddBoolToObject (result, "mime", 1);
1216
1217   verify_result = gpgme_op_verify_result (ctx);
1218   if (verify_result && verify_result->signatures)
1219     {
1220       err = add_signatures_object (result, "info", verify_result);
1221     }
1222
1223   if (err)
1224     {
1225       gpg_error_object (result, err, "Info output failed: %s",
1226                         gpg_strerror (err));
1227       goto leave;
1228     }
1229
1230   err = make_data_object (result, output, chunksize, "plaintext", -1);
1231   output = NULL;
1232
1233   if (err)
1234     {
1235       gpg_error_object (result, err, "Plaintext output failed: %s",
1236                         gpg_strerror (err));
1237       goto leave;
1238     }
1239
1240  leave:
1241   release_context (ctx);
1242   gpgme_data_release (input);
1243   gpgme_data_release (output);
1244   return err;
1245 }
1246
1247
1248 \f
1249 static const char hlp_sign[] =
1250   "op:     \"sign\"\n"
1251   "keys:   Array of strings with the fingerprints of the signing key.\n"
1252   "        For a single key a String may be used instead of an array.\n"
1253   "data:   Input data. \n"
1254   "\n"
1255   "Optional parameters:\n"
1256   "protocol:      Either \"openpgp\" (default) or \"cms\".\n"
1257   "chunksize:     Max number of bytes in the resulting \"data\".\n"
1258   "sender:        The mail address of the sender.\n"
1259   "mode:          A string with the signing mode can be:\n"
1260   "               detached (default)\n"
1261   "               opaque\n"
1262   "               clearsign\n"
1263   "\n"
1264   "Optional boolean flags (default is false):\n"
1265   "base64:        Input data is base64 encoded.\n"
1266   "armor:         Request output in armored format.\n"
1267   "\n"
1268   "Response on success:\n"
1269   "type:   \"signature\"\n"
1270   "data:   Unless armor mode is used a Base64 encoded binary\n"
1271   "        signature.  In armor mode a string with an armored\n"
1272   "        OpenPGP or a PEM message.\n"
1273   "base64: Boolean indicating whether data is base64 encoded.\n"
1274   "more:   Optional boolean indicating that \"getmore\" is required.";
1275 static gpg_error_t
1276 op_sign (cjson_t request, cjson_t result)
1277 {
1278   gpg_error_t err;
1279   gpgme_ctx_t ctx = NULL;
1280   gpgme_protocol_t protocol;
1281   size_t chunksize;
1282   int opt_base64;
1283   char *keystring = NULL;
1284   cjson_t j_input;
1285   gpgme_data_t input = NULL;
1286   gpgme_data_t output = NULL;
1287   int abool;
1288   cjson_t j_tmp;
1289   gpgme_sig_mode_t mode = GPGME_SIG_MODE_DETACH;
1290   gpgme_ctx_t keylist_ctx = NULL;
1291   gpgme_key_t key = NULL;
1292
1293   if ((err = get_protocol (request, &protocol)))
1294     goto leave;
1295   ctx = get_context (protocol);
1296   if ((err = get_chunksize (request, &chunksize)))
1297     goto leave;
1298
1299   if ((err = get_boolean_flag (request, "base64", 0, &opt_base64)))
1300     goto leave;
1301
1302   if ((err = get_boolean_flag (request, "armor", 0, &abool)))
1303     goto leave;
1304   gpgme_set_armor (ctx, abool);
1305
1306   j_tmp = cJSON_GetObjectItem (request, "mode");
1307   if (j_tmp && cjson_is_string (j_tmp))
1308     {
1309       if (!strcmp (j_tmp->valuestring, "opaque"))
1310         {
1311           mode = GPGME_SIG_MODE_NORMAL;
1312         }
1313       else if (!strcmp (j_tmp->valuestring, "clearsign"))
1314         {
1315           mode = GPGME_SIG_MODE_CLEAR;
1316         }
1317     }
1318
1319   j_tmp = cJSON_GetObjectItem (request, "sender");
1320   if (j_tmp && cjson_is_string (j_tmp))
1321     {
1322       gpgme_set_sender (ctx, j_tmp->valuestring);
1323     }
1324
1325   /* Get the keys.  */
1326   err = get_keys (request, &keystring);
1327   if (err)
1328     {
1329       /* Provide a custom error response.  */
1330       gpg_error_object (result, err, "Error getting keys: %s",
1331                         gpg_strerror (err));
1332       goto leave;
1333     }
1334
1335   /* Do a keylisting and add the keys */
1336   if ((err = gpgme_new (&keylist_ctx)))
1337     goto leave;
1338   gpgme_set_protocol (keylist_ctx, protocol);
1339   gpgme_set_keylist_mode (keylist_ctx, GPGME_KEYLIST_MODE_LOCAL);
1340
1341   err = gpgme_op_keylist_start (ctx, keystring, 1);
1342   if (err)
1343     {
1344       gpg_error_object (result, err, "Error listing keys: %s",
1345                         gpg_strerror (err));
1346       goto leave;
1347     }
1348   while (!(err = gpgme_op_keylist_next (ctx, &key)))
1349     {
1350       if ((err = gpgme_signers_add (ctx, key)))
1351         {
1352           gpg_error_object (result, err, "Error adding signer: %s",
1353                             gpg_strerror (err));
1354           goto leave;
1355         }
1356       gpgme_key_unref (key);
1357     }
1358
1359   /* Get the data.  Note that INPUT is a shallow data object with the
1360    * storage hold in REQUEST.  */
1361   j_input = cJSON_GetObjectItem (request, "data");
1362   if (!j_input)
1363     {
1364       err = gpg_error (GPG_ERR_NO_DATA);
1365       goto leave;
1366     }
1367   if (!cjson_is_string (j_input))
1368     {
1369       err = gpg_error (GPG_ERR_INV_VALUE);
1370       goto leave;
1371     }
1372   if (opt_base64)
1373     {
1374       err = data_from_base64_string (&input, j_input);
1375       if (err)
1376         {
1377           gpg_error_object (result, err,
1378                             "Error decoding Base-64 encoded 'data': %s",
1379                             gpg_strerror (err));
1380           goto leave;
1381         }
1382     }
1383   else
1384     {
1385       err = gpgme_data_new_from_mem (&input, j_input->valuestring,
1386                                      strlen (j_input->valuestring), 0);
1387       if (err)
1388         {
1389           gpg_error_object (result, err, "Error getting 'data': %s",
1390                             gpg_strerror (err));
1391           goto leave;
1392         }
1393     }
1394
1395   /* Create an output data object.  */
1396   err = gpgme_data_new (&output);
1397   if (err)
1398     {
1399       gpg_error_object (result, err, "Error creating output data object: %s",
1400                         gpg_strerror (err));
1401       goto leave;
1402     }
1403
1404   /* Sign. */
1405   err = gpgme_op_sign (ctx, input, output, mode);
1406   if (err)
1407     {
1408       gpg_error_object (result, err, "Signing failed: %s",
1409                         gpg_strerror (err));
1410       goto leave;
1411     }
1412
1413   gpgme_data_release (input);
1414   input = NULL;
1415
1416   /* We need to base64 if armoring has not been requested.  */
1417   err = make_data_object (result, output, chunksize,
1418                           "ciphertext", !gpgme_get_armor (ctx));
1419   output = NULL;
1420
1421  leave:
1422   xfree (keystring);
1423   release_context (ctx);
1424   release_context (keylist_ctx);
1425   gpgme_data_release (input);
1426   gpgme_data_release (output);
1427   return err;
1428 }
1429 \f
1430 static const char hlp_verify[] =
1431   "op:     \"verify\"\n"
1432   "data:   The data to verify.\n"
1433   "\n"
1434   "Optional parameters:\n"
1435   "protocol:      Either \"openpgp\" (default) or \"cms\".\n"
1436   "chunksize:     Max number of bytes in the resulting \"data\".\n"
1437   "signature:     A detached signature. If missing opaque is assumed.\n"
1438   "\n"
1439   "Optional boolean flags (default is false):\n"
1440   "base64:        Input data is base64 encoded.\n"
1441   "\n"
1442   "Response on success:\n"
1443   "type:   \"plaintext\"\n"
1444   "data:   The verified data.  This may be base64 encoded.\n"
1445   "base64: Boolean indicating whether data is base64 encoded.\n"
1446   "info:   An object with signature information.\n"
1447   "more:   Optional boolean indicating that \"getmore\" is required.";
1448 static gpg_error_t
1449 op_verify (cjson_t request, cjson_t result)
1450 {
1451   gpg_error_t err;
1452   gpgme_ctx_t ctx = NULL;
1453   gpgme_protocol_t protocol;
1454   size_t chunksize;
1455   int opt_base64;
1456   cjson_t j_input, j_signature;
1457   gpgme_data_t input = NULL;
1458   gpgme_data_t signature = NULL;
1459   gpgme_data_t output = NULL;
1460   gpgme_verify_result_t verify_result;
1461
1462   if ((err = get_protocol (request, &protocol)))
1463     goto leave;
1464   ctx = get_context (protocol);
1465   if ((err = get_chunksize (request, &chunksize)))
1466     goto leave;
1467
1468   if ((err = get_boolean_flag (request, "base64", 0, &opt_base64)))
1469     goto leave;
1470
1471   /* Get the data.  Note that INPUT is a shallow data object with the
1472    * storage hold in REQUEST.  */
1473   j_input = cJSON_GetObjectItem (request, "data");
1474   if (!j_input)
1475     {
1476       err = gpg_error (GPG_ERR_NO_DATA);
1477       goto leave;
1478     }
1479   if (!cjson_is_string (j_input))
1480     {
1481       err = gpg_error (GPG_ERR_INV_VALUE);
1482       goto leave;
1483     }
1484   if (opt_base64)
1485     {
1486       err = data_from_base64_string (&input, j_input);
1487       if (err)
1488         {
1489           gpg_error_object (result, err, "Error decoding Base-64 encoded "
1490                             "'data': %s", gpg_strerror (err));
1491           goto leave;
1492         }
1493     }
1494   else
1495     {
1496       err = gpgme_data_new_from_mem (&input, j_input->valuestring,
1497                                      strlen (j_input->valuestring), 0);
1498       if (err)
1499         {
1500           gpg_error_object (result, err, "Error getting 'data': %s",
1501                             gpg_strerror (err));
1502           goto leave;
1503         }
1504     }
1505
1506   /* Get the signature.  */
1507   j_signature = cJSON_GetObjectItem (request, "signature");
1508   if (j_signature && cjson_is_string (j_signature))
1509     {
1510       if (opt_base64)
1511         {
1512           err = data_from_base64_string (&signature, j_signature);
1513           if (err)
1514             {
1515               gpg_error_object (result, err, "Error decoding Base-64 encoded "
1516                                 "'signature': %s", gpg_strerror (err));
1517               goto leave;
1518             }
1519         }
1520       else
1521         {
1522           err = gpgme_data_new_from_mem (&signature, j_signature->valuestring,
1523                                          strlen (j_signature->valuestring),
1524                                          0);
1525           if (err)
1526             {
1527               gpg_error_object (result, err, "Error getting 'signature': %s",
1528                                 gpg_strerror (err));
1529               goto leave;
1530             }
1531         }
1532     }
1533
1534   /* Create an output data object.  */
1535   err = gpgme_data_new (&output);
1536   if (err)
1537     {
1538       gpg_error_object (result, err, "Error creating output data object: %s",
1539                         gpg_strerror (err));
1540       goto leave;
1541     }
1542
1543   gpgme_data_rewind (input);
1544   gpgme_data_rewind (signature);
1545
1546   /* Verify.  */
1547   if (signature)
1548     {
1549       err = gpgme_op_verify (ctx, signature, input, output);
1550     }
1551   else
1552     {
1553       err = gpgme_op_verify (ctx, input, 0, output);
1554     }
1555   if (err)
1556     {
1557       gpg_error_object (result, err, "Verify failed: %s", gpg_strerror (err));
1558       goto leave;
1559     }
1560   gpgme_data_release (input);
1561   input = NULL;
1562   gpgme_data_release (signature);
1563   signature = NULL;
1564
1565   verify_result = gpgme_op_verify_result (ctx);
1566   if (verify_result && verify_result->signatures)
1567     {
1568       err = add_signatures_object (result, "info", verify_result);
1569     }
1570
1571   if (err)
1572     {
1573       gpg_error_object (result, err, "Info output failed: %s",
1574                     gpg_strerror (err));
1575       goto leave;
1576     }
1577
1578   err = make_data_object (result, output, chunksize, "plaintext", -1);
1579   output = NULL;
1580
1581   if (err)
1582     {
1583       gpg_error_object (result, err, "Plaintext output failed: %s",
1584                         gpg_strerror (err));
1585       goto leave;
1586     }
1587
1588  leave:
1589   release_context (ctx);
1590   gpgme_data_release (input);
1591   gpgme_data_release (output);
1592   gpgme_data_release (signature);
1593   return err;
1594 }
1595 \f
1596 static const char hlp_version[] =
1597   "op:     \"version\"\n"
1598   "\n"
1599   "Response on success:\n"
1600   "gpgme:  The GPGME Version.\n"
1601   "info:   dump of engine info. containing:\n"
1602   "        protocol: The protocol.\n"
1603   "        fname:    The file name.\n"
1604   "        version:  The version.\n"
1605   "        req_ver:  The required version.\n"
1606   "        homedir:  The homedir of the engine or \"default\".\n";
1607 static gpg_error_t
1608 op_version (cjson_t request, cjson_t result)
1609 {
1610   gpg_error_t err = 0;
1611   gpgme_engine_info_t ei = NULL;
1612   cjson_t infos = xjson_CreateArray ();
1613
1614   if (!cJSON_AddStringToObject (result, "gpgme", gpgme_check_version (NULL)))
1615     {
1616       cJSON_Delete (infos);
1617       return gpg_error_from_syserror ();
1618     }
1619
1620   if ((err = gpgme_get_engine_info (&ei)))
1621     {
1622       cJSON_Delete (infos);
1623       return err;
1624     }
1625
1626   for (; ei; ei = ei->next)
1627     {
1628       cjson_t obj = xjson_CreateObject ();
1629       if ((err = add_ei_to_object (obj, ei)))
1630         {
1631           cJSON_Delete (infos);
1632           return err;
1633         }
1634       cJSON_AddItemToArray (infos, obj);
1635     }
1636
1637   if (!cJSON_AddItemToObject (result, "info", infos))
1638     {
1639       err = gpg_error_from_syserror ();
1640       cJSON_Delete (infos);
1641       return err;
1642     }
1643
1644   return 0;
1645 }
1646 \f
1647 static const char hlp_getmore[] =
1648   "op:     \"getmore\"\n"
1649   "\n"
1650   "Optional parameters:\n"
1651   "chunksize:  Max number of bytes in the \"data\" object.\n"
1652   "\n"
1653   "Response on success:\n"
1654   "type:       Type of the pending data\n"
1655   "data:       The next chunk of data\n"
1656   "base64:     Boolean indicating whether data is base64 encoded\n"
1657   "more:       Optional boolean requesting another \"getmore\".";
1658 static gpg_error_t
1659 op_getmore (cjson_t request, cjson_t result)
1660 {
1661   gpg_error_t err;
1662   int c;
1663   size_t n;
1664   size_t chunksize;
1665
1666   if ((err = get_chunksize (request, &chunksize)))
1667     goto leave;
1668
1669   /* Adjust the chunksize if we need to do base64 conversion.  */
1670   if (pending_data.base64)
1671     chunksize = (chunksize / 4) * 3;
1672
1673   /* Do we have anything pending?  */
1674   if (!pending_data.buffer)
1675     {
1676       err = gpg_error (GPG_ERR_NO_DATA);
1677       gpg_error_object (result, err, "Operation not possible: %s",
1678                         gpg_strerror (err));
1679       goto leave;
1680     }
1681
1682   xjson_AddStringToObject (result, "type", pending_data.type);
1683   xjson_AddBoolToObject (result, "base64", pending_data.base64);
1684
1685   if (pending_data.written >= pending_data.length)
1686     {
1687       /* EOF reached.  This should not happen but we return an empty
1688        * string once in case of client errors.  */
1689       gpgme_free (pending_data.buffer);
1690       pending_data.buffer = NULL;
1691       xjson_AddBoolToObject (result, "more", 0);
1692       err = cjson_AddStringToObject (result, "data", "");
1693     }
1694   else
1695     {
1696       n = pending_data.length - pending_data.written;
1697       if (n > chunksize)
1698         {
1699           n = chunksize;
1700           xjson_AddBoolToObject (result, "more", 1);
1701         }
1702       else
1703         xjson_AddBoolToObject (result, "more", 0);
1704
1705       c = pending_data.buffer[pending_data.written + n];
1706       pending_data.buffer[pending_data.written + n] = 0;
1707       if (pending_data.base64)
1708         err = add_base64_to_object (result, "data",
1709                                     (pending_data.buffer
1710                                      + pending_data.written), n);
1711       else
1712         err = cjson_AddStringToObject (result, "data",
1713                                        (pending_data.buffer
1714                                         + pending_data.written));
1715       pending_data.buffer[pending_data.written + n] = c;
1716       if (!err)
1717         {
1718           pending_data.written += n;
1719           if (pending_data.written >= pending_data.length)
1720             {
1721               gpgme_free (pending_data.buffer);
1722               pending_data.buffer = NULL;
1723             }
1724         }
1725     }
1726
1727  leave:
1728   return err;
1729 }
1730
1731
1732 \f
1733 static const char hlp_help[] =
1734   "The tool expects a JSON object with the request and responds with\n"
1735   "another JSON object.  Even on error a JSON object is returned.  The\n"
1736   "property \"op\" is mandatory and its string value selects the\n"
1737   "operation; if the property \"help\" with the value \"true\" exists, the\n"
1738   "operation is not performned but a string with the documentation\n"
1739   "returned.  To list all operations it is allowed to leave out \"op\" in\n"
1740   "help mode.  Supported values for \"op\" are:\n\n"
1741   "  encrypt     Encrypt data.\n"
1742   "  decrypt     Decrypt data.\n"
1743   "  sign        Sign data.\n"
1744   "  getmore     Retrieve remaining data.\n"
1745   "  help        Help overview.";
1746 static gpg_error_t
1747 op_help (cjson_t request, cjson_t result)
1748 {
1749   cjson_t j_tmp;
1750   char *buffer = NULL;
1751   const char *msg;
1752
1753   j_tmp = cJSON_GetObjectItem (request, "interactive_help");
1754   if (opt_interactive && j_tmp && cjson_is_string (j_tmp))
1755     msg = buffer = xstrconcat (hlp_help, "\n", j_tmp->valuestring, NULL);
1756   else
1757     msg = hlp_help;
1758
1759   xjson_AddStringToObject (result, "type", "help");
1760   xjson_AddStringToObject (result, "msg", msg);
1761
1762   xfree (buffer);
1763   return 0;
1764 }
1765
1766
1767 \f
1768 /*
1769  * Dispatcher
1770  */
1771
1772 /* Process a request and return the response.  The response is a newly
1773  * allocated string or NULL in case of an error.  */
1774 static char *
1775 process_request (const char *request)
1776 {
1777   static struct {
1778     const char *op;
1779     gpg_error_t (*handler)(cjson_t request, cjson_t result);
1780     const char * const helpstr;
1781   } optbl[] = {
1782     { "encrypt", op_encrypt, hlp_encrypt },
1783     { "decrypt", op_decrypt, hlp_decrypt },
1784     { "sign",    op_sign,    hlp_sign },
1785     { "verify",  op_verify,  hlp_verify },
1786     { "version", op_version, hlp_version },
1787     { "getmore", op_getmore, hlp_getmore },
1788     { "help",    op_help,    hlp_help },
1789     { NULL }
1790   };
1791   size_t erroff;
1792   cjson_t json;
1793   cjson_t j_tmp, j_op;
1794   cjson_t response;
1795   int helpmode;
1796   const char *op;
1797   char *res;
1798   int idx;
1799
1800   response = xjson_CreateObject ();
1801
1802   json = cJSON_Parse (request, &erroff);
1803   if (!json)
1804     {
1805       log_string (GPGRT_LOGLVL_INFO, request);
1806       log_info ("invalid JSON object at offset %zu\n", erroff);
1807       error_object (response, "invalid JSON object at offset %zu\n", erroff);
1808       goto leave;
1809     }
1810
1811   j_tmp = cJSON_GetObjectItem (json, "help");
1812   helpmode = (j_tmp && cjson_is_true (j_tmp));
1813
1814   j_op = cJSON_GetObjectItem (json, "op");
1815   if (!j_op || !cjson_is_string (j_op))
1816     {
1817       if (!helpmode)
1818         {
1819           error_object (response, "Property \"op\" missing");
1820           goto leave;
1821         }
1822       op = "help";  /* Help summary.  */
1823     }
1824   else
1825     op = j_op->valuestring;
1826
1827   for (idx=0; optbl[idx].op; idx++)
1828     if (!strcmp (op, optbl[idx].op))
1829       break;
1830   if (optbl[idx].op)
1831     {
1832       if (helpmode && strcmp (op, "help"))
1833         {
1834           xjson_AddStringToObject (response, "type", "help");
1835           xjson_AddStringToObject (response, "op", op);
1836           xjson_AddStringToObject (response, "msg", optbl[idx].helpstr);
1837         }
1838       else
1839         {
1840           gpg_error_t err;
1841
1842           /* If this is not the "getmore" command and we have any
1843            * pending data release that data.  */
1844           if (pending_data.buffer && optbl[idx].handler != op_getmore)
1845             {
1846               gpgme_free (pending_data.buffer);
1847               pending_data.buffer = NULL;
1848             }
1849
1850           err = optbl[idx].handler (json, response);
1851           if (err)
1852             {
1853               if (!(j_tmp = cJSON_GetObjectItem (response, "type"))
1854                   || !cjson_is_string (j_tmp)
1855                   || strcmp (j_tmp->valuestring, "error"))
1856                 {
1857                   /* No error type response - provide a generic one.  */
1858                   gpg_error_object (response, err, "Operation failed: %s",
1859                                     gpg_strerror (err));
1860                 }
1861
1862               xjson_AddStringToObject (response, "op", op);
1863             }
1864         }
1865     }
1866   else  /* Operation not supported.  */
1867     {
1868       error_object (response, "Unknown operation '%s'", op);
1869       xjson_AddStringToObject (response, "op", op);
1870     }
1871
1872  leave:
1873   cJSON_Delete (json);
1874   if (opt_interactive)
1875     res = cJSON_Print (response);
1876   else
1877     res = cJSON_PrintUnformatted (response);
1878   if (!res)
1879     log_error ("Printing JSON data failed\n");
1880   cJSON_Delete (response);
1881   return res;
1882 }
1883
1884
1885 \f
1886 /*
1887  *  Driver code
1888  */
1889
1890 static char *
1891 get_file (const char *fname)
1892 {
1893   gpg_error_t err;
1894   estream_t fp;
1895   struct stat st;
1896   char *buf;
1897   size_t buflen;
1898
1899   fp = es_fopen (fname, "r");
1900   if (!fp)
1901     {
1902       err = gpg_error_from_syserror ();
1903       log_error ("can't open '%s': %s\n", fname, gpg_strerror (err));
1904       return NULL;
1905     }
1906
1907   if (fstat (es_fileno(fp), &st))
1908     {
1909       err = gpg_error_from_syserror ();
1910       log_error ("can't stat '%s': %s\n", fname, gpg_strerror (err));
1911       es_fclose (fp);
1912       return NULL;
1913     }
1914
1915   buflen = st.st_size;
1916   buf = xmalloc (buflen+1);
1917   if (es_fread (buf, buflen, 1, fp) != 1)
1918     {
1919       err = gpg_error_from_syserror ();
1920       log_error ("error reading '%s': %s\n", fname, gpg_strerror (err));
1921       es_fclose (fp);
1922       xfree (buf);
1923       return NULL;
1924     }
1925   buf[buflen] = 0;
1926   es_fclose (fp);
1927
1928   return buf;
1929 }
1930
1931
1932 /* Return a malloced line or NULL on EOF.  Terminate on read
1933  * error.  */
1934 static char *
1935 get_line (void)
1936 {
1937   char *line = NULL;
1938   size_t linesize = 0;
1939   gpg_error_t err;
1940   size_t maxlength = 2048;
1941   int n;
1942   const char *s;
1943   char *p;
1944
1945  again:
1946   n = es_read_line (es_stdin, &line, &linesize, &maxlength);
1947   if (n < 0)
1948     {
1949       err = gpg_error_from_syserror ();
1950       log_error ("error reading line: %s\n", gpg_strerror (err));
1951       exit (1);
1952     }
1953   if (!n)
1954     {
1955       xfree (line);
1956       line = NULL;
1957       return NULL;  /* EOF */
1958     }
1959   if (!maxlength)
1960     {
1961       log_info ("line too long - skipped\n");
1962       goto again;
1963     }
1964   if (memchr (line, 0, n))
1965     log_info ("warning: line shortened due to embedded Nul character\n");
1966
1967   if (line[n-1] == '\n')
1968     line[n-1] = 0;
1969
1970   /* Trim leading spaces.  */
1971   for (s=line; spacep (s); s++)
1972     ;
1973   if (s != line)
1974     {
1975       for (p=line; *s;)
1976         *p++ = *s++;
1977       *p = 0;
1978       n = p - line;
1979     }
1980
1981   return line;
1982 }
1983
1984
1985 /* Process meta commands used with the standard REPL.  */
1986 static char *
1987 process_meta_commands (const char *request)
1988 {
1989   char *result = NULL;
1990
1991   while (spacep (request))
1992     request++;
1993
1994   if (!strncmp (request, "help", 4) && (spacep (request+4) || !request[4]))
1995     {
1996       if (request[4])
1997         {
1998           char *buf = xstrconcat ("{ \"help\":true, \"op\":\"", request+5,
1999                                   "\" }", NULL);
2000           result = process_request (buf);
2001           xfree (buf);
2002         }
2003       else
2004         result = process_request ("{ \"op\": \"help\","
2005                                   " \"interactive_help\": "
2006                                   "\"\\nMeta commands:\\n"
2007                                   "  ,read FNAME Process data from FILE\\n"
2008                                   "  ,help CMD   Print help for a command\\n"
2009                                   "  ,quit       Terminate process\""
2010                                   "}");
2011     }
2012   else if (!strncmp (request, "quit", 4) && (spacep (request+4) || !request[4]))
2013     exit (0);
2014   else if (!strncmp (request, "read", 4) && (spacep (request+4) || !request[4]))
2015     {
2016       if (!request[4])
2017         log_info ("usage: ,read FILENAME\n");
2018       else
2019         {
2020           char *buffer = get_file (request + 5);
2021           if (buffer)
2022             {
2023               result = process_request (buffer);
2024               xfree (buffer);
2025             }
2026         }
2027     }
2028   else
2029     log_info ("invalid meta command\n");
2030
2031   return result;
2032 }
2033
2034
2035 /* If STRING has a help response, return the MSG property in a human
2036  * readable format.  */
2037 static char *
2038 get_help_msg (const char *string)
2039 {
2040   cjson_t json, j_type, j_msg;
2041   const char *msg;
2042   char *buffer = NULL;
2043   char *p;
2044
2045   json = cJSON_Parse (string, NULL);
2046   if (json)
2047     {
2048       j_type = cJSON_GetObjectItem (json, "type");
2049       if (j_type && cjson_is_string (j_type)
2050           && !strcmp (j_type->valuestring, "help"))
2051         {
2052           j_msg = cJSON_GetObjectItem (json, "msg");
2053           if (j_msg || cjson_is_string (j_msg))
2054             {
2055               msg = j_msg->valuestring;
2056               buffer = malloc (strlen (msg)+1);
2057               if (buffer)
2058                 {
2059                   for (p=buffer; *msg; msg++)
2060                     {
2061                       if (*msg == '\\' && msg[1] == '\n')
2062                         *p++ = '\n';
2063                       else
2064                         *p++ = *msg;
2065                     }
2066                   *p = 0;
2067                 }
2068             }
2069         }
2070       cJSON_Delete (json);
2071     }
2072   return buffer;
2073 }
2074
2075
2076 /* An interactive standard REPL.  */
2077 static void
2078 interactive_repl (void)
2079 {
2080   char *line = NULL;
2081   char *request = NULL;
2082   char *response = NULL;
2083   char *p;
2084   int first;
2085
2086   es_setvbuf (es_stdin, NULL, _IONBF, 0);
2087 #if GPGRT_VERSION_NUMBER >= 0x011d00 /* 1.29 */
2088   es_fprintf (es_stderr, "%s %s ready (enter \",help\" for help)\n",
2089               gpgrt_strusage (11), gpgrt_strusage (13));
2090 #endif
2091   do
2092     {
2093       es_fputs ("> ", es_stderr);
2094       es_fflush (es_stderr);
2095       es_fflush (es_stdout);
2096       xfree (line);
2097       line = get_line ();
2098       es_fflush (es_stderr);
2099       es_fflush (es_stdout);
2100
2101       first = !request;
2102       if (line && *line)
2103         {
2104           if (!request)
2105             request = xstrdup (line);
2106           else
2107             request = xstrconcat (request, "\n", line, NULL);
2108         }
2109
2110       if (!line)
2111         es_fputs ("\n", es_stderr);
2112
2113       if (!line || !*line || (first && *request == ','))
2114         {
2115           /* Process the input.  */
2116           xfree (response);
2117           response = NULL;
2118           if (request && *request == ',')
2119             {
2120               response = process_meta_commands (request+1);
2121             }
2122           else if (request)
2123             {
2124               response = process_request (request);
2125             }
2126           xfree (request);
2127           request = NULL;
2128
2129           if (response)
2130             {
2131               if (opt_interactive)
2132                 {
2133                   char *msg = get_help_msg (response);
2134                   if (msg)
2135                     {
2136                       xfree (response);
2137                       response = msg;
2138                     }
2139                 }
2140
2141               es_fputs ("===> ", es_stderr);
2142               es_fflush (es_stderr);
2143               for (p=response; *p; p++)
2144                 {
2145                   if (*p == '\n')
2146                     {
2147                       es_fflush (es_stdout);
2148                       es_fputs ("\n===> ", es_stderr);
2149                       es_fflush (es_stderr);
2150                     }
2151                   else
2152                     es_putc (*p, es_stdout);
2153                 }
2154               es_fflush (es_stdout);
2155               es_fputs ("\n", es_stderr);
2156             }
2157         }
2158     }
2159   while (line);
2160
2161   xfree (request);
2162   xfree (response);
2163   xfree (line);
2164 }
2165
2166
2167 /* Read and process a single request.  */
2168 static void
2169 read_and_process_single_request (void)
2170 {
2171   char *line = NULL;
2172   char *request = NULL;
2173   char *response = NULL;
2174   size_t n;
2175
2176   for (;;)
2177     {
2178       xfree (line);
2179       line = get_line ();
2180       if (line && *line)
2181         request = (request? xstrconcat (request, "\n", line, NULL)
2182                    /**/   : xstrdup (line));
2183       if (!line)
2184         {
2185           if (request)
2186             {
2187               xfree (response);
2188               response = process_request (request);
2189               if (response)
2190                 {
2191                   es_fputs (response, es_stdout);
2192                   if ((n = strlen (response)) && response[n-1] != '\n')
2193                     es_fputc ('\n', es_stdout);
2194                 }
2195               es_fflush (es_stdout);
2196             }
2197           break;
2198         }
2199     }
2200
2201   xfree (response);
2202   xfree (request);
2203   xfree (line);
2204 }
2205
2206
2207 /* The Native Messaging processing loop.  */
2208 static void
2209 native_messaging_repl (void)
2210 {
2211   gpg_error_t err;
2212   uint32_t nrequest, nresponse;
2213   char *request = NULL;
2214   char *response = NULL;
2215   size_t n;
2216
2217   /* Due to the length octets we need to switch the I/O stream into
2218    * binary mode.  */
2219   es_set_binary (es_stdin);
2220   es_set_binary (es_stdout);
2221   es_setbuf (es_stdin, NULL);  /* stdin needs to be unbuffered! */
2222
2223   for (;;)
2224     {
2225       /* Read length.  Note that the protocol uses native endianess.
2226        * Is it allowed to call such a thing a well thought out
2227        * protocol?  */
2228       if (es_read (es_stdin, &nrequest, sizeof nrequest, &n))
2229         {
2230           err = gpg_error_from_syserror ();
2231           log_error ("error reading request header: %s\n", gpg_strerror (err));
2232           break;
2233         }
2234       if (!n)
2235         break;  /* EOF */
2236       if (n != sizeof nrequest)
2237         {
2238           log_error ("error reading request header: short read\n");
2239           break;
2240         }
2241       if (nrequest > MAX_REQUEST_SIZE)
2242         {
2243           log_error ("error reading request: request too long (%zu MiB)\n",
2244                      (size_t)nrequest / (1024*1024));
2245           /* Fixme: Shall we read the request to the bit bucket and
2246            * return an error reponse or just return an error reponse
2247            * and terminate?  Needs some testing.  */
2248           break;
2249         }
2250
2251       /* Read request.  */
2252       request = xtrymalloc (nrequest);
2253       if (!request)
2254         {
2255           err = gpg_error_from_syserror ();
2256           log_error ("error reading request: Not enough memory for %zu MiB)\n",
2257                      (size_t)nrequest / (1024*1024));
2258           /* FIXME: See comment above.  */
2259           break;
2260         }
2261       if (es_read (es_stdin, request, nrequest, &n))
2262         {
2263           err = gpg_error_from_syserror ();
2264           log_error ("error reading request: %s\n", gpg_strerror (err));
2265           break;
2266         }
2267       if (n != nrequest)
2268         {
2269           /* That is a protocol violation.  */
2270           xfree (response);
2271           response = error_object_string ("Invalid request:"
2272                                           " short read (%zu of %zu bytes)\n",
2273                                           n, (size_t)nrequest);
2274         }
2275       else /* Process request  */
2276         {
2277           if (opt_debug)
2278             log_debug ("request='%s'\n", request);
2279           xfree (response);
2280           response = process_request (request);
2281           if (opt_debug)
2282             log_debug ("response='%s'\n", response);
2283         }
2284       nresponse = strlen (response);
2285
2286       /* Write response */
2287       if (es_write (es_stdout, &nresponse, sizeof nresponse, &n))
2288         {
2289           err = gpg_error_from_syserror ();
2290           log_error ("error writing request header: %s\n", gpg_strerror (err));
2291           break;
2292         }
2293       if (n != sizeof nrequest)
2294         {
2295           log_error ("error writing request header: short write\n");
2296           break;
2297         }
2298       if (es_write (es_stdout, response, nresponse, &n))
2299         {
2300           err = gpg_error_from_syserror ();
2301           log_error ("error writing request: %s\n", gpg_strerror (err));
2302           break;
2303         }
2304       if (n != nresponse)
2305         {
2306           log_error ("error writing request: short write\n");
2307           break;
2308         }
2309       if (es_fflush (es_stdout) || es_ferror (es_stdout))
2310         {
2311           err = gpg_error_from_syserror ();
2312           log_error ("error writing request: %s\n", gpg_strerror (err));
2313           break;
2314         }
2315     }
2316
2317   xfree (response);
2318   xfree (request);
2319 }
2320
2321
2322 \f
2323 static const char *
2324 my_strusage( int level )
2325 {
2326   const char *p;
2327
2328   switch (level)
2329     {
2330     case  9: p = "LGPL-2.1-or-later"; break;
2331     case 11: p = "gpgme-json"; break;
2332     case 13: p = PACKAGE_VERSION; break;
2333     case 14: p = "Copyright (C) 2018 g10 Code GmbH"; break;
2334     case 19: p = "Please report bugs to <" PACKAGE_BUGREPORT ">.\n"; break;
2335     case 1:
2336     case 40:
2337       p = "Usage: gpgme-json [OPTIONS]";
2338       break;
2339     case 41:
2340       p = "Native messaging based GPGME operations.\n";
2341       break;
2342     case 42:
2343       p = "1"; /* Flag print 40 as part of 41. */
2344       break;
2345     default: p = NULL; break;
2346     }
2347   return p;
2348 }
2349
2350 int
2351 main (int argc, char *argv[])
2352 {
2353 #if GPGRT_VERSION_NUMBER < 0x011d00 /* 1.29 */
2354
2355   fprintf (stderr, "WARNING: Old libgpg-error - using limited mode\n");
2356   native_messaging_repl ();
2357
2358 #else /* This is a modern libgp-error.  */
2359
2360   enum { CMD_DEFAULT     = 0,
2361          CMD_INTERACTIVE = 'i',
2362          CMD_SINGLE      = 's',
2363          CMD_LIBVERSION  = 501,
2364   } cmd = CMD_DEFAULT;
2365   enum {
2366     OPT_DEBUG = 600
2367   };
2368
2369   static gpgrt_opt_t opts[] = {
2370     ARGPARSE_c  (CMD_INTERACTIVE, "interactive", "Interactive REPL"),
2371     ARGPARSE_c  (CMD_SINGLE,      "single",      "Single request mode"),
2372     ARGPARSE_c  (CMD_LIBVERSION,  "lib-version", "Show library version"),
2373     ARGPARSE_s_n(OPT_DEBUG,       "debug",       "Flyswatter"),
2374
2375     ARGPARSE_end()
2376   };
2377   gpgrt_argparse_t pargs = { &argc, &argv};
2378
2379   gpgrt_set_strusage (my_strusage);
2380
2381 #ifdef HAVE_SETLOCALE
2382   setlocale (LC_ALL, "");
2383 #endif
2384   gpgme_check_version (NULL);
2385 #ifdef LC_CTYPE
2386   gpgme_set_locale (NULL, LC_CTYPE, setlocale (LC_CTYPE, NULL));
2387 #endif
2388 #ifdef LC_MESSAGES
2389   gpgme_set_locale (NULL, LC_MESSAGES, setlocale (LC_MESSAGES, NULL));
2390 #endif
2391
2392   while (gpgrt_argparse (NULL, &pargs, opts))
2393     {
2394       switch (pargs.r_opt)
2395         {
2396         case CMD_INTERACTIVE:
2397           opt_interactive = 1;
2398           /* Fall trough.  */
2399         case CMD_SINGLE:
2400         case CMD_LIBVERSION:
2401           cmd = pargs.r_opt;
2402           break;
2403
2404         case OPT_DEBUG: opt_debug = 1; break;
2405
2406         default:
2407           pargs.err = ARGPARSE_PRINT_WARNING;
2408           break;
2409         }
2410     }
2411   gpgrt_argparse (NULL, &pargs, NULL);
2412
2413   if (!opt_debug)
2414     {
2415       const char *s = getenv ("GPGME_JSON_DEBUG");
2416       if (s && atoi (s) > 0)
2417         opt_debug = 1;
2418     }
2419
2420   if (opt_debug)
2421     {
2422       const char *home = getenv ("HOME");
2423       char *file = xstrconcat ("socket://",
2424                                home? home:"/tmp",
2425                                "/.gnupg/S.gpgme-json.log", NULL);
2426       log_set_file (file);
2427       xfree (file);
2428     }
2429
2430   if (opt_debug)
2431     { int i;
2432       for (i=0; argv[i]; i++)
2433         log_debug ("argv[%d]='%s'\n", i, argv[i]);
2434     }
2435
2436   switch (cmd)
2437     {
2438     case CMD_DEFAULT:
2439       native_messaging_repl ();
2440       break;
2441
2442     case CMD_SINGLE:
2443       read_and_process_single_request ();
2444       break;
2445
2446     case CMD_INTERACTIVE:
2447       interactive_repl ();
2448       break;
2449
2450     case CMD_LIBVERSION:
2451       printf ("Version from header: %s (0x%06x)\n",
2452               GPGME_VERSION, GPGME_VERSION_NUMBER);
2453       printf ("Version from binary: %s\n", gpgme_check_version (NULL));
2454       printf ("Copyright blurb ...:%s\n", gpgme_check_version ("\x01\x01"));
2455       break;
2456     }
2457
2458   if (opt_debug)
2459     log_debug ("ready");
2460
2461 #endif /* This is a modern libgp-error.  */
2462   return 0;
2463 }
2464 #endif /* libgpg-error >= 1.28 */