2002-07-29 Marcus Brinkmann <marcus@g10code.de>
[gpgme.git] / gpgme / rungpg.c
1 /* rungpg.c 
2  *      Copyright (C) 2000 Werner Koch (dd9jn)
3  *      Copyright (C) 2001, 2002 g10 Code GmbH
4  *
5  * This file is part of GPGME.
6  *
7  * GPGME is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * GPGME is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
20  */
21
22 #include <config.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <assert.h>
27 #include <errno.h>
28 #include <time.h>
29 #include <sys/time.h>
30 #include <sys/types.h>
31 #include <signal.h>
32 #include <fcntl.h>
33 #include "unistd.h"
34
35 #include "gpgme.h"
36 #include "util.h"
37 #include "ops.h"
38 #include "wait.h"
39 #include "rungpg.h"
40 #include "context.h"  /*temp hack until we have GpmeData methods to do I/O */
41 #include "io.h"
42 #include "sema.h"
43
44 #include "status-table.h"
45
46
47 /* This type is used to build a list of gpg arguments and
48  * data sources/sinks */
49 struct arg_and_data_s {
50     struct arg_and_data_s *next;
51     GpgmeData data;  /* If this is not NULL .. */
52     int dup_to;
53     int print_fd;    /* print the fd number and not the special form of it */
54     char arg[1];     /* .. this is used */
55 };
56
57 struct fd_data_map_s
58 {
59   GpgmeData data;
60   int inbound;  /* true if this is used for reading from gpg */
61   int dup_to;
62   int fd;       /* the fd to use */
63   int peer_fd;  /* the outher side of the pipe */
64   void *tag;
65 };
66
67
68 struct gpg_object_s
69 {
70   struct arg_and_data_s *arglist;
71   struct arg_and_data_s **argtail;
72   int arg_error;  
73
74   struct
75   {
76     int fd[2];  
77     size_t bufsize;
78     char *buffer;
79     size_t readpos;
80     int eof;
81     GpgStatusHandler fnc;
82     void *fnc_value;
83     void *tag;
84   } status;
85
86   /* This is a kludge - see the comment at gpg_colon_line_handler */
87   struct
88   {
89     int fd[2];  
90     size_t bufsize;
91     char *buffer;
92     size_t readpos;
93     int eof;
94     GpgColonLineHandler fnc;  /* this indicate use of this structrue */
95     void *fnc_value;
96     void *tag;
97     int simple;
98   } colon;
99
100   char **argv;  
101   struct fd_data_map_s *fd_data_map;
102
103   int pid; /* we can't use pid_t because we don't use it in Windoze */
104
105   /* stuff needed for pipemode */
106   struct
107   {
108     int used;
109     int active;
110     GpgmeData sig;
111     GpgmeData text;
112     int stream_started;
113   } pm;
114
115   /* stuff needed for interactive (command) mode */
116   struct
117   {
118     int used;
119     int fd;
120     int idx;            /* Index in fd_data_map */
121     GpgmeData cb_data;   /* hack to get init the above idx later */
122     GpgmeStatusCode code;  /* last code */
123     char *keyword;       /* what has been requested (malloced) */
124     GpgCommandHandler fnc; 
125     void *fnc_value;
126     /* The kludges never end.  This is used to couple command handlers
127        with output data in edit key mode.  */
128     GpgmeData linked_data;
129     int linked_idx;
130   } cmd;
131
132   struct GpgmeIOCbs io_cbs;
133 };
134
135 static void free_argv (char **argv);
136 static void free_fd_data_map (struct fd_data_map_s *fd_data_map);
137
138 static void gpg_status_handler (void *opaque, int fd);
139 static GpgmeError read_status (GpgObject gpg);
140
141 static void gpg_colon_line_handler (void *opaque, int fd);
142 static GpgmeError read_colon_line (GpgObject gpg);
143
144 static int pipemode_cb (void *opaque, char *buffer, size_t length,
145                         size_t *nread);
146 static int command_cb (void *opaque, char *buffer, size_t length,
147                        size_t *nread);
148
149 static void
150 close_notify_handler (int fd, void *opaque)
151 {
152   GpgObject gpg = opaque;
153   int possibly_done = 0;
154   int not_done = 0;
155   assert (fd != -1);
156
157   if (gpg->status.fd[0] == fd)
158     {
159       if (gpg->status.tag)
160         {
161           (*gpg->io_cbs.remove) (gpg->status.tag);
162           possibly_done = 1;
163         }
164       gpg->status.fd[0] = -1;
165     }
166   else if (gpg->status.fd[1] == fd)
167     gpg->status.fd[1] = -1;
168   else if (gpg->colon.fd[0] == fd)
169     {
170       if (gpg->colon.tag)
171         {
172           (*gpg->io_cbs.remove) (gpg->colon.tag);
173           possibly_done = 1;
174         }
175       gpg->colon.fd[0] = -1;
176     }
177   else if (gpg->colon.fd[1] == fd)
178     gpg->colon.fd[1] = -1;
179   else if (gpg->fd_data_map)
180     {
181       int i;
182
183       for (i = 0; gpg->fd_data_map[i].data; i++)
184         {
185           if (gpg->fd_data_map[i].fd == fd)
186             {
187               if (gpg->fd_data_map[i].tag)
188                 {
189                   (*gpg->io_cbs.remove) (gpg->fd_data_map[i].tag);
190                   possibly_done = 1;
191                 }
192               gpg->fd_data_map[i].fd = -1;
193               break;
194             }
195           if (gpg->fd_data_map[i].peer_fd == fd)
196             {
197               gpg->fd_data_map[i].peer_fd = -1;
198               break;
199             }
200         }
201     }
202   if (!possibly_done)
203     not_done = 1;
204   else if (gpg->status.fd[0] != -1)
205     not_done = 1;
206   else if (gpg->colon.fd[0] != -1)
207     not_done = 1;
208   else if (gpg->fd_data_map)
209     {
210       int i;
211
212       for (i = 0; gpg->fd_data_map[i].data; i++)
213         if (gpg->fd_data_map[i].fd != -1)
214           {
215             not_done = 1;
216             break;
217           }
218     }
219   if (!not_done)
220     _gpgme_gpg_io_event (gpg, GPGME_EVENT_DONE, NULL);
221 }
222
223 const char *
224 _gpgme_gpg_get_version (void)
225 {
226   static const char *gpg_version;
227   DEFINE_STATIC_LOCK (gpg_version_lock);
228
229   LOCK (gpg_version_lock);
230   if (!gpg_version)
231     gpg_version = _gpgme_get_program_version (_gpgme_get_gpg_path ());
232   UNLOCK (gpg_version_lock);
233   return gpg_version;
234 }
235
236 GpgmeError
237 _gpgme_gpg_check_version (void)
238 {
239   return _gpgme_compare_versions (_gpgme_gpg_get_version (),
240                                   NEED_GPG_VERSION)
241     ? 0 : mk_error (Invalid_Engine);
242 }
243
244 GpgmeError
245 _gpgme_gpg_new (GpgObject *r_gpg)
246 {
247   GpgObject gpg;
248   int rc = 0;
249
250   gpg = xtrycalloc (1, sizeof *gpg);
251   if (!gpg)
252     {
253       rc = mk_error (Out_Of_Core);
254       goto leave;
255     }
256   gpg->argtail = &gpg->arglist;
257
258   gpg->status.fd[0] = -1;
259   gpg->status.fd[1] = -1;
260   gpg->colon.fd[0] = -1;
261   gpg->colon.fd[1] = -1;
262   gpg->cmd.fd = -1;
263   gpg->cmd.idx = -1;
264   gpg->cmd.linked_data = NULL;
265   gpg->cmd.linked_idx = -1;
266
267   gpg->pid = -1;
268
269   /* Allocate the read buffer for the status pipe.  */
270   gpg->status.bufsize = 1024;
271   gpg->status.readpos = 0;
272   gpg->status.buffer = xtrymalloc (gpg->status.bufsize);
273   if (!gpg->status.buffer)
274     {
275       rc = mk_error (Out_Of_Core);
276       goto leave;
277     }
278   /* In any case we need a status pipe - create it right here and
279      don't handle it with our generic GpgmeData mechanism.  */
280   if (_gpgme_io_pipe (gpg->status.fd, 1) == -1)
281     {
282       rc = mk_error (Pipe_Error);
283       goto leave;
284     }
285   if (_gpgme_io_set_close_notify (gpg->status.fd[0],
286                                   close_notify_handler, gpg)
287       || _gpgme_io_set_close_notify (gpg->status.fd[1],
288                                      close_notify_handler, gpg))
289     {
290       rc = mk_error (General_Error);
291       goto leave;
292     }
293   gpg->status.eof = 0;
294   _gpgme_gpg_add_arg (gpg, "--status-fd");
295   {
296     char buf[25];
297     sprintf (buf, "%d", gpg->status.fd[1]);
298     _gpgme_gpg_add_arg (gpg, buf);
299   }
300   _gpgme_gpg_add_arg (gpg, "--no-tty");
301   _gpgme_gpg_add_arg (gpg, "--charset");
302   _gpgme_gpg_add_arg (gpg, "utf8");
303
304  leave:
305   if (rc)
306     {
307       _gpgme_gpg_release (gpg);
308       *r_gpg = NULL;
309     }
310   else
311     *r_gpg = gpg;
312   return rc;
313 }
314
315
316 void
317 _gpgme_gpg_release (GpgObject gpg)
318 {
319   if (!gpg)
320     return;
321
322   while (gpg->arglist)
323     {
324       struct arg_and_data_s *next = gpg->arglist->next;
325
326       xfree (gpg->arglist);
327       gpg->arglist = next;
328     }
329
330   xfree (gpg->status.buffer);
331   xfree (gpg->colon.buffer);
332   if (gpg->argv)
333     free_argv (gpg->argv);
334   gpgme_data_release (gpg->cmd.cb_data);
335   xfree (gpg->cmd.keyword);
336
337   if (gpg->status.fd[0] != -1)
338     _gpgme_io_close (gpg->status.fd[0]);
339   if (gpg->status.fd[1] != -1)
340     _gpgme_io_close (gpg->status.fd[1]);
341   if (gpg->colon.fd[0] != -1)
342     _gpgme_io_close (gpg->colon.fd[0]);
343   if (gpg->colon.fd[1] != -1)
344     _gpgme_io_close (gpg->colon.fd[1]);
345   free_fd_data_map (gpg->fd_data_map);
346   if (gpg->cmd.fd != -1)
347     _gpgme_io_close (gpg->cmd.fd);
348   if (gpg->pid != -1)
349     _gpgme_engine_add_child_to_reap_list (gpg, sizeof *gpg, gpg->pid);
350   else
351     xfree (gpg);
352 }
353
354 void
355 _gpgme_gpg_enable_pipemode ( GpgObject gpg )
356 {
357     gpg->pm.used = 1;
358     assert ( !gpg->pm.sig );
359     assert ( !gpg->pm.text );
360 }
361     
362 GpgmeError
363 _gpgme_gpg_add_arg ( GpgObject gpg, const char *arg )
364 {
365     struct arg_and_data_s *a;
366
367     assert (gpg);
368     assert (arg);
369
370     if (gpg->pm.active)
371         return 0;
372
373     a = xtrymalloc ( sizeof *a + strlen (arg) );
374     if ( !a ) {
375         gpg->arg_error = 1;
376         return mk_error(Out_Of_Core);
377     }
378     a->next = NULL;
379     a->data = NULL;
380     a->dup_to = -1;
381     strcpy ( a->arg, arg );
382     *gpg->argtail = a;
383     gpg->argtail = &a->next;
384     return 0;
385 }
386
387 GpgmeError
388 _gpgme_gpg_add_data ( GpgObject gpg, GpgmeData data, int dup_to )
389 {
390     struct arg_and_data_s *a;
391
392     assert (gpg);
393     assert (data);
394     if (gpg->pm.active)
395         return 0;
396
397     a = xtrymalloc ( sizeof *a - 1 );
398     if ( !a ) {
399         gpg->arg_error = 1;
400         return mk_error(Out_Of_Core);
401     }
402     a->next = NULL;
403     a->data = data;
404     if ( dup_to == -2 ) {
405         a->print_fd = 1;
406         a->dup_to = -1;
407     }
408     else {
409         a->print_fd = 0;
410         a->dup_to = dup_to;
411     }
412     *gpg->argtail = a;
413     gpg->argtail = &a->next;
414     return 0;
415 }
416
417 GpgmeError
418 _gpgme_gpg_add_pm_data ( GpgObject gpg, GpgmeData data, int what )
419 {
420     GpgmeError rc=0;
421
422     assert ( gpg->pm.used );
423     
424     if ( !what ) {
425         /* the signature */
426         assert ( !gpg->pm.sig );
427         gpg->pm.sig = data;
428     }
429     else if (what == 1) {
430         /* the signed data */
431         assert ( !gpg->pm.text );
432         gpg->pm.text = data;
433     }
434     else {
435         assert (0);
436     }
437
438     if ( gpg->pm.sig && gpg->pm.text ) {
439         if ( !gpg->pm.active ) {
440             /* create the callback handler and connect it to stdin */
441             GpgmeData tmp;
442             
443             rc = gpgme_data_new_with_read_cb ( &tmp, pipemode_cb, gpg );
444             if (!rc )
445                 rc = _gpgme_gpg_add_data (gpg, tmp, 0);
446         }
447         if ( !rc ) {
448             /* here we can reset the handler stuff */
449             gpg->pm.stream_started = 0;
450         }
451     }
452
453     return rc;
454 }
455
456 /*
457  * Note, that the status_handler is allowed to modifiy the args value
458  */
459 void
460 _gpgme_gpg_set_status_handler ( GpgObject gpg,
461                                 GpgStatusHandler fnc, void *fnc_value ) 
462 {
463     assert (gpg);
464     if (gpg->pm.active)
465         return;
466
467     gpg->status.fnc = fnc;
468     gpg->status.fnc_value = fnc_value;
469 }
470
471 /* Kludge to process --with-colon output */
472 GpgmeError
473 _gpgme_gpg_set_colon_line_handler ( GpgObject gpg,
474                                     GpgColonLineHandler fnc, void *fnc_value ) 
475 {
476     assert (gpg);
477     if (gpg->pm.active)
478         return 0;
479
480     gpg->colon.bufsize = 1024;
481     gpg->colon.readpos = 0;
482     gpg->colon.buffer = xtrymalloc (gpg->colon.bufsize);
483     if (!gpg->colon.buffer) {
484         return mk_error (Out_Of_Core);
485     }
486     if (_gpgme_io_pipe (gpg->colon.fd, 1) == -1) {
487         xfree (gpg->colon.buffer); gpg->colon.buffer = NULL;
488         return mk_error (Pipe_Error);
489     }
490     if ( _gpgme_io_set_close_notify (gpg->colon.fd[0],
491                                      close_notify_handler, gpg)
492          ||  _gpgme_io_set_close_notify (gpg->colon.fd[1],
493                                          close_notify_handler, gpg) ) {
494         return mk_error (General_Error);
495     }
496     gpg->colon.eof = 0;
497     gpg->colon.fnc = fnc;
498     gpg->colon.fnc_value = fnc_value;
499     gpg->colon.simple = 0;
500     return 0;
501 }
502
503
504 GpgmeError
505 _gpgme_gpg_set_simple_line_handler ( GpgObject gpg,
506                                      GpgColonLineHandler fnc,
507                                      void *fnc_value ) 
508 {
509     GpgmeError err;
510
511     err = _gpgme_gpg_set_colon_line_handler (gpg, fnc, fnc_value);
512     if (!err)
513         gpg->colon.simple = 1;
514     return err;
515 }
516
517
518 /* 
519  * The Fnc will be called to get a value for one of the commands with
520  * a key KEY.  If the Code pssed to FNC is 0, the function may release
521  * resources associated with the returned value from another call.  To
522  * match such a second call to a first call, the returned value from
523  * the first call is passed as keyword.
524  */
525
526 GpgmeError
527 _gpgme_gpg_set_command_handler (GpgObject gpg,
528                                 GpgCommandHandler fnc, void *fnc_value,
529                                 GpgmeData linked_data)
530 {
531   GpgmeData tmp;
532   GpgmeError err;
533
534   assert (gpg);
535   if (gpg->pm.active)
536     return 0;
537
538   err = gpgme_data_new_with_read_cb (&tmp, command_cb, gpg);
539   if (err)
540     return err;
541         
542   _gpgme_gpg_add_arg ( gpg, "--command-fd" );
543   _gpgme_gpg_add_data (gpg, tmp, -2);
544   gpg->cmd.cb_data = tmp;
545   gpg->cmd.fnc = fnc;
546   gpg->cmd.fnc_value = fnc_value;
547   gpg->cmd.linked_data = linked_data;
548   gpg->cmd.used = 1;
549   return 0;
550 }
551
552
553 static void
554 free_argv ( char **argv )
555 {
556     int i;
557
558     for (i=0; argv[i]; i++ )
559         xfree (argv[i]);
560     xfree (argv);
561 }
562
563 static void
564 free_fd_data_map ( struct fd_data_map_s *fd_data_map )
565 {
566     int i;
567
568     if ( !fd_data_map )
569         return;
570
571     for (i=0; fd_data_map[i].data; i++ ) {
572         if ( fd_data_map[i].fd != -1 )
573             _gpgme_io_close (fd_data_map[i].fd);
574         if ( fd_data_map[i].peer_fd != -1 )
575             _gpgme_io_close (fd_data_map[i].peer_fd);
576         /* don't release data because this is only a reference */
577     }
578     xfree (fd_data_map);
579 }
580
581
582 static GpgmeError
583 build_argv (GpgObject gpg)
584 {
585   struct arg_and_data_s *a;
586   struct fd_data_map_s *fd_data_map;
587   size_t datac=0, argc=0;  
588   char **argv;
589   int need_special = 0;
590   int use_agent = 0;
591   char *p;
592
593   /* We don't want to use the agent with a malformed environment
594      variable.  This is only a very basic test but sufficient to make
595      our life in the regression tests easier. */
596   p = getenv ("GPG_AGENT_INFO");
597   use_agent = (p && strchr (p, ':'));
598        
599   if (gpg->argv)
600     {
601       free_argv (gpg->argv);
602       gpg->argv = NULL;
603     }
604   if (gpg->fd_data_map)
605     {
606       free_fd_data_map (gpg->fd_data_map);
607       gpg->fd_data_map = NULL;
608     }
609
610   argc++;       /* For argv[0].  */
611   for (a = gpg->arglist; a; a = a->next)
612     {
613       argc++;
614       if (a->data)
615         {
616           /*fprintf (stderr, "build_argv: data\n" );*/
617           datac++;
618           if (a->dup_to == -1 && !a->print_fd)
619             need_special = 1;
620         }
621       else
622         {
623           /*   fprintf (stderr, "build_argv: arg=`%s'\n", a->arg );*/
624         }
625     }
626   if (need_special)
627     argc++;
628   if (use_agent)
629     argc++;
630   if (!gpg->cmd.used)
631     argc++;
632   argc += 2; /* --comment */
633
634   argv = xtrycalloc (argc + 1, sizeof *argv);
635   if (!argv)
636     return mk_error (Out_Of_Core);
637   fd_data_map = xtrycalloc (datac + 1, sizeof *fd_data_map);
638   if (!fd_data_map)
639     {
640       free_argv (argv);
641       return mk_error (Out_Of_Core);
642     }
643
644   argc = datac = 0;
645   argv[argc] = xtrystrdup ("gpg"); /* argv[0] */
646   if (!argv[argc])
647     {
648       xfree (fd_data_map);
649       free_argv (argv);
650       return mk_error (Out_Of_Core);
651     }
652   argc++;
653   if (need_special)
654     {
655       argv[argc] = xtrystrdup ("--enable-special-filenames");
656       if (!argv[argc])
657         {
658           xfree (fd_data_map);
659           free_argv (argv);
660           return mk_error (Out_Of_Core);
661         }
662       argc++;
663     }
664   if (use_agent)
665     {
666       argv[argc] = xtrystrdup ("--use-agent");
667       if (!argv[argc])
668         {
669           xfree (fd_data_map);
670           free_argv (argv);
671           return mk_error (Out_Of_Core);
672         }
673       argc++;
674     }
675   if (!gpg->cmd.used)
676     {
677       argv[argc] = xtrystrdup ("--batch");
678       if (!argv[argc])
679         {
680           xfree (fd_data_map);
681           free_argv (argv);
682           return mk_error (Out_Of_Core);
683         }
684       argc++;
685     }
686   argv[argc] = xtrystrdup ("--comment");
687   if (!argv[argc])
688     {
689       xfree (fd_data_map);
690       free_argv (argv);
691       return mk_error (Out_Of_Core);
692     }
693   argc++;
694   argv[argc] = xtrystrdup ("");
695   if (!argv[argc])
696     {
697       xfree (fd_data_map);
698       free_argv (argv);
699       return mk_error (Out_Of_Core);
700     }
701   argc++;
702   for (a = gpg->arglist; a; a = a->next)
703     {
704       if (a->data)
705         {
706           switch (_gpgme_data_get_mode (a->data))
707             {
708             case GPGME_DATA_MODE_NONE:
709             case GPGME_DATA_MODE_INOUT:
710               xfree (fd_data_map);
711               free_argv (argv);
712               return mk_error (Invalid_Mode);
713             case GPGME_DATA_MODE_IN:
714               /* Create a pipe to read from gpg.  */
715               fd_data_map[datac].inbound = 1;
716               break;
717             case GPGME_DATA_MODE_OUT:
718               /* Create a pipe to pass it down to gpg.  */
719               fd_data_map[datac].inbound = 0;
720               break;
721             }
722
723           switch (gpgme_data_get_type (a->data))
724             {
725             case GPGME_DATA_TYPE_NONE:
726               if (fd_data_map[datac].inbound)
727                 break;  /* Allowed.  */
728               xfree (fd_data_map);
729               free_argv (argv);
730               return mk_error (Invalid_Type);
731             case GPGME_DATA_TYPE_MEM:
732             case GPGME_DATA_TYPE_CB:
733               break;
734             case GPGME_DATA_TYPE_FD:
735             case GPGME_DATA_TYPE_FILE:
736               xfree (fd_data_map);
737               free_argv (argv);
738               return mk_error (Not_Implemented);
739             }
740   
741           /* Create a pipe.  */
742           {   
743             int fds[2];
744             
745             if (_gpgme_io_pipe (fds, fd_data_map[datac].inbound ? 1 : 0)
746                 == -1)
747               {
748                 xfree (fd_data_map);
749                 free_argv (argv);
750                 return mk_error (Pipe_Error);
751               }
752             if (_gpgme_io_set_close_notify (fds[0],
753                                             close_notify_handler, gpg)
754                 || _gpgme_io_set_close_notify (fds[1],
755                                                close_notify_handler,
756                                                gpg))
757               {
758                 return mk_error (General_Error);
759               }
760             /* If the data_type is FD, we have to do a dup2 here.  */
761             if (fd_data_map[datac].inbound)
762               {
763                 fd_data_map[datac].fd       = fds[0];
764                 fd_data_map[datac].peer_fd  = fds[1];
765               }
766             else
767               {
768                 fd_data_map[datac].fd       = fds[1];
769                 fd_data_map[datac].peer_fd  = fds[0];
770               }
771           }
772
773           /* Hack to get hands on the fd later.  */
774           if (gpg->cmd.used)
775             {
776               if (gpg->cmd.cb_data == a->data)
777                 {
778                   assert (gpg->cmd.idx == -1);
779                   gpg->cmd.idx = datac;
780                 }
781               else if (gpg->cmd.linked_data == a->data)
782                 {
783                   assert (gpg->cmd.linked_idx == -1);
784                   gpg->cmd.linked_idx = datac;
785                 }
786             }
787
788           fd_data_map[datac].data = a->data;
789           fd_data_map[datac].dup_to = a->dup_to;
790           if (a->dup_to == -1)
791             {
792               argv[argc] = xtrymalloc (25);
793               if (!argv[argc])
794                 {
795                   xfree (fd_data_map);
796                   free_argv (argv);
797                   return mk_error (Out_Of_Core);
798                 }
799               sprintf (argv[argc], 
800                        a->print_fd ? "%d" : "-&%d",
801                        fd_data_map[datac].peer_fd);
802               argc++;
803             }
804           datac++;
805         }
806       else
807         {
808           argv[argc] = xtrystrdup (a->arg);
809           if (!argv[argc])
810             {
811               xfree (fd_data_map);
812               free_argv (argv);
813               return mk_error (Out_Of_Core);
814             }
815             argc++;
816         }
817     }
818
819   gpg->argv = argv;
820   gpg->fd_data_map = fd_data_map;
821   return 0;
822 }
823
824 static GpgmeError
825 _gpgme_gpg_add_io_cb (GpgObject gpg, int fd, int dir,
826                       GpgmeIOCb handler, void *data, void **tag)
827 {
828   GpgmeError err;
829
830   err = (*gpg->io_cbs.add) (gpg->io_cbs.add_priv, fd, dir, handler, data, tag);
831   if (err)
832     return err;
833   if (!dir)
834     /* FIXME Kludge around poll() problem.  */
835     err = _gpgme_io_set_nonblocking (fd);
836   return err;
837 }
838
839 GpgmeError
840 _gpgme_gpg_spawn (GpgObject gpg, void *opaque)
841 {
842   GpgmeError rc;
843   int i, n;
844   int pid;
845   struct spawn_fd_item_s *fd_child_list, *fd_parent_list;
846
847   if (!gpg)
848     return mk_error (Invalid_Value);
849
850   if (! _gpgme_get_gpg_path ())
851     return mk_error (Invalid_Engine);
852
853   /* Kludge, so that we don't need to check the return code of all the
854      gpgme_gpg_add_arg().  we bail out here instead */
855   if (gpg->arg_error)
856     return mk_error (Out_Of_Core);
857
858   if (gpg->pm.active)
859     return 0;
860
861   rc = build_argv (gpg);
862   if (rc)
863     return rc;
864
865   n = 3; /* status_fd, colon_fd and end of list */
866   for (i = 0; gpg->fd_data_map[i].data; i++) 
867     n++;
868   fd_child_list = xtrycalloc (n + n, sizeof *fd_child_list);
869   if (!fd_child_list)
870     return mk_error (Out_Of_Core);
871   fd_parent_list = fd_child_list + n;
872
873   /* build the fd list for the child */
874   n = 0;
875   if (gpg->colon.fnc)
876     {
877       fd_child_list[n].fd = gpg->colon.fd[1]; 
878       fd_child_list[n].dup_to = 1; /* dup to stdout */
879       n++;
880     }
881   for (i = 0; gpg->fd_data_map[i].data; i++)
882     {
883       if (gpg->fd_data_map[i].dup_to != -1)
884         {
885           fd_child_list[n].fd = gpg->fd_data_map[i].peer_fd;
886           fd_child_list[n].dup_to = gpg->fd_data_map[i].dup_to;
887           n++;
888         }
889     }
890   fd_child_list[n].fd = -1;
891   fd_child_list[n].dup_to = -1;
892
893   /* Build the fd list for the parent.  */
894   n = 0;
895   if (gpg->status.fd[1] != -1)
896     {
897       fd_parent_list[n].fd = gpg->status.fd[1];
898       fd_parent_list[n].dup_to = -1;
899       n++;
900       gpg->status.fd[1] = -1;
901     }
902   if (gpg->colon.fd[1] != -1)
903     {
904       fd_parent_list[n].fd = gpg->colon.fd[1];
905       fd_parent_list[n].dup_to = -1;
906       n++;
907       gpg->colon.fd[1] = -1;
908     }
909   for (i = 0; gpg->fd_data_map[i].data; i++)
910     {
911       fd_parent_list[n].fd = gpg->fd_data_map[i].peer_fd;
912       fd_parent_list[n].dup_to = -1;
913       n++;
914       gpg->fd_data_map[i].peer_fd = -1;
915     }        
916   fd_parent_list[n].fd = -1;
917   fd_parent_list[n].dup_to = -1;
918
919   pid = _gpgme_io_spawn (_gpgme_get_gpg_path (),
920                          gpg->argv, fd_child_list, fd_parent_list);
921   xfree (fd_child_list);
922   if (pid == -1)
923     return mk_error (Exec_Error);
924
925   gpg->pid = pid;
926   if (gpg->pm.used)
927     gpg->pm.active = 1;
928
929   /*_gpgme_register_term_handler ( closure, closure_value, pid );*/
930
931   rc = _gpgme_gpg_add_io_cb (gpg, gpg->status.fd[0], 1,
932                              gpg_status_handler, gpg, &gpg->status.tag);
933   if (rc)
934     /* FIXME: kill the child */
935     return rc;
936
937   if (gpg->colon.fnc)
938     {
939       assert (gpg->colon.fd[0] != -1);
940       rc = _gpgme_gpg_add_io_cb (gpg, gpg->colon.fd[0], 1,
941                                  gpg_colon_line_handler, gpg,
942                                  &gpg->colon.tag);
943       if (rc)
944         /* FIXME: kill the child */
945         return rc;
946     }
947
948   for (i = 0; gpg->fd_data_map[i].data; i++)
949     {
950       if (gpg->cmd.used && i == gpg->cmd.idx)
951         {
952           /* Park the cmd fd.  */
953           gpg->cmd.fd = gpg->fd_data_map[i].fd;
954           gpg->fd_data_map[i].fd = -1;
955         }
956       else
957         {
958           rc = _gpgme_gpg_add_io_cb (gpg, gpg->fd_data_map[i].fd,
959                                      gpg->fd_data_map[i].inbound,
960                                      gpg->fd_data_map[i].inbound
961                                      ? _gpgme_data_inbound_handler
962                                      : _gpgme_data_outbound_handler,
963                                      gpg->fd_data_map[i].data,
964                                      &gpg->fd_data_map[i].tag);
965           
966           if (rc)
967             /* FIXME: kill the child */
968             return rc;
969         }
970     }
971   
972   /* fixme: check what data we can release here */
973   return 0;
974 }
975
976
977 static void
978 gpg_status_handler (void *opaque, int fd)
979 {
980   GpgObject gpg = opaque;
981   int err;
982
983   assert (fd == gpg->status.fd[0]);
984   err = read_status (gpg);
985   if (err)
986     {
987       /* XXX Horrible kludge.  We really must not make use of
988          fnc_value.  */
989       GpgmeCtx ctx = (GpgmeCtx) gpg->status.fnc_value;
990       ctx->error = err;
991       DEBUG1 ("gpg_handler: read_status problem %d\n - stop", err);
992       _gpgme_io_close (fd);
993       return;
994     }
995   if (gpg->status.eof)
996     _gpgme_io_close (fd);
997 }
998
999
1000 static int
1001 status_cmp (const void *ap, const void *bp)
1002 {
1003     const struct status_table_s *a = ap;
1004     const struct status_table_s *b = bp;
1005
1006     return strcmp (a->name, b->name);
1007 }
1008
1009
1010
1011 /*
1012  * Handle the status output of GnuPG.  This function does read entire
1013  * lines and passes them as C strings to the callback function (we can
1014  * use C Strings because the status output is always UTF-8 encoded).
1015  * Of course we have to buffer the lines to cope with long lines
1016  * e.g. with a large user ID.  Note: We can optimize this to only cope
1017  * with status line code we know about and skip all other stuff
1018  * without buffering (i.e. without extending the buffer).  */
1019 static GpgmeError
1020 read_status (GpgObject gpg)
1021 {
1022   char *p;
1023   int nread;
1024   size_t bufsize = gpg->status.bufsize; 
1025   char *buffer = gpg->status.buffer;
1026   size_t readpos = gpg->status.readpos; 
1027
1028   assert (buffer);
1029   if (bufsize - readpos < 256)
1030     { 
1031       /* Need more room for the read.  */
1032       bufsize += 1024;
1033       buffer = xtryrealloc (buffer, bufsize);
1034       if (!buffer)
1035         return mk_error (Out_Of_Core);
1036     }
1037
1038   nread = _gpgme_io_read (gpg->status.fd[0],
1039                           buffer + readpos, bufsize-readpos);
1040   if (nread == -1)
1041     return mk_error(Read_Error);
1042
1043   if (!nread)
1044     {
1045       gpg->status.eof = 1;
1046       if (gpg->status.fnc)
1047         gpg->status.fnc (gpg->status.fnc_value, GPGME_STATUS_EOF, "");
1048       return 0;
1049     }
1050
1051   while (nread > 0)
1052     {
1053       for (p = buffer + readpos; nread; nread--, p++)
1054         {
1055           if (*p == '\n')
1056             {
1057               /* (we require that the last line is terminated by a LF) */
1058               *p = 0;
1059               if (!strncmp (buffer, "[GNUPG:] ", 9)
1060                   && buffer[9] >= 'A' && buffer[9] <= 'Z')
1061                 {
1062                   struct status_table_s t, *r;
1063                   char *rest;
1064
1065                   rest = strchr (buffer + 9, ' ');
1066                   if (!rest)
1067                     rest = p; /* Set to an empty string.  */
1068                   else
1069                     *rest++ = 0;
1070                     
1071                   t.name = buffer+9;
1072                   /* (the status table has one extra element) */
1073                   r = bsearch (&t, status_table, DIM(status_table) - 1,
1074                                sizeof t, status_cmp);
1075                   if (r)
1076                     {
1077                       if (gpg->cmd.used
1078                           && (r->code == GPGME_STATUS_GET_BOOL
1079                               || r->code == GPGME_STATUS_GET_LINE
1080                               || r->code == GPGME_STATUS_GET_HIDDEN))
1081                         {
1082                           gpg->cmd.code = r->code;
1083                           xfree (gpg->cmd.keyword);
1084                           gpg->cmd.keyword = xtrystrdup (rest);
1085                           if (!gpg->cmd.keyword)
1086                             return mk_error (Out_Of_Core);
1087                           /* This should be the last thing we have
1088                              received and the next thing will be that
1089                              the command handler does its action.  */
1090                           if (nread > 1)
1091                             DEBUG0 ("ERROR, unexpected data in read_status");
1092
1093                           /* Before we can actually add the command
1094                              fd, we might have to flush the linked
1095                              output data pipe.  */
1096                           if (gpg->cmd.linked_idx != -1
1097                               && gpg->fd_data_map[gpg->cmd.linked_idx].fd != -1)
1098                             {
1099                               struct io_select_fd_s fds;
1100                               fds.fd = gpg->fd_data_map[gpg->cmd.linked_idx].fd;
1101                               fds.for_read = 1;
1102                               fds.for_write = 0;
1103                               fds.frozen = 0;
1104                               fds.opaque = NULL;
1105                               do
1106                                 {
1107                                   fds.signaled = 0;
1108                                   _gpgme_io_select (&fds, 1, 1);
1109                                   if (fds.signaled)
1110                                     _gpgme_data_inbound_handler
1111                                       (gpg->cmd.linked_data, fds.fd);
1112                                 }
1113                               while (fds.signaled);
1114                             }
1115
1116                           _gpgme_gpg_add_io_cb
1117                             (gpg, gpg->cmd.fd,
1118                              0, _gpgme_data_outbound_handler,
1119                              gpg->fd_data_map[gpg->cmd.idx].data,
1120                              &gpg->fd_data_map[gpg->cmd.idx].tag);
1121                           gpg->fd_data_map[gpg->cmd.idx].fd = gpg->cmd.fd;
1122                           gpg->cmd.fd = -1;
1123                         }
1124                       else if (gpg->status.fnc)
1125                         {
1126                           gpg->status.fnc (gpg->status.fnc_value, 
1127                                            r->code, rest);
1128                         }
1129                     
1130                       if (r->code == GPGME_STATUS_END_STREAM)
1131                         {
1132                           if (gpg->cmd.used)
1133                             {
1134                               /* XXX We must check if there are any
1135                                  more fds active after removing this
1136                                  one.  */
1137                               (*gpg->io_cbs.remove)
1138                                 (gpg->fd_data_map[gpg->cmd.idx].tag);
1139                               gpg->cmd.fd = gpg->fd_data_map[gpg->cmd.idx].fd;
1140                               gpg->fd_data_map[gpg->cmd.idx].fd = -1;
1141                             }
1142                         }
1143                     }
1144                 }
1145               /* To reuse the buffer for the next line we have to
1146                  shift the remaining data to the buffer start and
1147                  restart the loop Hmmm: We can optimize this function
1148                  by looking forward in the buffer to see whether a
1149                  second complete line is available and in this case
1150                  avoid the memmove for this line.  */
1151               nread--; p++;
1152               if (nread)
1153                 memmove (buffer, p, nread);
1154               readpos = 0;
1155               break; /* the for loop */
1156             }
1157           else
1158             readpos++;
1159         }
1160     } 
1161
1162   /* Update the gpg object.  */
1163   gpg->status.bufsize = bufsize;
1164   gpg->status.buffer = buffer;
1165   gpg->status.readpos = readpos;
1166   return 0;
1167 }
1168
1169
1170 /*
1171  * This colonline handler thing is not the clean way to do it.
1172  * It might be better to enhance the GpgmeData object to act as
1173  * a wrapper for a callback.  Same goes for the status thing.
1174  * For now we use this thing here becuase it is easier to implement.
1175  */
1176 static void
1177 gpg_colon_line_handler (void *opaque, int fd)
1178 {
1179   GpgObject gpg = opaque;
1180   GpgmeError rc = 0;
1181
1182   assert (fd == gpg->colon.fd[0]);
1183   rc = read_colon_line (gpg);
1184   if (rc)
1185     {
1186       DEBUG1 ("gpg_colon_line_handler: "
1187               "read problem %d\n - stop", rc);
1188       _gpgme_io_close (fd);
1189       return;
1190     }
1191   if (gpg->colon.eof)
1192     _gpgme_io_close (fd);
1193 }
1194
1195 static GpgmeError
1196 read_colon_line ( GpgObject gpg )
1197 {
1198     char *p;
1199     int nread;
1200     size_t bufsize = gpg->colon.bufsize; 
1201     char *buffer = gpg->colon.buffer;
1202     size_t readpos = gpg->colon.readpos; 
1203
1204     assert (buffer);
1205     if (bufsize - readpos < 256) { 
1206         /* need more room for the read */
1207         bufsize += 1024;
1208         buffer = xtryrealloc (buffer, bufsize);
1209         if ( !buffer ) 
1210             return mk_error (Out_Of_Core);
1211     }
1212     
1213
1214     nread = _gpgme_io_read ( gpg->colon.fd[0],
1215                              buffer+readpos, bufsize-readpos );
1216     if (nread == -1)
1217         return mk_error(Read_Error);
1218
1219     if (!nread) {
1220         gpg->colon.eof = 1;
1221         assert (gpg->colon.fnc);
1222         gpg->colon.fnc ( gpg->colon.fnc_value, NULL );
1223         return 0;
1224     }
1225
1226     while (nread > 0) {
1227         for (p = buffer + readpos; nread; nread--, p++) {
1228             if ( *p == '\n' ) {
1229                 /* (we require that the last line is terminated by a
1230                  * LF) and we skip empty lines.  Note: we use UTF8
1231                  * encoding and escaping of special characters
1232                  * We require at least one colon to cope with
1233                  * some other printed information.
1234                  */
1235                 *p = 0;
1236                 if ( gpg->colon.simple
1237                      || (*buffer && strchr (buffer, ':')) ) {
1238                     assert (gpg->colon.fnc);
1239                     gpg->colon.fnc ( gpg->colon.fnc_value, buffer );
1240                 }
1241             
1242                 /* To reuse the buffer for the next line we have to
1243                  * shift the remaining data to the buffer start and
1244                  * restart the loop Hmmm: We can optimize this
1245                  * function by looking forward in the buffer to see
1246                  * whether a second complete line is available and in
1247                  * this case avoid the memmove for this line.  */
1248                 nread--; p++;
1249                 if (nread)
1250                     memmove (buffer, p, nread);
1251                 readpos = 0;
1252                 break; /* the for loop */
1253             }
1254             else
1255                 readpos++;
1256         }
1257     } 
1258     
1259     /* Update the gpg object.  */
1260     gpg->colon.bufsize = bufsize;
1261     gpg->colon.buffer  = buffer;
1262     gpg->colon.readpos = readpos;
1263     return 0;
1264 }
1265
1266 static GpgmeError
1267 pipemode_copy (char *buffer, size_t length, size_t *nread, GpgmeData data )
1268 {
1269     GpgmeError err;
1270     size_t nbytes;
1271     char tmp[1000], *s, *d;
1272
1273     /* we can optimize this whole thing but for now we just
1274      * return after each escape character */
1275     if (length > 990)
1276         length = 990;
1277
1278     err = gpgme_data_read ( data, tmp, length, &nbytes );
1279     if (err)
1280         return err;
1281     for (s=tmp, d=buffer; nbytes; s++, nbytes--) {
1282         *d++ = *s;
1283         if (*s == '@' ) {
1284             *d++ = '@';
1285             break;
1286         }
1287     }
1288     *nread = d - buffer;
1289     return 0;
1290 }
1291
1292
1293 static int
1294 pipemode_cb ( void *opaque, char *buffer, size_t length, size_t *nread )
1295 {
1296     GpgObject gpg = opaque;
1297     GpgmeError err;
1298
1299     if ( !buffer || !length || !nread )
1300         return 0; /* those values are reserved for extensions */
1301     *nread =0;
1302     if ( !gpg->pm.stream_started ) {
1303         assert (length > 4 );
1304         strcpy (buffer, "@<@B" );
1305         *nread = 4;
1306         gpg->pm.stream_started = 1;
1307     }
1308     else if ( gpg->pm.sig ) {
1309         err = pipemode_copy ( buffer, length, nread, gpg->pm.sig );
1310         if ( err == GPGME_EOF ) {
1311             gpg->pm.sig = NULL;
1312             assert (length > 4 );
1313             strcpy (buffer, "@t" );
1314             *nread = 2;
1315         }
1316         else if (err) {
1317             DEBUG1 ("pipemode_cb: copy sig failed: %s\n",
1318                      gpgme_strerror (err) );
1319             return -1;
1320         }
1321     }
1322     else if ( gpg->pm.text ) {
1323         err = pipemode_copy ( buffer, length, nread, gpg->pm.text );
1324         if ( err == GPGME_EOF ) {
1325             gpg->pm.text = NULL;
1326             assert (length > 4 );
1327             strcpy (buffer, "@.@>" );
1328             *nread = 4;
1329         }
1330         else if (err) {
1331             DEBUG1 ("pipemode_cb: copy data failed: %s\n",
1332                      gpgme_strerror (err) );
1333             return -1;
1334         }
1335     }
1336     else {
1337         return 0; /* eof */
1338     }
1339
1340     return 0;
1341 }
1342
1343
1344 /* 
1345  * Here we handle --command-fd.  This works closely together with
1346  * the status handler.  
1347  */
1348
1349 static int
1350 command_cb (void *opaque, char *buffer, size_t length, size_t *nread)
1351 {
1352   GpgObject gpg = opaque;
1353   const char *value;
1354   int value_len;
1355
1356   DEBUG0 ("command_cb: enter\n");
1357   assert (gpg->cmd.used);
1358   if (!buffer || !length || !nread)
1359     return 0; /* These values are reserved for extensions.  */
1360   *nread = 0;
1361   if (!gpg->cmd.code)
1362     {
1363       DEBUG0 ("command_cb: no code\n");
1364       return -1;
1365     }
1366     
1367   if (!gpg->cmd.fnc)
1368     {
1369       DEBUG0 ("command_cb: no user cb\n");
1370       return -1;
1371     }
1372
1373   value = gpg->cmd.fnc (gpg->cmd.fnc_value, 
1374                         gpg->cmd.code, gpg->cmd.keyword);
1375   if (!value)
1376     {
1377       DEBUG0 ("command_cb: no data from user cb\n");
1378       gpg->cmd.fnc (gpg->cmd.fnc_value, 0, value);
1379       return -1;
1380     }
1381
1382   value_len = strlen (value);
1383   if (value_len + 1 > length)
1384     {
1385       DEBUG0 ("command_cb: too much data from user cb\n");
1386       gpg->cmd.fnc (gpg->cmd.fnc_value, 0, value);
1387       return -1;
1388     }
1389
1390   memcpy (buffer, value, value_len);
1391   if (!value_len || (value_len && value[value_len-1] != '\n')) 
1392     buffer[value_len++] = '\n';
1393   *nread = value_len;
1394     
1395   gpg->cmd.fnc (gpg->cmd.fnc_value, 0, value);
1396   gpg->cmd.code = 0;
1397   /* And sleep again until read_status will wake us up again.  */
1398   /* XXX We must check if there are any more fds active after removing
1399      this one.  */
1400   (*gpg->io_cbs.remove) (gpg->fd_data_map[gpg->cmd.idx].tag);
1401   gpg->cmd.fd = gpg->fd_data_map[gpg->cmd.idx].fd;
1402   gpg->fd_data_map[gpg->cmd.idx].fd = -1;
1403
1404   return 0;
1405 }
1406
1407 GpgmeError
1408 _gpgme_gpg_op_decrypt (GpgObject gpg, GpgmeData ciph, GpgmeData plain)
1409 {
1410   GpgmeError err;
1411
1412   err = _gpgme_gpg_add_arg (gpg, "--decrypt");
1413
1414   /* Tell the gpg object about the data.  */
1415   if (!err)
1416     err = _gpgme_gpg_add_arg (gpg, "--output");
1417   if (!err)
1418     err = _gpgme_gpg_add_arg (gpg, "-");
1419   if (!err)
1420     err = _gpgme_gpg_add_data (gpg, plain, 1);
1421   if (!err)
1422     err = _gpgme_gpg_add_data (gpg, ciph, 0);
1423
1424   return err;
1425 }
1426
1427 GpgmeError
1428 _gpgme_gpg_op_delete (GpgObject gpg, GpgmeKey key, int allow_secret)
1429 {
1430   GpgmeError err;
1431
1432   err = _gpgme_gpg_add_arg (gpg, allow_secret
1433                             ? "--delete-secret-and-public-key"
1434                             : "--delete-key");
1435   if (!err)
1436     err = _gpgme_gpg_add_arg (gpg, "--");
1437   if (!err)
1438     {
1439       const char *s = gpgme_key_get_string_attr (key, GPGME_ATTR_FPR, NULL, 0);
1440       if (!s)
1441         err = mk_error (Invalid_Key);
1442       else
1443         err = _gpgme_gpg_add_arg (gpg, s);
1444     }
1445
1446   return err;
1447 }
1448
1449
1450 GpgmeError
1451 _gpgme_gpg_op_edit (GpgObject gpg, GpgmeKey key, GpgmeData out)
1452 {
1453   GpgmeError err;
1454
1455   err = _gpgme_gpg_add_arg (gpg, "--with-colons");
1456   if (!err)
1457   err = _gpgme_gpg_add_arg (gpg, "--edit-key");
1458   if (!err)
1459     err = _gpgme_gpg_add_data (gpg, out, 1);
1460   if (!err)
1461     err = _gpgme_gpg_add_arg (gpg, "--");
1462   if (!err)
1463     {
1464       const char *s = gpgme_key_get_string_attr (key, GPGME_ATTR_FPR, NULL, 0);
1465       if (!s)
1466         err = mk_error (Invalid_Key);
1467       else
1468         err = _gpgme_gpg_add_arg (gpg, s);
1469     }
1470
1471   return err;
1472 }
1473
1474
1475 static GpgmeError
1476 _gpgme_append_gpg_args_from_recipients (GpgObject gpg,
1477                                         const GpgmeRecipients rset)
1478 {
1479   GpgmeError err = 0;
1480   struct user_id_s *r;
1481
1482   assert (rset);
1483   for (r = rset->list; r; r = r->next)
1484     {
1485       err = _gpgme_gpg_add_arg (gpg, "-r");
1486       if (!err)
1487         _gpgme_gpg_add_arg (gpg, r->name);
1488       if (err)
1489         break;
1490     }    
1491   return err;
1492 }
1493
1494
1495 static GpgmeError
1496 _gpgme_append_gpg_args_from_signers (GpgObject gpg,
1497                                      GpgmeCtx ctx /* FIXME */)
1498 {
1499   GpgmeError err = 0;
1500   int i;
1501   GpgmeKey key;
1502
1503   for (i = 0; (key = gpgme_signers_enum (ctx, i)); i++)
1504     {
1505       const char *s = gpgme_key_get_string_attr (key, GPGME_ATTR_KEYID,
1506                                                  NULL, 0);
1507       if (s)
1508         {
1509           if (!err)
1510             err = _gpgme_gpg_add_arg (gpg, "-u");
1511           if (!err)
1512             err = _gpgme_gpg_add_arg (gpg, s);
1513         }
1514       gpgme_key_unref (key);
1515       if (err) break;
1516     }
1517   return err;
1518 }
1519
1520
1521 GpgmeError
1522 _gpgme_gpg_op_encrypt (GpgObject gpg, GpgmeRecipients recp,
1523                        GpgmeData plain, GpgmeData ciph, int use_armor)
1524 {
1525   GpgmeError err;
1526   int symmetric = !recp;
1527
1528   err = _gpgme_gpg_add_arg (gpg, symmetric ? "--symmetric" : "--encrypt");
1529
1530   if (!err && use_armor)
1531     err = _gpgme_gpg_add_arg (gpg, "--armor");
1532
1533   if (!symmetric)
1534     {
1535       /* If we know that all recipients are valid (full or ultimate trust)
1536          we can suppress further checks.  */
1537       if (!err && !symmetric && _gpgme_recipients_all_valid (recp))
1538         err = _gpgme_gpg_add_arg (gpg, "--always-trust");
1539
1540       if (!err)
1541         err = _gpgme_append_gpg_args_from_recipients (gpg, recp);
1542     }
1543
1544   /* Tell the gpg object about the data.  */
1545   if (!err)
1546     err = _gpgme_gpg_add_arg (gpg, "--output");
1547   if (!err)
1548     err = _gpgme_gpg_add_arg (gpg, "-");
1549   if (!err)
1550     err = _gpgme_gpg_add_data (gpg, ciph, 1);
1551   if (!err)
1552     err = _gpgme_gpg_add_arg (gpg, "--");
1553   if (!err)
1554     err = _gpgme_gpg_add_data (gpg, plain, 0);
1555
1556   return err;
1557 }
1558
1559 GpgmeError
1560 _gpgme_gpg_op_encrypt_sign (GpgObject gpg, GpgmeRecipients recp,
1561                             GpgmeData plain, GpgmeData ciph, int use_armor,
1562                             GpgmeCtx ctx /* FIXME */)
1563 {
1564   GpgmeError err;
1565
1566   err = _gpgme_gpg_add_arg (gpg, "--encrypt");
1567   if (!err)
1568     err = _gpgme_gpg_add_arg (gpg, "--sign");
1569   if (!err && use_armor)
1570     err = _gpgme_gpg_add_arg (gpg, "--armor");
1571
1572   /* If we know that all recipients are valid (full or ultimate trust)
1573    * we can suppress further checks */
1574   if (!err && _gpgme_recipients_all_valid (recp))
1575     err = _gpgme_gpg_add_arg (gpg, "--always-trust");
1576
1577   if (!err)
1578     err = _gpgme_append_gpg_args_from_recipients (gpg, recp);
1579
1580   if (!err)
1581     err = _gpgme_append_gpg_args_from_signers (gpg, ctx);
1582
1583   /* Tell the gpg object about the data.  */
1584   if (!err)
1585     err = _gpgme_gpg_add_arg (gpg, "--output");
1586   if (!err)
1587     err = _gpgme_gpg_add_arg (gpg, "-");
1588   if (!err)
1589     err = _gpgme_gpg_add_data (gpg, ciph, 1);
1590   if (!err)
1591     err = _gpgme_gpg_add_arg (gpg, "--");
1592   if (!err)
1593     err = _gpgme_gpg_add_data (gpg, plain, 0);
1594
1595   return err;
1596 }
1597
1598 GpgmeError
1599 _gpgme_gpg_op_export (GpgObject gpg, GpgmeRecipients recp,
1600                       GpgmeData keydata, int use_armor)
1601 {
1602   GpgmeError err;
1603
1604   err = _gpgme_gpg_add_arg (gpg, "--export");
1605   if (!err && use_armor)
1606     err = _gpgme_gpg_add_arg (gpg, "--armor");
1607   if (!err)
1608     err = _gpgme_gpg_add_data (gpg, keydata, 1);
1609   if (!err)
1610     err = _gpgme_gpg_add_arg (gpg, "--");
1611
1612   if (!err)
1613     {
1614       void *ec;
1615       const char *s;
1616
1617       err = gpgme_recipients_enum_open (recp, &ec);
1618       while (!err && (s = gpgme_recipients_enum_read (recp, &ec)))
1619         err = _gpgme_gpg_add_arg (gpg, s);
1620       if (!err)
1621         err = gpgme_recipients_enum_close (recp, &ec);
1622     }
1623
1624   return err;
1625 }
1626
1627 GpgmeError
1628 _gpgme_gpg_op_genkey (GpgObject gpg, GpgmeData help_data, int use_armor,
1629                       GpgmeData pubkey, GpgmeData seckey)
1630 {
1631   GpgmeError err;
1632
1633   if (!gpg)
1634     return mk_error (Invalid_Value);
1635
1636   /* We need a special mechanism to get the fd of a pipe here, so
1637    * that we can use this for the %pubring and %secring parameters.
1638    * We don't have this yet, so we implement only the adding to the
1639    * standard keyrings */
1640   if (pubkey || seckey)
1641     return err = mk_error (Not_Implemented);
1642
1643   err = _gpgme_gpg_add_arg (gpg, "--gen-key");
1644   if (!err && use_armor)
1645     err = _gpgme_gpg_add_arg (gpg, "--armor");
1646   if (!err)
1647     err = _gpgme_gpg_add_data (gpg, help_data, 0);
1648
1649   return err;
1650 }
1651
1652 GpgmeError
1653 _gpgme_gpg_op_import (GpgObject gpg, GpgmeData keydata)
1654 {
1655   GpgmeError err;
1656
1657   err = _gpgme_gpg_add_arg (gpg, "--import");
1658   if (!err)
1659     err = _gpgme_gpg_add_data (gpg, keydata, 0);
1660
1661   return err;
1662 }
1663
1664
1665 GpgmeError
1666 _gpgme_gpg_op_keylist (GpgObject gpg, const char *pattern, int secret_only,
1667                        int keylist_mode)
1668 {
1669   GpgmeError err;
1670
1671   err = _gpgme_gpg_add_arg (gpg, "--with-colons");
1672   if (!err)
1673     err = _gpgme_gpg_add_arg (gpg, "--fixed-list-mode");
1674   if (!err)
1675     err = _gpgme_gpg_add_arg (gpg, "--with-fingerprint");
1676   if (!err)
1677     err = _gpgme_gpg_add_arg (gpg, secret_only ? "--list-secret-keys"
1678                               : "--list-keys");
1679   
1680   /* Tell the gpg object about the data */
1681   if (!err)
1682     err = _gpgme_gpg_add_arg (gpg, "--");
1683   if (!err && pattern && *pattern)
1684     err = _gpgme_gpg_add_arg (gpg, pattern);
1685
1686   return err;
1687 }
1688
1689
1690 GpgmeError
1691 _gpgme_gpg_op_keylist_ext (GpgObject gpg, const char *pattern[],
1692                            int secret_only, int reserved, int keylist_mode)
1693 {
1694   GpgmeError err;
1695
1696   if (reserved)
1697     return mk_error (Invalid_Value);
1698
1699   err = _gpgme_gpg_add_arg (gpg, "--with-colons");
1700   if (!err)
1701     err = _gpgme_gpg_add_arg (gpg, "--fixed-list-mode");
1702   if (!err)
1703     err = _gpgme_gpg_add_arg (gpg, "--with-fingerprint");
1704   if (!err)
1705     err = _gpgme_gpg_add_arg (gpg, secret_only ? "--list-secret-keys"
1706                               : "--list-keys");
1707   
1708   /* Tell the gpg object about the data */
1709   if (!err)
1710     err = _gpgme_gpg_add_arg (gpg, "--");
1711   if (!err && pattern && *pattern)
1712     {
1713       while (*pattern && **pattern)
1714         err = _gpgme_gpg_add_arg (gpg, *(pattern++));
1715     }
1716
1717   return err;
1718 }
1719
1720
1721 GpgmeError
1722 _gpgme_gpg_op_sign (GpgObject gpg, GpgmeData in, GpgmeData out,
1723                     GpgmeSigMode mode, int use_armor,
1724                     int use_textmode, GpgmeCtx ctx /* FIXME */)
1725 {
1726   GpgmeError err;
1727
1728   if (mode == GPGME_SIG_MODE_CLEAR)
1729     err = _gpgme_gpg_add_arg (gpg, "--clearsign");
1730   else
1731     {
1732       err = _gpgme_gpg_add_arg (gpg, "--sign");
1733       if (!err && mode == GPGME_SIG_MODE_DETACH)
1734         err = _gpgme_gpg_add_arg (gpg, "--detach");
1735       if (!err && use_armor)
1736         err = _gpgme_gpg_add_arg (gpg, "--armor");
1737       if (!err && use_textmode)
1738         _gpgme_gpg_add_arg (gpg, "--textmode");
1739     }
1740
1741   if (!err)
1742     err = _gpgme_append_gpg_args_from_signers (gpg, ctx);
1743
1744   /* Tell the gpg object about the data.  */
1745   if (!err)
1746     err = _gpgme_gpg_add_data (gpg, in, 0);
1747   if (!err)
1748     err = _gpgme_gpg_add_data (gpg, out, 1);
1749
1750   return err;
1751 }
1752
1753 GpgmeError
1754 _gpgme_gpg_op_trustlist (GpgObject gpg, const char *pattern)
1755 {
1756   GpgmeError err;
1757
1758   err = _gpgme_gpg_add_arg (gpg, "--with-colons");
1759   if (!err)
1760     err = _gpgme_gpg_add_arg (gpg, "--list-trust-path");
1761   
1762   /* Tell the gpg object about the data */
1763   if (!err)
1764     err = _gpgme_gpg_add_arg (gpg, "--");
1765   if (!err)
1766     err = _gpgme_gpg_add_arg (gpg, pattern);
1767
1768   return err;
1769 }
1770
1771 GpgmeError
1772 _gpgme_gpg_op_verify (GpgObject gpg, GpgmeData sig, GpgmeData text)
1773 {
1774   GpgmeError err = 0;
1775
1776   if (_gpgme_data_get_mode (text) == GPGME_DATA_MODE_IN)
1777     {
1778       /* Normal or cleartext signature.  */
1779
1780       err = _gpgme_gpg_add_arg (gpg, "--output");
1781       if (!err)
1782         err = _gpgme_gpg_add_arg (gpg, "-");
1783       if (!err)
1784         err = _gpgme_gpg_add_arg (gpg, "--");
1785       if (!err)
1786         err = _gpgme_gpg_add_data (gpg, sig, 0);
1787       if (!err)
1788         err = _gpgme_gpg_add_data (gpg, text, 1);
1789     }
1790   else
1791     {
1792       if (gpg->pm.used)
1793         {
1794           err = _gpgme_gpg_add_arg (gpg, gpg->pm.used ? "--pipemode" : "--verify");
1795           if (!err)
1796             err = _gpgme_gpg_add_arg (gpg, "--");
1797           if (!err)
1798             err = _gpgme_gpg_add_pm_data (gpg, sig, 0);
1799           if (!err)
1800             err = _gpgme_gpg_add_pm_data (gpg, text, 1);
1801         }
1802       else
1803         {
1804           err = _gpgme_gpg_add_arg (gpg, "--verify");
1805           if (!err)
1806             err = _gpgme_gpg_add_arg (gpg, "--");
1807           if (!err)
1808             err = _gpgme_gpg_add_data (gpg, sig, -1);
1809           if (text)
1810             {
1811               if (!err)
1812                 err = _gpgme_gpg_add_arg (gpg, "-");
1813               if (!err)
1814                 err = _gpgme_gpg_add_data (gpg, text, 0);
1815             }
1816         }
1817     }
1818   return err;
1819 }
1820
1821
1822 void
1823 _gpgme_gpg_set_io_cbs (GpgObject gpg, struct GpgmeIOCbs *io_cbs)
1824 {
1825   gpg->io_cbs = *io_cbs;
1826 }
1827
1828
1829 void
1830 _gpgme_gpg_io_event (GpgObject gpg, GpgmeEventIO type, void *type_data)
1831 {
1832   if (gpg->io_cbs.event)
1833     (*gpg->io_cbs.event) (gpg->io_cbs.event_priv, type, type_data);
1834 }