json: Rework verify_result_to_json
[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 /* Free a NULL terminated array */
132 static void
133 xfree_array (char **array)
134 {
135   if (array)
136     {
137       int idx;
138       for (idx = 0; array[idx]; idx++)
139         xfree (array[idx]);
140       xfree (array);
141     }
142 }
143
144
145 static void
146 xoutofcore (const char *type)
147 {
148   gpg_error_t err = gpg_error_from_syserror ();
149   log_error ("%s failed: %s\n", type, gpg_strerror (err));
150   exit (2);
151 }
152
153
154 /* Call cJSON_CreateObject but terminate in case of an error.  */
155 static cjson_t
156 xjson_CreateObject (void)
157 {
158   cjson_t json = cJSON_CreateObject ();
159   if (!json)
160     xoutofcore ("cJSON_CreateObject");
161   return json;
162 }
163
164 /* Call cJSON_CreateArray but terminate in case of an error.  */
165 static cjson_t
166 xjson_CreateArray (void)
167 {
168   cjson_t json = cJSON_CreateArray ();
169   if (!json)
170     xoutofcore ("cJSON_CreateArray");
171   return json;
172 }
173
174
175 /* Wrapper around cJSON_AddStringToObject which returns an gpg-error
176  * code instead of the NULL or the new object.  */
177 static gpg_error_t
178 cjson_AddStringToObject (cjson_t object, const char *name, const char *string)
179 {
180   if (!cJSON_AddStringToObject (object, name, string))
181     return gpg_error_from_syserror ();
182   return 0;
183 }
184
185
186 /* Same as cjson_AddStringToObject but prints an error message and
187  * terminates the process.  */
188 static void
189 xjson_AddStringToObject (cjson_t object, const char *name, const char *string)
190 {
191   if (!cJSON_AddStringToObject (object, name, string))
192     xoutofcore ("cJSON_AddStringToObject");
193 }
194
195
196 /* Same as xjson_AddStringToObject but ignores NULL strings */
197 static void
198 xjson_AddStringToObject0 (cjson_t object, const char *name, const char *string)
199 {
200   if (!string)
201     return;
202   xjson_AddStringToObject (object, name, string);
203 }
204
205 /* Wrapper around cJSON_AddBoolToObject which terminates the process
206  * in case of an error.  */
207 static void
208 xjson_AddBoolToObject (cjson_t object, const char *name, int abool)
209 {
210   if (!cJSON_AddBoolToObject (object, name, abool))
211     xoutofcore ("cJSON_AddStringToObject");
212   return ;
213 }
214
215 /* Wrapper around cJSON_AddNumberToObject which terminates the process
216  * in case of an error.  */
217 static void
218 xjson_AddNumberToObject (cjson_t object, const char *name, double dbl)
219 {
220   if (!cJSON_AddNumberToObject (object, name, dbl))
221     xoutofcore ("cJSON_AddNumberToObject");
222   return ;
223 }
224
225 /* Wrapper around cJSON_AddItemToObject which terminates the process
226  * in case of an error.  */
227 static void
228 xjson_AddItemToObject (cjson_t object, const char *name, cjson_t item)
229 {
230   if (!cJSON_AddItemToObject (object, name, item))
231     xoutofcore ("cJSON_AddItemToObject");
232   return ;
233 }
234
235 /* This is similar to cJSON_AddStringToObject but takes (DATA,
236  * DATALEN) and adds it under NAME as a base 64 encoded string to
237  * OBJECT.  */
238 static gpg_error_t
239 add_base64_to_object (cjson_t object, const char *name,
240                       const void *data, size_t datalen)
241 {
242 #if GPGRT_VERSION_NUMBER < 0x011d00 /* 1.29 */
243   return gpg_error (GPG_ERR_NOT_SUPPORTED);
244 #else
245   gpg_err_code_t err;
246   estream_t fp = NULL;
247   gpgrt_b64state_t state = NULL;
248   cjson_t j_str = NULL;
249   void *buffer = NULL;
250
251   fp = es_fopenmem (0, "rwb");
252   if (!fp)
253     {
254       err = gpg_err_code_from_syserror ();
255       goto leave;
256     }
257   state = gpgrt_b64enc_start (fp, "");
258   if (!state)
259     {
260       err = gpg_err_code_from_syserror ();
261       goto leave;
262     }
263
264   err = gpgrt_b64enc_write (state, data, datalen);
265   if (err)
266     goto leave;
267
268   err = gpgrt_b64enc_finish (state);
269   state = NULL;
270   if (err)
271     return err;
272
273   es_fputc (0, fp);
274   if (es_fclose_snatch (fp, &buffer, NULL))
275     {
276       fp = NULL;
277       err = gpg_error_from_syserror ();
278       goto leave;
279     }
280   fp = NULL;
281
282   j_str = cJSON_CreateStringConvey (buffer);
283   if (!j_str)
284     {
285       err = gpg_error_from_syserror ();
286       goto leave;
287     }
288   buffer = NULL;
289
290   if (!cJSON_AddItemToObject (object, name, j_str))
291     {
292       err = gpg_error_from_syserror ();
293       cJSON_Delete (j_str);
294       j_str = NULL;
295       goto leave;
296     }
297   j_str = NULL;
298
299  leave:
300   xfree (buffer);
301   cJSON_Delete (j_str);
302   gpgrt_b64enc_finish (state);
303   es_fclose (fp);
304   return err;
305 #endif
306 }
307
308
309 /* Create a JSON error object.  If JSON is not NULL the error message
310  * is appended to that object.  An existing "type" item will be replaced. */
311 static cjson_t
312 error_object_v (cjson_t json, const char *message, va_list arg_ptr,
313                 gpg_error_t err)
314 {
315   cjson_t response, j_tmp;
316   char *msg;
317
318   msg = gpgrt_vbsprintf (message, arg_ptr);
319   if (!msg)
320     xoutofcore ("error_object");
321
322   response = json? json : xjson_CreateObject ();
323
324   if (!(j_tmp = cJSON_GetObjectItem (response, "type")))
325     xjson_AddStringToObject (response, "type", "error");
326   else /* Replace existing "type".  */
327     {
328       j_tmp = cJSON_CreateString ("error");
329       if (!j_tmp)
330         xoutofcore ("cJSON_CreateString");
331       cJSON_ReplaceItemInObject (response, "type", j_tmp);
332      }
333   xjson_AddStringToObject (response, "msg", msg);
334   xfree (msg);
335
336   xjson_AddNumberToObject (response, "code", err);
337
338   return response;
339 }
340
341
342 /* Call cJSON_Print but terminate in case of an error.  */
343 static char *
344 xjson_Print (cjson_t object)
345 {
346   char *buf;
347   buf = cJSON_Print (object);
348   if (!buf)
349     xoutofcore ("cJSON_Print");
350   return buf;
351 }
352
353
354 static cjson_t
355 error_object (cjson_t json, const char *message, ...)
356 {
357   cjson_t response;
358   va_list arg_ptr;
359
360   va_start (arg_ptr, message);
361   response = error_object_v (json, message, arg_ptr, 0);
362   va_end (arg_ptr);
363   return response;
364 }
365
366
367 static cjson_t
368 gpg_error_object (cjson_t json, gpg_error_t err, const char *message, ...)
369 {
370   cjson_t response;
371   va_list arg_ptr;
372
373   va_start (arg_ptr, message);
374   response = error_object_v (json, message, arg_ptr, err);
375   va_end (arg_ptr);
376   return response;
377 }
378
379
380 static char *
381 error_object_string (const char *message, ...)
382 {
383   cjson_t response;
384   va_list arg_ptr;
385   char *msg;
386
387   va_start (arg_ptr, message);
388   response = error_object_v (NULL, message, arg_ptr, 0);
389   va_end (arg_ptr);
390
391   msg = xjson_Print (response);
392   cJSON_Delete (response);
393   return msg;
394 }
395
396
397 /* Get the boolean property NAME from the JSON object and store true
398  * or valse at R_VALUE.  If the name is unknown the value of DEF_VALUE
399  * is returned.  If the type of the value is not boolean,
400  * GPG_ERR_INV_VALUE is returned and R_VALUE set to DEF_VALUE.  */
401 static gpg_error_t
402 get_boolean_flag (cjson_t json, const char *name, int def_value, int *r_value)
403 {
404   cjson_t j_item;
405
406   j_item = cJSON_GetObjectItem (json, name);
407   if (!j_item)
408     *r_value = def_value;
409   else if (cjson_is_true (j_item))
410     *r_value = 1;
411   else if (cjson_is_false (j_item))
412     *r_value = 0;
413   else
414     {
415       *r_value = def_value;
416       return gpg_error (GPG_ERR_INV_VALUE);
417     }
418
419   return 0;
420 }
421
422
423 /* Get the boolean property PROTOCOL from the JSON object and store
424  * its value at R_PROTOCOL.  The default is OpenPGP.  */
425 static gpg_error_t
426 get_protocol (cjson_t json, gpgme_protocol_t *r_protocol)
427 {
428   cjson_t j_item;
429
430   *r_protocol = GPGME_PROTOCOL_OpenPGP;
431   j_item = cJSON_GetObjectItem (json, "protocol");
432   if (!j_item)
433     ;
434   else if (!cjson_is_string (j_item))
435     return gpg_error (GPG_ERR_INV_VALUE);
436   else if (!strcmp(j_item->valuestring, "openpgp"))
437     ;
438   else if (!strcmp(j_item->valuestring, "cms"))
439     *r_protocol = GPGME_PROTOCOL_CMS;
440   else
441     return gpg_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
442
443   return 0;
444 }
445
446
447 /* Get the chunksize from JSON and store it at R_CHUNKSIZE.  */
448 static gpg_error_t
449 get_chunksize (cjson_t json, size_t *r_chunksize)
450 {
451   cjson_t j_item;
452
453   *r_chunksize = DEF_REPLY_CHUNK_SIZE;
454   j_item = cJSON_GetObjectItem (json, "chunksize");
455   if (!j_item)
456     ;
457   else if (!cjson_is_number (j_item))
458     return gpg_error (GPG_ERR_INV_VALUE);
459   else if ((size_t)j_item->valueint < MIN_REPLY_CHUNK_SIZE)
460     *r_chunksize = MIN_REPLY_CHUNK_SIZE;
461   else if ((size_t)j_item->valueint > MAX_REPLY_CHUNK_SIZE)
462     *r_chunksize = MAX_REPLY_CHUNK_SIZE;
463   else
464     *r_chunksize = (size_t)j_item->valueint;
465
466   return 0;
467 }
468
469
470 /* Extract the keys from the array or string with the name "name"
471  * in the JSON object.  On success a string with the keys identifiers
472  * is stored at R_KEYS.
473  * The keys in that string are LF delimited.  On failure an error code
474  * is returned.  */
475 static gpg_error_t
476 get_keys (cjson_t json, const char *name, char **r_keystring)
477 {
478   cjson_t j_keys, j_item;
479   int i, nkeys;
480   char *p;
481   size_t length;
482
483   *r_keystring = NULL;
484
485   j_keys = cJSON_GetObjectItem (json, name);
486   if (!j_keys)
487     return gpg_error (GPG_ERR_NO_KEY);
488   if (!cjson_is_array (j_keys) && !cjson_is_string (j_keys))
489     return gpg_error (GPG_ERR_INV_VALUE);
490
491   /* Fixme: We should better use a membuf like thing.  */
492   length = 1; /* For the EOS.  */
493   if (cjson_is_string (j_keys))
494     {
495       nkeys = 1;
496       length += strlen (j_keys->valuestring);
497       if (strchr (j_keys->valuestring, '\n'))
498         return gpg_error (GPG_ERR_INV_USER_ID);
499     }
500   else
501     {
502       nkeys = cJSON_GetArraySize (j_keys);
503       if (!nkeys)
504         return gpg_error (GPG_ERR_NO_KEY);
505       for (i=0; i < nkeys; i++)
506         {
507           j_item = cJSON_GetArrayItem (j_keys, i);
508           if (!j_item || !cjson_is_string (j_item))
509             return gpg_error (GPG_ERR_INV_VALUE);
510           if (i)
511             length++; /* Space for delimiter. */
512           length += strlen (j_item->valuestring);
513           if (strchr (j_item->valuestring, '\n'))
514             return gpg_error (GPG_ERR_INV_USER_ID);
515         }
516     }
517
518   p = *r_keystring = xtrymalloc (length);
519   if (!p)
520     return gpg_error_from_syserror ();
521
522   if (cjson_is_string (j_keys))
523     {
524       strcpy (p, j_keys->valuestring);
525     }
526   else
527     {
528       for (i=0; i < nkeys; i++)
529         {
530           j_item = cJSON_GetArrayItem (j_keys, i);
531           if (i)
532             *p++ = '\n'; /* Add delimiter.  */
533           p = stpcpy (p, j_item->valuestring);
534         }
535     }
536   return 0;
537 }
538
539
540
541 \f
542 /*
543  *  GPGME support functions.
544  */
545
546 /* Helper for get_context.  */
547 static gpgme_ctx_t
548 _create_new_context (gpgme_protocol_t proto)
549 {
550   gpg_error_t err;
551   gpgme_ctx_t ctx;
552
553   err = gpgme_new (&ctx);
554   if (err)
555     log_fatal ("error creating GPGME context: %s\n", gpg_strerror (err));
556   gpgme_set_protocol (ctx, proto);
557   gpgme_set_ctx_flag (ctx, "request-origin", "browser");
558   return ctx;
559 }
560
561
562 /* Return a context object for protocol PROTO.  This is currently a
563  * statically allocated context initialized for PROTO.  Terminates
564  * process on failure.  */
565 static gpgme_ctx_t
566 get_context (gpgme_protocol_t proto)
567 {
568   static gpgme_ctx_t ctx_openpgp, ctx_cms, ctx_conf;
569
570   if (proto == GPGME_PROTOCOL_OpenPGP)
571     {
572       if (!ctx_openpgp)
573         ctx_openpgp = _create_new_context (proto);
574       return ctx_openpgp;
575     }
576   else if (proto == GPGME_PROTOCOL_CMS)
577     {
578       if (!ctx_cms)
579         ctx_cms = _create_new_context (proto);
580       return ctx_cms;
581     }
582   else if (proto == GPGME_PROTOCOL_GPGCONF)
583     {
584       if (!ctx_conf)
585         ctx_conf = _create_new_context (proto);
586       return ctx_conf;
587     }
588   else
589     log_bug ("invalid protocol %d requested\n", proto);
590 }
591
592
593 /* Free context object retrieved by get_context.  */
594 static void
595 release_context (gpgme_ctx_t ctx)
596 {
597   /* Nothing to do right now.  */
598   (void)ctx;
599 }
600
601
602 /* Create an addition context for short operations. */
603 static gpgme_ctx_t
604 create_onetime_context (gpgme_protocol_t proto)
605 {
606   return _create_new_context (proto);
607
608 }
609
610
611 /* Release a one-time context.  */
612 static void
613 release_onetime_context (gpgme_ctx_t ctx)
614 {
615   return gpgme_release (ctx);
616
617 }
618
619
620 /* Given a Base-64 encoded string object in JSON return a gpgme data
621  * object at R_DATA.  */
622 static gpg_error_t
623 data_from_base64_string (gpgme_data_t *r_data, cjson_t json)
624 {
625 #if GPGRT_VERSION_NUMBER < 0x011d00 /* 1.29 */
626   *r_data = NULL;
627   return gpg_error (GPG_ERR_NOT_SUPPORTED);
628 #else
629   gpg_error_t err;
630   size_t len;
631   char *buf = NULL;
632   gpgrt_b64state_t state = NULL;
633   gpgme_data_t data = NULL;
634
635   *r_data = NULL;
636
637   /* A quick check on the JSON.  */
638   if (!cjson_is_string (json))
639     {
640       err = gpg_error (GPG_ERR_INV_VALUE);
641       goto leave;
642     }
643
644   state = gpgrt_b64dec_start (NULL);
645   if (!state)
646     {
647       err = gpg_err_code_from_syserror ();
648       goto leave;
649     }
650
651   /* Fixme: Data duplication - we should see how to snatch the memory
652    * from the json object.  */
653   len = strlen (json->valuestring);
654   buf = xtrystrdup (json->valuestring);
655   if (!buf)
656     {
657       err = gpg_error_from_syserror ();
658       goto leave;
659     }
660
661   err = gpgrt_b64dec_proc (state, buf, len, &len);
662   if (err)
663     goto leave;
664
665   err = gpgrt_b64dec_finish (state);
666   state = NULL;
667   if (err)
668     goto leave;
669
670   err = gpgme_data_new_from_mem (&data, buf, len, 1);
671   if (err)
672     goto leave;
673   *r_data = data;
674   data = NULL;
675
676  leave:
677   xfree (data);
678   xfree (buf);
679   gpgrt_b64dec_finish (state);
680   return err;
681 #endif
682 }
683
684
685 /* Create a keylist pattern array from a json keys object
686  * in the request. Returns either a malloced NULL terminated
687  * string array which can be used as patterns for
688  * op_keylist_ext or NULL. */
689 static char **
690 create_keylist_patterns (cjson_t request, const char *name)
691 {
692   char *keystring;
693   char *p;
694   char *tmp;
695   char **ret;
696   int cnt = 1;
697   int i = 0;
698
699   if (get_keys (request, name, &keystring))
700     return NULL;
701
702   for (p = keystring; p; p++)
703     if (*p == '\n')
704       cnt++;
705
706   ret = xmalloc (cnt * sizeof *ret);
707
708   for (p = keystring, tmp = keystring; *p; p++)
709     {
710       if (*p != '\n')
711         continue;
712       *p = '\0';
713       ret[i++] = xstrdup (tmp);
714       tmp = p + 1;
715     }
716   /* The last key is not newline delimted. */
717   ret[i++] = *tmp ? xstrdup (tmp) : NULL;
718   ret[i] = NULL;
719
720   xfree (keystring);
721   return ret;
722 }
723
724
725 /* Create sigsum json array */
726 static cjson_t
727 sigsum_to_json (gpgme_sigsum_t summary)
728 {
729   cjson_t result = xjson_CreateObject ();
730   cjson_t sigsum_array = xjson_CreateArray ();
731
732   if ( (summary & GPGME_SIGSUM_VALID      ))
733     cJSON_AddItemToArray (sigsum_array,
734         cJSON_CreateString ("valid"));
735   if ( (summary & GPGME_SIGSUM_GREEN      ))
736     cJSON_AddItemToArray (sigsum_array,
737         cJSON_CreateString ("green"));
738   if ( (summary & GPGME_SIGSUM_RED        ))
739     cJSON_AddItemToArray (sigsum_array,
740         cJSON_CreateString ("red"));
741   if ( (summary & GPGME_SIGSUM_KEY_REVOKED))
742     cJSON_AddItemToArray (sigsum_array,
743         cJSON_CreateString ("revoked"));
744   if ( (summary & GPGME_SIGSUM_KEY_EXPIRED))
745     cJSON_AddItemToArray (sigsum_array,
746         cJSON_CreateString ("key-expired"));
747   if ( (summary & GPGME_SIGSUM_SIG_EXPIRED))
748     cJSON_AddItemToArray (sigsum_array,
749         cJSON_CreateString ("sig-expired"));
750   if ( (summary & GPGME_SIGSUM_KEY_MISSING))
751     cJSON_AddItemToArray (sigsum_array,
752         cJSON_CreateString ("key-missing"));
753   if ( (summary & GPGME_SIGSUM_CRL_MISSING))
754     cJSON_AddItemToArray (sigsum_array,
755         cJSON_CreateString ("crl-missing"));
756   if ( (summary & GPGME_SIGSUM_CRL_TOO_OLD))
757     cJSON_AddItemToArray (sigsum_array,
758         cJSON_CreateString ("crl-too-old"));
759   if ( (summary & GPGME_SIGSUM_BAD_POLICY ))
760     cJSON_AddItemToArray (sigsum_array,
761         cJSON_CreateString ("bad-policy"));
762   if ( (summary & GPGME_SIGSUM_SYS_ERROR  ))
763     cJSON_AddItemToArray (sigsum_array,
764         cJSON_CreateString ("sys-error"));
765   /* The signature summary as string array. */
766   xjson_AddItemToObject (result, "sigsum", sigsum_array);
767
768   /* Bools for the same. */
769   xjson_AddBoolToObject (result, "valid",
770                          (summary & GPGME_SIGSUM_VALID      ));
771   xjson_AddBoolToObject (result, "green",
772                          (summary & GPGME_SIGSUM_GREEN      ));
773   xjson_AddBoolToObject (result, "red",
774                          (summary & GPGME_SIGSUM_RED        ));
775   xjson_AddBoolToObject (result, "revoked",
776                          (summary & GPGME_SIGSUM_KEY_REVOKED));
777   xjson_AddBoolToObject (result, "key-expired",
778                          (summary & GPGME_SIGSUM_KEY_EXPIRED));
779   xjson_AddBoolToObject (result, "sig-expired",
780                          (summary & GPGME_SIGSUM_SIG_EXPIRED));
781   xjson_AddBoolToObject (result, "key-missing",
782                          (summary & GPGME_SIGSUM_KEY_MISSING));
783   xjson_AddBoolToObject (result, "crl-missing",
784                          (summary & GPGME_SIGSUM_CRL_MISSING));
785   xjson_AddBoolToObject (result, "crl-too-old",
786                          (summary & GPGME_SIGSUM_CRL_TOO_OLD));
787   xjson_AddBoolToObject (result, "bad-policy",
788                          (summary & GPGME_SIGSUM_BAD_POLICY ));
789   xjson_AddBoolToObject (result, "sys-error",
790                          (summary & GPGME_SIGSUM_SYS_ERROR  ));
791
792   return result;
793 }
794
795
796 /* Helper for summary formatting */
797 static const char *
798 validity_to_string (gpgme_validity_t val)
799 {
800   switch (val)
801     {
802     case GPGME_VALIDITY_UNDEFINED:return "undefined";
803     case GPGME_VALIDITY_NEVER:    return "never";
804     case GPGME_VALIDITY_MARGINAL: return "marginal";
805     case GPGME_VALIDITY_FULL:     return "full";
806     case GPGME_VALIDITY_ULTIMATE: return "ultimate";
807     case GPGME_VALIDITY_UNKNOWN:
808     default:                      return "unknown";
809     }
810 }
811
812 static const char *
813 protocol_to_string (gpgme_protocol_t proto)
814 {
815   switch (proto)
816     {
817     case GPGME_PROTOCOL_OpenPGP: return "OpenPGP";
818     case GPGME_PROTOCOL_CMS:     return "CMS";
819     case GPGME_PROTOCOL_GPGCONF: return "gpgconf";
820     case GPGME_PROTOCOL_ASSUAN:  return "assuan";
821     case GPGME_PROTOCOL_G13:     return "g13";
822     case GPGME_PROTOCOL_UISERVER:return "uiserver";
823     case GPGME_PROTOCOL_SPAWN:   return "spawn";
824     default:
825                                  return "unknown";
826     }
827 }
828
829 /* Create a sig_notation json object */
830 static cjson_t
831 sig_notation_to_json (gpgme_sig_notation_t not)
832 {
833   cjson_t result = xjson_CreateObject ();
834   xjson_AddBoolToObject (result, "human_readable", not->human_readable);
835   xjson_AddBoolToObject (result, "critical", not->critical);
836
837   xjson_AddStringToObject0 (result, "name", not->name);
838   xjson_AddStringToObject0 (result, "value", not->value);
839
840   xjson_AddNumberToObject (result, "flags", not->flags);
841
842   return result;
843 }
844
845 /* Create a key_sig json object */
846 static cjson_t
847 key_sig_to_json (gpgme_key_sig_t sig)
848 {
849   cjson_t result = xjson_CreateObject ();
850
851   xjson_AddBoolToObject (result, "revoked", sig->revoked);
852   xjson_AddBoolToObject (result, "expired", sig->expired);
853   xjson_AddBoolToObject (result, "invalid", sig->invalid);
854   xjson_AddBoolToObject (result, "exportable", sig->exportable);
855
856   xjson_AddStringToObject0 (result, "pubkey_algo_name",
857                             gpgme_pubkey_algo_name (sig->pubkey_algo));
858   xjson_AddStringToObject0 (result, "keyid", sig->keyid);
859   xjson_AddStringToObject0 (result, "status", gpgme_strerror (sig->status));
860   xjson_AddStringToObject0 (result, "name", sig->name);
861   xjson_AddStringToObject0 (result, "email", sig->email);
862   xjson_AddStringToObject0 (result, "comment", sig->comment);
863
864   xjson_AddNumberToObject (result, "pubkey_algo", sig->pubkey_algo);
865   xjson_AddNumberToObject (result, "timestamp", sig->timestamp);
866   xjson_AddNumberToObject (result, "expires", sig->expires);
867   xjson_AddNumberToObject (result, "status_code", sig->status);
868   xjson_AddNumberToObject (result, "sig_class", sig->sig_class);
869
870   if (sig->notations)
871     {
872       gpgme_sig_notation_t not;
873       cjson_t array = xjson_CreateArray ();
874       for (not = sig->notations; not; not = not->next)
875         cJSON_AddItemToArray (array, sig_notation_to_json (not));
876       xjson_AddItemToObject (result, "notations", array);
877     }
878
879   return result;
880 }
881
882 /* Create a tofu info object */
883 static cjson_t
884 tofu_to_json (gpgme_tofu_info_t tofu)
885 {
886   cjson_t result = xjson_CreateObject ();
887
888   xjson_AddStringToObject0 (result, "description", tofu->description);
889
890   xjson_AddNumberToObject (result, "validity", tofu->validity);
891   xjson_AddNumberToObject (result, "policy", tofu->policy);
892   xjson_AddNumberToObject (result, "signcount", tofu->signcount);
893   xjson_AddNumberToObject (result, "encrcount", tofu->encrcount);
894   xjson_AddNumberToObject (result, "signfirst", tofu->signfirst);
895   xjson_AddNumberToObject (result, "signlast", tofu->signlast);
896   xjson_AddNumberToObject (result, "encrfirst", tofu->encrfirst);
897   xjson_AddNumberToObject (result, "encrlast", tofu->encrlast);
898
899   return result;
900 }
901
902 /* Create a userid json object */
903 static cjson_t
904 uid_to_json (gpgme_user_id_t uid)
905 {
906   cjson_t result = xjson_CreateObject ();
907
908   xjson_AddBoolToObject (result, "revoked", uid->revoked);
909   xjson_AddBoolToObject (result, "invalid", uid->invalid);
910
911   xjson_AddStringToObject0 (result, "validity",
912                             validity_to_string (uid->validity));
913   xjson_AddStringToObject0 (result, "uid", uid->uid);
914   xjson_AddStringToObject0 (result, "name", uid->name);
915   xjson_AddStringToObject0 (result, "email", uid->email);
916   xjson_AddStringToObject0 (result, "comment", uid->comment);
917   xjson_AddStringToObject0 (result, "address", uid->address);
918
919   xjson_AddNumberToObject (result, "origin", uid->origin);
920   xjson_AddNumberToObject (result, "last_update", uid->last_update);
921
922   /* Key sigs */
923   if (uid->signatures)
924     {
925       cjson_t sig_array = xjson_CreateArray ();
926       gpgme_key_sig_t sig;
927
928       for (sig = uid->signatures; sig; sig = sig->next)
929         cJSON_AddItemToArray (sig_array, key_sig_to_json (sig));
930
931       xjson_AddItemToObject (result, "signatures", sig_array);
932     }
933
934   /* TOFU info */
935   if (uid->tofu)
936     {
937       gpgme_tofu_info_t tofu;
938       cjson_t array = xjson_CreateArray ();
939       for (tofu = uid->tofu; tofu; tofu = tofu->next)
940         cJSON_AddItemToArray (array, tofu_to_json (tofu));
941       xjson_AddItemToObject (result, "tofu", array);
942     }
943
944   return result;
945 }
946
947 /* Create a subkey json object */
948 static cjson_t
949 subkey_to_json (gpgme_subkey_t sub)
950 {
951   cjson_t result = xjson_CreateObject ();
952
953   xjson_AddBoolToObject (result, "revoked", sub->revoked);
954   xjson_AddBoolToObject (result, "expired", sub->expired);
955   xjson_AddBoolToObject (result, "disabled", sub->disabled);
956   xjson_AddBoolToObject (result, "invalid", sub->invalid);
957   xjson_AddBoolToObject (result, "can_encrypt", sub->can_encrypt);
958   xjson_AddBoolToObject (result, "can_sign", sub->can_sign);
959   xjson_AddBoolToObject (result, "can_certify", sub->can_certify);
960   xjson_AddBoolToObject (result, "can_authenticate", sub->can_authenticate);
961   xjson_AddBoolToObject (result, "secret", sub->secret);
962   xjson_AddBoolToObject (result, "is_qualified", sub->is_qualified);
963   xjson_AddBoolToObject (result, "is_cardkey", sub->is_cardkey);
964   xjson_AddBoolToObject (result, "is_de_vs", sub->is_de_vs);
965
966   xjson_AddStringToObject0 (result, "pubkey_algo_name",
967                             gpgme_pubkey_algo_name (sub->pubkey_algo));
968   xjson_AddStringToObject0 (result, "pubkey_algo_string",
969                             gpgme_pubkey_algo_string (sub));
970   xjson_AddStringToObject0 (result, "keyid", sub->keyid);
971   xjson_AddStringToObject0 (result, "card_number", sub->card_number);
972   xjson_AddStringToObject0 (result, "curve", sub->curve);
973   xjson_AddStringToObject0 (result, "keygrip", sub->keygrip);
974
975   xjson_AddNumberToObject (result, "pubkey_algo", sub->pubkey_algo);
976   xjson_AddNumberToObject (result, "length", sub->length);
977   xjson_AddNumberToObject (result, "timestamp", sub->timestamp);
978   xjson_AddNumberToObject (result, "expires", sub->expires);
979
980   return result;
981 }
982
983 /* Create a key json object */
984 static cjson_t
985 key_to_json (gpgme_key_t key)
986 {
987   cjson_t result = xjson_CreateObject ();
988
989   xjson_AddBoolToObject (result, "revoked", key->revoked);
990   xjson_AddBoolToObject (result, "expired", key->expired);
991   xjson_AddBoolToObject (result, "disabled", key->disabled);
992   xjson_AddBoolToObject (result, "invalid", key->invalid);
993   xjson_AddBoolToObject (result, "can_encrypt", key->can_encrypt);
994   xjson_AddBoolToObject (result, "can_sign", key->can_sign);
995   xjson_AddBoolToObject (result, "can_certify", key->can_certify);
996   xjson_AddBoolToObject (result, "can_authenticate", key->can_authenticate);
997   xjson_AddBoolToObject (result, "secret", key->secret);
998   xjson_AddBoolToObject (result, "is_qualified", key->is_qualified);
999
1000   xjson_AddStringToObject0 (result, "protocol",
1001                             protocol_to_string (key->protocol));
1002   xjson_AddStringToObject0 (result, "issuer_serial", key->issuer_serial);
1003   xjson_AddStringToObject0 (result, "issuer_name", key->issuer_name);
1004   xjson_AddStringToObject0 (result, "fingerprint", key->fpr);
1005   xjson_AddStringToObject0 (result, "chain_id", key->chain_id);
1006   xjson_AddStringToObject0 (result, "owner_trust",
1007                             validity_to_string (key->owner_trust));
1008
1009   xjson_AddNumberToObject (result, "origin", key->origin);
1010   xjson_AddNumberToObject (result, "last_update", key->last_update);
1011
1012   /* Add subkeys */
1013   if (key->subkeys)
1014     {
1015       cjson_t subkey_array = xjson_CreateArray ();
1016       gpgme_subkey_t sub;
1017       for (sub = key->subkeys; sub; sub = sub->next)
1018         cJSON_AddItemToArray (subkey_array, subkey_to_json (sub));
1019
1020       xjson_AddItemToObject (result, "subkeys", subkey_array);
1021     }
1022
1023   /* User Ids */
1024   if (key->uids)
1025     {
1026       cjson_t uid_array = xjson_CreateArray ();
1027       gpgme_user_id_t uid;
1028       for (uid = key->uids; uid; uid = uid->next)
1029         cJSON_AddItemToArray (uid_array, uid_to_json (uid));
1030
1031       xjson_AddItemToObject (result, "userids", uid_array);
1032     }
1033
1034   return result;
1035 }
1036
1037
1038 /* Create a signature json object */
1039 static cjson_t
1040 signature_to_json (gpgme_signature_t sig)
1041 {
1042   cjson_t result = xjson_CreateObject ();
1043
1044   xjson_AddItemToObject (result, "summary", sigsum_to_json (sig->summary));
1045
1046   xjson_AddBoolToObject (result, "wrong_key_usage", sig->wrong_key_usage);
1047   xjson_AddBoolToObject (result, "chain_model", sig->chain_model);
1048   xjson_AddBoolToObject (result, "is_de_vs", sig->is_de_vs);
1049
1050   xjson_AddStringToObject0 (result, "status_string",
1051                             gpgme_strerror (sig->status));
1052   xjson_AddStringToObject0 (result, "fingerprint", sig->fpr);
1053   xjson_AddStringToObject0 (result, "validity_string",
1054                             validity_to_string (sig->validity));
1055   xjson_AddStringToObject0 (result, "pubkey_algo_name",
1056                             gpgme_pubkey_algo_name (sig->pubkey_algo));
1057   xjson_AddStringToObject0 (result, "hash_algo_name",
1058                             gpgme_hash_algo_name (sig->hash_algo));
1059   xjson_AddStringToObject0 (result, "pka_address", sig->pka_address);
1060
1061   xjson_AddNumberToObject (result, "status_code", sig->status);
1062   xjson_AddNumberToObject (result, "timestamp", sig->timestamp);
1063   xjson_AddNumberToObject (result, "exp_timestamp", sig->exp_timestamp);
1064   xjson_AddNumberToObject (result, "pka_trust", sig->pka_trust);
1065   xjson_AddNumberToObject (result, "validity", sig->validity);
1066   xjson_AddNumberToObject (result, "validity_reason", sig->validity_reason);
1067
1068   if (sig->notations)
1069     {
1070       gpgme_sig_notation_t not;
1071       cjson_t array = xjson_CreateArray ();
1072       for (not = sig->notations; not; not = not->next)
1073         cJSON_AddItemToArray (array, sig_notation_to_json (not));
1074       xjson_AddItemToObject (result, "notations", array);
1075     }
1076
1077   return result;
1078 }
1079
1080
1081 /* Create a JSON object from a gpgme_verify result */
1082 static cjson_t
1083 verify_result_to_json (gpgme_verify_result_t verify_result)
1084 {
1085   cjson_t result = xjson_CreateObject ();
1086
1087   xjson_AddStringToObject0 (result, "file_name", verify_result->file_name);
1088   xjson_AddBoolToObject (result, "is_mime", verify_result->is_mime);
1089
1090   if (verify_result->signatures)
1091     {
1092       cjson_t array = xjson_CreateArray ();
1093       gpgme_signature_t sig;
1094
1095       for (sig = verify_result->signatures; sig; sig = sig->next)
1096         cJSON_AddItemToArray (array, signature_to_json (sig));
1097       xjson_AddItemToObject (result, "signatures", array);
1098     }
1099
1100   return result;
1101 }
1102
1103
1104 /* Create a JSON object from an engine_info */
1105 static cjson_t
1106 engine_info_to_json (gpgme_engine_info_t info)
1107 {
1108   cjson_t result = xjson_CreateObject ();
1109
1110   xjson_AddStringToObject0 (result, "protocol",
1111                             protocol_to_string (info->protocol));
1112   xjson_AddStringToObject0 (result, "fname", info->file_name);
1113   xjson_AddStringToObject0 (result, "version", info->version);
1114   xjson_AddStringToObject0 (result, "req_version", info->req_version);
1115   xjson_AddStringToObject0 (result, "homedir", info->home_dir ?
1116                                                 info->home_dir :
1117                                                 "default");
1118   return result;
1119 }
1120
1121
1122 /* Create a JSON object from an import_status */
1123 static cjson_t
1124 import_status_to_json (gpgme_import_status_t sts)
1125 {
1126   cjson_t result = xjson_CreateObject ();
1127
1128   xjson_AddStringToObject0 (result, "fingerprint", sts->fpr);
1129   xjson_AddStringToObject0 (result, "error_string",
1130                             gpgme_strerror (sts->result));
1131
1132   xjson_AddNumberToObject (result, "status", sts->status);
1133
1134   return result;
1135 }
1136
1137 /* Create a JSON object from an import result */
1138 static cjson_t
1139 import_result_to_json (gpgme_import_result_t imp)
1140 {
1141   cjson_t result = xjson_CreateObject ();
1142
1143   xjson_AddNumberToObject (result, "considered", imp->considered);
1144   xjson_AddNumberToObject (result, "no_user_id", imp->no_user_id);
1145   xjson_AddNumberToObject (result, "imported", imp->imported);
1146   xjson_AddNumberToObject (result, "imported_rsa", imp->imported_rsa);
1147   xjson_AddNumberToObject (result, "unchanged", imp->unchanged);
1148   xjson_AddNumberToObject (result, "new_user_ids", imp->new_user_ids);
1149   xjson_AddNumberToObject (result, "new_sub_keys", imp->new_sub_keys);
1150   xjson_AddNumberToObject (result, "new_signatures", imp->new_signatures);
1151   xjson_AddNumberToObject (result, "new_revocations", imp->new_revocations);
1152   xjson_AddNumberToObject (result, "secret_read", imp->secret_read);
1153   xjson_AddNumberToObject (result, "secret_imported", imp->secret_imported);
1154   xjson_AddNumberToObject (result, "secret_unchanged", imp->secret_unchanged);
1155   xjson_AddNumberToObject (result, "skipped_new_keys", imp->skipped_new_keys);
1156   xjson_AddNumberToObject (result, "not_imported", imp->not_imported);
1157   xjson_AddNumberToObject (result, "skipped_v3_keys", imp->skipped_v3_keys);
1158
1159
1160   if (imp->imports)
1161     {
1162       cjson_t array = xjson_CreateArray ();
1163       gpgme_import_status_t status;
1164
1165       for (status = imp->imports; status; status = status->next)
1166         cJSON_AddItemToArray (array, import_status_to_json (status));
1167       xjson_AddItemToObject (result, "imports", array);
1168     }
1169
1170   return result;
1171 }
1172
1173
1174 /* Create a JSON object from a gpgconf arg */
1175 static cjson_t
1176 conf_arg_to_json (gpgme_conf_arg_t arg, gpgme_conf_type_t type)
1177 {
1178   cjson_t result = xjson_CreateObject ();
1179   int is_none = 0;
1180   switch (type)
1181     {
1182       case GPGME_CONF_STRING:
1183       case GPGME_CONF_PATHNAME:
1184       case GPGME_CONF_LDAP_SERVER:
1185       case GPGME_CONF_KEY_FPR:
1186       case GPGME_CONF_PUB_KEY:
1187       case GPGME_CONF_SEC_KEY:
1188       case GPGME_CONF_ALIAS_LIST:
1189         xjson_AddStringToObject0 (result, "string", arg->value.string);
1190         break;
1191
1192       case GPGME_CONF_UINT32:
1193         xjson_AddNumberToObject (result, "number", arg->value.uint32);
1194         break;
1195
1196       case GPGME_CONF_INT32:
1197         xjson_AddNumberToObject (result, "number", arg->value.int32);
1198         break;
1199
1200       case GPGME_CONF_NONE:
1201       default:
1202         is_none = 1;
1203         break;
1204     }
1205   xjson_AddBoolToObject (result, "is_none", is_none);
1206   return result;
1207 }
1208
1209
1210 /* Create a JSON object from a gpgconf option */
1211 static cjson_t
1212 conf_opt_to_json (gpgme_conf_opt_t opt)
1213 {
1214   cjson_t result = xjson_CreateObject ();
1215
1216   xjson_AddStringToObject0 (result, "name", opt->name);
1217   xjson_AddStringToObject0 (result, "description", opt->description);
1218   xjson_AddStringToObject0 (result, "argname", opt->argname);
1219   xjson_AddStringToObject0 (result, "default_description",
1220                             opt->default_description);
1221   xjson_AddStringToObject0 (result, "no_arg_description",
1222                             opt->no_arg_description);
1223
1224   xjson_AddNumberToObject (result, "flags", opt->flags);
1225   xjson_AddNumberToObject (result, "level", opt->level);
1226   xjson_AddNumberToObject (result, "type", opt->type);
1227   xjson_AddNumberToObject (result, "alt_type", opt->alt_type);
1228
1229   if (opt->default_value)
1230     {
1231       cjson_t array = xjson_CreateArray ();
1232       gpgme_conf_arg_t arg;
1233
1234       for (arg = opt->default_value; arg; arg = arg->next)
1235         cJSON_AddItemToArray (array, conf_arg_to_json (arg, opt->alt_type));
1236       xjson_AddItemToObject (result, "default_value", array);
1237     }
1238
1239   if (opt->no_arg_value)
1240     {
1241       cjson_t array = xjson_CreateArray ();
1242       gpgme_conf_arg_t arg;
1243
1244       for (arg = opt->no_arg_value; arg; arg = arg->next)
1245         cJSON_AddItemToArray (array, conf_arg_to_json (arg, opt->alt_type));
1246       xjson_AddItemToObject (result, "no_arg_value", array);
1247     }
1248
1249   if (opt->value)
1250     {
1251       cjson_t array = xjson_CreateArray ();
1252       gpgme_conf_arg_t arg;
1253
1254       for (arg = opt->value; arg; arg = arg->next)
1255         cJSON_AddItemToArray (array, conf_arg_to_json (arg, opt->alt_type));
1256       xjson_AddItemToObject (result, "value", array);
1257     }
1258   return result;
1259 }
1260
1261
1262 /* Create a JSON object from a gpgconf component*/
1263 static cjson_t
1264 conf_comp_to_json (gpgme_conf_comp_t cmp)
1265 {
1266   cjson_t result = xjson_CreateObject ();
1267
1268   xjson_AddStringToObject0 (result, "name", cmp->name);
1269   xjson_AddStringToObject0 (result, "description", cmp->description);
1270   xjson_AddStringToObject0 (result, "program_name", cmp->program_name);
1271
1272
1273   if (cmp->options)
1274     {
1275       cjson_t array = xjson_CreateArray ();
1276       gpgme_conf_opt_t opt;
1277
1278       for (opt = cmp->options; opt; opt = opt->next)
1279         cJSON_AddItemToArray (array, conf_opt_to_json (opt));
1280       xjson_AddItemToObject (result, "options", array);
1281     }
1282
1283   return result;
1284 }
1285
1286
1287 /* Create a gpgme_data from json string data named "name"
1288  * in the request. Takes the base64 option into account.
1289  *
1290  * Adds an error to the "result" on error. */
1291 static gpg_error_t
1292 get_string_data (cjson_t request, cjson_t result, const char *name,
1293                  gpgme_data_t *r_data)
1294 {
1295   gpgme_error_t err;
1296   int opt_base64;
1297   cjson_t j_data;
1298
1299   if ((err = get_boolean_flag (request, "base64", 0, &opt_base64)))
1300     return err;
1301
1302   /* Get the data.  Note that INPUT is a shallow data object with the
1303    * storage hold in REQUEST.  */
1304   j_data = cJSON_GetObjectItem (request, name);
1305   if (!j_data)
1306     {
1307       return gpg_error (GPG_ERR_NO_DATA);
1308     }
1309   if (!cjson_is_string (j_data))
1310     {
1311       return gpg_error (GPG_ERR_INV_VALUE);
1312     }
1313   if (opt_base64)
1314     {
1315       err = data_from_base64_string (r_data, j_data);
1316       if (err)
1317         {
1318           gpg_error_object (result, err,
1319                             "Error decoding Base-64 encoded '%s': %s",
1320                             name, gpg_strerror (err));
1321           return err;
1322         }
1323     }
1324   else
1325     {
1326       err = gpgme_data_new_from_mem (r_data, j_data->valuestring,
1327                                      strlen (j_data->valuestring), 0);
1328       if (err)
1329         {
1330           gpg_error_object (result, err, "Error getting '%s': %s",
1331                             name, gpg_strerror (err));
1332           return err;
1333         }
1334     }
1335   return 0;
1336 }
1337
1338
1339 \f
1340 /*
1341  * Implementation of the commands.
1342  */
1343
1344
1345 /* Create a "data" object and the "type", "base64" and "more" flags
1346  * from DATA and append them to RESULT.  Ownership of DATA is
1347  * transferred to this function.  TYPE must be a fixed string.
1348  * CHUNKSIZE is the chunksize requested from the caller.  If BASE64 is
1349  * -1 the need for base64 encoding is determined by the content of
1350  * DATA, all other values are taken as true or false.  Note that
1351  * op_getmore has similar code but works on PENDING_DATA which is set
1352  * here.  */
1353 static gpg_error_t
1354 make_data_object (cjson_t result, gpgme_data_t data, size_t chunksize,
1355                   const char *type, int base64)
1356 {
1357   gpg_error_t err;
1358   char *buffer;
1359   const char *s;
1360   size_t buflen, n;
1361   int c;
1362
1363   if (!base64 || base64 == -1) /* Make sure that we really have a string.  */
1364     gpgme_data_write (data, "", 1);
1365
1366   buffer = gpgme_data_release_and_get_mem (data, &buflen);
1367   data = NULL;
1368   if (!buffer)
1369     {
1370       err = gpg_error_from_syserror ();
1371       goto leave;
1372     }
1373
1374   if (base64 == -1)
1375     {
1376       base64 = 0;
1377       if (!buflen)
1378         log_fatal ("Appended Nul byte got lost\n");
1379       /* Figure out if there is any Nul octet in the buffer.  In that
1380        * case we need to Base-64 the buffer.  Due to problems with the
1381        * browser's Javascript we use Base-64 also in case an UTF-8
1382        * character is in the buffer.  This is because the chunking may
1383        * split an UTF-8 characters and JS can't handle this.  */
1384       for (s=buffer, n=0; n < buflen -1; s++, n++)
1385         if (!*s || (*s & 0x80))
1386           {
1387             buflen--; /* Adjust for the extra nul byte.  */
1388             base64 = 1;
1389             break;
1390           }
1391     }
1392
1393   /* Adjust the chunksize if we need to do base64 conversion.  */
1394   if (base64)
1395     chunksize = (chunksize / 4) * 3;
1396
1397   xjson_AddStringToObject (result, "type", type);
1398   xjson_AddBoolToObject (result, "base64", base64);
1399
1400   if (buflen > chunksize)
1401     {
1402       xjson_AddBoolToObject (result, "more", 1);
1403
1404       c = buffer[chunksize];
1405       buffer[chunksize] = 0;
1406       if (base64)
1407         err = add_base64_to_object (result, "data", buffer, chunksize);
1408       else
1409         err = cjson_AddStringToObject (result, "data", buffer);
1410       buffer[chunksize] = c;
1411       if (err)
1412         goto leave;
1413
1414       pending_data.buffer = buffer;
1415       buffer = NULL;
1416       pending_data.length = buflen;
1417       pending_data.written = chunksize;
1418       pending_data.type = type;
1419       pending_data.base64 = base64;
1420     }
1421   else
1422     {
1423       if (base64)
1424         err = add_base64_to_object (result, "data", buffer, buflen);
1425       else
1426         err = cjson_AddStringToObject (result, "data", buffer);
1427     }
1428
1429  leave:
1430   gpgme_free (buffer);
1431   return err;
1432 }
1433
1434
1435 \f
1436 static const char hlp_encrypt[] =
1437   "op:     \"encrypt\"\n"
1438   "keys:   Array of strings with the fingerprints or user-ids\n"
1439   "        of the keys to encrypt the data.  For a single key\n"
1440   "        a String may be used instead of an array.\n"
1441   "data:   Input data. \n"
1442   "\n"
1443   "Optional parameters:\n"
1444   "protocol:      Either \"openpgp\" (default) or \"cms\".\n"
1445   "chunksize:     Max number of bytes in the resulting \"data\".\n"
1446   "signing_keys:  Similar to the keys parameter for added signing.\n"
1447   "               (openpgp only)"
1448   "\n"
1449   "Optional boolean flags (default is false):\n"
1450   "base64:        Input data is base64 encoded.\n"
1451   "mime:          Indicate that data is a MIME object.\n"
1452   "armor:         Request output in armored format.\n"
1453   "always-trust:  Request --always-trust option.\n"
1454   "no-encrypt-to: Do not use a default recipient.\n"
1455   "no-compress:   Do not compress the plaintext first.\n"
1456   "throw-keyids:  Request the --throw-keyids option.\n"
1457   "want-address:  Require that the keys include a mail address.\n"
1458   "wrap:          Assume the input is an OpenPGP message.\n"
1459   "\n"
1460   "Response on success:\n"
1461   "type:   \"ciphertext\"\n"
1462   "data:   Unless armor mode is used a Base64 encoded binary\n"
1463   "        ciphertext.  In armor mode a string with an armored\n"
1464   "        OpenPGP or a PEM message.\n"
1465   "base64: Boolean indicating whether data is base64 encoded.\n"
1466   "more:   Optional boolean indicating that \"getmore\" is required.";
1467 static gpg_error_t
1468 op_encrypt (cjson_t request, cjson_t result)
1469 {
1470   gpg_error_t err;
1471   gpgme_ctx_t ctx = NULL;
1472   gpgme_protocol_t protocol;
1473   char **signing_patterns = NULL;
1474   size_t chunksize;
1475   int opt_mime;
1476   char *keystring = NULL;
1477   gpgme_data_t input = NULL;
1478   gpgme_data_t output = NULL;
1479   int abool;
1480   gpgme_encrypt_flags_t encrypt_flags = 0;
1481   gpgme_ctx_t keylist_ctx = NULL;
1482   gpgme_key_t key = NULL;
1483
1484   if ((err = get_protocol (request, &protocol)))
1485     goto leave;
1486   ctx = get_context (protocol);
1487   if ((err = get_chunksize (request, &chunksize)))
1488     goto leave;
1489
1490   if ((err = get_boolean_flag (request, "mime", 0, &opt_mime)))
1491     goto leave;
1492
1493   if ((err = get_boolean_flag (request, "armor", 0, &abool)))
1494     goto leave;
1495   gpgme_set_armor (ctx, abool);
1496   if ((err = get_boolean_flag (request, "always-trust", 0, &abool)))
1497     goto leave;
1498   if (abool)
1499     encrypt_flags |= GPGME_ENCRYPT_ALWAYS_TRUST;
1500   if ((err = get_boolean_flag (request, "no-encrypt-to", 0,&abool)))
1501     goto leave;
1502   if (abool)
1503     encrypt_flags |= GPGME_ENCRYPT_NO_ENCRYPT_TO;
1504   if ((err = get_boolean_flag (request, "no-compress", 0, &abool)))
1505     goto leave;
1506   if (abool)
1507     encrypt_flags |= GPGME_ENCRYPT_NO_COMPRESS;
1508   if ((err = get_boolean_flag (request, "throw-keyids", 0, &abool)))
1509     goto leave;
1510   if (abool)
1511     encrypt_flags |= GPGME_ENCRYPT_THROW_KEYIDS;
1512   if ((err = get_boolean_flag (request, "wrap", 0, &abool)))
1513     goto leave;
1514   if (abool)
1515     encrypt_flags |= GPGME_ENCRYPT_WRAP;
1516   if ((err = get_boolean_flag (request, "want-address", 0, &abool)))
1517     goto leave;
1518   if (abool)
1519     encrypt_flags |= GPGME_ENCRYPT_WANT_ADDRESS;
1520
1521
1522   /* Get the keys.  */
1523   err = get_keys (request, "keys", &keystring);
1524   if (err)
1525     {
1526       /* Provide a custom error response.  */
1527       gpg_error_object (result, err, "Error getting keys: %s",
1528                         gpg_strerror (err));
1529       goto leave;
1530     }
1531
1532   /* Do we have signing keys ? */
1533   signing_patterns = create_keylist_patterns (request, "signing_keys");
1534   if (signing_patterns)
1535     {
1536       keylist_ctx = create_onetime_context (protocol);
1537       gpgme_set_keylist_mode (keylist_ctx, GPGME_KEYLIST_MODE_LOCAL);
1538
1539       err = gpgme_op_keylist_ext_start (keylist_ctx,
1540                                         (const char **) signing_patterns,
1541                                         1, 0);
1542       if (err)
1543         {
1544           gpg_error_object (result, err, "Error listing keys: %s",
1545                             gpg_strerror (err));
1546           goto leave;
1547         }
1548       while (!(err = gpgme_op_keylist_next (keylist_ctx, &key)))
1549         {
1550           if ((err = gpgme_signers_add (ctx, key)))
1551             {
1552               gpg_error_object (result, err, "Error adding signer: %s",
1553                                 gpg_strerror (err));
1554               goto leave;
1555             }
1556           gpgme_key_unref (key);
1557           key = NULL;
1558         }
1559       release_onetime_context (keylist_ctx);
1560       keylist_ctx = NULL;
1561     }
1562
1563   if ((err = get_string_data (request, result, "data", &input)))
1564       goto leave;
1565
1566   if (opt_mime)
1567     gpgme_data_set_encoding (input, GPGME_DATA_ENCODING_MIME);
1568
1569
1570   /* Create an output data object.  */
1571   err = gpgme_data_new (&output);
1572   if (err)
1573     {
1574       gpg_error_object (result, err, "Error creating output data object: %s",
1575                         gpg_strerror (err));
1576       goto leave;
1577     }
1578
1579   /* Encrypt.  */
1580   if (!signing_patterns)
1581     {
1582       err = gpgme_op_encrypt_ext (ctx, NULL, keystring, encrypt_flags,
1583                                   input, output);
1584     }
1585   else
1586     {
1587       err = gpgme_op_encrypt_sign_ext (ctx, NULL, keystring, encrypt_flags,
1588                                        input, output);
1589
1590     }
1591   /* encrypt_result = gpgme_op_encrypt_result (ctx); */
1592   if (err)
1593     {
1594       gpg_error_object (result, err, "Encryption failed: %s",
1595                         gpg_strerror (err));
1596       goto leave;
1597     }
1598   gpgme_data_release (input);
1599   input = NULL;
1600
1601   /* We need to base64 if armoring has not been requested.  */
1602   err = make_data_object (result, output, chunksize,
1603                           "ciphertext", !gpgme_get_armor (ctx));
1604   output = NULL;
1605
1606  leave:
1607   xfree_array (signing_patterns);
1608   xfree (keystring);
1609   release_onetime_context (keylist_ctx);
1610   gpgme_key_unref (key);
1611   gpgme_signers_clear (ctx);
1612   release_context (ctx);
1613   gpgme_data_release (input);
1614   gpgme_data_release (output);
1615   return err;
1616 }
1617
1618
1619 \f
1620 static const char hlp_decrypt[] =
1621   "op:     \"decrypt\"\n"
1622   "data:   The encrypted data.\n"
1623   "\n"
1624   "Optional parameters:\n"
1625   "protocol:      Either \"openpgp\" (default) or \"cms\".\n"
1626   "chunksize:     Max number of bytes in the resulting \"data\".\n"
1627   "\n"
1628   "Optional boolean flags (default is false):\n"
1629   "base64:        Input data is base64 encoded.\n"
1630   "\n"
1631   "Response on success:\n"
1632   "type:   \"plaintext\"\n"
1633   "data:   The decrypted data.  This may be base64 encoded.\n"
1634   "base64: Boolean indicating whether data is base64 encoded.\n"
1635   "mime:   A Boolean indicating whether the data is a MIME object.\n"
1636   "info:   An object with verification information. (gpgme_verify_result_t)\n"
1637   " file_name: Optional string of the plaintext file name.\n"
1638   " is_mime:    Boolean that is true if the messages claims it is MIME.\n"
1639   " signatures: Array of signatures\n"
1640   "  summary: Object containing summary information.\n"
1641   "   Boolean values: (Check gpgme_sigsum_t doc for meaning)\n"
1642   "    valid\n"
1643   "    green\n"
1644   "    red\n"
1645   "    revoked\n"
1646   "    key-expired\n"
1647   "    sig-expired\n"
1648   "    key-missing\n"
1649   "    crl-missing\n"
1650   "    crl-too-old\n"
1651   "    bad-policy\n"
1652   "    sys-error\n"
1653   "   sigsum: Array of strings representing the sigsum.\n"
1654   "  Boolean values:\n"
1655   "   wrong_key_usage: Key should not have been used for signing.\n"
1656   "   chain_model:     Validity has been verified using the chain model.\n"
1657   "   is_de_vs:        signature is in compliance to the de-vs mode.\n"
1658   "  String values:\n"
1659   "   status_string:      The status code as localized gpg-error string\n"
1660   "   fingerprint:        The fingerprint of the signing key.\n"
1661   "   validity_string:    The validity as string.\n"
1662   "   pubkey_algo_name:   gpgme_pubkey_algo_name of used algo.\n"
1663   "   hash_algo_name:     gpgme_hash_algo_name of used hash algo\n"
1664   "   pka_address:        The mailbox from the PKA information.\n"
1665   "  Number values:\n"
1666   "   status_code:     The status as a number. (gpg_error_t)\n"
1667   "   timestamp:       Signature creation time. (secs since epoch)\n"
1668   "   exp_timestamp:   Signature expiration or 0. (secs since epoch)\n"
1669   "   pka_trust: PKA status: 0 = not available, 1 = bad, 2 = okay, 3 = RFU.\n"
1670   "   validity: validity as number (gpgme_validity_t)\n"
1671   "   validity_reason: (gpg_error_t)\n"
1672   "  Array values:\n"
1673   "   notations: Notation data and policy urls (gpgme_sig_notation_t)\n"
1674   "    Boolean values:\n"
1675   "     human_readable\n"
1676   "     critical\n"
1677   "    String values:\n"
1678   "     name\n"
1679   "     value\n"
1680   "    Number values:\n"
1681   "     flags\n"
1682   "more:   Optional boolean indicating that \"getmore\" is required.";
1683 static gpg_error_t
1684 op_decrypt (cjson_t request, cjson_t result)
1685 {
1686   gpg_error_t err;
1687   gpgme_ctx_t ctx = NULL;
1688   gpgme_protocol_t protocol;
1689   size_t chunksize;
1690   gpgme_data_t input = NULL;
1691   gpgme_data_t output = NULL;
1692   gpgme_decrypt_result_t decrypt_result;
1693   gpgme_verify_result_t verify_result;
1694
1695   if ((err = get_protocol (request, &protocol)))
1696     goto leave;
1697   ctx = get_context (protocol);
1698   if ((err = get_chunksize (request, &chunksize)))
1699     goto leave;
1700
1701   if ((err = get_string_data (request, result, "data", &input)))
1702       goto leave;
1703
1704   /* Create an output data object.  */
1705   err = gpgme_data_new (&output);
1706   if (err)
1707     {
1708       gpg_error_object (result, err,
1709                         "Error creating output data object: %s",
1710                         gpg_strerror (err));
1711       goto leave;
1712     }
1713
1714   /* Decrypt.  */
1715   err = gpgme_op_decrypt_ext (ctx, GPGME_DECRYPT_VERIFY,
1716                               input, output);
1717   decrypt_result = gpgme_op_decrypt_result (ctx);
1718   if (err)
1719     {
1720       gpg_error_object (result, err, "Decryption failed: %s",
1721                         gpg_strerror (err));
1722       goto leave;
1723     }
1724   gpgme_data_release (input);
1725   input = NULL;
1726
1727   if (decrypt_result->is_mime)
1728     xjson_AddBoolToObject (result, "mime", 1);
1729
1730   verify_result = gpgme_op_verify_result (ctx);
1731   if (verify_result && verify_result->signatures)
1732     {
1733       xjson_AddItemToObject (result, "info",
1734                              verify_result_to_json (verify_result));
1735     }
1736
1737   err = make_data_object (result, output, chunksize, "plaintext", -1);
1738   output = NULL;
1739
1740   if (err)
1741     {
1742       gpg_error_object (result, err, "Plaintext output failed: %s",
1743                         gpg_strerror (err));
1744       goto leave;
1745     }
1746
1747  leave:
1748   release_context (ctx);
1749   gpgme_data_release (input);
1750   gpgme_data_release (output);
1751   return err;
1752 }
1753
1754
1755 \f
1756 static const char hlp_sign[] =
1757   "op:     \"sign\"\n"
1758   "keys:   Array of strings with the fingerprints of the signing key.\n"
1759   "        For a single key a String may be used instead of an array.\n"
1760   "data:   Input data. \n"
1761   "\n"
1762   "Optional parameters:\n"
1763   "protocol:      Either \"openpgp\" (default) or \"cms\".\n"
1764   "chunksize:     Max number of bytes in the resulting \"data\".\n"
1765   "sender:        The mail address of the sender.\n"
1766   "mode:          A string with the signing mode can be:\n"
1767   "               detached (default)\n"
1768   "               opaque\n"
1769   "               clearsign\n"
1770   "\n"
1771   "Optional boolean flags (default is false):\n"
1772   "base64:        Input data is base64 encoded.\n"
1773   "armor:         Request output in armored format.\n"
1774   "\n"
1775   "Response on success:\n"
1776   "type:   \"signature\"\n"
1777   "data:   Unless armor mode is used a Base64 encoded binary\n"
1778   "        signature.  In armor mode a string with an armored\n"
1779   "        OpenPGP or a PEM message.\n"
1780   "base64: Boolean indicating whether data is base64 encoded.\n"
1781   "more:   Optional boolean indicating that \"getmore\" is required.";
1782 static gpg_error_t
1783 op_sign (cjson_t request, cjson_t result)
1784 {
1785   gpg_error_t err;
1786   gpgme_ctx_t ctx = NULL;
1787   gpgme_protocol_t protocol;
1788   size_t chunksize;
1789   char **patterns = NULL;
1790   gpgme_data_t input = NULL;
1791   gpgme_data_t output = NULL;
1792   int abool;
1793   cjson_t j_tmp;
1794   gpgme_sig_mode_t mode = GPGME_SIG_MODE_DETACH;
1795   gpgme_ctx_t keylist_ctx = NULL;
1796   gpgme_key_t key = NULL;
1797
1798   if ((err = get_protocol (request, &protocol)))
1799     goto leave;
1800   ctx = get_context (protocol);
1801   if ((err = get_chunksize (request, &chunksize)))
1802     goto leave;
1803
1804   if ((err = get_boolean_flag (request, "armor", 0, &abool)))
1805     goto leave;
1806   gpgme_set_armor (ctx, abool);
1807
1808   j_tmp = cJSON_GetObjectItem (request, "mode");
1809   if (j_tmp && cjson_is_string (j_tmp))
1810     {
1811       if (!strcmp (j_tmp->valuestring, "opaque"))
1812         {
1813           mode = GPGME_SIG_MODE_NORMAL;
1814         }
1815       else if (!strcmp (j_tmp->valuestring, "clearsign"))
1816         {
1817           mode = GPGME_SIG_MODE_CLEAR;
1818         }
1819     }
1820
1821   j_tmp = cJSON_GetObjectItem (request, "sender");
1822   if (j_tmp && cjson_is_string (j_tmp))
1823     {
1824       gpgme_set_sender (ctx, j_tmp->valuestring);
1825     }
1826
1827   patterns = create_keylist_patterns (request, "keys");
1828   if (!patterns)
1829     {
1830       gpg_error_object (result, err, "Error getting keys: %s",
1831                         gpg_strerror (gpg_error (GPG_ERR_NO_KEY)));
1832       goto leave;
1833     }
1834
1835   /* Do a keylisting and add the keys */
1836   keylist_ctx = create_onetime_context (protocol);
1837   gpgme_set_keylist_mode (keylist_ctx, GPGME_KEYLIST_MODE_LOCAL);
1838
1839   err = gpgme_op_keylist_ext_start (keylist_ctx,
1840                                     (const char **) patterns, 1, 0);
1841   if (err)
1842     {
1843       gpg_error_object (result, err, "Error listing keys: %s",
1844                         gpg_strerror (err));
1845       goto leave;
1846     }
1847   while (!(err = gpgme_op_keylist_next (keylist_ctx, &key)))
1848     {
1849       if ((err = gpgme_signers_add (ctx, key)))
1850         {
1851           gpg_error_object (result, err, "Error adding signer: %s",
1852                             gpg_strerror (err));
1853           goto leave;
1854         }
1855       gpgme_key_unref (key);
1856       key = NULL;
1857     }
1858
1859   if ((err = get_string_data (request, result, "data", &input)))
1860     goto leave;
1861
1862   /* Create an output data object.  */
1863   err = gpgme_data_new (&output);
1864   if (err)
1865     {
1866       gpg_error_object (result, err, "Error creating output data object: %s",
1867                         gpg_strerror (err));
1868       goto leave;
1869     }
1870
1871   /* Sign. */
1872   err = gpgme_op_sign (ctx, input, output, mode);
1873   if (err)
1874     {
1875       gpg_error_object (result, err, "Signing failed: %s",
1876                         gpg_strerror (err));
1877       goto leave;
1878     }
1879
1880   gpgme_data_release (input);
1881   input = NULL;
1882
1883   /* We need to base64 if armoring has not been requested.  */
1884   err = make_data_object (result, output, chunksize,
1885                           "signature", !gpgme_get_armor (ctx));
1886   output = NULL;
1887
1888  leave:
1889   xfree_array (patterns);
1890   gpgme_signers_clear (ctx);
1891   gpgme_key_unref (key);
1892   release_onetime_context (keylist_ctx);
1893   release_context (ctx);
1894   gpgme_data_release (input);
1895   gpgme_data_release (output);
1896   return err;
1897 }
1898
1899
1900 \f
1901 static const char hlp_verify[] =
1902   "op:     \"verify\"\n"
1903   "data:   The data to verify.\n"
1904   "\n"
1905   "Optional parameters:\n"
1906   "protocol:      Either \"openpgp\" (default) or \"cms\".\n"
1907   "chunksize:     Max number of bytes in the resulting \"data\".\n"
1908   "signature:     A detached signature. If missing opaque is assumed.\n"
1909   "\n"
1910   "Optional boolean flags (default is false):\n"
1911   "base64:        Input data is base64 encoded.\n"
1912   "\n"
1913   "Response on success:\n"
1914   "type:   \"plaintext\"\n"
1915   "data:   The verified data.  This may be base64 encoded.\n"
1916   "base64: Boolean indicating whether data is base64 encoded.\n"
1917   "info:   An object with verification information (gpgme_verify_result_t).\n"
1918   " file_name: Optional string of the plaintext file name.\n"
1919   " is_mime:    Boolean that is true if the messages claims it is MIME.\n"
1920   " signatures: Array of signatures\n"
1921   "  summary: Object containing summary information.\n"
1922   "   Boolean values: (Check gpgme_sigsum_t doc for meaning)\n"
1923   "    valid\n"
1924   "    green\n"
1925   "    red\n"
1926   "    revoked\n"
1927   "    key-expired\n"
1928   "    sig-expired\n"
1929   "    key-missing\n"
1930   "    crl-missing\n"
1931   "    crl-too-old\n"
1932   "    bad-policy\n"
1933   "    sys-error\n"
1934   "   sigsum: Array of strings representing the sigsum.\n"
1935   "  Boolean values:\n"
1936   "   wrong_key_usage: Key should not have been used for signing.\n"
1937   "   chain_model:     Validity has been verified using the chain model.\n"
1938   "   is_de_vs:        signature is in compliance to the de-vs mode.\n"
1939   "  String values:\n"
1940   "   status_string:      The status code as localized gpg-error string\n"
1941   "   fingerprint:        The fingerprint of the signing key.\n"
1942   "   validity_string:    The validity as string.\n"
1943   "   pubkey_algo_name:   gpgme_pubkey_algo_name of used algo.\n"
1944   "   hash_algo_name:     gpgme_hash_algo_name of used hash algo\n"
1945   "   pka_address:        The mailbox from the PKA information.\n"
1946   "  Number values:\n"
1947   "   status_code:     The status as a number. (gpg_error_t)\n"
1948   "   timestamp:       Signature creation time. (secs since epoch)\n"
1949   "   exp_timestamp:   Signature expiration or 0. (secs since epoch)\n"
1950   "   pka_trust: PKA status: 0 = not available, 1 = bad, 2 = okay, 3 = RFU.\n"
1951   "   validity: validity as number (gpgme_validity_t)\n"
1952   "   validity_reason: (gpg_error_t)\n"
1953   "  Array values:\n"
1954   "   notations: Notation data and policy urls (gpgme_sig_notation_t)\n"
1955   "    Boolean values:\n"
1956   "     human_readable\n"
1957   "     critical\n"
1958   "    String values:\n"
1959   "     name\n"
1960   "     value\n"
1961   "    Number values:\n"
1962   "     flags\n"
1963   "more:   Optional boolean indicating that \"getmore\" is required.";
1964 static gpg_error_t
1965 op_verify (cjson_t request, cjson_t result)
1966 {
1967   gpg_error_t err;
1968   gpgme_ctx_t ctx = NULL;
1969   gpgme_protocol_t protocol;
1970   size_t chunksize;
1971   gpgme_data_t input = NULL;
1972   gpgme_data_t signature = NULL;
1973   gpgme_data_t output = NULL;
1974   gpgme_verify_result_t verify_result;
1975
1976   if ((err = get_protocol (request, &protocol)))
1977     goto leave;
1978   ctx = get_context (protocol);
1979   if ((err = get_chunksize (request, &chunksize)))
1980     goto leave;
1981
1982   if ((err = get_string_data (request, result, "data", &input)))
1983     goto leave;
1984
1985   err = get_string_data (request, result, "signature", &signature);
1986   /* Signature data is optional otherwise we expect opaque or clearsigned. */
1987   if (err && err != gpg_error (GPG_ERR_NO_DATA))
1988     goto leave;
1989
1990   /* Create an output data object.  */
1991   err = gpgme_data_new (&output);
1992   if (err)
1993     {
1994       gpg_error_object (result, err, "Error creating output data object: %s",
1995                         gpg_strerror (err));
1996       goto leave;
1997     }
1998
1999   /* Verify.  */
2000   if (signature)
2001     {
2002       err = gpgme_op_verify (ctx, signature, input, output);
2003     }
2004   else
2005     {
2006       err = gpgme_op_verify (ctx, input, 0, output);
2007     }
2008   if (err)
2009     {
2010       gpg_error_object (result, err, "Verify failed: %s", gpg_strerror (err));
2011       goto leave;
2012     }
2013   gpgme_data_release (input);
2014   input = NULL;
2015   gpgme_data_release (signature);
2016   signature = NULL;
2017
2018   verify_result = gpgme_op_verify_result (ctx);
2019   if (verify_result && verify_result->signatures)
2020     {
2021       xjson_AddItemToObject (result, "info",
2022                              verify_result_to_json (verify_result));
2023     }
2024
2025   err = make_data_object (result, output, chunksize, "plaintext", -1);
2026   output = NULL;
2027
2028   if (err)
2029     {
2030       gpg_error_object (result, err, "Plaintext output failed: %s",
2031                         gpg_strerror (err));
2032       goto leave;
2033     }
2034
2035  leave:
2036   release_context (ctx);
2037   gpgme_data_release (input);
2038   gpgme_data_release (output);
2039   gpgme_data_release (signature);
2040   return err;
2041 }
2042
2043
2044 \f
2045 static const char hlp_version[] =
2046   "op:     \"version\"\n"
2047   "\n"
2048   "Response on success:\n"
2049   "gpgme:  The GPGME Version.\n"
2050   "info:   dump of engine info. containing:\n"
2051   "        protocol: The protocol.\n"
2052   "        fname:    The file name.\n"
2053   "        version:  The version.\n"
2054   "        req_ver:  The required version.\n"
2055   "        homedir:  The homedir of the engine or \"default\".\n";
2056 static gpg_error_t
2057 op_version (cjson_t request, cjson_t result)
2058 {
2059   gpg_error_t err = 0;
2060   gpgme_engine_info_t ei = NULL;
2061   cjson_t infos = xjson_CreateArray ();
2062
2063   (void)request;
2064
2065   if (!cJSON_AddStringToObject (result, "gpgme", gpgme_check_version (NULL)))
2066     {
2067       cJSON_Delete (infos);
2068       return gpg_error_from_syserror ();
2069     }
2070
2071   if ((err = gpgme_get_engine_info (&ei)))
2072     {
2073       cJSON_Delete (infos);
2074       return err;
2075     }
2076
2077   for (; ei; ei = ei->next)
2078     cJSON_AddItemToArray (infos, engine_info_to_json (ei));
2079
2080   if (!cJSON_AddItemToObject (result, "info", infos))
2081     {
2082       err = gpg_error_from_syserror ();
2083       cJSON_Delete (infos);
2084       return err;
2085     }
2086
2087   return 0;
2088 }
2089
2090
2091 \f
2092 static const char hlp_keylist[] =
2093   "op:     \"keylist\"\n"
2094   "\n"
2095   "Optional parameters:\n"
2096   "keys:          Array of strings or fingerprints to lookup\n"
2097   "               For a single key a String may be used instead of an array.\n"
2098   "               default lists all keys.\n"
2099   "protocol:      Either \"openpgp\" (default) or \"cms\".\n"
2100   "chunksize:     Max number of bytes in the resulting \"data\".\n"
2101   "\n"
2102   "Optional boolean flags (default is false):\n"
2103   "secret:        List secret keys.\n"
2104   "extern:        Add KEYLIST_MODE_EXTERN.\n"
2105   "local:         Add KEYLIST_MODE_LOCAL. (default mode).\n"
2106   "sigs:          Add KEYLIST_MODE_SIGS.\n"
2107   "notations:     Add KEYLIST_MODE_SIG_NOTATIONS.\n"
2108   "tofu:          Add KEYLIST_MODE_WITH_TOFU.\n"
2109   "ephemeral:     Add KEYLIST_MODE_EPHEMERAL.\n"
2110   "validate:      Add KEYLIST_MODE_VALIDATE.\n"
2111   "\n"
2112   "Response on success:\n"
2113   "keys:   Array of keys.\n"
2114   "  Boolean values:\n"
2115   "   revoked\n"
2116   "   expired\n"
2117   "   disabled\n"
2118   "   invalid\n"
2119   "   can_encrypt\n"
2120   "   can_sign\n"
2121   "   can_certify\n"
2122   "   can_authenticate\n"
2123   "   secret\n"
2124   "   is_qualified\n"
2125   "  String values:\n"
2126   "   protocol\n"
2127   "   issuer_serial (CMS Only)\n"
2128   "   issuer_name (CMS Only)\n"
2129   "   chain_id (CMS Only)\n"
2130   "   owner_trust (OpenPGP only)\n"
2131   "   fingerprint\n"
2132   "  Number values:\n"
2133   "   last_update\n"
2134   "   origin\n"
2135   "  Array values:\n"
2136   "   subkeys\n"
2137   "    Boolean values:\n"
2138   "     revoked\n"
2139   "     expired\n"
2140   "     disabled\n"
2141   "     invalid\n"
2142   "     can_encrypt\n"
2143   "     can_sign\n"
2144   "     can_certify\n"
2145   "     can_authenticate\n"
2146   "     secret\n"
2147   "     is_qualified\n"
2148   "     is_cardkey\n"
2149   "     is_de_vs\n"
2150   "    String values:\n"
2151   "     pubkey_algo_name\n"
2152   "     pubkey_algo_string\n"
2153   "     keyid\n"
2154   "     card_number\n"
2155   "     curve\n"
2156   "     keygrip\n"
2157   "    Number values:\n"
2158   "     pubkey_algo\n"
2159   "     length\n"
2160   "     timestamp\n"
2161   "     expires\n"
2162   "   userids\n"
2163   "    Boolean values:\n"
2164   "     revoked\n"
2165   "     invalid\n"
2166   "    String values:\n"
2167   "     validity\n"
2168   "     uid\n"
2169   "     name\n"
2170   "     email\n"
2171   "     comment\n"
2172   "     address\n"
2173   "    Number values:\n"
2174   "     origin\n"
2175   "     last_update\n"
2176   "    Array values:\n"
2177   "     signatures\n"
2178   "      Boolean values:\n"
2179   "       revoked\n"
2180   "       expired\n"
2181   "       invalid\n"
2182   "       exportable\n"
2183   "      String values:\n"
2184   "       pubkey_algo_name\n"
2185   "       keyid\n"
2186   "       status\n"
2187   "       uid\n"
2188   "       name\n"
2189   "       email\n"
2190   "       comment\n"
2191   "      Number values:\n"
2192   "       pubkey_algo\n"
2193   "       timestamp\n"
2194   "       expires\n"
2195   "       status_code\n"
2196   "       sig_class\n"
2197   "      Array values:\n"
2198   "       notations\n"
2199   "        Boolean values:\n"
2200   "         human_readable\n"
2201   "         critical\n"
2202   "        String values:\n"
2203   "         name\n"
2204   "         value\n"
2205   "        Number values:\n"
2206   "         flags\n"
2207   "     tofu\n"
2208   "      String values:\n"
2209   "       description\n"
2210   "      Number values:\n"
2211   "       validity\n"
2212   "       policy\n"
2213   "       signcount\n"
2214   "       encrcount\n"
2215   "       signfirst\n"
2216   "       signlast\n"
2217   "       encrfirst\n"
2218   "       encrlast\n"
2219   "more:   Optional boolean indicating that \"getmore\" is required.\n"
2220   "        (not implemented)";
2221 static gpg_error_t
2222 op_keylist (cjson_t request, cjson_t result)
2223 {
2224   gpg_error_t err;
2225   gpgme_ctx_t ctx = NULL;
2226   gpgme_protocol_t protocol;
2227   size_t chunksize;
2228   char **patterns = NULL;
2229   int abool;
2230   gpgme_keylist_mode_t mode = 0;
2231   gpgme_key_t key = NULL;
2232   cjson_t keyarray = xjson_CreateArray ();
2233
2234   if ((err = get_protocol (request, &protocol)))
2235     goto leave;
2236   ctx = get_context (protocol);
2237   if ((err = get_chunksize (request, &chunksize)))
2238     goto leave;
2239
2240   /* Handle the various keylist mode bools. */
2241   if ((err = get_boolean_flag (request, "secret", 0, &abool)))
2242     goto leave;
2243   if (abool)
2244     mode |= GPGME_KEYLIST_MODE_WITH_SECRET;
2245
2246   if ((err = get_boolean_flag (request, "extern", 0, &abool)))
2247     goto leave;
2248   if (abool)
2249     mode |= GPGME_KEYLIST_MODE_EXTERN;
2250
2251   if ((err = get_boolean_flag (request, "local", 0, &abool)))
2252     goto leave;
2253   if (abool)
2254     mode |= GPGME_KEYLIST_MODE_LOCAL;
2255
2256   if ((err = get_boolean_flag (request, "sigs", 0, &abool)))
2257     goto leave;
2258   if (abool)
2259     mode |= GPGME_KEYLIST_MODE_SIGS;
2260
2261   if ((err = get_boolean_flag (request, "notations", 0, &abool)))
2262     goto leave;
2263   if (abool)
2264     mode |= GPGME_KEYLIST_MODE_SIG_NOTATIONS;
2265
2266   if ((err = get_boolean_flag (request, "tofu", 0, &abool)))
2267     goto leave;
2268   if (abool)
2269     mode |= GPGME_KEYLIST_MODE_WITH_TOFU;
2270
2271   if ((err = get_boolean_flag (request, "ephemeral", 0, &abool)))
2272     goto leave;
2273   if (abool)
2274     mode |= GPGME_KEYLIST_MODE_EPHEMERAL;
2275
2276   if ((err = get_boolean_flag (request, "validate", 0, &abool)))
2277     goto leave;
2278   if (abool)
2279     mode |= GPGME_KEYLIST_MODE_VALIDATE;
2280
2281   if (!mode)
2282     {
2283       /* default to local */
2284       mode = GPGME_KEYLIST_MODE_LOCAL;
2285     }
2286
2287   /* Get the keys.  */
2288   patterns = create_keylist_patterns (request, "keys");
2289
2290   /* Do a keylisting and add the keys */
2291   gpgme_set_keylist_mode (ctx, mode);
2292
2293   err = gpgme_op_keylist_ext_start (ctx, (const char **) patterns,
2294                                     (mode & GPGME_KEYLIST_MODE_WITH_SECRET),
2295                                     0);
2296   if (err)
2297     {
2298       gpg_error_object (result, err, "Error listing keys: %s",
2299                         gpg_strerror (err));
2300       goto leave;
2301     }
2302
2303   while (!(err = gpgme_op_keylist_next (ctx, &key)))
2304     {
2305       cJSON_AddItemToArray (keyarray, key_to_json (key));
2306       gpgme_key_unref (key);
2307     }
2308   err = 0;
2309
2310   if (!cJSON_AddItemToObject (result, "keys", keyarray))
2311     {
2312       err = gpg_error_from_syserror ();
2313       goto leave;
2314     }
2315
2316  leave:
2317   xfree_array (patterns);
2318   if (err)
2319     {
2320       cJSON_Delete (keyarray);
2321     }
2322   return err;
2323 }
2324
2325
2326 \f
2327 static const char hlp_import[] =
2328   "op:     \"import\"\n"
2329   "data:   The data to import.\n"
2330   "\n"
2331   "Optional parameters:\n"
2332   "protocol:      Either \"openpgp\" (default) or \"cms\".\n"
2333   "\n"
2334   "Optional boolean flags (default is false):\n"
2335   "base64:        Input data is base64 encoded.\n"
2336   "\n"
2337   "Response on success:\n"
2338   "result: The import result.\n"
2339   "  Number values:\n"
2340   "   considered\n"
2341   "   no_user_id\n"
2342   "   imported\n"
2343   "   imported_rsa\n"
2344   "   unchanged\n"
2345   "   new_user_ids\n"
2346   "   new_sub_keys\n"
2347   "   new_signatures\n"
2348   "   new_revocations\n"
2349   "   secret_read\n"
2350   "   secret_imported\n"
2351   "   secret_unchanged\n"
2352   "   skipped_new_keys\n"
2353   "   not_imported\n"
2354   "   skipped_v3_keys\n"
2355   "  Array values:\n"
2356   "   imports: List of keys for which an import was attempted\n"
2357   "    String values:\n"
2358   "     fingerprint\n"
2359   "     error_string\n"
2360   "    Number values:\n"
2361   "     error_code\n"
2362   "     status\n";
2363 static gpg_error_t
2364 op_import (cjson_t request, cjson_t result)
2365 {
2366   gpg_error_t err;
2367   gpgme_ctx_t ctx = NULL;
2368   gpgme_data_t input = NULL;
2369   gpgme_import_result_t import_result;
2370   gpgme_protocol_t protocol;
2371
2372   if ((err = get_protocol (request, &protocol)))
2373     goto leave;
2374   ctx = get_context (protocol);
2375
2376   if ((err = get_string_data (request, result, "data", &input)))
2377       goto leave;
2378
2379   /* Import.  */
2380   err = gpgme_op_import (ctx, input);
2381   import_result = gpgme_op_import_result (ctx);
2382   if (err)
2383     {
2384       gpg_error_object (result, err, "Import failed: %s",
2385                         gpg_strerror (err));
2386       goto leave;
2387     }
2388   gpgme_data_release (input);
2389   input = NULL;
2390
2391   xjson_AddItemToObject (result, "result",
2392                          import_result_to_json (import_result));
2393
2394  leave:
2395   release_context (ctx);
2396   gpgme_data_release (input);
2397   return err;
2398 }
2399
2400
2401 static const char hlp_export[] =
2402   "op:     \"export\"\n"
2403   "\n"
2404   "Optional parameters:\n"
2405   "keys:          Array of strings or fingerprints to lookup\n"
2406   "               For a single key a String may be used instead of an array.\n"
2407   "               default exports all keys.\n"
2408   "protocol:      Either \"openpgp\" (default) or \"cms\".\n"
2409   "chunksize:     Max number of bytes in the resulting \"data\".\n"
2410   "\n"
2411   "Optional boolean flags (default is false):\n"
2412   "armor:         Request output in armored format.\n"
2413   "extern:        Add EXPORT_MODE_EXTERN.\n"
2414   "minimal:       Add EXPORT_MODE_MINIMAL.\n"
2415   "raw:           Add EXPORT_MODE_RAW.\n"
2416   "pkcs12:        Add EXPORT_MODE_PKCS12.\n"
2417   "\n"
2418   "Response on success:\n"
2419   "type:   \"keys\"\n"
2420   "data:   Unless armor mode is used a Base64 encoded binary.\n"
2421   "        In armor mode a string with an armored\n"
2422   "        OpenPGP or a PEM / PKCS12 key.\n"
2423   "base64: Boolean indicating whether data is base64 encoded.\n"
2424   "more:   Optional boolean indicating that \"getmore\" is required.";
2425 static gpg_error_t
2426 op_export (cjson_t request, cjson_t result)
2427 {
2428   gpg_error_t err;
2429   gpgme_ctx_t ctx = NULL;
2430   gpgme_protocol_t protocol;
2431   size_t chunksize;
2432   char **patterns = NULL;
2433   int abool;
2434   gpgme_export_mode_t mode = 0;
2435   gpgme_data_t output = NULL;
2436
2437   if ((err = get_protocol (request, &protocol)))
2438     goto leave;
2439   ctx = get_context (protocol);
2440   if ((err = get_chunksize (request, &chunksize)))
2441     goto leave;
2442
2443   if ((err = get_boolean_flag (request, "armor", 0, &abool)))
2444     goto leave;
2445   gpgme_set_armor (ctx, abool);
2446
2447   /* Handle the various export mode bools. */
2448   if ((err = get_boolean_flag (request, "secret", 0, &abool)))
2449     goto leave;
2450   if (abool)
2451     {
2452       err = gpg_error (GPG_ERR_FORBIDDEN);
2453       goto leave;
2454     }
2455
2456   if ((err = get_boolean_flag (request, "extern", 0, &abool)))
2457     goto leave;
2458   if (abool)
2459     mode |= GPGME_EXPORT_MODE_EXTERN;
2460
2461   if ((err = get_boolean_flag (request, "minimal", 0, &abool)))
2462     goto leave;
2463   if (abool)
2464     mode |= GPGME_EXPORT_MODE_MINIMAL;
2465
2466   if ((err = get_boolean_flag (request, "raw", 0, &abool)))
2467     goto leave;
2468   if (abool)
2469     mode |= GPGME_EXPORT_MODE_RAW;
2470
2471   if ((err = get_boolean_flag (request, "pkcs12", 0, &abool)))
2472     goto leave;
2473   if (abool)
2474     mode |= GPGME_EXPORT_MODE_PKCS12;
2475
2476   /* Get the export patterns.  */
2477   patterns = create_keylist_patterns (request, "keys");
2478
2479   /* Create an output data object.  */
2480   err = gpgme_data_new (&output);
2481   if (err)
2482     {
2483       gpg_error_object (result, err, "Error creating output data object: %s",
2484                         gpg_strerror (err));
2485       goto leave;
2486     }
2487
2488   err = gpgme_op_export_ext (ctx, (const char **) patterns,
2489                              mode, output);
2490   if (err)
2491     {
2492       gpg_error_object (result, err, "Error exporting keys: %s",
2493                         gpg_strerror (err));
2494       goto leave;
2495     }
2496
2497   /* We need to base64 if armoring has not been requested.  */
2498   err = make_data_object (result, output, chunksize,
2499                           "keys", !gpgme_get_armor (ctx));
2500   output = NULL;
2501
2502 leave:
2503   xfree_array (patterns);
2504   release_context (ctx);
2505   gpgme_data_release (output);
2506
2507   return err;
2508 }
2509
2510
2511 static const char hlp_delete[] =
2512   "op:     \"delete\"\n"
2513   "key:    Fingerprint of the key to delete.\n"
2514   "\n"
2515   "Optional parameters:\n"
2516   "protocol:      Either \"openpgp\" (default) or \"cms\".\n"
2517   "\n"
2518   "Response on success:\n"
2519   "success:   Boolean true.\n";
2520 static gpg_error_t
2521 op_delete (cjson_t request, cjson_t result)
2522 {
2523   gpg_error_t err;
2524   gpgme_ctx_t ctx = NULL;
2525   gpgme_ctx_t keylist_ctx = NULL;
2526   gpgme_protocol_t protocol;
2527   gpgme_key_t key;
2528   int secret = 0;
2529   cjson_t j_key = NULL;
2530
2531   if ((err = get_protocol (request, &protocol)))
2532     goto leave;
2533   ctx = get_context (protocol);
2534   keylist_ctx = get_context (protocol);
2535
2536   if ((err = get_boolean_flag (request, "secret", 0, &secret)))
2537     goto leave;
2538   if (secret)
2539     {
2540       err = gpg_error (GPG_ERR_FORBIDDEN);
2541       goto leave;
2542     }
2543
2544   j_key = cJSON_GetObjectItem (request, "key");
2545   if (!j_key)
2546     {
2547       err = gpg_error (GPG_ERR_NO_KEY);
2548       goto leave;
2549     }
2550   if (!cjson_is_string (j_key))
2551     {
2552       err = gpg_error (GPG_ERR_INV_VALUE);
2553       goto leave;
2554     }
2555
2556   /* Get the key */
2557   if ((err = gpgme_get_key (keylist_ctx, j_key->valuestring, &key, 0)))
2558     {
2559       gpg_error_object (result, err, "Error fetching key for delete: %s",
2560                         gpg_strerror (err));
2561       goto leave;
2562     }
2563
2564   err = gpgme_op_delete (ctx, key, 0);
2565   if (err)
2566     {
2567       gpg_error_object (result, err, "Error deleting key: %s",
2568                         gpg_strerror (err));
2569       goto leave;
2570     }
2571
2572   xjson_AddBoolToObject (result, "success", 1);
2573
2574 leave:
2575   gpgme_key_unref (key);
2576   release_context (ctx);
2577   release_context (keylist_ctx);
2578
2579   return err;
2580 }
2581
2582
2583 static const char hlp_config_opt[] =
2584   "op:       \"config_opt\"\n"
2585   "component: The component of the option.\n"
2586   "option:    The name of the option.\n"
2587   "\n"
2588   "Response on success:\n"
2589   "\n"
2590   "option: Information about the option.\n"
2591   " String values:\n"
2592   "  name: The name of the option\n"
2593   "  description: Localized description of the opt.\n"
2594   "  argname: Thhe argument name e.g. --verbose\n"
2595   "  default_description\n"
2596   "  no_arg_description\n"
2597   " Number values:\n"
2598   "  flags: Flags for this option.\n"
2599   "  level: the level of the description. See gpgme_conf_level_t.\n"
2600   "  type: The type of the option. See gpgme_conf_type_t.\n"
2601   "  alt_type: Alternate type of the option. See gpgme_conf_type_t\n"
2602   " Arg type values: (see desc. below)\n"
2603   "  default_value: Array of the default value.\n"
2604   "  no_arg_value: Array of the value if it is not set.\n"
2605   "  value: Array for the current value if the option is set.\n"
2606   "\n"
2607   "If the response is empty the option was not found\n"
2608   "";
2609 static gpg_error_t
2610 op_config_opt (cjson_t request, cjson_t result)
2611 {
2612   gpg_error_t err;
2613   gpgme_ctx_t ctx = NULL;
2614   gpgme_conf_comp_t conf = NULL;
2615   gpgme_conf_comp_t comp = NULL;
2616   cjson_t j_tmp;
2617   char *comp_name = NULL;
2618   char *opt_name = NULL;
2619
2620   ctx = get_context (GPGME_PROTOCOL_GPGCONF);
2621
2622   j_tmp = cJSON_GetObjectItem (request, "component");
2623   if (!j_tmp || !cjson_is_string (j_tmp))
2624     {
2625       err = gpg_error (GPG_ERR_INV_VALUE);
2626       goto leave;
2627     }
2628   comp_name = j_tmp->valuestring;
2629
2630
2631   j_tmp = cJSON_GetObjectItem (request, "option");
2632   if (!j_tmp || !cjson_is_string (j_tmp))
2633     {
2634       err = gpg_error (GPG_ERR_INV_VALUE);
2635       goto leave;
2636     }
2637   opt_name = j_tmp->valuestring;
2638
2639   /* Load the config */
2640   err = gpgme_op_conf_load (ctx, &conf);
2641   if (err)
2642     {
2643       goto leave;
2644     }
2645
2646   comp = conf;
2647   for (comp = conf; comp; comp = comp->next)
2648     {
2649       gpgme_conf_opt_t opt = NULL;
2650       int found = 0;
2651       if (!comp->name || strcmp (comp->name, comp_name))
2652         {
2653           /* Skip components if a single one is specified */
2654           continue;
2655         }
2656       for (opt = comp->options; opt; opt = opt->next)
2657         {
2658           if (!opt->name || strcmp (opt->name, opt_name))
2659             {
2660               /* Skip components if a single one is specified */
2661               continue;
2662             }
2663           xjson_AddItemToObject (result, "option", conf_opt_to_json (opt));
2664           found = 1;
2665           break;
2666         }
2667       if (found)
2668         break;
2669     }
2670
2671 leave:
2672   gpgme_conf_release (conf);
2673   release_context (ctx);
2674
2675   return err;
2676 }
2677
2678
2679 static const char hlp_config[] =
2680   "op:     \"config\"\n"
2681   "\n"
2682   "Optional parameters:\n"
2683   "component:    Component of entries to list.\n"
2684   "              Default: all\n"
2685   "\n"
2686   "Response on success:\n"
2687   "   components: Array of the component program configs.\n"
2688   "     name:         The component name.\n"
2689   "     description:  Description of the component.\n"
2690   "     program_name: The absolute path to the program.\n"
2691   "     options: Array of config options\n"
2692   "      String values:\n"
2693   "       name: The name of the option\n"
2694   "       description: Localized description of the opt.\n"
2695   "       argname: Thhe argument name e.g. --verbose\n"
2696   "       default_description\n"
2697   "       no_arg_description\n"
2698   "      Number values:\n"
2699   "       flags: Flags for this option.\n"
2700   "       level: the level of the description. See gpgme_conf_level_t.\n"
2701   "       type: The type of the option. See gpgme_conf_type_t.\n"
2702   "       alt_type: Alternate type of the option. See gpgme_conf_type_t\n"
2703   "      Arg type values: (see desc. below)\n"
2704   "       default_value: Array of the default value.\n"
2705   "       no_arg_value: Array of the value if it is not set.\n"
2706   "       value: Array for the current value if the option is set.\n"
2707   "\n"
2708   "Conf type values are an array of values that are either\n"
2709   "of type number named \"number\" or of type string,\n"
2710   "named \"string\".\n"
2711   "If the type is none the bool value is_none is true.\n"
2712   "";
2713 static gpg_error_t
2714 op_config (cjson_t request, cjson_t result)
2715 {
2716   gpg_error_t err;
2717   gpgme_ctx_t ctx = NULL;
2718   gpgme_conf_comp_t conf = NULL;
2719   gpgme_conf_comp_t comp = NULL;
2720   cjson_t j_tmp;
2721   char *comp_name = NULL;
2722   cjson_t j_comps = xjson_CreateArray ();
2723
2724   ctx = get_context (GPGME_PROTOCOL_GPGCONF);
2725
2726   j_tmp = cJSON_GetObjectItem (request, "component");
2727   if (j_tmp && cjson_is_string (j_tmp))
2728     {
2729       comp_name = j_tmp->valuestring;
2730     }
2731   else if (j_tmp && !cjson_is_string (j_tmp))
2732     {
2733       err = gpg_error (GPG_ERR_INV_VALUE);
2734       goto leave;
2735     }
2736
2737   /* Load the config */
2738   err = gpgme_op_conf_load (ctx, &conf);
2739   if (err)
2740     {
2741       goto leave;
2742     }
2743
2744   comp = conf;
2745   for (comp = conf; comp; comp = comp->next)
2746     {
2747       if (comp_name && comp->name && strcmp (comp->name, comp_name))
2748         {
2749           /* Skip components if a single one is specified */
2750           continue;
2751         }
2752       cJSON_AddItemToArray (j_comps, conf_comp_to_json (comp));
2753     }
2754   xjson_AddItemToObject (result, "components", j_comps);
2755
2756 leave:
2757   gpgme_conf_release (conf);
2758   release_context (ctx);
2759
2760   return err;
2761 }
2762
2763
2764 \f
2765 static const char hlp_getmore[] =
2766   "op:     \"getmore\"\n"
2767   "\n"
2768   "Optional parameters:\n"
2769   "chunksize:  Max number of bytes in the \"data\" object.\n"
2770   "\n"
2771   "Response on success:\n"
2772   "type:       Type of the pending data\n"
2773   "data:       The next chunk of data\n"
2774   "base64:     Boolean indicating whether data is base64 encoded\n"
2775   "more:       Optional boolean requesting another \"getmore\".";
2776 static gpg_error_t
2777 op_getmore (cjson_t request, cjson_t result)
2778 {
2779   gpg_error_t err;
2780   int c;
2781   size_t n;
2782   size_t chunksize;
2783
2784   if ((err = get_chunksize (request, &chunksize)))
2785     goto leave;
2786
2787   /* Adjust the chunksize if we need to do base64 conversion.  */
2788   if (pending_data.base64)
2789     chunksize = (chunksize / 4) * 3;
2790
2791   /* Do we have anything pending?  */
2792   if (!pending_data.buffer)
2793     {
2794       err = gpg_error (GPG_ERR_NO_DATA);
2795       gpg_error_object (result, err, "Operation not possible: %s",
2796                         gpg_strerror (err));
2797       goto leave;
2798     }
2799
2800   xjson_AddStringToObject (result, "type", pending_data.type);
2801   xjson_AddBoolToObject (result, "base64", pending_data.base64);
2802
2803   if (pending_data.written >= pending_data.length)
2804     {
2805       /* EOF reached.  This should not happen but we return an empty
2806        * string once in case of client errors.  */
2807       gpgme_free (pending_data.buffer);
2808       pending_data.buffer = NULL;
2809       xjson_AddBoolToObject (result, "more", 0);
2810       err = cjson_AddStringToObject (result, "data", "");
2811     }
2812   else
2813     {
2814       n = pending_data.length - pending_data.written;
2815       if (n > chunksize)
2816         {
2817           n = chunksize;
2818           xjson_AddBoolToObject (result, "more", 1);
2819         }
2820       else
2821         xjson_AddBoolToObject (result, "more", 0);
2822
2823       c = pending_data.buffer[pending_data.written + n];
2824       pending_data.buffer[pending_data.written + n] = 0;
2825       if (pending_data.base64)
2826         err = add_base64_to_object (result, "data",
2827                                     (pending_data.buffer
2828                                      + pending_data.written), n);
2829       else
2830         err = cjson_AddStringToObject (result, "data",
2831                                        (pending_data.buffer
2832                                         + pending_data.written));
2833       pending_data.buffer[pending_data.written + n] = c;
2834       if (!err)
2835         {
2836           pending_data.written += n;
2837           if (pending_data.written >= pending_data.length)
2838             {
2839               gpgme_free (pending_data.buffer);
2840               pending_data.buffer = NULL;
2841             }
2842         }
2843     }
2844
2845  leave:
2846   return err;
2847 }
2848
2849
2850 \f
2851 static const char hlp_help[] =
2852   "The tool expects a JSON object with the request and responds with\n"
2853   "another JSON object.  Even on error a JSON object is returned.  The\n"
2854   "property \"op\" is mandatory and its string value selects the\n"
2855   "operation; if the property \"help\" with the value \"true\" exists, the\n"
2856   "operation is not performned but a string with the documentation\n"
2857   "returned.  To list all operations it is allowed to leave out \"op\" in\n"
2858   "help mode.  Supported values for \"op\" are:\n\n"
2859   "  config      Read configuration values.\n"
2860   "  decrypt     Decrypt data.\n"
2861   "  delete      Delete a key.\n"
2862   "  encrypt     Encrypt data.\n"
2863   "  export      Export keys.\n"
2864   "  import      Import data.\n"
2865   "  keylist     List keys.\n"
2866   "  sign        Sign data.\n"
2867   "  verify      Verify data.\n"
2868   "  version     Get engine information.\n"
2869   "  getmore     Retrieve remaining data.\n"
2870   "  help        Help overview.";
2871 static gpg_error_t
2872 op_help (cjson_t request, cjson_t result)
2873 {
2874   cjson_t j_tmp;
2875   char *buffer = NULL;
2876   const char *msg;
2877
2878   j_tmp = cJSON_GetObjectItem (request, "interactive_help");
2879   if (opt_interactive && j_tmp && cjson_is_string (j_tmp))
2880     msg = buffer = xstrconcat (hlp_help, "\n", j_tmp->valuestring, NULL);
2881   else
2882     msg = hlp_help;
2883
2884   xjson_AddStringToObject (result, "type", "help");
2885   xjson_AddStringToObject (result, "msg", msg);
2886
2887   xfree (buffer);
2888   return 0;
2889 }
2890
2891
2892 \f
2893 /*
2894  * Dispatcher
2895  */
2896
2897 /* Process a request and return the response.  The response is a newly
2898  * allocated string or NULL in case of an error.  */
2899 static char *
2900 process_request (const char *request)
2901 {
2902   static struct {
2903     const char *op;
2904     gpg_error_t (*handler)(cjson_t request, cjson_t result);
2905     const char * const helpstr;
2906   } optbl[] = {
2907     { "config",     op_config,     hlp_config },
2908     { "config_opt", op_config_opt, hlp_config_opt },
2909     { "encrypt",    op_encrypt,    hlp_encrypt },
2910     { "export",     op_export,     hlp_export },
2911     { "decrypt",    op_decrypt,    hlp_decrypt },
2912     { "delete",     op_delete,     hlp_delete },
2913     { "keylist",    op_keylist,    hlp_keylist },
2914     { "import",     op_import,     hlp_import },
2915     { "sign",       op_sign,       hlp_sign },
2916     { "verify",     op_verify,     hlp_verify },
2917     { "version",    op_version,    hlp_version },
2918     { "getmore",    op_getmore,    hlp_getmore },
2919     { "help",       op_help,       hlp_help },
2920     { NULL }
2921   };
2922   size_t erroff;
2923   cjson_t json;
2924   cjson_t j_tmp, j_op;
2925   cjson_t response;
2926   int helpmode;
2927   const char *op;
2928   char *res;
2929   int idx;
2930
2931   response = xjson_CreateObject ();
2932
2933   json = cJSON_Parse (request, &erroff);
2934   if (!json)
2935     {
2936       log_string (GPGRT_LOGLVL_INFO, request);
2937       log_info ("invalid JSON object at offset %zu\n", erroff);
2938       error_object (response, "invalid JSON object at offset %zu\n", erroff);
2939       goto leave;
2940     }
2941
2942   j_tmp = cJSON_GetObjectItem (json, "help");
2943   helpmode = (j_tmp && cjson_is_true (j_tmp));
2944
2945   j_op = cJSON_GetObjectItem (json, "op");
2946   if (!j_op || !cjson_is_string (j_op))
2947     {
2948       if (!helpmode)
2949         {
2950           error_object (response, "Property \"op\" missing");
2951           goto leave;
2952         }
2953       op = "help";  /* Help summary.  */
2954     }
2955   else
2956     op = j_op->valuestring;
2957
2958   for (idx=0; optbl[idx].op; idx++)
2959     if (!strcmp (op, optbl[idx].op))
2960       break;
2961   if (optbl[idx].op)
2962     {
2963       if (helpmode && strcmp (op, "help"))
2964         {
2965           xjson_AddStringToObject (response, "type", "help");
2966           xjson_AddStringToObject (response, "op", op);
2967           xjson_AddStringToObject (response, "msg", optbl[idx].helpstr);
2968         }
2969       else
2970         {
2971           gpg_error_t err;
2972
2973           /* If this is not the "getmore" command and we have any
2974            * pending data release that data.  */
2975           if (pending_data.buffer && optbl[idx].handler != op_getmore)
2976             {
2977               gpgme_free (pending_data.buffer);
2978               pending_data.buffer = NULL;
2979             }
2980
2981           err = optbl[idx].handler (json, response);
2982           if (err)
2983             {
2984               if (!(j_tmp = cJSON_GetObjectItem (response, "type"))
2985                   || !cjson_is_string (j_tmp)
2986                   || strcmp (j_tmp->valuestring, "error"))
2987                 {
2988                   /* No error type response - provide a generic one.  */
2989                   gpg_error_object (response, err, "Operation failed: %s",
2990                                     gpg_strerror (err));
2991                 }
2992
2993               xjson_AddStringToObject (response, "op", op);
2994             }
2995         }
2996     }
2997   else  /* Operation not supported.  */
2998     {
2999       error_object (response, "Unknown operation '%s'", op);
3000       xjson_AddStringToObject (response, "op", op);
3001     }
3002
3003  leave:
3004   cJSON_Delete (json);
3005   if (opt_interactive)
3006     res = cJSON_Print (response);
3007   else
3008     res = cJSON_PrintUnformatted (response);
3009   if (!res)
3010     log_error ("Printing JSON data failed\n");
3011   cJSON_Delete (response);
3012   return res;
3013 }
3014
3015
3016 \f
3017 /*
3018  *  Driver code
3019  */
3020
3021 static char *
3022 get_file (const char *fname)
3023 {
3024   gpg_error_t err;
3025   estream_t fp;
3026   struct stat st;
3027   char *buf;
3028   size_t buflen;
3029
3030   fp = es_fopen (fname, "r");
3031   if (!fp)
3032     {
3033       err = gpg_error_from_syserror ();
3034       log_error ("can't open '%s': %s\n", fname, gpg_strerror (err));
3035       return NULL;
3036     }
3037
3038   if (fstat (es_fileno(fp), &st))
3039     {
3040       err = gpg_error_from_syserror ();
3041       log_error ("can't stat '%s': %s\n", fname, gpg_strerror (err));
3042       es_fclose (fp);
3043       return NULL;
3044     }
3045
3046   buflen = st.st_size;
3047   buf = xmalloc (buflen+1);
3048   if (es_fread (buf, buflen, 1, fp) != 1)
3049     {
3050       err = gpg_error_from_syserror ();
3051       log_error ("error reading '%s': %s\n", fname, gpg_strerror (err));
3052       es_fclose (fp);
3053       xfree (buf);
3054       return NULL;
3055     }
3056   buf[buflen] = 0;
3057   es_fclose (fp);
3058
3059   return buf;
3060 }
3061
3062
3063 /* Return a malloced line or NULL on EOF.  Terminate on read
3064  * error.  */
3065 static char *
3066 get_line (void)
3067 {
3068   char *line = NULL;
3069   size_t linesize = 0;
3070   gpg_error_t err;
3071   size_t maxlength = 2048;
3072   int n;
3073   const char *s;
3074   char *p;
3075
3076  again:
3077   n = es_read_line (es_stdin, &line, &linesize, &maxlength);
3078   if (n < 0)
3079     {
3080       err = gpg_error_from_syserror ();
3081       log_error ("error reading line: %s\n", gpg_strerror (err));
3082       exit (1);
3083     }
3084   if (!n)
3085     {
3086       xfree (line);
3087       line = NULL;
3088       return NULL;  /* EOF */
3089     }
3090   if (!maxlength)
3091     {
3092       log_info ("line too long - skipped\n");
3093       goto again;
3094     }
3095   if (memchr (line, 0, n))
3096     log_info ("warning: line shortened due to embedded Nul character\n");
3097
3098   if (line[n-1] == '\n')
3099     line[n-1] = 0;
3100
3101   /* Trim leading spaces.  */
3102   for (s=line; spacep (s); s++)
3103     ;
3104   if (s != line)
3105     {
3106       for (p=line; *s;)
3107         *p++ = *s++;
3108       *p = 0;
3109       n = p - line;
3110     }
3111
3112   return line;
3113 }
3114
3115
3116 /* Process meta commands used with the standard REPL.  */
3117 static char *
3118 process_meta_commands (const char *request)
3119 {
3120   char *result = NULL;
3121
3122   while (spacep (request))
3123     request++;
3124
3125   if (!strncmp (request, "help", 4) && (spacep (request+4) || !request[4]))
3126     {
3127       if (request[4])
3128         {
3129           char *buf = xstrconcat ("{ \"help\":true, \"op\":\"", request+5,
3130                                   "\" }", NULL);
3131           result = process_request (buf);
3132           xfree (buf);
3133         }
3134       else
3135         result = process_request ("{ \"op\": \"help\","
3136                                   " \"interactive_help\": "
3137                                   "\"\\nMeta commands:\\n"
3138                                   "  ,read FNAME Process data from FILE\\n"
3139                                   "  ,help CMD   Print help for a command\\n"
3140                                   "  ,quit       Terminate process\""
3141                                   "}");
3142     }
3143   else if (!strncmp (request, "quit", 4) && (spacep (request+4) || !request[4]))
3144     exit (0);
3145   else if (!strncmp (request, "read", 4) && (spacep (request+4) || !request[4]))
3146     {
3147       if (!request[4])
3148         log_info ("usage: ,read FILENAME\n");
3149       else
3150         {
3151           char *buffer = get_file (request + 5);
3152           if (buffer)
3153             {
3154               result = process_request (buffer);
3155               xfree (buffer);
3156             }
3157         }
3158     }
3159   else
3160     log_info ("invalid meta command\n");
3161
3162   return result;
3163 }
3164
3165
3166 /* If STRING has a help response, return the MSG property in a human
3167  * readable format.  */
3168 static char *
3169 get_help_msg (const char *string)
3170 {
3171   cjson_t json, j_type, j_msg;
3172   const char *msg;
3173   char *buffer = NULL;
3174   char *p;
3175
3176   json = cJSON_Parse (string, NULL);
3177   if (json)
3178     {
3179       j_type = cJSON_GetObjectItem (json, "type");
3180       if (j_type && cjson_is_string (j_type)
3181           && !strcmp (j_type->valuestring, "help"))
3182         {
3183           j_msg = cJSON_GetObjectItem (json, "msg");
3184           if (j_msg || cjson_is_string (j_msg))
3185             {
3186               msg = j_msg->valuestring;
3187               buffer = malloc (strlen (msg)+1);
3188               if (buffer)
3189                 {
3190                   for (p=buffer; *msg; msg++)
3191                     {
3192                       if (*msg == '\\' && msg[1] == '\n')
3193                         *p++ = '\n';
3194                       else
3195                         *p++ = *msg;
3196                     }
3197                   *p = 0;
3198                 }
3199             }
3200         }
3201       cJSON_Delete (json);
3202     }
3203   return buffer;
3204 }
3205
3206
3207 /* An interactive standard REPL.  */
3208 static void
3209 interactive_repl (void)
3210 {
3211   char *line = NULL;
3212   char *request = NULL;
3213   char *response = NULL;
3214   char *p;
3215   int first;
3216
3217   es_setvbuf (es_stdin, NULL, _IONBF, 0);
3218 #if GPGRT_VERSION_NUMBER >= 0x011d00 /* 1.29 */
3219   es_fprintf (es_stderr, "%s %s ready (enter \",help\" for help)\n",
3220               gpgrt_strusage (11), gpgrt_strusage (13));
3221 #endif
3222   do
3223     {
3224       es_fputs ("> ", es_stderr);
3225       es_fflush (es_stderr);
3226       es_fflush (es_stdout);
3227       xfree (line);
3228       line = get_line ();
3229       es_fflush (es_stderr);
3230       es_fflush (es_stdout);
3231
3232       first = !request;
3233       if (line && *line)
3234         {
3235           if (!request)
3236             request = xstrdup (line);
3237           else
3238             request = xstrconcat (request, "\n", line, NULL);
3239         }
3240
3241       if (!line)
3242         es_fputs ("\n", es_stderr);
3243
3244       if (!line || !*line || (first && *request == ','))
3245         {
3246           /* Process the input.  */
3247           xfree (response);
3248           response = NULL;
3249           if (request && *request == ',')
3250             {
3251               response = process_meta_commands (request+1);
3252             }
3253           else if (request)
3254             {
3255               response = process_request (request);
3256             }
3257           xfree (request);
3258           request = NULL;
3259
3260           if (response)
3261             {
3262               if (opt_interactive)
3263                 {
3264                   char *msg = get_help_msg (response);
3265                   if (msg)
3266                     {
3267                       xfree (response);
3268                       response = msg;
3269                     }
3270                 }
3271
3272               es_fputs ("===> ", es_stderr);
3273               es_fflush (es_stderr);
3274               for (p=response; *p; p++)
3275                 {
3276                   if (*p == '\n')
3277                     {
3278                       es_fflush (es_stdout);
3279                       es_fputs ("\n===> ", es_stderr);
3280                       es_fflush (es_stderr);
3281                     }
3282                   else
3283                     es_putc (*p, es_stdout);
3284                 }
3285               es_fflush (es_stdout);
3286               es_fputs ("\n", es_stderr);
3287             }
3288         }
3289     }
3290   while (line);
3291
3292   xfree (request);
3293   xfree (response);
3294   xfree (line);
3295 }
3296
3297
3298 /* Read and process a single request.  */
3299 static void
3300 read_and_process_single_request (void)
3301 {
3302   char *line = NULL;
3303   char *request = NULL;
3304   char *response = NULL;
3305   size_t n;
3306
3307   for (;;)
3308     {
3309       xfree (line);
3310       line = get_line ();
3311       if (line && *line)
3312         request = (request? xstrconcat (request, "\n", line, NULL)
3313                    /**/   : xstrdup (line));
3314       if (!line)
3315         {
3316           if (request)
3317             {
3318               xfree (response);
3319               response = process_request (request);
3320               if (response)
3321                 {
3322                   es_fputs (response, es_stdout);
3323                   if ((n = strlen (response)) && response[n-1] != '\n')
3324                     es_fputc ('\n', es_stdout);
3325                 }
3326               es_fflush (es_stdout);
3327             }
3328           break;
3329         }
3330     }
3331
3332   xfree (response);
3333   xfree (request);
3334   xfree (line);
3335 }
3336
3337
3338 /* The Native Messaging processing loop.  */
3339 static void
3340 native_messaging_repl (void)
3341 {
3342   gpg_error_t err;
3343   uint32_t nrequest, nresponse;
3344   char *request = NULL;
3345   char *response = NULL;
3346   size_t n;
3347
3348   /* Due to the length octets we need to switch the I/O stream into
3349    * binary mode.  */
3350   es_set_binary (es_stdin);
3351   es_set_binary (es_stdout);
3352   es_setbuf (es_stdin, NULL);  /* stdin needs to be unbuffered! */
3353
3354   for (;;)
3355     {
3356       /* Read length.  Note that the protocol uses native endianess.
3357        * Is it allowed to call such a thing a well thought out
3358        * protocol?  */
3359       if (es_read (es_stdin, &nrequest, sizeof nrequest, &n))
3360         {
3361           err = gpg_error_from_syserror ();
3362           log_error ("error reading request header: %s\n", gpg_strerror (err));
3363           break;
3364         }
3365       if (!n)
3366         break;  /* EOF */
3367       if (n != sizeof nrequest)
3368         {
3369           log_error ("error reading request header: short read\n");
3370           break;
3371         }
3372       if (nrequest > MAX_REQUEST_SIZE)
3373         {
3374           log_error ("error reading request: request too long (%zu MiB)\n",
3375                      (size_t)nrequest / (1024*1024));
3376           /* Fixme: Shall we read the request to the bit bucket and
3377            * return an error reponse or just return an error reponse
3378            * and terminate?  Needs some testing.  */
3379           break;
3380         }
3381
3382       /* Read request.  */
3383       request = xtrymalloc (nrequest);
3384       if (!request)
3385         {
3386           err = gpg_error_from_syserror ();
3387           log_error ("error reading request: Not enough memory for %zu MiB)\n",
3388                      (size_t)nrequest / (1024*1024));
3389           /* FIXME: See comment above.  */
3390           break;
3391         }
3392       if (es_read (es_stdin, request, nrequest, &n))
3393         {
3394           err = gpg_error_from_syserror ();
3395           log_error ("error reading request: %s\n", gpg_strerror (err));
3396           break;
3397         }
3398       if (n != nrequest)
3399         {
3400           /* That is a protocol violation.  */
3401           xfree (response);
3402           response = error_object_string ("Invalid request:"
3403                                           " short read (%zu of %zu bytes)\n",
3404                                           n, (size_t)nrequest);
3405         }
3406       else /* Process request  */
3407         {
3408           if (opt_debug)
3409             log_debug ("request='%s'\n", request);
3410           xfree (response);
3411           response = process_request (request);
3412           if (opt_debug)
3413             log_debug ("response='%s'\n", response);
3414         }
3415       nresponse = strlen (response);
3416
3417       /* Write response */
3418       if (es_write (es_stdout, &nresponse, sizeof nresponse, &n))
3419         {
3420           err = gpg_error_from_syserror ();
3421           log_error ("error writing request header: %s\n", gpg_strerror (err));
3422           break;
3423         }
3424       if (n != sizeof nrequest)
3425         {
3426           log_error ("error writing request header: short write\n");
3427           break;
3428         }
3429       if (es_write (es_stdout, response, nresponse, &n))
3430         {
3431           err = gpg_error_from_syserror ();
3432           log_error ("error writing request: %s\n", gpg_strerror (err));
3433           break;
3434         }
3435       if (n != nresponse)
3436         {
3437           log_error ("error writing request: short write\n");
3438           break;
3439         }
3440       if (es_fflush (es_stdout) || es_ferror (es_stdout))
3441         {
3442           err = gpg_error_from_syserror ();
3443           log_error ("error writing request: %s\n", gpg_strerror (err));
3444           break;
3445         }
3446     }
3447
3448   xfree (response);
3449   xfree (request);
3450 }
3451
3452
3453 \f
3454 static const char *
3455 my_strusage( int level )
3456 {
3457   const char *p;
3458
3459   switch (level)
3460     {
3461     case  9: p = "LGPL-2.1-or-later"; break;
3462     case 11: p = "gpgme-json"; break;
3463     case 13: p = PACKAGE_VERSION; break;
3464     case 14: p = "Copyright (C) 2018 g10 Code GmbH"; break;
3465     case 19: p = "Please report bugs to <" PACKAGE_BUGREPORT ">.\n"; break;
3466     case 1:
3467     case 40:
3468       p = "Usage: gpgme-json [OPTIONS]";
3469       break;
3470     case 41:
3471       p = "Native messaging based GPGME operations.\n";
3472       break;
3473     case 42:
3474       p = "1"; /* Flag print 40 as part of 41. */
3475       break;
3476     default: p = NULL; break;
3477     }
3478   return p;
3479 }
3480
3481 int
3482 main (int argc, char *argv[])
3483 {
3484 #if GPGRT_VERSION_NUMBER < 0x011d00 /* 1.29 */
3485
3486   fprintf (stderr, "WARNING: Old libgpg-error - using limited mode\n");
3487   native_messaging_repl ();
3488
3489 #else /* This is a modern libgp-error.  */
3490
3491   enum { CMD_DEFAULT     = 0,
3492          CMD_INTERACTIVE = 'i',
3493          CMD_SINGLE      = 's',
3494          CMD_LIBVERSION  = 501,
3495   } cmd = CMD_DEFAULT;
3496   enum {
3497     OPT_DEBUG = 600
3498   };
3499
3500   static gpgrt_opt_t opts[] = {
3501     ARGPARSE_c  (CMD_INTERACTIVE, "interactive", "Interactive REPL"),
3502     ARGPARSE_c  (CMD_SINGLE,      "single",      "Single request mode"),
3503     ARGPARSE_c  (CMD_LIBVERSION,  "lib-version", "Show library version"),
3504     ARGPARSE_s_n(OPT_DEBUG,       "debug",       "Flyswatter"),
3505
3506     ARGPARSE_end()
3507   };
3508   gpgrt_argparse_t pargs = { &argc, &argv};
3509
3510   gpgrt_set_strusage (my_strusage);
3511
3512 #ifdef HAVE_SETLOCALE
3513   setlocale (LC_ALL, "");
3514 #endif
3515   gpgme_check_version (NULL);
3516 #ifdef LC_CTYPE
3517   gpgme_set_locale (NULL, LC_CTYPE, setlocale (LC_CTYPE, NULL));
3518 #endif
3519 #ifdef LC_MESSAGES
3520   gpgme_set_locale (NULL, LC_MESSAGES, setlocale (LC_MESSAGES, NULL));
3521 #endif
3522
3523   while (gpgrt_argparse (NULL, &pargs, opts))
3524     {
3525       switch (pargs.r_opt)
3526         {
3527         case CMD_INTERACTIVE:
3528           opt_interactive = 1;
3529           /* Fall trough.  */
3530         case CMD_SINGLE:
3531         case CMD_LIBVERSION:
3532           cmd = pargs.r_opt;
3533           break;
3534
3535         case OPT_DEBUG: opt_debug = 1; break;
3536
3537         default:
3538           pargs.err = ARGPARSE_PRINT_WARNING;
3539           break;
3540         }
3541     }
3542   gpgrt_argparse (NULL, &pargs, NULL);
3543
3544   if (!opt_debug)
3545     {
3546       const char *s = getenv ("GPGME_JSON_DEBUG");
3547       if (s && atoi (s) > 0)
3548         opt_debug = 1;
3549     }
3550
3551   if (opt_debug)
3552     {
3553       const char *home = getenv ("HOME");
3554       char *file = xstrconcat ("socket://",
3555                                home? home:"/tmp",
3556                                "/.gnupg/S.gpgme-json.log", NULL);
3557       log_set_file (file);
3558       xfree (file);
3559     }
3560
3561   if (opt_debug)
3562     { int i;
3563       for (i=0; argv[i]; i++)
3564         log_debug ("argv[%d]='%s'\n", i, argv[i]);
3565     }
3566
3567   switch (cmd)
3568     {
3569     case CMD_DEFAULT:
3570       native_messaging_repl ();
3571       break;
3572
3573     case CMD_SINGLE:
3574       read_and_process_single_request ();
3575       break;
3576
3577     case CMD_INTERACTIVE:
3578       interactive_repl ();
3579       break;
3580
3581     case CMD_LIBVERSION:
3582       printf ("Version from header: %s (0x%06x)\n",
3583               GPGME_VERSION, GPGME_VERSION_NUMBER);
3584       printf ("Version from binary: %s\n", gpgme_check_version (NULL));
3585       printf ("Copyright blurb ...:%s\n", gpgme_check_version ("\x01\x01"));
3586       break;
3587     }
3588
3589   if (opt_debug)
3590     log_debug ("ready");
3591
3592 #endif /* This is a modern libgp-error.  */
3593   return 0;
3594 }
3595 #endif /* libgpg-error >= 1.28 */