Make handling of new conf values more robust (bug#1413).
[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 g10 Code GmbH
4
5    This file is part of GPGME.
6
7    GPGME is free software; you can redistribute it and/or modify it
8    under the terms of the GNU Lesser General Public License as
9    published by the Free Software Foundation; either version 2.1 of
10    the License, or (at your option) any later version.
11
12    GPGME is distributed in the hope that it will be useful, but
13    WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    Lesser General Public License for more details.
16
17    You should have received a copy of the GNU Lesser General Public
18    License along with this program; if not, see <http://www.gnu.org/licenses/>.
19  */
20
21 #if HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #include <stdlib.h>
26 #include <string.h>
27 #ifdef HAVE_SYS_TYPES_H
28 # include <sys/types.h>
29 #endif
30 #include <assert.h>
31 #ifdef HAVE_UNISTD_H
32 # include <unistd.h>
33 #endif
34 #include <fcntl.h> /* FIXME */
35 #include <errno.h>
36
37 #include "gpgme.h"
38 #include "util.h"
39 #include "ops.h"
40 #include "wait.h"
41 #include "priv-io.h"
42 #include "sema.h"
43
44 #include "assuan.h"
45 #include "debug.h"
46
47 #include "engine-backend.h"
48
49 \f
50 struct engine_gpgconf
51 {
52   char *file_name;
53   char *home_dir;
54 };
55
56 typedef struct engine_gpgconf *engine_gpgconf_t;
57
58 \f
59 static char *
60 gpgconf_get_version (const char *file_name)
61 {
62   return _gpgme_get_program_version (file_name ? file_name
63                                      : _gpgme_get_gpgconf_path ());
64 }
65
66
67 static const char *
68 gpgconf_get_req_version (void)
69 {
70   return NEED_GPGCONF_VERSION;
71 }
72
73 \f
74 static void
75 gpgconf_release (void *engine)
76 {
77   engine_gpgconf_t gpgconf = engine;
78
79   if (!gpgconf)
80     return;
81
82   if (gpgconf->file_name)
83     free (gpgconf->file_name);
84   if (gpgconf->home_dir)
85     free (gpgconf->home_dir);
86
87   free (gpgconf);
88 }
89
90
91 static gpgme_error_t
92 gpgconf_new (void **engine, const char *file_name, const char *home_dir)
93 {
94   gpgme_error_t err = 0;
95   engine_gpgconf_t gpgconf;
96
97   gpgconf = calloc (1, sizeof *gpgconf);
98   if (!gpgconf)
99     return gpg_error_from_errno (errno);
100
101   gpgconf->file_name = strdup (file_name ? file_name
102                                : _gpgme_get_gpgconf_path ());
103   if (!gpgconf->file_name)
104     err = gpg_error_from_syserror ();
105
106   if (!err && home_dir)
107     {
108       gpgconf->home_dir = strdup (home_dir);
109       if (!gpgconf->home_dir)
110         err = gpg_error_from_syserror ();
111     }
112
113   if (err)
114     gpgconf_release (gpgconf);
115   else
116     *engine = gpgconf;
117
118   return err;
119 }
120
121 \f
122 static void
123 release_arg (gpgme_conf_arg_t arg, gpgme_conf_type_t alt_type)
124 {
125   while (arg)
126     {
127       gpgme_conf_arg_t next = arg->next;
128
129       if (alt_type == GPGME_CONF_STRING)
130         free (arg->value.string);
131       free (arg);
132       arg = next;
133     }
134 }
135
136
137 static void
138 release_opt (gpgme_conf_opt_t opt)
139 {
140   if (opt->name)
141     free (opt->name);
142   if (opt->description)
143     free (opt->description);
144   if (opt->argname)
145     free (opt->argname);
146
147   release_arg (opt->default_value, opt->alt_type);
148   if (opt->default_description)
149     free (opt->default_description);
150
151   release_arg (opt->no_arg_value, opt->alt_type);
152   release_arg (opt->value, opt->alt_type);
153   release_arg (opt->new_value, opt->alt_type);
154
155   free (opt);
156 }
157
158
159 static void
160 release_comp (gpgme_conf_comp_t comp)
161 {
162   gpgme_conf_opt_t opt;
163
164   if (comp->name)
165     free (comp->name);
166   if (comp->description)
167     free (comp->description);
168   if (comp->program_name)
169     free (comp->program_name);
170
171   opt = comp->options;
172   while (opt)
173     {
174       gpgme_conf_opt_t next = opt->next;
175       release_opt (opt);
176       opt = next;
177     }
178
179   free (comp);
180 }
181
182
183 static void
184 gpgconf_config_release (gpgme_conf_comp_t conf)
185 {
186   while (conf)
187     {
188       gpgme_conf_comp_t next = conf->next;
189       release_comp (conf);
190       conf = next;
191     }
192 }
193
194
195 static gpgme_error_t
196 gpgconf_read (void *engine, char *arg1, char *arg2,
197               gpgme_error_t (*cb) (void *hook, char *line),
198               void *hook)
199 {
200   struct engine_gpgconf *gpgconf = engine;
201   gpgme_error_t err = 0;
202 #define LINELENGTH 1024
203   char linebuf[LINELENGTH] = "";
204   int linelen = 0;
205   char *argv[4] = { NULL /* file_name */, NULL, NULL, NULL };
206   int rp[2];
207   struct spawn_fd_item_s cfd[] = { {-1, 1 /* STDOUT_FILENO */, -1, 0},
208                                    {-1, -1} };
209   int status;
210   int nread;
211   char *mark = NULL;
212
213   argv[1] = arg1;
214   argv[2] = arg2;
215
216
217   /* FIXME: Deal with engine->home_dir.  */
218
219   /* _gpgme_engine_new guarantees that this is not NULL.  */
220   argv[0] = gpgconf->file_name;
221
222   if (_gpgme_io_pipe (rp, 1) < 0)
223     return gpg_error_from_syserror ();
224
225   cfd[0].fd = rp[1];
226
227   status = _gpgme_io_spawn (gpgconf->file_name, argv, 0, cfd, NULL, NULL, NULL);
228   if (status < 0)
229     {
230       _gpgme_io_close (rp[0]);
231       _gpgme_io_close (rp[1]);
232       return gpg_error_from_syserror ();
233     }
234
235   do
236     {
237       nread = _gpgme_io_read (rp[0],
238                               linebuf + linelen, LINELENGTH - linelen - 1);
239       if (nread > 0)
240         {
241           char *line;
242           const char *lastmark = NULL;
243           size_t nused;
244
245           linelen += nread;
246           linebuf[linelen] = '\0';
247
248           for (line=linebuf; (mark = strchr (line, '\n')); line = mark+1 )
249             {
250               lastmark = mark;
251               if (mark > line && mark[-1] == '\r')
252                 mark[-1] = '\0';
253               else
254                 mark[0] = '\0';
255
256               /* Got a full line.  Due to the CR removal code (which
257                  occurs only on Windows) we might be one-off and thus
258                  would see empty lines.  Don't pass them to the
259                  callback. */
260               err = *line? (*cb) (hook, line) : 0;
261               if (err)
262                 goto leave;
263             }
264
265           nused = lastmark? (lastmark + 1 - linebuf) : 0;
266           memmove (linebuf, linebuf + nused, linelen - nused);
267           linelen -= nused;
268         }
269     }
270   while (nread > 0 && linelen < LINELENGTH - 1);
271
272   if (!err && nread < 0)
273     err = gpg_error_from_syserror ();
274   if (!err && nread > 0)
275     err = gpg_error (GPG_ERR_LINE_TOO_LONG);
276
277  leave:
278   _gpgme_io_close (rp[0]);
279
280   return err;
281 }
282
283
284 static gpgme_error_t
285 gpgconf_config_load_cb (void *hook, char *line)
286 {
287   gpgme_conf_comp_t *comp_p = hook;
288   gpgme_conf_comp_t comp = *comp_p;
289 #define NR_FIELDS 16
290   char *field[NR_FIELDS];
291   int fields = 0;
292
293   while (line && fields < NR_FIELDS)
294     {
295       field[fields++] = line;
296       line = strchr (line, ':');
297       if (line)
298         *(line++) = '\0';
299     }
300
301   /* We require at least the first 3 fields.  */
302   if (fields < 2)
303     return gpg_error (GPG_ERR_INV_ENGINE);
304
305   /* Find the pointer to the new component in the list.  */
306   while (comp && comp->next)
307     comp = comp->next;
308   if (comp)
309     comp_p = &comp->next;
310
311   comp = calloc (1, sizeof (*comp));
312   if (!comp)
313     return gpg_error_from_syserror ();
314   /* Prepare return value.  */
315   comp->_last_opt_p = &comp->options;
316   *comp_p = comp;
317
318   comp->name = strdup (field[0]);
319   if (!comp->name)
320     return gpg_error_from_syserror ();
321
322   comp->description = strdup (field[1]);
323   if (!comp->description)
324     return gpg_error_from_syserror ();
325
326   if (fields >= 3)
327     {
328       comp->program_name = strdup (field[2]);
329       if (!comp->program_name)
330         return gpg_error_from_syserror ();
331     }
332
333   return 0;
334 }
335
336
337 static gpgme_error_t
338 gpgconf_parse_option (gpgme_conf_opt_t opt,
339                       gpgme_conf_arg_t *arg_p, char *line)
340 {
341   gpgme_error_t err;
342   char *mark;
343
344   if (!line[0])
345     return 0;
346
347   while (line)
348     {
349       gpgme_conf_arg_t arg;
350
351       mark = strchr (line, ',');
352       if (mark)
353         *mark = '\0';
354
355       arg = calloc (1, sizeof (*arg));
356       if (!arg)
357         return gpg_error_from_syserror ();
358       *arg_p = arg;
359       arg_p = &arg->next;
360
361       if (*line == '\0')
362         arg->no_arg = 1;
363       else
364         {
365           switch (opt->alt_type)
366             {
367               /* arg->value.count is an alias for arg->value.uint32.  */
368             case GPGME_CONF_NONE:
369             case GPGME_CONF_UINT32:
370               arg->value.uint32 = strtoul (line, NULL, 0);
371               break;
372
373             case GPGME_CONF_INT32:
374               arg->value.uint32 = strtol (line, NULL, 0);
375               break;
376
377             case GPGME_CONF_STRING:
378               /* The complex types below are only here to silent the
379                  compiler warning. */
380             case GPGME_CONF_FILENAME:
381             case GPGME_CONF_LDAP_SERVER:
382             case GPGME_CONF_KEY_FPR:
383             case GPGME_CONF_PUB_KEY:
384             case GPGME_CONF_SEC_KEY:
385             case GPGME_CONF_ALIAS_LIST:
386               /* Skip quote character.  */
387               line++;
388
389               err = _gpgme_decode_percent_string (line, &arg->value.string,
390                                                   0, 0);
391               if (err)
392                 return err;
393               break;
394             }
395         }
396
397       /* Find beginning of next value.  */
398       if (mark++ && *mark)
399         line = mark;
400       else
401         line = NULL;
402     }
403
404   return 0;
405 }
406
407
408 static gpgme_error_t
409 gpgconf_config_load_cb2 (void *hook, char *line)
410 {
411   gpgme_error_t err;
412   gpgme_conf_comp_t comp = hook;
413   gpgme_conf_opt_t *opt_p = comp->_last_opt_p;
414   gpgme_conf_opt_t opt;
415 #define NR_FIELDS 16
416   char *field[NR_FIELDS];
417   int fields = 0;
418
419   while (line && fields < NR_FIELDS)
420     {
421       field[fields++] = line;
422       line = strchr (line, ':');
423       if (line)
424         *(line++) = '\0';
425     }
426
427   /* We require at least the first 10 fields.  */
428   if (fields < 10)
429     return gpg_error (GPG_ERR_INV_ENGINE);
430
431   opt = calloc (1, sizeof (*opt));
432   if (!opt)
433     return gpg_error_from_syserror ();
434
435   comp->_last_opt_p = &opt->next;
436   *opt_p = opt;
437
438   if (field[0][0])
439     {
440       opt->name = strdup (field[0]);
441       if (!opt->name)
442         return gpg_error_from_syserror ();
443     }
444
445   opt->flags = strtoul (field[1], NULL, 0);
446
447   opt->level = strtoul (field[2], NULL, 0);
448
449   if (field[3][0])
450     {
451       opt->description = strdup (field[3]);
452       if (!opt->description)
453         return gpg_error_from_syserror ();
454     }
455
456   opt->type = strtoul (field[4], NULL, 0);
457
458   opt->alt_type = strtoul (field[5], NULL, 0);
459
460   if (field[6][0])
461     {
462       opt->argname = strdup (field[6]);
463       if (!opt->argname)
464         return gpg_error_from_syserror ();
465     }
466
467   if (opt->flags & GPGME_CONF_DEFAULT)
468     {
469       err = gpgconf_parse_option (opt, &opt->default_value, field[7]);
470       if (err)
471         return err;
472     }
473   else if ((opt->flags & GPGME_CONF_DEFAULT_DESC) && field[7][0])
474     {
475       opt->default_description = strdup (field[7]);
476       if (!opt->default_description)
477         return gpg_error_from_syserror ();
478     }
479
480   if (opt->flags & GPGME_CONF_NO_ARG_DESC)
481     {
482       opt->no_arg_description = strdup (field[8]);
483       if (!opt->no_arg_description)
484         return gpg_error_from_syserror ();
485     }
486   else
487     {
488       err = gpgconf_parse_option (opt, &opt->no_arg_value, field[8]);
489       if (err)
490         return err;
491     }
492
493   err = gpgconf_parse_option (opt, &opt->value, field[9]);
494   if (err)
495     return err;
496
497   return 0;
498 }
499
500
501 static gpgme_error_t
502 gpgconf_conf_load (void *engine, gpgme_conf_comp_t *comp_p)
503 {
504   gpgme_error_t err;
505   gpgme_conf_comp_t comp = NULL;
506   gpgme_conf_comp_t cur_comp;
507
508   *comp_p = NULL;
509
510   err = gpgconf_read (engine, "--list-components", NULL,
511                       gpgconf_config_load_cb, &comp);
512   if (err)
513     {
514       gpgconf_release (comp);
515       return err;
516     }
517
518   cur_comp = comp;
519   while (!err && cur_comp)
520     {
521       err = gpgconf_read (engine, "--list-options", cur_comp->name,
522                           gpgconf_config_load_cb2, cur_comp);
523       cur_comp = cur_comp->next;
524     }
525
526   if (err)
527     {
528       gpgconf_release (comp);
529       return err;
530     }
531
532   *comp_p = comp;
533   return 0;
534 }
535
536
537 \f
538 gpgme_error_t
539 _gpgme_conf_arg_new (gpgme_conf_arg_t *arg_p,
540                      gpgme_conf_type_t type, const void *value)
541 {
542   gpgme_conf_arg_t arg;
543
544   arg = calloc (1, sizeof (*arg));
545   if (!arg)
546     return gpg_error_from_syserror ();
547
548   if (!value)
549     arg->no_arg = 1;
550   else
551     {
552       /* We need to switch on type here because the alt-type is not
553          yet known.  */
554       switch (type)
555         {
556         case GPGME_CONF_NONE:
557         case GPGME_CONF_UINT32:
558           arg->value.uint32 = *((unsigned int *) value);
559           break;
560
561         case GPGME_CONF_INT32:
562           arg->value.int32 = *((int *) value);
563           break;
564
565         case GPGME_CONF_STRING:
566         case GPGME_CONF_FILENAME:
567         case GPGME_CONF_LDAP_SERVER:
568         case GPGME_CONF_KEY_FPR:
569         case GPGME_CONF_PUB_KEY:
570         case GPGME_CONF_SEC_KEY:
571         case GPGME_CONF_ALIAS_LIST:
572           arg->value.string = strdup (value);
573           if (!arg->value.string)
574             {
575               free (arg);
576               return gpg_error_from_syserror ();
577             }
578           break;
579
580         default:
581           free (arg);
582           return gpg_error (GPG_ERR_INV_VALUE);
583         }
584     }
585
586   *arg_p = arg;
587   return 0;
588 }
589
590
591 void
592 _gpgme_conf_arg_release (gpgme_conf_arg_t arg, gpgme_conf_type_t type)
593 {
594   /* Lacking the alt_type we need to switch on type here.  */
595   switch (type)
596     {
597     case GPGME_CONF_NONE:
598     case GPGME_CONF_UINT32:
599     case GPGME_CONF_INT32:
600     case GPGME_CONF_STRING:
601     default:
602       break;
603
604     case GPGME_CONF_FILENAME:
605     case GPGME_CONF_LDAP_SERVER:
606     case GPGME_CONF_KEY_FPR:
607     case GPGME_CONF_PUB_KEY:
608     case GPGME_CONF_SEC_KEY:
609     case GPGME_CONF_ALIAS_LIST:
610       type = GPGME_CONF_STRING;
611       break;
612     }
613
614   release_arg (arg, type);
615 }
616
617
618 gpgme_error_t
619 _gpgme_conf_opt_change (gpgme_conf_opt_t opt, int reset, gpgme_conf_arg_t arg)
620 {
621   if (reset)
622     {
623       if (opt->new_value)
624         release_arg (opt->new_value, opt->alt_type);
625       opt->new_value = NULL;
626       opt->change_value = 0;
627     }
628   else
629     {
630       /* Support self-assignment, for example for adding an item to an
631          existing list.  */
632       if (opt->new_value && arg != opt->new_value)
633         release_arg (opt->new_value, opt->alt_type);
634       opt->new_value = arg;
635       opt->change_value = 1;
636     }
637   return 0;
638 }
639
640 \f
641 /* FIXME: Major problem: We don't get errors from gpgconf.  */
642
643 static gpgme_error_t
644 gpgconf_write (void *engine, char *arg1, char *arg2, gpgme_data_t conf)
645 {
646   struct engine_gpgconf *gpgconf = engine;
647   gpgme_error_t err = 0;
648 #define BUFLEN 1024
649   char buf[BUFLEN];
650   int buflen = 0;
651   char *argv[] = { NULL /* file_name */, arg1, arg2, 0 };
652   int rp[2];
653   struct spawn_fd_item_s cfd[] = { {-1, 0 /* STDIN_FILENO */}, {-1, -1} };
654   int status;
655   int nwrite;
656
657   /* FIXME: Deal with engine->home_dir.  */
658
659   /* _gpgme_engine_new guarantees that this is not NULL.  */
660   argv[0] = gpgconf->file_name;
661
662   if (_gpgme_io_pipe (rp, 0) < 0)
663     return gpg_error_from_syserror ();
664
665   cfd[0].fd = rp[0];
666
667   status = _gpgme_io_spawn (gpgconf->file_name, argv, 0, cfd, NULL, NULL, NULL);
668   if (status < 0)
669     {
670       _gpgme_io_close (rp[0]);
671       _gpgme_io_close (rp[1]);
672       return gpg_error_from_syserror ();
673     }
674
675   for (;;)
676     {
677       if (buflen == 0)
678         {
679           do
680             {
681               buflen = gpgme_data_read (conf, buf, BUFLEN);
682             }
683           while (buflen < 0 && errno == EAGAIN);
684
685           if (buflen < 0)
686             {
687               err = gpg_error_from_syserror ();
688               _gpgme_io_close (rp[1]);
689               return err;
690             }
691           else if (buflen == 0)
692             {
693               /* All is written.  */
694               _gpgme_io_close (rp[1]);
695               return 0;
696             }
697         }
698
699       do
700         {
701           nwrite = _gpgme_io_write (rp[1], buf, buflen);
702         }
703       while (nwrite < 0 && errno == EAGAIN);
704
705       if (nwrite > 0)
706         {
707           buflen -= nwrite;
708           if (buflen > 0)
709             memmove (&buf[0], &buf[nwrite], buflen);
710         }
711       else if (nwrite < 0)
712         {
713           _gpgme_io_close (rp[1]);
714           return gpg_error_from_syserror ();
715         }
716     }
717
718   return 0;
719 }
720
721
722 static gpgme_error_t
723 arg_to_data (gpgme_data_t conf, gpgme_conf_opt_t option, gpgme_conf_arg_t arg)
724 {
725   gpgme_error_t err = 0;
726   int amt = 0;
727   char buf[16];
728
729   while (amt >= 0 && arg)
730     {
731       switch (option->alt_type)
732         {
733         case GPGME_CONF_NONE:
734         case GPGME_CONF_UINT32:
735         default:
736           snprintf (buf, sizeof (buf), "%u", arg->value.uint32);
737           buf[sizeof (buf) - 1] = '\0';
738           amt = gpgme_data_write (conf, buf, strlen (buf));
739           break;
740
741         case GPGME_CONF_INT32:
742           snprintf (buf, sizeof (buf), "%i", arg->value.uint32);
743           buf[sizeof (buf) - 1] = '\0';
744           amt = gpgme_data_write (conf, buf, strlen (buf));
745           break;
746
747
748         case GPGME_CONF_STRING:
749           /* The complex types below are only here to silent the
750              compiler warning. */
751         case GPGME_CONF_FILENAME:
752         case GPGME_CONF_LDAP_SERVER:
753         case GPGME_CONF_KEY_FPR:
754         case GPGME_CONF_PUB_KEY:
755         case GPGME_CONF_SEC_KEY:
756         case GPGME_CONF_ALIAS_LIST:
757           if (arg->value.string)
758             {
759               /* One quote character, and three times to allow for
760                  percent escaping.  */
761               char *ptr = arg->value.string;
762               amt = gpgme_data_write (conf, "\"", 1);
763               if (amt < 0)
764                 break;
765
766               while (!err && *ptr)
767                 {
768                   switch (*ptr)
769                     {
770                     case '%':
771                       amt = gpgme_data_write (conf, "%25", 3);
772                       break;
773
774                     case ':':
775                       amt = gpgme_data_write (conf, "%3a", 3);
776                       break;
777
778                     case ',':
779                       amt = gpgme_data_write (conf, "%2c", 3);
780                       break;
781
782                     default:
783                       amt = gpgme_data_write (conf, ptr, 1);
784                     }
785                   ptr++;
786                 }
787             }
788           break;
789         }
790
791       if (amt < 0)
792         break;
793
794       arg = arg->next;
795       /* Comma separator.  */
796       if (arg)
797         amt = gpgme_data_write (conf, ",", 1);
798     }
799
800   if (amt < 0)
801     return gpg_error_from_syserror ();
802
803   return 0;
804 }
805
806
807 static gpgme_error_t
808 gpgconf_conf_save (void *engine, gpgme_conf_comp_t comp)
809 {
810   gpgme_error_t err;
811   int amt = 0;
812   /* We use a data object to store the new configuration.  */
813   gpgme_data_t conf;
814   gpgme_conf_opt_t option;
815   int something_changed = 0;
816
817   err = gpgme_data_new (&conf);
818   if (err)
819     return err;
820
821   option = comp->options;
822   while (!err && amt >= 0 && option)
823     {
824       if (option->change_value)
825         {
826           unsigned int flags = 0;
827           char buf[16];
828
829           something_changed = 1;
830
831           amt = gpgme_data_write (conf, option->name, strlen (option->name));
832           if (amt >= 0)
833             amt = gpgme_data_write (conf, ":", 1);
834           if (amt < 0)
835             break;
836
837           if (!option->new_value)
838             flags |= GPGME_CONF_DEFAULT;
839           snprintf (buf, sizeof (buf), "%u", flags);
840           buf[sizeof (buf) - 1] = '\0';
841
842           amt = gpgme_data_write (conf, buf, strlen (buf));
843           if (amt >= 0)
844             amt = gpgme_data_write (conf, ":", 1);
845           if (amt < 0)
846             break;
847
848           if (option->new_value)
849             {
850               err = arg_to_data (conf, option, option->new_value);
851               if (err)
852                 break;
853             }
854           amt = gpgme_data_write (conf, "\n", 1);
855         }
856       option = option->next;
857     }
858   if (!err && amt < 0)
859     err = gpg_error_from_syserror ();
860   if (err || !something_changed)
861     goto bail;
862
863   err = gpgme_data_seek (conf, 0, SEEK_SET);
864   if (err)
865     goto bail;
866
867   err = gpgconf_write (engine, "--change-options", comp->name, conf);
868  bail:
869   gpgme_data_release (conf);
870   return err;
871 }
872
873
874 static void
875 gpgconf_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
876 {
877   /* Nothing to do.  */
878 }
879
880 \f
881 /* Currently, we do not use the engine interface for the various
882    operations.  */
883 void
884 _gpgme_conf_release (gpgme_conf_comp_t conf)
885 {
886   gpgconf_config_release (conf);
887 }
888
889 \f
890 struct engine_ops _gpgme_engine_ops_gpgconf =
891   {
892     /* Static functions.  */
893     _gpgme_get_gpgconf_path,
894     NULL,
895     gpgconf_get_version,
896     gpgconf_get_req_version,
897     gpgconf_new,
898
899     /* Member functions.  */
900     gpgconf_release,
901     NULL,               /* reset */
902     NULL,               /* set_status_handler */
903     NULL,               /* set_command_handler */
904     NULL,               /* set_colon_line_handler */
905     NULL,               /* set_locale */
906     NULL,               /* set_protocol */
907     NULL,               /* decrypt */
908     NULL,               /* decrypt_verify */
909     NULL,               /* delete */
910     NULL,               /* edit */
911     NULL,               /* encrypt */
912     NULL,               /* encrypt_sign */
913     NULL,               /* export */
914     NULL,               /* export_ext */
915     NULL,               /* genkey */
916     NULL,               /* import */
917     NULL,               /* keylist */
918     NULL,               /* keylist_ext */
919     NULL,               /* sign */
920     NULL,               /* trustlist */
921     NULL,               /* verify */
922     NULL,               /* getauditlog */
923     NULL,               /* opassuan_transact */
924     gpgconf_conf_load,
925     gpgconf_conf_save,
926     gpgconf_set_io_cbs,
927     NULL,               /* io_event */
928     NULL                /* cancel */
929   };