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