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