json: Put signature info before data output
[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 \f
1353 static const char hlp_getmore[] =
1354   "op:     \"getmore\"\n"
1355   "\n"
1356   "Optional parameters:\n"
1357   "chunksize:  Max number of bytes in the \"data\" object.\n"
1358   "\n"
1359   "Response on success:\n"
1360   "type:       Type of the pending data\n"
1361   "data:       The next chunk of data\n"
1362   "base64:     Boolean indicating whether data is base64 encoded\n"
1363   "more:       Optional boolean requesting another \"getmore\".";
1364 static gpg_error_t
1365 op_getmore (cjson_t request, cjson_t result)
1366 {
1367   gpg_error_t err;
1368   int c;
1369   size_t n;
1370   size_t chunksize;
1371
1372   if ((err = get_chunksize (request, &chunksize)))
1373     goto leave;
1374
1375   /* Adjust the chunksize if we need to do base64 conversion.  */
1376   if (pending_data.base64)
1377     chunksize = (chunksize / 4) * 3;
1378
1379   /* Do we have anything pending?  */
1380   if (!pending_data.buffer)
1381     {
1382       err = gpg_error (GPG_ERR_NO_DATA);
1383       error_object (result, "Operation not possible: %s", gpg_strerror (err));
1384       goto leave;
1385     }
1386
1387   xjson_AddStringToObject (result, "type", pending_data.type);
1388   xjson_AddBoolToObject (result, "base64", pending_data.base64);
1389
1390   if (pending_data.written >= pending_data.length)
1391     {
1392       /* EOF reached.  This should not happen but we return an empty
1393        * string once in case of client errors.  */
1394       gpgme_free (pending_data.buffer);
1395       pending_data.buffer = NULL;
1396       xjson_AddBoolToObject (result, "more", 0);
1397       err = cjson_AddStringToObject (result, "data", "");
1398     }
1399   else
1400     {
1401       n = pending_data.length - pending_data.written;
1402       if (n > chunksize)
1403         {
1404           n = chunksize;
1405           xjson_AddBoolToObject (result, "more", 1);
1406         }
1407       else
1408         xjson_AddBoolToObject (result, "more", 0);
1409
1410       c = pending_data.buffer[pending_data.written + n];
1411       pending_data.buffer[pending_data.written + n] = 0;
1412       if (pending_data.base64)
1413         err = add_base64_to_object (result, "data",
1414                                     (pending_data.buffer
1415                                      + pending_data.written), n);
1416       else
1417         err = cjson_AddStringToObject (result, "data",
1418                                        (pending_data.buffer
1419                                         + pending_data.written));
1420       pending_data.buffer[pending_data.written + n] = c;
1421       if (!err)
1422         {
1423           pending_data.written += n;
1424           if (pending_data.written >= pending_data.length)
1425             {
1426               gpgme_free (pending_data.buffer);
1427               pending_data.buffer = NULL;
1428             }
1429         }
1430     }
1431
1432  leave:
1433   return err;
1434 }
1435
1436
1437 \f
1438 static const char hlp_help[] =
1439   "The tool expects a JSON object with the request and responds with\n"
1440   "another JSON object.  Even on error a JSON object is returned.  The\n"
1441   "property \"op\" is mandatory and its string value selects the\n"
1442   "operation; if the property \"help\" with the value \"true\" exists, the\n"
1443   "operation is not performned but a string with the documentation\n"
1444   "returned.  To list all operations it is allowed to leave out \"op\" in\n"
1445   "help mode.  Supported values for \"op\" are:\n\n"
1446   "  encrypt     Encrypt data.\n"
1447   "  decrypt     Decrypt data.\n"
1448   "  sign        Sign data.\n"
1449   "  getmore     Retrieve remaining data.\n"
1450   "  help        Help overview.";
1451 static gpg_error_t
1452 op_help (cjson_t request, cjson_t result)
1453 {
1454   cjson_t j_tmp;
1455   char *buffer = NULL;
1456   const char *msg;
1457
1458   j_tmp = cJSON_GetObjectItem (request, "interactive_help");
1459   if (opt_interactive && j_tmp && cjson_is_string (j_tmp))
1460     msg = buffer = xstrconcat (hlp_help, "\n", j_tmp->valuestring, NULL);
1461   else
1462     msg = hlp_help;
1463
1464   xjson_AddStringToObject (result, "type", "help");
1465   xjson_AddStringToObject (result, "msg", msg);
1466
1467   xfree (buffer);
1468   return 0;
1469 }
1470
1471
1472 \f
1473 /*
1474  * Dispatcher
1475  */
1476
1477 /* Process a request and return the response.  The response is a newly
1478  * allocated string or NULL in case of an error.  */
1479 static char *
1480 process_request (const char *request)
1481 {
1482   static struct {
1483     const char *op;
1484     gpg_error_t (*handler)(cjson_t request, cjson_t result);
1485     const char * const helpstr;
1486   } optbl[] = {
1487     { "encrypt", op_encrypt, hlp_encrypt },
1488     { "decrypt", op_decrypt, hlp_decrypt },
1489     { "sign",    op_sign,    hlp_sign },
1490     { "getmore", op_getmore, hlp_getmore },
1491     { "help",    op_help,    hlp_help },
1492     { NULL }
1493   };
1494   size_t erroff;
1495   cjson_t json;
1496   cjson_t j_tmp, j_op;
1497   cjson_t response;
1498   int helpmode;
1499   const char *op;
1500   char *res;
1501   int idx;
1502
1503   response = xjson_CreateObject ();
1504
1505   json = cJSON_Parse (request, &erroff);
1506   if (!json)
1507     {
1508       log_string (GPGRT_LOGLVL_INFO, request);
1509       log_info ("invalid JSON object at offset %zu\n", erroff);
1510       error_object (response, "invalid JSON object at offset %zu\n", erroff);
1511       goto leave;
1512     }
1513
1514   j_tmp = cJSON_GetObjectItem (json, "help");
1515   helpmode = (j_tmp && cjson_is_true (j_tmp));
1516
1517   j_op = cJSON_GetObjectItem (json, "op");
1518   if (!j_op || !cjson_is_string (j_op))
1519     {
1520       if (!helpmode)
1521         {
1522           error_object (response, "Property \"op\" missing");
1523           goto leave;
1524         }
1525       op = "help";  /* Help summary.  */
1526     }
1527   else
1528     op = j_op->valuestring;
1529
1530   for (idx=0; optbl[idx].op; idx++)
1531     if (!strcmp (op, optbl[idx].op))
1532       break;
1533   if (optbl[idx].op)
1534     {
1535       if (helpmode && strcmp (op, "help"))
1536         {
1537           xjson_AddStringToObject (response, "type", "help");
1538           xjson_AddStringToObject (response, "op", op);
1539           xjson_AddStringToObject (response, "msg", optbl[idx].helpstr);
1540         }
1541       else
1542         {
1543           gpg_error_t err;
1544
1545           /* If this is not the "getmore" command and we have any
1546            * pending data release that data.  */
1547           if (pending_data.buffer && optbl[idx].handler != op_getmore)
1548             {
1549               gpgme_free (pending_data.buffer);
1550               pending_data.buffer = NULL;
1551             }
1552
1553           err = optbl[idx].handler (json, response);
1554           if (err)
1555             {
1556               if (!(j_tmp = cJSON_GetObjectItem (response, "type"))
1557                   || !cjson_is_string (j_tmp)
1558                   || strcmp (j_tmp->valuestring, "error"))
1559                 {
1560                   /* No error type response - provide a generic one.  */
1561                   error_object (response, "Operation failed: %s",
1562                                 gpg_strerror (err));
1563                 }
1564
1565               xjson_AddStringToObject (response, "op", op);
1566             }
1567         }
1568     }
1569   else  /* Operation not supported.  */
1570     {
1571       error_object (response, "Unknown operation '%s'", op);
1572       xjson_AddStringToObject (response, "op", op);
1573     }
1574
1575  leave:
1576   cJSON_Delete (json);
1577   if (opt_interactive)
1578     res = cJSON_Print (response);
1579   else
1580     res = cJSON_PrintUnformatted (response);
1581   if (!res)
1582     log_error ("Printing JSON data failed\n");
1583   cJSON_Delete (response);
1584   return res;
1585 }
1586
1587
1588 \f
1589 /*
1590  *  Driver code
1591  */
1592
1593 static char *
1594 get_file (const char *fname)
1595 {
1596   gpg_error_t err;
1597   estream_t fp;
1598   struct stat st;
1599   char *buf;
1600   size_t buflen;
1601
1602   fp = es_fopen (fname, "r");
1603   if (!fp)
1604     {
1605       err = gpg_error_from_syserror ();
1606       log_error ("can't open '%s': %s\n", fname, gpg_strerror (err));
1607       return NULL;
1608     }
1609
1610   if (fstat (es_fileno(fp), &st))
1611     {
1612       err = gpg_error_from_syserror ();
1613       log_error ("can't stat '%s': %s\n", fname, gpg_strerror (err));
1614       es_fclose (fp);
1615       return NULL;
1616     }
1617
1618   buflen = st.st_size;
1619   buf = xmalloc (buflen+1);
1620   if (es_fread (buf, buflen, 1, fp) != 1)
1621     {
1622       err = gpg_error_from_syserror ();
1623       log_error ("error reading '%s': %s\n", fname, gpg_strerror (err));
1624       es_fclose (fp);
1625       xfree (buf);
1626       return NULL;
1627     }
1628   buf[buflen] = 0;
1629   es_fclose (fp);
1630
1631   return buf;
1632 }
1633
1634
1635 /* Return a malloced line or NULL on EOF.  Terminate on read
1636  * error.  */
1637 static char *
1638 get_line (void)
1639 {
1640   char *line = NULL;
1641   size_t linesize = 0;
1642   gpg_error_t err;
1643   size_t maxlength = 2048;
1644   int n;
1645   const char *s;
1646   char *p;
1647
1648  again:
1649   n = es_read_line (es_stdin, &line, &linesize, &maxlength);
1650   if (n < 0)
1651     {
1652       err = gpg_error_from_syserror ();
1653       log_error ("error reading line: %s\n", gpg_strerror (err));
1654       exit (1);
1655     }
1656   if (!n)
1657     {
1658       xfree (line);
1659       line = NULL;
1660       return NULL;  /* EOF */
1661     }
1662   if (!maxlength)
1663     {
1664       log_info ("line too long - skipped\n");
1665       goto again;
1666     }
1667   if (memchr (line, 0, n))
1668     log_info ("warning: line shortened due to embedded Nul character\n");
1669
1670   if (line[n-1] == '\n')
1671     line[n-1] = 0;
1672
1673   /* Trim leading spaces.  */
1674   for (s=line; spacep (s); s++)
1675     ;
1676   if (s != line)
1677     {
1678       for (p=line; *s;)
1679         *p++ = *s++;
1680       *p = 0;
1681       n = p - line;
1682     }
1683
1684   return line;
1685 }
1686
1687
1688 /* Process meta commands used with the standard REPL.  */
1689 static char *
1690 process_meta_commands (const char *request)
1691 {
1692   char *result = NULL;
1693
1694   while (spacep (request))
1695     request++;
1696
1697   if (!strncmp (request, "help", 4) && (spacep (request+4) || !request[4]))
1698     {
1699       if (request[4])
1700         {
1701           char *buf = xstrconcat ("{ \"help\":true, \"op\":\"", request+5,
1702                                   "\" }", NULL);
1703           result = process_request (buf);
1704           xfree (buf);
1705         }
1706       else
1707         result = process_request ("{ \"op\": \"help\","
1708                                   " \"interactive_help\": "
1709                                   "\"\\nMeta commands:\\n"
1710                                   "  ,read FNAME Process data from FILE\\n"
1711                                   "  ,help CMD   Print help for a command\\n"
1712                                   "  ,quit       Terminate process\""
1713                                   "}");
1714     }
1715   else if (!strncmp (request, "quit", 4) && (spacep (request+4) || !request[4]))
1716     exit (0);
1717   else if (!strncmp (request, "read", 4) && (spacep (request+4) || !request[4]))
1718     {
1719       if (!request[4])
1720         log_info ("usage: ,read FILENAME\n");
1721       else
1722         {
1723           char *buffer = get_file (request + 5);
1724           if (buffer)
1725             {
1726               result = process_request (buffer);
1727               xfree (buffer);
1728             }
1729         }
1730     }
1731   else
1732     log_info ("invalid meta command\n");
1733
1734   return result;
1735 }
1736
1737
1738 /* If STRING has a help response, return the MSG property in a human
1739  * readable format.  */
1740 static char *
1741 get_help_msg (const char *string)
1742 {
1743   cjson_t json, j_type, j_msg;
1744   const char *msg;
1745   char *buffer = NULL;
1746   char *p;
1747
1748   json = cJSON_Parse (string, NULL);
1749   if (json)
1750     {
1751       j_type = cJSON_GetObjectItem (json, "type");
1752       if (j_type && cjson_is_string (j_type)
1753           && !strcmp (j_type->valuestring, "help"))
1754         {
1755           j_msg = cJSON_GetObjectItem (json, "msg");
1756           if (j_msg || cjson_is_string (j_msg))
1757             {
1758               msg = j_msg->valuestring;
1759               buffer = malloc (strlen (msg)+1);
1760               if (buffer)
1761                 {
1762                   for (p=buffer; *msg; msg++)
1763                     {
1764                       if (*msg == '\\' && msg[1] == '\n')
1765                         *p++ = '\n';
1766                       else
1767                         *p++ = *msg;
1768                     }
1769                   *p = 0;
1770                 }
1771             }
1772         }
1773       cJSON_Delete (json);
1774     }
1775   return buffer;
1776 }
1777
1778
1779 /* An interactive standard REPL.  */
1780 static void
1781 interactive_repl (void)
1782 {
1783   char *line = NULL;
1784   char *request = NULL;
1785   char *response = NULL;
1786   char *p;
1787   int first;
1788
1789   es_setvbuf (es_stdin, NULL, _IONBF, 0);
1790 #if GPGRT_VERSION_NUMBER >= 0x011d00 /* 1.29 */
1791   es_fprintf (es_stderr, "%s %s ready (enter \",help\" for help)\n",
1792               gpgrt_strusage (11), gpgrt_strusage (13));
1793 #endif
1794   do
1795     {
1796       es_fputs ("> ", es_stderr);
1797       es_fflush (es_stderr);
1798       es_fflush (es_stdout);
1799       xfree (line);
1800       line = get_line ();
1801       es_fflush (es_stderr);
1802       es_fflush (es_stdout);
1803
1804       first = !request;
1805       if (line && *line)
1806         {
1807           if (!request)
1808             request = xstrdup (line);
1809           else
1810             request = xstrconcat (request, "\n", line, NULL);
1811         }
1812
1813       if (!line)
1814         es_fputs ("\n", es_stderr);
1815
1816       if (!line || !*line || (first && *request == ','))
1817         {
1818           /* Process the input.  */
1819           xfree (response);
1820           response = NULL;
1821           if (request && *request == ',')
1822             {
1823               response = process_meta_commands (request+1);
1824             }
1825           else if (request)
1826             {
1827               response = process_request (request);
1828             }
1829           xfree (request);
1830           request = NULL;
1831
1832           if (response)
1833             {
1834               if (opt_interactive)
1835                 {
1836                   char *msg = get_help_msg (response);
1837                   if (msg)
1838                     {
1839                       xfree (response);
1840                       response = msg;
1841                     }
1842                 }
1843
1844               es_fputs ("===> ", es_stderr);
1845               es_fflush (es_stderr);
1846               for (p=response; *p; p++)
1847                 {
1848                   if (*p == '\n')
1849                     {
1850                       es_fflush (es_stdout);
1851                       es_fputs ("\n===> ", es_stderr);
1852                       es_fflush (es_stderr);
1853                     }
1854                   else
1855                     es_putc (*p, es_stdout);
1856                 }
1857               es_fflush (es_stdout);
1858               es_fputs ("\n", es_stderr);
1859             }
1860         }
1861     }
1862   while (line);
1863
1864   xfree (request);
1865   xfree (response);
1866   xfree (line);
1867 }
1868
1869
1870 /* Read and process a single request.  */
1871 static void
1872 read_and_process_single_request (void)
1873 {
1874   char *line = NULL;
1875   char *request = NULL;
1876   char *response = NULL;
1877   size_t n;
1878
1879   for (;;)
1880     {
1881       xfree (line);
1882       line = get_line ();
1883       if (line && *line)
1884         request = (request? xstrconcat (request, "\n", line, NULL)
1885                    /**/   : xstrdup (line));
1886       if (!line)
1887         {
1888           if (request)
1889             {
1890               xfree (response);
1891               response = process_request (request);
1892               if (response)
1893                 {
1894                   es_fputs (response, es_stdout);
1895                   if ((n = strlen (response)) && response[n-1] != '\n')
1896                     es_fputc ('\n', es_stdout);
1897                 }
1898               es_fflush (es_stdout);
1899             }
1900           break;
1901         }
1902     }
1903
1904   xfree (response);
1905   xfree (request);
1906   xfree (line);
1907 }
1908
1909
1910 /* The Native Messaging processing loop.  */
1911 static void
1912 native_messaging_repl (void)
1913 {
1914   gpg_error_t err;
1915   uint32_t nrequest, nresponse;
1916   char *request = NULL;
1917   char *response = NULL;
1918   size_t n;
1919
1920   /* Due to the length octets we need to switch the I/O stream into
1921    * binary mode.  */
1922   es_set_binary (es_stdin);
1923   es_set_binary (es_stdout);
1924   es_setbuf (es_stdin, NULL);  /* stdin needs to be unbuffered! */
1925
1926   for (;;)
1927     {
1928       /* Read length.  Note that the protocol uses native endianess.
1929        * Is it allowed to call such a thing a well thought out
1930        * protocol?  */
1931       if (es_read (es_stdin, &nrequest, sizeof nrequest, &n))
1932         {
1933           err = gpg_error_from_syserror ();
1934           log_error ("error reading request header: %s\n", gpg_strerror (err));
1935           break;
1936         }
1937       if (!n)
1938         break;  /* EOF */
1939       if (n != sizeof nrequest)
1940         {
1941           log_error ("error reading request header: short read\n");
1942           break;
1943         }
1944       if (nrequest > MAX_REQUEST_SIZE)
1945         {
1946           log_error ("error reading request: request too long (%zu MiB)\n",
1947                      (size_t)nrequest / (1024*1024));
1948           /* Fixme: Shall we read the request to the bit bucket and
1949            * return an error reponse or just return an error reponse
1950            * and terminate?  Needs some testing.  */
1951           break;
1952         }
1953
1954       /* Read request.  */
1955       request = xtrymalloc (nrequest);
1956       if (!request)
1957         {
1958           err = gpg_error_from_syserror ();
1959           log_error ("error reading request: Not enough memory for %zu MiB)\n",
1960                      (size_t)nrequest / (1024*1024));
1961           /* FIXME: See comment above.  */
1962           break;
1963         }
1964       if (es_read (es_stdin, request, nrequest, &n))
1965         {
1966           err = gpg_error_from_syserror ();
1967           log_error ("error reading request: %s\n", gpg_strerror (err));
1968           break;
1969         }
1970       if (n != nrequest)
1971         {
1972           /* That is a protocol violation.  */
1973           xfree (response);
1974           response = error_object_string ("Invalid request:"
1975                                           " short read (%zu of %zu bytes)\n",
1976                                           n, (size_t)nrequest);
1977         }
1978       else /* Process request  */
1979         {
1980           if (opt_debug)
1981             log_debug ("request='%s'\n", request);
1982           xfree (response);
1983           response = process_request (request);
1984           if (opt_debug)
1985             log_debug ("response='%s'\n", response);
1986         }
1987       nresponse = strlen (response);
1988
1989       /* Write response */
1990       if (es_write (es_stdout, &nresponse, sizeof nresponse, &n))
1991         {
1992           err = gpg_error_from_syserror ();
1993           log_error ("error writing request header: %s\n", gpg_strerror (err));
1994           break;
1995         }
1996       if (n != sizeof nrequest)
1997         {
1998           log_error ("error writing request header: short write\n");
1999           break;
2000         }
2001       if (es_write (es_stdout, response, nresponse, &n))
2002         {
2003           err = gpg_error_from_syserror ();
2004           log_error ("error writing request: %s\n", gpg_strerror (err));
2005           break;
2006         }
2007       if (n != nresponse)
2008         {
2009           log_error ("error writing request: short write\n");
2010           break;
2011         }
2012       if (es_fflush (es_stdout) || es_ferror (es_stdout))
2013         {
2014           err = gpg_error_from_syserror ();
2015           log_error ("error writing request: %s\n", gpg_strerror (err));
2016           break;
2017         }
2018     }
2019
2020   xfree (response);
2021   xfree (request);
2022 }
2023
2024
2025 \f
2026 static const char *
2027 my_strusage( int level )
2028 {
2029   const char *p;
2030
2031   switch (level)
2032     {
2033     case  9: p = "LGPL-2.1-or-later"; break;
2034     case 11: p = "gpgme-json"; break;
2035     case 13: p = PACKAGE_VERSION; break;
2036     case 14: p = "Copyright (C) 2018 g10 Code GmbH"; break;
2037     case 19: p = "Please report bugs to <" PACKAGE_BUGREPORT ">.\n"; break;
2038     case 1:
2039     case 40:
2040       p = "Usage: gpgme-json [OPTIONS]";
2041       break;
2042     case 41:
2043       p = "Native messaging based GPGME operations.\n";
2044       break;
2045     case 42:
2046       p = "1"; /* Flag print 40 as part of 41. */
2047       break;
2048     default: p = NULL; break;
2049     }
2050   return p;
2051 }
2052
2053 int
2054 main (int argc, char *argv[])
2055 {
2056 #if GPGRT_VERSION_NUMBER < 0x011d00 /* 1.29 */
2057
2058   fprintf (stderr, "WARNING: Old libgpg-error - using limited mode\n");
2059   native_messaging_repl ();
2060
2061 #else /* This is a modern libgp-error.  */
2062
2063   enum { CMD_DEFAULT     = 0,
2064          CMD_INTERACTIVE = 'i',
2065          CMD_SINGLE      = 's',
2066          CMD_LIBVERSION  = 501,
2067   } cmd = CMD_DEFAULT;
2068   enum {
2069     OPT_DEBUG = 600
2070   };
2071
2072   static gpgrt_opt_t opts[] = {
2073     ARGPARSE_c  (CMD_INTERACTIVE, "interactive", "Interactive REPL"),
2074     ARGPARSE_c  (CMD_SINGLE,      "single",      "Single request mode"),
2075     ARGPARSE_c  (CMD_LIBVERSION,  "lib-version", "Show library version"),
2076     ARGPARSE_s_n(OPT_DEBUG,       "debug",       "Flyswatter"),
2077
2078     ARGPARSE_end()
2079   };
2080   gpgrt_argparse_t pargs = { &argc, &argv};
2081
2082   gpgrt_set_strusage (my_strusage);
2083
2084 #ifdef HAVE_SETLOCALE
2085   setlocale (LC_ALL, "");
2086 #endif
2087   gpgme_check_version (NULL);
2088 #ifdef LC_CTYPE
2089   gpgme_set_locale (NULL, LC_CTYPE, setlocale (LC_CTYPE, NULL));
2090 #endif
2091 #ifdef LC_MESSAGES
2092   gpgme_set_locale (NULL, LC_MESSAGES, setlocale (LC_MESSAGES, NULL));
2093 #endif
2094
2095   while (gpgrt_argparse (NULL, &pargs, opts))
2096     {
2097       switch (pargs.r_opt)
2098         {
2099         case CMD_INTERACTIVE:
2100           opt_interactive = 1;
2101           /* Fall trough.  */
2102         case CMD_SINGLE:
2103         case CMD_LIBVERSION:
2104           cmd = pargs.r_opt;
2105           break;
2106
2107         case OPT_DEBUG: opt_debug = 1; break;
2108
2109         default:
2110           pargs.err = ARGPARSE_PRINT_WARNING;
2111           break;
2112         }
2113     }
2114   gpgrt_argparse (NULL, &pargs, NULL);
2115
2116   if (!opt_debug)
2117     {
2118       const char *s = getenv ("GPGME_JSON_DEBUG");
2119       if (s && atoi (s) > 0)
2120         opt_debug = 1;
2121     }
2122
2123   if (opt_debug)
2124     {
2125       const char *home = getenv ("HOME");
2126       char *file = xstrconcat ("socket://",
2127                                home? home:"/tmp",
2128                                "/.gnupg/S.gpgme-json.log", NULL);
2129       log_set_file (file);
2130       xfree (file);
2131     }
2132
2133   if (opt_debug)
2134     { int i;
2135       for (i=0; argv[i]; i++)
2136         log_debug ("argv[%d]='%s'\n", i, argv[i]);
2137     }
2138
2139   switch (cmd)
2140     {
2141     case CMD_DEFAULT:
2142       native_messaging_repl ();
2143       break;
2144
2145     case CMD_SINGLE:
2146       read_and_process_single_request ();
2147       break;
2148
2149     case CMD_INTERACTIVE:
2150       interactive_repl ();
2151       break;
2152
2153     case CMD_LIBVERSION:
2154       printf ("Version from header: %s (0x%06x)\n",
2155               GPGME_VERSION, GPGME_VERSION_NUMBER);
2156       printf ("Version from binary: %s\n", gpgme_check_version (NULL));
2157       printf ("Copyright blurb ...:%s\n", gpgme_check_version ("\x01\x01"));
2158       break;
2159     }
2160
2161   if (opt_debug)
2162     log_debug ("ready");
2163
2164 #endif /* This is a modern libgp-error.  */
2165   return 0;
2166 }
2167 #endif /* libgpg-error >= 1.28 */