2008-01-14 Marcus Brinkmann <marcus@g10code.de>
[gpgme.git] / gpgme / 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, write to the Free Software
19    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20    02111-1307, USA.  */
21
22 #if HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdlib.h>
27 #include <string.h>
28 #include <sys/types.h>
29 #include <assert.h>
30 #include <unistd.h>
31 #include <locale.h>
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 line[LINELENGTH] = "";
202   int linelen = 0;
203   char *argv[] = { NULL /* file_name */, arg1, arg2, 0 };
204   int rp[2];
205   struct spawn_fd_item_s pfd[] = { {0, -1}, {-1, -1} };
206   struct spawn_fd_item_s cfd[] = { {-1, 1 /* STDOUT_FILENO */}, {-1, -1} };
207   int status;
208   int nread;
209   char *mark = NULL;
210
211   /* FIXME: Deal with engine->home_dir.  */
212
213   /* _gpgme_engine_new guarantees that this is not NULL.  */
214   argv[0] = gpgconf->file_name;
215   
216   if (_gpgme_io_pipe (rp, 1) < 0)
217     return gpg_error_from_syserror ();
218
219   pfd[0].fd = rp[1];
220   cfd[0].fd = rp[1];
221
222   status = _gpgme_io_spawn (gpgconf->file_name, argv, cfd, pfd);
223   if (status < 0)
224     {
225       _gpgme_io_close (rp[0]);
226       _gpgme_io_close (rp[1]);
227       return gpg_error_from_syserror ();
228     }
229
230   do
231     {
232       nread = _gpgme_io_read (rp[0], &line[linelen], LINELENGTH - linelen - 1);
233       if (nread > 0)
234         {
235           line[linelen + nread] = '\0';
236           linelen += nread;
237
238           while ((mark = strchr (line, '\n')))
239             {
240               char *eol = mark;
241               
242               if (eol > &line[0] && *eol == '\r')
243                 eol--;
244               *eol = '\0';
245               
246               /* Got a full line.  */
247               err = (*cb) (hook, line);
248               if (err)
249                 break;
250               
251               linelen -= mark - line;
252               memmove (line, eol + 1, linelen);
253             }
254         }
255     }
256   while (nread > 0 && linelen < LINELENGTH - 1);
257   
258   if (!err && nread < 0)
259     err = gpg_error_from_syserror ();
260   if (!err && nread > 0)
261     err = gpg_error (GPG_ERR_LINE_TOO_LONG);
262
263   _gpgme_io_close (rp[0]);
264
265   return err;
266 }
267
268
269 static gpgme_error_t
270 gpgconf_config_load_cb (void *hook, char *line)
271 {
272   gpgme_conf_comp_t *comp_p = hook;
273   gpgme_conf_comp_t comp = *comp_p;
274 #define NR_FIELDS 16
275   char *field[NR_FIELDS];
276   int fields = 0;
277
278   while (line && fields < NR_FIELDS)
279     {
280       field[fields++] = line;
281       line = strchr (line, ':');
282       if (line)
283         *(line++) = '\0';
284     }
285
286   /* We require at least the first 3 fields.  */
287   if (fields < 2)
288     return gpg_error (GPG_ERR_INV_ENGINE);
289
290   /* Find the pointer to the new component in the list.  */
291   while (comp && comp->next)
292     comp = comp->next;
293   if (comp)
294     comp_p = &comp->next;
295
296   comp = calloc (1, sizeof (*comp));
297   if (!comp)
298     return gpg_error_from_syserror ();
299   /* Prepare return value.  */
300   comp->_last_opt_p = &comp->options;
301   *comp_p = comp;
302
303   comp->name = strdup (field[0]);
304   if (!comp->name)
305     return gpg_error_from_syserror ();
306
307   comp->description = strdup (field[1]);
308   if (!comp->description)
309     return gpg_error_from_syserror ();
310
311   if (fields >= 3)
312     {
313       comp->program_name = strdup (field[2]);
314       if (!comp->program_name)
315         return gpg_error_from_syserror ();
316     }
317
318   return 0;
319 }
320
321
322 static gpgme_error_t
323 gpgconf_parse_option (gpgme_conf_opt_t opt,
324                       gpgme_conf_arg_t *arg_p, char *line)
325 {
326   gpgme_error_t err;
327   char *mark;
328
329   if (!line[0])
330     return 0;
331
332   mark = strchr (line, ',');
333   if (mark)
334     *mark = '\0';
335
336   while (line)
337     {
338       gpgme_conf_arg_t arg = calloc (1, sizeof (*arg));
339       if (!arg)
340         return gpg_error_from_syserror ();
341       *arg_p = arg;
342       arg_p = &arg->next;
343
344       if (*line == '\0')
345         arg->no_arg = 1;
346       else
347         {
348           switch (opt->alt_type)
349             {
350               /* arg->value.count is an alias for arg->value.uint32.  */
351             case GPGME_CONF_NONE:
352             case GPGME_CONF_UINT32:
353               arg->value.uint32 = strtoul (line, NULL, 0);
354               break;
355               
356             case GPGME_CONF_INT32:
357               arg->value.uint32 = strtol (line, NULL, 0);
358               break;
359               
360             case GPGME_CONF_STRING:
361             case GPGME_CONF_PATHNAME:
362             case GPGME_CONF_LDAP_SERVER:
363               /* Skip quote character.  */
364               line++;
365               
366               err = _gpgme_decode_percent_string (line, &arg->value.string,
367                                                   0, 0);
368               if (err)
369                 return err;
370               break;
371             }
372         }
373
374       /* Find beginning of next value.  */
375       if (mark++ && *mark)
376         line = mark;
377       else
378         line = NULL;
379     }
380
381   return 0;
382 }
383
384
385 static gpgme_error_t
386 gpgconf_config_load_cb2 (void *hook, char *line)
387 {
388   gpgme_error_t err;
389   gpgme_conf_comp_t comp = hook;
390   gpgme_conf_opt_t *opt_p = comp->_last_opt_p;
391   gpgme_conf_opt_t opt;
392 #define NR_FIELDS 16
393   char *field[NR_FIELDS];
394   int fields = 0;
395
396   while (line && fields < NR_FIELDS)
397     {
398       field[fields++] = line;
399       line = strchr (line, ':');
400       if (line)
401         *(line++) = '\0';
402     }
403
404   /* We require at least the first 10 fields.  */
405   if (fields < 10)
406     return gpg_error (GPG_ERR_INV_ENGINE);
407
408   opt = calloc (1, sizeof (*opt));
409   if (!opt)
410     return gpg_error_from_syserror ();
411
412   comp->_last_opt_p = &opt->next;
413   *opt_p = opt;
414
415   if (field[0][0])
416     {
417       opt->name = strdup (field[0]);
418       if (!opt->name)
419         return gpg_error_from_syserror ();
420     }
421
422   opt->flags = strtoul (field[1], NULL, 0);
423
424   opt->level = strtoul (field[2], NULL, 0);
425
426   if (field[3][0])
427     {
428       opt->description = strdup (field[3]);
429       if (!opt->description)
430         return gpg_error_from_syserror ();
431     }
432
433   opt->type = strtoul (field[4], NULL, 0);
434
435   opt->alt_type = strtoul (field[5], NULL, 0);
436
437   if (field[6][0])
438     {
439       opt->argname = strdup (field[6]);
440       if (!opt->argname)
441         return gpg_error_from_syserror ();
442     }
443
444   if (opt->flags & GPGME_CONF_DEFAULT)
445     {
446       err = gpgconf_parse_option (opt, &opt->default_value, field[7]);
447       if (err)
448         return err;
449     }
450   else if ((opt->flags & GPGME_CONF_DEFAULT_DESC) && field[7][0])
451     {
452       opt->default_description = strdup (field[7]);
453       if (!opt->default_description)
454         return gpg_error_from_syserror ();
455     }
456
457   if (opt->flags & GPGME_CONF_NO_ARG_DESC)
458     {
459       opt->no_arg_description = strdup (field[8]);
460       if (!opt->no_arg_description)
461         return gpg_error_from_syserror ();
462     }
463   else
464     {
465       err = gpgconf_parse_option (opt, &opt->no_arg_value, field[8]);
466       if (err)
467         return err;
468     }
469
470   err = gpgconf_parse_option (opt, &opt->value, field[9]);
471   if (err)
472     return err;
473
474   return 0;
475 }
476
477
478 static gpgme_error_t
479 gpgconf_conf_load (void *engine, gpgme_conf_comp_t *comp_p)
480 {
481   gpgme_error_t err;
482   gpgme_conf_comp_t comp = NULL;
483   gpgme_conf_comp_t cur_comp;
484
485   *comp_p = NULL;
486
487   err = gpgconf_read (engine, "--list-components", NULL,
488                       gpgconf_config_load_cb, &comp);
489   if (err)
490     {
491       gpgconf_release (comp);
492       return err;
493     }
494
495   cur_comp = comp;
496   while (!err && cur_comp)
497     {
498       err = gpgconf_read (engine, "--list-options", cur_comp->name,
499                           gpgconf_config_load_cb2, cur_comp);
500       cur_comp = cur_comp->next;
501     }
502
503   if (err)
504     {
505       gpgconf_release (comp);
506       return err;
507     }
508
509   *comp_p = comp;
510   return 0;
511 }
512
513
514 \f
515 gpgme_error_t
516 _gpgme_conf_arg_new (gpgme_conf_arg_t *arg_p,
517                      gpgme_conf_type_t type, void *value)
518 {
519   gpgme_conf_arg_t arg;
520
521   arg = calloc (1, sizeof (*arg));
522   if (!arg)
523     return gpg_error_from_syserror ();
524
525   if (!value)
526     arg->no_arg = 1;
527   else
528     {
529       switch (type)
530         {
531         case GPGME_CONF_NONE:
532         case GPGME_CONF_UINT32:
533           arg->value.uint32 = *((unsigned int *) value);
534           break;
535           
536         case GPGME_CONF_INT32:
537           arg->value.int32 = *((int *) value);
538           break;
539           
540         case GPGME_CONF_STRING:
541         case GPGME_CONF_PATHNAME:
542         case GPGME_CONF_LDAP_SERVER:
543           arg->value.string = strdup (value);
544           if (!arg->value.string)
545             {
546               free (arg);
547               return gpg_error_from_syserror ();
548             }
549           break;
550           
551         default:
552           free (arg);
553           return gpg_error (GPG_ERR_INV_VALUE);
554         }
555     }
556
557   *arg_p = arg;
558   return 0;
559 }
560
561
562 void
563 _gpgme_conf_arg_release (gpgme_conf_arg_t arg, gpgme_conf_type_t type)
564 {
565   switch (type)
566     {
567     case GPGME_CONF_NONE:
568     case GPGME_CONF_UINT32:
569     case GPGME_CONF_INT32:
570     case GPGME_CONF_STRING:
571     default:
572       break;
573        
574     case GPGME_CONF_PATHNAME:
575     case GPGME_CONF_LDAP_SERVER:
576       type = GPGME_CONF_STRING;
577       break;
578     }
579
580   release_arg (arg, type);
581 }
582
583
584 gpgme_error_t
585 _gpgme_conf_opt_change (gpgme_conf_opt_t opt, int reset, gpgme_conf_arg_t arg)
586 {
587   if (opt->new_value)
588     release_arg (opt->new_value, opt->alt_type);
589
590   if (reset)
591     {
592       opt->new_value = NULL;
593       opt->change_value = 0;
594     }
595   else
596     {
597       opt->new_value = arg;
598       opt->change_value = 1;
599     }
600   return 0;
601 }
602
603 \f
604 /* FIXME: Major problem: We don't get errors from gpgconf.  */
605
606 static gpgme_error_t
607 gpgconf_write (void *engine, char *arg1, char *arg2, gpgme_data_t conf)
608 {
609   struct engine_gpgconf *gpgconf = engine;
610   gpgme_error_t err = 0;
611 #define BUFLEN 1024
612   char buf[BUFLEN];
613   int buflen = 0;
614   char *argv[] = { NULL /* file_name */, arg1, arg2, 0 };
615   int rp[2];
616   struct spawn_fd_item_s pfd[] = { {1, -1}, {-1, -1} };
617   struct spawn_fd_item_s cfd[] = { {-1, 0 /* STDIN_FILENO */}, {-1, -1} };
618   int status;
619   int nwrite;
620
621   /* FIXME: Deal with engine->home_dir.  */
622
623   /* _gpgme_engine_new guarantees that this is not NULL.  */
624   argv[0] = gpgconf->file_name;
625   argv[0] = "/home/marcus/g10/install/bin/gpgconf";
626
627   if (_gpgme_io_pipe (rp, 0) < 0)
628     return gpg_error_from_syserror ();
629
630   pfd[0].fd = rp[0];
631   cfd[0].fd = rp[0];
632
633   status = _gpgme_io_spawn (gpgconf->file_name, argv, cfd, pfd);
634   if (status < 0)
635     {
636       _gpgme_io_close (rp[0]);
637       _gpgme_io_close (rp[1]);
638       return gpg_error_from_syserror ();
639     }
640
641   for (;;)
642     {
643       if (buflen == 0)
644         {
645           do
646             {
647               buflen = gpgme_data_read (conf, buf, BUFLEN);
648             }
649           while (buflen < 0 && errno == EAGAIN);
650
651           if (buflen < 0)
652             {
653               err = gpg_error_from_syserror ();
654               _gpgme_io_close (rp[1]);
655               return err;
656             }
657           else if (buflen == 0)
658             {
659               /* All is written.  */
660               _gpgme_io_close (rp[1]);
661               return 0;
662             }
663         }
664
665       do
666         {
667           nwrite = _gpgme_io_write (rp[1], buf, buflen);
668         }
669       while (nwrite < 0 && errno == EAGAIN);
670
671       if (nwrite > 0)
672         {
673           buflen -= nwrite;
674           if (buflen > 0)
675             memmove (&buf[0], &buf[nwrite], buflen);
676         }
677       else if (nwrite < 0)
678         {
679           _gpgme_io_close (rp[1]);
680           return gpg_error_from_syserror ();
681         }
682     }
683
684   return 0;
685 }
686
687
688 static gpgme_error_t
689 arg_to_data (gpgme_data_t conf, gpgme_conf_opt_t option, gpgme_conf_arg_t arg)
690 {
691   gpgme_error_t err = 0;
692   int amt = 0;
693   char buf[16];
694
695   while (amt >= 0 && arg)
696     {
697       switch (option->alt_type)
698         {
699         case GPGME_CONF_NONE:
700         case GPGME_CONF_UINT32:
701         default:
702           snprintf (buf, sizeof (buf), "%u", arg->value.uint32);
703           buf[sizeof (buf) - 1] = '\0';
704           amt = gpgme_data_write (conf, buf, strlen (buf));
705           break;
706           
707         case GPGME_CONF_INT32:
708           snprintf (buf, sizeof (buf), "%i", arg->value.uint32);
709           buf[sizeof (buf) - 1] = '\0';
710           amt = gpgme_data_write (conf, buf, strlen (buf));
711           break;
712           
713         case GPGME_CONF_STRING:
714         case GPGME_CONF_PATHNAME:
715         case GPGME_CONF_LDAP_SERVER:
716           /* One quote character, and three times to allow
717              for percent escaping.  */
718           {
719             char *ptr = arg->value.string;
720             amt = gpgme_data_write (conf, "\"", 1);
721             if (amt < 0)
722               break;
723
724             while (!err && *ptr)
725               {
726                 switch (*ptr)
727                   {
728                   case '%':
729                     amt = gpgme_data_write (conf, "%25", 3);
730                     break;
731
732                   case ':':
733                     amt = gpgme_data_write (conf, "%3a", 3);
734                     break;
735
736                   case ',':
737                     amt = gpgme_data_write (conf, "%2c", 3);
738                     break;
739
740                   default:
741                     amt = gpgme_data_write (conf, ptr, 1);
742                   }
743                 ptr++;
744               }
745           }
746           break;
747         }
748
749       if (amt < 0)
750         break;
751
752       arg = arg->next;
753       /* Comma separator.  */
754       if (arg)
755         amt = gpgme_data_write (conf, ",", 1);
756     }
757
758   if (amt < 0)
759     return gpg_error_from_syserror ();
760   
761   return 0;
762 }
763
764
765 static gpgme_error_t
766 gpgconf_conf_save (void *engine, gpgme_conf_comp_t comp)
767 {
768   gpgme_error_t err;
769   int amt = 0;
770   /* We use a data object to store the new configuration.  */
771   gpgme_data_t conf;
772   gpgme_conf_opt_t option;
773   int something_changed = 0;
774
775   err = gpgme_data_new (&conf);
776   if (err)
777     return err;
778
779   option = comp->options;
780   while (!err && amt >= 0 && option)
781     {
782       if (option->change_value)
783         {
784           unsigned int flags = 0;
785           char buf[16];
786
787           something_changed = 1;
788
789           amt = gpgme_data_write (conf, option->name, strlen (option->name));
790           if (amt >= 0)
791             amt = gpgme_data_write (conf, ":", 1);
792           if (amt < 0)
793             break;
794
795           if (!option->new_value)
796             flags |= GPGME_CONF_DEFAULT;
797           snprintf (buf, sizeof (buf), "%u", flags);
798           buf[sizeof (buf) - 1] = '\0';
799
800           amt = gpgme_data_write (conf, buf, strlen (buf));
801           if (amt >= 0)
802             amt = gpgme_data_write (conf, ":", 1);
803           if (amt < 0)
804             break;
805
806           if (option->new_value)
807             {
808               err = arg_to_data (conf, option, option->new_value);
809               if (err)
810                 break;
811             }
812           amt = gpgme_data_write (conf, "\n", 1);
813         }
814       option = option->next;
815     }
816   if (!err && amt < 0)
817     err = gpg_error_from_syserror ();
818   if (err || !something_changed)
819     goto bail;
820
821   err = gpgme_data_seek (conf, 0, SEEK_SET);
822   if (err)
823     goto bail;
824
825   err = gpgconf_write (engine, "--change-options", comp->name, conf);
826  bail:
827   gpgme_data_release (conf);
828   return err;
829 }
830
831
832 static void
833 gpgconf_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
834 {
835   /* Nothing to do.  */
836 }
837
838 \f
839 /* Currently, we do not use the engine interface for the various
840    operations.  */
841 void
842 _gpgme_conf_release (gpgme_conf_comp_t conf)
843 {
844   gpgconf_config_release (conf);
845 }
846
847 \f
848 struct engine_ops _gpgme_engine_ops_gpgconf =
849   {
850     /* Static functions.  */
851     _gpgme_get_gpgconf_path,
852     gpgconf_get_version,
853     gpgconf_get_req_version,
854     gpgconf_new,
855
856     /* Member functions.  */
857     gpgconf_release,
858     NULL,               /* reset */
859     NULL,               /* set_status_handler */
860     NULL,               /* set_command_handler */
861     NULL,               /* set_colon_line_handler */
862     NULL,               /* set_locale */
863     NULL,               /* decrypt */
864     NULL,               /* delete */
865     NULL,               /* edit */
866     NULL,               /* encrypt */
867     NULL,               /* encrypt_sign */
868     NULL,               /* export */
869     NULL,               /* export_ext */
870     NULL,               /* genkey */
871     NULL,               /* import */
872     NULL,               /* keylist */
873     NULL,               /* keylist_ext */
874     NULL,               /* sign */
875     NULL,               /* trustlist */
876     NULL,               /* verify */
877     NULL,               /* getauditlog */
878     gpgconf_conf_load,
879     gpgconf_conf_save,
880     gpgconf_set_io_cbs,
881     NULL,               /* io_event */
882     NULL                /* cancel */
883   };