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