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