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