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