configuration changes
[gpgme.git] / gpgme / rungpg.c
1 /* rungpg.c 
2  *      Copyright (C) 2000 Werner Koch (dd9jn)
3  *
4  * This file is part of GPGME.
5  *
6  * GPGME is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * GPGME is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19  */
20
21 #include <config.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <assert.h>
26 #include <errno.h>
27 #include <sys/time.h>
28 #include <sys/types.h>
29 #include <unistd.h>
30 #include <sys/wait.h>
31 #include <signal.h>
32 #include <fcntl.h>
33
34 #include "gpgme.h"
35 #include "util.h"
36 #include "ops.h"
37 #include "wait.h"
38 #include "rungpg.h"
39 #include "context.h"  /*temp hack until we have GpmeData methods to do I/O */
40
41 #include "status-table.h"
42
43 /* This type is used to build a list of gpg arguments and
44  * data sources/sinks */
45 struct arg_and_data_s {
46     struct arg_and_data_s *next;
47     GpgmeData data;  /* If this is not NULL .. */
48     int dup_to;
49     char arg[1];     /* .. this is used */
50 };
51
52 struct fd_data_map_s {
53     GpgmeData data;
54     int inbound;  /* true if this is used for reading from gpg */
55     int dup_to;
56     int fd;       /* the fd to use */
57     int peer_fd;  /* the outher side of the pipe */
58 };
59
60
61 struct gpg_object_s {
62     struct arg_and_data_s *arglist;
63     struct arg_and_data_s **argtail;
64     int arg_error;  
65
66     struct {
67         int fd[2];  
68         size_t bufsize;
69         char *buffer;
70         size_t readpos;
71         int eof;
72         GpgStatusHandler fnc;
73         void *fnc_value;
74     } status;
75
76     /* This is a kludge - see the comment at gpg_colon_line_handler */
77     struct {
78         int fd[2];  
79         size_t bufsize;
80         char *buffer;
81         size_t readpos;
82         int eof;
83         GpgColonLineHandler fnc;  /* this indicate use of this structrue */
84         void *fnc_value;
85     } colon;
86
87     char **argv;  
88     struct fd_data_map_s *fd_data_map;
89
90     pid_t pid; 
91
92     int running;
93     int exit_status;
94     int exit_signal;
95 };
96
97 static void kill_gpg ( GpgObject gpg );
98 static void free_argv ( char **argv );
99 static void free_fd_data_map ( struct fd_data_map_s *fd_data_map );
100
101 static int gpg_inbound_handler ( void *opaque, pid_t pid, int fd );
102 static int gpg_outbound_handler ( void *opaque, pid_t pid, int fd );
103
104 static int gpg_status_handler ( void *opaque, pid_t pid, int fd );
105 static GpgmeError read_status ( GpgObject gpg );
106
107 static int gpg_colon_line_handler ( void *opaque, pid_t pid, int fd );
108 static GpgmeError read_colon_line ( GpgObject gpg );
109
110
111
112 GpgmeError
113 _gpgme_gpg_new ( GpgObject *r_gpg )
114 {
115     GpgObject gpg;
116     int rc = 0;
117
118     gpg = xtrycalloc ( 1, sizeof *gpg );
119     if ( !gpg ) {
120         rc = mk_error (Out_Of_Core);
121         goto leave;
122     }
123     gpg->argtail = &gpg->arglist;
124
125     gpg->status.fd[0] = -1;
126     gpg->status.fd[1] = -1;
127     gpg->colon.fd[0] = -1;
128     gpg->colon.fd[1] = -1;
129
130     /* allocate the read buffer for the status pipe */
131     gpg->status.bufsize = 1024;
132     gpg->status.readpos = 0;
133     gpg->status.buffer = xtrymalloc (gpg->status.bufsize);
134     if (!gpg->status.buffer) {
135         rc = mk_error (Out_Of_Core);
136         goto leave;
137     }
138     /* In any case we need a status pipe - create it right here  and
139      * don't handle it with our generic GpgmeData mechanism */
140     if (pipe (gpg->status.fd) == -1) {
141         rc = mk_error (Pipe_Error);
142         goto leave;
143     }
144     gpg->status.eof = 0;
145     _gpgme_gpg_add_arg ( gpg, "--status-fd" );
146     {
147         char buf[25];
148         sprintf ( buf, "%d", gpg->status.fd[1]);
149         _gpgme_gpg_add_arg ( gpg, buf );
150     }
151     _gpgme_gpg_add_arg ( gpg, "--batch" );
152     _gpgme_gpg_add_arg ( gpg, "--no-tty" );
153
154  leave:
155     if (rc) {
156         _gpgme_gpg_release (gpg);
157         *r_gpg = NULL;
158     }
159     else
160         *r_gpg = gpg;
161     return rc;
162 }
163
164 void
165 _gpgme_gpg_release ( GpgObject gpg )
166 {
167     if ( !gpg )
168         return;
169     xfree (gpg->status.buffer);
170     xfree (gpg->colon.buffer);
171     if ( gpg->argv )
172         free_argv (gpg->argv);
173     if (gpg->status.fd[0] != -1 )
174         close (gpg->status.fd[0]);
175     if (gpg->status.fd[1] != -1 )
176         close (gpg->status.fd[1]);
177     if (gpg->colon.fd[0] != -1 )
178         close (gpg->colon.fd[0]);
179     if (gpg->colon.fd[1] != -1 )
180         close (gpg->colon.fd[1]);
181     free_fd_data_map (gpg->fd_data_map);
182     kill_gpg (gpg); /* fixme: should be done asyncronously */
183     xfree (gpg);
184 }
185
186 static void
187 kill_gpg ( GpgObject gpg )
188 {
189   #if 0
190     if ( gpg->running ) {
191         /* still running? Must send a killer */
192         kill ( gpg->pid, SIGTERM);
193         sleep (2);
194         if ( !waitpid (gpg->pid, NULL, WNOHANG) ) {
195             /* pay the murderer better and then forget about it */
196             kill (gpg->pid, SIGKILL);
197         }
198         gpg->running = 0;
199     }
200   #endif
201 }
202
203
204 GpgmeError
205 _gpgme_gpg_add_arg ( GpgObject gpg, const char *arg )
206 {
207     struct arg_and_data_s *a;
208
209     assert (gpg);
210     assert (arg);
211     a = xtrymalloc ( sizeof *a + strlen (arg) );
212     if ( !a ) {
213         gpg->arg_error = 1;
214         return mk_error(Out_Of_Core);
215     }
216     a->next = NULL;
217     a->data = NULL;
218     a->dup_to = -1;
219     strcpy ( a->arg, arg );
220     *gpg->argtail = a;
221     gpg->argtail = &a->next;
222     return 0;
223 }
224
225 GpgmeError
226 _gpgme_gpg_add_data ( GpgObject gpg, GpgmeData data, int dup_to )
227 {
228     struct arg_and_data_s *a;
229
230     assert (gpg);
231     assert (data);
232     a = xtrymalloc ( sizeof *a - 1 );
233     if ( !a ) {
234         gpg->arg_error = 1;
235         return mk_error(Out_Of_Core);
236     }
237     a->next = NULL;
238     a->data = data;
239     a->dup_to = dup_to;
240     *gpg->argtail = a;
241     gpg->argtail = &a->next;
242     return 0;
243 }
244
245 /*
246  * Note, that the status_handler is allowed to modifiy the args value
247  */
248 void
249 _gpgme_gpg_set_status_handler ( GpgObject gpg,
250                                 GpgStatusHandler fnc, void *fnc_value ) 
251 {
252     assert (gpg);
253     gpg->status.fnc = fnc;
254     gpg->status.fnc_value = fnc_value;
255 }
256
257 /* Kludge to process --with-colon output */
258 GpgmeError
259 _gpgme_gpg_set_colon_line_handler ( GpgObject gpg,
260                                     GpgColonLineHandler fnc, void *fnc_value ) 
261 {
262     assert (gpg);
263
264     gpg->colon.bufsize = 1024;
265     gpg->colon.readpos = 0;
266     gpg->colon.buffer = xtrymalloc (gpg->colon.bufsize);
267     if (!gpg->colon.buffer) {
268         return mk_error (Out_Of_Core);
269     }
270     if (pipe (gpg->colon.fd) == -1) {
271         xfree (gpg->colon.buffer); gpg->colon.buffer = NULL;
272         return mk_error (Pipe_Error);
273     }
274     gpg->colon.eof = 0;
275     gpg->colon.fnc = fnc;
276     gpg->colon.fnc_value = fnc_value;
277     return 0;
278 }
279
280
281 static void
282 free_argv ( char **argv )
283 {
284     int i;
285
286     for (i=0; argv[i]; i++ )
287         xfree (argv[i]);
288     xfree (argv);
289 }
290
291 static void
292 free_fd_data_map ( struct fd_data_map_s *fd_data_map )
293 {
294     int i;
295
296     for (i=0; fd_data_map[i].data; i++ ) {
297         if ( fd_data_map[i].fd != -1 )
298             close (fd_data_map[i].fd);
299         if ( fd_data_map[i].peer_fd != -1 )
300             close (fd_data_map[i].peer_fd);
301         /* don't realease data because this is only a reference */
302     }
303     xfree (fd_data_map);
304 }
305
306
307 static GpgmeError
308 build_argv ( GpgObject gpg )
309 {
310     struct arg_and_data_s *a;
311     struct fd_data_map_s *fd_data_map;
312     size_t datac=0, argc=0;  
313     char **argv;
314     int need_special = 0;
315     int use_agent = !!getenv ("GPG_AGENT_INFO");
316
317        
318     if ( gpg->argv ) {
319         free_argv ( gpg->argv );
320         gpg->argv = NULL;
321     }
322     if (gpg->fd_data_map) {
323         free_fd_data_map (gpg->fd_data_map);
324         gpg->fd_data_map = NULL;
325     }
326
327     argc++; /* for argv[0] */
328     for ( a=gpg->arglist; a; a = a->next ) {
329         argc++;
330         if (a->data) {
331             /*fprintf (stderr, "build_argv: data\n" );*/
332             datac++;
333             if ( a->dup_to == -1 )
334                 need_special = 1;
335         }
336         else {
337             /*   fprintf (stderr, "build_argv: arg=`%s'\n", a->arg );*/
338         }
339     }
340     if ( need_special )
341         argc++;
342     if (use_agent)
343         argc++;
344
345     argv = xtrycalloc ( argc+1, sizeof *argv );
346     if (!argv)
347         return mk_error (Out_Of_Core);
348     fd_data_map = xtrycalloc ( datac+1, sizeof *fd_data_map );
349     if (!fd_data_map) {
350         free_argv (argv);
351         return mk_error (Out_Of_Core);
352     }
353
354     argc = datac = 0;
355     argv[argc] = xtrystrdup ( "gpg" ); /* argv[0] */
356     if (!argv[argc]) {
357         xfree (fd_data_map);
358         free_argv (argv);
359         return mk_error (Out_Of_Core);
360     }
361     argc++;
362     if ( need_special ) {
363         argv[argc] = xtrystrdup ( "--enable-special-filenames" );
364         if (!argv[argc]) {
365             xfree (fd_data_map);
366             free_argv (argv);
367             return mk_error (Out_Of_Core);
368         }
369         argc++;
370     }
371     if ( use_agent ) {
372         argv[argc] = xtrystrdup ( "--use-agent" );
373         if (!argv[argc]) {
374             xfree (fd_data_map);
375             free_argv (argv);
376             return mk_error (Out_Of_Core);
377         }
378         argc++;
379     }
380     for ( a=gpg->arglist; a; a = a->next ) {
381         if ( a->data ) {
382             switch ( _gpgme_data_get_mode (a->data) ) {
383               case GPGME_DATA_MODE_NONE:
384               case GPGME_DATA_MODE_INOUT:
385                 xfree (fd_data_map);
386                 free_argv (argv);
387                 return mk_error (Invalid_Mode);
388               case GPGME_DATA_MODE_IN:
389                 /* create a pipe to read from gpg */
390                 fd_data_map[datac].inbound = 1;
391                 break;
392               case GPGME_DATA_MODE_OUT:
393                 /* create a pipe to pass it down to gpg */
394                 fd_data_map[datac].inbound = 0;
395                 break;
396             }
397
398             switch ( gpgme_data_get_type (a->data) ) {
399               case GPGME_DATA_TYPE_NONE:
400                 if ( fd_data_map[datac].inbound )
401                     break;  /* allowed */
402                 xfree (fd_data_map);
403                 free_argv (argv);
404                 return mk_error (Invalid_Type);
405               case GPGME_DATA_TYPE_MEM:
406                 break;
407               case GPGME_DATA_TYPE_FD:
408               case GPGME_DATA_TYPE_FILE:
409                 xfree (fd_data_map);
410                 free_argv (argv);
411                 return mk_error (Not_Implemented);
412             }
413   
414             /* create a pipe */
415             {   
416                 int fds[2];
417                 
418                 if (pipe (fds) == -1) {
419                     xfree (fd_data_map);
420                     free_argv (argv);
421                     return mk_error (Pipe_Error);
422                 }
423                 /* if the data_type is FD, we have to do a dup2 here */
424                 if (fd_data_map[datac].inbound) {
425                     fd_data_map[datac].fd       = fds[0];
426                     fd_data_map[datac].peer_fd  = fds[1];
427                 }
428                 else {
429                     fd_data_map[datac].fd       = fds[1];
430                     fd_data_map[datac].peer_fd  = fds[0];
431                 }
432             }
433             fd_data_map[datac].data = a->data;
434             fd_data_map[datac].dup_to = a->dup_to;
435             if ( a->dup_to == -1 ) {
436                 argv[argc] = xtrymalloc ( 25 );
437                 if (!argv[argc]) {
438                     xfree (fd_data_map);
439                     free_argv (argv);
440                     return mk_error (Out_Of_Core);
441                 }
442                 sprintf ( argv[argc], "-&%d", fd_data_map[datac].peer_fd );
443                 argc++;
444             }
445             datac++;
446         }
447         else {
448             argv[argc] = xtrystrdup ( a->arg );
449             if (!argv[argc]) {
450                 xfree (fd_data_map);
451                 free_argv (argv);
452                 return mk_error (Out_Of_Core);
453             }
454             argc++;
455         }
456     }
457
458     gpg->argv = argv;
459     gpg->fd_data_map = fd_data_map;
460     return 0;
461 }
462
463 GpgmeError
464 _gpgme_gpg_spawn( GpgObject gpg, void *opaque )
465 {
466     int rc;
467     int i;
468     pid_t pid;
469
470     if ( !gpg )
471         return mk_error (Invalid_Value);
472
473     /* Kludge, so that we don't need to check the return code of
474      * all the gpgme_gpg_add_arg().  we bail out here instead */
475     if ( gpg->arg_error )
476         return mk_error (Out_Of_Core);
477
478     rc = build_argv ( gpg );
479     if ( rc )
480         return rc;
481
482     fflush (stderr);
483     pid = fork ();
484     if (pid == -1) {
485         return mk_error (Exec_Error);
486     }
487         
488     if ( !pid ) { /* child */
489         int duped_stdin = 0;
490         int duped_stderr = 0;
491
492         close (gpg->status.fd[0]);
493
494         if (gpg->colon.fnc) {
495             /* dup it to stdout */
496             if ( dup2 ( gpg->colon.fd[1], 1 ) == -1 ) {
497                 fprintf (stderr,"dup2(colon, 1) failed: %s\n",
498                          strerror (errno) );
499                 _exit (8);
500             }
501             close (gpg->colon.fd[0]);
502             close (gpg->colon.fd[1]);
503         }
504             
505         for (i=0; gpg->fd_data_map[i].data; i++ ) {
506             close (gpg->fd_data_map[i].fd);
507             gpg->fd_data_map[i].fd = -1;
508             if ( gpg->fd_data_map[i].dup_to != -1 ) {
509                 if ( dup2 (gpg->fd_data_map[i].peer_fd,
510                            gpg->fd_data_map[i].dup_to ) == -1 ) {
511                     fprintf (stderr, "dup2 failed in child: %s\n",
512                              strerror (errno));
513                     _exit (8);
514                 }
515                 if ( gpg->fd_data_map[i].dup_to == 0 )
516                     duped_stdin=1;
517                 if ( gpg->fd_data_map[i].dup_to == 2 )
518                     duped_stderr=1;
519                 close ( gpg->fd_data_map[i].peer_fd );
520             }
521         }
522
523         if( !duped_stdin || !duped_stderr ) {
524             int fd = open ( "/dev/null", O_RDONLY );
525             if ( fd == -1 ) {
526                 fprintf (stderr,"can't open `/dev/null': %s\n",
527                          strerror (errno) );
528                 _exit (8);
529             }
530             /* Make sure that gpg has a connected stdin */
531             if ( !duped_stdin ) {
532                 if ( dup2 ( fd, 0 ) == -1 ) {
533                     fprintf (stderr,"dup2(/dev/null, 0) failed: %s\n",
534                              strerror (errno) );
535                     _exit (8);
536                 }
537             }
538             /* We normally don't want all the normal output */
539             if ( !duped_stderr ) {
540                 if (!getenv ("GPGME_DEBUG") ) {
541                     if ( dup2 ( fd, 2 ) == -1 ) {
542                         fprintf (stderr,"dup2(dev/null, 2) failed: %s\n",
543                                  strerror (errno) );
544                         _exit (8);
545                     }
546                 }
547             }
548             close (fd);
549         }
550
551         execv ( GPG_PATH, gpg->argv );
552         fprintf (stderr,"exec of gpg failed\n");
553         _exit (8);
554     }
555     /* parent */
556     gpg->pid = pid;
557
558     /*_gpgme_register_term_handler ( closure, closure_value, pid );*/
559
560     if ( gpg->status.fd[1] != -1 ) {
561         close (gpg->status.fd[1]);
562         gpg->status.fd[1] = -1;
563     }
564     if ( _gpgme_register_pipe_handler ( opaque, gpg_status_handler,
565                                         gpg, pid, gpg->status.fd[0], 1 ) ) {
566         /* FIXME: kill the child */
567         return mk_error (General_Error);
568
569     }
570
571     if ( gpg->colon.fd[1] != -1 ) {
572         close (gpg->colon.fd[1]);
573         gpg->colon.fd[1] = -1;
574         assert ( gpg->colon.fd[0] != -1 );
575         if ( _gpgme_register_pipe_handler ( opaque, gpg_colon_line_handler,
576                                             gpg, pid, gpg->colon.fd[0], 1 ) ) {
577             /* FIXME: kill the child */
578             return mk_error (General_Error);
579             
580         }
581     }
582
583     for (i=0; gpg->fd_data_map[i].data; i++ ) {
584         close (gpg->fd_data_map[i].peer_fd);
585         gpg->fd_data_map[i].peer_fd = -1;
586         if ( _gpgme_register_pipe_handler (
587                  opaque, 
588                  gpg->fd_data_map[i].inbound?
589                        gpg_inbound_handler:gpg_outbound_handler,
590                  gpg->fd_data_map[i].data,
591                  pid, gpg->fd_data_map[i].fd,
592                  gpg->fd_data_map[i].inbound )
593            ) {
594             /* FIXME: kill the child */
595             return mk_error (General_Error);
596         }
597     }
598
599     /* fixme: check what data we can release here */
600
601     gpg->running = 1;
602     return 0;
603 }
604
605
606 static int
607 gpg_inbound_handler ( void *opaque, pid_t pid, int fd )
608 {
609     GpgmeData dh = opaque;
610     GpgmeError err;
611     int nread;
612     char buf[200];
613
614     assert ( _gpgme_data_get_mode (dh) == GPGME_DATA_MODE_IN );
615
616     do {
617         nread = read (fd, buf, 200 );
618     } while ( nread == -1 && errno == EINTR);
619     fprintf(stderr, "inbound on fd %d: nread=%d\n", fd, nread );
620     if ( nread < 0 ) {
621         fprintf (stderr, "read_mem_data: read failed on fd %d (n=%d): %s\n",
622                  fd, nread, strerror (errno) );
623         return 1;
624     }
625     else if (!nread)
626         return 1; /* eof */
627
628     /* We could improve this with a GpgmeData function which takes
629      * the read function or provides a memory area for writing to it.
630      */
631     
632     err = _gpgme_data_append ( dh, buf, nread );
633     if ( err ) {
634         fprintf (stderr, "_gpgme_append_data failed: %s\n",
635                  gpgme_strerror(err));
636         /* Fixme: we should close the pipe or read it to /dev/null in
637          * this case. Returnin EOF is not sufficient */
638         return 1;
639     }
640
641     return 0;
642 }
643
644
645 static int
646 write_mem_data ( GpgmeData dh, int fd )
647 {
648     size_t nbytes;
649     int  nwritten; 
650
651     nbytes = dh->len - dh->readpos;
652     if ( !nbytes ) {
653         close (fd);
654         return 1;
655     }
656     
657     do {
658         nwritten = write ( fd, dh->data+dh->readpos, nbytes );
659     } while ( nwritten == -1 && errno == EINTR );
660     if ( nwritten < 1 ) {
661         fprintf (stderr, "write_mem_data: write failed on fd %d (n=%d): %s\n",
662                  fd, nwritten, strerror (errno) );
663         close (fd);
664         return 1;
665     }
666
667     dh->readpos += nwritten;
668     return 0;
669 }
670
671
672 static int
673 gpg_outbound_handler ( void *opaque, pid_t pid, int fd )
674 {
675     GpgmeData dh = opaque;
676
677     assert ( _gpgme_data_get_mode (dh) == GPGME_DATA_MODE_OUT );
678     switch ( gpgme_data_get_type (dh) ) {
679       case GPGME_DATA_TYPE_MEM:
680         if ( write_mem_data ( dh, fd ) )
681             return 1; /* ready */
682         break;
683       default:
684         assert (0);
685     }
686
687
688     return 0;
689 }
690
691
692
693 static int
694 gpg_status_handler ( void *opaque, pid_t pid, int fd )
695 {
696     GpgObject gpg = opaque;
697     int rc = 0;
698
699     assert ( fd == gpg->status.fd[0] );
700     rc = read_status ( gpg );
701     if ( rc ) {
702         fprintf (stderr, "gpg_handler: read_status problem %d\n - stop", rc);
703         return 1;
704     }
705
706     return gpg->status.eof;
707 }
708
709
710 static int
711 status_cmp (const void *ap, const void *bp)
712 {
713     const struct status_table_s *a = ap;
714     const struct status_table_s *b = bp;
715
716     return strcmp (a->name, b->name);
717 }
718
719
720
721 /*
722  * Handle the status output of GnuPG.  This function does read entire
723  * lines and passes them as C strings to the callback function (we can
724  * use C Strings because the status output is always UTF-8 encoded).
725  * Of course we have to buffer the lines to cope with long lines
726  * e.g. with a large user ID.  Note: We can optimize this to only cope
727  * with status line code we know about and skip all other stuff
728  * without buffering (i.e. without extending the buffer).  */
729 static GpgmeError
730 read_status ( GpgObject gpg )
731 {
732     char *p;
733     int nread;
734     size_t bufsize = gpg->status.bufsize; 
735     char *buffer = gpg->status.buffer;
736     size_t readpos = gpg->status.readpos; 
737
738     assert (buffer);
739     if (bufsize - readpos < 256) { 
740         /* need more room for the read */
741         bufsize += 1024;
742         buffer = xtryrealloc (buffer, bufsize);
743         if ( !buffer ) 
744             return mk_error (Out_Of_Core);
745     }
746     
747
748     do { 
749         nread = read ( gpg->status.fd[0], buffer+readpos, bufsize-readpos );
750     } while (nread == -1 && errno == EINTR);
751
752     if (nread == -1)
753         return mk_error(Read_Error);
754
755     if (!nread) {
756         gpg->status.eof = 1;
757         if (gpg->status.fnc)
758             gpg->status.fnc ( gpg->status.fnc_value, STATUS_EOF, "" );
759         return 0;
760     }
761
762     while (nread > 0) {
763         for (p = buffer + readpos; nread; nread--, p++) {
764             if ( *p == '\n' ) {
765                 /* (we require that the last line is terminated by a LF) */
766                 *p = 0;
767                 fprintf (stderr, "read_status: `%s'\n", buffer);
768                 if (!strncmp (buffer, "[GNUPG:] ", 9 )
769                     && buffer[9] >= 'A' && buffer[9] <= 'Z'
770                     && gpg->status.fnc ) {
771                     struct status_table_s t, *r;
772                     char *rest;
773
774                     rest = strchr (buffer+9, ' ');
775                     if ( !rest )
776                         rest = p; /* set to an empty string */
777                     else
778                         *rest++ = 0;
779                     
780                     t.name = buffer+9;
781                     /* (the status table as one extra element) */
782                     r = bsearch ( &t, status_table, DIM(status_table)-1,
783                                   sizeof t, status_cmp );
784                     if ( r ) {
785                         gpg->status.fnc ( gpg->status.fnc_value, 
786                                           r->code, rest);
787                     }
788                 }
789                 /* To reuse the buffer for the next line we have to
790                  * shift the remaining data to the buffer start and
791                  * restart the loop Hmmm: We can optimize this
792                  * function by looking forward in the buffer to see
793                  * whether a second complete line is available and in
794                  * this case avoid the memmove for this line.  */
795                 nread--; p++;
796                 if (nread)
797                     memmove (buffer, p, nread);
798                 readpos = 0;
799                 break; /* the for loop */
800             }
801             else
802                 readpos++;
803         }
804     } 
805
806     /* Update the gpg object.  */
807     gpg->status.bufsize = bufsize;
808     gpg->status.buffer = buffer;
809     gpg->status.readpos = readpos;
810     return 0;
811 }
812
813
814 /*
815  * This colonline handler thing is not the clean way to do it.
816  * It might be better to enhance the GpgmeData object to act as
817  * a wrapper for a callback.  Same goes for the status thing.
818  * For now we use this thing here becuase it is easier to implement.
819  */
820 static int
821 gpg_colon_line_handler ( void *opaque, pid_t pid, int fd )
822 {
823     GpgObject gpg = opaque;
824     GpgmeError rc = 0;
825
826     assert ( fd == gpg->colon.fd[0] );
827     rc = read_colon_line ( gpg );
828     if ( rc ) {
829         fprintf (stderr, "gpg_colon_line_handler: "
830                  "read problem %d\n - stop", rc);
831         return 1;
832     }
833
834     return gpg->status.eof;
835 }
836
837 static GpgmeError
838 read_colon_line ( GpgObject gpg )
839 {
840     char *p;
841     int nread;
842     size_t bufsize = gpg->colon.bufsize; 
843     char *buffer = gpg->colon.buffer;
844     size_t readpos = gpg->colon.readpos; 
845
846     assert (buffer);
847     if (bufsize - readpos < 256) { 
848         /* need more room for the read */
849         bufsize += 1024;
850         buffer = xtryrealloc (buffer, bufsize);
851         if ( !buffer ) 
852             return mk_error (Out_Of_Core);
853     }
854     
855
856     do { 
857         nread = read ( gpg->colon.fd[0], buffer+readpos, bufsize-readpos );
858     } while (nread == -1 && errno == EINTR);
859
860     if (nread == -1)
861         return mk_error(Read_Error);
862
863     if (!nread) {
864         gpg->colon.eof = 1;
865         assert (gpg->colon.fnc);
866         gpg->colon.fnc ( gpg->colon.fnc_value, NULL );
867         return 0;
868     }
869
870     while (nread > 0) {
871         for (p = buffer + readpos; nread; nread--, p++) {
872             if ( *p == '\n' ) {
873                 /* (we require that the last line is terminated by a
874                  * LF) and we skip empty lines.  Note: we use UTF8
875                  * encoding and escaping of special characters
876                  * We require at least one colon to cope with
877                  * some other printed information.
878                  */
879                 *p = 0;
880                 if ( *buffer && strchr (buffer, ':') ) {
881                     assert (gpg->colon.fnc);
882                     gpg->colon.fnc ( gpg->colon.fnc_value, buffer );
883                 }
884             
885                 /* To reuse the buffer for the next line we have to
886                  * shift the remaining data to the buffer start and
887                  * restart the loop Hmmm: We can optimize this
888                  * function by looking forward in the buffer to see
889                  * whether a second complete line is available and in
890                  * this case avoid the memmove for this line.  */
891                 nread--; p++;
892                 if (nread)
893                     memmove (buffer, p, nread);
894                 readpos = 0;
895                 break; /* the for loop */
896             }
897             else
898                 readpos++;
899         }
900     } 
901     
902     /* Update the gpg object.  */
903     gpg->colon.bufsize = bufsize;
904     gpg->colon.buffer  = buffer;
905     gpg->colon.readpos = readpos;
906     return 0;
907 }
908