b7a3943ca06a8364f8d0ea8204648df635f625cd
[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 <signal.h>
31 #include <fcntl.h>
32
33 #include "gpgme.h"
34 #include "util.h"
35 #include "ops.h"
36 #include "wait.h"
37 #include "rungpg.h"
38 #include "context.h"  /*temp hack until we have GpmeData methods to do I/O */
39 #include "io.h"
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     int pid; /* we can't use pid_t because we don't use it in Windoze */
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, int pid, int fd );
102 static int gpg_outbound_handler ( void *opaque, int pid, int fd );
103
104 static int gpg_status_handler ( void *opaque, int pid, int fd );
105 static GpgmeError read_status ( GpgObject gpg );
106
107 static int gpg_colon_line_handler ( void *opaque, int 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 (_gpgme_io_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         _gpgme_io_close (gpg->status.fd[0]);
175     if (gpg->status.fd[1] != -1 )
176         _gpgme_io_close (gpg->status.fd[1]);
177     if (gpg->colon.fd[0] != -1 )
178         _gpgme_io_close (gpg->colon.fd[0]);
179     if (gpg->colon.fd[1] != -1 )
180         _gpgme_io_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     
205 GpgmeError
206 _gpgme_gpg_add_arg ( GpgObject gpg, const char *arg )
207 {
208     struct arg_and_data_s *a;
209
210     assert (gpg);
211     assert (arg);
212     a = xtrymalloc ( sizeof *a + strlen (arg) );
213     if ( !a ) {
214         gpg->arg_error = 1;
215         return mk_error(Out_Of_Core);
216     }
217     a->next = NULL;
218     a->data = NULL;
219     a->dup_to = -1;
220     strcpy ( a->arg, arg );
221     *gpg->argtail = a;
222     gpg->argtail = &a->next;
223     return 0;
224 }
225
226 GpgmeError
227 _gpgme_gpg_add_data ( GpgObject gpg, GpgmeData data, int dup_to )
228 {
229     struct arg_and_data_s *a;
230
231     assert (gpg);
232     assert (data);
233     a = xtrymalloc ( sizeof *a - 1 );
234     if ( !a ) {
235         gpg->arg_error = 1;
236         return mk_error(Out_Of_Core);
237     }
238     a->next = NULL;
239     a->data = data;
240     a->dup_to = dup_to;
241     *gpg->argtail = a;
242     gpg->argtail = &a->next;
243     return 0;
244 }
245
246 /*
247  * Note, that the status_handler is allowed to modifiy the args value
248  */
249 void
250 _gpgme_gpg_set_status_handler ( GpgObject gpg,
251                                 GpgStatusHandler fnc, void *fnc_value ) 
252 {
253     assert (gpg);
254     gpg->status.fnc = fnc;
255     gpg->status.fnc_value = fnc_value;
256 }
257
258 /* Kludge to process --with-colon output */
259 GpgmeError
260 _gpgme_gpg_set_colon_line_handler ( GpgObject gpg,
261                                     GpgColonLineHandler fnc, void *fnc_value ) 
262 {
263     assert (gpg);
264
265     gpg->colon.bufsize = 1024;
266     gpg->colon.readpos = 0;
267     gpg->colon.buffer = xtrymalloc (gpg->colon.bufsize);
268     if (!gpg->colon.buffer) {
269         return mk_error (Out_Of_Core);
270     }
271     if (_gpgme_io_pipe (gpg->colon.fd) == -1) {
272         xfree (gpg->colon.buffer); gpg->colon.buffer = NULL;
273         return mk_error (Pipe_Error);
274     }
275     gpg->colon.eof = 0;
276     gpg->colon.fnc = fnc;
277     gpg->colon.fnc_value = fnc_value;
278     return 0;
279 }
280
281
282 static void
283 free_argv ( char **argv )
284 {
285     int i;
286
287     for (i=0; argv[i]; i++ )
288         xfree (argv[i]);
289     xfree (argv);
290 }
291
292 static void
293 free_fd_data_map ( struct fd_data_map_s *fd_data_map )
294 {
295     int i;
296
297     for (i=0; fd_data_map[i].data; i++ ) {
298         if ( fd_data_map[i].fd != -1 )
299             _gpgme_io_close (fd_data_map[i].fd);
300         if ( fd_data_map[i].peer_fd != -1 )
301             _gpgme_io_close (fd_data_map[i].peer_fd);
302         /* don't realease data because this is only a reference */
303     }
304     xfree (fd_data_map);
305 }
306
307
308 static GpgmeError
309 build_argv ( GpgObject gpg )
310 {
311     struct arg_and_data_s *a;
312     struct fd_data_map_s *fd_data_map;
313     size_t datac=0, argc=0;  
314     char **argv;
315     int need_special = 0;
316     int use_agent = !!getenv ("GPG_AGENT_INFO");
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 (_gpgme_io_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, n;
468     int pid;
469     struct spawn_fd_item_s *fd_child_list, *fd_parent_list;
470
471     if ( !gpg )
472         return mk_error (Invalid_Value);
473
474     /* Kludge, so that we don't need to check the return code of
475      * all the gpgme_gpg_add_arg().  we bail out here instead */
476     if ( gpg->arg_error )
477         return mk_error (Out_Of_Core);
478
479     rc = build_argv ( gpg );
480     if ( rc )
481         return rc;
482
483     n = 4; /* status fd, 2*colon_fd and end of list */
484     for (i=0; gpg->fd_data_map[i].data; i++ ) 
485         n += 2;
486     fd_child_list = xtrycalloc ( n+n, sizeof *fd_child_list );
487     if (!fd_child_list)
488         return mk_error (Out_Of_Core);
489     fd_parent_list = fd_child_list + n;
490
491     /* build the fd list for the child */
492     n=0;
493     fd_child_list[n].fd = gpg->status.fd[0];
494     fd_child_list[n].dup_to = -1;
495     n++;
496     if ( gpg->colon.fnc ) {
497         fd_child_list[n].fd = gpg->colon.fd[0];
498         fd_child_list[n].dup_to = -1;
499         n++;
500         fd_child_list[n].fd = gpg->colon.fd[1]; 
501         fd_child_list[n].dup_to = 1; /* dup to stdout */
502         n++;
503     }
504     for (i=0; gpg->fd_data_map[i].data; i++ ) {
505         fd_child_list[n].fd = gpg->fd_data_map[i].fd;
506         fd_child_list[n].dup_to = -1;
507         n++;
508         if (gpg->fd_data_map[i].dup_to != -1) {
509             fd_child_list[n].fd = gpg->fd_data_map[i].peer_fd;
510             fd_child_list[n].dup_to = gpg->fd_data_map[i].dup_to;
511             n++;
512         }
513     }
514     fd_child_list[n].fd = -1;
515     fd_child_list[n].dup_to = -1;
516
517     /* build the fd list for the parent */
518     n=0;
519     if ( gpg->status.fd[1] != -1 ) {
520         fd_parent_list[n].fd = gpg->status.fd[1];
521         fd_parent_list[n].dup_to = -1;
522         n++;
523         gpg->status.fd[1] = -1;
524     }
525     if ( gpg->colon.fd[1] != -1 ) {
526         fd_parent_list[n].fd = gpg->colon.fd[1];
527         fd_parent_list[n].dup_to = -1;
528         n++;
529         gpg->colon.fd[1] = -1;
530     }
531     for (i=0; gpg->fd_data_map[i].data; i++ ) {
532         fd_parent_list[n].fd = gpg->fd_data_map[i].peer_fd;
533         fd_parent_list[n].dup_to = -1;
534         n++;
535         gpg->fd_data_map[i].peer_fd = -1;
536     }        
537     fd_parent_list[n].fd = -1;
538     fd_parent_list[n].dup_to = -1;
539
540
541     fflush (stderr);
542     pid = _gpgme_io_spawn (GPG_PATH, gpg->argv, fd_child_list, fd_parent_list);
543     xfree (fd_child_list);
544     if (pid == -1) {
545         return mk_error (Exec_Error);
546     }
547
548     gpg->pid = pid;
549
550     /*_gpgme_register_term_handler ( closure, closure_value, pid );*/
551
552     if ( _gpgme_register_pipe_handler ( opaque, gpg_status_handler,
553                                         gpg, pid, gpg->status.fd[0], 1 ) ) {
554         /* FIXME: kill the child */
555         return mk_error (General_Error);
556
557     }
558
559     if ( gpg->colon.fnc ) {
560         assert ( gpg->colon.fd[0] != -1 );
561         if ( _gpgme_register_pipe_handler ( opaque, gpg_colon_line_handler,
562                                             gpg, pid, gpg->colon.fd[0], 1 ) ) {
563             /* FIXME: kill the child */
564             return mk_error (General_Error);
565             
566         }
567     }
568
569     for (i=0; gpg->fd_data_map[i].data; i++ ) {
570         /* Due to problems with select and write we set outbound pipes
571          * to non-blocking */
572         if (!gpg->fd_data_map[i].inbound) {
573             _gpgme_io_set_nonblocking (gpg->fd_data_map[i].fd);
574         }
575
576         if ( _gpgme_register_pipe_handler (
577                  opaque, 
578                  gpg->fd_data_map[i].inbound?
579                        gpg_inbound_handler:gpg_outbound_handler,
580                  gpg->fd_data_map[i].data,
581                  pid, gpg->fd_data_map[i].fd,
582                  gpg->fd_data_map[i].inbound )
583            ) {
584             /* FIXME: kill the child */
585             return mk_error (General_Error);
586         }
587     }
588
589     /* fixme: check what data we can release here */
590
591     gpg->running = 1;
592     return 0;
593 }
594
595
596 static int
597 gpg_inbound_handler ( void *opaque, int pid, int fd )
598 {
599     GpgmeData dh = opaque;
600     GpgmeError err;
601     int nread;
602     char buf[200];
603
604     assert ( _gpgme_data_get_mode (dh) == GPGME_DATA_MODE_IN );
605
606     nread = _gpgme_io_read (fd, buf, 200 );
607     fprintf(stderr, "inbound on fd %d: nread=%d\n", fd, nread );
608     if ( nread < 0 ) {
609         fprintf (stderr, "read_mem_data: read failed on fd %d (n=%d): %s\n",
610                  fd, nread, strerror (errno) );
611         return 1;
612     }
613     else if (!nread)
614         return 1; /* eof */
615
616     /* We could improve this with a GpgmeData function which takes
617      * the read function or provides a memory area for writing to it.
618      */
619     
620     err = _gpgme_data_append ( dh, buf, nread );
621     if ( err ) {
622         fprintf (stderr, "_gpgme_append_data failed: %s\n",
623                  gpgme_strerror(err));
624         /* Fixme: we should close the pipe or read it to /dev/null in
625          * this case. Returnin EOF is not sufficient */
626         return 1;
627     }
628
629     return 0;
630 }
631
632
633 static int
634 write_mem_data ( GpgmeData dh, int fd )
635 {
636     size_t nbytes;
637     int  nwritten; 
638
639     nbytes = dh->len - dh->readpos;
640     if ( !nbytes ) {
641         fprintf (stderr, "write_mem_data(%d): closing\n", fd );
642         _gpgme_io_close (fd);
643         return 1;
644     }
645     
646     /* FIXME: Arggg, the pipe blocks on large write request, although
647      * select told us that it is okay to write - need to figure out
648      * why this happens?  Stevens says nothing about this problem (or
649      * is it my Linux kernel 2.4.0test1)
650      * To avoid that we have set the pipe to nonblocking.
651      */
652
653
654     fprintf (stderr, "write_mem_data(%d): about to write %d bytes len=%d rpos=%d\n",
655                  fd, (int)nbytes, (int)dh->len, dh->readpos );
656     nwritten = _gpgme_io_write ( fd, dh->data+dh->readpos, nbytes );
657     fprintf (stderr, "write_mem_data(%d): wrote %d bytes\n", fd, nwritten );
658     if (nwritten == -1 && errno == EAGAIN )
659         return 0;
660     if ( nwritten < 1 ) {
661         fprintf (stderr, "write_mem_data(%d): write failed (n=%d): %s\n",
662                  fd, nwritten, strerror (errno) );
663         _gpgme_io_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, int 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, int 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     nread = _gpgme_io_read ( gpg->status.fd[0],
749                              buffer+readpos, bufsize-readpos );
750     if (nread == -1)
751         return mk_error(Read_Error);
752
753     if (!nread) {
754         gpg->status.eof = 1;
755         if (gpg->status.fnc)
756             gpg->status.fnc ( gpg->status.fnc_value, STATUS_EOF, "" );
757         return 0;
758     }
759
760     while (nread > 0) {
761         for (p = buffer + readpos; nread; nread--, p++) {
762             if ( *p == '\n' ) {
763                 /* (we require that the last line is terminated by a LF) */
764                 *p = 0;
765                 fprintf (stderr, "read_status: `%s'\n", buffer);
766                 if (!strncmp (buffer, "[GNUPG:] ", 9 )
767                     && buffer[9] >= 'A' && buffer[9] <= 'Z'
768                     && gpg->status.fnc ) {
769                     struct status_table_s t, *r;
770                     char *rest;
771
772                     rest = strchr (buffer+9, ' ');
773                     if ( !rest )
774                         rest = p; /* set to an empty string */
775                     else
776                         *rest++ = 0;
777                     
778                     t.name = buffer+9;
779                     /* (the status table as one extra element) */
780                     r = bsearch ( &t, status_table, DIM(status_table)-1,
781                                   sizeof t, status_cmp );
782                     if ( r ) {
783                         gpg->status.fnc ( gpg->status.fnc_value, 
784                                           r->code, rest);
785                     }
786                 }
787                 /* To reuse the buffer for the next line we have to
788                  * shift the remaining data to the buffer start and
789                  * restart the loop Hmmm: We can optimize this
790                  * function by looking forward in the buffer to see
791                  * whether a second complete line is available and in
792                  * this case avoid the memmove for this line.  */
793                 nread--; p++;
794                 if (nread)
795                     memmove (buffer, p, nread);
796                 readpos = 0;
797                 break; /* the for loop */
798             }
799             else
800                 readpos++;
801         }
802     } 
803
804     /* Update the gpg object.  */
805     gpg->status.bufsize = bufsize;
806     gpg->status.buffer = buffer;
807     gpg->status.readpos = readpos;
808     return 0;
809 }
810
811
812 /*
813  * This colonline handler thing is not the clean way to do it.
814  * It might be better to enhance the GpgmeData object to act as
815  * a wrapper for a callback.  Same goes for the status thing.
816  * For now we use this thing here becuase it is easier to implement.
817  */
818 static int
819 gpg_colon_line_handler ( void *opaque, int pid, int fd )
820 {
821     GpgObject gpg = opaque;
822     GpgmeError rc = 0;
823
824     assert ( fd == gpg->colon.fd[0] );
825     rc = read_colon_line ( gpg );
826     if ( rc ) {
827         fprintf (stderr, "gpg_colon_line_handler: "
828                  "read problem %d\n - stop", rc);
829         return 1;
830     }
831
832     return gpg->status.eof;
833 }
834
835 static GpgmeError
836 read_colon_line ( GpgObject gpg )
837 {
838     char *p;
839     int nread;
840     size_t bufsize = gpg->colon.bufsize; 
841     char *buffer = gpg->colon.buffer;
842     size_t readpos = gpg->colon.readpos; 
843
844     assert (buffer);
845     if (bufsize - readpos < 256) { 
846         /* need more room for the read */
847         bufsize += 1024;
848         buffer = xtryrealloc (buffer, bufsize);
849         if ( !buffer ) 
850             return mk_error (Out_Of_Core);
851     }
852     
853
854     nread = _gpgme_io_read ( gpg->colon.fd[0],
855                              buffer+readpos, bufsize-readpos );
856     if (nread == -1)
857         return mk_error(Read_Error);
858
859     if (!nread) {
860         gpg->colon.eof = 1;
861         assert (gpg->colon.fnc);
862         gpg->colon.fnc ( gpg->colon.fnc_value, NULL );
863         return 0;
864     }
865
866     while (nread > 0) {
867         for (p = buffer + readpos; nread; nread--, p++) {
868             if ( *p == '\n' ) {
869                 /* (we require that the last line is terminated by a
870                  * LF) and we skip empty lines.  Note: we use UTF8
871                  * encoding and escaping of special characters
872                  * We require at least one colon to cope with
873                  * some other printed information.
874                  */
875                 *p = 0;
876                 if ( *buffer && strchr (buffer, ':') ) {
877                     assert (gpg->colon.fnc);
878                     gpg->colon.fnc ( gpg->colon.fnc_value, buffer );
879                 }
880             
881                 /* To reuse the buffer for the next line we have to
882                  * shift the remaining data to the buffer start and
883                  * restart the loop Hmmm: We can optimize this
884                  * function by looking forward in the buffer to see
885                  * whether a second complete line is available and in
886                  * this case avoid the memmove for this line.  */
887                 nread--; p++;
888                 if (nread)
889                     memmove (buffer, p, nread);
890                 readpos = 0;
891                 break; /* the for loop */
892             }
893             else
894                 readpos++;
895         }
896     } 
897     
898     /* Update the gpg object.  */
899     gpg->colon.bufsize = bufsize;
900     gpg->colon.buffer  = buffer;
901     gpg->colon.readpos = readpos;
902     return 0;
903 }
904