core: Fail loudly in case w32 spawner not found
[gpgme.git] / src / engine-uiserver.c
1 /* engine-uiserver.c - Uiserver engine.
2    Copyright (C) 2000 Werner Koch (dd9jn)
3    Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2009 g10 Code GmbH
4
5    This file is part of GPGME.
6
7    GPGME is free software; you can redistribute it and/or modify it
8    under the terms of the GNU Lesser General Public License as
9    published by the Free Software Foundation; either version 2.1 of
10    the License, or (at your option) any later version.
11
12    GPGME is distributed in the hope that it will be useful, but
13    WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    Lesser General Public License for more details.
16
17    You should have received a copy of the GNU Lesser General Public
18    License along with this program; if not, write to the Free Software
19    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20    02111-1307, USA.  */
21
22 /* Peculiar: Use special keys from email address for recipient and
23    signer (==sender).  Use no data objects with encryption for
24    prep_encrypt.  */
25
26 #if HAVE_CONFIG_H
27 #include <config.h>
28 #endif
29
30 #include <stdlib.h>
31 #include <string.h>
32 #ifdef HAVE_SYS_TYPES_H
33 # include <sys/types.h>
34 #endif
35 #include <assert.h>
36 #ifdef HAVE_UNISTD_H
37 # include <unistd.h>
38 #endif
39 #include <locale.h>
40 #include <fcntl.h> /* FIXME */
41 #include <errno.h>
42
43 #include "gpgme.h"
44 #include "util.h"
45 #include "ops.h"
46 #include "wait.h"
47 #include "priv-io.h"
48 #include "sema.h"
49 #include "data.h"
50
51 #include "assuan.h"
52 #include "debug.h"
53
54 #include "engine-backend.h"
55
56 \f
57 typedef struct
58 {
59   int fd;       /* FD we talk about.  */
60   int server_fd;/* Server FD for this connection.  */
61   int dir;      /* Inbound/Outbound, maybe given implicit?  */
62   void *data;   /* Handler-specific data.  */
63   void *tag;    /* ID from the user for gpgme_remove_io_callback.  */
64   char server_fd_str[15]; /* Same as SERVER_FD but as a string.  We
65                              need this because _gpgme_io_fd2str can't
66                              be used on a closed descriptor.  */
67 } iocb_data_t;
68
69
70 struct engine_uiserver
71 {
72   assuan_context_t assuan_ctx;
73
74   int lc_ctype_set;
75   int lc_messages_set;
76   gpgme_protocol_t protocol;
77
78   iocb_data_t status_cb;
79
80   /* Input, output etc are from the servers perspective.  */
81   iocb_data_t input_cb;
82   gpgme_data_t input_helper_data;  /* Input helper data object.  */
83   void *input_helper_memory;       /* Input helper memory block.  */
84
85   iocb_data_t output_cb;
86
87   iocb_data_t message_cb;
88
89   struct
90   {
91     engine_status_handler_t fnc;
92     void *fnc_value;
93     gpgme_status_cb_t mon_cb;
94     void *mon_cb_value;
95   } status;
96
97   struct
98   {
99     engine_colon_line_handler_t fnc;
100     void *fnc_value;
101     struct
102     {
103       char *line;
104       int linesize;
105       int linelen;
106     } attic;
107     int any; /* any data line seen */
108   } colon;
109
110   gpgme_data_t inline_data;  /* Used to collect D lines.  */
111
112   struct gpgme_io_cbs io_cbs;
113 };
114
115 typedef struct engine_uiserver *engine_uiserver_t;
116
117
118 static void uiserver_io_event (void *engine,
119                             gpgme_event_io_t type, void *type_data);
120
121
122 \f
123 static char *
124 uiserver_get_version (const char *file_name)
125 {
126   (void)file_name;
127   return NULL;
128 }
129
130
131 static const char *
132 uiserver_get_req_version (void)
133 {
134   return NULL;
135 }
136
137 \f
138 static void
139 close_notify_handler (int fd, void *opaque)
140 {
141   engine_uiserver_t uiserver = opaque;
142
143   assert (fd != -1);
144   if (uiserver->status_cb.fd == fd)
145     {
146       if (uiserver->status_cb.tag)
147         (*uiserver->io_cbs.remove) (uiserver->status_cb.tag);
148       uiserver->status_cb.fd = -1;
149       uiserver->status_cb.tag = NULL;
150     }
151   else if (uiserver->input_cb.fd == fd)
152     {
153       if (uiserver->input_cb.tag)
154         (*uiserver->io_cbs.remove) (uiserver->input_cb.tag);
155       uiserver->input_cb.fd = -1;
156       uiserver->input_cb.tag = NULL;
157       if (uiserver->input_helper_data)
158         {
159           gpgme_data_release (uiserver->input_helper_data);
160           uiserver->input_helper_data = NULL;
161         }
162       if (uiserver->input_helper_memory)
163         {
164           free (uiserver->input_helper_memory);
165           uiserver->input_helper_memory = NULL;
166         }
167     }
168   else if (uiserver->output_cb.fd == fd)
169     {
170       if (uiserver->output_cb.tag)
171         (*uiserver->io_cbs.remove) (uiserver->output_cb.tag);
172       uiserver->output_cb.fd = -1;
173       uiserver->output_cb.tag = NULL;
174     }
175   else if (uiserver->message_cb.fd == fd)
176     {
177       if (uiserver->message_cb.tag)
178         (*uiserver->io_cbs.remove) (uiserver->message_cb.tag);
179       uiserver->message_cb.fd = -1;
180       uiserver->message_cb.tag = NULL;
181     }
182 }
183
184
185 /* This is the default inquiry callback.  We use it to handle the
186    Pinentry notifications.  */
187 static gpgme_error_t
188 default_inq_cb (engine_uiserver_t uiserver, const char *line)
189 {
190   if (!strncmp (line, "PINENTRY_LAUNCHED", 17) && (line[17]==' '||!line[17]))
191     {
192       _gpgme_allow_set_foreground_window ((pid_t)strtoul (line+17, NULL, 10));
193     }
194
195   return 0;
196 }
197
198
199 static gpgme_error_t
200 uiserver_cancel (void *engine)
201 {
202   engine_uiserver_t uiserver = engine;
203
204   if (!uiserver)
205     return gpg_error (GPG_ERR_INV_VALUE);
206
207   if (uiserver->status_cb.fd != -1)
208     _gpgme_io_close (uiserver->status_cb.fd);
209   if (uiserver->input_cb.fd != -1)
210     _gpgme_io_close (uiserver->input_cb.fd);
211   if (uiserver->output_cb.fd != -1)
212     _gpgme_io_close (uiserver->output_cb.fd);
213   if (uiserver->message_cb.fd != -1)
214     _gpgme_io_close (uiserver->message_cb.fd);
215
216   if (uiserver->assuan_ctx)
217     {
218       assuan_release (uiserver->assuan_ctx);
219       uiserver->assuan_ctx = NULL;
220     }
221
222   return 0;
223 }
224
225
226 static void
227 uiserver_release (void *engine)
228 {
229   engine_uiserver_t uiserver = engine;
230
231   if (!uiserver)
232     return;
233
234   uiserver_cancel (engine);
235
236   free (uiserver->colon.attic.line);
237   free (uiserver);
238 }
239
240
241 static gpgme_error_t
242 uiserver_new (void **engine, const char *file_name, const char *home_dir,
243               const char *version)
244 {
245   gpgme_error_t err = 0;
246   engine_uiserver_t uiserver;
247   char *dft_display = NULL;
248   char dft_ttyname[64];
249   char *dft_ttytype = NULL;
250   char *optstr;
251
252   (void)version; /* Not yet used.  */
253
254   uiserver = calloc (1, sizeof *uiserver);
255   if (!uiserver)
256     return gpg_error_from_syserror ();
257
258   uiserver->protocol = GPGME_PROTOCOL_DEFAULT;
259   uiserver->status_cb.fd = -1;
260   uiserver->status_cb.dir = 1;
261   uiserver->status_cb.tag = 0;
262   uiserver->status_cb.data = uiserver;
263
264   uiserver->input_cb.fd = -1;
265   uiserver->input_cb.dir = 0;
266   uiserver->input_cb.tag = 0;
267   uiserver->input_cb.server_fd = -1;
268   *uiserver->input_cb.server_fd_str = 0;
269   uiserver->output_cb.fd = -1;
270   uiserver->output_cb.dir = 1;
271   uiserver->output_cb.tag = 0;
272   uiserver->output_cb.server_fd = -1;
273   *uiserver->output_cb.server_fd_str = 0;
274   uiserver->message_cb.fd = -1;
275   uiserver->message_cb.dir = 0;
276   uiserver->message_cb.tag = 0;
277   uiserver->message_cb.server_fd = -1;
278   *uiserver->message_cb.server_fd_str = 0;
279
280   uiserver->status.fnc = 0;
281   uiserver->colon.fnc = 0;
282   uiserver->colon.attic.line = 0;
283   uiserver->colon.attic.linesize = 0;
284   uiserver->colon.attic.linelen = 0;
285   uiserver->colon.any = 0;
286
287   uiserver->inline_data = NULL;
288
289   uiserver->io_cbs.add = NULL;
290   uiserver->io_cbs.add_priv = NULL;
291   uiserver->io_cbs.remove = NULL;
292   uiserver->io_cbs.event = NULL;
293   uiserver->io_cbs.event_priv = NULL;
294
295   err = assuan_new_ext (&uiserver->assuan_ctx, GPG_ERR_SOURCE_GPGME,
296                         &_gpgme_assuan_malloc_hooks, _gpgme_assuan_log_cb,
297                         NULL);
298   if (err)
299     goto leave;
300   assuan_ctx_set_system_hooks (uiserver->assuan_ctx,
301                                &_gpgme_assuan_system_hooks);
302
303   err = assuan_socket_connect (uiserver->assuan_ctx,
304                                file_name ?
305                                file_name : _gpgme_get_default_uisrv_socket (),
306                                0, ASSUAN_SOCKET_SERVER_FDPASSING);
307   if (err)
308     goto leave;
309
310   err = _gpgme_getenv ("DISPLAY", &dft_display);
311   if (err)
312     goto leave;
313   if (dft_display)
314     {
315       if (asprintf (&optstr, "OPTION display=%s", dft_display) < 0)
316         {
317           err = gpg_error_from_syserror ();
318           free (dft_display);
319           goto leave;
320         }
321       free (dft_display);
322
323       err = assuan_transact (uiserver->assuan_ctx, optstr, NULL, NULL, NULL,
324                              NULL, NULL, NULL);
325       free (optstr);
326       if (err)
327         goto leave;
328     }
329
330   if (isatty (1))
331     {
332       int rc;
333
334       rc = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname));
335
336       /* Even though isatty() returns 1, ttyname_r() may fail in many
337          ways, e.g., when /dev/pts is not accessible under chroot.  */
338       if (!rc)
339         {
340           if (asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0)
341             {
342               err = gpg_error_from_syserror ();
343               goto leave;
344             }
345           err = assuan_transact (uiserver->assuan_ctx, optstr, NULL, NULL, NULL,
346                                  NULL, NULL, NULL);
347           free (optstr);
348           if (err)
349             goto leave;
350
351           err = _gpgme_getenv ("TERM", &dft_ttytype);
352           if (err)
353             goto leave;
354           if (dft_ttytype)
355             {
356               if (asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype) < 0)
357                 {
358                   err = gpg_error_from_syserror ();
359                   free (dft_ttytype);
360                   goto leave;
361                 }
362               free (dft_ttytype);
363
364               err = assuan_transact (uiserver->assuan_ctx, optstr, NULL, NULL,
365                                      NULL, NULL, NULL, NULL);
366               free (optstr);
367               if (err)
368                 goto leave;
369             }
370         }
371     }
372
373 #ifdef HAVE_W32_SYSTEM
374   /* Under Windows we need to use AllowSetForegroundWindow.  Tell
375      uiserver to tell us when it needs it.  */
376   if (!err)
377     {
378       err = assuan_transact (uiserver->assuan_ctx, "OPTION allow-pinentry-notify",
379                              NULL, NULL, NULL, NULL, NULL, NULL);
380       if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
381         err = 0; /* This is a new feature of uiserver.  */
382     }
383 #endif /*HAVE_W32_SYSTEM*/
384
385  leave:
386   if (err)
387     uiserver_release (uiserver);
388   else
389     *engine = uiserver;
390
391   return err;
392 }
393
394
395 static gpgme_error_t
396 uiserver_set_locale (void *engine, int category, const char *value)
397 {
398   engine_uiserver_t uiserver = engine;
399   gpgme_error_t err;
400   char *optstr;
401   char *catstr;
402
403   /* FIXME: If value is NULL, we need to reset the option to default.
404      But we can't do this.  So we error out here.  UISERVER needs support
405      for this.  */
406   if (category == LC_CTYPE)
407     {
408       catstr = "lc-ctype";
409       if (!value && uiserver->lc_ctype_set)
410         return gpg_error (GPG_ERR_INV_VALUE);
411       if (value)
412         uiserver->lc_ctype_set = 1;
413     }
414 #ifdef LC_MESSAGES
415   else if (category == LC_MESSAGES)
416     {
417       catstr = "lc-messages";
418       if (!value && uiserver->lc_messages_set)
419         return gpg_error (GPG_ERR_INV_VALUE);
420       if (value)
421         uiserver->lc_messages_set = 1;
422     }
423 #endif /* LC_MESSAGES */
424   else
425     return gpg_error (GPG_ERR_INV_VALUE);
426
427   /* FIXME: Reset value to default.  */
428   if (!value)
429     return 0;
430
431   if (asprintf (&optstr, "OPTION %s=%s", catstr, value) < 0)
432     err = gpg_error_from_syserror ();
433   else
434     {
435       err = assuan_transact (uiserver->assuan_ctx, optstr, NULL, NULL,
436                              NULL, NULL, NULL, NULL);
437       free (optstr);
438     }
439
440   return err;
441 }
442
443
444 static gpgme_error_t
445 uiserver_set_protocol (void *engine, gpgme_protocol_t protocol)
446 {
447   engine_uiserver_t uiserver = engine;
448
449   if (protocol != GPGME_PROTOCOL_OpenPGP
450       && protocol != GPGME_PROTOCOL_CMS
451       && protocol != GPGME_PROTOCOL_DEFAULT)
452     return gpg_error (GPG_ERR_INV_VALUE);
453
454   uiserver->protocol = protocol;
455   return 0;
456 }
457
458
459 static gpgme_error_t
460 uiserver_assuan_simple_command (engine_uiserver_t uiserver, char *cmd,
461                                 engine_status_handler_t status_fnc,
462                                 void *status_fnc_value)
463 {
464   assuan_context_t ctx = uiserver->assuan_ctx;
465   gpg_error_t err;
466   char *line;
467   size_t linelen;
468
469   err = assuan_write_line (ctx, cmd);
470   if (err)
471     return err;
472
473   do
474     {
475       err = assuan_read_line (ctx, &line, &linelen);
476       if (err)
477         return err;
478
479       if (*line == '#' || !linelen)
480         continue;
481
482       if (linelen >= 2
483           && line[0] == 'O' && line[1] == 'K'
484           && (line[2] == '\0' || line[2] == ' '))
485         return 0;
486       else if (linelen >= 4
487           && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
488           && line[3] == ' ')
489         err = atoi (&line[4]);
490       else if (linelen >= 2
491                && line[0] == 'S' && line[1] == ' ')
492         {
493           char *rest;
494           gpgme_status_code_t r;
495
496           rest = strchr (line + 2, ' ');
497           if (!rest)
498             rest = line + linelen; /* set to an empty string */
499           else
500             *(rest++) = 0;
501
502           r = _gpgme_parse_status (line + 2);
503           if (uiserver->status.mon_cb && r != GPGME_STATUS_PROGRESS)
504             {
505               /* Note that we call the monitor even if we do
506                * not know the status code (r < 0).  */
507               err = uiserver->status.mon_cb (uiserver->status.mon_cb_value,
508                                              line + 2, rest);
509             }
510
511           if (err)
512             ;
513           else if (r >= 0 && status_fnc)
514             err = status_fnc (status_fnc_value, r, rest);
515           else
516             err = gpg_error (GPG_ERR_GENERAL);
517         }
518       else
519         err = gpg_error (GPG_ERR_GENERAL);
520     }
521   while (!err);
522
523   return err;
524 }
525
526
527 typedef enum { INPUT_FD, OUTPUT_FD, MESSAGE_FD } fd_type_t;
528
529 #define COMMANDLINELEN 40
530 static gpgme_error_t
531 uiserver_set_fd (engine_uiserver_t uiserver, fd_type_t fd_type, const char *opt)
532 {
533   gpg_error_t err = 0;
534   char line[COMMANDLINELEN];
535   char *which;
536   iocb_data_t *iocb_data;
537   int dir;
538
539   switch (fd_type)
540     {
541     case INPUT_FD:
542       which = "INPUT";
543       iocb_data = &uiserver->input_cb;
544       break;
545
546     case OUTPUT_FD:
547       which = "OUTPUT";
548       iocb_data = &uiserver->output_cb;
549       break;
550
551     case MESSAGE_FD:
552       which = "MESSAGE";
553       iocb_data = &uiserver->message_cb;
554       break;
555
556     default:
557       return gpg_error (GPG_ERR_INV_VALUE);
558     }
559
560   dir = iocb_data->dir;
561
562   /* We try to short-cut the communication by giving UISERVER direct
563      access to the file descriptor, rather than using a pipe.  */
564   iocb_data->server_fd = _gpgme_data_get_fd (iocb_data->data);
565   if (iocb_data->server_fd < 0)
566     {
567       int fds[2];
568
569       if (_gpgme_io_pipe (fds, 0) < 0)
570         return gpg_error_from_syserror ();
571
572       iocb_data->fd = dir ? fds[0] : fds[1];
573       iocb_data->server_fd = dir ? fds[1] : fds[0];
574
575       if (_gpgme_io_set_close_notify (iocb_data->fd,
576                                       close_notify_handler, uiserver))
577         {
578           err = gpg_error (GPG_ERR_GENERAL);
579           goto leave_set_fd;
580         }
581     }
582
583   err = assuan_sendfd (uiserver->assuan_ctx, iocb_data->server_fd);
584   if (err)
585     goto leave_set_fd;
586
587   _gpgme_io_close (iocb_data->server_fd);
588   iocb_data->server_fd = -1;
589
590   if (opt)
591     snprintf (line, COMMANDLINELEN, "%s FD %s", which, opt);
592   else
593     snprintf (line, COMMANDLINELEN, "%s FD", which);
594
595   err = uiserver_assuan_simple_command (uiserver, line, NULL, NULL);
596
597  leave_set_fd:
598   if (err)
599     {
600       _gpgme_io_close (iocb_data->fd);
601       iocb_data->fd = -1;
602       if (iocb_data->server_fd != -1)
603         {
604           _gpgme_io_close (iocb_data->server_fd);
605           iocb_data->server_fd = -1;
606         }
607     }
608
609   return err;
610 }
611
612
613 static const char *
614 map_data_enc (gpgme_data_t d)
615 {
616   switch (gpgme_data_get_encoding (d))
617     {
618     case GPGME_DATA_ENCODING_NONE:
619       break;
620     case GPGME_DATA_ENCODING_BINARY:
621       return "--binary";
622     case GPGME_DATA_ENCODING_BASE64:
623       return "--base64";
624     case GPGME_DATA_ENCODING_ARMOR:
625       return "--armor";
626     default:
627       break;
628     }
629   return NULL;
630 }
631
632
633 static gpgme_error_t
634 status_handler (void *opaque, int fd)
635 {
636   struct io_cb_data *data = (struct io_cb_data *) opaque;
637   engine_uiserver_t uiserver = (engine_uiserver_t) data->handler_value;
638   gpgme_error_t err = 0;
639   char *line;
640   size_t linelen;
641
642   do
643     {
644       err = assuan_read_line (uiserver->assuan_ctx, &line, &linelen);
645       if (err)
646         {
647           /* Try our best to terminate the connection friendly.  */
648           /*      assuan_write_line (uiserver->assuan_ctx, "BYE"); */
649           TRACE3 (DEBUG_CTX, "gpgme:status_handler", uiserver,
650                   "fd 0x%x: error from assuan (%d) getting status line : %s",
651                   fd, err, gpg_strerror (err));
652         }
653       else if (linelen >= 3
654                && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
655                && (line[3] == '\0' || line[3] == ' '))
656         {
657           if (line[3] == ' ')
658             err = atoi (&line[4]);
659           if (! err)
660             err = gpg_error (GPG_ERR_GENERAL);
661           TRACE2 (DEBUG_CTX, "gpgme:status_handler", uiserver,
662                   "fd 0x%x: ERR line - mapped to: %s",
663                   fd, err ? gpg_strerror (err) : "ok");
664           /* Try our best to terminate the connection friendly.  */
665           /*      assuan_write_line (uiserver->assuan_ctx, "BYE"); */
666         }
667       else if (linelen >= 2
668                && line[0] == 'O' && line[1] == 'K'
669                && (line[2] == '\0' || line[2] == ' '))
670         {
671           if (uiserver->status.fnc)
672             err = uiserver->status.fnc (uiserver->status.fnc_value,
673                                      GPGME_STATUS_EOF, "");
674
675           if (!err && uiserver->colon.fnc && uiserver->colon.any)
676             {
677               /* We must tell a colon function about the EOF. We do
678                  this only when we have seen any data lines.  Note
679                  that this inlined use of colon data lines will
680                  eventually be changed into using a regular data
681                  channel. */
682               uiserver->colon.any = 0;
683               err = uiserver->colon.fnc (uiserver->colon.fnc_value, NULL);
684             }
685           TRACE2 (DEBUG_CTX, "gpgme:status_handler", uiserver,
686                   "fd 0x%x: OK line - final status: %s",
687                   fd, err ? gpg_strerror (err) : "ok");
688           _gpgme_io_close (uiserver->status_cb.fd);
689           return err;
690         }
691       else if (linelen > 2
692                && line[0] == 'D' && line[1] == ' '
693                && uiserver->colon.fnc)
694         {
695           /* We are using the colon handler even for plain inline data
696              - strange name for that function but for historic reasons
697              we keep it.  */
698           /* FIXME We can't use this for binary data because we
699              assume this is a string.  For the current usage of colon
700              output it is correct.  */
701           char *src = line + 2;
702           char *end = line + linelen;
703           char *dst;
704           char **aline = &uiserver->colon.attic.line;
705           int *alinelen = &uiserver->colon.attic.linelen;
706
707           if (uiserver->colon.attic.linesize < *alinelen + linelen + 1)
708             {
709               char *newline = realloc (*aline, *alinelen + linelen + 1);
710               if (!newline)
711                 err = gpg_error_from_syserror ();
712               else
713                 {
714                   *aline = newline;
715                   uiserver->colon.attic.linesize = *alinelen + linelen + 1;
716                 }
717             }
718           if (!err)
719             {
720               dst = *aline + *alinelen;
721
722               while (!err && src < end)
723                 {
724                   if (*src == '%' && src + 2 < end)
725                     {
726                       /* Handle escaped characters.  */
727                       ++src;
728                       *dst = _gpgme_hextobyte (src);
729                       (*alinelen)++;
730                       src += 2;
731                     }
732                   else
733                     {
734                       *dst = *src++;
735                       (*alinelen)++;
736                     }
737
738                   if (*dst == '\n')
739                     {
740                       /* Terminate the pending line, pass it to the colon
741                          handler and reset it.  */
742
743                       uiserver->colon.any = 1;
744                       if (*alinelen > 1 && *(dst - 1) == '\r')
745                         dst--;
746                       *dst = '\0';
747
748                       /* FIXME How should we handle the return code?  */
749                       err = uiserver->colon.fnc (uiserver->colon.fnc_value, *aline);
750                       if (!err)
751                         {
752                           dst = *aline;
753                           *alinelen = 0;
754                         }
755                     }
756                   else
757                     dst++;
758                 }
759             }
760           TRACE2 (DEBUG_CTX, "gpgme:status_handler", uiserver,
761                   "fd 0x%x: D line; final status: %s",
762                   fd, err? gpg_strerror (err):"ok");
763         }
764       else if (linelen > 2
765                && line[0] == 'D' && line[1] == ' '
766                && uiserver->inline_data)
767         {
768           char *src = line + 2;
769           char *end = line + linelen;
770           char *dst = src;
771           gpgme_ssize_t nwritten;
772
773           linelen = 0;
774           while (src < end)
775             {
776               if (*src == '%' && src + 2 < end)
777                 {
778                   /* Handle escaped characters.  */
779                   ++src;
780                   *dst++ = _gpgme_hextobyte (src);
781                   src += 2;
782                 }
783               else
784                 *dst++ = *src++;
785
786               linelen++;
787             }
788
789           src = line + 2;
790           while (linelen > 0)
791             {
792               nwritten = gpgme_data_write (uiserver->inline_data, src, linelen);
793               if (!nwritten || (nwritten < 0 && errno != EINTR)
794                   || nwritten > linelen)
795                 {
796                   err = gpg_error_from_syserror ();
797                   break;
798                 }
799               src += nwritten;
800               linelen -= nwritten;
801             }
802
803           TRACE2 (DEBUG_CTX, "gpgme:status_handler", uiserver,
804                   "fd 0x%x: D inlinedata; final status: %s",
805                   fd, err? gpg_strerror (err):"ok");
806         }
807       else if (linelen > 2
808                && line[0] == 'S' && line[1] == ' ')
809         {
810           char *rest;
811           gpgme_status_code_t r;
812
813           rest = strchr (line + 2, ' ');
814           if (!rest)
815             rest = line + linelen; /* set to an empty string */
816           else
817             *(rest++) = 0;
818
819           r = _gpgme_parse_status (line + 2);
820
821           if (r >= 0)
822             {
823               if (uiserver->status.fnc)
824                 err = uiserver->status.fnc (uiserver->status.fnc_value, r, rest);
825             }
826           else
827             fprintf (stderr, "[UNKNOWN STATUS]%s %s", line + 2, rest);
828           TRACE3 (DEBUG_CTX, "gpgme:status_handler", uiserver,
829                   "fd 0x%x: S line (%s) - final status: %s",
830                   fd, line+2, err? gpg_strerror (err):"ok");
831         }
832       else if (linelen >= 7
833                && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
834                && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
835                && line[6] == 'E'
836                && (line[7] == '\0' || line[7] == ' '))
837         {
838           char *keyword = line+7;
839
840           while (*keyword == ' ')
841             keyword++;;
842           default_inq_cb (uiserver, keyword);
843           assuan_write_line (uiserver->assuan_ctx, "END");
844         }
845
846     }
847   while (!err && assuan_pending_line (uiserver->assuan_ctx));
848
849   return err;
850 }
851
852
853 static gpgme_error_t
854 add_io_cb (engine_uiserver_t uiserver, iocb_data_t *iocbd, gpgme_io_cb_t handler)
855 {
856   gpgme_error_t err;
857
858   TRACE_BEG2 (DEBUG_ENGINE, "engine-uiserver:add_io_cb", uiserver,
859               "fd %d, dir %d", iocbd->fd, iocbd->dir);
860   err = (*uiserver->io_cbs.add) (uiserver->io_cbs.add_priv,
861                               iocbd->fd, iocbd->dir,
862                               handler, iocbd->data, &iocbd->tag);
863   if (err)
864     return TRACE_ERR (err);
865   if (!iocbd->dir)
866     /* FIXME Kludge around poll() problem.  */
867     err = _gpgme_io_set_nonblocking (iocbd->fd);
868   return TRACE_ERR (err);
869 }
870
871
872 static gpgme_error_t
873 start (engine_uiserver_t uiserver, const char *command)
874 {
875   gpgme_error_t err;
876   int fdlist[5];
877   int nfds;
878
879   /* We need to know the fd used by assuan for reads.  We do this by
880      using the assumption that the first returned fd from
881      assuan_get_active_fds() is always this one.  */
882   nfds = assuan_get_active_fds (uiserver->assuan_ctx, 0 /* read fds */,
883                                 fdlist, DIM (fdlist));
884   if (nfds < 1)
885     return gpg_error (GPG_ERR_GENERAL); /* FIXME */
886
887   /* We "duplicate" the file descriptor, so we can close it here (we
888      can't close fdlist[0], as that is closed by libassuan, and
889      closing it here might cause libassuan to close some unrelated FD
890      later).  Alternatively, we could special case status_fd and
891      register/unregister it manually as needed, but this increases
892      code duplication and is more complicated as we can not use the
893      close notifications etc.  A third alternative would be to let
894      Assuan know that we closed the FD, but that complicates the
895      Assuan interface.  */
896
897   uiserver->status_cb.fd = _gpgme_io_dup (fdlist[0]);
898   if (uiserver->status_cb.fd < 0)
899     return gpg_error_from_syserror ();
900
901   if (_gpgme_io_set_close_notify (uiserver->status_cb.fd,
902                                   close_notify_handler, uiserver))
903     {
904       _gpgme_io_close (uiserver->status_cb.fd);
905       uiserver->status_cb.fd = -1;
906       return gpg_error (GPG_ERR_GENERAL);
907     }
908
909   err = add_io_cb (uiserver, &uiserver->status_cb, status_handler);
910   if (!err && uiserver->input_cb.fd != -1)
911     err = add_io_cb (uiserver, &uiserver->input_cb, _gpgme_data_outbound_handler);
912   if (!err && uiserver->output_cb.fd != -1)
913     err = add_io_cb (uiserver, &uiserver->output_cb, _gpgme_data_inbound_handler);
914   if (!err && uiserver->message_cb.fd != -1)
915     err = add_io_cb (uiserver, &uiserver->message_cb, _gpgme_data_outbound_handler);
916
917   if (!err)
918     err = assuan_write_line (uiserver->assuan_ctx, command);
919
920   if (!err)
921     uiserver_io_event (uiserver, GPGME_EVENT_START, NULL);
922
923   return err;
924 }
925
926
927 static gpgme_error_t
928 uiserver_reset (void *engine)
929 {
930   engine_uiserver_t uiserver = engine;
931
932   /* We must send a reset because we need to reset the list of
933      signers.  Note that RESET does not reset OPTION commands. */
934   return uiserver_assuan_simple_command (uiserver, "RESET", NULL, NULL);
935 }
936
937
938 static gpgme_error_t
939 _uiserver_decrypt (void *engine, int verify,
940                    gpgme_data_t ciph, gpgme_data_t plain)
941 {
942   engine_uiserver_t uiserver = engine;
943   gpgme_error_t err;
944   const char *protocol;
945   char *cmd;
946
947   if (!uiserver)
948     return gpg_error (GPG_ERR_INV_VALUE);
949   if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT)
950     protocol = "";
951   else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP)
952     protocol = " --protocol=OpenPGP";
953   else if (uiserver->protocol == GPGME_PROTOCOL_CMS)
954     protocol = " --protocol=CMS";
955   else
956     return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
957
958   if (asprintf (&cmd, "DECRYPT%s%s", protocol,
959                 verify ? "" : " --no-verify") < 0)
960     return gpg_error_from_syserror ();
961
962   uiserver->input_cb.data = ciph;
963   err = uiserver_set_fd (uiserver, INPUT_FD,
964                          map_data_enc (uiserver->input_cb.data));
965   if (err)
966     {
967       free (cmd);
968       return gpg_error (GPG_ERR_GENERAL);       /* FIXME */
969     }
970   uiserver->output_cb.data = plain;
971   err = uiserver_set_fd (uiserver, OUTPUT_FD, 0);
972   if (err)
973     {
974       free (cmd);
975       return gpg_error (GPG_ERR_GENERAL);       /* FIXME */
976     }
977   uiserver->inline_data = NULL;
978
979   err = start (engine, cmd);
980   free (cmd);
981   return err;
982 }
983
984
985 static gpgme_error_t
986 uiserver_decrypt (void *engine, gpgme_data_t ciph, gpgme_data_t plain)
987 {
988   return _uiserver_decrypt (engine, 0, ciph, plain);
989 }
990
991
992 static gpgme_error_t
993 uiserver_decrypt_verify (void *engine, gpgme_data_t ciph, gpgme_data_t plain)
994 {
995   return _uiserver_decrypt (engine, 1, ciph, plain);
996 }
997
998
999 static gpgme_error_t
1000 set_recipients (engine_uiserver_t uiserver, gpgme_key_t recp[])
1001 {
1002   gpgme_error_t err = 0;
1003   char *line;
1004   int linelen;
1005   int invalid_recipients = 0;
1006   int i;
1007
1008   linelen = 10 + 40 + 1;        /* "RECIPIENT " + guess + '\0'.  */
1009   line = malloc (10 + 40 + 1);
1010   if (!line)
1011     return gpg_error_from_syserror ();
1012   strcpy (line, "RECIPIENT ");
1013   for (i=0; !err && recp[i]; i++)
1014     {
1015       char *uid;
1016       int newlen;
1017
1018       /* We use only the first user ID of the key.  */
1019       if (!recp[i]->uids || !(uid=recp[i]->uids->uid) || !*uid)
1020         {
1021           invalid_recipients++;
1022           continue;
1023         }
1024
1025       newlen = 11 + strlen (uid);
1026       if (linelen < newlen)
1027         {
1028           char *newline = realloc (line, newlen);
1029           if (! newline)
1030             {
1031               int saved_err = gpg_error_from_syserror ();
1032               free (line);
1033               return saved_err;
1034             }
1035           line = newline;
1036           linelen = newlen;
1037         }
1038       /* FIXME: need to do proper escaping  */
1039       strcpy (&line[10], uid);
1040
1041       err = uiserver_assuan_simple_command (uiserver, line,
1042                                             uiserver->status.fnc,
1043                                             uiserver->status.fnc_value);
1044       /* FIXME: This might requires more work.  */
1045       if (gpg_err_code (err) == GPG_ERR_NO_PUBKEY)
1046         invalid_recipients++;
1047       else if (err)
1048         {
1049           free (line);
1050           return err;
1051         }
1052     }
1053   free (line);
1054   return gpg_error (invalid_recipients
1055                     ? GPG_ERR_UNUSABLE_PUBKEY : GPG_ERR_NO_ERROR);
1056 }
1057
1058
1059 static gpgme_error_t
1060 uiserver_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags,
1061                   gpgme_data_t plain, gpgme_data_t ciph, int use_armor)
1062 {
1063   engine_uiserver_t uiserver = engine;
1064   gpgme_error_t err;
1065   const char *protocol;
1066   char *cmd;
1067
1068   if (!uiserver)
1069     return gpg_error (GPG_ERR_INV_VALUE);
1070   if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT)
1071     protocol = "";
1072   else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP)
1073     protocol = " --protocol=OpenPGP";
1074   else if (uiserver->protocol == GPGME_PROTOCOL_CMS)
1075     protocol = " --protocol=CMS";
1076   else
1077     return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
1078
1079   if (flags & GPGME_ENCRYPT_PREPARE)
1080     {
1081       if (!recp || plain || ciph)
1082         return gpg_error (GPG_ERR_INV_VALUE);
1083
1084       if (asprintf (&cmd, "PREP_ENCRYPT%s%s", protocol,
1085                     (flags & GPGME_ENCRYPT_EXPECT_SIGN)
1086                     ? " --expect-sign" : "") < 0)
1087         return gpg_error_from_syserror ();
1088     }
1089   else
1090     {
1091       if (!plain || !ciph)
1092         return gpg_error (GPG_ERR_INV_VALUE);
1093
1094       if (asprintf (&cmd, "ENCRYPT%s", protocol) < 0)
1095         return gpg_error_from_syserror ();
1096     }
1097
1098   if (plain)
1099     {
1100       uiserver->input_cb.data = plain;
1101       err = uiserver_set_fd (uiserver, INPUT_FD,
1102                              map_data_enc (uiserver->input_cb.data));
1103       if (err)
1104         {
1105           free (cmd);
1106           return err;
1107         }
1108     }
1109
1110   if (ciph)
1111     {
1112       uiserver->output_cb.data = ciph;
1113       err = uiserver_set_fd (uiserver, OUTPUT_FD, use_armor ? "--armor"
1114                              : map_data_enc (uiserver->output_cb.data));
1115       if (err)
1116         {
1117           free (cmd);
1118           return err;
1119         }
1120     }
1121
1122   uiserver->inline_data = NULL;
1123
1124   if (recp)
1125     {
1126       err = set_recipients (uiserver, recp);
1127       if (err)
1128         {
1129           free (cmd);
1130           return err;
1131         }
1132     }
1133
1134   err = start (uiserver, cmd);
1135   free (cmd);
1136   return err;
1137 }
1138
1139
1140 static gpgme_error_t
1141 uiserver_sign (void *engine, gpgme_data_t in, gpgme_data_t out,
1142                gpgme_sig_mode_t mode, int use_armor, int use_textmode,
1143                int include_certs, gpgme_ctx_t ctx /* FIXME */)
1144 {
1145   engine_uiserver_t uiserver = engine;
1146   gpgme_error_t err = 0;
1147   const char *protocol;
1148   char *cmd;
1149   gpgme_key_t key;
1150
1151   if (!uiserver || !in || !out)
1152     return gpg_error (GPG_ERR_INV_VALUE);
1153   if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT)
1154     protocol = "";
1155   else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP)
1156     protocol = " --protocol=OpenPGP";
1157   else if (uiserver->protocol == GPGME_PROTOCOL_CMS)
1158     protocol = " --protocol=CMS";
1159   else
1160     return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
1161
1162   if (asprintf (&cmd, "SIGN%s%s", protocol,
1163                 (mode == GPGME_SIG_MODE_DETACH) ? " --detached" : "") < 0)
1164     return gpg_error_from_syserror ();
1165
1166   key = gpgme_signers_enum (ctx, 0);
1167   if (key)
1168     {
1169       const char *s = NULL;
1170
1171       if (key && key->uids)
1172         s = key->uids->email;
1173
1174       if (s && strlen (s) < 80)
1175         {
1176           char buf[100];
1177
1178           strcpy (stpcpy (buf, "SENDER --info "), s);
1179           err = uiserver_assuan_simple_command (uiserver, buf,
1180                                                 uiserver->status.fnc,
1181                                                 uiserver->status.fnc_value);
1182         }
1183       else
1184         err = gpg_error (GPG_ERR_INV_VALUE);
1185       gpgme_key_unref (key);
1186       if (err)
1187       {
1188         free (cmd);
1189         return err;
1190       }
1191   }
1192
1193   uiserver->input_cb.data = in;
1194   err = uiserver_set_fd (uiserver, INPUT_FD,
1195                          map_data_enc (uiserver->input_cb.data));
1196   if (err)
1197     {
1198       free (cmd);
1199       return err;
1200     }
1201   uiserver->output_cb.data = out;
1202   err = uiserver_set_fd (uiserver, OUTPUT_FD, use_armor ? "--armor"
1203                          : map_data_enc (uiserver->output_cb.data));
1204   if (err)
1205     {
1206       free (cmd);
1207       return err;
1208     }
1209   uiserver->inline_data = NULL;
1210
1211   err = start (uiserver, cmd);
1212   free (cmd);
1213   return err;
1214 }
1215
1216
1217 /* FIXME: Missing a way to specify --silent.  */
1218 static gpgme_error_t
1219 uiserver_verify (void *engine, gpgme_data_t sig, gpgme_data_t signed_text,
1220               gpgme_data_t plaintext)
1221 {
1222   engine_uiserver_t uiserver = engine;
1223   gpgme_error_t err;
1224   const char *protocol;
1225   char *cmd;
1226
1227   if (!uiserver)
1228     return gpg_error (GPG_ERR_INV_VALUE);
1229   if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT)
1230     protocol = "";
1231   else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP)
1232     protocol = " --protocol=OpenPGP";
1233   else if (uiserver->protocol == GPGME_PROTOCOL_CMS)
1234     protocol = " --protocol=CMS";
1235   else
1236     return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
1237
1238   if (asprintf (&cmd, "VERIFY%s", protocol) < 0)
1239     return gpg_error_from_syserror ();
1240
1241   uiserver->input_cb.data = sig;
1242   err = uiserver_set_fd (uiserver, INPUT_FD,
1243                          map_data_enc (uiserver->input_cb.data));
1244   if (err)
1245     {
1246       free (cmd);
1247       return err;
1248     }
1249   if (plaintext)
1250     {
1251       /* Normal or cleartext signature.  */
1252       uiserver->output_cb.data = plaintext;
1253       err = uiserver_set_fd (uiserver, OUTPUT_FD, 0);
1254     }
1255   else
1256     {
1257       /* Detached signature.  */
1258       uiserver->message_cb.data = signed_text;
1259       err = uiserver_set_fd (uiserver, MESSAGE_FD, 0);
1260     }
1261   uiserver->inline_data = NULL;
1262
1263   if (!err)
1264     err = start (uiserver, cmd);
1265
1266   free (cmd);
1267   return err;
1268 }
1269
1270
1271 /* This sets a status callback for monitoring status lines before they
1272  * are passed to a caller set handler.  */
1273 static void
1274 uiserver_set_status_cb (void *engine, gpgme_status_cb_t cb, void *cb_value)
1275 {
1276   engine_uiserver_t uiserver = engine;
1277
1278   uiserver->status.mon_cb = cb;
1279   uiserver->status.mon_cb_value = cb_value;
1280 }
1281
1282
1283 static void
1284 uiserver_set_status_handler (void *engine, engine_status_handler_t fnc,
1285                           void *fnc_value)
1286 {
1287   engine_uiserver_t uiserver = engine;
1288
1289   uiserver->status.fnc = fnc;
1290   uiserver->status.fnc_value = fnc_value;
1291 }
1292
1293
1294 static gpgme_error_t
1295 uiserver_set_colon_line_handler (void *engine, engine_colon_line_handler_t fnc,
1296                               void *fnc_value)
1297 {
1298   engine_uiserver_t uiserver = engine;
1299
1300   uiserver->colon.fnc = fnc;
1301   uiserver->colon.fnc_value = fnc_value;
1302   uiserver->colon.any = 0;
1303   return 0;
1304 }
1305
1306
1307 static void
1308 uiserver_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
1309 {
1310   engine_uiserver_t uiserver = engine;
1311   uiserver->io_cbs = *io_cbs;
1312 }
1313
1314
1315 static void
1316 uiserver_io_event (void *engine, gpgme_event_io_t type, void *type_data)
1317 {
1318   engine_uiserver_t uiserver = engine;
1319
1320   TRACE3 (DEBUG_ENGINE, "gpgme:uiserver_io_event", uiserver,
1321           "event %p, type %d, type_data %p",
1322           uiserver->io_cbs.event, type, type_data);
1323   if (uiserver->io_cbs.event)
1324     (*uiserver->io_cbs.event) (uiserver->io_cbs.event_priv, type, type_data);
1325 }
1326
1327
1328 struct engine_ops _gpgme_engine_ops_uiserver =
1329   {
1330     /* Static functions.  */
1331     _gpgme_get_default_uisrv_socket,
1332     NULL,
1333     uiserver_get_version,
1334     uiserver_get_req_version,
1335     uiserver_new,
1336
1337     /* Member functions.  */
1338     uiserver_release,
1339     uiserver_reset,
1340     uiserver_set_status_cb,
1341     uiserver_set_status_handler,
1342     NULL,               /* set_command_handler */
1343     uiserver_set_colon_line_handler,
1344     uiserver_set_locale,
1345     uiserver_set_protocol,
1346     uiserver_decrypt,
1347     uiserver_decrypt_verify,
1348     NULL,               /* delete */
1349     NULL,               /* edit */
1350     uiserver_encrypt,
1351     NULL,               /* encrypt_sign */
1352     NULL,               /* export */
1353     NULL,               /* export_ext */
1354     NULL,               /* genkey */
1355     NULL,               /* import */
1356     NULL,               /* keylist */
1357     NULL,               /* keylist_ext */
1358     uiserver_sign,
1359     NULL,               /* trustlist */
1360     uiserver_verify,
1361     NULL,               /* getauditlog */
1362     NULL,               /* opassuan_transact */
1363     NULL,               /* conf_load */
1364     NULL,               /* conf_save */
1365     uiserver_set_io_cbs,
1366     uiserver_io_event,
1367     uiserver_cancel,
1368     NULL,               /* cancel_op */
1369     NULL,               /* passwd */
1370     NULL,                /* set_pinentry_mode */
1371     NULL                /* opspawn */
1372   };