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