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