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