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