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