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