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