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