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