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