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