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