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