Reanmed public functions
[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        
316     if ( gpg->argv ) {
317         free_argv ( gpg->argv );
318         gpg->argv = NULL;
319     }
320     if (gpg->fd_data_map) {
321         free_fd_data_map (gpg->fd_data_map);
322         gpg->fd_data_map = NULL;
323     }
324
325     argc++; /* for argv[0] */
326     for ( a=gpg->arglist; a; a = a->next ) {
327         argc++;
328         if (a->data) {
329             /*fprintf (stderr, "build_argv: data\n" );*/
330             datac++;
331             if ( a->dup_to == -1 )
332                 need_special = 1;
333         }
334         else {
335             /*   fprintf (stderr, "build_argv: arg=`%s'\n", a->arg );*/
336         }
337     }
338     if ( need_special )
339         argc++;
340
341     argv = xtrycalloc ( argc+1, sizeof *argv );
342     if (!argv)
343         return mk_error (Out_Of_Core);
344     fd_data_map = xtrycalloc ( datac+1, sizeof *fd_data_map );
345     if (!fd_data_map) {
346         free_argv (argv);
347         return mk_error (Out_Of_Core);
348     }
349
350     argc = datac = 0;
351     argv[argc] = xtrystrdup ( "gpg" ); /* argv[0] */
352     if (!argv[argc]) {
353         xfree (fd_data_map);
354         free_argv (argv);
355         return mk_error (Out_Of_Core);
356     }
357     argc++;
358     if ( need_special ) {
359         argv[argc] = xtrystrdup ( "--enable-special-filenames" );
360         if (!argv[argc]) {
361             xfree (fd_data_map);
362             free_argv (argv);
363             return mk_error (Out_Of_Core);
364         }
365         argc++;
366     }
367     for ( a=gpg->arglist; a; a = a->next ) {
368         if ( a->data ) {
369             switch ( _gpgme_data_get_mode (a->data) ) {
370               case GPGME_DATA_MODE_NONE:
371               case GPGME_DATA_MODE_INOUT:
372                 xfree (fd_data_map);
373                 free_argv (argv);
374                 return mk_error (Invalid_Mode);
375               case GPGME_DATA_MODE_IN:
376                 /* create a pipe to read from gpg */
377                 fd_data_map[datac].inbound = 1;
378                 break;
379               case GPGME_DATA_MODE_OUT:
380                 /* create a pipe to pass it down to gpg */
381                 fd_data_map[datac].inbound = 0;
382                 break;
383             }
384
385             switch ( gpgme_data_get_type (a->data) ) {
386               case GPGME_DATA_TYPE_NONE:
387                 if ( fd_data_map[datac].inbound )
388                     break;  /* allowed */
389                 xfree (fd_data_map);
390                 free_argv (argv);
391                 return mk_error (Invalid_Type);
392               case GPGME_DATA_TYPE_MEM:
393                 break;
394               case GPGME_DATA_TYPE_FD:
395               case GPGME_DATA_TYPE_FILE:
396                 xfree (fd_data_map);
397                 free_argv (argv);
398                 return mk_error (Not_Implemented);
399             }
400   
401             /* create a pipe */
402             {   
403                 int fds[2];
404                 
405                 if (pipe (fds) == -1) {
406                     xfree (fd_data_map);
407                     free_argv (argv);
408                     return mk_error (Pipe_Error);
409                 }
410                 /* if the data_type is FD, we have to do a dup2 here */
411                 if (fd_data_map[datac].inbound) {
412                     fd_data_map[datac].fd       = fds[0];
413                     fd_data_map[datac].peer_fd  = fds[1];
414                 }
415                 else {
416                     fd_data_map[datac].fd       = fds[1];
417                     fd_data_map[datac].peer_fd  = fds[0];
418                 }
419             }
420             fd_data_map[datac].data = a->data;
421             fd_data_map[datac].dup_to = a->dup_to;
422             if ( a->dup_to == -1 ) {
423                 argv[argc] = xtrymalloc ( 25 );
424                 if (!argv[argc]) {
425                     xfree (fd_data_map);
426                     free_argv (argv);
427                     return mk_error (Out_Of_Core);
428                 }
429                 sprintf ( argv[argc], "-&%d", fd_data_map[datac].peer_fd );
430                 argc++;
431             }
432             datac++;
433         }
434         else {
435             argv[argc] = xtrystrdup ( a->arg );
436             if (!argv[argc]) {
437                 xfree (fd_data_map);
438                 free_argv (argv);
439                 return mk_error (Out_Of_Core);
440             }
441             argc++;
442         }
443     }
444
445     gpg->argv = argv;
446     gpg->fd_data_map = fd_data_map;
447     return 0;
448 }
449
450 GpgmeError
451 _gpgme_gpg_spawn( GpgObject gpg, void *opaque )
452 {
453     int rc;
454     int i;
455     pid_t pid;
456
457     if ( !gpg )
458         return mk_error (Invalid_Value);
459
460     /* Kludge, so that we don't need to check the return code of
461      * all the gpgme_gpg_add_arg().  we bail out here instead */
462     if ( gpg->arg_error )
463         return mk_error (Out_Of_Core);
464
465     rc = build_argv ( gpg );
466     if ( rc )
467         return rc;
468
469     fflush (stderr);
470     pid = fork ();
471     if (pid == -1) {
472         return mk_error (Exec_Error);
473     }
474         
475     if ( !pid ) { /* child */
476         int duped_stdin = 0;
477         int duped_stderr = 0;
478
479         close (gpg->status.fd[0]);
480
481         if (gpg->colon.fnc) {
482             /* dup it to stdout */
483             if ( dup2 ( gpg->colon.fd[1], 1 ) == -1 ) {
484                 fprintf (stderr,"dup2(colon, 1) failed: %s\n",
485                          strerror (errno) );
486                 _exit (8);
487             }
488             close (gpg->colon.fd[0]);
489             close (gpg->colon.fd[1]);
490         }
491             
492         for (i=0; gpg->fd_data_map[i].data; i++ ) {
493             close (gpg->fd_data_map[i].fd);
494             gpg->fd_data_map[i].fd = -1;
495             if ( gpg->fd_data_map[i].dup_to != -1 ) {
496                 if ( dup2 (gpg->fd_data_map[i].peer_fd,
497                            gpg->fd_data_map[i].dup_to ) == -1 ) {
498                     fprintf (stderr, "dup2 failed in child: %s\n",
499                              strerror (errno));
500                     _exit (8);
501                 }
502                 if ( gpg->fd_data_map[i].dup_to == 0 )
503                     duped_stdin=1;
504                 if ( gpg->fd_data_map[i].dup_to == 2 )
505                     duped_stderr=1;
506                 close ( gpg->fd_data_map[i].peer_fd );
507             }
508         }
509
510         if( !duped_stdin || !duped_stderr ) {
511             int fd = open ( "/dev/null", O_RDONLY );
512             if ( fd == -1 ) {
513                 fprintf (stderr,"can't open `/dev/null': %s\n",
514                          strerror (errno) );
515                 _exit (8);
516             }
517             /* Make sure that gpg has a connected stdin */
518             if ( !duped_stdin ) {
519                 if ( dup2 ( fd, 0 ) == -1 ) {
520                     fprintf (stderr,"dup2(/dev/null, 0) failed: %s\n",
521                              strerror (errno) );
522                     _exit (8);
523                 }
524             }
525             /* We normally don't want all the normal output */
526             if ( !duped_stderr ) {
527                 if ( dup2 ( fd, 2 ) == -1 ) {
528                     fprintf (stderr,"dup2(dev/null, 2) failed: %s\n",
529                              strerror (errno) );
530                     _exit (8);
531                 }
532             }
533             close (fd);
534         }
535
536         execv ("./gpg", gpg->argv );
537         fprintf (stderr,"exec of gpg failed\n");
538         _exit (8);
539     }
540     /* parent */
541     gpg->pid = pid;
542
543     /*_gpgme_register_term_handler ( closure, closure_value, pid );*/
544
545     if ( gpg->status.fd[1] != -1 ) {
546         close (gpg->status.fd[1]);
547         gpg->status.fd[1] = -1;
548     }
549     if ( _gpgme_register_pipe_handler ( opaque, gpg_status_handler,
550                                         gpg, pid, gpg->status.fd[0], 1 ) ) {
551         /* FIXME: kill the child */
552         return mk_error (General_Error);
553
554     }
555
556     if ( gpg->colon.fd[1] != -1 ) {
557         close (gpg->colon.fd[1]);
558         gpg->colon.fd[1] = -1;
559         assert ( gpg->colon.fd[0] != -1 );
560         if ( _gpgme_register_pipe_handler ( opaque, gpg_colon_line_handler,
561                                             gpg, pid, gpg->colon.fd[0], 1 ) ) {
562             /* FIXME: kill the child */
563             return mk_error (General_Error);
564             
565         }
566     }
567
568     for (i=0; gpg->fd_data_map[i].data; i++ ) {
569         close (gpg->fd_data_map[i].peer_fd);
570         gpg->fd_data_map[i].peer_fd = -1;
571         if ( _gpgme_register_pipe_handler (
572                  opaque, 
573                  gpg->fd_data_map[i].inbound?
574                        gpg_inbound_handler:gpg_outbound_handler,
575                  gpg->fd_data_map[i].data,
576                  pid, gpg->fd_data_map[i].fd,
577                  gpg->fd_data_map[i].inbound )
578            ) {
579             /* FIXME: kill the child */
580             return mk_error (General_Error);
581         }
582     }
583
584     /* fixme: check what data we can release here */
585
586     gpg->running = 1;
587     return 0;
588 }
589
590
591 static int
592 gpg_inbound_handler ( void *opaque, pid_t pid, int fd )
593 {
594     GpgmeData dh = opaque;
595     GpgmeError err;
596     int nread;
597     char buf[200];
598
599     assert ( _gpgme_data_get_mode (dh) == GPGME_DATA_MODE_IN );
600
601     do {
602         nread = read (fd, buf, 200 );
603     } while ( nread == -1 && errno == EINTR);
604     fprintf(stderr, "inbound on fd %d: nread=%d\n", fd, nread );
605     if ( nread < 0 ) {
606         fprintf (stderr, "read_mem_data: read failed on fd %d (n=%d): %s\n",
607                  fd, nread, strerror (errno) );
608         return 1;
609     }
610     else if (!nread)
611         return 1; /* eof */
612
613     /* We could improve this with a GpgmeData function which takes
614      * the read function or provides a memory area for writing to it.
615      */
616     
617     err = _gpgme_data_append ( dh, buf, nread );
618     if ( err ) {
619         fprintf (stderr, "_gpgme_append_data failed: %s\n",
620                  gpgme_strerror(err));
621         /* Fixme: we should close the pipe or read it to /dev/null in
622          * this case. Returnin EOF is not sufficient */
623         return 1;
624     }
625
626     return 0;
627 }
628
629
630 static int
631 write_mem_data ( GpgmeData dh, int fd )
632 {
633     size_t nbytes;
634     int  nwritten; 
635
636     nbytes = dh->len - dh->readpos;
637     if ( !nbytes ) {
638         close (fd);
639         return 1;
640     }
641     
642     do {
643         nwritten = write ( fd, dh->data+dh->readpos, nbytes );
644     } while ( nwritten == -1 && errno == EINTR );
645     if ( nwritten < 1 ) {
646         fprintf (stderr, "write_mem_data: write failed on fd %d (n=%d): %s\n",
647                  fd, nwritten, strerror (errno) );
648         close (fd);
649         return 1;
650     }
651
652     dh->readpos += nwritten;
653     return 0;
654 }
655
656
657 static int
658 gpg_outbound_handler ( void *opaque, pid_t pid, int fd )
659 {
660     GpgmeData dh = opaque;
661
662     assert ( _gpgme_data_get_mode (dh) == GPGME_DATA_MODE_OUT );
663     switch ( gpgme_data_get_type (dh) ) {
664       case GPGME_DATA_TYPE_MEM:
665         if ( write_mem_data ( dh, fd ) )
666             return 1; /* ready */
667         break;
668       default:
669         assert (0);
670     }
671
672
673     return 0;
674 }
675
676
677
678 static int
679 gpg_status_handler ( void *opaque, pid_t pid, int fd )
680 {
681     GpgObject gpg = opaque;
682     int rc = 0;
683
684     assert ( fd == gpg->status.fd[0] );
685     rc = read_status ( gpg );
686     if ( rc ) {
687         fprintf (stderr, "gpg_handler: read_status problem %d\n - stop", rc);
688         return 1;
689     }
690
691     return gpg->status.eof;
692 }
693
694
695 static int
696 status_cmp (const void *ap, const void *bp)
697 {
698     const struct status_table_s *a = ap;
699     const struct status_table_s *b = bp;
700
701     return strcmp (a->name, b->name);
702 }
703
704
705
706 /*
707  * Handle the status output of GnuPG.  This function does read entire
708  * lines and passes them as C strings to the callback function (we can
709  * use C Strings because the status output is always UTF-8 encoded).
710  * Of course we have to buffer the lines to cope with long lines
711  * e.g. with a large user ID.  Note: We can optimize this to only cope
712  * with status line code we know about and skip all other stuff
713  * without buffering (i.e. without extending the buffer).  */
714 static GpgmeError
715 read_status ( GpgObject gpg )
716 {
717     char *p;
718     int nread;
719     size_t bufsize = gpg->status.bufsize; 
720     char *buffer = gpg->status.buffer;
721     size_t readpos = gpg->status.readpos; 
722
723     assert (buffer);
724     if (bufsize - readpos < 256) { 
725         /* need more room for the read */
726         bufsize += 1024;
727         buffer = xtryrealloc (buffer, bufsize);
728         if ( !buffer ) 
729             return mk_error (Out_Of_Core);
730     }
731     
732
733     do { 
734         nread = read ( gpg->status.fd[0], buffer+readpos, bufsize-readpos );
735     } while (nread == -1 && errno == EINTR);
736
737     if (nread == -1)
738         return mk_error(Read_Error);
739
740     if (!nread) {
741         gpg->status.eof = 1;
742         if (gpg->status.fnc)
743             gpg->status.fnc ( gpg->status.fnc_value, STATUS_EOF, "" );
744         return 0;
745     }
746
747     while (nread > 0) {
748         for (p = buffer + readpos; nread; nread--, p++) {
749             if ( *p == '\n' ) {
750                 /* (we require that the last line is terminated by a LF) */
751                 *p = 0;
752                 fprintf (stderr, "read_status: `%s'\n", buffer);
753                 if (!strncmp (buffer, "[GNUPG:] ", 9 )
754                     && buffer[9] >= 'A' && buffer[9] <= 'Z'
755                     && gpg->status.fnc ) {
756                     struct status_table_s t, *r;
757                     char *rest;
758
759                     rest = strchr (buffer+9, ' ');
760                     if ( !rest )
761                         rest = p; /* set to an empty string */
762                     else
763                         *rest++ = 0;
764                     
765                     t.name = buffer+9;
766                     /* (the status table as one extra element) */
767                     r = bsearch ( &t, status_table, DIM(status_table)-1,
768                                   sizeof t, status_cmp );
769                     if ( r ) {
770                         gpg->status.fnc ( gpg->status.fnc_value, 
771                                           r->code, rest);
772                     }
773                 }
774                 /* To reuse the buffer for the next line we have to
775                  * shift the remaining data to the buffer start and
776                  * restart the loop Hmmm: We can optimize this
777                  * function by looking forward in the buffer to see
778                  * whether a second complete line is available and in
779                  * this case avoid the memmove for this line.  */
780                 nread--; p++;
781                 if (nread)
782                     memmove (buffer, p, nread);
783                 readpos = 0;
784                 break; /* the for loop */
785             }
786             else
787                 readpos++;
788         }
789     } 
790
791     /* Update the gpg object.  */
792     gpg->status.bufsize = bufsize;
793     gpg->status.buffer = buffer;
794     gpg->status.readpos = readpos;
795     return 0;
796 }
797
798
799 /*
800  * This colonline handler thing is not the clean way to do it.
801  * It might be better to enhance the GpgmeData object to act as
802  * a wrapper for a callback.  Same goes for the status thing.
803  * For now we use this thing here becuase it is easier to implement.
804  */
805 static int
806 gpg_colon_line_handler ( void *opaque, pid_t pid, int fd )
807 {
808     GpgObject gpg = opaque;
809     GpgmeError rc = 0;
810
811     assert ( fd == gpg->colon.fd[0] );
812     rc = read_colon_line ( gpg );
813     if ( rc ) {
814         fprintf (stderr, "gpg_colon_line_handler: "
815                  "read problem %d\n - stop", rc);
816         return 1;
817     }
818
819     return gpg->status.eof;
820 }
821
822 static GpgmeError
823 read_colon_line ( GpgObject gpg )
824 {
825     char *p;
826     int nread;
827     size_t bufsize = gpg->colon.bufsize; 
828     char *buffer = gpg->colon.buffer;
829     size_t readpos = gpg->colon.readpos; 
830
831     assert (buffer);
832     if (bufsize - readpos < 256) { 
833         /* need more room for the read */
834         bufsize += 1024;
835         buffer = xtryrealloc (buffer, bufsize);
836         if ( !buffer ) 
837             return mk_error (Out_Of_Core);
838     }
839     
840
841     do { 
842         nread = read ( gpg->colon.fd[0], buffer+readpos, bufsize-readpos );
843     } while (nread == -1 && errno == EINTR);
844
845     if (nread == -1)
846         return mk_error(Read_Error);
847
848     if (!nread) {
849         gpg->colon.eof = 1;
850         assert (gpg->colon.fnc);
851         gpg->colon.fnc ( gpg->colon.fnc_value, NULL );
852         return 0;
853     }
854
855     while (nread > 0) {
856         for (p = buffer + readpos; nread; nread--, p++) {
857             if ( *p == '\n' ) {
858                 /* (we require that the last line is terminated by a
859                  * LF) and we skip empty lines.  Note: we use UTF8
860                  * encoding and escaping of special characters
861                  * We require at least one colon to cope with
862                  * some other printed information.
863                  */
864                 *p = 0;
865                 if ( *buffer && strchr (buffer, ':') ) {
866                     assert (gpg->colon.fnc);
867                     gpg->colon.fnc ( gpg->colon.fnc_value, buffer );
868                 }
869             
870                 /* To reuse the buffer for the next line we have to
871                  * shift the remaining data to the buffer start and
872                  * restart the loop Hmmm: We can optimize this
873                  * function by looking forward in the buffer to see
874                  * whether a second complete line is available and in
875                  * this case avoid the memmove for this line.  */
876                 nread--; p++;
877                 if (nread)
878                     memmove (buffer, p, nread);
879                 readpos = 0;
880                 break; /* the for loop */
881             }
882             else
883                 readpos++;
884         }
885     } 
886     
887     /* Update the gpg object.  */
888     gpg->colon.bufsize = bufsize;
889     gpg->colon.buffer  = buffer;
890     gpg->colon.readpos = readpos;
891     return 0;
892 }
893