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