Fix a couple of bugs pointed out by clang compiler warnings.
[gpgme.git] / src / engine-gpgconf.c
1 /* engine-gpgconf.c - gpg-conf engine.
2    Copyright (C) 2000 Werner Koch (dd9jn)
3    Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2008,
4                  2013 g10 Code GmbH
5
6    This file is part of GPGME.
7
8    GPGME is free software; you can redistribute it and/or modify it
9    under the terms of the GNU Lesser General Public License as
10    published by the Free Software Foundation; either version 2.1 of
11    the License, or (at your option) any later version.
12
13    GPGME is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16    Lesser General Public License for more details.
17
18    You should have received a copy of the GNU Lesser General Public
19    License along with this program; if not, see <https://www.gnu.org/licenses/>.
20  */
21
22 #if HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdlib.h>
27 #include <string.h>
28 #ifdef HAVE_SYS_TYPES_H
29 # include <sys/types.h>
30 #endif
31 #include <assert.h>
32 #ifdef HAVE_UNISTD_H
33 # include <unistd.h>
34 #endif
35 #include <fcntl.h> /* FIXME */
36 #include <errno.h>
37
38 #include "gpgme.h"
39 #include "util.h"
40 #include "ops.h"
41 #include "wait.h"
42 #include "priv-io.h"
43 #include "sema.h"
44
45 #include "assuan.h"
46 #include "debug.h"
47
48 #include "engine-backend.h"
49
50
51 \f
52 struct engine_gpgconf
53 {
54   char *file_name;
55   char *home_dir;
56   char *version;
57 };
58
59 typedef struct engine_gpgconf *engine_gpgconf_t;
60
61 \f
62 /* Return true if the engine's version is at least VERSION.  */
63 static int
64 have_gpgconf_version (engine_gpgconf_t gpgconf, const char *version)
65 {
66   return _gpgme_compare_versions (gpgconf->version, version);
67 }
68
69
70 static char *
71 gpgconf_get_version (const char *file_name)
72 {
73   return _gpgme_get_program_version (file_name ? file_name
74                                      : _gpgme_get_default_gpgconf_name ());
75 }
76
77
78 static const char *
79 gpgconf_get_req_version (void)
80 {
81   return "2.0.4";
82 }
83
84 \f
85 static void
86 gpgconf_release (void *engine)
87 {
88   engine_gpgconf_t gpgconf = engine;
89
90   if (!gpgconf)
91     return;
92
93   if (gpgconf->file_name)
94     free (gpgconf->file_name);
95   if (gpgconf->home_dir)
96     free (gpgconf->home_dir);
97   if (gpgconf->version)
98     free (gpgconf->version);
99
100   free (gpgconf);
101 }
102
103
104 static gpgme_error_t
105 gpgconf_new (void **engine, const char *file_name, const char *home_dir,
106              const char *version)
107 {
108   gpgme_error_t err = 0;
109   engine_gpgconf_t gpgconf;
110
111   gpgconf = calloc (1, sizeof *gpgconf);
112   if (!gpgconf)
113     return gpg_error_from_syserror ();
114
115   gpgconf->file_name = strdup (file_name ? file_name
116                                : _gpgme_get_default_gpgconf_name ());
117   if (!gpgconf->file_name)
118     err = gpg_error_from_syserror ();
119
120   if (!err && home_dir)
121     {
122       gpgconf->home_dir = strdup (home_dir);
123       if (!gpgconf->home_dir)
124         err = gpg_error_from_syserror ();
125     }
126
127   if (!err && version)
128     {
129       gpgconf->version = strdup (version);
130       if (!gpgconf->version)
131         err = gpg_error_from_syserror ();
132     }
133
134   if (err)
135     gpgconf_release (gpgconf);
136   else
137     *engine = gpgconf;
138
139   return err;
140 }
141
142 \f
143 static void
144 release_arg (gpgme_conf_arg_t arg, gpgme_conf_type_t alt_type)
145 {
146   while (arg)
147     {
148       gpgme_conf_arg_t next = arg->next;
149
150       if (alt_type == GPGME_CONF_STRING)
151         free (arg->value.string);
152       free (arg);
153       arg = next;
154     }
155 }
156
157
158 static void
159 release_opt (gpgme_conf_opt_t opt)
160 {
161   if (opt->name)
162     free (opt->name);
163   if (opt->description)
164     free (opt->description);
165   if (opt->argname)
166     free (opt->argname);
167
168   release_arg (opt->default_value, opt->alt_type);
169   if (opt->default_description)
170     free (opt->default_description);
171
172   release_arg (opt->no_arg_value, opt->alt_type);
173   release_arg (opt->value, opt->alt_type);
174   release_arg (opt->new_value, opt->alt_type);
175
176   free (opt);
177 }
178
179
180 static void
181 release_comp (gpgme_conf_comp_t comp)
182 {
183   gpgme_conf_opt_t opt;
184
185   if (comp->name)
186     free (comp->name);
187   if (comp->description)
188     free (comp->description);
189   if (comp->program_name)
190     free (comp->program_name);
191
192   opt = comp->options;
193   while (opt)
194     {
195       gpgme_conf_opt_t next = opt->next;
196       release_opt (opt);
197       opt = next;
198     }
199
200   free (comp);
201 }
202
203
204 static void
205 gpgconf_config_release (gpgme_conf_comp_t conf)
206 {
207   while (conf)
208     {
209       gpgme_conf_comp_t next = conf->next;
210       release_comp (conf);
211       conf = next;
212     }
213 }
214
215 /* Read from gpgconf and pass line after line to the hook function.
216    We put a limit of 64 k on the maximum size for a line.  This should
217    allow for quite a long "group" line, which is usually the longest
218    line (mine is currently ~3k).  */
219 static gpgme_error_t
220 gpgconf_read (void *engine, const char *arg1, char *arg2,
221               gpgme_error_t (*cb) (void *hook, char *line),
222               void *hook)
223 {
224   struct engine_gpgconf *gpgconf = engine;
225   gpgme_error_t err = 0;
226   char *linebuf;
227   size_t linebufsize;
228   int linelen;
229   char *argv[6];
230   int argc = 0;
231   int rp[2];
232   struct spawn_fd_item_s cfd[] = { {-1, 1 /* STDOUT_FILENO */, -1, 0},
233                                    {-1, -1} };
234   int status;
235   int nread;
236   char *mark = NULL;
237
238   /* _gpgme_engine_new guarantees that this is not NULL.  */
239   argv[argc++] = gpgconf->file_name;
240
241   if (gpgconf->home_dir && have_gpgconf_version (gpgconf, "2.1.13"))
242     {
243       argv[argc++] = (char*)"--homedir";
244       argv[argc++] = gpgconf->home_dir;
245     }
246
247   argv[argc++] = (char*)arg1;
248   argv[argc++] = arg2;
249   argv[argc] = NULL;
250   assert (argc < DIM (argv));
251
252   if (_gpgme_io_pipe (rp, 1) < 0)
253     return gpg_error_from_syserror ();
254
255   cfd[0].fd = rp[1];
256
257   status = _gpgme_io_spawn (gpgconf->file_name, argv,
258                             IOSPAWN_FLAG_DETACHED, cfd, NULL, NULL, NULL);
259   if (status < 0)
260     {
261       _gpgme_io_close (rp[0]);
262       _gpgme_io_close (rp[1]);
263       return gpg_error_from_syserror ();
264     }
265
266   linebufsize = 1024; /* Usually enough for conf lines.  */
267   linebuf = malloc (linebufsize);
268   if (!linebuf)
269     {
270       err = gpg_error_from_syserror ();
271       goto leave;
272     }
273   linelen = 0;
274
275   while ((nread = _gpgme_io_read (rp[0], linebuf + linelen,
276                                   linebufsize - linelen - 1)))
277     {
278       char *line;
279       const char *lastmark = NULL;
280       size_t nused;
281
282       if (nread < 0)
283         {
284           err = gpg_error_from_syserror ();
285           goto leave;
286         }
287
288       linelen += nread;
289       linebuf[linelen] = '\0';
290
291       for (line=linebuf; (mark = strchr (line, '\n')); line = mark+1 )
292         {
293           lastmark = mark;
294           if (mark > line && mark[-1] == '\r')
295             mark[-1] = '\0';
296           else
297             mark[0] = '\0';
298
299           /* Got a full line.  Due to the CR removal code (which
300              occurs only on Windows) we might be one-off and thus
301              would see empty lines.  Don't pass them to the
302              callback. */
303           err = *line? (*cb) (hook, line) : 0;
304           if (err)
305             goto leave;
306         }
307
308       nused = lastmark? (lastmark + 1 - linebuf) : 0;
309       memmove (linebuf, linebuf + nused, linelen - nused);
310       linelen -= nused;
311
312       if (!(linelen < linebufsize - 1))
313         {
314           char *newlinebuf;
315
316           if (linelen <  8 * 1024 - 1)
317             linebufsize = 8 * 1024;
318           else if (linelen < 64 * 1024 - 1)
319             linebufsize = 64 * 1024;
320           else
321             {
322               /* We reached our limit - give up.  */
323               err = gpg_error (GPG_ERR_LINE_TOO_LONG);
324               goto leave;
325             }
326
327           newlinebuf = realloc (linebuf, linebufsize);
328           if (!newlinebuf)
329             {
330               err = gpg_error_from_syserror ();
331               goto leave;
332             }
333           linebuf = newlinebuf;
334         }
335     }
336
337  leave:
338   free (linebuf);
339   _gpgme_io_close (rp[0]);
340   return err;
341 }
342
343
344 static gpgme_error_t
345 gpgconf_config_load_cb (void *hook, char *line)
346 {
347   gpgme_conf_comp_t *comp_p = hook;
348   gpgme_conf_comp_t comp = *comp_p;
349 #define NR_FIELDS 16
350   char *field[NR_FIELDS];
351   int fields = 0;
352
353   while (line && fields < NR_FIELDS)
354     {
355       field[fields++] = line;
356       line = strchr (line, ':');
357       if (line)
358         *(line++) = '\0';
359     }
360
361   /* We require at least the first 3 fields.  */
362   if (fields < 2)
363     return trace_gpg_error (GPG_ERR_INV_ENGINE);
364
365   /* Find the pointer to the new component in the list.  */
366   while (comp && comp->next)
367     comp = comp->next;
368   if (comp)
369     comp_p = &comp->next;
370
371   comp = calloc (1, sizeof (*comp));
372   if (!comp)
373     return gpg_error_from_syserror ();
374   /* Prepare return value.  */
375   comp->_last_opt_p = &comp->options;
376   *comp_p = comp;
377
378   comp->name = strdup (field[0]);
379   if (!comp->name)
380     return gpg_error_from_syserror ();
381
382   comp->description = strdup (field[1]);
383   if (!comp->description)
384     return gpg_error_from_syserror ();
385
386   if (fields >= 3)
387     {
388       comp->program_name = strdup (field[2]);
389       if (!comp->program_name)
390         return gpg_error_from_syserror ();
391     }
392
393   return 0;
394 }
395
396
397 static gpgme_error_t
398 gpgconf_parse_option (gpgme_conf_opt_t opt,
399                       gpgme_conf_arg_t *arg_p, char *line)
400 {
401   gpgme_error_t err;
402   char *mark = NULL;
403
404   if (!line[0])
405     return 0;
406
407   while (line)
408     {
409       gpgme_conf_arg_t arg;
410
411       if (opt->type != GPGME_CONF_STRING)
412         mark = strchr (line, ',');
413       if (mark)
414         *mark = '\0';
415
416       arg = calloc (1, sizeof (*arg));
417       if (!arg)
418         return gpg_error_from_syserror ();
419       *arg_p = arg;
420       arg_p = &arg->next;
421
422       if (*line == '\0')
423         arg->no_arg = 1;
424       else
425         {
426           switch (opt->alt_type)
427             {
428               /* arg->value.count is an alias for arg->value.uint32.  */
429             case GPGME_CONF_NONE:
430             case GPGME_CONF_UINT32:
431               arg->value.uint32 = strtoul (line, NULL, 0);
432               break;
433
434             case GPGME_CONF_INT32:
435               arg->value.uint32 = strtol (line, NULL, 0);
436               break;
437
438             case GPGME_CONF_STRING:
439               /* The complex types below are only here to silent the
440                  compiler warning. */
441             case GPGME_CONF_FILENAME:
442             case GPGME_CONF_LDAP_SERVER:
443             case GPGME_CONF_KEY_FPR:
444             case GPGME_CONF_PUB_KEY:
445             case GPGME_CONF_SEC_KEY:
446             case GPGME_CONF_ALIAS_LIST:
447               /* Skip quote character.  */
448               line++;
449
450               err = _gpgme_decode_percent_string (line, &arg->value.string,
451                                                   0, 0);
452               if (err)
453                 return err;
454               break;
455             }
456         }
457
458       /* Find beginning of next value.  */
459       if (mark++ && *mark)
460         line = mark;
461       else
462         line = NULL;
463     }
464
465   return 0;
466 }
467
468
469 static gpgme_error_t
470 gpgconf_config_load_cb2 (void *hook, char *line)
471 {
472   gpgme_error_t err;
473   gpgme_conf_comp_t comp = hook;
474   gpgme_conf_opt_t *opt_p = comp->_last_opt_p;
475   gpgme_conf_opt_t opt;
476 #define NR_FIELDS 16
477   char *field[NR_FIELDS];
478   int fields = 0;
479
480   while (line && fields < NR_FIELDS)
481     {
482       field[fields++] = line;
483       line = strchr (line, ':');
484       if (line)
485         *(line++) = '\0';
486     }
487
488   /* We require at least the first 10 fields.  */
489   if (fields < 10)
490     return trace_gpg_error (GPG_ERR_INV_ENGINE);
491
492   opt = calloc (1, sizeof (*opt));
493   if (!opt)
494     return gpg_error_from_syserror ();
495
496   comp->_last_opt_p = &opt->next;
497   *opt_p = opt;
498
499   if (field[0][0])
500     {
501       opt->name = strdup (field[0]);
502       if (!opt->name)
503         return gpg_error_from_syserror ();
504     }
505
506   opt->flags = strtoul (field[1], NULL, 0);
507
508   opt->level = strtoul (field[2], NULL, 0);
509
510   if (field[3][0])
511     {
512       opt->description = strdup (field[3]);
513       if (!opt->description)
514         return gpg_error_from_syserror ();
515     }
516
517   opt->type = strtoul (field[4], NULL, 0);
518
519   opt->alt_type = strtoul (field[5], NULL, 0);
520
521   if (field[6][0])
522     {
523       opt->argname = strdup (field[6]);
524       if (!opt->argname)
525         return gpg_error_from_syserror ();
526     }
527
528   if (opt->flags & GPGME_CONF_DEFAULT)
529     {
530       err = gpgconf_parse_option (opt, &opt->default_value, field[7]);
531       if (err)
532         return err;
533     }
534   else if ((opt->flags & GPGME_CONF_DEFAULT_DESC) && field[7][0])
535     {
536       opt->default_description = strdup (field[7]);
537       if (!opt->default_description)
538         return gpg_error_from_syserror ();
539     }
540
541   if (opt->flags & GPGME_CONF_NO_ARG_DESC)
542     {
543       opt->no_arg_description = strdup (field[8]);
544       if (!opt->no_arg_description)
545         return gpg_error_from_syserror ();
546     }
547   else
548     {
549       err = gpgconf_parse_option (opt, &opt->no_arg_value, field[8]);
550       if (err)
551         return err;
552     }
553
554   err = gpgconf_parse_option (opt, &opt->value, field[9]);
555   if (err)
556     return err;
557
558   return 0;
559 }
560
561
562 static gpgme_error_t
563 gpgconf_conf_load (void *engine, gpgme_conf_comp_t *comp_p)
564 {
565   gpgme_error_t err;
566   gpgme_conf_comp_t comp = NULL;
567   gpgme_conf_comp_t cur_comp;
568
569   *comp_p = NULL;
570
571   err = gpgconf_read (engine, "--list-components", NULL,
572                       gpgconf_config_load_cb, &comp);
573   if (err)
574     {
575       gpgconf_release (comp);
576       return err;
577     }
578
579   cur_comp = comp;
580   while (!err && cur_comp)
581     {
582       err = gpgconf_read (engine, "--list-options", cur_comp->name,
583                           gpgconf_config_load_cb2, cur_comp);
584       cur_comp = cur_comp->next;
585     }
586
587   if (err)
588     {
589       gpgconf_release (comp);
590       return err;
591     }
592
593   *comp_p = comp;
594   return 0;
595 }
596
597
598 \f
599 gpgme_error_t
600 _gpgme_conf_arg_new (gpgme_conf_arg_t *arg_p,
601                      gpgme_conf_type_t type, const void *value)
602 {
603   gpgme_conf_arg_t arg;
604
605   arg = calloc (1, sizeof (*arg));
606   if (!arg)
607     return gpg_error_from_syserror ();
608
609   if (!value)
610     arg->no_arg = 1;
611   else
612     {
613       /* We need to switch on type here because the alt-type is not
614          yet known.  */
615       switch (type)
616         {
617         case GPGME_CONF_NONE:
618         case GPGME_CONF_UINT32:
619           arg->value.uint32 = *((unsigned int *) value);
620           break;
621
622         case GPGME_CONF_INT32:
623           arg->value.int32 = *((int *) value);
624           break;
625
626         case GPGME_CONF_STRING:
627         case GPGME_CONF_FILENAME:
628         case GPGME_CONF_LDAP_SERVER:
629         case GPGME_CONF_KEY_FPR:
630         case GPGME_CONF_PUB_KEY:
631         case GPGME_CONF_SEC_KEY:
632         case GPGME_CONF_ALIAS_LIST:
633           arg->value.string = strdup (value);
634           if (!arg->value.string)
635             {
636               free (arg);
637               return gpg_error_from_syserror ();
638             }
639           break;
640
641         default:
642           free (arg);
643           return gpg_error (GPG_ERR_INV_VALUE);
644         }
645     }
646
647   *arg_p = arg;
648   return 0;
649 }
650
651
652 void
653 _gpgme_conf_arg_release (gpgme_conf_arg_t arg, gpgme_conf_type_t type)
654 {
655   /* Lacking the alt_type we need to switch on type here.  */
656   switch (type)
657     {
658     case GPGME_CONF_NONE:
659     case GPGME_CONF_UINT32:
660     case GPGME_CONF_INT32:
661     case GPGME_CONF_STRING:
662     default:
663       break;
664
665     case GPGME_CONF_FILENAME:
666     case GPGME_CONF_LDAP_SERVER:
667     case GPGME_CONF_KEY_FPR:
668     case GPGME_CONF_PUB_KEY:
669     case GPGME_CONF_SEC_KEY:
670     case GPGME_CONF_ALIAS_LIST:
671       type = GPGME_CONF_STRING;
672       break;
673     }
674
675   release_arg (arg, type);
676 }
677
678
679 gpgme_error_t
680 _gpgme_conf_opt_change (gpgme_conf_opt_t opt, int reset, gpgme_conf_arg_t arg)
681 {
682   if (reset)
683     {
684       if (opt->new_value)
685         release_arg (opt->new_value, opt->alt_type);
686       opt->new_value = NULL;
687       opt->change_value = 0;
688     }
689   else
690     {
691       /* Support self-assignment, for example for adding an item to an
692          existing list.  */
693       if (opt->new_value && arg != opt->new_value)
694         release_arg (opt->new_value, opt->alt_type);
695       opt->new_value = arg;
696       opt->change_value = 1;
697     }
698   return 0;
699 }
700
701 \f
702 /* FIXME: Major problem: We don't get errors from gpgconf.  */
703
704 static gpgme_error_t
705 gpgconf_write (void *engine, const char *arg1, char *arg2, gpgme_data_t conf)
706 {
707   struct engine_gpgconf *gpgconf = engine;
708   gpgme_error_t err = 0;
709 #define BUFLEN 1024
710   char buf[BUFLEN];
711   int buflen = 0;
712   char *argv[7];
713   int argc = 0;
714   int rp[2] = { -1, -1 };
715   int errp[2] = { -1, -1 };
716   struct spawn_fd_item_s cfd[] = { {-1, 0 /* STDIN_FILENO */},
717                                    {-1, 2 /* STDERR_FILENO */, -1},
718                                    {-1, -1} };
719   int status;
720   int nwrite;
721
722   /* _gpgme_engine_new guarantees that this is not NULL.  */
723   argv[argc++] = gpgconf->file_name;
724
725   if (gpgconf->home_dir && have_gpgconf_version (gpgconf, "2.1.13"))
726     {
727       argv[argc++] = (char*)"--homedir";
728       argv[argc++] = gpgconf->home_dir;
729     }
730
731   argv[argc++] = (char*)"--runtime";
732   argv[argc++] = (char*)arg1;
733   argv[argc++] = arg2;
734   argv[argc] = NULL;
735   assert (argc < DIM (argv));
736
737   if (_gpgme_io_pipe (rp, 0) < 0)
738     {
739       err = gpg_error_from_syserror ();
740       goto leave;
741     }
742
743   if (_gpgme_io_pipe (errp, 1) < 0)
744     {
745       err = gpg_error_from_syserror ();
746       goto leave;
747     }
748
749   cfd[0].fd = rp[0];
750   cfd[1].fd = errp[1];
751
752   status = _gpgme_io_spawn (gpgconf->file_name, argv,
753                             IOSPAWN_FLAG_DETACHED, cfd, NULL, NULL, NULL);
754   if (status < 0)
755     {
756       err = gpg_error_from_syserror ();
757       goto leave;
758     }
759
760   rp[0] = -1;
761   errp[1] = -1;
762
763   for (;;)
764     {
765       if (buflen == 0)
766         {
767           do
768             {
769               buflen = gpgme_data_read (conf, buf, BUFLEN);
770             }
771           while (buflen < 0 && errno == EAGAIN);
772
773           if (buflen < 0)
774             {
775               err = gpg_error_from_syserror ();
776               goto leave;
777             }
778           else if (buflen == 0)
779             {
780               /* All is written.  */
781               _gpgme_io_close (rp[1]);
782               rp[1] = -1;
783
784               for (;;)
785                 {
786                   do
787                     {
788                       buflen = _gpgme_io_read (errp[0], buf, BUFLEN);
789                     }
790                   while (buflen < 0 && errno == EAGAIN);
791
792                   if (buflen == 0)
793                     {
794                       err = 0;
795                       goto leave;
796                     }
797                   /* XXX: Do something useful with BUF.  */
798                 }
799             }
800         }
801
802       do
803         {
804           nwrite = _gpgme_io_write (rp[1], buf, buflen);
805         }
806       while (nwrite < 0 && errno == EAGAIN);
807
808       if (nwrite > 0)
809         {
810           buflen -= nwrite;
811           if (buflen > 0)
812             memmove (&buf[0], &buf[nwrite], buflen);
813         }
814       else if (nwrite < 0)
815         {
816           err = gpg_error_from_syserror ();
817           goto leave;
818         }
819     }
820
821   assert (! "reached");
822
823  leave:
824   if (rp[0] != -1)
825     _gpgme_io_close (rp[0]);
826   if (rp[1] != -1)
827   _gpgme_io_close (rp[1]);
828   if (errp[0] != -1)
829     _gpgme_io_close (errp[0]);
830   if (errp[1] != -1)
831   _gpgme_io_close (errp[1]);
832
833   return err;
834 }
835
836
837 static gpgme_error_t
838 arg_to_data (gpgme_data_t conf, gpgme_conf_opt_t option, gpgme_conf_arg_t arg)
839 {
840   gpgme_error_t err = 0;
841   int amt = 0;
842   char buf[16];
843
844   while (amt >= 0 && arg)
845     {
846       switch (option->alt_type)
847         {
848         case GPGME_CONF_NONE:
849         case GPGME_CONF_UINT32:
850         default:
851           snprintf (buf, sizeof (buf), "%u", arg->value.uint32);
852           buf[sizeof (buf) - 1] = '\0';
853           amt = gpgme_data_write (conf, buf, strlen (buf));
854           break;
855
856         case GPGME_CONF_INT32:
857           snprintf (buf, sizeof (buf), "%i", arg->value.uint32);
858           buf[sizeof (buf) - 1] = '\0';
859           amt = gpgme_data_write (conf, buf, strlen (buf));
860           break;
861
862
863         case GPGME_CONF_STRING:
864           /* The complex types below are only here to silent the
865              compiler warning. */
866         case GPGME_CONF_FILENAME:
867         case GPGME_CONF_LDAP_SERVER:
868         case GPGME_CONF_KEY_FPR:
869         case GPGME_CONF_PUB_KEY:
870         case GPGME_CONF_SEC_KEY:
871         case GPGME_CONF_ALIAS_LIST:
872           if (arg->value.string)
873             {
874               /* One quote character, and three times to allow for
875                  percent escaping.  */
876               char *ptr = arg->value.string;
877               amt = gpgme_data_write (conf, "\"", 1);
878               if (amt < 0)
879                 break;
880
881               while (!err && *ptr)
882                 {
883                   switch (*ptr)
884                     {
885                     case '%':
886                       amt = gpgme_data_write (conf, "%25", 3);
887                       break;
888
889                     case ':':
890                       amt = gpgme_data_write (conf, "%3a", 3);
891                       break;
892
893                     case ',':
894                       amt = gpgme_data_write (conf, "%2c", 3);
895                       break;
896
897                     default:
898                       amt = gpgme_data_write (conf, ptr, 1);
899                     }
900                   ptr++;
901                 }
902             }
903           break;
904         }
905
906       if (amt < 0)
907         break;
908
909       arg = arg->next;
910       /* Comma separator.  */
911       if (arg)
912         amt = gpgme_data_write (conf, ",", 1);
913     }
914
915   if (amt < 0)
916     return gpg_error_from_syserror ();
917
918   return 0;
919 }
920
921
922 static gpgme_error_t
923 gpgconf_conf_save (void *engine, gpgme_conf_comp_t comp)
924 {
925   gpgme_error_t err;
926   int amt = 0;
927   /* We use a data object to store the new configuration.  */
928   gpgme_data_t conf;
929   gpgme_conf_opt_t option;
930   int something_changed = 0;
931
932   err = gpgme_data_new (&conf);
933   if (err)
934     return err;
935
936   option = comp->options;
937   while (!err && amt >= 0 && option)
938     {
939       if (option->change_value)
940         {
941           unsigned int flags = 0;
942           char buf[16];
943
944           something_changed = 1;
945
946           amt = gpgme_data_write (conf, option->name, strlen (option->name));
947           if (amt >= 0)
948             amt = gpgme_data_write (conf, ":", 1);
949           if (amt < 0)
950             break;
951
952           if (!option->new_value)
953             flags |= GPGME_CONF_DEFAULT;
954           snprintf (buf, sizeof (buf), "%u", flags);
955           buf[sizeof (buf) - 1] = '\0';
956
957           amt = gpgme_data_write (conf, buf, strlen (buf));
958           if (amt >= 0)
959             amt = gpgme_data_write (conf, ":", 1);
960           if (amt < 0)
961             break;
962
963           if (option->new_value)
964             {
965               err = arg_to_data (conf, option, option->new_value);
966               if (err)
967                 break;
968             }
969           amt = gpgme_data_write (conf, "\n", 1);
970         }
971       option = option->next;
972     }
973   if (!err && amt < 0)
974     err = gpg_error_from_syserror ();
975   if (err || !something_changed)
976     goto bail;
977
978   err = gpgme_data_seek (conf, 0, SEEK_SET);
979   if (err)
980     goto bail;
981
982   err = gpgconf_write (engine, "--change-options", comp->name, conf);
983  bail:
984   gpgme_data_release (conf);
985   return err;
986 }
987
988
989 struct gpgconf_config_dir_s
990 {
991   const char *what;
992   char *result;
993 };
994
995 static gpgme_error_t
996 gpgconf_config_dir_cb (void *hook, char *line)
997 {
998   /* This is an input- and output-parameter.  */
999   struct gpgconf_config_dir_s *data = (struct gpgconf_config_dir_s *) hook;
1000   int len = strlen(data->what);
1001
1002   if (!strncmp(line, data->what, len) && line[len] == ':')
1003     {
1004       char *result = strdup(&line[len + 1]);
1005       if (!result)
1006         return gpg_error_from_syserror ();
1007       data->result = result;
1008       return gpg_error(GPG_ERR_USER_1);
1009     }
1010   return 0;
1011 }
1012
1013
1014 static gpgme_error_t
1015 gpgconf_conf_dir (void *engine, const char *what, char **result)
1016 {
1017   gpgme_error_t err;
1018   struct gpgconf_config_dir_s data;
1019
1020   data.what = what;
1021   data.result = NULL;
1022   err = gpgconf_read (engine, "--list-dirs", NULL,
1023                       gpgconf_config_dir_cb, &data);
1024   if (gpg_err_code (err) == GPG_ERR_USER_1)
1025     {
1026       /* This signals to use that a result was found.  */
1027       *result = data.result;
1028       return 0;
1029     }
1030
1031   if (!err)
1032     err = gpg_error(GPG_ERR_NOT_FOUND);
1033   return 0;
1034 }
1035
1036
1037 /* Parse a line received from gpgconf --query-swdb.  This function may
1038  * modify LINE.  The result is stored at RESUL.  */
1039 static gpg_error_t
1040 parse_swdb_line (char *line, gpgme_query_swdb_result_t result)
1041 {
1042   char *field[9];
1043   int fields = 0;
1044   gpg_err_code_t ec;
1045
1046   while (line && fields < DIM (field))
1047     {
1048       field[fields++] = line;
1049       line = strchr (line, ':');
1050       if (line)
1051         *line++ = 0;
1052     }
1053   /* We require that all fields exists - gpgme emits all these fields
1054    * even on error.  They might be empty, though. */
1055   if (fields < 9)
1056     return gpg_error (GPG_ERR_INV_ENGINE);
1057
1058   free (result->name);
1059   result->name = strdup (field[0]);
1060   if (!result->name)
1061     return gpg_error_from_syserror ();
1062
1063   free (result->iversion);
1064   result->iversion = strdup (field[1]);
1065   if (!result->iversion)
1066     return gpg_error_from_syserror ();
1067
1068   result->urgent = (strtol (field[3], NULL, 10) > 0);
1069
1070   ec = gpg_err_code (strtoul (field[4], NULL, 10));
1071
1072   result->created  = _gpgme_parse_timestamp (field[5], NULL);
1073   result->retrieved= _gpgme_parse_timestamp (field[6], NULL);
1074
1075   free (result->version);
1076   result->version  = strdup (field[7]);
1077   if (!result->version)
1078     return gpg_error_from_syserror ();
1079
1080   result->reldate  = _gpgme_parse_timestamp (field[8], NULL);
1081
1082   /* Set other flags.  */
1083   result->warning = !!ec;
1084   result->update = 0;
1085   result->noinfo = 0;
1086   result->unknown = 0;
1087   result->tooold = 0;
1088   result->error = 0;
1089
1090   switch (*field[2])
1091     {
1092     case '-': result->warning = 1; break;
1093     case '?': result->unknown = result->warning = 1; break;
1094     case 'u': result->update = 1; break;
1095     case 'c': break;
1096     case 'n': break;
1097     default:
1098       result->warning = 1;
1099       if (!ec)
1100         ec = GPG_ERR_INV_ENGINE;
1101       break;
1102     }
1103
1104   if (ec == GPG_ERR_TOO_OLD)
1105     result->tooold = 1;
1106   else if (ec == GPG_ERR_ENOENT)
1107     result->noinfo = 1;
1108   else if (ec)
1109     result->error = 1;
1110
1111
1112   return 0;
1113 }
1114
1115
1116 static gpgme_error_t
1117 gpgconf_query_swdb (void *engine,
1118                     const char *name, const char *iversion,
1119                     gpgme_query_swdb_result_t result)
1120 {
1121   struct engine_gpgconf *gpgconf = engine;
1122   gpgme_error_t err = 0;
1123   char *linebuf;
1124   size_t linebufsize;
1125   int linelen;
1126   char *argv[7];
1127   int argc = 0;
1128   int rp[2];
1129   struct spawn_fd_item_s cfd[] = { {-1, 1 /* STDOUT_FILENO */, -1, 0},
1130                                    {-1, -1} };
1131   int status;
1132   int nread;
1133   char *mark = NULL;
1134
1135   if (!have_gpgconf_version (gpgconf, "2.1.16"))
1136     return gpg_error (GPG_ERR_ENGINE_TOO_OLD);
1137
1138   /* _gpgme_engine_new guarantees that this is not NULL.  */
1139   argv[argc++] = gpgconf->file_name;
1140
1141   if (gpgconf->home_dir)
1142     {
1143       argv[argc++] = (char*)"--homedir";
1144       argv[argc++] = gpgconf->home_dir;
1145     }
1146
1147   argv[argc++] = (char*)"--query-swdb";
1148   argv[argc++] = (char*)name;
1149   argv[argc++] = (char*)iversion;
1150   argv[argc] = NULL;
1151   assert (argc < DIM (argv));
1152
1153   if (_gpgme_io_pipe (rp, 1) < 0)
1154     return gpg_error_from_syserror ();
1155
1156   cfd[0].fd = rp[1];
1157
1158   status = _gpgme_io_spawn (gpgconf->file_name, argv,
1159                             IOSPAWN_FLAG_DETACHED, cfd, NULL, NULL, NULL);
1160   if (status < 0)
1161     {
1162       _gpgme_io_close (rp[0]);
1163       _gpgme_io_close (rp[1]);
1164       return gpg_error_from_syserror ();
1165     }
1166
1167   linebufsize = 2048; /* Same as used by gpgconf.  */
1168   linebuf = malloc (linebufsize);
1169   if (!linebuf)
1170     {
1171       err = gpg_error_from_syserror ();
1172       goto leave;
1173     }
1174   linelen = 0;
1175
1176   while ((nread = _gpgme_io_read (rp[0], linebuf + linelen,
1177                                   linebufsize - linelen - 1)))
1178     {
1179       char *line;
1180       const char *lastmark = NULL;
1181       size_t nused;
1182
1183       if (nread < 0)
1184         {
1185           err = gpg_error_from_syserror ();
1186           goto leave;
1187         }
1188
1189       linelen += nread;
1190       linebuf[linelen] = '\0';
1191
1192       for (line=linebuf; (mark = strchr (line, '\n')); line = mark+1 )
1193         {
1194           lastmark = mark;
1195           if (mark > line && mark[-1] == '\r')
1196             mark[-1] = '\0';
1197           else
1198             mark[0] = '\0';
1199
1200           /* Got a full line.  Due to the CR removal code (which
1201              occurs only on Windows) we might be one-off and thus
1202              would see empty lines.  */
1203           if (*line)
1204             {
1205               err = parse_swdb_line (line, result);
1206               goto leave; /* Ready.  */
1207             }
1208           else /* empty line.  */
1209             err = 0;
1210         }
1211
1212       nused = lastmark? (lastmark + 1 - linebuf) : 0;
1213       memmove (linebuf, linebuf + nused, linelen - nused);
1214       linelen -= nused;
1215
1216       if (!(linelen < linebufsize - 1))
1217         {
1218           char *newlinebuf;
1219
1220           if (linelen <  8 * 1024 - 1)
1221             linebufsize = 8 * 1024;
1222           else if (linelen < 64 * 1024 - 1)
1223             linebufsize = 64 * 1024;
1224           else
1225             {
1226               /* We reached our limit - give up.  */
1227               err = gpg_error (GPG_ERR_LINE_TOO_LONG);
1228               goto leave;
1229             }
1230
1231           newlinebuf = realloc (linebuf, linebufsize);
1232           if (!newlinebuf)
1233             {
1234               err = gpg_error_from_syserror ();
1235               goto leave;
1236             }
1237           linebuf = newlinebuf;
1238         }
1239     }
1240
1241  leave:
1242   free (linebuf);
1243   _gpgme_io_close (rp[0]);
1244   return err;
1245 }
1246
1247
1248 static void
1249 gpgconf_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
1250 {
1251   (void)engine;
1252   (void)io_cbs;
1253   /* Nothing to do.  */
1254 }
1255
1256 \f
1257 /* Currently, we do not use the engine interface for the various
1258    operations.  */
1259 void
1260 _gpgme_conf_release (gpgme_conf_comp_t conf)
1261 {
1262   gpgconf_config_release (conf);
1263 }
1264
1265 \f
1266 struct engine_ops _gpgme_engine_ops_gpgconf =
1267   {
1268     /* Static functions.  */
1269     _gpgme_get_default_gpgconf_name,
1270     NULL,
1271     gpgconf_get_version,
1272     gpgconf_get_req_version,
1273     gpgconf_new,
1274
1275     /* Member functions.  */
1276     gpgconf_release,
1277     NULL,               /* reset */
1278     NULL,               /* set_status_cb */
1279     NULL,               /* set_status_handler */
1280     NULL,               /* set_command_handler */
1281     NULL,               /* set_colon_line_handler */
1282     NULL,               /* set_locale */
1283     NULL,               /* set_protocol */
1284     NULL,               /* decrypt */
1285     NULL,               /* delete */
1286     NULL,               /* edit */
1287     NULL,               /* encrypt */
1288     NULL,               /* encrypt_sign */
1289     NULL,               /* export */
1290     NULL,               /* export_ext */
1291     NULL,               /* genkey */
1292     NULL,               /* import */
1293     NULL,               /* keylist */
1294     NULL,               /* keylist_ext */
1295     NULL,               /* keylist_data */
1296     NULL,               /* keysign */
1297     NULL,               /* tofu_policy */
1298     NULL,               /* sign */
1299     NULL,               /* trustlist */
1300     NULL,               /* verify */
1301     NULL,               /* getauditlog */
1302     NULL,               /* opassuan_transact */
1303     gpgconf_conf_load,
1304     gpgconf_conf_save,
1305     gpgconf_conf_dir,
1306     gpgconf_query_swdb,
1307     gpgconf_set_io_cbs,
1308     NULL,               /* io_event */
1309     NULL,               /* cancel */
1310     NULL,               /* cancel_op */
1311     NULL,               /* passwd */
1312     NULL,               /* set_pinentry_mode */
1313     NULL                /* opspawn */
1314   };