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