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