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