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