core: New function gpgme_op_tofu_policy
[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   (void)uiserver;
191
192   if (!strncmp (line, "PINENTRY_LAUNCHED", 17) && (line[17]==' '||!line[17]))
193     {
194       _gpgme_allow_set_foreground_window ((pid_t)strtoul (line+17, NULL, 10));
195     }
196
197   return 0;
198 }
199
200
201 static gpgme_error_t
202 uiserver_cancel (void *engine)
203 {
204   engine_uiserver_t uiserver = engine;
205
206   if (!uiserver)
207     return gpg_error (GPG_ERR_INV_VALUE);
208
209   if (uiserver->status_cb.fd != -1)
210     _gpgme_io_close (uiserver->status_cb.fd);
211   if (uiserver->input_cb.fd != -1)
212     _gpgme_io_close (uiserver->input_cb.fd);
213   if (uiserver->output_cb.fd != -1)
214     _gpgme_io_close (uiserver->output_cb.fd);
215   if (uiserver->message_cb.fd != -1)
216     _gpgme_io_close (uiserver->message_cb.fd);
217
218   if (uiserver->assuan_ctx)
219     {
220       assuan_release (uiserver->assuan_ctx);
221       uiserver->assuan_ctx = NULL;
222     }
223
224   return 0;
225 }
226
227
228 static void
229 uiserver_release (void *engine)
230 {
231   engine_uiserver_t uiserver = engine;
232
233   if (!uiserver)
234     return;
235
236   uiserver_cancel (engine);
237
238   free (uiserver->colon.attic.line);
239   free (uiserver);
240 }
241
242
243 static gpgme_error_t
244 uiserver_new (void **engine, const char *file_name, const char *home_dir,
245               const char *version)
246 {
247   gpgme_error_t err = 0;
248   engine_uiserver_t uiserver;
249   char *dft_display = NULL;
250   char dft_ttyname[64];
251   char *dft_ttytype = NULL;
252   char *optstr;
253
254   (void)home_dir;
255   (void)version; /* Not yet used.  */
256
257   uiserver = calloc (1, sizeof *uiserver);
258   if (!uiserver)
259     return gpg_error_from_syserror ();
260
261   uiserver->protocol = GPGME_PROTOCOL_DEFAULT;
262   uiserver->status_cb.fd = -1;
263   uiserver->status_cb.dir = 1;
264   uiserver->status_cb.tag = 0;
265   uiserver->status_cb.data = uiserver;
266
267   uiserver->input_cb.fd = -1;
268   uiserver->input_cb.dir = 0;
269   uiserver->input_cb.tag = 0;
270   uiserver->input_cb.server_fd = -1;
271   *uiserver->input_cb.server_fd_str = 0;
272   uiserver->output_cb.fd = -1;
273   uiserver->output_cb.dir = 1;
274   uiserver->output_cb.tag = 0;
275   uiserver->output_cb.server_fd = -1;
276   *uiserver->output_cb.server_fd_str = 0;
277   uiserver->message_cb.fd = -1;
278   uiserver->message_cb.dir = 0;
279   uiserver->message_cb.tag = 0;
280   uiserver->message_cb.server_fd = -1;
281   *uiserver->message_cb.server_fd_str = 0;
282
283   uiserver->status.fnc = 0;
284   uiserver->colon.fnc = 0;
285   uiserver->colon.attic.line = 0;
286   uiserver->colon.attic.linesize = 0;
287   uiserver->colon.attic.linelen = 0;
288   uiserver->colon.any = 0;
289
290   uiserver->inline_data = NULL;
291
292   uiserver->io_cbs.add = NULL;
293   uiserver->io_cbs.add_priv = NULL;
294   uiserver->io_cbs.remove = NULL;
295   uiserver->io_cbs.event = NULL;
296   uiserver->io_cbs.event_priv = NULL;
297
298   err = assuan_new_ext (&uiserver->assuan_ctx, GPG_ERR_SOURCE_GPGME,
299                         &_gpgme_assuan_malloc_hooks, _gpgme_assuan_log_cb,
300                         NULL);
301   if (err)
302     goto leave;
303   assuan_ctx_set_system_hooks (uiserver->assuan_ctx,
304                                &_gpgme_assuan_system_hooks);
305
306   err = assuan_socket_connect (uiserver->assuan_ctx,
307                                file_name ?
308                                file_name : _gpgme_get_default_uisrv_socket (),
309                                0, ASSUAN_SOCKET_SERVER_FDPASSING);
310   if (err)
311     goto leave;
312
313   err = _gpgme_getenv ("DISPLAY", &dft_display);
314   if (err)
315     goto leave;
316   if (dft_display)
317     {
318       if (asprintf (&optstr, "OPTION display=%s", dft_display) < 0)
319         {
320           err = gpg_error_from_syserror ();
321           free (dft_display);
322           goto leave;
323         }
324       free (dft_display);
325
326       err = assuan_transact (uiserver->assuan_ctx, optstr, NULL, NULL, NULL,
327                              NULL, NULL, NULL);
328       free (optstr);
329       if (err)
330         goto leave;
331     }
332
333   if (isatty (1))
334     {
335       int rc;
336
337       rc = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname));
338
339       /* Even though isatty() returns 1, ttyname_r() may fail in many
340          ways, e.g., when /dev/pts is not accessible under chroot.  */
341       if (!rc)
342         {
343           if (asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0)
344             {
345               err = gpg_error_from_syserror ();
346               goto leave;
347             }
348           err = assuan_transact (uiserver->assuan_ctx, optstr, NULL, NULL, NULL,
349                                  NULL, NULL, NULL);
350           free (optstr);
351           if (err)
352             goto leave;
353
354           err = _gpgme_getenv ("TERM", &dft_ttytype);
355           if (err)
356             goto leave;
357           if (dft_ttytype)
358             {
359               if (asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype) < 0)
360                 {
361                   err = gpg_error_from_syserror ();
362                   free (dft_ttytype);
363                   goto leave;
364                 }
365               free (dft_ttytype);
366
367               err = assuan_transact (uiserver->assuan_ctx, optstr, NULL, NULL,
368                                      NULL, NULL, NULL, NULL);
369               free (optstr);
370               if (err)
371                 goto leave;
372             }
373         }
374     }
375
376 #ifdef HAVE_W32_SYSTEM
377   /* Under Windows we need to use AllowSetForegroundWindow.  Tell
378      uiserver to tell us when it needs it.  */
379   if (!err)
380     {
381       err = assuan_transact (uiserver->assuan_ctx, "OPTION allow-pinentry-notify",
382                              NULL, NULL, NULL, NULL, NULL, NULL);
383       if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
384         err = 0; /* This is a new feature of uiserver.  */
385     }
386 #endif /*HAVE_W32_SYSTEM*/
387
388  leave:
389   if (err)
390     uiserver_release (uiserver);
391   else
392     *engine = uiserver;
393
394   return err;
395 }
396
397
398 static gpgme_error_t
399 uiserver_set_locale (void *engine, int category, const char *value)
400 {
401   engine_uiserver_t uiserver = engine;
402   gpgme_error_t err;
403   char *optstr;
404   const char *catstr;
405
406   /* FIXME: If value is NULL, we need to reset the option to default.
407      But we can't do this.  So we error out here.  UISERVER needs support
408      for this.  */
409   if (category == LC_CTYPE)
410     {
411       catstr = "lc-ctype";
412       if (!value && uiserver->lc_ctype_set)
413         return gpg_error (GPG_ERR_INV_VALUE);
414       if (value)
415         uiserver->lc_ctype_set = 1;
416     }
417 #ifdef LC_MESSAGES
418   else if (category == LC_MESSAGES)
419     {
420       catstr = "lc-messages";
421       if (!value && uiserver->lc_messages_set)
422         return gpg_error (GPG_ERR_INV_VALUE);
423       if (value)
424         uiserver->lc_messages_set = 1;
425     }
426 #endif /* LC_MESSAGES */
427   else
428     return gpg_error (GPG_ERR_INV_VALUE);
429
430   /* FIXME: Reset value to default.  */
431   if (!value)
432     return 0;
433
434   if (asprintf (&optstr, "OPTION %s=%s", catstr, value) < 0)
435     err = gpg_error_from_syserror ();
436   else
437     {
438       err = assuan_transact (uiserver->assuan_ctx, optstr, NULL, NULL,
439                              NULL, NULL, NULL, NULL);
440       free (optstr);
441     }
442
443   return err;
444 }
445
446
447 static gpgme_error_t
448 uiserver_set_protocol (void *engine, gpgme_protocol_t protocol)
449 {
450   engine_uiserver_t uiserver = engine;
451
452   if (protocol != GPGME_PROTOCOL_OpenPGP
453       && protocol != GPGME_PROTOCOL_CMS
454       && protocol != GPGME_PROTOCOL_DEFAULT)
455     return gpg_error (GPG_ERR_INV_VALUE);
456
457   uiserver->protocol = protocol;
458   return 0;
459 }
460
461
462 static gpgme_error_t
463 uiserver_assuan_simple_command (engine_uiserver_t uiserver, const char *cmd,
464                                 engine_status_handler_t status_fnc,
465                                 void *status_fnc_value)
466 {
467   assuan_context_t ctx = uiserver->assuan_ctx;
468   gpg_error_t err;
469   char *line;
470   size_t linelen;
471
472   err = assuan_write_line (ctx, cmd);
473   if (err)
474     return err;
475
476   do
477     {
478       err = assuan_read_line (ctx, &line, &linelen);
479       if (err)
480         return err;
481
482       if (*line == '#' || !linelen)
483         continue;
484
485       if (linelen >= 2
486           && line[0] == 'O' && line[1] == 'K'
487           && (line[2] == '\0' || line[2] == ' '))
488         return 0;
489       else if (linelen >= 4
490           && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
491           && line[3] == ' ')
492         err = atoi (&line[4]);
493       else if (linelen >= 2
494                && line[0] == 'S' && line[1] == ' ')
495         {
496           char *rest;
497           gpgme_status_code_t r;
498
499           rest = strchr (line + 2, ' ');
500           if (!rest)
501             rest = line + linelen; /* set to an empty string */
502           else
503             *(rest++) = 0;
504
505           r = _gpgme_parse_status (line + 2);
506           if (uiserver->status.mon_cb && r != GPGME_STATUS_PROGRESS)
507             {
508               /* Note that we call the monitor even if we do
509                * not know the status code (r < 0).  */
510               err = uiserver->status.mon_cb (uiserver->status.mon_cb_value,
511                                              line + 2, rest);
512             }
513
514           if (err)
515             ;
516           else if (r >= 0 && status_fnc)
517             err = status_fnc (status_fnc_value, r, rest);
518           else
519             err = gpg_error (GPG_ERR_GENERAL);
520         }
521       else
522         err = gpg_error (GPG_ERR_GENERAL);
523     }
524   while (!err);
525
526   return err;
527 }
528
529
530 typedef enum { INPUT_FD, OUTPUT_FD, MESSAGE_FD } fd_type_t;
531
532 #define COMMANDLINELEN 40
533 static gpgme_error_t
534 uiserver_set_fd (engine_uiserver_t uiserver, fd_type_t fd_type, const char *opt)
535 {
536   gpg_error_t err = 0;
537   char line[COMMANDLINELEN];
538   const char *which;
539   iocb_data_t *iocb_data;
540   int dir;
541
542   switch (fd_type)
543     {
544     case INPUT_FD:
545       which = "INPUT";
546       iocb_data = &uiserver->input_cb;
547       break;
548
549     case OUTPUT_FD:
550       which = "OUTPUT";
551       iocb_data = &uiserver->output_cb;
552       break;
553
554     case MESSAGE_FD:
555       which = "MESSAGE";
556       iocb_data = &uiserver->message_cb;
557       break;
558
559     default:
560       return gpg_error (GPG_ERR_INV_VALUE);
561     }
562
563   dir = iocb_data->dir;
564
565   /* We try to short-cut the communication by giving UISERVER direct
566      access to the file descriptor, rather than using a pipe.  */
567   iocb_data->server_fd = _gpgme_data_get_fd (iocb_data->data);
568   if (iocb_data->server_fd < 0)
569     {
570       int fds[2];
571
572       if (_gpgme_io_pipe (fds, 0) < 0)
573         return gpg_error_from_syserror ();
574
575       iocb_data->fd = dir ? fds[0] : fds[1];
576       iocb_data->server_fd = dir ? fds[1] : fds[0];
577
578       if (_gpgme_io_set_close_notify (iocb_data->fd,
579                                       close_notify_handler, uiserver))
580         {
581           err = gpg_error (GPG_ERR_GENERAL);
582           goto leave_set_fd;
583         }
584     }
585
586   err = assuan_sendfd (uiserver->assuan_ctx, iocb_data->server_fd);
587   if (err)
588     goto leave_set_fd;
589
590   _gpgme_io_close (iocb_data->server_fd);
591   iocb_data->server_fd = -1;
592
593   if (opt)
594     snprintf (line, COMMANDLINELEN, "%s FD %s", which, opt);
595   else
596     snprintf (line, COMMANDLINELEN, "%s FD", which);
597
598   err = uiserver_assuan_simple_command (uiserver, line, NULL, NULL);
599
600  leave_set_fd:
601   if (err)
602     {
603       _gpgme_io_close (iocb_data->fd);
604       iocb_data->fd = -1;
605       if (iocb_data->server_fd != -1)
606         {
607           _gpgme_io_close (iocb_data->server_fd);
608           iocb_data->server_fd = -1;
609         }
610     }
611
612   return err;
613 }
614
615
616 static const char *
617 map_data_enc (gpgme_data_t d)
618 {
619   switch (gpgme_data_get_encoding (d))
620     {
621     case GPGME_DATA_ENCODING_NONE:
622       break;
623     case GPGME_DATA_ENCODING_BINARY:
624       return "--binary";
625     case GPGME_DATA_ENCODING_BASE64:
626       return "--base64";
627     case GPGME_DATA_ENCODING_ARMOR:
628       return "--armor";
629     default:
630       break;
631     }
632   return NULL;
633 }
634
635
636 static gpgme_error_t
637 status_handler (void *opaque, int fd)
638 {
639   struct io_cb_data *data = (struct io_cb_data *) opaque;
640   engine_uiserver_t uiserver = (engine_uiserver_t) data->handler_value;
641   gpgme_error_t err = 0;
642   char *line;
643   size_t linelen;
644
645   do
646     {
647       err = assuan_read_line (uiserver->assuan_ctx, &line, &linelen);
648       if (err)
649         {
650           /* Try our best to terminate the connection friendly.  */
651           /*      assuan_write_line (uiserver->assuan_ctx, "BYE"); */
652           TRACE3 (DEBUG_CTX, "gpgme:status_handler", uiserver,
653                   "fd 0x%x: error from assuan (%d) getting status line : %s",
654                   fd, err, gpg_strerror (err));
655         }
656       else if (linelen >= 3
657                && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
658                && (line[3] == '\0' || line[3] == ' '))
659         {
660           if (line[3] == ' ')
661             err = atoi (&line[4]);
662           if (! err)
663             err = gpg_error (GPG_ERR_GENERAL);
664           TRACE2 (DEBUG_CTX, "gpgme:status_handler", uiserver,
665                   "fd 0x%x: ERR line - mapped to: %s",
666                   fd, err ? gpg_strerror (err) : "ok");
667           /* Try our best to terminate the connection friendly.  */
668           /*      assuan_write_line (uiserver->assuan_ctx, "BYE"); */
669         }
670       else if (linelen >= 2
671                && line[0] == 'O' && line[1] == 'K'
672                && (line[2] == '\0' || line[2] == ' '))
673         {
674           if (uiserver->status.fnc)
675             {
676               char emptystring[1] = {0};
677               err = uiserver->status.fnc (uiserver->status.fnc_value,
678                                           GPGME_STATUS_EOF, emptystring);
679             }
680
681           if (!err && uiserver->colon.fnc && uiserver->colon.any)
682             {
683               /* We must tell a colon function about the EOF. We do
684                  this only when we have seen any data lines.  Note
685                  that this inlined use of colon data lines will
686                  eventually be changed into using a regular data
687                  channel. */
688               uiserver->colon.any = 0;
689               err = uiserver->colon.fnc (uiserver->colon.fnc_value, NULL);
690             }
691           TRACE2 (DEBUG_CTX, "gpgme:status_handler", uiserver,
692                   "fd 0x%x: OK line - final status: %s",
693                   fd, err ? gpg_strerror (err) : "ok");
694           _gpgme_io_close (uiserver->status_cb.fd);
695           return err;
696         }
697       else if (linelen > 2
698                && line[0] == 'D' && line[1] == ' '
699                && uiserver->colon.fnc)
700         {
701           /* We are using the colon handler even for plain inline data
702              - strange name for that function but for historic reasons
703              we keep it.  */
704           /* FIXME We can't use this for binary data because we
705              assume this is a string.  For the current usage of colon
706              output it is correct.  */
707           char *src = line + 2;
708           char *end = line + linelen;
709           char *dst;
710           char **aline = &uiserver->colon.attic.line;
711           int *alinelen = &uiserver->colon.attic.linelen;
712
713           if (uiserver->colon.attic.linesize < *alinelen + linelen + 1)
714             {
715               char *newline = realloc (*aline, *alinelen + linelen + 1);
716               if (!newline)
717                 err = gpg_error_from_syserror ();
718               else
719                 {
720                   *aline = newline;
721                   uiserver->colon.attic.linesize = *alinelen + linelen + 1;
722                 }
723             }
724           if (!err)
725             {
726               dst = *aline + *alinelen;
727
728               while (!err && src < end)
729                 {
730                   if (*src == '%' && src + 2 < end)
731                     {
732                       /* Handle escaped characters.  */
733                       ++src;
734                       *dst = _gpgme_hextobyte (src);
735                       (*alinelen)++;
736                       src += 2;
737                     }
738                   else
739                     {
740                       *dst = *src++;
741                       (*alinelen)++;
742                     }
743
744                   if (*dst == '\n')
745                     {
746                       /* Terminate the pending line, pass it to the colon
747                          handler and reset it.  */
748
749                       uiserver->colon.any = 1;
750                       if (*alinelen > 1 && *(dst - 1) == '\r')
751                         dst--;
752                       *dst = '\0';
753
754                       /* FIXME How should we handle the return code?  */
755                       err = uiserver->colon.fnc (uiserver->colon.fnc_value, *aline);
756                       if (!err)
757                         {
758                           dst = *aline;
759                           *alinelen = 0;
760                         }
761                     }
762                   else
763                     dst++;
764                 }
765             }
766           TRACE2 (DEBUG_CTX, "gpgme:status_handler", uiserver,
767                   "fd 0x%x: D line; final status: %s",
768                   fd, err? gpg_strerror (err):"ok");
769         }
770       else if (linelen > 2
771                && line[0] == 'D' && line[1] == ' '
772                && uiserver->inline_data)
773         {
774           char *src = line + 2;
775           char *end = line + linelen;
776           char *dst = src;
777           gpgme_ssize_t nwritten;
778
779           linelen = 0;
780           while (src < end)
781             {
782               if (*src == '%' && src + 2 < end)
783                 {
784                   /* Handle escaped characters.  */
785                   ++src;
786                   *dst++ = _gpgme_hextobyte (src);
787                   src += 2;
788                 }
789               else
790                 *dst++ = *src++;
791
792               linelen++;
793             }
794
795           src = line + 2;
796           while (linelen > 0)
797             {
798               nwritten = gpgme_data_write (uiserver->inline_data, src, linelen);
799               if (!nwritten || (nwritten < 0 && errno != EINTR)
800                   || nwritten > linelen)
801                 {
802                   err = gpg_error_from_syserror ();
803                   break;
804                 }
805               src += nwritten;
806               linelen -= nwritten;
807             }
808
809           TRACE2 (DEBUG_CTX, "gpgme:status_handler", uiserver,
810                   "fd 0x%x: D inlinedata; final status: %s",
811                   fd, err? gpg_strerror (err):"ok");
812         }
813       else if (linelen > 2
814                && line[0] == 'S' && line[1] == ' ')
815         {
816           char *rest;
817           gpgme_status_code_t r;
818
819           rest = strchr (line + 2, ' ');
820           if (!rest)
821             rest = line + linelen; /* set to an empty string */
822           else
823             *(rest++) = 0;
824
825           r = _gpgme_parse_status (line + 2);
826
827           if (r >= 0)
828             {
829               if (uiserver->status.fnc)
830                 err = uiserver->status.fnc (uiserver->status.fnc_value, r, rest);
831             }
832           else
833             fprintf (stderr, "[UNKNOWN STATUS]%s %s", line + 2, rest);
834           TRACE3 (DEBUG_CTX, "gpgme:status_handler", uiserver,
835                   "fd 0x%x: S line (%s) - final status: %s",
836                   fd, line+2, err? gpg_strerror (err):"ok");
837         }
838       else if (linelen >= 7
839                && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
840                && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
841                && line[6] == 'E'
842                && (line[7] == '\0' || line[7] == ' '))
843         {
844           char *keyword = line+7;
845
846           while (*keyword == ' ')
847             keyword++;;
848           default_inq_cb (uiserver, keyword);
849           assuan_write_line (uiserver->assuan_ctx, "END");
850         }
851
852     }
853   while (!err && assuan_pending_line (uiserver->assuan_ctx));
854
855   return err;
856 }
857
858
859 static gpgme_error_t
860 add_io_cb (engine_uiserver_t uiserver, iocb_data_t *iocbd, gpgme_io_cb_t handler)
861 {
862   gpgme_error_t err;
863
864   TRACE_BEG2 (DEBUG_ENGINE, "engine-uiserver:add_io_cb", uiserver,
865               "fd %d, dir %d", iocbd->fd, iocbd->dir);
866   err = (*uiserver->io_cbs.add) (uiserver->io_cbs.add_priv,
867                               iocbd->fd, iocbd->dir,
868                               handler, iocbd->data, &iocbd->tag);
869   if (err)
870     return TRACE_ERR (err);
871   if (!iocbd->dir)
872     /* FIXME Kludge around poll() problem.  */
873     err = _gpgme_io_set_nonblocking (iocbd->fd);
874   return TRACE_ERR (err);
875 }
876
877
878 static gpgme_error_t
879 start (engine_uiserver_t uiserver, const char *command)
880 {
881   gpgme_error_t err;
882   int fdlist[5];
883   int nfds;
884
885   /* We need to know the fd used by assuan for reads.  We do this by
886      using the assumption that the first returned fd from
887      assuan_get_active_fds() is always this one.  */
888   nfds = assuan_get_active_fds (uiserver->assuan_ctx, 0 /* read fds */,
889                                 fdlist, DIM (fdlist));
890   if (nfds < 1)
891     return gpg_error (GPG_ERR_GENERAL); /* FIXME */
892
893   /* We "duplicate" the file descriptor, so we can close it here (we
894      can't close fdlist[0], as that is closed by libassuan, and
895      closing it here might cause libassuan to close some unrelated FD
896      later).  Alternatively, we could special case status_fd and
897      register/unregister it manually as needed, but this increases
898      code duplication and is more complicated as we can not use the
899      close notifications etc.  A third alternative would be to let
900      Assuan know that we closed the FD, but that complicates the
901      Assuan interface.  */
902
903   uiserver->status_cb.fd = _gpgme_io_dup (fdlist[0]);
904   if (uiserver->status_cb.fd < 0)
905     return gpg_error_from_syserror ();
906
907   if (_gpgme_io_set_close_notify (uiserver->status_cb.fd,
908                                   close_notify_handler, uiserver))
909     {
910       _gpgme_io_close (uiserver->status_cb.fd);
911       uiserver->status_cb.fd = -1;
912       return gpg_error (GPG_ERR_GENERAL);
913     }
914
915   err = add_io_cb (uiserver, &uiserver->status_cb, status_handler);
916   if (!err && uiserver->input_cb.fd != -1)
917     err = add_io_cb (uiserver, &uiserver->input_cb, _gpgme_data_outbound_handler);
918   if (!err && uiserver->output_cb.fd != -1)
919     err = add_io_cb (uiserver, &uiserver->output_cb, _gpgme_data_inbound_handler);
920   if (!err && uiserver->message_cb.fd != -1)
921     err = add_io_cb (uiserver, &uiserver->message_cb, _gpgme_data_outbound_handler);
922
923   if (!err)
924     err = assuan_write_line (uiserver->assuan_ctx, command);
925
926   if (!err)
927     uiserver_io_event (uiserver, GPGME_EVENT_START, NULL);
928
929   return err;
930 }
931
932
933 static gpgme_error_t
934 uiserver_reset (void *engine)
935 {
936   engine_uiserver_t uiserver = engine;
937
938   /* We must send a reset because we need to reset the list of
939      signers.  Note that RESET does not reset OPTION commands. */
940   return uiserver_assuan_simple_command (uiserver, "RESET", NULL, NULL);
941 }
942
943
944 static gpgme_error_t
945 _uiserver_decrypt (void *engine, int verify,
946                    gpgme_data_t ciph, gpgme_data_t plain)
947 {
948   engine_uiserver_t uiserver = engine;
949   gpgme_error_t err;
950   const char *protocol;
951   char *cmd;
952
953   if (!uiserver)
954     return gpg_error (GPG_ERR_INV_VALUE);
955   if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT)
956     protocol = "";
957   else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP)
958     protocol = " --protocol=OpenPGP";
959   else if (uiserver->protocol == GPGME_PROTOCOL_CMS)
960     protocol = " --protocol=CMS";
961   else
962     return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
963
964   if (asprintf (&cmd, "DECRYPT%s%s", protocol,
965                 verify ? "" : " --no-verify") < 0)
966     return gpg_error_from_syserror ();
967
968   uiserver->input_cb.data = ciph;
969   err = uiserver_set_fd (uiserver, INPUT_FD,
970                          map_data_enc (uiserver->input_cb.data));
971   if (err)
972     {
973       free (cmd);
974       return gpg_error (GPG_ERR_GENERAL);       /* FIXME */
975     }
976   uiserver->output_cb.data = plain;
977   err = uiserver_set_fd (uiserver, OUTPUT_FD, 0);
978   if (err)
979     {
980       free (cmd);
981       return gpg_error (GPG_ERR_GENERAL);       /* FIXME */
982     }
983   uiserver->inline_data = NULL;
984
985   err = start (engine, cmd);
986   free (cmd);
987   return err;
988 }
989
990
991 static gpgme_error_t
992 uiserver_decrypt (void *engine, gpgme_data_t ciph, gpgme_data_t plain)
993 {
994   return _uiserver_decrypt (engine, 0, ciph, plain);
995 }
996
997
998 static gpgme_error_t
999 uiserver_decrypt_verify (void *engine, gpgme_data_t ciph, gpgme_data_t plain)
1000 {
1001   return _uiserver_decrypt (engine, 1, ciph, plain);
1002 }
1003
1004
1005 static gpgme_error_t
1006 set_recipients (engine_uiserver_t uiserver, gpgme_key_t recp[])
1007 {
1008   gpgme_error_t err = 0;
1009   char *line;
1010   int linelen;
1011   int invalid_recipients = 0;
1012   int i;
1013
1014   linelen = 10 + 40 + 1;        /* "RECIPIENT " + guess + '\0'.  */
1015   line = malloc (10 + 40 + 1);
1016   if (!line)
1017     return gpg_error_from_syserror ();
1018   strcpy (line, "RECIPIENT ");
1019   for (i=0; !err && recp[i]; i++)
1020     {
1021       char *uid;
1022       int newlen;
1023
1024       /* We use only the first user ID of the key.  */
1025       if (!recp[i]->uids || !(uid=recp[i]->uids->uid) || !*uid)
1026         {
1027           invalid_recipients++;
1028           continue;
1029         }
1030
1031       newlen = 11 + strlen (uid);
1032       if (linelen < newlen)
1033         {
1034           char *newline = realloc (line, newlen);
1035           if (! newline)
1036             {
1037               int saved_err = gpg_error_from_syserror ();
1038               free (line);
1039               return saved_err;
1040             }
1041           line = newline;
1042           linelen = newlen;
1043         }
1044       /* FIXME: need to do proper escaping  */
1045       strcpy (&line[10], uid);
1046
1047       err = uiserver_assuan_simple_command (uiserver, line,
1048                                             uiserver->status.fnc,
1049                                             uiserver->status.fnc_value);
1050       /* FIXME: This might 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     }
1059   free (line);
1060   return gpg_error (invalid_recipients
1061                     ? GPG_ERR_UNUSABLE_PUBKEY : GPG_ERR_NO_ERROR);
1062 }
1063
1064
1065 static gpgme_error_t
1066 uiserver_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags,
1067                   gpgme_data_t plain, gpgme_data_t ciph, int use_armor)
1068 {
1069   engine_uiserver_t uiserver = engine;
1070   gpgme_error_t err;
1071   const char *protocol;
1072   char *cmd;
1073
1074   if (!uiserver)
1075     return gpg_error (GPG_ERR_INV_VALUE);
1076   if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT)
1077     protocol = "";
1078   else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP)
1079     protocol = " --protocol=OpenPGP";
1080   else if (uiserver->protocol == GPGME_PROTOCOL_CMS)
1081     protocol = " --protocol=CMS";
1082   else
1083     return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
1084
1085   if (flags & GPGME_ENCRYPT_PREPARE)
1086     {
1087       if (!recp || plain || ciph)
1088         return gpg_error (GPG_ERR_INV_VALUE);
1089
1090       if (asprintf (&cmd, "PREP_ENCRYPT%s%s", protocol,
1091                     (flags & GPGME_ENCRYPT_EXPECT_SIGN)
1092                     ? " --expect-sign" : "") < 0)
1093         return gpg_error_from_syserror ();
1094     }
1095   else
1096     {
1097       if (!plain || !ciph)
1098         return gpg_error (GPG_ERR_INV_VALUE);
1099
1100       if (asprintf (&cmd, "ENCRYPT%s", protocol) < 0)
1101         return gpg_error_from_syserror ();
1102     }
1103
1104   if (plain)
1105     {
1106       uiserver->input_cb.data = plain;
1107       err = uiserver_set_fd (uiserver, INPUT_FD,
1108                              map_data_enc (uiserver->input_cb.data));
1109       if (err)
1110         {
1111           free (cmd);
1112           return err;
1113         }
1114     }
1115
1116   if (ciph)
1117     {
1118       uiserver->output_cb.data = ciph;
1119       err = uiserver_set_fd (uiserver, OUTPUT_FD, use_armor ? "--armor"
1120                              : map_data_enc (uiserver->output_cb.data));
1121       if (err)
1122         {
1123           free (cmd);
1124           return err;
1125         }
1126     }
1127
1128   uiserver->inline_data = NULL;
1129
1130   if (recp)
1131     {
1132       err = set_recipients (uiserver, recp);
1133       if (err)
1134         {
1135           free (cmd);
1136           return err;
1137         }
1138     }
1139
1140   err = start (uiserver, cmd);
1141   free (cmd);
1142   return err;
1143 }
1144
1145
1146 static gpgme_error_t
1147 uiserver_sign (void *engine, gpgme_data_t in, gpgme_data_t out,
1148                gpgme_sig_mode_t mode, int use_armor, int use_textmode,
1149                int include_certs, gpgme_ctx_t ctx /* FIXME */)
1150 {
1151   engine_uiserver_t uiserver = engine;
1152   gpgme_error_t err = 0;
1153   const char *protocol;
1154   char *cmd;
1155   gpgme_key_t key;
1156
1157   (void)use_textmode;
1158   (void)include_certs;
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_syserror ();
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, 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_syserror ();
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 /* This sets a status callback for monitoring status lines before they
1281  * are passed to a caller set handler.  */
1282 static void
1283 uiserver_set_status_cb (void *engine, gpgme_status_cb_t cb, void *cb_value)
1284 {
1285   engine_uiserver_t uiserver = engine;
1286
1287   uiserver->status.mon_cb = cb;
1288   uiserver->status.mon_cb_value = cb_value;
1289 }
1290
1291
1292 static void
1293 uiserver_set_status_handler (void *engine, engine_status_handler_t fnc,
1294                           void *fnc_value)
1295 {
1296   engine_uiserver_t uiserver = engine;
1297
1298   uiserver->status.fnc = fnc;
1299   uiserver->status.fnc_value = fnc_value;
1300 }
1301
1302
1303 static gpgme_error_t
1304 uiserver_set_colon_line_handler (void *engine, engine_colon_line_handler_t fnc,
1305                               void *fnc_value)
1306 {
1307   engine_uiserver_t uiserver = engine;
1308
1309   uiserver->colon.fnc = fnc;
1310   uiserver->colon.fnc_value = fnc_value;
1311   uiserver->colon.any = 0;
1312   return 0;
1313 }
1314
1315
1316 static void
1317 uiserver_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
1318 {
1319   engine_uiserver_t uiserver = engine;
1320   uiserver->io_cbs = *io_cbs;
1321 }
1322
1323
1324 static void
1325 uiserver_io_event (void *engine, gpgme_event_io_t type, void *type_data)
1326 {
1327   engine_uiserver_t uiserver = engine;
1328
1329   TRACE3 (DEBUG_ENGINE, "gpgme:uiserver_io_event", uiserver,
1330           "event %p, type %d, type_data %p",
1331           uiserver->io_cbs.event, type, type_data);
1332   if (uiserver->io_cbs.event)
1333     (*uiserver->io_cbs.event) (uiserver->io_cbs.event_priv, type, type_data);
1334 }
1335
1336
1337 struct engine_ops _gpgme_engine_ops_uiserver =
1338   {
1339     /* Static functions.  */
1340     _gpgme_get_default_uisrv_socket,
1341     NULL,
1342     uiserver_get_version,
1343     uiserver_get_req_version,
1344     uiserver_new,
1345
1346     /* Member functions.  */
1347     uiserver_release,
1348     uiserver_reset,
1349     uiserver_set_status_cb,
1350     uiserver_set_status_handler,
1351     NULL,               /* set_command_handler */
1352     uiserver_set_colon_line_handler,
1353     uiserver_set_locale,
1354     uiserver_set_protocol,
1355     uiserver_decrypt,
1356     uiserver_decrypt_verify,
1357     NULL,               /* delete */
1358     NULL,               /* edit */
1359     uiserver_encrypt,
1360     NULL,               /* encrypt_sign */
1361     NULL,               /* export */
1362     NULL,               /* export_ext */
1363     NULL,               /* genkey */
1364     NULL,               /* import */
1365     NULL,               /* keylist */
1366     NULL,               /* keylist_ext */
1367     NULL,               /* keysign */
1368     NULL,               /* tofu_policy */
1369     uiserver_sign,
1370     NULL,               /* trustlist */
1371     uiserver_verify,
1372     NULL,               /* getauditlog */
1373     NULL,               /* opassuan_transact */
1374     NULL,               /* conf_load */
1375     NULL,               /* conf_save */
1376     uiserver_set_io_cbs,
1377     uiserver_io_event,
1378     uiserver_cancel,
1379     NULL,               /* cancel_op */
1380     NULL,               /* passwd */
1381     NULL,                /* set_pinentry_mode */
1382     NULL                /* opspawn */
1383   };